5cdb514ffc
This commit replaces multiple occurences of new features which were not yet implemented with Python 3.5.3, which is the reference backwards compatibility version for this package. The version is based on the current Python version in Debian Stretch (oldstable). According to pkgs.org, all other distros use 3.6+, so 3.5.3 is the lower boundary. Changes: * Add maxsize argument to functools.lru_cache decorator * Replace f"" with .format() * Replace variable type hints "var: type = val" with "# type:" comments * Replace pstats.SortKey enum with strings in performance tests Additionally, various styling fixes were applied. The version compatibility was tested with tox, pyenv and Python 3.5.3, but there is no tox.ini yet which automates this test. Bump patch version number to 0.10.3 Update author's email address. Resolves #27
87 lines
2.4 KiB
Python
87 lines
2.4 KiB
Python
#!/usr/bin/env python3
|
|
|
|
"""
|
|
Netflow V1 collector and parser implementation in Python 3.
|
|
This file belongs to https://github.com/bitkeks/python-netflow-v9-softflowd.
|
|
Created purely for fun. Not battled tested nor will it be.
|
|
|
|
Reference https://www.cisco.com/c/en/us/td/docs/net_mgmt/netflow_collection_engine/3-6/user/guide/format.html
|
|
This script is specifically implemented in combination with softflowd. See https://github.com/djmdjm/softflowd
|
|
"""
|
|
|
|
import struct
|
|
|
|
__all__ = ["V1DataFlow", "V1ExportPacket", "V1Header"]
|
|
|
|
|
|
class V1DataFlow:
|
|
"""Holds one v1 DataRecord
|
|
"""
|
|
length = 48
|
|
|
|
def __init__(self, data):
|
|
pack = struct.unpack('!IIIHHIIIIHHxxBBBxxxxxxx', data)
|
|
fields = [
|
|
'IPV4_SRC_ADDR',
|
|
'IPV4_DST_ADDR',
|
|
'NEXT_HOP',
|
|
'INPUT',
|
|
'OUTPUT',
|
|
'IN_PACKETS',
|
|
'IN_OCTETS',
|
|
'FIRST_SWITCHED',
|
|
'LAST_SWITCHED',
|
|
'SRC_PORT',
|
|
'DST_PORT',
|
|
# Word at 36-37 is used for padding
|
|
'PROTO',
|
|
'TOS',
|
|
'TCP_FLAGS',
|
|
# Data at 41-47 is padding/reserved
|
|
]
|
|
|
|
self.data = {}
|
|
for idx, field in enumerate(fields):
|
|
self.data[field] = pack[idx]
|
|
self.__dict__.update(self.data) # Make data dict entries accessible as object attributes
|
|
|
|
def __repr__(self):
|
|
return "<DataRecord with data {}>".format(self.data)
|
|
|
|
|
|
class V1Header:
|
|
"""The header of the V1ExportPacket
|
|
"""
|
|
length = 16
|
|
|
|
def __init__(self, data):
|
|
pack = struct.unpack('!HHIII', data[:self.length])
|
|
self.version = pack[0]
|
|
self.count = pack[1]
|
|
self.uptime = pack[2]
|
|
self.timestamp = pack[3]
|
|
self.timestamp_nano = pack[4]
|
|
|
|
def to_dict(self):
|
|
return self.__dict__
|
|
|
|
|
|
class V1ExportPacket:
|
|
"""The flow record holds the header and data flowsets.
|
|
"""
|
|
|
|
def __init__(self, data):
|
|
self.flows = []
|
|
self.header = V1Header(data)
|
|
|
|
offset = self.header.length
|
|
for flow_count in range(0, self.header.count):
|
|
end = offset + V1DataFlow.length
|
|
flow = V1DataFlow(data[offset:end])
|
|
self.flows.append(flow)
|
|
offset += flow.length
|
|
|
|
def __repr__(self):
|
|
return "<ExportPacket v{} with {} records>".format(
|
|
self.header.version, self.header.count)
|