Netflowv9 flowrecord serializes IN_PKTS=0,IN_BYTES=0
Brief description
Hello,
While working on some testing code for pmacct using scapy I found what appears to be a bug serializing v9 netflow flow records.
The TL;DR IN_BYTES and IN_PKTS (OUT_ too) are always serialized to a value of 0. The interesting bit is that show() prints the right values set, but when show2() is invoked the resulting 8 first bytes are 0.
Scapy version
2.6.1
Python version
3.13.3
Operating system
Debian trixie (testing)
Additional environment information
No response
How to reproduce
Here is a simple repro:
>>> 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"
...: ),
...: ],
...: )
...:
4 {'enum': </etc/protocols - 56 elements>}
8 {}
12 {}
>>> dataflowset.show()
###[ Netflow DataFlowSet V9/10 ]###
templateID= 256
length = None
\records \
|###[ Netflow DataFlowset Record V9/10 ]###
| IN_BYTES = 4660
| IN_PKTS = 2748
| PROTOCOL = tcp
| IPV4_SRC_ADDR= 192.168.0.10
| IPV4_DST_ADDR= 192.168.0.11
>>> dataflowset.show2()
###[ Netflow DataFlowSet V9/10 ]###
templateID= 256
length = 24
\records \
|###[ Netflow DataFlowset Record V9/10 ]###
| fieldValue= b'\x00\x00\x00\x00\x00\x00\x00\x00\x06\xc0\xa8\x00\n\xc0\xa8\x00\x0b\x00\x00\x00'
>>>
Worth noting, there is some weird output:
4 {'enum': </etc/protocols - 56 elements>}
8 {}
12 {}
Actual result
No response
Expected result
No response
Related resources
No response
It seems:
85 recordClass(
86 #IN_BYTES=0x1234,
87 #IN_PKTS=0xABC,
88 IN_BYTES=int(0x1234).to_bytes(4, 'big'),
89 IN_PKTS=int(0xABC).to_bytes(4, 'big'),
works around the issue:
>>> 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=int(0x1234).to_bytes(4, 'big'),
...: IN_PKTS=int(0xABC).to_bytes(4, 'big'),
...: PROTOCOL=6,
...: IPV4_SRC_ADDR="192.168.0.10",
...: IPV4_DST_ADDR="192.168.0.11"
...: ),
...: ],
...: )
...:
4 {'enum': </etc/protocols - 56 elements>}
8 {}
12 {}
>>> dataflowset.show()
###[ Netflow DataFlowSet V9/10 ]###
templateID= 256
length = None
\records \
|###[ Netflow DataFlowset Record V9/10 ]###
| IN_BYTES = b'\x00\x00\x124'
| IN_PKTS = b'\x00\x00\n\xbc'
| PROTOCOL = tcp
| IPV4_SRC_ADDR= 192.168.0.10
| IPV4_DST_ADDR= 192.168.0.11
>>> dataflowset.show2()
###[ Netflow DataFlowSet V9/10 ]###
templateID= 256
length = 24
\records \
|###[ Netflow DataFlowset Record V9/10 ]###
| fieldValue= b'\x00\x00\x124\x00\x00\n\xbc\x06\xc0\xa8\x00\n\xc0\xa8\x00\x0b\x00\x00\x00'
I don't think this is the intended behaviour, as I would expect to accept int in HBO.
Let me know your thoughts.
Hi & thanks for the report.
It doesn't really surprise me that for something named "BYTES" you have to pass bytes. I would've expected b"\x01\x02\x03\x04".
However two side problems come from your example that I think need fixing:
- show2() should work nicer
- the weird debug logs should be removed
It might also be a good idea to add a warning when the types passed are incorrect, to help debug this kind of issues.
It doesn't really surprise me that for something named "BYTES" you have to pass bytes. I would've expected b"\x01\x02\x03\x04".
I think there is a confusion. IN_BYTES (and OUT_BYTES) is a predefined field type in Netflowv9 (see RFC3954#section-8).
In other words, it doesn't indicate the input value type, but it's actually a counter of the number of octets associated to the flow record. Note IN_PKTS and OUT_PKTS (which doesn't have the suffix _BYTES) suffers from the same serialization issue.
Deleting the original comment, I need further investigation. The above still holds true.
I think there is a confusion. IN_BYTES (and OUT_BYTES) is a predefined field type in Netflowv9 (see RFC3954#section-8).
You're absolutely right, for those types, it very likely should be an int.
You're absolutely right, for those types, it very likely should be an int.
Thx!
The L4_SRC_PORT, L4_DST_PORT issues was a red herring. I might look into other field types - and the actual Scapy implementation - sometime (adding it to the backlog).