scapy icon indicating copy to clipboard operation
scapy copied to clipboard

Netflowv9 flowrecord serializes IN_PKTS=0,IN_BYTES=0

Open msune opened this issue 5 months ago • 6 comments

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

msune avatar Aug 01 '25 22:08 msune

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'
Image

I don't think this is the intended behaviour, as I would expect to accept int in HBO.

Let me know your thoughts.

msune avatar Aug 02 '25 13:08 msune

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.

gpotter2 avatar Aug 02 '25 13:08 gpotter2

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.

msune avatar Aug 02 '25 13:08 msune

Deleting the original comment, I need further investigation. The above still holds true.

msune avatar Aug 02 '25 21:08 msune

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.

gpotter2 avatar Aug 02 '25 22:08 gpotter2

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).

msune avatar Aug 02 '25 22:08 msune