diff --git a/cmd/goflow2/main.go b/cmd/goflow2/main.go index af7fe2d5..9c2da81d 100644 --- a/cmd/goflow2/main.go +++ b/cmd/goflow2/main.go @@ -2,6 +2,7 @@ package main import ( "context" + "encoding/json" "errors" "flag" "fmt" @@ -307,6 +308,26 @@ func main() { os.Exit(1) } + // Add optional HTTP handler for templates + if nfP, ok := p.(*utils.NetFlowPipe); ok && *TemplatePath != "" { + http.HandleFunc(*TemplatePath, func(wr http.ResponseWriter, r *http.Request) { + templates := nfP.GetTemplatesForAllSources() + if body, err := json.MarshalIndent(templates, "", " "); err != nil { + slog.Error("error writing JSON body for /templates", slog.String("error", err.Error())) + wr.WriteHeader(http.StatusInternalServerError) + if _, err := wr.Write([]byte("Internal Server Error\n")); err != nil { + slog.Error("error writing HTTP", slog.String("error", err.Error())) + } + } else { + wr.Header().Add("Content-Type", "application/json") + wr.WriteHeader(http.StatusOK) + if _, err := wr.Write(body); err != nil { + slog.Error("error writing HTTP", slog.String("error", err.Error())) + } + } + }) + } + decodeFunc = p.DecodeFlow // intercept panic and generate error decodeFunc = debug.PanicDecoderWrapper(decodeFunc) diff --git a/decoders/netflow/templates.go b/decoders/netflow/templates.go index 42be916b..62ba061c 100644 --- a/decoders/netflow/templates.go +++ b/decoders/netflow/templates.go @@ -20,6 +20,7 @@ type NetFlowTemplateSystem interface { RemoveTemplate(version uint16, obsDomainId uint32, templateId uint16) (interface{}, error) GetTemplate(version uint16, obsDomainId uint32, templateId uint16) (interface{}, error) AddTemplate(version uint16, obsDomainId uint32, templateId uint16, template interface{}) error + GetTemplates() FlowBaseTemplateSet } func (ts *BasicTemplateSystem) GetTemplates() FlowBaseTemplateSet { diff --git a/metrics/templates.go b/metrics/templates.go index f5e21993..fba0ec19 100644 --- a/metrics/templates.go +++ b/metrics/templates.go @@ -70,3 +70,7 @@ func (s *PromTemplateSystem) RemoveTemplate(version uint16, obsDomainId uint32, return template, err } + +func (s *PromTemplateSystem) GetTemplates() netflow.FlowBaseTemplateSet { + return s.wrapped.GetTemplates() +} diff --git a/utils/pipe.go b/utils/pipe.go index 14079708..bb1450a5 100644 --- a/utils/pipe.go +++ b/utils/pipe.go @@ -3,6 +3,7 @@ package utils import ( "bytes" "fmt" + "maps" "sync" "github.com/netsampler/goflow2/v2/decoders/netflow" @@ -223,6 +224,23 @@ func (p *NetFlowPipe) DecodeFlow(msg interface{}) error { func (p *NetFlowPipe) Close() { } +// Return a copy of the template set for all known netflow sources. Useful for exporting the +// templates via an HTTP endpoint +func (p *NetFlowPipe) GetTemplatesForAllSources() map[string]map[uint64]interface{} { + p.templateslock.RLock() + defer p.templateslock.RUnlock() + + ret := make(map[string]map[uint64]interface{}) + for k, v := range p.templates { + templateRef := v.GetTemplates() + var templateCopy = make(netflow.FlowBaseTemplateSet) + maps.Copy(templateCopy, templateRef) + ret[k] = templateCopy + } + + return ret +} + type AutoFlowPipe struct { *SFlowPipe *NetFlowPipe