blob: 59957a74e9115eca017d7662653bcd4bd2025934 [file] [log] [blame]
Klement Sekeraacb9b8e2017-02-14 02:55:31 +01001""" test framework utilities """
2
Paul Vinciguerra2f156312020-05-02 22:34:40 -04003import ipaddress
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05004import logging
Matej Klotton0178d522016-11-04 11:11:44 +01005import socket
Paul Vinciguerrae8fece82019-02-28 15:34:00 -08006from socket import AF_INET6
juraj.linkes40dd73b2018-09-21 13:55:16 +02007import os.path
Tom Jones4941afb2024-02-07 13:29:51 +00008import platform
Klement Sekeraad3187f2022-02-18 10:34:35 +00009from copy import deepcopy
Klement Sekera26cd0242022-02-18 10:35:08 +000010from collections import UserDict
Klement Sekera7bb873a2016-11-18 07:38:42 +010011
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -070012import scapy.compat
Andrew Yourtchenko92dc12a2017-09-07 13:22:24 +020013from scapy.layers.l2 import Ether
Klement Sekera75e7d132017-09-20 08:26:30 +020014from scapy.layers.inet import IP
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020015from scapy.layers.inet6 import (
16 IPv6,
17 IPv6ExtHdrFragment,
18 IPv6ExtHdrRouting,
19 IPv6ExtHdrHopByHop,
20)
Paul Vinciguerrae8fece82019-02-28 15:34:00 -080021from scapy.packet import Raw
Klement Sekera611864f2018-09-26 11:19:00 +020022from scapy.utils import hexdump
Paul Vinciguerrae8fece82019-02-28 15:34:00 -080023from scapy.utils6 import in6_mactoifaceid
24
Ole Troan7f991832018-12-06 17:35:12 +010025from io import BytesIO
Ole Troan8006c6a2018-12-17 12:02:26 +010026from vpp_papi import mac_pton
Andrew Yourtchenko92dc12a2017-09-07 13:22:24 +020027
Paul Vinciguerra9beabd82019-12-01 22:24:28 -050028# Set up an empty logger for the testcase that can be overridden as necessary
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020029null_logger = logging.getLogger("VppTestCase.util")
Paul Vinciguerra9beabd82019-12-01 22:24:28 -050030null_logger.addHandler(logging.NullHandler())
31
Klement Sekera7bb873a2016-11-18 07:38:42 +010032
Klement Sekera26cd0242022-02-18 10:35:08 +000033def pr(packet):
34 return packet.__repr__()
35
36
Klement Sekera7bb873a2016-11-18 07:38:42 +010037def ppp(headline, packet):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020038 """Return string containing headline and output of scapy packet.show()"""
39 return "%s\n%s\n\n%s\n" % (
40 headline,
41 hexdump(packet, dump=True),
42 packet.show(dump=True),
43 )
Klement Sekera7bb873a2016-11-18 07:38:42 +010044
Damjan Marionf56b77a2016-10-03 19:44:57 +020045
Klement Sekera9225dee2016-12-12 08:36:58 +010046def ppc(headline, capture, limit=10):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020047 """Return string containing ppp() printout for a capture.
Klement Sekera9225dee2016-12-12 08:36:58 +010048
49 :param headline: printed as first line of output
50 :param capture: packets to print
51 :param limit: limit the print to # of packets
52 """
53 if not capture:
54 return headline
Klement Sekeradab231a2016-12-21 08:50:14 +010055 tail = ""
Klement Sekera9225dee2016-12-12 08:36:58 +010056 if limit < len(capture):
Klement Sekeradab231a2016-12-21 08:50:14 +010057 tail = "\nPrint limit reached, %s out of %s packets printed" % (
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020058 limit,
59 len(capture),
60 )
61 body = "".join(
62 [ppp("Packet #%s:" % count, p) for count, p in zip(range(0, limit), capture)]
63 )
Klement Sekeradab231a2016-12-21 08:50:14 +010064 return "%s\n%s%s" % (headline, body, tail)
Klement Sekera9225dee2016-12-12 08:36:58 +010065
66
Eyal Barid81da8c2017-01-11 13:39:54 +020067def ip4_range(ip4, s, e):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020068 tmp = ip4.rsplit(".", 1)[0]
Eyal Barid81da8c2017-01-11 13:39:54 +020069 return ("%s.%d" % (tmp, i) for i in range(s, e))
70
71
Paul Vinciguerra2f156312020-05-02 22:34:40 -040072def mcast_ip_to_mac(ip):
73 ip = ipaddress.ip_address(ip)
74 if not ip.is_multicast:
75 raise ValueError("Must be multicast address.")
76 ip_as_int = int(ip)
77 if ip.version == 4:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020078 mcast_mac = "01:00:5e:%02x:%02x:%02x" % (
79 (ip_as_int >> 16) & 0x7F,
80 (ip_as_int >> 8) & 0xFF,
81 ip_as_int & 0xFF,
82 )
Paul Vinciguerra2f156312020-05-02 22:34:40 -040083 else:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020084 mcast_mac = "33:33:%02x:%02x:%02x:%02x" % (
85 (ip_as_int >> 24) & 0xFF,
86 (ip_as_int >> 16) & 0xFF,
87 (ip_as_int >> 8) & 0xFF,
88 ip_as_int & 0xFF,
89 )
Paul Vinciguerra2f156312020-05-02 22:34:40 -040090 return mcast_mac
Eyal Barid81da8c2017-01-11 13:39:54 +020091
92
Paul Vinciguerrae8fece82019-02-28 15:34:00 -080093# wrapper around scapy library function.
Neale Ranns2a3ea492017-04-19 05:24:40 -070094def mk_ll_addr(mac):
Ole Troan6ed154f2019-10-15 19:31:55 +020095 euid = in6_mactoifaceid(str(mac))
Neale Ranns2a3ea492017-04-19 05:24:40 -070096 addr = "fe80::" + euid
97 return addr
98
99
Juraj Sloboda4b9669d2018-01-15 10:39:21 +0100100def ip6_normalize(ip6):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200101 return socket.inet_ntop(socket.AF_INET6, socket.inet_pton(socket.AF_INET6, ip6))
Juraj Sloboda4b9669d2018-01-15 10:39:21 +0100102
103
juraj.linkes40dd73b2018-09-21 13:55:16 +0200104def get_core_path(tempdir):
105 return "%s/%s" % (tempdir, get_core_pattern())
106
107
108def is_core_present(tempdir):
109 return os.path.isfile(get_core_path(tempdir))
110
111
112def get_core_pattern():
Tom Jones4941afb2024-02-07 13:29:51 +0000113 if platform.uname().system == "FreeBSD":
114 import sysctl
115
116 corefmt = sysctl.filter("kern.corefile")[0].value
117 elif platform.uname().system == "Linux":
118 with open("/proc/sys/kernel/core_pattern", "r") as f:
119 corefmt = f.read().strip()
juraj.linkes40dd73b2018-09-21 13:55:16 +0200120 return corefmt
121
122
123def check_core_path(logger, core_path):
124 corefmt = get_core_pattern()
125 if corefmt.startswith("|"):
126 logger.error(
127 "WARNING: redirecting the core dump through a"
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200128 " filter may result in truncated dumps."
129 )
juraj.linkes40dd73b2018-09-21 13:55:16 +0200130 logger.error(
131 " You may want to check the filter settings"
132 " or uninstall it and edit the"
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200133 " /proc/sys/kernel/core_pattern accordingly."
134 )
135 logger.error(" current core pattern is: %s" % corefmt)
Andrew Yourtchenko57612eb2018-03-28 15:32:10 +0200136
137
Paul Vinciguerrae061dad2020-12-04 14:57:51 -0500138class NumericConstant:
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200139 desc_dict = {}
140
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200141 def __init__(self, value):
142 self._value = value
143
144 def __int__(self):
145 return self._value
146
147 def __long__(self):
148 return self._value
149
150 def __str__(self):
151 if self._value in self.desc_dict:
152 return self.desc_dict[self._value]
153 return ""
154
155
Paul Vinciguerrae061dad2020-12-04 14:57:51 -0500156class Host:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200157 """Generic test host "connected" to VPPs interface."""
Damjan Marionf56b77a2016-10-03 19:44:57 +0200158
Klement Sekeraf62ae122016-10-11 11:47:09 +0200159 @property
160 def mac(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200161 """MAC address"""
Klement Sekeraf62ae122016-10-11 11:47:09 +0200162 return self._mac
Damjan Marionf56b77a2016-10-03 19:44:57 +0200163
Klement Sekeraf62ae122016-10-11 11:47:09 +0200164 @property
Eyal Baric86e5922017-07-02 18:33:16 +0300165 def bin_mac(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200166 """MAC address"""
Ole Troan8006c6a2018-12-17 12:02:26 +0100167 return mac_pton(self._mac)
Eyal Baric86e5922017-07-02 18:33:16 +0300168
169 @property
Klement Sekeraf62ae122016-10-11 11:47:09 +0200170 def ip4(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200171 """IPv4 address - string"""
Klement Sekeraf62ae122016-10-11 11:47:09 +0200172 return self._ip4
Damjan Marionf56b77a2016-10-03 19:44:57 +0200173
Klement Sekeraf62ae122016-10-11 11:47:09 +0200174 @property
Matej Klotton0178d522016-11-04 11:11:44 +0100175 def ip4n(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200176 """IPv4 address of remote host - raw, suitable as API parameter."""
Matej Klotton0178d522016-11-04 11:11:44 +0100177 return socket.inet_pton(socket.AF_INET, self._ip4)
178
179 @property
Klement Sekeraf62ae122016-10-11 11:47:09 +0200180 def ip6(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200181 """IPv6 address - string"""
Klement Sekeraf62ae122016-10-11 11:47:09 +0200182 return self._ip6
Damjan Marionf56b77a2016-10-03 19:44:57 +0200183
Klement Sekera46a87ad2017-01-02 08:22:23 +0100184 @property
185 def ip6n(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200186 """IPv6 address of remote host - raw, suitable as API parameter."""
Klement Sekera46a87ad2017-01-02 08:22:23 +0100187 return socket.inet_pton(socket.AF_INET6, self._ip6)
188
Neale Ranns2a3ea492017-04-19 05:24:40 -0700189 @property
190 def ip6_ll(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200191 """IPv6 link-local address - string"""
Neale Ranns2a3ea492017-04-19 05:24:40 -0700192 return self._ip6_ll
193
194 @property
195 def ip6n_ll(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200196 """IPv6 link-local address of remote host -
Neale Ranns2a3ea492017-04-19 05:24:40 -0700197 raw, suitable as API parameter."""
198 return socket.inet_pton(socket.AF_INET6, self._ip6_ll)
199
Eyal Baric86e5922017-07-02 18:33:16 +0300200 def __eq__(self, other):
201 if isinstance(other, Host):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200202 return (
203 self.mac == other.mac
204 and self.ip4 == other.ip4
205 and self.ip6 == other.ip6
206 and self.ip6_ll == other.ip6_ll
207 )
Eyal Baric86e5922017-07-02 18:33:16 +0300208 else:
209 return False
210
211 def __ne__(self, other):
212 return not self.__eq__(other)
213
214 def __repr__(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200215 return "Host { mac:%s ip4:%s ip6:%s ip6_ll:%s }" % (
216 self.mac,
217 self.ip4,
218 self.ip6,
219 self.ip6_ll,
220 )
Eyal Baric86e5922017-07-02 18:33:16 +0300221
222 def __hash__(self):
223 return hash(self.__repr__())
224
Neale Ranns2a3ea492017-04-19 05:24:40 -0700225 def __init__(self, mac=None, ip4=None, ip6=None, ip6_ll=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200226 self._mac = mac
227 self._ip4 = ip4
228 self._ip6 = ip6
Neale Ranns2a3ea492017-04-19 05:24:40 -0700229 self._ip6_ll = ip6_ll
Filip Tehlar770e89e2017-01-31 10:39:16 +0100230
231
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200232class L4_Conn:
233 """L4 'connection' tied to two VPP interfaces"""
Klement Sekera75e7d132017-09-20 08:26:30 +0200234
Andrew Yourtchenko92dc12a2017-09-07 13:22:24 +0200235 def __init__(self, testcase, if1, if2, af, l4proto, port1, port2):
236 self.testcase = testcase
237 self.ifs = [None, None]
238 self.ifs[0] = if1
239 self.ifs[1] = if2
240 self.address_family = af
241 self.l4proto = l4proto
242 self.ports = [None, None]
243 self.ports[0] = port1
244 self.ports[1] = port2
245 self
246
247 def pkt(self, side, l4args={}, payload="x"):
248 is_ip6 = 1 if self.address_family == AF_INET6 else 0
249 s0 = side
Klement Sekera75e7d132017-09-20 08:26:30 +0200250 s1 = 1 - side
Andrew Yourtchenko92dc12a2017-09-07 13:22:24 +0200251 src_if = self.ifs[s0]
252 dst_if = self.ifs[s1]
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200253 layer_3 = [
254 IP(src=src_if.remote_ip4, dst=dst_if.remote_ip4),
255 IPv6(src=src_if.remote_ip6, dst=dst_if.remote_ip6),
256 ]
257 merged_l4args = {"sport": self.ports[s0], "dport": self.ports[s1]}
Andrew Yourtchenko92dc12a2017-09-07 13:22:24 +0200258 merged_l4args.update(l4args)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200259 p = (
260 Ether(dst=src_if.local_mac, src=src_if.remote_mac)
261 / layer_3[is_ip6]
262 / self.l4proto(**merged_l4args)
263 / Raw(payload)
264 )
Andrew Yourtchenko92dc12a2017-09-07 13:22:24 +0200265 return p
266
267 def send(self, side, flags=None, payload=""):
268 l4args = {}
269 if flags is not None:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200270 l4args["flags"] = flags
271 self.ifs[side].add_stream(self.pkt(side, l4args=l4args, payload=payload))
Klement Sekera75e7d132017-09-20 08:26:30 +0200272 self.ifs[1 - side].enable_capture()
Andrew Yourtchenko92dc12a2017-09-07 13:22:24 +0200273 self.testcase.pg_start()
274
275 def recv(self, side):
276 p = self.ifs[side].wait_for_packet(1)
277 return p
278
279 def send_through(self, side, flags=None, payload=""):
280 self.send(side, flags, payload)
Klement Sekera75e7d132017-09-20 08:26:30 +0200281 p = self.recv(1 - side)
Andrew Yourtchenko92dc12a2017-09-07 13:22:24 +0200282 return p
283
284 def send_pingpong(self, side, flags1=None, flags2=None):
285 p1 = self.send_through(side, flags1)
Klement Sekera75e7d132017-09-20 08:26:30 +0200286 p2 = self.send_through(1 - side, flags2)
Andrew Yourtchenko92dc12a2017-09-07 13:22:24 +0200287 return [p1, p2]
288
289
290class L4_CONN_SIDE:
291 L4_CONN_SIDE_ZERO = 0
292 L4_CONN_SIDE_ONE = 1
Klement Sekera75e7d132017-09-20 08:26:30 +0200293
294
Paul Vinciguerra9beabd82019-12-01 22:24:28 -0500295def fragment_rfc791(packet, fragsize, logger=null_logger):
Klement Sekera75e7d132017-09-20 08:26:30 +0200296 """
297 Fragment an IPv4 packet per RFC 791
298 :param packet: packet to fragment
299 :param fragsize: size at which to fragment
300 :note: IP options are not supported
301 :returns: list of fragments
302 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200303 logger.debug(ppp("Fragmenting packet:", packet))
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700304 packet = packet.__class__(scapy.compat.raw(packet)) # recalc. all values
Klement Sekera75e7d132017-09-20 08:26:30 +0200305 if len(packet[IP].options) > 0:
306 raise Exception("Not implemented")
307 if len(packet) <= fragsize:
308 return [packet]
309
310 pre_ip_len = len(packet) - len(packet[IP])
311 ip_header_len = packet[IP].ihl * 4
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700312 hex_packet = scapy.compat.raw(packet)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200313 hex_headers = hex_packet[: (pre_ip_len + ip_header_len)]
314 hex_payload = hex_packet[(pre_ip_len + ip_header_len) :]
Klement Sekera75e7d132017-09-20 08:26:30 +0200315
316 pkts = []
317 ihl = packet[IP].ihl
318 otl = len(packet[IP])
snaramre3030bea2019-10-17 16:39:03 +0000319 nfb = int((fragsize - pre_ip_len - ihl * 4) / 8)
Klement Sekera75e7d132017-09-20 08:26:30 +0200320 fo = packet[IP].frag
321
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200322 p = packet.__class__(hex_headers + hex_payload[: nfb * 8])
Klement Sekera75e7d132017-09-20 08:26:30 +0200323 p[IP].flags = "MF"
324 p[IP].frag = fo
325 p[IP].len = ihl * 4 + nfb * 8
326 del p[IP].chksum
327 pkts.append(p)
328
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200329 p = packet.__class__(hex_headers + hex_payload[nfb * 8 :])
Klement Sekera75e7d132017-09-20 08:26:30 +0200330 p[IP].len = otl - nfb * 8
331 p[IP].frag = fo + nfb
332 del p[IP].chksum
333
Paul Vinciguerra9beabd82019-12-01 22:24:28 -0500334 more_fragments = fragment_rfc791(p, fragsize, logger)
Klement Sekera75e7d132017-09-20 08:26:30 +0200335 pkts.extend(more_fragments)
336
337 return pkts
338
339
Paul Vinciguerra9beabd82019-12-01 22:24:28 -0500340def fragment_rfc8200(packet, identification, fragsize, logger=null_logger):
Klement Sekera75e7d132017-09-20 08:26:30 +0200341 """
342 Fragment an IPv6 packet per RFC 8200
343 :param packet: packet to fragment
344 :param fragsize: size at which to fragment
345 :note: IP options are not supported
346 :returns: list of fragments
347 """
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700348 packet = packet.__class__(scapy.compat.raw(packet)) # recalc. all values
Klement Sekera75e7d132017-09-20 08:26:30 +0200349 if len(packet) <= fragsize:
350 return [packet]
351 logger.debug(ppp("Fragmenting packet:", packet))
352 pkts = []
353 counter = 0
354 routing_hdr = None
355 hop_by_hop_hdr = None
356 upper_layer = None
357 seen_ipv6 = False
358 ipv6_nr = -1
359 l = packet.getlayer(counter)
360 while l is not None:
361 if l.__class__ is IPv6:
362 if seen_ipv6:
363 # ignore 2nd IPv6 header and everything below..
364 break
365 ipv6_nr = counter
366 seen_ipv6 = True
367 elif l.__class__ is IPv6ExtHdrFragment:
368 raise Exception("Already fragmented")
369 elif l.__class__ is IPv6ExtHdrRouting:
370 routing_hdr = counter
371 elif l.__class__ is IPv6ExtHdrHopByHop:
372 hop_by_hop_hdr = counter
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200373 elif (
374 seen_ipv6
375 and not upper_layer
376 and not l.__class__.__name__.startswith("IPv6ExtHdr")
377 ):
Klement Sekera75e7d132017-09-20 08:26:30 +0200378 upper_layer = counter
379 counter = counter + 1
380 l = packet.getlayer(counter)
381
382 logger.debug(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200383 "Layers seen: IPv6(#%s), Routing(#%s), HopByHop(#%s), upper(#%s)"
384 % (ipv6_nr, routing_hdr, hop_by_hop_hdr, upper_layer)
385 )
Klement Sekera75e7d132017-09-20 08:26:30 +0200386
387 if upper_layer is None:
388 raise Exception("Upper layer header not found in IPv6 packet")
389
390 last_per_fragment_hdr = ipv6_nr
391 if routing_hdr is None:
392 if hop_by_hop_hdr is not None:
393 last_per_fragment_hdr = hop_by_hop_hdr
394 else:
395 last_per_fragment_hdr = routing_hdr
396 logger.debug("Last per-fragment hdr is #%s" % (last_per_fragment_hdr))
397
398 per_fragment_headers = packet.copy()
399 per_fragment_headers[last_per_fragment_hdr].remove_payload()
400 logger.debug(ppp("Per-fragment headers:", per_fragment_headers))
401
402 ext_and_upper_layer = packet.getlayer(last_per_fragment_hdr)[1]
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700403 hex_payload = scapy.compat.raw(ext_and_upper_layer)
Klement Sekera75e7d132017-09-20 08:26:30 +0200404 logger.debug("Payload length is %s" % len(hex_payload))
405 logger.debug(ppp("Ext and upper layer:", ext_and_upper_layer))
406
407 fragment_ext_hdr = IPv6ExtHdrFragment()
408 logger.debug(ppp("Fragment header:", fragment_ext_hdr))
409
Neale Ranns14046982019-07-29 14:49:52 +0000410 len_ext_and_upper_layer_payload = len(ext_and_upper_layer.payload)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200411 if not len_ext_and_upper_layer_payload and hasattr(ext_and_upper_layer, "data"):
Neale Ranns14046982019-07-29 14:49:52 +0000412 len_ext_and_upper_layer_payload = len(ext_and_upper_layer.data)
413
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200414 if (
415 len(per_fragment_headers)
416 + len(fragment_ext_hdr)
417 + len(ext_and_upper_layer)
418 - len_ext_and_upper_layer_payload
419 > fragsize
420 ):
421 raise Exception(
422 "Cannot fragment this packet - MTU too small "
423 "(%s, %s, %s, %s, %s)"
424 % (
425 len(per_fragment_headers),
426 len(fragment_ext_hdr),
427 len(ext_and_upper_layer),
428 len_ext_and_upper_layer_payload,
429 fragsize,
430 )
431 )
Klement Sekera75e7d132017-09-20 08:26:30 +0200432
433 orig_nh = packet[IPv6].nh
434 p = per_fragment_headers
435 del p[IPv6].plen
436 del p[IPv6].nh
437 p = p / fragment_ext_hdr
438 del p[IPv6ExtHdrFragment].nh
snaramre3030bea2019-10-17 16:39:03 +0000439 first_payload_len_nfb = int((fragsize - len(p)) / 8)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200440 p = p / Raw(hex_payload[: first_payload_len_nfb * 8])
Klement Sekera75e7d132017-09-20 08:26:30 +0200441 del p[IPv6].plen
442 p[IPv6ExtHdrFragment].nh = orig_nh
443 p[IPv6ExtHdrFragment].id = identification
444 p[IPv6ExtHdrFragment].offset = 0
445 p[IPv6ExtHdrFragment].m = 1
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700446 p = p.__class__(scapy.compat.raw(p))
Klement Sekera75e7d132017-09-20 08:26:30 +0200447 logger.debug(ppp("Fragment %s:" % len(pkts), p))
448 pkts.append(p)
449 offset = first_payload_len_nfb * 8
450 logger.debug("Offset after first fragment: %s" % offset)
451 while len(hex_payload) > offset:
452 p = per_fragment_headers
453 del p[IPv6].plen
454 del p[IPv6].nh
455 p = p / fragment_ext_hdr
456 del p[IPv6ExtHdrFragment].nh
snaramre3030bea2019-10-17 16:39:03 +0000457 l_nfb = int((fragsize - len(p)) / 8)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200458 p = p / Raw(hex_payload[offset : offset + l_nfb * 8])
Klement Sekera75e7d132017-09-20 08:26:30 +0200459 p[IPv6ExtHdrFragment].nh = orig_nh
460 p[IPv6ExtHdrFragment].id = identification
snaramre3030bea2019-10-17 16:39:03 +0000461 p[IPv6ExtHdrFragment].offset = int(offset / 8)
Klement Sekera75e7d132017-09-20 08:26:30 +0200462 p[IPv6ExtHdrFragment].m = 1
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700463 p = p.__class__(scapy.compat.raw(p))
Klement Sekera75e7d132017-09-20 08:26:30 +0200464 logger.debug(ppp("Fragment %s:" % len(pkts), p))
465 pkts.append(p)
466 offset = offset + l_nfb * 8
467
468 pkts[-1][IPv6ExtHdrFragment].m = 0 # reset more-flags in last fragment
469
470 return pkts
Ole Troan7f991832018-12-06 17:35:12 +0100471
472
473def reassemble4_core(listoffragments, return_ip):
474 buffer = BytesIO()
475 first = listoffragments[0]
476 buffer.seek(20)
477 for pkt in listoffragments:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200478 buffer.seek(pkt[IP].frag * 8)
Ole Troan7f991832018-12-06 17:35:12 +0100479 buffer.write(bytes(pkt[IP].payload))
480 first.len = len(buffer.getvalue()) + 20
481 first.flags = 0
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200482 del first.chksum
Ole Troan7f991832018-12-06 17:35:12 +0100483 if return_ip:
484 header = bytes(first[IP])[:20]
485 return first[IP].__class__(header + buffer.getvalue())
486 else:
487 header = bytes(first[Ether])[:34]
488 return first[Ether].__class__(header + buffer.getvalue())
489
490
491def reassemble4_ether(listoffragments):
492 return reassemble4_core(listoffragments, False)
493
494
495def reassemble4(listoffragments):
496 return reassemble4_core(listoffragments, True)
Klement Sekeraad3187f2022-02-18 10:34:35 +0000497
498
Klement Sekera26cd0242022-02-18 10:35:08 +0000499class UnexpectedPacketError(Exception):
500 def __init__(self, packet, msg=""):
501 self.packet = packet
502 self.msg = msg
503
504 def __str__(self):
505 return f"\nUnexpected packet:\n{pr(self.packet)}{self.msg}"
506
507
Klement Sekeraad3187f2022-02-18 10:34:35 +0000508def recursive_dict_merge(dict_base, dict_update):
509 """Recursively merge base dict with update dict, return merged dict"""
510 for key in dict_update:
511 if key in dict_base:
512 if type(dict_update[key]) is dict:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200513 dict_base[key] = recursive_dict_merge(dict_base[key], dict_update[key])
Klement Sekeraad3187f2022-02-18 10:34:35 +0000514 else:
515 dict_base[key] = dict_update[key]
516 else:
517 dict_base[key] = dict_update[key]
518 return dict_base
519
520
Klement Sekera26cd0242022-02-18 10:35:08 +0000521class StatsDiff(UserDict):
Klement Sekeraad3187f2022-02-18 10:34:35 +0000522 """
523 Diff dictionary is a dictionary of dictionaries of interesting stats:
524
525 diff_dictionary =
526 {
527 "err" : { '/error/counter1' : 4, },
528 sw_if_index1 : { '/stat/segment/counter1' : 5,
529 '/stat/segment/counter2' : 6,
530 },
531 sw_if_index2 : { '/stat/segment/counter1' : 7,
532 },
533 }
534
535 It describes a per sw-if-index diffset, where each key is stat segment
536 path and value is the expected change for that counter for sw-if-index.
537 Special case string "err" is used for error counters, which are not per
538 sw-if-index.
539 """
540
Klement Sekera26cd0242022-02-18 10:35:08 +0000541 __slots__ = () # prevent setting properties to act like a dictionary
Klement Sekeraad3187f2022-02-18 10:34:35 +0000542
Klement Sekera26cd0242022-02-18 10:35:08 +0000543 def __init__(self, data):
544 super().__init__(data)
Klement Sekeraad3187f2022-02-18 10:34:35 +0000545
546 def __or__(self, other):
Klement Sekera26cd0242022-02-18 10:35:08 +0000547 return recursive_dict_merge(deepcopy(self.data), other)