pythonpython-3.xscapynetflow

How do I update specific Netflow v10/ IPFIX flow data fields in Python3 Scapy?


Scenario
I have a PCAP containing Netflow v10/ IPFIX data templates and data flows and would like to replay the PCAP. Before the packet is sent, I would like to update one of the flow data fields (i.e. startTime with the current time).


Current code
My current code is able to read the PCAP, grab the last layer (Netflowv10), create a socket and send the packet over the interface. The socket takes care of the Ethernet -> IP -> UDP layers, and the 'getlayer' function in Scapy preserves the Netflow v10 template & data flow layers as is from the donor PCAP.


Question
The issue I would like help with is, how do I update the startTime flow data field?.


Code
I have only provided the Python3 code relating to the issue in scope for this question, relating to Scapy

packets = sniff(session=NetflowSession, offline=open(pcap_file, "rb"))

for packet in packets:

    if packet.haslayer(NetflowDataflowsetV9):
        # This return the NetflowDateflowset Records key/value pairs
        flowset = netflowv9_defragment(packet[NetflowDataflowsetV9].records)

Variable 'flowset' returns a list and isn't a callable object. Looking through Scapy's docs, there isn't a great deal of assistance provided.

Any pointers or suggestions would greatly be appreciated. Thank you in advance :)


Solution

  • First of, the netflowv9_defragment should not be used here: it has the same effect as session=NetflowSession and should be given a list of packets.

    The main help page for flow set is https://scapy.readthedocs.io/en/latest/layers/netflow.html but there are also some test cases in https://github.com/secdev/scapy/blob/master/test/scapy/layers/netflow.uts that give info on how the module works.

    You could do something like

    for packet in packets:
        if packet.haslayer(NetflowDataflowsetV9):
            for rec in packet.records:
                if 'startTime' in rec:
                    rec.startTime = 12345
    

    The build example is in my opinion a great way of understanding how netflow packets are build:

    header = Ether()/IP()/UDP()
    netflow_header = NetflowHeader()/NetflowHeaderV9()
    
    # Let's first build the template. Those need an ID > 255.
    # The (full) list of possible fieldType is available in the
    # NetflowV910TemplateFieldTypes list. You can also use the int value.
    flowset = NetflowFlowsetV9(
        templates=[NetflowTemplateV9(
            template_fields=[
                NetflowTemplateFieldV9(fieldType="IN_BYTES", fieldLength=1),
                NetflowTemplateFieldV9(fieldType="IN_PKTS", fieldLength=4),
                NetflowTemplateFieldV9(fieldType="PROTOCOL"),
                NetflowTemplateFieldV9(fieldType="IPV4_SRC_ADDR"),
                NetflowTemplateFieldV9(fieldType="IPV4_DST_ADDR"),
            ],
            templateID=256,
            fieldCount=5)
        ],
        flowSetID=0
    )
    # Let's generate the record class. This will be a Packet class
    # In case you provided several templates in ghe flowset, you will need
    # to pass the template ID as second parameter
    recordClass = GetNetflowRecordV9(flowset)
    # Now lets build the data records
    dataFS = NetflowDataflowsetV9(
        templateID=256,
        records=[ # Some random data.
            recordClass(
                IN_BYTES=b"\x12",
                IN_PKTS=b"\0\0\0\0",
                PROTOCOL=6,
                IPV4_SRC_ADDR="192.168.0.10",
                IPV4_DST_ADDR="192.168.0.11"
            ),
            recordClass(
                IN_BYTES=b"\x0c",
                IN_PKTS=b"\1\1\1\1",
                PROTOCOL=3,
                IPV4_SRC_ADDR="172.0.0.10",
                IPV4_DST_ADDR="172.0.0.11"
            )
        ],
    )
    pkt = header / netflow_header / flowset / dataFS