blob: 4b274c9223273c909b3e77705352bc32f5f18ff8 [file] [log] [blame]
Francois Claded7c62a2020-04-03 11:33:02 +02001#!/usr/bin/env python3
2
3import unittest
4import binascii
5from socket import AF_INET6
6
7from framework import VppTestCase, VppTestRunner
8from vpp_ip import DpoProto
9from vpp_ip_route import VppIpRoute, VppRoutePath, VppIpTable
10
11import scapy.compat
12from scapy.packet import Raw
13from scapy.layers.l2 import Ether, Dot1Q
14from scapy.layers.inet6 import IPv6, UDP, IPv6ExtHdrSegmentRouting
15from scapy.layers.inet import IP, UDP
16
17from util import ppp
18
19
Tianyu Li6d95f8c2022-02-25 05:51:10 +000020class TestSRv6AdFlow(VppTestCase):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020021 """SRv6 Flow-based Dynamic Proxy plugin Test Case"""
Francois Claded7c62a2020-04-03 11:33:02 +020022
23 @classmethod
24 def setUpClass(self):
Tianyu Li6d95f8c2022-02-25 05:51:10 +000025 super(TestSRv6AdFlow, self).setUpClass()
Francois Claded7c62a2020-04-03 11:33:02 +020026
27 @classmethod
28 def tearDownClass(cls):
Tianyu Li6d95f8c2022-02-25 05:51:10 +000029 super(TestSRv6AdFlow, cls).tearDownClass()
Francois Claded7c62a2020-04-03 11:33:02 +020030
31 def setUp(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020032 """Perform test setup before each test case."""
Tianyu Li6d95f8c2022-02-25 05:51:10 +000033 super(TestSRv6AdFlow, self).setUp()
Francois Claded7c62a2020-04-03 11:33:02 +020034
35 # packet sizes, inclusive L2 overhead
36 self.pg_packet_sizes = [64, 512, 1518, 9018]
37
38 # reset packet_infos
39 self.reset_packet_infos()
40
41 def tearDown(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020042 """Clean up test setup after each test case."""
Francois Claded7c62a2020-04-03 11:33:02 +020043 self.teardown_interfaces()
44
Tianyu Li6d95f8c2022-02-25 05:51:10 +000045 super(TestSRv6AdFlow, self).tearDown()
Francois Claded7c62a2020-04-03 11:33:02 +020046
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020047 def configure_interface(
48 self, interface, ipv6=False, ipv4=False, ipv6_table_id=0, ipv4_table_id=0
49 ):
50 """Configure interface.
Francois Claded7c62a2020-04-03 11:33:02 +020051 :param ipv6: configure IPv6 on interface
52 :param ipv4: configure IPv4 on interface
53 :param ipv6_table_id: FIB table_id for IPv6
54 :param ipv4_table_id: FIB table_id for IPv4
55 """
56 self.logger.debug("Configuring interface %s" % (interface.name))
57 if ipv6:
58 self.logger.debug("Configuring IPv6")
59 interface.set_table_ip6(ipv6_table_id)
60 interface.config_ip6()
61 interface.resolve_ndp(timeout=5)
62 if ipv4:
63 self.logger.debug("Configuring IPv4")
64 interface.set_table_ip4(ipv4_table_id)
65 interface.config_ip4()
66 interface.resolve_arp()
67 interface.admin_up()
68
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020069 def setup_interfaces(self, ipv6=[], ipv4=[], ipv6_table_id=[], ipv4_table_id=[]):
70 """Create and configure interfaces.
Francois Claded7c62a2020-04-03 11:33:02 +020071
72 :param ipv6: list of interface IPv6 capabilities
73 :param ipv4: list of interface IPv4 capabilities
74 :param ipv6_table_id: list of intf IPv6 FIB table_ids
75 :param ipv4_table_id: list of intf IPv4 FIB table_ids
76 :returns: List of created interfaces.
77 """
78 # how many interfaces?
79 if len(ipv6):
80 count = len(ipv6)
81 else:
82 count = len(ipv4)
83 self.logger.debug("Creating and configuring %d interfaces" % (count))
84
85 # fill up ipv6 and ipv4 lists if needed
86 # not enabled (False) is the default
87 if len(ipv6) < count:
88 ipv6 += (count - len(ipv6)) * [False]
89 if len(ipv4) < count:
90 ipv4 += (count - len(ipv4)) * [False]
91
92 # fill up table_id lists if needed
93 # table_id 0 (global) is the default
94 if len(ipv6_table_id) < count:
95 ipv6_table_id += (count - len(ipv6_table_id)) * [0]
96 if len(ipv4_table_id) < count:
97 ipv4_table_id += (count - len(ipv4_table_id)) * [0]
98
99 # create 'count' pg interfaces
100 self.create_pg_interfaces(range(count))
101
102 # setup all interfaces
103 for i in range(count):
104 intf = self.pg_interfaces[i]
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200105 self.configure_interface(
106 intf, ipv6[i], ipv4[i], ipv6_table_id[i], ipv4_table_id[i]
107 )
Francois Claded7c62a2020-04-03 11:33:02 +0200108
109 if any(ipv6):
110 self.logger.debug(self.vapi.cli("show ip6 neighbors"))
111 if any(ipv4):
112 self.logger.debug(self.vapi.cli("show ip4 neighbors"))
113 self.logger.debug(self.vapi.cli("show interface"))
114 self.logger.debug(self.vapi.cli("show hardware"))
115
116 return self.pg_interfaces
117
118 def teardown_interfaces(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200119 """Unconfigure and bring down interface."""
Francois Claded7c62a2020-04-03 11:33:02 +0200120 self.logger.debug("Tearing down interfaces")
121 # tear down all interfaces
122 # AFAIK they cannot be deleted
123 for i in self.pg_interfaces:
124 self.logger.debug("Tear down interface %s" % (i.name))
125 i.admin_down()
126 i.unconfig()
127 i.set_table_ip4(0)
128 i.set_table_ip6(0)
129
130 def test_SRv6_End_AD_IPv6(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200131 """Test SRv6 End.AD behavior with IPv6 traffic."""
132 self.src_addr = "a0::"
133 self.sid_list = ["a1::", "a2::a6", "a3::"]
Francois Claded7c62a2020-04-03 11:33:02 +0200134 self.test_sid_index = 1
135
136 # send traffic to one destination interface
137 # source and destination interfaces are IPv6 only
138 self.setup_interfaces(ipv6=[True, True])
139
140 # configure route to next segment
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200141 route = VppIpRoute(
142 self,
143 self.sid_list[self.test_sid_index + 1],
144 128,
145 [
146 VppRoutePath(
147 self.pg0.remote_ip6,
148 self.pg0.sw_if_index,
149 proto=DpoProto.DPO_PROTO_IP6,
150 )
151 ],
152 )
Francois Claded7c62a2020-04-03 11:33:02 +0200153 route.add_vpp_config()
154
155 # configure SRv6 localSID behavior
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200156 cli_str = (
157 "sr localsid address "
158 + self.sid_list[self.test_sid_index]
159 + " behavior end.ad.flow"
160 + " nh "
161 + self.pg1.remote_ip6
162 + " oif "
163 + self.pg1.name
164 + " iif "
165 + self.pg1.name
166 )
Francois Claded7c62a2020-04-03 11:33:02 +0200167 self.vapi.cli(cli_str)
168
169 # log the localsids
170 self.logger.debug(self.vapi.cli("show sr localsid"))
171
172 # send one packet per packet size
173 count = len(self.pg_packet_sizes)
174
175 # prepare IPv6 in SRv6 headers
176 packet_header1 = self.create_packet_header_IPv6_SRH_IPv6(
177 srcaddr=self.src_addr,
178 sidlist=self.sid_list[::-1],
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200179 segleft=len(self.sid_list) - self.test_sid_index - 1,
180 )
Francois Claded7c62a2020-04-03 11:33:02 +0200181
182 # generate packets (pg0->pg1)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200183 pkts1 = self.create_stream(
184 self.pg0, self.pg1, packet_header1, self.pg_packet_sizes, count
185 )
Francois Claded7c62a2020-04-03 11:33:02 +0200186
187 # send packets and verify received packets
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200188 self.send_and_verify_pkts(
189 self.pg0, pkts1, self.pg1, self.compare_rx_tx_packet_End_AD_IPv6_out
190 )
Francois Claded7c62a2020-04-03 11:33:02 +0200191
192 # log the localsid counters
193 self.logger.info(self.vapi.cli("show sr localsid"))
194
195 # prepare IPv6 header for returning packets
196 packet_header2 = self.create_packet_header_IPv6()
197
198 # generate returning packets (pg1->pg0)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200199 pkts2 = self.create_stream(
200 self.pg1, self.pg0, packet_header2, self.pg_packet_sizes, count
201 )
Francois Claded7c62a2020-04-03 11:33:02 +0200202
203 # send packets and verify received packets
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200204 self.send_and_verify_pkts(
205 self.pg1, pkts2, self.pg0, self.compare_rx_tx_packet_End_AD_IPv6_in
206 )
Francois Claded7c62a2020-04-03 11:33:02 +0200207
208 # log the localsid counters
209 self.logger.info(self.vapi.cli("show sr localsid"))
210
211 # remove SRv6 localSIDs
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200212 cli_str = "sr localsid del address " + self.sid_list[self.test_sid_index]
Francois Claded7c62a2020-04-03 11:33:02 +0200213 self.vapi.cli(cli_str)
214
215 # cleanup interfaces
216 self.teardown_interfaces()
217
218 def compare_rx_tx_packet_End_AD_IPv6_out(self, tx_pkt, rx_pkt):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200219 """Compare input and output packet after passing End.AD with IPv6
Francois Claded7c62a2020-04-03 11:33:02 +0200220
221 :param tx_pkt: transmitted packet
222 :param rx_pkt: received packet
223 """
224
225 # get first (outer) IPv6 header of rx'ed packet
226 rx_ip = rx_pkt.getlayer(IPv6)
227
228 tx_ip = tx_pkt.getlayer(IPv6)
229 tx_ip2 = tx_pkt.getlayer(IPv6, 2)
230
231 # verify if rx'ed packet has no SRH
232 self.assertFalse(rx_pkt.haslayer(IPv6ExtHdrSegmentRouting))
233
234 # the whole rx_ip pkt should be equal to tx_ip2
235 # except for the hlim field
236 # -> adjust tx'ed hlim to expected hlim
237 tx_ip2.hlim = tx_ip2.hlim - 1
238
239 self.assertEqual(rx_ip, tx_ip2)
240
241 self.logger.debug("packet verification: SUCCESS")
242
243 def compare_rx_tx_packet_End_AD_IPv6_in(self, tx_pkt, rx_pkt):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200244 """Compare input and output packet after passing End.AD
Francois Claded7c62a2020-04-03 11:33:02 +0200245
246 :param tx_pkt: transmitted packet
247 :param rx_pkt: received packet
248 """
249
250 # get first (outer) IPv6 header of rx'ed packet
251 rx_ip = rx_pkt.getlayer(IPv6)
252 # received ip.src should be equal to SR Policy source
253 self.assertEqual(rx_ip.src, self.src_addr)
254 # received ip.dst should be equal to expected sidlist next segment
255 self.assertEqual(rx_ip.dst, self.sid_list[self.test_sid_index + 1])
256
257 # rx'ed packet should have SRH
258 self.assertTrue(rx_pkt.haslayer(IPv6ExtHdrSegmentRouting))
259
260 # get SRH
261 rx_srh = rx_pkt.getlayer(IPv6ExtHdrSegmentRouting)
262 # rx'ed seglist should be equal to SID-list in reversed order
263 self.assertEqual(rx_srh.addresses, self.sid_list[::-1])
264 # segleft should be equal to previous segleft value minus 1
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200265 self.assertEqual(rx_srh.segleft, len(self.sid_list) - self.test_sid_index - 2)
Francois Claded7c62a2020-04-03 11:33:02 +0200266 # lastentry should be equal to the SID-list length minus 1
267 self.assertEqual(rx_srh.lastentry, len(self.sid_list) - 1)
268
269 # the whole rx'ed pkt beyond SRH should be equal to tx'ed pkt
270 # except for the hop-limit field
271 tx_ip = tx_pkt.getlayer(IPv6)
272 # -> update tx'ed hlim to the expected hlim
273 tx_ip.hlim -= 1
274 # -> check payload
275 self.assertEqual(rx_srh.payload, tx_ip)
276
277 self.logger.debug("packet verification: SUCCESS")
278
279 def test_SRv6_End_AD_IPv4(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200280 """Test SRv6 End.AD behavior with IPv4 traffic."""
281 self.src_addr = "a0::"
282 self.sid_list = ["a1::", "a2::a4", "a3::"]
Francois Claded7c62a2020-04-03 11:33:02 +0200283 self.test_sid_index = 1
284
285 # send traffic to one destination interface
286 # source and destination interfaces are IPv6 only
287 self.setup_interfaces(ipv6=[True, False], ipv4=[False, True])
288
289 # configure route to next segment
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200290 route = VppIpRoute(
291 self,
292 self.sid_list[self.test_sid_index + 1],
293 128,
294 [
295 VppRoutePath(
296 self.pg0.remote_ip6,
297 self.pg0.sw_if_index,
298 proto=DpoProto.DPO_PROTO_IP6,
299 )
300 ],
301 )
Francois Claded7c62a2020-04-03 11:33:02 +0200302 route.add_vpp_config()
303
304 # configure SRv6 localSID behavior
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200305 cli_str = (
306 "sr localsid address "
307 + self.sid_list[self.test_sid_index]
308 + " behavior end.ad.flow"
309 + " nh "
310 + self.pg1.remote_ip4
311 + " oif "
312 + self.pg1.name
313 + " iif "
314 + self.pg1.name
315 )
Francois Claded7c62a2020-04-03 11:33:02 +0200316 self.vapi.cli(cli_str)
317
318 # log the localsids
319 self.logger.debug(self.vapi.cli("show sr localsid"))
320
321 # send one packet per packet size
322 count = len(self.pg_packet_sizes)
323
324 # prepare IPv4 in SRv6 headers
325 packet_header1 = self.create_packet_header_IPv6_SRH_IPv4(
326 srcaddr=self.src_addr,
327 sidlist=self.sid_list[::-1],
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200328 segleft=len(self.sid_list) - self.test_sid_index - 1,
329 )
Francois Claded7c62a2020-04-03 11:33:02 +0200330
331 # generate packets (pg0->pg1)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200332 pkts1 = self.create_stream(
333 self.pg0, self.pg1, packet_header1, self.pg_packet_sizes, count
334 )
Francois Claded7c62a2020-04-03 11:33:02 +0200335
336 # send packets and verify received packets
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200337 self.send_and_verify_pkts(
338 self.pg0, pkts1, self.pg1, self.compare_rx_tx_packet_End_AD_IPv4_out
339 )
Francois Claded7c62a2020-04-03 11:33:02 +0200340
341 # log the localsid counters
342 self.logger.info(self.vapi.cli("show sr localsid"))
343
344 # prepare IPv6 header for returning packets
345 packet_header2 = self.create_packet_header_IPv4()
346
347 # generate returning packets (pg1->pg0)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200348 pkts2 = self.create_stream(
349 self.pg1, self.pg0, packet_header2, self.pg_packet_sizes, count
350 )
Francois Claded7c62a2020-04-03 11:33:02 +0200351
352 # send packets and verify received packets
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200353 self.send_and_verify_pkts(
354 self.pg1, pkts2, self.pg0, self.compare_rx_tx_packet_End_AD_IPv4_in
355 )
Francois Claded7c62a2020-04-03 11:33:02 +0200356
357 # log the localsid counters
358 self.logger.info(self.vapi.cli("show sr localsid"))
359
360 # remove SRv6 localSIDs
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200361 cli_str = "sr localsid del address " + self.sid_list[self.test_sid_index]
Francois Claded7c62a2020-04-03 11:33:02 +0200362 self.vapi.cli(cli_str)
363
364 # cleanup interfaces
365 self.teardown_interfaces()
366
367 def compare_rx_tx_packet_End_AD_IPv4_out(self, tx_pkt, rx_pkt):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200368 """Compare input and output packet after passing End.AD with IPv4
Francois Claded7c62a2020-04-03 11:33:02 +0200369
370 :param tx_pkt: transmitted packet
371 :param rx_pkt: received packet
372 """
373
374 # get IPv4 header of rx'ed packet
375 rx_ip = rx_pkt.getlayer(IP)
376
377 tx_ip = tx_pkt.getlayer(IPv6)
378 tx_ip2 = tx_pkt.getlayer(IP)
379
380 # verify if rx'ed packet has no SRH
381 self.assertFalse(rx_pkt.haslayer(IPv6ExtHdrSegmentRouting))
382
383 # the whole rx_ip pkt should be equal to tx_ip2
384 # except for the ttl field and ip checksum
385 # -> adjust tx'ed ttl to expected ttl
386 tx_ip2.ttl = tx_ip2.ttl - 1
387 # -> set tx'ed ip checksum to None and let scapy recompute
388 tx_ip2.chksum = None
389 # read back the pkt (with str()) to force computing these fields
390 # probably other ways to accomplish this are possible
391 tx_ip2 = IP(scapy.compat.raw(tx_ip2))
392
393 self.assertEqual(rx_ip, tx_ip2)
394
395 self.logger.debug("packet verification: SUCCESS")
396
397 def compare_rx_tx_packet_End_AD_IPv4_in(self, tx_pkt, rx_pkt):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200398 """Compare input and output packet after passing End.AD
Francois Claded7c62a2020-04-03 11:33:02 +0200399
400 :param tx_pkt: transmitted packet
401 :param rx_pkt: received packet
402 """
403
404 # get first (outer) IPv6 header of rx'ed packet
405 rx_ip = rx_pkt.getlayer(IPv6)
406 # received ip.src should be equal to SR Policy source
407 self.assertEqual(rx_ip.src, self.src_addr)
408 # received ip.dst should be equal to expected sidlist next segment
409 self.assertEqual(rx_ip.dst, self.sid_list[self.test_sid_index + 1])
410
411 # rx'ed packet should have SRH
412 self.assertTrue(rx_pkt.haslayer(IPv6ExtHdrSegmentRouting))
413
414 # get SRH
415 rx_srh = rx_pkt.getlayer(IPv6ExtHdrSegmentRouting)
416 # rx'ed seglist should be equal to SID-list in reversed order
417 self.assertEqual(rx_srh.addresses, self.sid_list[::-1])
418 # segleft should be equal to previous segleft value minus 1
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200419 self.assertEqual(rx_srh.segleft, len(self.sid_list) - self.test_sid_index - 2)
Francois Claded7c62a2020-04-03 11:33:02 +0200420 # lastentry should be equal to the SID-list length minus 1
421 self.assertEqual(rx_srh.lastentry, len(self.sid_list) - 1)
422
423 # the whole rx'ed pkt beyond SRH should be equal to tx'ed pkt
424 # except for the ttl field and ip checksum
425 tx_ip = tx_pkt.getlayer(IP)
426 # -> adjust tx'ed ttl to expected ttl
427 tx_ip.ttl = tx_ip.ttl - 1
428 # -> set tx'ed ip checksum to None and let scapy recompute
429 tx_ip.chksum = None
430 # -> read back the pkt (with str()) to force computing these fields
431 # probably other ways to accomplish this are possible
432 self.assertEqual(rx_srh.payload, IP(scapy.compat.raw(tx_ip)))
433
434 self.logger.debug("packet verification: SUCCESS")
435
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200436 def create_stream(self, src_if, dst_if, packet_header, packet_sizes, count):
Francois Claded7c62a2020-04-03 11:33:02 +0200437 """Create SRv6 input packet stream for defined interface.
438
439 :param VppInterface src_if: Interface to create packet stream for
440 :param VppInterface dst_if: destination interface of packet stream
441 :param packet_header: Layer3 scapy packet headers,
442 L2 is added when not provided,
443 Raw(payload) with packet_info is added
444 :param list packet_sizes: packet stream pckt sizes,sequentially applied
445 to packets in stream have
446 :param int count: number of packets in packet stream
447 :return: list of packets
448 """
449 self.logger.info("Creating packets")
450 pkts = []
451 for i in range(0, count - 1):
452 payload_info = self.create_packet_info(src_if, dst_if)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200453 self.logger.debug("Creating packet with index %d" % (payload_info.index))
Francois Claded7c62a2020-04-03 11:33:02 +0200454 payload = self.info_to_payload(payload_info)
455 # add L2 header if not yet provided in packet_header
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200456 if packet_header.getlayer(0).name == "Ethernet":
Francois Claded7c62a2020-04-03 11:33:02 +0200457 p = packet_header / Raw(payload)
458 else:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200459 p = (
460 Ether(dst=src_if.local_mac, src=src_if.remote_mac)
461 / packet_header
462 / Raw(payload)
463 )
Francois Claded7c62a2020-04-03 11:33:02 +0200464 size = packet_sizes[i % len(packet_sizes)]
465 self.logger.debug("Packet size %d" % (size))
466 self.extend_packet(p, size)
467 # we need to store the packet with the automatic fields computed
468 # read back the dumped packet (with str())
469 # to force computing these fields
470 # probably other ways are possible
471 p = Ether(scapy.compat.raw(p))
472 payload_info.data = p.copy()
473 self.logger.debug(ppp("Created packet:", p))
474 pkts.append(p)
475 self.logger.info("Done creating packets")
476 return pkts
477
478 def send_and_verify_pkts(self, input, pkts, output, compare_func):
479 """Send packets and verify received packets using compare_func
480
481 :param input: ingress interface of DUT
482 :param pkts: list of packets to transmit
483 :param output: egress interface of DUT
484 :param compare_func: function to compare in and out packets
485 """
486 # add traffic stream to input interface
487 input.add_stream(pkts)
488
489 # enable capture on all interfaces
490 self.pg_enable_capture(self.pg_interfaces)
491
492 # start traffic
493 self.logger.info("Starting traffic")
494 self.pg_start()
495
496 # get output capture
497 self.logger.info("Getting packet capture")
498 capture = output.get_capture()
499
500 # assert nothing was captured on input interface
501 # input.assert_nothing_captured()
502
503 # verify captured packets
504 self.verify_captured_pkts(output, capture, compare_func)
505
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200506 def create_packet_header_IPv6(
507 self, saddr="1234::1", daddr="4321::1", sport=1234, dport=1234
508 ):
Francois Claded7c62a2020-04-03 11:33:02 +0200509 """Create packet header: IPv6 header, UDP header
510
511 :param dst: IPv6 destination address
512
513 IPv6 source address is 1234::1
514 IPv6 destination address is 4321::1
515 UDP source port and destination port are 1234
516 """
517
518 p = IPv6(src=saddr, dst=daddr) / UDP(sport=sport, dport=dport)
519 return p
520
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200521 def create_packet_header_IPv6_SRH_IPv6(
522 self,
523 srcaddr,
524 sidlist,
525 segleft,
526 insrc="1234::1",
527 indst="4321::1",
528 sport=1234,
529 dport=1234,
530 ):
Francois Claded7c62a2020-04-03 11:33:02 +0200531 """Create packet header: IPv6 encapsulated in SRv6:
532 IPv6 header with SRH, IPv6 header, UDP header
533
534 :param int srcaddr: outer source address
535 :param list sidlist: segment list of outer IPv6 SRH
536 :param int segleft: segments-left field of outer IPv6 SRH
537
538 Outer IPv6 source address is set to srcaddr
539 Outer IPv6 destination address is set to sidlist[segleft]
540 Inner IPv6 source addresses is 1234::1
541 Inner IPv6 destination address is 4321::1
542 UDP source port and destination port are 1234
543 """
544
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200545 p = (
546 IPv6(src=srcaddr, dst=sidlist[segleft])
547 / IPv6ExtHdrSegmentRouting(addresses=sidlist, segleft=segleft, nh=41)
548 / IPv6(src=insrc, dst=indst)
549 / UDP(sport=sport, dport=dport)
550 )
Francois Claded7c62a2020-04-03 11:33:02 +0200551 return p
552
553 def create_packet_header_IPv4(self):
554 """Create packet header: IPv4 header, UDP header
555
556 :param dst: IPv4 destination address
557
558 IPv4 source address is 123.1.1.1
559 IPv4 destination address is 124.1.1.1
560 UDP source port and destination port are 1234
561 """
562
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200563 p = IP(src="123.1.1.1", dst="124.1.1.1") / UDP(sport=1234, dport=1234)
Francois Claded7c62a2020-04-03 11:33:02 +0200564 return p
565
566 def create_packet_header_IPv6_SRH_IPv4(self, srcaddr, sidlist, segleft):
567 """Create packet header: IPv4 encapsulated in SRv6:
568 IPv6 header with SRH, IPv4 header, UDP header
569
570 :param int srcaddr: outer source address
571 :param list sidlist: segment list of outer IPv6 SRH
572 :param int segleft: segments-left field of outer IPv6 SRH
573
574 Outer IPv6 source address is set to srcaddr
575 Outer IPv6 destination address is set to sidlist[segleft]
576 Inner IPv4 source address is 123.1.1.1
577 Inner IPv4 destination address is 124.1.1.1
578 UDP source port and destination port are 1234
579 """
580
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200581 p = (
582 IPv6(src=srcaddr, dst=sidlist[segleft])
583 / IPv6ExtHdrSegmentRouting(addresses=sidlist, segleft=segleft, nh=4)
584 / IP(src="123.1.1.1", dst="124.1.1.1")
585 / UDP(sport=1234, dport=1234)
586 )
Francois Claded7c62a2020-04-03 11:33:02 +0200587 return p
588
589 def get_payload_info(self, packet):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200590 """Extract the payload_info from the packet"""
Francois Claded7c62a2020-04-03 11:33:02 +0200591 # in most cases, payload_info is in packet[Raw]
592 # but packet[Raw] gives the complete payload
593 # (incl L2 header) for the T.Encaps L2 case
594 try:
595 payload_info = self.payload_to_info(packet[Raw])
596
597 except:
598 # remote L2 header from packet[Raw]:
599 # take packet[Raw], convert it to an Ether layer
600 # and then extract Raw from it
601 payload_info = self.payload_to_info(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200602 Ether(scapy.compat.raw(packet[Raw]))[Raw]
603 )
Francois Claded7c62a2020-04-03 11:33:02 +0200604
605 return payload_info
606
607 def verify_captured_pkts(self, dst_if, capture, compare_func):
608 """
609 Verify captured packet stream for specified interface.
610 Compare ingress with egress packets using the specified compare fn
611
612 :param dst_if: egress interface of DUT
613 :param capture: captured packets
614 :param compare_func: function to compare in and out packet
615 """
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200616 self.logger.info(
617 "Verifying capture on interface %s using function %s"
618 % (dst_if.name, compare_func.__name__)
619 )
Francois Claded7c62a2020-04-03 11:33:02 +0200620
621 last_info = dict()
622 for i in self.pg_interfaces:
623 last_info[i.sw_if_index] = None
624 dst_sw_if_index = dst_if.sw_if_index
625
626 for packet in capture:
627 try:
628 # extract payload_info from packet's payload
629 payload_info = self.get_payload_info(packet)
630 packet_index = payload_info.index
631
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200632 self.logger.debug("Verifying packet with index %d" % (packet_index))
Francois Claded7c62a2020-04-03 11:33:02 +0200633 # packet should have arrived on the expected interface
634 self.assertEqual(payload_info.dst, dst_sw_if_index)
635 self.logger.debug(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200636 "Got packet on interface %s: src=%u (idx=%u)"
637 % (dst_if.name, payload_info.src, packet_index)
638 )
Francois Claded7c62a2020-04-03 11:33:02 +0200639
640 # search for payload_info with same src and dst if_index
641 # this will give us the transmitted packet
642 next_info = self.get_next_packet_info_for_interface2(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200643 payload_info.src, dst_sw_if_index, last_info[payload_info.src]
644 )
Francois Claded7c62a2020-04-03 11:33:02 +0200645 last_info[payload_info.src] = next_info
646 # next_info should not be None
647 self.assertTrue(next_info is not None)
648 # index of tx and rx packets should be equal
649 self.assertEqual(packet_index, next_info.index)
650 # data field of next_info contains the tx packet
651 txed_packet = next_info.data
652
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200653 self.logger.debug(
654 ppp("Transmitted packet:", txed_packet)
655 ) # ppp=Pretty Print Packet
Francois Claded7c62a2020-04-03 11:33:02 +0200656
657 self.logger.debug(ppp("Received packet:", packet))
658
659 # compare rcvd packet with expected packet using compare_func
660 compare_func(txed_packet, packet)
661
662 except:
663 self.logger.error(ppp("Unexpected or invalid packet:", packet))
664 raise
665
666 # have all expected packets arrived?
667 for i in self.pg_interfaces:
668 remaining_packet = self.get_next_packet_info_for_interface2(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200669 i.sw_if_index, dst_sw_if_index, last_info[i.sw_if_index]
670 )
671 self.assertTrue(
672 remaining_packet is None,
673 "Interface %s: Packet expected from interface %s "
674 "didn't arrive" % (dst_if.name, i.name),
675 )
Francois Claded7c62a2020-04-03 11:33:02 +0200676
677
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200678if __name__ == "__main__":
Francois Claded7c62a2020-04-03 11:33:02 +0200679 unittest.main(testRunner=VppTestRunner)