Go言語でNetFlowを受信する方法

# はじめに

Go言語でNetFlowのパケットを受信して処理するためのパッケージ

https://github.com/tehmaze/netflow

の使い方を説明します。


# サンプルプログラム

GiHUBのリポジトリにcmd/nf-dumpというサンプルプログラムがあります。
そのままです。


```go

/*
Command nf-dump decodes NetFlow packets from UDP datagrams.

Usage:

	nf-dump [flags]

Flags:

	-addr string 	Listen address (default ":2055")
*/
package main

import (
	"bytes"
	"flag"
	"log"
	"net"

	"github.com/tehmaze/netflow"
	"github.com/tehmaze/netflow/ipfix"
	"github.com/tehmaze/netflow/netflow1"
	"github.com/tehmaze/netflow/netflow5"
	"github.com/tehmaze/netflow/netflow6"
	"github.com/tehmaze/netflow/netflow7"
	"github.com/tehmaze/netflow/netflow9"
	"github.com/tehmaze/netflow/session"
)

// Safe default
var readSize = 2 << 16

func main() {
	listen := flag.String("addr", ":2055", "Listen address")
	flag.Parse()

	var addr *net.UDPAddr
	var err error
	if addr, err = net.ResolveUDPAddr("udp", *listen); err != nil {
		log.Fatal(err)
	}

	var server *net.UDPConn
	if server, err = net.ListenUDP("udp", addr); err != nil {
		log.Fatal(err)
	}

	if err = server.SetReadBuffer(readSize); err != nil {
		log.Fatal(err)
	}

	decoders := make(map[string]*netflow.Decoder)
	for {
		buf := make([]byte, 8192)
		var remote *net.UDPAddr
		var octets int
		if octets, remote, err = server.ReadFromUDP(buf); err != nil {
			log.Printf("error reading from %s: %v\n", remote, err)
			continue
		}

		log.Printf("received %d bytes from %s\n", octets, remote)

		d, found := decoders[remote.String()]
		if !found {
			s := session.New()
			d = netflow.NewDecoder(s)
			decoders[remote.String()] = d
		}

		m, err := d.Read(bytes.NewBuffer(buf[:octets]))
		if err != nil {
			log.Println("decoder error:", err)
			continue
		}

		switch p := m.(type) {
		case *netflow1.Packet:
			netflow1.Dump(p)

		case *netflow5.Packet:
			netflow5.Dump(p)

		case *netflow6.Packet:
			netflow6.Dump(p)

		case *netflow7.Packet:
			netflow7.Dump(p)

		case *netflow9.Packet:
			netflow9.Dump(p)

		case *ipfix.Message:
			ipfix.Dump(p)
		}
	}
}

```

このプログラムのポイントは、

### UDPのポートをオープンして受信状態にする

```go
	listen := flag.String("addr", ":2055", "Listen address")
	flag.Parse()

	var addr *net.UDPAddr
	var err error
	if addr, err = net.ResolveUDPAddr("udp", *listen); err != nil {
		log.Fatal(err)
	}

	var server *net.UDPConn
	if server, err = net.ListenUDP("udp", addr); err != nil {
		log.Fatal(err)
	}

```

2055番ポートです。

### 送信元毎にデコーダーを作成する

```go
		d, found := decoders[remote.String()]
		if !found {
			s := session.New()
			d = netflow.NewDecoder(s)
			decoders[remote.String()] = d
		}

```

### NetFlowパケットをデコードする

```go
		m, err := d.Read(bytes.NewBuffer(buf[:octets]))
		if err != nil {
			log.Println("decoder error:", err)
			continue
		}


```


### NetFlowのバージョン毎にダンプする

```go
		switch p := m.(type) {
		case *netflow1.Packet:
			netflow1.Dump(p)

		case *netflow5.Packet:
			netflow5.Dump(p)

		case *netflow6.Packet:
			netflow6.Dump(p)

		case *netflow7.Packet:
			netflow7.Dump(p)

		case *netflow9.Packet:
			netflow9.Dump(p)

		case *ipfix.Message:
			ipfix.Dump(p)
		}

```

よく使われるのは、v5,v9,ipfix(v10)です。

# 実行結果

<details><summary>実行結果</summary>

