diff --git a/scapy/layers/netflow.py b/scapy/layers/netflow.py index ef0fe8e9646..04d8e9fbac2 100644 --- a/scapy/layers/netflow.py +++ b/scapy/layers/netflow.py @@ -28,6 +28,8 @@ >>> sniff(session=NetflowSession, prn=[...]) +.. note:: You will find more examples over + https://scapy.readthedocs.io/en/latest/layers/netflow.html """ import dataclasses @@ -48,10 +50,11 @@ Field, FieldLenField, FlagsField, - IPField, IntField, + IPField, LongField, MACField, + NBytesField, PacketListField, SecondsIntField, ShortEnumField, @@ -203,6 +206,7 @@ class _N910F: length: int = 0 field: Field = None kwargs: Dict[str, Any] = dataclasses.field(default_factory=dict) + isint: bool = False # NetflowV9 Ready-made fields @@ -260,8 +264,10 @@ def __init__(self, name, default, *args, **kargs): NTOP_BASE = 57472 NetflowV910TemplateFields = { - 1: _N910F("IN_BYTES", length=4), - 2: _N910F("IN_PKTS", length=4), + 1: _N910F("IN_BYTES", length=4, + isint=True), + 2: _N910F("IN_PKTS", length=4, + isint=True), 3: _N910F("FLOWS", length=4), 4: _N910F("PROTOCOL", length=1, field=ByteEnumField, kwargs={"enum": IP_PROTOS}), @@ -275,14 +281,16 @@ def __init__(self, name, default, *args, **kargs): field=IPField), 9: _N910F("SRC_MASK", length=1, field=ByteField), - 10: _N910F("INPUT_SNMP"), + 10: _N910F("INPUT_SNMP", + isint=True), 11: _N910F("L4_DST_PORT", length=2, field=ShortField), 12: _N910F("IPV4_DST_ADDR", length=4, field=IPField), 13: _N910F("DST_MASK", length=1, field=ByteField), - 14: _N910F("OUTPUT_SNMP"), + 14: _N910F("OUTPUT_SNMP", + isint=True), 15: _N910F("IPV4_NEXT_HOP", length=4, field=IPField), 16: _N910F("SRC_AS", length=2, @@ -291,16 +299,20 @@ def __init__(self, name, default, *args, **kargs): field=ShortOrInt), 18: _N910F("BGP_IPV4_NEXT_HOP", length=4, field=IPField), - 19: _N910F("MUL_DST_PKTS", length=4), - 20: _N910F("MUL_DST_BYTES", length=4), + 19: _N910F("MUL_DST_PKTS", length=4, + isint=True), + 20: _N910F("MUL_DST_BYTES", length=4, + isint=True), 21: _N910F("LAST_SWITCHED", length=4, field=SecondsIntField, kwargs={"use_msec": True}), 22: _N910F("FIRST_SWITCHED", length=4, field=SecondsIntField, kwargs={"use_msec": True}), - 23: _N910F("OUT_BYTES", length=4), - 24: _N910F("OUT_PKTS", length=4), + 23: _N910F("OUT_BYTES", length=4, + isint=True), + 24: _N910F("OUT_PKTS", length=4, + isint=True), 25: _N910F("IP_LENGTH_MINIMUM"), 26: _N910F("IP_LENGTH_MAXIMUM"), 27: _N910F("IPV6_SRC_ADDR", length=16, @@ -329,9 +341,12 @@ def __init__(self, name, default, *args, **kargs): field=ByteField), 39: _N910F("ENGINE_ID", length=1, field=ByteField), - 40: _N910F("TOTAL_BYTES_EXP", length=4), - 41: _N910F("TOTAL_PKTS_EXP", length=4), - 42: _N910F("TOTAL_FLOWS_EXP", length=4), + 40: _N910F("TOTAL_BYTES_EXP", length=4, + isint=True), + 41: _N910F("TOTAL_PKTS_EXP", length=4, + isint=True), + 42: _N910F("TOTAL_FLOWS_EXP", length=4, + isint=True), 43: _N910F("IPV4_ROUTER_SC"), 44: _N910F("IP_SRC_PREFIX"), 45: _N910F("IP_DST_PREFIX"), @@ -376,16 +391,26 @@ def __init__(self, name, default, *args, **kargs): 63: _N910F("BGP_IPV6_NEXT_HOP", length=16, field=IP6Field), 64: _N910F("IPV6_OPTION_HEADERS", length=4), - 70: _N910F("MPLS_LABEL_1", length=3), - 71: _N910F("MPLS_LABEL_2", length=3), - 72: _N910F("MPLS_LABEL_3", length=3), - 73: _N910F("MPLS_LABEL_4", length=3), - 74: _N910F("MPLS_LABEL_5", length=3), - 75: _N910F("MPLS_LABEL_6", length=3), - 76: _N910F("MPLS_LABEL_7", length=3), - 77: _N910F("MPLS_LABEL_8", length=3), - 78: _N910F("MPLS_LABEL_9", length=3), - 79: _N910F("MPLS_LABEL_10", length=3), + 70: _N910F("MPLS_LABEL_1", length=3, + field=ThreeBytesField), + 71: _N910F("MPLS_LABEL_2", length=3, + field=ThreeBytesField), + 72: _N910F("MPLS_LABEL_3", length=3, + field=ThreeBytesField), + 73: _N910F("MPLS_LABEL_4", length=3, + field=ThreeBytesField), + 74: _N910F("MPLS_LABEL_5", length=3, + field=ThreeBytesField), + 75: _N910F("MPLS_LABEL_6", length=3, + field=ThreeBytesField), + 76: _N910F("MPLS_LABEL_7", length=3, + field=ThreeBytesField), + 77: _N910F("MPLS_LABEL_8", length=3, + field=ThreeBytesField), + 78: _N910F("MPLS_LABEL_9", length=3, + field=ThreeBytesField), + 79: _N910F("MPLS_LABEL_10", length=3, + field=ThreeBytesField), 80: _N910F("DESTINATION_MAC"), 81: _N910F("SOURCE_MAC"), 82: _N910F("IF_NAME"), @@ -1329,28 +1354,40 @@ def i2repr(self, pkt, v): def _GenNetflowRecordV9(cls, lengths_list): - """Internal function used to generate the Records from + """ + Internal function used to generate the Records from their template. """ _fields_desc = [] for j, k in lengths_list: + # For each field, if it's known in our template list, + # try to make a nice field for it. Otherwise use an integer + # or a string default. _f_type = None _f_kwargs = {} + _f_isint = False if k in NetflowV910TemplateFields: _f = NetflowV910TemplateFields[k] _f_type = _f.field _f_kwargs = _f.kwargs + _f_isint = _f.isint if _f_type: if issubclass(_f_type, _AdjustableNetflowField): _f_kwargs["length"] = j - print(k, _f_kwargs) _fields_desc.append( _f_type( NetflowV910TemplateFieldTypes.get(k, "unknown_data"), 0, **_f_kwargs ) ) + elif _f_isint: + _fields_desc.append( + NBytesField( + NetflowV910TemplateFieldTypes.get(k, "unknown_data"), + 0, sz=j + ) + ) else: _fields_desc.append( _CustomStrFixedLenField( diff --git a/test/regression.uts b/test/regression.uts index 4beb41787ac..af7738a41e0 100644 --- a/test/regression.uts +++ b/test/regression.uts @@ -952,6 +952,7 @@ assert sane(corrupt_bits("ABCDE", n=3)) in ["AF.EE", "QB.TE"] if not WINDOWS: result = whois("193.0.6.139") + print(result) assert b"inetnum" in result and b"Amsterdam" in result = Test manuf DB methods diff --git a/test/scapy/layers/netflow.uts b/test/scapy/layers/netflow.uts index 5ad94a68feb..2f78de78759 100644 --- a/test/scapy/layers/netflow.uts +++ b/test/scapy/layers/netflow.uts @@ -64,7 +64,7 @@ assert nfv9_options_fl[NetflowOptionsFlowsetV9].options[0].optionFieldType == 36 nfv9_options_ds = a[4] assert NetflowDataflowsetV9 in nfv9_options_ds assert isinstance(nfv9_options_ds.records[0], NetflowOptionsRecordScopeV9) -assert nfv9_options_ds.records[0].IN_BYTES == b'\x01\x00\x00\x00' +assert nfv9_options_ds.records[0].IN_BYTES == 0x01000000 assert nfv9_options_ds.records[1].SAMPLING_INTERVAL == 12 assert nfv9_options_ds.records[1].SAMPLING_ALGORITHM == 0x2 @@ -114,8 +114,8 @@ dataFS = NetflowDataflowsetV9( templateID=256, records=[ # Some random data. recordClass( - IN_BYTES=b"\x12", - IN_PKTS=b"\0\0\0\0", + IN_BYTES=0x12, + IN_PKTS=0, PROTOCOL=6, IPV4_SRC_ADDR="192.168.0.10", IPV4_DST_ADDR="192.168.0.11" @@ -144,7 +144,7 @@ dataflowset = NetflowDataflowsetV9(records=[NetflowRecordV9(fieldValue=b'\x14\x0 pkt = netflowv9_defragment(list(header/flowset/dataflowset))[0] assert pkt.records[0].IPV4_NEXT_HOP == "10.100.103.1" -assert pkt.records[0].OUTPUT_SNMP == b'\x00\x00\x02\xfb' +assert pkt.records[0].OUTPUT_SNMP == 0x000002fb assert raw(pkt) == b'\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x08\x00E\x00\x00\xcc\x00\x01\x00\x00@\x11|\x1e\x7f\x00\x00\x01\x7f\x00\x00\x01\x08\x07\x08\x07\x00\xb8\x86\xe7\x00\t\x00\x02\x00\x00\x00\x00\\C\x7f5\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\\\x01\xa8\x00\x15\x00\x08\x00\x04\x00\x0c\x00\x04\x00\x05\x00\x01\x00\x04\x00\x01\x00\x07\x00\x02\x00\x0b\x00\x02\x00 \x00\x02\x00\n\x00\x04\x00\x10\x00\x04\x00\x11\x00\x04\x00\x12\x00\x04\x00\x0e\x00\x04\x00\x01\x00\x04\x00\x02\x00\x04\x00\x16\x00\x04\x00\x15\x00\x04\x00\x0f\x00\x04\x00\t\x00\x01\x00\r\x00\x01\x00\x06\x00\x01\x00<\x00\x01\x01\xa8\x00@\x14\x00\x00\xfd\x1e\x00\x00\xfd\x00\xfd\x00\x00\x00\x00\x00\x00\x00\x00\x03 \x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x02\xfb\x00\x15a|\x00\x00\x07\x0f$\x95x\xed$\x99\x91<\ndg\x01 \x00\x04' @@ -210,36 +210,36 @@ dataFlowset_1 = NetflowDataflowsetV9( templateID=256, records=[ Record256( - IN_BYTES=b"\x12", - IN_PKTS=b"\0\0\0\0", + IN_BYTES=0x12, + IN_PKTS=0, PROTOCOL=1, IPV4_SRC_ADDR="192.168.0.10", IPV4_DST_ADDR="192.168.0.11" ), Record256( - IN_BYTES=b"\x0c", - IN_PKTS=b"\1\1\1\1", + IN_BYTES=0x0c, + IN_PKTS=0x01010101, PROTOCOL=2, IPV4_SRC_ADDR="172.0.0.10", IPV4_DST_ADDR="172.0.0.11" ), Record256( - IN_BYTES=b"\x0c", - IN_PKTS=b"\1\1\1\1", + IN_BYTES=0x0c, + IN_PKTS=0x01010101, PROTOCOL=3, IPV4_SRC_ADDR="172.0.0.10", IPV4_DST_ADDR="172.0.0.11" ), Record256( - IN_BYTES=b"\x0c", - IN_PKTS=b"\1\1\1\1", + IN_BYTES=0x0c, + IN_PKTS=0x01010101, PROTOCOL=4, IPV4_SRC_ADDR="172.0.0.10", IPV4_DST_ADDR="172.0.0.11" ), Record256( - IN_BYTES=b"\x0c", - IN_PKTS=b"\1\1\1\1", + IN_BYTES=0x0c, + IN_PKTS=0x01010101, PROTOCOL=5, IPV4_SRC_ADDR="172.0.0.10", IPV4_DST_ADDR="172.0.0.11" @@ -251,15 +251,15 @@ dataFlowset_2 = NetflowDataflowsetV9( templateID=257, records=[ Record257( - IN_BYTES=b"\x12", - IN_PKTS=b"\0\0\0\0", + IN_BYTES=0x12, + IN_PKTS=0, PROTOCOL=1, IPV6_SRC_ADDR="2001:db8:3333:4444:5555:6666:7777:8888", IPV6_DST_ADDR="2001:db8::" ), Record257( - IN_BYTES=b"\x0c", - IN_PKTS=b"\1\1\1\1", + IN_BYTES=0x0c, + IN_PKTS=0x01010101, PROTOCOL=2, IPV6_SRC_ADDR="2001:db8:3333:4444:CCCC:DDDD:EEEE:FFFF", IPV6_DST_ADDR="2001:db8::" @@ -400,7 +400,7 @@ pkt = netflowv9_defragment(Ether(s))[0] for i in range(1,3): assert pkt.getlayer(NetflowDataflowsetV9, i).templateID == 257 - assert pkt.getlayer(NetflowDataflowsetV9, i).records[0].IN_PKTS == b'\x00\x00\x00\x00' + assert pkt.getlayer(NetflowDataflowsetV9, i).records[0].IN_PKTS == 0 assert pkt.getlayer(NetflowDataflowsetV9, i).records[0].PROTOCOL == 6 assert pkt.getlayer(NetflowDataflowsetV9, i).records[0].IPV4_SRC_ADDR == "192.168.0.10" assert pkt.getlayer(NetflowDataflowsetV9, i).records[0].IPV4_DST_ADDR == "192.168.0.11" @@ -430,8 +430,8 @@ dataFS = NetflowDataflowsetV9( templateID=256, records=[ # Some random data. recordClass( - IN_BYTES=b"\x12", - IN_PKTS=b"\0\0\0\0", + IN_BYTES=0x12, + IN_PKTS=0, PROTOCOL=6, IPV4_SRC_ADDR="192.168.0.10", IPV4_DST_ADDR="192.168.0.11" @@ -442,6 +442,47 @@ dataFS = NetflowDataflowsetV9( pkt = netflow_header / flowset / dataFS assert raw(pkt) == b'\x00\n\x00>\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1c\x01\x00\x00\x05\x00\x01\x00\x01\x00\x02\x00\x04\x00\x04\x00\x01\x00\x08\x00\x04\x00\x0c\x00\x04\x01\x00\x00\x14\x12\x00\x00\x00\x00\x06\xc0\xa8\x00\n\xc0\xa8\x00\x0b\x00\x00' += Netflow9 - Build and dissect more int fields + +template_flowset = NetflowFlowsetV9( + flowSetID=0, + templates=[ + NetflowTemplateV9( + templateID=256, + fieldCount=5, + template_fields=[ + NetflowTemplateFieldV9(fieldType="IN_BYTES", fieldLength=4), + NetflowTemplateFieldV9(fieldType="IN_PKTS", fieldLength=4), + NetflowTemplateFieldV9(fieldType="PROTOCOL", fieldLength=1), + NetflowTemplateFieldV9(fieldType="IPV4_SRC_ADDR", fieldLength=4), + NetflowTemplateFieldV9(fieldType="IPV4_DST_ADDR", fieldLength=4), + ] + ) + ] +) +recordClass = GetNetflowRecordV9(template_flowset) +dataflowset = NetflowDataflowsetV9( + templateID=256, + records=[ + recordClass( + IN_BYTES=0x1234, + IN_PKTS=0xABC, + PROTOCOL=6, + IPV4_SRC_ADDR="192.168.0.10", + IPV4_DST_ADDR="192.168.0.11" + ), + ], +) + +assert bytes(dataflowset) == b'\x01\x00\x00\x18\x00\x00\x124\x00\x00\n\xbc\x06\xc0\xa8\x00\n\xc0\xa8\x00\x0b\x00\x00\x00' + +# Re-dissect after build +dataflowset = NetflowDataflowsetV9(bytes(dataflowset)) +rec = recordClass(dataflowset.records[0].fieldValue) +assert rec.IN_BYTES == 0x1234 +assert rec.IN_PKTS == 0xABC +assert rec.IPV4_SRC_ADDR == "192.168.0.10" + = NetflowSession - dissect packet NetflowV9 packets on-the-flow import os