3 # Copyright (c) 2016 Cisco and/or its affiliates.
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at:
8 # http://www.apache.org/licenses/LICENSE-2.0
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
16 """Traffic script - IPFIX listener."""
20 from ipaddress import IPv4Address, IPv6Address, AddressValueError
21 from scapy.layers.inet import IP, TCP, UDP
22 from scapy.layers.inet6 import IPv6
23 from scapy.layers.l2 import Ether
25 from resources.libraries.python.PacketVerifier import RxQueue, TxQueue, auto_pad
26 from resources.libraries.python.TrafficScriptArg import TrafficScriptArg
27 from resources.libraries.python.telemetry.IPFIXUtil import IPFIXHandler
28 from resources.libraries.python.telemetry.IPFIXUtil import IPFIXData
32 """Check if IP address has the correct IPv4 address format.
34 :param ip: IP address.
36 :return: True in case of correct IPv4 address format,
37 otherwise return false.
41 IPv4Address(unicode(ip))
43 except (AttributeError, AddressValueError):
48 """Check if IP address has the correct IPv6 address format.
50 :param ip: IP address.
52 :return: True in case of correct IPv6 address format,
53 otherwise return false.
57 IPv6Address(unicode(ip))
59 except (AttributeError, AddressValueError):
64 """Send packets to VPP, then listen for IPFIX flow report. Verify that
65 the correct packet count was reported."""
66 args = TrafficScriptArg(
67 ['src_mac', 'dst_mac', 'src_ip', 'dst_ip', 'protocol', 'port', 'count']
70 dst_mac = args.get_arg('dst_mac')
71 src_mac = args.get_arg('src_mac')
72 src_ip = args.get_arg('src_ip')
73 dst_ip = args.get_arg('dst_ip')
74 tx_if = args.get_arg('tx_if')
76 protocol = args.get_arg('protocol')
77 source_port = int(args.get_arg('port'))
78 destination_port = int(args.get_arg('port'))
79 count = int(args.get_arg('count'))
84 # generate simple packet based on arguments
85 if valid_ipv4(src_ip) and valid_ipv4(dst_ip):
87 elif valid_ipv6(src_ip) and valid_ipv6(dst_ip):
90 raise ValueError("Invalid IP version!")
92 if protocol.upper() == 'TCP':
94 elif protocol.upper() == 'UDP':
97 raise ValueError("Invalid type of protocol!")
99 pkt_raw = (Ether(src=src_mac, dst=dst_mac) /
100 ip_version(src=src_ip, dst=dst_ip) /
101 protocol(sport=int(source_port),
102 dport=int(destination_port)))
104 # do not print details for sent packets when sending more than one
107 print("Sending more than one packet. Details will be filtered for "
112 pkt_pad = auto_pad(pkt_raw)
114 for _ in range(count):
115 txq.send(pkt_pad, verbose=verbose)
116 ignore.append(pkt_pad)
118 # allow scapy to recognize IPFIX headers and templates
119 ipfix = IPFIXHandler()
121 # get IPFIX template and data
123 pkt = rxq.recv(10, ignore=ignore, verbose=verbose)
125 raise RuntimeError("RX timeout")
127 if pkt.haslayer("ICMPv6ND_NS"):
128 # read another packet in the queue if the current one is ICMPv6ND_NS
131 if pkt.haslayer("IPFIXHeader"):
132 if pkt.haslayer("IPFIXTemplate"):
133 # create or update template for IPFIX data packets
134 ipfix.update_template(pkt)
135 elif pkt.haslayer("IPFIXData"):
136 data = pkt.getlayer(IPFIXData).fields
139 raise RuntimeError("Unable to parse IPFIX set after header.")
141 raise RuntimeError("Received non-IPFIX packet or IPFIX header "
144 # verify packet count
145 if data["packetTotalCount"] != count:
146 raise RuntimeError("IPFIX reported wrong packet count. Count was {0},"
147 "but should be {1}".format(data["packetTotalCount"],
149 # verify IP addresses
151 err = "{0} mismatch. Packets used {1}, but were classified as {2}."
153 if "IPv4_src" in keys:
154 if data["IPv4_src"] != src_ip:
156 err.format("Source IP", src_ip, data["IPv4_src"]))
157 if "IPv4_dst" in keys:
158 if data["IPv4_dst"] != dst_ip:
160 err.format("Destination IP", dst_ip, data["IPv4_dst"]))
162 if "IPv6_src" in keys:
163 if data["IPv6_src"] != src_ip:
165 err.format("Source IP", src_ip, data["IPv6_src"]))
166 if "IPv6_dst" in keys:
167 if data["IPv6_dst"] != dst_ip:
169 err.format("Source IP", src_ip, data["IPv6_dst"]))
170 # verify port numbers
171 for item in ("src_port", "tcp_src_port", "udp_src_port"):
173 if int(data[item]) != source_port:
175 err.format("Source port", source_port, data[item]))
178 for item in ("dst_port", "tcp_dst_port", "udp_dst_port"):
180 if int(data[item]) != destination_port:
182 err.format("Source port", destination_port, data[item]))
186 if "Protocol_ID" in keys:
187 if protocol == TCP and int(data["Protocol_ID"]) != 6:
188 raise RuntimeError("TCP Packets were classified as not TCP.")
189 if protocol == UDP and int(data["Protocol_ID"]) != 17:
190 raise RuntimeError("UDP Packets were classified as not UDP.")
194 if __name__ == "__main__":