```terminal
$go run main.go
2024/09/27 17:48:57 received 172 bytes from 10.30.1.12:3285
NetFlow version 9 packet
2024/09/27 17:49:05 received 472 bytes from 192.168.1.32:36573
IPFIX message
2024/09/27 17:49:05 received 472 bytes from 192.168.1.32:36573
IPFIX message
2024/09/27 17:49:05 received 472 bytes from 192.168.1.32:36573
IPFIX message
2024/09/27 17:49:05 received 96 bytes from 192.168.1.32:36573
IPFIX message
2024/09/27 17:49:05 received 352 bytes from 192.168.1.32:36573
IPFIX message
2024/09/27 17:49:07 received 172 bytes from 10.30.1.12:3285
NetFlow version 9 packet
2024/09/27 17:49:07 received 60 bytes from 10.30.1.12:3285
NetFlow version 9 packet
2024/09/27 17:49:22 received 172 bytes from 10.30.1.12:3285
NetFlow version 9 packet
2024/09/27 17:49:22 received 60 bytes from 10.30.1.12:3285
NetFlow version 9 packet
2024/09/27 17:49:27 received 400 bytes from 10.30.1.12:3285
NetFlow version 9 packet
2024/09/27 17:49:37 received 60 bytes from 10.30.1.12:3285
NetFlow version 9 packet
2024/09/27 17:49:43 received 1236 bytes from 10.30.1.12:3285
NetFlow version 9 packet
2024/09/27 17:49:48 received 400 bytes from 10.30.1.12:3285
NetFlow version 9 packet
  data set template 258, length: 76
    1 records:
      record 0:
        octetDeltaCount: 3534
        postOctetDeltaCount: 3534
        packetDeltaCount: 6
        postPacketDeltaCount: 6
        flowStartSysUpTime: 1857598410
        flowEndSysUpTime: 1857658690
        ingressInterface: 1416
        egressInterface: 514
        icmpTypeCodeIPv4: 0
        protocolIdentifier: 0
        ipClassOfService: 1
        postIpClassOfService: 17
        applicationId: [0 0 0 20 0 0 48 68 0]
        66: [0 0 0 0]
        65: [0 0]
        forwardingStatus: 0
        flowEndReason: 12
        sourceIPv6Address: 540:20a:1e01:a0a:1e01:900::
  data set template 258, length: 76
    1 records:
      record 0:
        octetDeltaCount: 222
        postOctetDeltaCount: 222
        packetDeltaCount: 3
        postPacketDeltaCount: 3
        flowStartSysUpTime: 1857477520
        flowEndSysUpTime: 1857477520
        ingressInterface: 161
        egressInterface: 61598
        icmpTypeCodeIPv4: 0
        protocolIdentifier: 0
        ipClassOfService: 1
        postIpClassOfService: 17
        applicationId: [0 0 0 20 0 0 48 68 0]
        66: [0 0 0 0]
        65: [0 0]
        forwardingStatus: 0
        flowEndReason: 14
        sourceIPv6Address: 4c3:a:1e01:ac0:a801:fa00::
  data set template 258, length: 76
    1 records:
      record 0:
        octetDeltaCount: 219
        postOctetDeltaCount: 219
        packetDeltaCount: 3
        postPacketDeltaCount: 3
        flowStartSysUpTime: 1857477520
        flowEndSysUpTime: 1857477520
        ingressInterface: 61598
        egressInterface: 161
        icmpTypeCodeIPv4: 1
        protocolIdentifier: 0
        ipClassOfService: 0
        postIpClassOfService: 17
        applicationId: [0 0 0 20 0 0 48 68 0]
        66: [0 0 0 0]
        65: [0 0]
        forwardingStatus: 0
        flowEndReason: 14
        sourceIPv6Address: 4c3:c0:a801:fa0a:1e01:a00::
  data set template 258, length: 76
    1 records:
      record 0:
        octetDeltaCount: 296
        postOctetDeltaCount: 296
        packetDeltaCount: 1
        postPacketDeltaCount: 1
        flowStartSysUpTime: 1857579750
        flowEndSysUpTime: 1857654850
        ingressInterface: 53
        egressInterface: 4004
        icmpTypeCodeIPv4: 1
        protocolIdentifier: 0
        ipClassOfService: 0
        postIpClassOfService: 17
        applicationId: [0 0 0 20 0 0 48 68 0]
        66: [0 0 0 0]
        65: [0 0]
        forwardingStatus: 0
        flowEndReason: 12
        sourceIPv6Address: 540:20a:1e01:fe0a:1e01:a00::
  data set template 258, length: 76
    1 records:
      record 0:
        octetDeltaCount: 63
        postOctetDeltaCount: 63
        packetDeltaCount: 1
        postPacketDeltaCount: 1
        flowStartSysUpTime: 1857579750
        flowEndSysUpTime: 1857654850
        ingressInterface: 4004
        egressInterface: 53
        icmpTypeCodeIPv4: 0
        protocolIdentifier: 0
        ipClassOfService: 1
        postIpClassOfService: 17
        applicationId: [0 0 0 20 0 0 48 68 0]
        66: [0 0 0 0]
        65: [0 0]
        forwardingStatus: 0
        flowEndReason: 12
        sourceIPv6Address: 540:20a:1e01:a0a:1e01:fe00::

```


</details>



# 余談

このパッケージもTWSNMP　FC/FKで使用しています。

https://note.com/twsnmp/n/na5c62f59833d