blob: 69bdd313851e0a262253966639c5400505bd1c5b [file] [log] [blame]
Renato Botelho do Coutoead1e532019-10-31 13:31:07 -05001#!/usr/bin/env python3
Francois Cladbcd4c4a2018-07-26 13:11:09 -04002
3import unittest
Francois Cladbcd4c4a2018-07-26 13:11:09 -04004
Dave Wallace8800f732023-08-31 00:47:44 -04005from framework import VppTestCase
6from asfframework import VppTestRunner
Neale Rannsc0a93142018-09-05 15:42:26 -07007from vpp_ip import DpoProto
Dave Wallace8800f732023-08-31 00:47:44 -04008from vpp_ip_route import VppIpRoute, VppRoutePath
9
Francois Cladbcd4c4a2018-07-26 13:11:09 -040010
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -070011import scapy.compat
Francois Cladbcd4c4a2018-07-26 13:11:09 -040012from 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
Francois Cladbcd4c4a2018-07-26 13:11:09 -040017from util import ppp
Dmitry Valter34fa0ce2024-03-11 10:38:46 +000018from config import config
Francois Cladbcd4c4a2018-07-26 13:11:09 -040019
20
Dmitry Valter34fa0ce2024-03-11 10:38:46 +000021@unittest.skipIf("srv6-ad" in config.excluded_plugins, "Exclude srv6-ad plugin tests")
Tianyu Li6d95f8c2022-02-25 05:51:10 +000022class TestSRv6Ad(VppTestCase):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020023 """SRv6 Dynamic Proxy plugin Test Case"""
Francois Cladbcd4c4a2018-07-26 13:11:09 -040024
25 @classmethod
26 def setUpClass(self):
Tianyu Li6d95f8c2022-02-25 05:51:10 +000027 super(TestSRv6Ad, self).setUpClass()
Francois Cladbcd4c4a2018-07-26 13:11:09 -040028
Paul Vinciguerra7f9b7f92019-03-12 19:23:27 -070029 @classmethod
30 def tearDownClass(cls):
Tianyu Li6d95f8c2022-02-25 05:51:10 +000031 super(TestSRv6Ad, cls).tearDownClass()
Paul Vinciguerra7f9b7f92019-03-12 19:23:27 -070032
Francois Cladbcd4c4a2018-07-26 13:11:09 -040033 def setUp(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020034 """Perform test setup before each test case."""
Tianyu Li6d95f8c2022-02-25 05:51:10 +000035 super(TestSRv6Ad, self).setUp()
Francois Cladbcd4c4a2018-07-26 13:11:09 -040036
37 # packet sizes, inclusive L2 overhead
38 self.pg_packet_sizes = [64, 512, 1518, 9018]
39
40 # reset packet_infos
41 self.reset_packet_infos()
42
43 def tearDown(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020044 """Clean up test setup after each test case."""
Francois Cladbcd4c4a2018-07-26 13:11:09 -040045 self.teardown_interfaces()
46
Tianyu Li6d95f8c2022-02-25 05:51:10 +000047 super(TestSRv6Ad, self).tearDown()
Francois Cladbcd4c4a2018-07-26 13:11:09 -040048
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020049 def configure_interface(
50 self, interface, ipv6=False, ipv4=False, ipv6_table_id=0, ipv4_table_id=0
51 ):
52 """Configure interface.
Francois Cladbcd4c4a2018-07-26 13:11:09 -040053 :param ipv6: configure IPv6 on interface
54 :param ipv4: configure IPv4 on interface
55 :param ipv6_table_id: FIB table_id for IPv6
56 :param ipv4_table_id: FIB table_id for IPv4
57 """
58 self.logger.debug("Configuring interface %s" % (interface.name))
59 if ipv6:
60 self.logger.debug("Configuring IPv6")
61 interface.set_table_ip6(ipv6_table_id)
62 interface.config_ip6()
63 interface.resolve_ndp(timeout=5)
64 if ipv4:
65 self.logger.debug("Configuring IPv4")
66 interface.set_table_ip4(ipv4_table_id)
67 interface.config_ip4()
68 interface.resolve_arp()
69 interface.admin_up()
70
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020071 def setup_interfaces(self, ipv6=[], ipv4=[], ipv6_table_id=[], ipv4_table_id=[]):
72 """Create and configure interfaces.
Francois Cladbcd4c4a2018-07-26 13:11:09 -040073
74 :param ipv6: list of interface IPv6 capabilities
75 :param ipv4: list of interface IPv4 capabilities
76 :param ipv6_table_id: list of intf IPv6 FIB table_ids
77 :param ipv4_table_id: list of intf IPv4 FIB table_ids
78 :returns: List of created interfaces.
79 """
80 # how many interfaces?
81 if len(ipv6):
82 count = len(ipv6)
83 else:
84 count = len(ipv4)
85 self.logger.debug("Creating and configuring %d interfaces" % (count))
86
87 # fill up ipv6 and ipv4 lists if needed
88 # not enabled (False) is the default
89 if len(ipv6) < count:
90 ipv6 += (count - len(ipv6)) * [False]
91 if len(ipv4) < count:
92 ipv4 += (count - len(ipv4)) * [False]
93
94 # fill up table_id lists if needed
95 # table_id 0 (global) is the default
96 if len(ipv6_table_id) < count:
97 ipv6_table_id += (count - len(ipv6_table_id)) * [0]
98 if len(ipv4_table_id) < count:
99 ipv4_table_id += (count - len(ipv4_table_id)) * [0]
100
101 # create 'count' pg interfaces
102 self.create_pg_interfaces(range(count))
103
104 # setup all interfaces
105 for i in range(count):
106 intf = self.pg_interfaces[i]
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200107 self.configure_interface(
108 intf, ipv6[i], ipv4[i], ipv6_table_id[i], ipv4_table_id[i]
109 )
Francois Cladbcd4c4a2018-07-26 13:11:09 -0400110
111 if any(ipv6):
112 self.logger.debug(self.vapi.cli("show ip6 neighbors"))
113 if any(ipv4):
Neale Rannscbe25aa2019-09-30 10:53:31 +0000114 self.logger.debug(self.vapi.cli("show ip4 neighbors"))
Francois Cladbcd4c4a2018-07-26 13:11:09 -0400115 self.logger.debug(self.vapi.cli("show interface"))
116 self.logger.debug(self.vapi.cli("show hardware"))
117
118 return self.pg_interfaces
119
120 def teardown_interfaces(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200121 """Unconfigure and bring down interface."""
Francois Cladbcd4c4a2018-07-26 13:11:09 -0400122 self.logger.debug("Tearing down interfaces")
123 # tear down all interfaces
124 # AFAIK they cannot be deleted
125 for i in self.pg_interfaces:
126 self.logger.debug("Tear down interface %s" % (i.name))
127 i.admin_down()
128 i.unconfig()
129 i.set_table_ip4(0)
130 i.set_table_ip6(0)
131
132 def test_SRv6_End_AD_IPv6(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200133 """Test SRv6 End.AD behavior with IPv6 traffic."""
134 self.src_addr = "a0::"
135 self.sid_list = ["a1::", "a2::a6", "a3::"]
Francois Cladbcd4c4a2018-07-26 13:11:09 -0400136 self.test_sid_index = 1
137
138 # send traffic to one destination interface
139 # source and destination interfaces are IPv6 only
140 self.setup_interfaces(ipv6=[True, True])
141
142 # configure route to next segment
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200143 route = VppIpRoute(
144 self,
145 self.sid_list[self.test_sid_index + 1],
146 128,
147 [
148 VppRoutePath(
149 self.pg0.remote_ip6,
150 self.pg0.sw_if_index,
151 proto=DpoProto.DPO_PROTO_IP6,
152 )
153 ],
154 )
Francois Cladbcd4c4a2018-07-26 13:11:09 -0400155 route.add_vpp_config()
156
157 # configure SRv6 localSID behavior
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200158 cli_str = (
159 "sr localsid address "
160 + self.sid_list[self.test_sid_index]
161 + " behavior end.ad"
162 + " nh "
163 + self.pg1.remote_ip6
164 + " oif "
165 + self.pg1.name
166 + " iif "
167 + self.pg1.name
168 )
Francois Cladbcd4c4a2018-07-26 13:11:09 -0400169 self.vapi.cli(cli_str)
170
171 # log the localsids
172 self.logger.debug(self.vapi.cli("show sr localsid"))
173
174 # send one packet per packet size
175 count = len(self.pg_packet_sizes)
176
177 # prepare IPv6 in SRv6 headers
178 packet_header1 = self.create_packet_header_IPv6_SRH_IPv6(
179 srcaddr=self.src_addr,
180 sidlist=self.sid_list[::-1],
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200181 segleft=len(self.sid_list) - self.test_sid_index - 1,
182 )
Francois Cladbcd4c4a2018-07-26 13:11:09 -0400183
184 # generate packets (pg0->pg1)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200185 pkts1 = self.create_stream(
186 self.pg0, self.pg1, packet_header1, self.pg_packet_sizes, count
187 )
Francois Cladbcd4c4a2018-07-26 13:11:09 -0400188
189 # send packets and verify received packets
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200190 self.send_and_verify_pkts(
191 self.pg0, pkts1, self.pg1, self.compare_rx_tx_packet_End_AD_IPv6_out
192 )
Francois Cladbcd4c4a2018-07-26 13:11:09 -0400193
194 # log the localsid counters
195 self.logger.info(self.vapi.cli("show sr localsid"))
196
197 # prepare IPv6 header for returning packets
198 packet_header2 = self.create_packet_header_IPv6()
199
200 # generate returning packets (pg1->pg0)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200201 pkts2 = self.create_stream(
202 self.pg1, self.pg0, packet_header2, self.pg_packet_sizes, count
203 )
Francois Cladbcd4c4a2018-07-26 13:11:09 -0400204
205 # send packets and verify received packets
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200206 self.send_and_verify_pkts(
207 self.pg1, pkts2, self.pg0, self.compare_rx_tx_packet_End_AD_IPv6_in
208 )
Francois Cladbcd4c4a2018-07-26 13:11:09 -0400209
210 # log the localsid counters
211 self.logger.info(self.vapi.cli("show sr localsid"))
212
213 # remove SRv6 localSIDs
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200214 cli_str = "sr localsid del address " + self.sid_list[self.test_sid_index]
Francois Cladbcd4c4a2018-07-26 13:11:09 -0400215 self.vapi.cli(cli_str)
216
217 # cleanup interfaces
218 self.teardown_interfaces()
219
220 def compare_rx_tx_packet_End_AD_IPv6_out(self, tx_pkt, rx_pkt):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200221 """Compare input and output packet after passing End.AD with IPv6
Francois Cladbcd4c4a2018-07-26 13:11:09 -0400222
223 :param tx_pkt: transmitted packet
224 :param rx_pkt: received packet
225 """
226
227 # get first (outer) IPv6 header of rx'ed packet
228 rx_ip = rx_pkt.getlayer(IPv6)
229
230 tx_ip = tx_pkt.getlayer(IPv6)
231 tx_ip2 = tx_pkt.getlayer(IPv6, 2)
232
233 # verify if rx'ed packet has no SRH
234 self.assertFalse(rx_pkt.haslayer(IPv6ExtHdrSegmentRouting))
235
236 # the whole rx_ip pkt should be equal to tx_ip2
237 # except for the hlim field
238 # -> adjust tx'ed hlim to expected hlim
239 tx_ip2.hlim = tx_ip2.hlim - 1
240
241 self.assertEqual(rx_ip, tx_ip2)
242
243 self.logger.debug("packet verification: SUCCESS")
244
245 def compare_rx_tx_packet_End_AD_IPv6_in(self, tx_pkt, rx_pkt):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200246 """Compare input and output packet after passing End.AD
Francois Cladbcd4c4a2018-07-26 13:11:09 -0400247
248 :param tx_pkt: transmitted packet
249 :param rx_pkt: received packet
250 """
251
252 # get first (outer) IPv6 header of rx'ed packet
253 rx_ip = rx_pkt.getlayer(IPv6)
254 # received ip.src should be equal to SR Policy source
255 self.assertEqual(rx_ip.src, self.src_addr)
256 # received ip.dst should be equal to expected sidlist next segment
257 self.assertEqual(rx_ip.dst, self.sid_list[self.test_sid_index + 1])
258
259 # rx'ed packet should have SRH
260 self.assertTrue(rx_pkt.haslayer(IPv6ExtHdrSegmentRouting))
261
262 # get SRH
263 rx_srh = rx_pkt.getlayer(IPv6ExtHdrSegmentRouting)
264 # rx'ed seglist should be equal to SID-list in reversed order
265 self.assertEqual(rx_srh.addresses, self.sid_list[::-1])
266 # segleft should be equal to previous segleft value minus 1
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200267 self.assertEqual(rx_srh.segleft, len(self.sid_list) - self.test_sid_index - 2)
Francois Cladbcd4c4a2018-07-26 13:11:09 -0400268 # lastentry should be equal to the SID-list length minus 1
269 self.assertEqual(rx_srh.lastentry, len(self.sid_list) - 1)
270
271 # the whole rx'ed pkt beyond SRH should be equal to tx'ed pkt
272 # except for the hop-limit field
273 tx_ip = tx_pkt.getlayer(IPv6)
274 # -> update tx'ed hlim to the expected hlim
275 tx_ip.hlim -= 1
276 # -> check payload
277 self.assertEqual(rx_srh.payload, tx_ip)
278
279 self.logger.debug("packet verification: SUCCESS")
280
281 def test_SRv6_End_AD_IPv4(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200282 """Test SRv6 End.AD behavior with IPv4 traffic."""
283 self.src_addr = "a0::"
284 self.sid_list = ["a1::", "a2::a4", "a3::"]
Francois Cladbcd4c4a2018-07-26 13:11:09 -0400285 self.test_sid_index = 1
286
287 # send traffic to one destination interface
288 # source and destination interfaces are IPv6 only
289 self.setup_interfaces(ipv6=[True, False], ipv4=[False, True])
290
291 # configure route to next segment
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200292 route = VppIpRoute(
293 self,
294 self.sid_list[self.test_sid_index + 1],
295 128,
296 [
297 VppRoutePath(
298 self.pg0.remote_ip6,
299 self.pg0.sw_if_index,
300 proto=DpoProto.DPO_PROTO_IP6,
301 )
302 ],
303 )
Francois Cladbcd4c4a2018-07-26 13:11:09 -0400304 route.add_vpp_config()
305
306 # configure SRv6 localSID behavior
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200307 cli_str = (
308 "sr localsid address "
309 + self.sid_list[self.test_sid_index]
310 + " behavior end.ad"
311 + " nh "
312 + self.pg1.remote_ip4
313 + " oif "
314 + self.pg1.name
315 + " iif "
316 + self.pg1.name
317 )
Francois Cladbcd4c4a2018-07-26 13:11:09 -0400318 self.vapi.cli(cli_str)
319
320 # log the localsids
321 self.logger.debug(self.vapi.cli("show sr localsid"))
322
323 # send one packet per packet size
324 count = len(self.pg_packet_sizes)
325
326 # prepare IPv4 in SRv6 headers
327 packet_header1 = self.create_packet_header_IPv6_SRH_IPv4(
328 srcaddr=self.src_addr,
329 sidlist=self.sid_list[::-1],
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200330 segleft=len(self.sid_list) - self.test_sid_index - 1,
331 )
Francois Cladbcd4c4a2018-07-26 13:11:09 -0400332
333 # generate packets (pg0->pg1)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200334 pkts1 = self.create_stream(
335 self.pg0, self.pg1, packet_header1, self.pg_packet_sizes, count
336 )
Francois Cladbcd4c4a2018-07-26 13:11:09 -0400337
338 # send packets and verify received packets
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200339 self.send_and_verify_pkts(
340 self.pg0, pkts1, self.pg1, self.compare_rx_tx_packet_End_AD_IPv4_out
341 )
Francois Cladbcd4c4a2018-07-26 13:11:09 -0400342
343 # log the localsid counters
344 self.logger.info(self.vapi.cli("show sr localsid"))
345
346 # prepare IPv6 header for returning packets
347 packet_header2 = self.create_packet_header_IPv4()
348
349 # generate returning packets (pg1->pg0)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200350 pkts2 = self.create_stream(
351 self.pg1, self.pg0, packet_header2, self.pg_packet_sizes, count
352 )
Francois Cladbcd4c4a2018-07-26 13:11:09 -0400353
354 # send packets and verify received packets
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200355 self.send_and_verify_pkts(
356 self.pg1, pkts2, self.pg0, self.compare_rx_tx_packet_End_AD_IPv4_in
357 )
Francois Cladbcd4c4a2018-07-26 13:11:09 -0400358
359 # log the localsid counters
360 self.logger.info(self.vapi.cli("show sr localsid"))
361
362 # remove SRv6 localSIDs
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200363 cli_str = "sr localsid del address " + self.sid_list[self.test_sid_index]
Francois Cladbcd4c4a2018-07-26 13:11:09 -0400364 self.vapi.cli(cli_str)
365
366 # cleanup interfaces
367 self.teardown_interfaces()
368
369 def compare_rx_tx_packet_End_AD_IPv4_out(self, tx_pkt, rx_pkt):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200370 """Compare input and output packet after passing End.AD with IPv4
Francois Cladbcd4c4a2018-07-26 13:11:09 -0400371
372 :param tx_pkt: transmitted packet
373 :param rx_pkt: received packet
374 """
375
376 # get IPv4 header of rx'ed packet
377 rx_ip = rx_pkt.getlayer(IP)
378
379 tx_ip = tx_pkt.getlayer(IPv6)
380 tx_ip2 = tx_pkt.getlayer(IP)
381
382 # verify if rx'ed packet has no SRH
383 self.assertFalse(rx_pkt.haslayer(IPv6ExtHdrSegmentRouting))
384
385 # the whole rx_ip pkt should be equal to tx_ip2
386 # except for the ttl field and ip checksum
387 # -> adjust tx'ed ttl to expected ttl
388 tx_ip2.ttl = tx_ip2.ttl - 1
389 # -> set tx'ed ip checksum to None and let scapy recompute
390 tx_ip2.chksum = None
391 # read back the pkt (with str()) to force computing these fields
392 # probably other ways to accomplish this are possible
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700393 tx_ip2 = IP(scapy.compat.raw(tx_ip2))
Francois Cladbcd4c4a2018-07-26 13:11:09 -0400394
395 self.assertEqual(rx_ip, tx_ip2)
396
397 self.logger.debug("packet verification: SUCCESS")
398
399 def compare_rx_tx_packet_End_AD_IPv4_in(self, tx_pkt, rx_pkt):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200400 """Compare input and output packet after passing End.AD
Francois Cladbcd4c4a2018-07-26 13:11:09 -0400401
402 :param tx_pkt: transmitted packet
403 :param rx_pkt: received packet
404 """
405
406 # get first (outer) IPv6 header of rx'ed packet
407 rx_ip = rx_pkt.getlayer(IPv6)
408 # received ip.src should be equal to SR Policy source
409 self.assertEqual(rx_ip.src, self.src_addr)
410 # received ip.dst should be equal to expected sidlist next segment
411 self.assertEqual(rx_ip.dst, self.sid_list[self.test_sid_index + 1])
412
413 # rx'ed packet should have SRH
414 self.assertTrue(rx_pkt.haslayer(IPv6ExtHdrSegmentRouting))
415
416 # get SRH
417 rx_srh = rx_pkt.getlayer(IPv6ExtHdrSegmentRouting)
418 # rx'ed seglist should be equal to SID-list in reversed order
419 self.assertEqual(rx_srh.addresses, self.sid_list[::-1])
420 # segleft should be equal to previous segleft value minus 1
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200421 self.assertEqual(rx_srh.segleft, len(self.sid_list) - self.test_sid_index - 2)
Francois Cladbcd4c4a2018-07-26 13:11:09 -0400422 # lastentry should be equal to the SID-list length minus 1
423 self.assertEqual(rx_srh.lastentry, len(self.sid_list) - 1)
424
425 # the whole rx'ed pkt beyond SRH should be equal to tx'ed pkt
426 # except for the ttl field and ip checksum
427 tx_ip = tx_pkt.getlayer(IP)
428 # -> adjust tx'ed ttl to expected ttl
429 tx_ip.ttl = tx_ip.ttl - 1
430 # -> set tx'ed ip checksum to None and let scapy recompute
431 tx_ip.chksum = None
432 # -> read back the pkt (with str()) to force computing these fields
433 # probably other ways to accomplish this are possible
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700434 self.assertEqual(rx_srh.payload, IP(scapy.compat.raw(tx_ip)))
Francois Cladbcd4c4a2018-07-26 13:11:09 -0400435
436 self.logger.debug("packet verification: SUCCESS")
437
Francois Clad42954242018-07-04 15:35:29 +0200438 def test_SRv6_End_AD_L2(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200439 """Test SRv6 End.AD behavior with L2 traffic."""
440 self.src_addr = "a0::"
441 self.sid_list = ["a1::", "a2::a4", "a3::"]
Francois Clad42954242018-07-04 15:35:29 +0200442 self.test_sid_index = 1
443
444 # send traffic to one destination interface
445 # source and destination interfaces are IPv6 only
446 self.setup_interfaces(ipv6=[True, False])
447
448 # configure route to next segment
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200449 route = VppIpRoute(
450 self,
451 self.sid_list[self.test_sid_index + 1],
452 128,
453 [
454 VppRoutePath(
455 self.pg0.remote_ip6,
456 self.pg0.sw_if_index,
457 proto=DpoProto.DPO_PROTO_IP6,
458 )
459 ],
460 )
Francois Clad42954242018-07-04 15:35:29 +0200461 route.add_vpp_config()
462
463 # configure SRv6 localSID behavior
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200464 cli_str = (
465 "sr localsid address "
466 + self.sid_list[self.test_sid_index]
467 + " behavior end.ad"
468 + " oif "
469 + self.pg1.name
470 + " iif "
471 + self.pg1.name
472 )
Francois Clad42954242018-07-04 15:35:29 +0200473 self.vapi.cli(cli_str)
474
475 # log the localsids
476 self.logger.debug(self.vapi.cli("show sr localsid"))
477
478 # send one packet per packet size
479 count = len(self.pg_packet_sizes)
480
481 # prepare L2 in SRv6 headers
482 packet_header1 = self.create_packet_header_IPv6_SRH_L2(
483 srcaddr=self.src_addr,
484 sidlist=self.sid_list[::-1],
485 segleft=len(self.sid_list) - self.test_sid_index - 1,
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200486 vlan=0,
487 )
Francois Clad42954242018-07-04 15:35:29 +0200488
489 # generate packets (pg0->pg1)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200490 pkts1 = self.create_stream(
491 self.pg0, self.pg1, packet_header1, self.pg_packet_sizes, count
492 )
Francois Clad42954242018-07-04 15:35:29 +0200493
494 # send packets and verify received packets
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200495 self.send_and_verify_pkts(
496 self.pg0, pkts1, self.pg1, self.compare_rx_tx_packet_End_AD_L2_out
497 )
Francois Clad42954242018-07-04 15:35:29 +0200498
499 # log the localsid counters
500 self.logger.info(self.vapi.cli("show sr localsid"))
501
502 # prepare L2 header for returning packets
503 packet_header2 = self.create_packet_header_L2()
504
505 # generate returning packets (pg1->pg0)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200506 pkts2 = self.create_stream(
507 self.pg1, self.pg0, packet_header2, self.pg_packet_sizes, count
508 )
Francois Clad42954242018-07-04 15:35:29 +0200509
510 # send packets and verify received packets
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200511 self.send_and_verify_pkts(
512 self.pg1, pkts2, self.pg0, self.compare_rx_tx_packet_End_AD_L2_in
513 )
Francois Clad42954242018-07-04 15:35:29 +0200514
515 # log the localsid counters
516 self.logger.info(self.vapi.cli("show sr localsid"))
517
518 # remove SRv6 localSIDs
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200519 cli_str = "sr localsid del address " + self.sid_list[self.test_sid_index]
Francois Clad42954242018-07-04 15:35:29 +0200520 self.vapi.cli(cli_str)
521
522 # cleanup interfaces
523 self.teardown_interfaces()
524
525 def compare_rx_tx_packet_End_AD_L2_out(self, tx_pkt, rx_pkt):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200526 """Compare input and output packet after passing End.AD with L2
Francois Clad42954242018-07-04 15:35:29 +0200527
528 :param tx_pkt: transmitted packet
529 :param rx_pkt: received packet
530 """
531
532 # get IPv4 header of rx'ed packet
533 rx_eth = rx_pkt.getlayer(Ether)
534
535 tx_ip = tx_pkt.getlayer(IPv6)
536 # we can't just get the 2nd Ether layer
537 # get the Raw content and dissect it as Ether
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700538 tx_eth1 = Ether(scapy.compat.raw(tx_pkt[Raw]))
Francois Clad42954242018-07-04 15:35:29 +0200539
540 # verify if rx'ed packet has no SRH
541 self.assertFalse(rx_pkt.haslayer(IPv6ExtHdrSegmentRouting))
542
543 # the whole rx_eth pkt should be equal to tx_eth1
544 self.assertEqual(rx_eth, tx_eth1)
545
546 self.logger.debug("packet verification: SUCCESS")
547
548 def compare_rx_tx_packet_End_AD_L2_in(self, tx_pkt, rx_pkt):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200549 """Compare input and output packet after passing End.AD
Francois Clad42954242018-07-04 15:35:29 +0200550
551 :param tx_pkt: transmitted packet
552 :param rx_pkt: received packet
553 """
554
555 ####
556 # get first (outer) IPv6 header of rx'ed packet
557 rx_ip = rx_pkt.getlayer(IPv6)
558 # received ip.src should be equal to SR Policy source
559 self.assertEqual(rx_ip.src, self.src_addr)
560 # received ip.dst should be equal to expected sidlist next segment
561 self.assertEqual(rx_ip.dst, self.sid_list[self.test_sid_index + 1])
562
563 # rx'ed packet should have SRH
564 self.assertTrue(rx_pkt.haslayer(IPv6ExtHdrSegmentRouting))
565
566 # get SRH
567 rx_srh = rx_pkt.getlayer(IPv6ExtHdrSegmentRouting)
568 # rx'ed seglist should be equal to SID-list in reversed order
569 self.assertEqual(rx_srh.addresses, self.sid_list[::-1])
570 # segleft should be equal to previous segleft value minus 1
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200571 self.assertEqual(rx_srh.segleft, len(self.sid_list) - self.test_sid_index - 2)
Francois Clad42954242018-07-04 15:35:29 +0200572 # lastentry should be equal to the SID-list length minus 1
573 self.assertEqual(rx_srh.lastentry, len(self.sid_list) - 1)
574
575 # the whole rx'ed pkt beyond SRH should be equal to tx'ed pkt
576 tx_ether = tx_pkt.getlayer(Ether)
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700577 self.assertEqual(Ether(scapy.compat.raw(rx_srh.payload)), tx_ether)
Francois Clad42954242018-07-04 15:35:29 +0200578
579 self.logger.debug("packet verification: SUCCESS")
580
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200581 def create_stream(self, src_if, dst_if, packet_header, packet_sizes, count):
Francois Cladbcd4c4a2018-07-26 13:11:09 -0400582 """Create SRv6 input packet stream for defined interface.
583
584 :param VppInterface src_if: Interface to create packet stream for
585 :param VppInterface dst_if: destination interface of packet stream
586 :param packet_header: Layer3 scapy packet headers,
587 L2 is added when not provided,
588 Raw(payload) with packet_info is added
589 :param list packet_sizes: packet stream pckt sizes,sequentially applied
590 to packets in stream have
591 :param int count: number of packets in packet stream
592 :return: list of packets
593 """
594 self.logger.info("Creating packets")
595 pkts = []
596 for i in range(0, count - 1):
597 payload_info = self.create_packet_info(src_if, dst_if)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200598 self.logger.debug("Creating packet with index %d" % (payload_info.index))
Francois Cladbcd4c4a2018-07-26 13:11:09 -0400599 payload = self.info_to_payload(payload_info)
600 # add L2 header if not yet provided in packet_header
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200601 if packet_header.getlayer(0).name == "Ethernet":
Francois Cladbcd4c4a2018-07-26 13:11:09 -0400602 p = packet_header / Raw(payload)
603 else:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200604 p = (
605 Ether(dst=src_if.local_mac, src=src_if.remote_mac)
606 / packet_header
607 / Raw(payload)
608 )
Francois Cladbcd4c4a2018-07-26 13:11:09 -0400609 size = packet_sizes[i % len(packet_sizes)]
610 self.logger.debug("Packet size %d" % (size))
611 self.extend_packet(p, size)
612 # we need to store the packet with the automatic fields computed
613 # read back the dumped packet (with str())
614 # to force computing these fields
615 # probably other ways are possible
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700616 p = Ether(scapy.compat.raw(p))
Francois Cladbcd4c4a2018-07-26 13:11:09 -0400617 payload_info.data = p.copy()
618 self.logger.debug(ppp("Created packet:", p))
619 pkts.append(p)
620 self.logger.info("Done creating packets")
621 return pkts
622
623 def send_and_verify_pkts(self, input, pkts, output, compare_func):
624 """Send packets and verify received packets using compare_func
625
626 :param input: ingress interface of DUT
627 :param pkts: list of packets to transmit
628 :param output: egress interface of DUT
629 :param compare_func: function to compare in and out packets
630 """
631 # add traffic stream to input interface
632 input.add_stream(pkts)
633
634 # enable capture on all interfaces
635 self.pg_enable_capture(self.pg_interfaces)
636
637 # start traffic
638 self.logger.info("Starting traffic")
639 self.pg_start()
640
641 # get output capture
642 self.logger.info("Getting packet capture")
643 capture = output.get_capture()
644
645 # assert nothing was captured on input interface
646 # input.assert_nothing_captured()
647
648 # verify captured packets
649 self.verify_captured_pkts(output, capture, compare_func)
650
651 def create_packet_header_IPv6(self):
652 """Create packet header: IPv6 header, UDP header
653
654 :param dst: IPv6 destination address
655
656 IPv6 source address is 1234::1
657 IPv6 destination address is 4321::1
658 UDP source port and destination port are 1234
659 """
660
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200661 p = IPv6(src="1234::1", dst="4321::1") / UDP(sport=1234, dport=1234)
Francois Cladbcd4c4a2018-07-26 13:11:09 -0400662 return p
663
664 def create_packet_header_IPv6_SRH_IPv6(self, srcaddr, sidlist, segleft):
665 """Create packet header: IPv6 encapsulated in SRv6:
666 IPv6 header with SRH, IPv6 header, UDP header
667
668 :param int srcaddr: outer source address
669 :param list sidlist: segment list of outer IPv6 SRH
670 :param int segleft: segments-left field of outer IPv6 SRH
671
672 Outer IPv6 source address is set to srcaddr
673 Outer IPv6 destination address is set to sidlist[segleft]
674 Inner IPv6 source addresses is 1234::1
675 Inner IPv6 destination address is 4321::1
676 UDP source port and destination port are 1234
677 """
678
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200679 p = (
680 IPv6(src=srcaddr, dst=sidlist[segleft])
681 / IPv6ExtHdrSegmentRouting(addresses=sidlist, segleft=segleft, nh=41)
682 / IPv6(src="1234::1", dst="4321::1")
683 / UDP(sport=1234, dport=1234)
684 )
Francois Cladbcd4c4a2018-07-26 13:11:09 -0400685 return p
686
687 def create_packet_header_IPv4(self):
688 """Create packet header: IPv4 header, UDP header
689
690 :param dst: IPv4 destination address
691
692 IPv4 source address is 123.1.1.1
693 IPv4 destination address is 124.1.1.1
694 UDP source port and destination port are 1234
695 """
696
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200697 p = IP(src="123.1.1.1", dst="124.1.1.1") / UDP(sport=1234, dport=1234)
Francois Cladbcd4c4a2018-07-26 13:11:09 -0400698 return p
699
700 def create_packet_header_IPv6_SRH_IPv4(self, srcaddr, sidlist, segleft):
701 """Create packet header: IPv4 encapsulated in SRv6:
702 IPv6 header with SRH, IPv4 header, UDP header
703
704 :param int srcaddr: outer source address
705 :param list sidlist: segment list of outer IPv6 SRH
706 :param int segleft: segments-left field of outer IPv6 SRH
707
708 Outer IPv6 source address is set to srcaddr
709 Outer IPv6 destination address is set to sidlist[segleft]
710 Inner IPv4 source address is 123.1.1.1
711 Inner IPv4 destination address is 124.1.1.1
712 UDP source port and destination port are 1234
713 """
714
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200715 p = (
716 IPv6(src=srcaddr, dst=sidlist[segleft])
717 / IPv6ExtHdrSegmentRouting(addresses=sidlist, segleft=segleft, nh=4)
718 / IP(src="123.1.1.1", dst="124.1.1.1")
719 / UDP(sport=1234, dport=1234)
720 )
Francois Cladbcd4c4a2018-07-26 13:11:09 -0400721 return p
722
Francois Clad42954242018-07-04 15:35:29 +0200723 def create_packet_header_L2(self, vlan=0):
724 """Create packet header: L2 header
725
726 :param vlan: if vlan!=0 then add 802.1q header
727 """
728 # Note: the dst addr ('00:55:44:33:22:11') is used in
729 # the compare function compare_rx_tx_packet_T_Encaps_L2
730 # to detect presence of L2 in SRH payload
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200731 p = Ether(src="00:11:22:33:44:55", dst="00:55:44:33:22:11")
Francois Clad42954242018-07-04 15:35:29 +0200732 etype = 0x8137 # IPX
733 if vlan:
734 # add 802.1q layer
735 p /= Dot1Q(vlan=vlan, type=etype)
736 else:
737 p.type = etype
738 return p
739
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200740 def create_packet_header_IPv6_SRH_L2(self, srcaddr, sidlist, segleft, vlan=0):
Francois Clad42954242018-07-04 15:35:29 +0200741 """Create packet header: L2 encapsulated in SRv6:
742 IPv6 header with SRH, L2
743
744 :param int srcaddr: IPv6 source address
745 :param list sidlist: segment list of outer IPv6 SRH
746 :param int segleft: segments-left field of outer IPv6 SRH
747 :param vlan: L2 vlan; if vlan!=0 then add 802.1q header
748
749 IPv6 source address is set to srcaddr
750 IPv6 destination address is set to sidlist[segleft]
751 """
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200752 eth = Ether(src="00:11:22:33:44:55", dst="00:55:44:33:22:11")
Francois Clad42954242018-07-04 15:35:29 +0200753 etype = 0x8137 # IPX
754 if vlan:
755 # add 802.1q layer
756 eth /= Dot1Q(vlan=vlan, type=etype)
757 else:
758 eth.type = etype
759
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200760 p = (
761 IPv6(src=srcaddr, dst=sidlist[segleft])
762 / IPv6ExtHdrSegmentRouting(addresses=sidlist, segleft=segleft, nh=143)
763 / eth
764 )
Francois Clad42954242018-07-04 15:35:29 +0200765 return p
766
Francois Cladbcd4c4a2018-07-26 13:11:09 -0400767 def get_payload_info(self, packet):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200768 """Extract the payload_info from the packet"""
Francois Cladbcd4c4a2018-07-26 13:11:09 -0400769 # in most cases, payload_info is in packet[Raw]
770 # but packet[Raw] gives the complete payload
771 # (incl L2 header) for the T.Encaps L2 case
772 try:
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800773 payload_info = self.payload_to_info(packet[Raw])
Francois Cladbcd4c4a2018-07-26 13:11:09 -0400774
775 except:
776 # remote L2 header from packet[Raw]:
777 # take packet[Raw], convert it to an Ether layer
778 # and then extract Raw from it
779 payload_info = self.payload_to_info(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200780 Ether(scapy.compat.raw(packet[Raw]))[Raw]
781 )
Francois Cladbcd4c4a2018-07-26 13:11:09 -0400782
783 return payload_info
784
785 def verify_captured_pkts(self, dst_if, capture, compare_func):
786 """
787 Verify captured packet stream for specified interface.
788 Compare ingress with egress packets using the specified compare fn
789
790 :param dst_if: egress interface of DUT
791 :param capture: captured packets
792 :param compare_func: function to compare in and out packet
793 """
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200794 self.logger.info(
795 "Verifying capture on interface %s using function %s"
796 % (dst_if.name, compare_func.__name__)
797 )
Francois Cladbcd4c4a2018-07-26 13:11:09 -0400798
799 last_info = dict()
800 for i in self.pg_interfaces:
801 last_info[i.sw_if_index] = None
802 dst_sw_if_index = dst_if.sw_if_index
803
804 for packet in capture:
805 try:
806 # extract payload_info from packet's payload
807 payload_info = self.get_payload_info(packet)
808 packet_index = payload_info.index
809
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200810 self.logger.debug("Verifying packet with index %d" % (packet_index))
Francois Cladbcd4c4a2018-07-26 13:11:09 -0400811 # packet should have arrived on the expected interface
812 self.assertEqual(payload_info.dst, dst_sw_if_index)
813 self.logger.debug(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200814 "Got packet on interface %s: src=%u (idx=%u)"
815 % (dst_if.name, payload_info.src, packet_index)
816 )
Francois Cladbcd4c4a2018-07-26 13:11:09 -0400817
818 # search for payload_info with same src and dst if_index
819 # this will give us the transmitted packet
820 next_info = self.get_next_packet_info_for_interface2(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200821 payload_info.src, dst_sw_if_index, last_info[payload_info.src]
822 )
Francois Cladbcd4c4a2018-07-26 13:11:09 -0400823 last_info[payload_info.src] = next_info
824 # next_info should not be None
825 self.assertTrue(next_info is not None)
826 # index of tx and rx packets should be equal
827 self.assertEqual(packet_index, next_info.index)
828 # data field of next_info contains the tx packet
829 txed_packet = next_info.data
830
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200831 self.logger.debug(
832 ppp("Transmitted packet:", txed_packet)
833 ) # ppp=Pretty Print Packet
Francois Cladbcd4c4a2018-07-26 13:11:09 -0400834
835 self.logger.debug(ppp("Received packet:", packet))
836
837 # compare rcvd packet with expected packet using compare_func
838 compare_func(txed_packet, packet)
839
840 except:
Francois Cladbcd4c4a2018-07-26 13:11:09 -0400841 self.logger.error(ppp("Unexpected or invalid packet:", packet))
842 raise
843
844 # have all expected packets arrived?
845 for i in self.pg_interfaces:
846 remaining_packet = self.get_next_packet_info_for_interface2(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200847 i.sw_if_index, dst_sw_if_index, last_info[i.sw_if_index]
848 )
849 self.assertTrue(
850 remaining_packet is None,
851 "Interface %s: Packet expected from interface %s "
852 "didn't arrive" % (dst_if.name, i.name),
853 )
Francois Cladbcd4c4a2018-07-26 13:11:09 -0400854
855
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200856if __name__ == "__main__":
Francois Cladbcd4c4a2018-07-26 13:11:09 -0400857 unittest.main(testRunner=VppTestRunner)