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."""
19 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.telemetry.IPFIXUtil import IPFIXHandler, \
27 from resources.libraries.python.PacketVerifier import RxQueue, TxQueue, auto_pad
28 from resources.libraries.python.TrafficScriptArg import TrafficScriptArg
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 # clear receive buffer
123 pkt = rxq.recv(1, ignore=ignore, verbose=verbose)
128 # get IPFIX template and data
132 raise RuntimeError("RX timeout")
133 if pkt.haslayer("IPFIXHeader"):
134 if pkt.haslayer("IPFIXTemplate"):
135 # create or update template for IPFIX data packets
136 ipfix.update_template(pkt)
137 elif pkt.haslayer("IPFIXData"):
138 data = pkt.getlayer(IPFIXData).fields
141 raise RuntimeError("Unable to parse IPFIX set after header.")
143 raise RuntimeError("Received non-IPFIX packet or IPFIX header "
146 # verify packet count
147 if data["packetTotalCount"] != count:
149 "IPFIX reported wrong packet count. Count was {0},"
150 " but should be {1}".format(data["packetTotalCount"], count))
151 # verify IP addresses
153 err = "{0} mismatch. Packets used {1}, but were classified as {2}."
155 if "IPv4_src" in keys:
156 if data["IPv4_src"] != src_ip:
158 err.format("Source IP", src_ip, data["IPv4_src"]))
159 if "IPv4_dst" in keys:
160 if data["IPv4_dst"] != dst_ip:
162 err.format("Destination IP", dst_ip, data["IPv4_dst"]))
164 if "IPv6_src" in keys:
165 if data["IPv6_src"] != src_ip:
167 err.format("Source IP", src_ip, data["IPv6_src"]))
168 if "IPv6_dst" in keys:
169 if data["IPv6_dst"] != dst_ip:
171 err.format("Source IP", src_ip, data["IPv6_dst"]))
172 # verify port numbers
173 for item in ("src_port", "tcp_src_port", "udp_src_port"):
175 if int(data[item]) != source_port:
177 err.format("Source port", source_port, data[item]))
180 for item in ("dst_port", "tcp_dst_port", "udp_dst_port"):
182 if int(data[item]) != destination_port:
184 err.format("Source port", destination_port, data[item]))
188 if "Protocol_ID" in keys:
189 if protocol == TCP and int(data["Protocol_ID"]) != 6:
190 raise RuntimeError("TCP Packets were classified as not TCP.")
191 if protocol == UDP and int(data["Protocol_ID"]) != 17:
192 raise RuntimeError("UDP Packets were classified as not UDP.")
196 if __name__ == "__main__":