1#!/usr/bin/env python3 2# 3# SPDX-License-Identifier: BSD-2-Clause 4# 5# Copyright © 2023. Rubicon Communications, LLC (Netgate). All Rights Reserved. 6# 7# Redistribution and use in source and binary forms, with or without 8# modification, are permitted provided that the following conditions 9# are met: 10# 1. Redistributions of source code must retain the above copyright 11# notice, this list of conditions and the following disclaimer. 12# 2. Redistributions in binary form must reproduce the above copyright 13# notice, this list of conditions and the following disclaimer in the 14# documentation and/or other materials provided with the distribution. 15# 16# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26# SUCH DAMAGE. 27# 28 29import argparse 30import logging 31logging.getLogger("scapy").setLevel(logging.CRITICAL) 32import scapy.all as sp 33 34def parse_ipfix(p): 35 if not sp.NetflowDataflowsetV9 in p: 36 # A template? 37 return 38 39 c = p 40 while sp.NetflowDataflowsetV9 in c: 41 datafl = c[sp.NetflowDataflowsetV9] 42 if datafl.templateID == 258: 43 # NAT 44 for r in datafl.records: 45 print("v=10,NAT=%d,proto=%s,src=%s-%s,dst=%s-%s" % 46 (int.from_bytes(r.natEvent, "big"), r.PROTOCOL, 47 r.IPV4_SRC_ADDR, r.postNATSourceIPv4Address, 48 r.IPV4_DST_ADDR, r.postNATDestinationIPv4Address)) 49 elif datafl.templateID == 257: 50 # IPv6 51 for r in datafl.records: 52 print("v=10,IPv6,proto=%s,src=%s,dst=%s" % 53 (r.PROTOCOL, r.IPV6_SRC_ADDR, r.IPV6_DST_ADDR)) 54 elif datafl.templateID == 256: 55 # IPv4 56 for r in datafl.records: 57 print("v=10,proto=%s,src=%s,dst=%s" % 58 (r.PROTOCOL, r.IPV4_SRC_ADDR, r.IPV4_DST_ADDR)) 59 60 c = datafl.payload 61 62def receive(recvif, recvport): 63 pkts = sp.sniff(iface=recvif, timeout=65) 64 65 if len(pkts) == 0: 66 print("No data") 67 return 68 69 pkts = sp.ipfix_defragment(pkts) 70 71 for pkt in pkts: 72 udp = pkt.getlayer(sp.UDP) 73 if not udp: 74 continue 75 76 if udp.dport != recvport: 77 continue 78 79 if not sp.NetflowHeader in pkt: 80 continue 81 82 hdr = pkt.getlayer(sp.NetflowHeader) 83 if hdr.version == 5: 84 v5hdr = pkt.getlayer(sp.NetflowHeaderV5) 85 out="" 86 for i in range(1, v5hdr.count + 1): 87 r = pkt.getlayer(sp.NetflowRecordV5, nb=i) 88 print("v=%d,proto=%d,src=%s,dst=%s,srcport=%d,dstport=%d" % 89 (hdr.version, r.prot, r.src, r.dst, r.srcport, r.dstport)) 90 elif hdr.version == 10: 91 parse_ipfix(pkt) 92 93def main(): 94 parser = argparse.ArgumentParser("pft_read_ipfix.py", 95 description="IPFix test tool") 96 parser.add_argument('--recvif', nargs=1, 97 required=True, 98 help='The interface on which to look for packets') 99 parser.add_argument('--port', nargs=1, 100 required=True, 101 help='The port number') 102 103 args = parser.parse_args() 104 105 receive(args.recvif[0], int(args.port[0])) 106 107if __name__ == '__main__': 108 main() 109 110