Monday 6 February 2017

Netflow version 5 collector in python

I am going to provide brief code snippet of netflow version 5 collector in python.
In netflow version 5, the template of data is fixed. It simply means that you know what will come at what place. Netflow version 5 packet structure is as following:-

Packet header Structure

BytesContent labelBrief Description
0-1     versionNetFlow export format version number
2-3     countNumber of flows exported in this packet (1-30)
4-7     sys_uptimeCurrent time in milliseconds since the export device booted
8-11     unix_secsCurrent count of seconds since 0000 UTC 1970
12-15     unix_nsecsResidual nanoseconds since 0000 UTC 1970
16-19     flow_sequenceSequence counter of total flows seen
20     engine_typeType of flow-switching engine
21     engine_idSlot number of the flow-switching engine
22-23      sampling_interval       First two bits hold the sampling mode; remaining 14 bits hold value of sampling interval

Packet record format


Bytes    Content Label     Brief Description
0-3srcaddrSource IP address
4-7dstaddrDestination IP address
8-11nexthopIP address of next hop router
12-13inputSNMP index of input interface
14-15outputSNMP index of output interface
16-19dPktsPackets in the flow
20-23dOctetsTotal number of Layer 3 bytes in the packets of the flow
24-27firstSysUptime at start of flow
28-31lastSysUptime at the time the last packet of the flow was received
32-33srcportTCP/UDP source port number or equivalent
34-35dstportTCP/UDP destination port number or equivalent
36pad1Unused (zero) bytes
37tcp_flagsCumulative OR of TCP flags
38protIP protocol type (for example, TCP = 6; UDP = 17)
39tosIP type of service (ToS)
40-41src_asAutonomous system number of the source, either origin or peer
42-43dst_asAutonomous system number of the destination, either origin or peer
44src_maskSource address prefix mask bits
45dst_maskDestination address prefix mask bits
46-47pad2Unused (zero) bytes


A network device like router, switch etc can send netflow data to a netflow collector. Netflow is proprietary protocol of Cisco but many other devices also support it. So if you have a cisco router and you want to write your own netflow collector, you can do in following ways :-

1. Using standard python library
 
import socket, struct
import threading
from socket import inet_ntoa

SIZE_OF_HEADER = 24
SIZE_OF_RECORD = 48

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind(('0.0.0.0', 9999))

def processPacket(buf,addr):
        (version, count) = struct.unpack('!HH',buf[0:4])
        if version != 5:
                print "Not NetFlow v5!"
                #continue

        # It's pretty unlikely you'll ever see more then 1000 records in a 1500 byte  UDP packet
        if count <= 0 or count >= 1000:
                print "Invalid count %s" % count
                #continue

        uptime = socket.ntohl(struct.unpack('I',buf[4:8])[0])
        epochseconds = socket.ntohl(struct.unpack('I',buf[8:12])[0])

        for i in range(0, count):
                try:
                        base = SIZE_OF_HEADER+(i*SIZE_OF_RECORD)

                        data = struct.unpack('!IIIIHH',buf[base+16:base+36])

                        nfdata = {}
                        nfdata['saddr'] = inet_ntoa(buf[base+0:base+4])
                        nfdata['daddr'] = inet_ntoa(buf[base+4:base+8])
                        nfdata['pcount'] = data[0]
                        nfdata['bcount'] = data[1]
                        nfdata['stime'] = data[2]
                        nfdata['etime'] = data[3]
                        nfdata['sport'] = data[4]
                        nfdata['dport'] = data[5]
                        nfdata['protocol'] = ord(buf[base+38])
                except:
                        continue

        # Do something with the netflow record..
        print "%s:%s -> %s:%s" % (nfdata['saddr'],nfdata['sport'],nfdata['daddr'],nfdata['dport'])


while True:
      buf, addr = sock.recvfrom(1500)
      t = threading.Thread(target=processPacket, args=(buf,addr))
      t.start()
               


2. Using twisted library

     
from twisted.internet.protocol import DatagramProtocol
from twisted.internet import reactor
import socket
from socket import inet_ntoa
import struct
SIZE_OF_HEADER = 24
SIZE_OF_RECORD = 48


class CaptureNetflowPacket(DatagramProtocol):

    def datagramReceived(self, data, addr):
        #print("received %r from %s" % (data, addr))
        #self.transport.write(data, addr)
         (version, count) = struct.unpack('!HH',data[0:4])
         print "Version %s, count %s "%(version,count)
         if version != 5:
            print "Not NetFlow v5!"
         uptime = socket.ntohl(struct.unpack('I',data[4:8])[0])
         epochseconds = socket.ntohl(struct.unpack('I',data[8:12])[0])
         print "Uptime %s , epochseconds %s "% (uptime, epochseconds)
         for i in range(0, count):
                #try:
                base = SIZE_OF_HEADER+(i*SIZE_OF_RECORD)

                fdata = struct.unpack('!IIIIHH',data[base+16:base+36])

                nfdata = {}
                nfdata['saddr'] = inet_ntoa(data[base+0:base+4])
                nfdata['daddr'] = inet_ntoa(data[base+4:base+8])
                nfdata['pcount'] = fdata[0]
                nfdata['bcount'] = fdata[1]
                nfdata['stime'] = fdata[2]
                nfdata['etime'] = fdata[3]
                nfdata['sport'] = fdata[4]
                nfdata['dport'] = fdata[5]
                nfdata['protocol'] = ord(data[base+38])
                #except:
                        #continue
                        #print "Exception occured"

                # Do something with the netflow record..
                print "%s:%s -> %s:%s" % (nfdata['saddr'],nfdata['sport'],nfdata['daddr'],nfdata['dport'])
                print "%s : %s : %s " %(nfdata['pcount'],nfdata['bcount'],nfdata['protocol'])




reactor.listenUDP(9999, CaptureNetflowPacket())
reactor.run()
    

Please note that collector is listening on port 9999 over UDP socket.
This code simply prints packet data on console but one can extend to save the packet data in database.



   

No comments:

Post a Comment