diff --git a/collector/collector.go b/collector/collector.go index 3142056..22a1b28 100644 --- a/collector/collector.go +++ b/collector/collector.go @@ -175,6 +175,13 @@ func WithIpsec() Option { } } +// WithConntrack enables firewall/NAT connection tracking metrics +func WithConntrack() Option { + return func(c *collector) { + c.collectors = append(c.collectors, newConntrackCollector()) + } +} + // Option applies options to collector type Option func(*collector) diff --git a/collector/conntrack_collector.go b/collector/conntrack_collector.go new file mode 100644 index 0000000..31b6dbc --- /dev/null +++ b/collector/conntrack_collector.go @@ -0,0 +1,68 @@ +package collector + +import ( + "strconv" + "strings" + + "github.com/prometheus/client_golang/prometheus" + log "github.com/sirupsen/logrus" + "gopkg.in/routeros.v2/proto" +) + +type conntrackCollector struct { + props []string + totalEntriesDesc *prometheus.Desc + maxEntriesDesc *prometheus.Desc +} + +func newConntrackCollector() routerOSCollector { + const prefix = "conntrack" + + labelNames := []string{"name", "address"} + return &conntrackCollector{ + props: []string{"total-entries", "max-entries"}, + totalEntriesDesc: description(prefix, "entries", "Number of tracked connections", labelNames), + maxEntriesDesc: description(prefix, "max_entries", "Conntrack table capacity", labelNames), + } +} + +func (c *conntrackCollector) describe(ch chan<- *prometheus.Desc) { + ch <- c.totalEntriesDesc + ch <- c.maxEntriesDesc +} + +func (c *conntrackCollector) collect(ctx *collectorContext) error { + reply, err := ctx.client.Run("/ip/firewall/connection/tracking/print", "=.proplist="+strings.Join(c.props, ",")) + if err != nil { + log.WithFields(log.Fields{ + "device": ctx.device.Name, + "error": err, + }).Error("error fetching conntrack table metrics") + return err + } + + for _, re := range reply.Re { + c.collectMetricForProperty("total-entries", c.totalEntriesDesc, re, ctx) + c.collectMetricForProperty("max-entries", c.maxEntriesDesc, re, ctx) + } + + return nil +} + +func (c *conntrackCollector) collectMetricForProperty(property string, desc *prometheus.Desc, re *proto.Sentence, ctx *collectorContext) { + if re.Map[property] == "" { + return + } + v, err := strconv.ParseFloat(re.Map[property], 64) + if err != nil { + log.WithFields(log.Fields{ + "device": ctx.device.Name, + "property": property, + "value": re.Map[property], + "error": err, + }).Error("error parsing conntrack metric value") + return + } + + ctx.ch <- prometheus.MustNewConstMetric(desc, prometheus.GaugeValue, v, ctx.device.Name, ctx.device.Address) +} diff --git a/config/config.go b/config/config.go index 6dabc6c..e900513 100644 --- a/config/config.go +++ b/config/config.go @@ -11,21 +11,22 @@ import ( type Config struct { Devices []Device `yaml:"devices"` Features struct { - BGP bool `yaml:"bgp,omitempty"` - DHCP bool `yaml:"dhcp,omitempty"` - DHCPL bool `yaml:"dhcpl,omitempty"` - DHCPv6 bool `yaml:"dhcpv6,omitempty"` - Firmware bool `yaml:"firmware,omitempty"` - Health bool `yaml:"health,omitempty"` - Routes bool `yaml:"routes,omitempty"` - POE bool `yaml:"poe,omitempty"` - Pools bool `yaml:"pools,omitempty"` - Optics bool `yaml:"optics,omitempty"` - W60G bool `yaml:"w60g,omitempty"` - WlanSTA bool `yaml:"wlansta,omitempty"` - WlanIF bool `yaml:"wlanif,omitempty"` - Monitor bool `yaml:"monitor,omitempty"` - Ipsec bool `yaml:"ipsec,omitempty"` + BGP bool `yaml:"bgp,omitempty"` + Conntrack bool `yaml:"conntrack,omitempty"` + DHCP bool `yaml:"dhcp,omitempty"` + DHCPL bool `yaml:"dhcpl,omitempty"` + DHCPv6 bool `yaml:"dhcpv6,omitempty"` + Firmware bool `yaml:"firmware,omitempty"` + Health bool `yaml:"health,omitempty"` + Routes bool `yaml:"routes,omitempty"` + POE bool `yaml:"poe,omitempty"` + Pools bool `yaml:"pools,omitempty"` + Optics bool `yaml:"optics,omitempty"` + W60G bool `yaml:"w60g,omitempty"` + WlanSTA bool `yaml:"wlansta,omitempty"` + WlanIF bool `yaml:"wlanif,omitempty"` + Monitor bool `yaml:"monitor,omitempty"` + Ipsec bool `yaml:"ipsec,omitempty"` } `yaml:"features,omitempty"` } diff --git a/config/config.test.yml b/config/config.test.yml index 36c6286..905f8c6 100644 --- a/config/config.test.yml +++ b/config/config.test.yml @@ -10,6 +10,7 @@ devices: features: bgp: true + conntrack: true dhcp: true dhcpv6: true dhcpl: true diff --git a/config/config_test.go b/config/config_test.go index e23cc9f..ac8caf9 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -20,6 +20,7 @@ func TestShouldParse(t *testing.T) { assertDevice("test1", "192.168.1.1", "foo", "bar", c.Devices[0], t) assertDevice("test2", "192.168.2.1", "test", "123", c.Devices[1], t) assertFeature("BGP", c.Features.BGP, t) + assertFeature("Conntrack", c.Features.Conntrack, t) assertFeature("DHCP", c.Features.DHCP, t) assertFeature("DHCPv6", c.Features.DHCPv6, t) assertFeature("Pools", c.Features.Pools, t) diff --git a/main.go b/main.go index 3adb4d0..cfdb65a 100644 --- a/main.go +++ b/main.go @@ -36,21 +36,22 @@ var ( user = flag.String("user", "", "user for authentication with single device") ver = flag.Bool("version", false, "find the version of binary") - withBgp = flag.Bool("with-bgp", false, "retrieves BGP routing infrormation") - withRoutes = flag.Bool("with-routes", false, "retrieves routing table information") - withDHCP = flag.Bool("with-dhcp", false, "retrieves DHCP server metrics") - withDHCPL = flag.Bool("with-dhcpl", false, "retrieves DHCP server lease metrics") - withDHCPv6 = flag.Bool("with-dhcpv6", false, "retrieves DHCPv6 server metrics") - withFirmware = flag.Bool("with-firmware", false, "retrieves firmware versions") - withHealth = flag.Bool("with-health", false, "retrieves board Health metrics") - withPOE = flag.Bool("with-poe", false, "retrieves PoE metrics") - withPools = flag.Bool("with-pools", false, "retrieves IP(v6) pool metrics") - withOptics = flag.Bool("with-optics", false, "retrieves optical diagnostic metrics") - withW60G = flag.Bool("with-w60g", false, "retrieves w60g interface metrics") - withWlanSTA = flag.Bool("with-wlansta", false, "retrieves connected wlan station metrics") - withWlanIF = flag.Bool("with-wlanif", false, "retrieves wlan interface metrics") - withMonitor = flag.Bool("with-monitor", false, "retrieves ethernet interface monitor info") - withIpsec = flag.Bool("with-ipsec", false, "retrieves ipsec metrics") + withBgp = flag.Bool("with-bgp", false, "retrieves BGP routing infrormation") + withConntrack = flag.Bool("with-conntrack", false, "retrieves connection tracking metrics") + withRoutes = flag.Bool("with-routes", false, "retrieves routing table information") + withDHCP = flag.Bool("with-dhcp", false, "retrieves DHCP server metrics") + withDHCPL = flag.Bool("with-dhcpl", false, "retrieves DHCP server lease metrics") + withDHCPv6 = flag.Bool("with-dhcpv6", false, "retrieves DHCPv6 server metrics") + withFirmware = flag.Bool("with-firmware", false, "retrieves firmware versions") + withHealth = flag.Bool("with-health", false, "retrieves board Health metrics") + withPOE = flag.Bool("with-poe", false, "retrieves PoE metrics") + withPools = flag.Bool("with-pools", false, "retrieves IP(v6) pool metrics") + withOptics = flag.Bool("with-optics", false, "retrieves optical diagnostic metrics") + withW60G = flag.Bool("with-w60g", false, "retrieves w60g interface metrics") + withWlanSTA = flag.Bool("with-wlansta", false, "retrieves connected wlan station metrics") + withWlanIF = flag.Bool("with-wlanif", false, "retrieves wlan interface metrics") + withMonitor = flag.Bool("with-monitor", false, "retrieves ethernet interface monitor info") + withIpsec = flag.Bool("with-ipsec", false, "retrieves ipsec metrics") cfg *config.Config @@ -241,6 +242,10 @@ func collectorOptions() []collector.Option { opts = append(opts, collector.WithIpsec()) } + if *withConntrack || cfg.Features.Conntrack { + opts = append(opts, collector.WithConntrack()) + } + if *timeout != collector.DefaultTimeout { opts = append(opts, collector.WithTimeout(*timeout)) }