From f8e6b9a6f1896c484de11131e36c4e065541af4b Mon Sep 17 00:00:00 2001 From: Mike Crute Date: Tue, 13 Oct 2020 22:20:55 +0000 Subject: Add conntrack_exec collector for Linux --- collector/conntrack_exec_linux.go | 195 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 195 insertions(+) create mode 100644 collector/conntrack_exec_linux.go diff --git a/collector/conntrack_exec_linux.go b/collector/conntrack_exec_linux.go new file mode 100644 index 0000000..1bce87a --- /dev/null +++ b/collector/conntrack_exec_linux.go @@ -0,0 +1,195 @@ +// Copyright 2015 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// +build !noconntrack + +package collector + +import ( + "encoding/xml" + "os/exec" + "strconv" + "time" + + "github.com/go-kit/kit/log" + "github.com/prometheus/client_golang/prometheus" +) + +type ConntrackStats struct { + CollectTime int64 + Flows []FlowRecord +} + +type FlowRecord struct { + L3Proto string + L4Proto string + SourceAddr string + SourcePort int + DestAddr string + DestPort int + PacketsIn int64 + PacketsOut int64 + BytesIn int64 + BytesOut int64 +} + +func (m *ConntrackStats) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { + m.CollectTime = time.Now().Unix() + + rd := struct { + Flows []struct { + Metas []struct { + Direction string `xml:"direction,attr"` + Layer3Meta struct { + ProtoName string `xml:"protoname,attr"` + SourceAddr string `xml:"src"` + DestAddr string `xml:"dst"` + } `xml:"layer3"` + Layer4Meta struct { + ProtoName string `xml:"protoname,attr"` + SourcePort int `xml:"sport"` + DestPort int `xml:"dport"` + } `xml:"layer4"` + Packets int64 `xml:"counters>packets"` + Bytes int64 `xml:"counters>bytes"` + } `xml:"meta"` + } `xml:"flow"` + }{} + + err := d.DecodeElement(&rd, &start) + if err != nil { + return err + } + + for _, f := range rd.Flows { + or := FlowRecord{} + + for _, m := range f.Metas { + if m.Direction == "original" { + or.L3Proto = m.Layer3Meta.ProtoName + or.L4Proto = m.Layer4Meta.ProtoName + or.SourceAddr = m.Layer3Meta.SourceAddr + or.SourcePort = m.Layer4Meta.SourcePort + or.DestAddr = m.Layer3Meta.DestAddr + or.DestPort = m.Layer4Meta.DestPort + or.PacketsIn = m.Packets + or.BytesIn = m.Bytes + } else if m.Direction == "reply" { + or.PacketsOut = m.Packets + or.BytesOut = m.Bytes + } + } + + m.Flows = append(m.Flows, or) + } + + return nil +} + +func loadStats(proto string, s *ConntrackStats) error { + cmd := exec.Command("/usr/sbin/conntrack", "-L", "-f", proto, "-o", "xml") + out, err := cmd.StdoutPipe() + if err != nil { + return err + } + + if err := cmd.Start(); err != nil { + return err + } + + dec := xml.NewDecoder(out) + if err = dec.Decode(s); err != nil { + return err + } + + if err := cmd.Wait(); err != nil { + return err + } + + return nil +} + +func init() { + registerCollector("conntrack_exec", defaultDisabled, NewConntrackExecCollector) +} + +func NewConntrackExecCollector(logger log.Logger) (Collector, error) { + subsystem := "conntrack_exec" + labels := []string{"l3", "l4", "saddr", "sport", "daddr", "dport"} + + return &conntrackExecCollector{ + bytesIn: prometheus.NewDesc( + prometheus.BuildFQName(namespace, subsystem, "bytes_in"), + "Conntrack bytes in for flow", + labels, nil, + ), + bytesOut: prometheus.NewDesc( + prometheus.BuildFQName(namespace, subsystem, "bytes_out"), + "Conntrack bytes out for flow", + labels, nil, + ), + packetsIn: prometheus.NewDesc( + prometheus.BuildFQName(namespace, subsystem, "packets_in"), + "Conntrack packets in for flow", + labels, nil, + ), + packetsOut: prometheus.NewDesc( + prometheus.BuildFQName(namespace, subsystem, "packets_out"), + "Conntrack packets out for flow", + labels, nil, + ), + }, nil +} + +type conntrackExecCollector struct { + bytesIn, bytesOut *prometheus.Desc + packetsIn, packetsOut *prometheus.Desc + logger log.Logger +} + +func (c *conntrackExecCollector) Update(ch chan<- prometheus.Metric) error { + stats := &ConntrackStats{} + + if err := loadStats("ipv4", stats); err != nil { + return err + } + + if err := loadStats("ipv6", stats); err != nil { + return err + } + + for _, f := range stats.Flows { + ch <- prometheus.MustNewConstMetric( + c.bytesIn, prometheus.CounterValue, + float64(f.BytesIn), + f.L3Proto, f.L4Proto, f.SourceAddr, strconv.Itoa(f.SourcePort), f.DestAddr, strconv.Itoa(f.DestPort), + ) + ch <- prometheus.MustNewConstMetric( + c.bytesOut, prometheus.CounterValue, + float64(f.BytesOut), + f.L3Proto, f.L4Proto, f.SourceAddr, strconv.Itoa(f.SourcePort), f.DestAddr, strconv.Itoa(f.DestPort), + ) + ch <- prometheus.MustNewConstMetric( + c.packetsIn, prometheus.CounterValue, + float64(f.PacketsIn), + f.L3Proto, f.L4Proto, f.SourceAddr, strconv.Itoa(f.SourcePort), f.DestAddr, strconv.Itoa(f.DestPort), + ) + ch <- prometheus.MustNewConstMetric( + c.packetsOut, prometheus.CounterValue, + float64(f.PacketsOut), + f.L3Proto, f.L4Proto, f.SourceAddr, strconv.Itoa(f.SourcePort), f.DestAddr, strconv.Itoa(f.DestPort), + ) + } + + return nil +} -- cgit v1.2.3