Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 61 additions & 24 deletions scapy/layers/netflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -48,10 +50,11 @@
Field,
FieldLenField,
FlagsField,
IPField,
IntField,
IPField,
LongField,
MACField,
NBytesField,
PacketListField,
SecondsIntField,
ShortEnumField,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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}),
Expand All @@ -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,
Expand All @@ -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,
Expand Down Expand Up @@ -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"),
Expand Down Expand Up @@ -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"),
Expand Down Expand Up @@ -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(
Expand Down
1 change: 1 addition & 0 deletions test/regression.uts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
83 changes: 62 additions & 21 deletions test/scapy/layers/netflow.uts
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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'

Expand Down Expand Up @@ -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"
Expand All @@ -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::"
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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"
Expand All @@ -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
Expand Down
Loading