├── README.md ├── go.mod ├── go.sum └── main.go /README.md: -------------------------------------------------------------------------------- 1 | # Prometheus netflow Exporter 2 | This is an exporter that exposes information gathered from netflow for use by the Prometheus monitoring system. 3 | 4 | now support netflow v5 and v9. 5 | 6 | ## Installation 7 | go get -d 8 | 9 | go build 10 | 11 | ## Usage 12 | ``` 13 | ./netflow_exporter 14 | ``` 15 | Visit http://localhost:9191/metrics 16 | 17 | ## note 18 | I think that netflow is not metric and analystic message. 19 | 20 | Label conbination is too much to collect. 21 | 22 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/paihu/netflow_exporter 2 | 3 | go 1.14 4 | 5 | require ( 6 | github.com/prometheus/client_golang v1.11.1 7 | github.com/prometheus/client_model v0.2.0 8 | github.com/prometheus/common v0.26.0 9 | github.com/tehmaze/netflow v0.0.0-20170921210347-852af103667f 10 | ) 11 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 3 | github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM= 4 | github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 5 | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 6 | github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 7 | github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d h1:UQZhZ2O0vMHr2cI+DC1Mbh0TJxzA3RcLoMsFw+aXw7E= 8 | github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= 9 | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= 10 | github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= 11 | github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= 12 | github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= 13 | github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= 14 | github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 15 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 16 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 17 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 18 | github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 19 | github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 20 | github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= 21 | github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= 22 | github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= 23 | github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= 24 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 25 | github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 26 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 27 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 28 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 29 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 30 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 31 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 32 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 33 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 34 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 35 | github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= 36 | github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 37 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 38 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 39 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 40 | github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 41 | github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= 42 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 43 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 44 | github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= 45 | github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 46 | github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 47 | github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 48 | github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= 49 | github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= 50 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 51 | github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= 52 | github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 53 | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= 54 | github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= 55 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 56 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 57 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 58 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 59 | github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= 60 | github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= 61 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 62 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 63 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 64 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 65 | github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 66 | github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 67 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 68 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 69 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 70 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 71 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 72 | github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= 73 | github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= 74 | github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= 75 | github.com/prometheus/client_golang v1.11.1 h1:+4eQaD7vAZ6DsfsxB15hbE0odUjGI5ARs9yskGu1v4s= 76 | github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= 77 | github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 78 | github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 79 | github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= 80 | github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 81 | github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= 82 | github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= 83 | github.com/prometheus/common v0.26.0 h1:iMAkS2TDoNWnKM+Kopnx/8tnEStIfpYA0ur0xQzzhMQ= 84 | github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= 85 | github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 86 | github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= 87 | github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= 88 | github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4= 89 | github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= 90 | github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= 91 | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= 92 | github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I= 93 | github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= 94 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 95 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 96 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 97 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 98 | github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= 99 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 100 | github.com/tehmaze/netflow v0.0.0-20170921210347-852af103667f h1:5dYJ4SDGn0SYo/BSXCDTf7W+LeUDj7v+VwXX723Kymw= 101 | github.com/tehmaze/netflow v0.0.0-20170921210347-852af103667f/go.mod h1:QRP5wJOf7gGMGL2fCAfmh/5CMZQspRxT5DqghaPRrjM= 102 | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 103 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 104 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 105 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 106 | golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 107 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 108 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 109 | golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 110 | golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 111 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 112 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 113 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 114 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 115 | golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 116 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 117 | golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 118 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 119 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 120 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 121 | golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 122 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 123 | golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 124 | golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 125 | golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 126 | golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40 h1:JWgyZ1qgdTaF3N3oxC+MdTV7qvEEgHo3otj+HB5CM7Q= 127 | golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 128 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 129 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 130 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 131 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= 132 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 133 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 134 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 135 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 136 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 137 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 138 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 139 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 140 | google.golang.org/protobuf v1.26.0-rc.1 h1:7QnIQpGRHE5RnLKnESfDoxm2dTapTZua5a0kS0A+VXQ= 141 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 142 | gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= 143 | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 144 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 145 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= 146 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 147 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 148 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 149 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 150 | gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 151 | gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= 152 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 153 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "flag" 6 | "fmt" 7 | "net" 8 | "net/http" 9 | "os" 10 | "regexp" 11 | "sort" 12 | "strconv" 13 | "strings" 14 | "sync" 15 | "time" 16 | 17 | "github.com/prometheus/client_golang/prometheus" 18 | "github.com/prometheus/client_golang/prometheus/promhttp" 19 | dto "github.com/prometheus/client_model/go" 20 | "github.com/prometheus/common/log" 21 | "github.com/prometheus/common/version" 22 | "github.com/tehmaze/netflow" 23 | "github.com/tehmaze/netflow/netflow5" 24 | "github.com/tehmaze/netflow/netflow9" 25 | "github.com/tehmaze/netflow/session" 26 | ) 27 | 28 | var ( 29 | showVersion = flag.Bool("version", false, "Print version information.") 30 | netflowAddress = flag.String("netflow.listen-address", ":2055", "Network address on which to accept netflow binary network packets, e.g. \":2055\".") 31 | listenAddress = flag.String("web.listen-address", ":9191", "Address on which to expose metrics.") 32 | metricsPath = flag.String("web.telemetry-path", "/metrics", "Path under which to expose Prometheus metrics.") 33 | netflowCollects = flag.String("netflow.collect", "Count$", "Regexp match type is Collect metrics.") 34 | netflowExclude = flag.String("netflow.exclude", "Time", "Regexp match type is not use Label.") 35 | sampleExpiry = flag.Duration("netflow.sample-expiry", 5*time.Minute, "How long a sample is valid for.") 36 | lastProcessed = prometheus.NewGauge( 37 | prometheus.GaugeOpts{ 38 | Name: "netflow_last_processed_timestamp_seconds", 39 | Help: "Unix timestamp of the last processed netflow metric.", 40 | }, 41 | ) 42 | ) 43 | 44 | type netflowSample struct { 45 | Labels map[string]string 46 | Counts map[string]float64 47 | TimestampMs int64 48 | } 49 | type netflowCollector struct { 50 | ch chan *netflowSample 51 | samples map[string]*netflowSample 52 | mu *sync.Mutex 53 | } 54 | 55 | func newNetflowCollector() *netflowCollector { 56 | c := &netflowCollector{ 57 | ch: make(chan *netflowSample, 0), 58 | samples: map[string]*netflowSample{}, 59 | mu: &sync.Mutex{}, 60 | } 61 | go c.processSamples() 62 | return c 63 | } 64 | 65 | func (c *netflowCollector) processReader(udpSock *net.UDPConn) { 66 | defer udpSock.Close() 67 | decoders := make(map[string]*netflow.Decoder) 68 | for { 69 | buf := make([]byte, 65535) 70 | chars, srcAddress, err := udpSock.ReadFromUDP(buf) 71 | if err != nil { 72 | log.Errorf("Error reading UDP packet from %s: %s", srcAddress, err) 73 | continue 74 | } 75 | timestampMs := int64(float64(time.Now().UnixNano()) / 1e6) 76 | d, found := decoders[srcAddress.String()] 77 | if !found { 78 | s := session.New() 79 | d = netflow.NewDecoder(s) 80 | decoders[srcAddress.String()] = d 81 | } 82 | m, err := d.Read(bytes.NewBuffer(buf[:chars])) 83 | if err != nil { 84 | } 85 | switch p := m.(type) { 86 | case *netflow5.Packet: 87 | 88 | for _, record := range p.Records { 89 | labels := prometheus.Labels{} 90 | counts := make(map[string]float64) 91 | labels["sourceIPv4Address"] = record.SrcAddr.String() 92 | labels["destinationIPv4Address"] = record.DstAddr.String() 93 | labels["sourceTransportPort"] = strconv.FormatUint(uint64(record.SrcPort), 10) 94 | labels["destinationTransportPort"] = strconv.FormatUint(uint64(record.DstPort), 10) 95 | counts["packetDeltaCount"] = float64(record.Packets) 96 | counts["octetDeltaCount"] = float64(record.Bytes) 97 | labels["protocolIdentifier"] = strconv.FormatUint(uint64(record.Protocol), 10) 98 | labels["tcpControlBits"] = strconv.FormatUint(uint64(record.TCPFlags), 10) 99 | labels["bgpSourceAsNumber"] = strconv.FormatUint(uint64(record.SrcAS), 10) 100 | labels["bgpDestinationAsNumber"] = strconv.FormatUint(uint64(record.DstAS), 10) 101 | labels["sourceIPv4PrefixLength"] = strconv.FormatUint(uint64(record.SrcMask), 10) 102 | labels["destinationIPv4PrefixLength"] = strconv.FormatUint(uint64(record.DstMask), 10) 103 | if (len(counts) > 0) && (len(labels) > 0) { 104 | labels["From"] = srcAddress.IP.String() 105 | labels["NetflowVersion"] = "5" 106 | 107 | sample := &netflowSample{ 108 | Labels: labels, 109 | Counts: counts, 110 | TimestampMs: timestampMs, 111 | } 112 | lastProcessed.Set(float64(time.Now().UnixNano()) / 1e9) 113 | c.ch <- sample 114 | } 115 | } 116 | 117 | case *netflow9.Packet: 118 | for _, set := range p.DataFlowSets { 119 | for _, record := range set.Records { 120 | labels := prometheus.Labels{} 121 | counts := make(map[string]float64) 122 | for _, field := range record.Fields { 123 | if len(*netflowExclude) > 0 && regexp.MustCompile(*netflowExclude).MatchString(field.Translated.Name) { 124 | //log.Infoln(field,"is not using label") 125 | } else if regexp.MustCompile(*netflowCollects).MatchString(field.Translated.Name) { 126 | counts[field.Translated.Name] = float64(field.Translated.Value.(uint64)) 127 | //log.Infoln(field,"is using metric") 128 | } else { 129 | labels[field.Translated.Name] = fmt.Sprintf("%v", field.Translated.Value) 130 | } 131 | 132 | } 133 | if (len(counts) > 0) && (len(labels) > 0) { 134 | labels["From"] = srcAddress.IP.String() 135 | labels["TemplateID"] = fmt.Sprintf("%d",record.TemplateID) 136 | labels["NetflowVersion"] = "9" 137 | 138 | sample := &netflowSample{ 139 | Labels: labels, 140 | Counts: counts, 141 | TimestampMs: timestampMs, 142 | } 143 | lastProcessed.Set(float64(time.Now().UnixNano()) / 1e9) 144 | c.ch <- sample 145 | } 146 | } 147 | } 148 | default: 149 | log.Infoln("packet is not supported version") 150 | } 151 | 152 | } 153 | } 154 | 155 | func makeEntryName(l map[string]string) string { 156 | keys := []string{} 157 | for key, _ := range l { 158 | keys = append(keys, key) 159 | } 160 | sort.Strings(keys) 161 | var name string 162 | for _, key := range keys { 163 | name += key + "=" + l[key] 164 | } 165 | return name 166 | } 167 | func (c *netflowCollector) processSamples() { 168 | ticker := time.NewTicker(time.Minute).C 169 | for { 170 | select { 171 | case sample := <-c.ch: 172 | 173 | c.mu.Lock() 174 | 175 | _, ok := c.samples[makeEntryName(sample.Labels)] 176 | 177 | if !ok || (c.samples[makeEntryName(sample.Labels)].TimestampMs < sample.TimestampMs) { 178 | c.samples[makeEntryName(sample.Labels)] = sample 179 | } 180 | c.mu.Unlock() 181 | case <-ticker: 182 | ageLimit := int64(float64(time.Now().Add(-*sampleExpiry).UnixNano()) / 1e6) 183 | c.mu.Lock() 184 | for k, sample := range c.samples { 185 | if ageLimit >= sample.TimestampMs { 186 | delete(c.samples, k) 187 | } 188 | } 189 | c.mu.Unlock() 190 | } 191 | } 192 | } 193 | 194 | func (c *netflowCollector) Describe(ch chan<- *prometheus.Desc) { 195 | ch <- lastProcessed.Desc() 196 | } 197 | 198 | func (c *netflowCollector) Collect(ch chan<- prometheus.Metric) { 199 | ch <- lastProcessed 200 | c.mu.Lock() 201 | samples := make([]*netflowSample, 0, len(c.samples)) 202 | for _, sample := range c.samples { 203 | samples = append(samples, sample) 204 | } 205 | c.mu.Unlock() 206 | ageLimit := int64(float64(time.Now().Add(-*sampleExpiry).UnixNano()) / 1e6) 207 | for _, sample := range samples { 208 | if ageLimit >= sample.TimestampMs { 209 | continue 210 | } 211 | for key, value := range sample.Counts { 212 | desc :="" 213 | if sample.Labels["TemplateID"] != "" { 214 | desc = fmt.Sprintf("netflow_%s_TemplateID%s_%s", sample.Labels["From"], sample.Labels["TemplateID"], key) 215 | } else { 216 | desc = fmt.Sprintf("netflow_%s_%s", sample.Labels["From"], key) 217 | } 218 | desc = strings.Replace(desc,".","",-1) 219 | log.Infoln(desc) 220 | ch <- MustNewTimeConstMetric( 221 | prometheus.NewDesc(desc, 222 | fmt.Sprintf("netflow metric %s", key), 223 | []string{}, sample.Labels), 224 | prometheus.GaugeValue, value, sample.TimestampMs) 225 | } 226 | } 227 | } 228 | 229 | func NewTimeConstMetric(desc *prometheus.Desc, valueType prometheus.ValueType, 230 | value float64, timestampMs int64) (prometheus.Metric, error) { 231 | return &timeConstMetric{ 232 | timestampMs: timestampMs, 233 | metric: prometheus.MustNewConstMetric(desc, prometheus.GaugeValue, value, []string{}...), 234 | }, nil 235 | } 236 | func MustNewTimeConstMetric(desc *prometheus.Desc, valueType prometheus.ValueType, 237 | value float64, timestampMs int64) prometheus.Metric { 238 | m, err := NewTimeConstMetric(desc, valueType, value, timestampMs) 239 | if err != nil { 240 | panic(err) 241 | } 242 | 243 | return m 244 | } 245 | 246 | type timeConstMetric struct { 247 | timestampMs int64 248 | metric prometheus.Metric 249 | } 250 | 251 | func (m *timeConstMetric) Desc() *prometheus.Desc { 252 | return m.metric.Desc() 253 | } 254 | func (m *timeConstMetric) Write(out *dto.Metric) error { 255 | out.TimestampMs = &m.timestampMs 256 | return m.metric.Write(out) 257 | } 258 | 259 | func main() { 260 | flag.Parse() 261 | 262 | if *showVersion { 263 | fmt.Fprintln(os.Stdout, version.Print("netflow_exporter")) 264 | os.Exit(0) 265 | } 266 | 267 | log.Infoln("Starting netflow_exporter", version.Info()) 268 | log.Infoln("Build context", version.BuildContext()) 269 | 270 | http.Handle(*metricsPath, promhttp.Handler()) 271 | 272 | c := newNetflowCollector() 273 | prometheus.MustRegister(c) 274 | 275 | udpAddress, err := net.ResolveUDPAddr("udp", *netflowAddress) 276 | if err != nil { 277 | log.Fatalf("Error resolving UDP address: %s", err) 278 | } 279 | udpSock, err := net.ListenUDP("udp", udpAddress) 280 | if err != nil { 281 | log.Fatalf("Error lisening to UDP address: %s", err) 282 | } 283 | log.Infoln("include", *netflowCollects) 284 | if len(*netflowExclude) > 0 { 285 | log.Infoln("exclude", *netflowExclude) 286 | } 287 | go c.processReader(udpSock) 288 | 289 | http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 290 | w.Write([]byte(` 291 | netflow Exporter 292 | 293 |

netflow Exporter

294 |

Metrics

295 | 296 | `)) 297 | }) 298 | 299 | log.Infoln("Listening on", *listenAddress) 300 | log.Infoln("Listening UDP on", *netflowAddress) 301 | log.Fatal(http.ListenAndServe(*listenAddress, nil)) 302 | } 303 | --------------------------------------------------------------------------------