Skip to content

Commit 2cb8101

Browse files
msunegpotter2
andauthored
Netflow: NFv9/10 flow record type fix (#4813)
* layers/netflow: NFv9/10 flow record type fix This commit fixes the Python types of a number of fields defined in Netflowv9/10 for the following pre-defined fields (mostly the ones in RFC3954 https://datatracker.ietf.org/doc/html/rfc3954). It appears that in absence of an assigned field type, it defaults to BytesField instead of IntField. Fields: * IN_BYTES, IN_PKTS, OUT_BYTES, OUT_PKTS: IntField (4 bytes). Please note code doesn't seem to support the larget 8byte optional length. * MUL_DST_PKTS, MUL_DST_BYTES: same as above * INPUT_SNMP, OUTPUT_SNMP: ShortField (2 bytes), iface index. Note: code doesn't seem to support optional larger values (undefined max. length, but 8 bytes might be sufficient). Note there are _many_ other field types defined that are not part of the spec / unknown without field type and length. Fixes #4810 Signed-off-by: Marc Sune <[email protected]> * layers/netflow: delete print() _GenNetflowRecordV9 Remove unnecessary `print()` in _GenNetflowRecordV9(). Fixes #4810 Signed-off-by: Marc Sune <[email protected]> * Update to support isint parameter * Fix tests with the changes * Fix PEP8 issues * Unrelated debug fix --------- Signed-off-by: Marc Sune <[email protected]> Co-authored-by: gpotter2 <[email protected]>
1 parent e3273c4 commit 2cb8101

File tree

3 files changed

+124
-45
lines changed

3 files changed

+124
-45
lines changed

scapy/layers/netflow.py

Lines changed: 61 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
2929
>>> sniff(session=NetflowSession, prn=[...])
3030
31+
.. note:: You will find more examples over
32+
https://scapy.readthedocs.io/en/latest/layers/netflow.html
3133
"""
3234

3335
import dataclasses
@@ -48,10 +50,11 @@
4850
Field,
4951
FieldLenField,
5052
FlagsField,
51-
IPField,
5253
IntField,
54+
IPField,
5355
LongField,
5456
MACField,
57+
NBytesField,
5558
PacketListField,
5659
SecondsIntField,
5760
ShortEnumField,
@@ -203,6 +206,7 @@ class _N910F:
203206
length: int = 0
204207
field: Field = None
205208
kwargs: Dict[str, Any] = dataclasses.field(default_factory=dict)
209+
isint: bool = False
206210

207211

208212
# NetflowV9 Ready-made fields
@@ -260,8 +264,10 @@ def __init__(self, name, default, *args, **kargs):
260264

261265
NTOP_BASE = 57472
262266
NetflowV910TemplateFields = {
263-
1: _N910F("IN_BYTES", length=4),
264-
2: _N910F("IN_PKTS", length=4),
267+
1: _N910F("IN_BYTES", length=4,
268+
isint=True),
269+
2: _N910F("IN_PKTS", length=4,
270+
isint=True),
265271
3: _N910F("FLOWS", length=4),
266272
4: _N910F("PROTOCOL", length=1,
267273
field=ByteEnumField, kwargs={"enum": IP_PROTOS}),
@@ -275,14 +281,16 @@ def __init__(self, name, default, *args, **kargs):
275281
field=IPField),
276282
9: _N910F("SRC_MASK", length=1,
277283
field=ByteField),
278-
10: _N910F("INPUT_SNMP"),
284+
10: _N910F("INPUT_SNMP",
285+
isint=True),
279286
11: _N910F("L4_DST_PORT", length=2,
280287
field=ShortField),
281288
12: _N910F("IPV4_DST_ADDR", length=4,
282289
field=IPField),
283290
13: _N910F("DST_MASK", length=1,
284291
field=ByteField),
285-
14: _N910F("OUTPUT_SNMP"),
292+
14: _N910F("OUTPUT_SNMP",
293+
isint=True),
286294
15: _N910F("IPV4_NEXT_HOP", length=4,
287295
field=IPField),
288296
16: _N910F("SRC_AS", length=2,
@@ -291,16 +299,20 @@ def __init__(self, name, default, *args, **kargs):
291299
field=ShortOrInt),
292300
18: _N910F("BGP_IPV4_NEXT_HOP", length=4,
293301
field=IPField),
294-
19: _N910F("MUL_DST_PKTS", length=4),
295-
20: _N910F("MUL_DST_BYTES", length=4),
302+
19: _N910F("MUL_DST_PKTS", length=4,
303+
isint=True),
304+
20: _N910F("MUL_DST_BYTES", length=4,
305+
isint=True),
296306
21: _N910F("LAST_SWITCHED", length=4,
297307
field=SecondsIntField,
298308
kwargs={"use_msec": True}),
299309
22: _N910F("FIRST_SWITCHED", length=4,
300310
field=SecondsIntField,
301311
kwargs={"use_msec": True}),
302-
23: _N910F("OUT_BYTES", length=4),
303-
24: _N910F("OUT_PKTS", length=4),
312+
23: _N910F("OUT_BYTES", length=4,
313+
isint=True),
314+
24: _N910F("OUT_PKTS", length=4,
315+
isint=True),
304316
25: _N910F("IP_LENGTH_MINIMUM"),
305317
26: _N910F("IP_LENGTH_MAXIMUM"),
306318
27: _N910F("IPV6_SRC_ADDR", length=16,
@@ -329,9 +341,12 @@ def __init__(self, name, default, *args, **kargs):
329341
field=ByteField),
330342
39: _N910F("ENGINE_ID", length=1,
331343
field=ByteField),
332-
40: _N910F("TOTAL_BYTES_EXP", length=4),
333-
41: _N910F("TOTAL_PKTS_EXP", length=4),
334-
42: _N910F("TOTAL_FLOWS_EXP", length=4),
344+
40: _N910F("TOTAL_BYTES_EXP", length=4,
345+
isint=True),
346+
41: _N910F("TOTAL_PKTS_EXP", length=4,
347+
isint=True),
348+
42: _N910F("TOTAL_FLOWS_EXP", length=4,
349+
isint=True),
335350
43: _N910F("IPV4_ROUTER_SC"),
336351
44: _N910F("IP_SRC_PREFIX"),
337352
45: _N910F("IP_DST_PREFIX"),
@@ -376,16 +391,26 @@ def __init__(self, name, default, *args, **kargs):
376391
63: _N910F("BGP_IPV6_NEXT_HOP", length=16,
377392
field=IP6Field),
378393
64: _N910F("IPV6_OPTION_HEADERS", length=4),
379-
70: _N910F("MPLS_LABEL_1", length=3),
380-
71: _N910F("MPLS_LABEL_2", length=3),
381-
72: _N910F("MPLS_LABEL_3", length=3),
382-
73: _N910F("MPLS_LABEL_4", length=3),
383-
74: _N910F("MPLS_LABEL_5", length=3),
384-
75: _N910F("MPLS_LABEL_6", length=3),
385-
76: _N910F("MPLS_LABEL_7", length=3),
386-
77: _N910F("MPLS_LABEL_8", length=3),
387-
78: _N910F("MPLS_LABEL_9", length=3),
388-
79: _N910F("MPLS_LABEL_10", length=3),
394+
70: _N910F("MPLS_LABEL_1", length=3,
395+
field=ThreeBytesField),
396+
71: _N910F("MPLS_LABEL_2", length=3,
397+
field=ThreeBytesField),
398+
72: _N910F("MPLS_LABEL_3", length=3,
399+
field=ThreeBytesField),
400+
73: _N910F("MPLS_LABEL_4", length=3,
401+
field=ThreeBytesField),
402+
74: _N910F("MPLS_LABEL_5", length=3,
403+
field=ThreeBytesField),
404+
75: _N910F("MPLS_LABEL_6", length=3,
405+
field=ThreeBytesField),
406+
76: _N910F("MPLS_LABEL_7", length=3,
407+
field=ThreeBytesField),
408+
77: _N910F("MPLS_LABEL_8", length=3,
409+
field=ThreeBytesField),
410+
78: _N910F("MPLS_LABEL_9", length=3,
411+
field=ThreeBytesField),
412+
79: _N910F("MPLS_LABEL_10", length=3,
413+
field=ThreeBytesField),
389414
80: _N910F("DESTINATION_MAC"),
390415
81: _N910F("SOURCE_MAC"),
391416
82: _N910F("IF_NAME"),
@@ -1329,28 +1354,40 @@ def i2repr(self, pkt, v):
13291354

13301355

13311356
def _GenNetflowRecordV9(cls, lengths_list):
1332-
"""Internal function used to generate the Records from
1357+
"""
1358+
Internal function used to generate the Records from
13331359
their template.
13341360
"""
13351361
_fields_desc = []
13361362
for j, k in lengths_list:
1363+
# For each field, if it's known in our template list,
1364+
# try to make a nice field for it. Otherwise use an integer
1365+
# or a string default.
13371366
_f_type = None
13381367
_f_kwargs = {}
1368+
_f_isint = False
13391369
if k in NetflowV910TemplateFields:
13401370
_f = NetflowV910TemplateFields[k]
13411371
_f_type = _f.field
13421372
_f_kwargs = _f.kwargs
1373+
_f_isint = _f.isint
13431374

13441375
if _f_type:
13451376
if issubclass(_f_type, _AdjustableNetflowField):
13461377
_f_kwargs["length"] = j
1347-
print(k, _f_kwargs)
13481378
_fields_desc.append(
13491379
_f_type(
13501380
NetflowV910TemplateFieldTypes.get(k, "unknown_data"),
13511381
0, **_f_kwargs
13521382
)
13531383
)
1384+
elif _f_isint:
1385+
_fields_desc.append(
1386+
NBytesField(
1387+
NetflowV910TemplateFieldTypes.get(k, "unknown_data"),
1388+
0, sz=j
1389+
)
1390+
)
13541391
else:
13551392
_fields_desc.append(
13561393
_CustomStrFixedLenField(

test/regression.uts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -952,6 +952,7 @@ assert sane(corrupt_bits("ABCDE", n=3)) in ["AF.EE", "QB.TE"]
952952

953953
if not WINDOWS:
954954
result = whois("193.0.6.139")
955+
print(result)
955956
assert b"inetnum" in result and b"Amsterdam" in result
956957

957958
= Test manuf DB methods

test/scapy/layers/netflow.uts

Lines changed: 62 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ assert nfv9_options_fl[NetflowOptionsFlowsetV9].options[0].optionFieldType == 36
6464
nfv9_options_ds = a[4]
6565
assert NetflowDataflowsetV9 in nfv9_options_ds
6666
assert isinstance(nfv9_options_ds.records[0], NetflowOptionsRecordScopeV9)
67-
assert nfv9_options_ds.records[0].IN_BYTES == b'\x01\x00\x00\x00'
67+
assert nfv9_options_ds.records[0].IN_BYTES == 0x01000000
6868
assert nfv9_options_ds.records[1].SAMPLING_INTERVAL == 12
6969
assert nfv9_options_ds.records[1].SAMPLING_ALGORITHM == 0x2
7070

@@ -114,8 +114,8 @@ dataFS = NetflowDataflowsetV9(
114114
templateID=256,
115115
records=[ # Some random data.
116116
recordClass(
117-
IN_BYTES=b"\x12",
118-
IN_PKTS=b"\0\0\0\0",
117+
IN_BYTES=0x12,
118+
IN_PKTS=0,
119119
PROTOCOL=6,
120120
IPV4_SRC_ADDR="192.168.0.10",
121121
IPV4_DST_ADDR="192.168.0.11"
@@ -144,7 +144,7 @@ dataflowset = NetflowDataflowsetV9(records=[NetflowRecordV9(fieldValue=b'\x14\x0
144144

145145
pkt = netflowv9_defragment(list(header/flowset/dataflowset))[0]
146146
assert pkt.records[0].IPV4_NEXT_HOP == "10.100.103.1"
147-
assert pkt.records[0].OUTPUT_SNMP == b'\x00\x00\x02\xfb'
147+
assert pkt.records[0].OUTPUT_SNMP == 0x000002fb
148148

149149
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'
150150

@@ -210,36 +210,36 @@ dataFlowset_1 = NetflowDataflowsetV9(
210210
templateID=256,
211211
records=[
212212
Record256(
213-
IN_BYTES=b"\x12",
214-
IN_PKTS=b"\0\0\0\0",
213+
IN_BYTES=0x12,
214+
IN_PKTS=0,
215215
PROTOCOL=1,
216216
IPV4_SRC_ADDR="192.168.0.10",
217217
IPV4_DST_ADDR="192.168.0.11"
218218
),
219219
Record256(
220-
IN_BYTES=b"\x0c",
221-
IN_PKTS=b"\1\1\1\1",
220+
IN_BYTES=0x0c,
221+
IN_PKTS=0x01010101,
222222
PROTOCOL=2,
223223
IPV4_SRC_ADDR="172.0.0.10",
224224
IPV4_DST_ADDR="172.0.0.11"
225225
),
226226
Record256(
227-
IN_BYTES=b"\x0c",
228-
IN_PKTS=b"\1\1\1\1",
227+
IN_BYTES=0x0c,
228+
IN_PKTS=0x01010101,
229229
PROTOCOL=3,
230230
IPV4_SRC_ADDR="172.0.0.10",
231231
IPV4_DST_ADDR="172.0.0.11"
232232
),
233233
Record256(
234-
IN_BYTES=b"\x0c",
235-
IN_PKTS=b"\1\1\1\1",
234+
IN_BYTES=0x0c,
235+
IN_PKTS=0x01010101,
236236
PROTOCOL=4,
237237
IPV4_SRC_ADDR="172.0.0.10",
238238
IPV4_DST_ADDR="172.0.0.11"
239239
),
240240
Record256(
241-
IN_BYTES=b"\x0c",
242-
IN_PKTS=b"\1\1\1\1",
241+
IN_BYTES=0x0c,
242+
IN_PKTS=0x01010101,
243243
PROTOCOL=5,
244244
IPV4_SRC_ADDR="172.0.0.10",
245245
IPV4_DST_ADDR="172.0.0.11"
@@ -251,15 +251,15 @@ dataFlowset_2 = NetflowDataflowsetV9(
251251
templateID=257,
252252
records=[
253253
Record257(
254-
IN_BYTES=b"\x12",
255-
IN_PKTS=b"\0\0\0\0",
254+
IN_BYTES=0x12,
255+
IN_PKTS=0,
256256
PROTOCOL=1,
257257
IPV6_SRC_ADDR="2001:db8:3333:4444:5555:6666:7777:8888",
258258
IPV6_DST_ADDR="2001:db8::"
259259
),
260260
Record257(
261-
IN_BYTES=b"\x0c",
262-
IN_PKTS=b"\1\1\1\1",
261+
IN_BYTES=0x0c,
262+
IN_PKTS=0x01010101,
263263
PROTOCOL=2,
264264
IPV6_SRC_ADDR="2001:db8:3333:4444:CCCC:DDDD:EEEE:FFFF",
265265
IPV6_DST_ADDR="2001:db8::"
@@ -400,7 +400,7 @@ pkt = netflowv9_defragment(Ether(s))[0]
400400

401401
for i in range(1,3):
402402
assert pkt.getlayer(NetflowDataflowsetV9, i).templateID == 257
403-
assert pkt.getlayer(NetflowDataflowsetV9, i).records[0].IN_PKTS == b'\x00\x00\x00\x00'
403+
assert pkt.getlayer(NetflowDataflowsetV9, i).records[0].IN_PKTS == 0
404404
assert pkt.getlayer(NetflowDataflowsetV9, i).records[0].PROTOCOL == 6
405405
assert pkt.getlayer(NetflowDataflowsetV9, i).records[0].IPV4_SRC_ADDR == "192.168.0.10"
406406
assert pkt.getlayer(NetflowDataflowsetV9, i).records[0].IPV4_DST_ADDR == "192.168.0.11"
@@ -430,8 +430,8 @@ dataFS = NetflowDataflowsetV9(
430430
templateID=256,
431431
records=[ # Some random data.
432432
recordClass(
433-
IN_BYTES=b"\x12",
434-
IN_PKTS=b"\0\0\0\0",
433+
IN_BYTES=0x12,
434+
IN_PKTS=0,
435435
PROTOCOL=6,
436436
IPV4_SRC_ADDR="192.168.0.10",
437437
IPV4_DST_ADDR="192.168.0.11"
@@ -442,6 +442,47 @@ dataFS = NetflowDataflowsetV9(
442442
pkt = netflow_header / flowset / dataFS
443443
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'
444444

445+
= Netflow9 - Build and dissect more int fields
446+
447+
template_flowset = NetflowFlowsetV9(
448+
flowSetID=0,
449+
templates=[
450+
NetflowTemplateV9(
451+
templateID=256,
452+
fieldCount=5,
453+
template_fields=[
454+
NetflowTemplateFieldV9(fieldType="IN_BYTES", fieldLength=4),
455+
NetflowTemplateFieldV9(fieldType="IN_PKTS", fieldLength=4),
456+
NetflowTemplateFieldV9(fieldType="PROTOCOL", fieldLength=1),
457+
NetflowTemplateFieldV9(fieldType="IPV4_SRC_ADDR", fieldLength=4),
458+
NetflowTemplateFieldV9(fieldType="IPV4_DST_ADDR", fieldLength=4),
459+
]
460+
)
461+
]
462+
)
463+
recordClass = GetNetflowRecordV9(template_flowset)
464+
dataflowset = NetflowDataflowsetV9(
465+
templateID=256,
466+
records=[
467+
recordClass(
468+
IN_BYTES=0x1234,
469+
IN_PKTS=0xABC,
470+
PROTOCOL=6,
471+
IPV4_SRC_ADDR="192.168.0.10",
472+
IPV4_DST_ADDR="192.168.0.11"
473+
),
474+
],
475+
)
476+
477+
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'
478+
479+
# Re-dissect after build
480+
dataflowset = NetflowDataflowsetV9(bytes(dataflowset))
481+
rec = recordClass(dataflowset.records[0].fieldValue)
482+
assert rec.IN_BYTES == 0x1234
483+
assert rec.IN_PKTS == 0xABC
484+
assert rec.IPV4_SRC_ADDR == "192.168.0.10"
485+
445486
= NetflowSession - dissect packet NetflowV9 packets on-the-flow
446487

447488
import os

0 commit comments

Comments
 (0)