blob: f1e5109f4722e93262510bbe5f5e92089af7692b [file] [log] [blame]
Francois Claded7c62a2020-04-03 11:33:02 +02001#!/usr/bin/env python3
2
3import unittest
Francois Claded7c62a2020-04-03 11:33:02 +02004
Dave Wallace8800f732023-08-31 00:47:44 -04005from framework import VppTestCase
6from asfframework import VppTestRunner
Francois Claded7c62a2020-04-03 11:33:02 +02007from vpp_ip import DpoProto
Dave Wallace8800f732023-08-31 00:47:44 -04008from vpp_ip_route import VppIpRoute, VppRoutePath
Francois Claded7c62a2020-04-03 11:33:02 +02009
10import scapy.compat
11from scapy.packet import Raw
Dave Wallace8800f732023-08-31 00:47:44 -040012from scapy.layers.l2 import Ether
Francois Claded7c62a2020-04-03 11:33:02 +020013from scapy.layers.inet6 import IPv6, UDP, IPv6ExtHdrSegmentRouting
14from scapy.layers.inet import IP, UDP
15
16from util import ppp
Dmitry Valter34fa0ce2024-03-11 10:38:46 +000017from config import config
Francois Claded7c62a2020-04-03 11:33:02 +020018
19
Dmitry Valter34fa0ce2024-03-11 10:38:46 +000020@unittest.skipIf(
21 "srv6-ad-flow" in config.excluded_plugins, "Exclude srv6-ad-flow plugin tests"
22)
Tianyu Li6d95f8c2022-02-25 05:51:10 +000023class TestSRv6AdFlow(VppTestCase):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020024 """SRv6 Flow-based Dynamic Proxy plugin Test Case"""
Francois Claded7c62a2020-04-03 11:33:02 +020025
26 @classmethod
27 def setUpClass(self):
Tianyu Li6d95f8c2022-02-25 05:51:10 +000028 super(TestSRv6AdFlow, self).setUpClass()
Francois Claded7c62a2020-04-03 11:33:02 +020029
30 @classmethod
31 def tearDownClass(cls):
Tianyu Li6d95f8c2022-02-25 05:51:10 +000032 super(TestSRv6AdFlow, cls).tearDownClass()
Francois Claded7c62a2020-04-03 11:33:02 +020033
34 def setUp(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020035 """Perform test setup before each test case."""
Tianyu Li6d95f8c2022-02-25 05:51:10 +000036 super(TestSRv6AdFlow, self).setUp()
Francois Claded7c62a2020-04-03 11:33:02 +020037
38 # packet sizes, inclusive L2 overhead
39 self.pg_packet_sizes = [64, 512, 1518, 9018]
40
41 # reset packet_infos
42 self.reset_packet_infos()
43
44 def tearDown(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020045 """Clean up test setup after each test case."""
Francois Claded7c62a2020-04-03 11:33:02 +020046 self.teardown_interfaces()
47
Tianyu Li6d95f8c2022-02-25 05:51:10 +000048 super(TestSRv6AdFlow, self).tearDown()
Francois Claded7c62a2020-04-03 11:33:02 +020049
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020050 def configure_interface(
51 self, interface, ipv6=False, ipv4=False, ipv6_table_id=0, ipv4_table_id=0
52 ):
53 """Configure interface.
Francois Claded7c62a2020-04-03 11:33:02 +020054 :param ipv6: configure IPv6 on interface
55 :param ipv4: configure IPv4 on interface
56 :param ipv6_table_id: FIB table_id for IPv6
57 :param ipv4_table_id: FIB table_id for IPv4
58 """
59 self.logger.debug("Configuring interface %s" % (interface.name))
60 if ipv6:
61 self.logger.debug("Configuring IPv6")
62 interface.set_table_ip6(ipv6_table_id)
63 interface.config_ip6()
64 interface.resolve_ndp(timeout=5)
65 if ipv4:
66 self.logger.debug("Configuring IPv4")
67 interface.set_table_ip4(ipv4_table_id)
68 interface.config_ip4()
69 interface.resolve_arp()
70 interface.admin_up()
71
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020072 def setup_interfaces(self, ipv6=[], ipv4=[], ipv6_table_id=[], ipv4_table_id=[]):
73 """Create and configure interfaces.
Francois Claded7c62a2020-04-03 11:33:02 +020074
75 :param ipv6: list of interface IPv6 capabilities
76 :param ipv4: list of interface IPv4 capabilities
77 :param ipv6_table_id: list of intf IPv6 FIB table_ids
78 :param ipv4_table_id: list of intf IPv4 FIB table_ids
79 :returns: List of created interfaces.
80 """
81 # how many interfaces?
82 if len(ipv6):
83 count = len(ipv6)
84 else:
85 count = len(ipv4)
86 self.logger.debug("Creating and configuring %d interfaces" % (count))
87
88 # fill up ipv6 and ipv4 lists if needed
89 # not enabled (False) is the default
90 if len(ipv6) < count:
91 ipv6 += (count - len(ipv6)) * [False]
92 if len(ipv4) < count:
93 ipv4 += (count - len(ipv4)) * [False]
94
95 # fill up table_id lists if needed
96 # table_id 0 (global) is the default
97 if len(ipv6_table_id) < count:
98 ipv6_table_id += (count - len(ipv6_table_id)) * [0]
99 if len(ipv4_table_id) < count:
100 ipv4_table_id += (count - len(ipv4_table_id)) * [0]
101
102 # create 'count' pg interfaces
103 self.create_pg_interfaces(range(count))
104
105 # setup all interfaces
106 for i in range(count):
107 intf = self.pg_interfaces[i]
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200108 self.configure_interface(
109 intf, ipv6[i], ipv4[i], ipv6_table_id[i], ipv4_table_id[i]
110 )
Francois Claded7c62a2020-04-03 11:33:02 +0200111
112 if any(ipv6):
113 self.logger.debug(self.vapi.cli("show ip6 neighbors"))
114 if any(ipv4):
115 self.logger.debug(self.vapi.cli("show ip4 neighbors"))
116 self.logger.debug(self.vapi.cli("show interface"))
117 self.logger.debug(self.vapi.cli("show hardware"))
118
119 return self.pg_interfaces
120
121 def teardown_interfaces(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200122 """Unconfigure and bring down interface."""
Francois Claded7c62a2020-04-03 11:33:02 +0200123 self.logger.debug("Tearing down interfaces")
124 # tear down all interfaces
125 # AFAIK they cannot be deleted
126 for i in self.pg_interfaces:
127 self.logger.debug("Tear down interface %s" % (i.name))
128 i.admin_down()
129 i.unconfig()
130 i.set_table_ip4(0)
131 i.set_table_ip6(0)
132
133 def test_SRv6_End_AD_IPv6(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200134 """Test SRv6 End.AD behavior with IPv6 traffic."""
135 self.src_addr = "a0::"
136 self.sid_list = ["a1::", "a2::a6", "a3::"]
Francois Claded7c62a2020-04-03 11:33:02 +0200137 self.test_sid_index = 1
138
139 # send traffic to one destination interface
140 # source and destination interfaces are IPv6 only
141 self.setup_interfaces(ipv6=[True, True])
142
143 # configure route to next segment
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200144 route = VppIpRoute(
145 self,
146 self.sid_list[self.test_sid_index + 1],
147 128,
148 [
149 VppRoutePath(
150 self.pg0.remote_ip6,
151 self.pg0.sw_if_index,
152 proto=DpoProto.DPO_PROTO_IP6,
153 )
154 ],
155 )
Francois Claded7c62a2020-04-03 11:33:02 +0200156 route.add_vpp_config()
157
158 # configure SRv6 localSID behavior
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200159 cli_str = (
160 "sr localsid address "
161 + self.sid_list[self.test_sid_index]
162 + " behavior end.ad.flow"
163 + " nh "
164 + self.pg1.remote_ip6
165 + " oif "
166 + self.pg1.name
167 + " iif "
168 + self.pg1.name
169 )
Francois Claded7c62a2020-04-03 11:33:02 +0200170 self.vapi.cli(cli_str)
171
172 # log the localsids
173 self.logger.debug(self.vapi.cli("show sr localsid"))
174
175 # send one packet per packet size
176 count = len(self.pg_packet_sizes)
177
178 # prepare IPv6 in SRv6 headers
179 packet_header1 = self.create_packet_header_IPv6_SRH_IPv6(
180 srcaddr=self.src_addr,
181 sidlist=self.sid_list[::-1],
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200182 segleft=len(self.sid_list) - self.test_sid_index - 1,
183 )
Francois Claded7c62a2020-04-03 11:33:02 +0200184
185 # generate packets (pg0->pg1)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200186 pkts1 = self.create_stream(
187 self.pg0, self.pg1, packet_header1, self.pg_packet_sizes, count
188 )
Francois Claded7c62a2020-04-03 11:33:02 +0200189
190 # send packets and verify received packets
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200191 self.send_and_verify_pkts(
192 self.pg0, pkts1, self.pg1, self.compare_rx_tx_packet_End_AD_IPv6_out
193 )
Francois Claded7c62a2020-04-03 11:33:02 +0200194
195 # log the localsid counters
196 self.logger.info(self.vapi.cli("show sr localsid"))
197
198 # prepare IPv6 header for returning packets
199 packet_header2 = self.create_packet_header_IPv6()
200
201 # generate returning packets (pg1->pg0)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200202 pkts2 = self.create_stream(
203 self.pg1, self.pg0, packet_header2, self.pg_packet_sizes, count
204 )
Francois Claded7c62a2020-04-03 11:33:02 +0200205
206 # send packets and verify received packets
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200207 self.send_and_verify_pkts(
208 self.pg1, pkts2, self.pg0, self.compare_rx_tx_packet_End_AD_IPv6_in
209 )
Francois Claded7c62a2020-04-03 11:33:02 +0200210
211 # log the localsid counters
212 self.logger.info(self.vapi.cli("show sr localsid"))
213
214 # remove SRv6 localSIDs
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200215 cli_str = "sr localsid del address " + self.sid_list[self.test_sid_index]
Francois Claded7c62a2020-04-03 11:33:02 +0200216 self.vapi.cli(cli_str)
217
218 # cleanup interfaces
219 self.teardown_interfaces()
220
221 def compare_rx_tx_packet_End_AD_IPv6_out(self, tx_pkt, rx_pkt):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200222 """Compare input and output packet after passing End.AD with IPv6
Francois Claded7c62a2020-04-03 11:33:02 +0200223
224 :param tx_pkt: transmitted packet
225 :param rx_pkt: received packet
226 """
227
228 # get first (outer) IPv6 header of rx'ed packet
229 rx_ip = rx_pkt.getlayer(IPv6)
230
231 tx_ip = tx_pkt.getlayer(IPv6)
232 tx_ip2 = tx_pkt.getlayer(IPv6, 2)
233
234 # verify if rx'ed packet has no SRH
235 self.assertFalse(rx_pkt.haslayer(IPv6ExtHdrSegmentRouting))
236
237 # the whole rx_ip pkt should be equal to tx_ip2
238 # except for the hlim field
239 # -> adjust tx'ed hlim to expected hlim
240 tx_ip2.hlim = tx_ip2.hlim - 1
241
242 self.assertEqual(rx_ip, tx_ip2)
243
244 self.logger.debug("packet verification: SUCCESS")
245
246 def compare_rx_tx_packet_End_AD_IPv6_in(self, tx_pkt, rx_pkt):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200247 """Compare input and output packet after passing End.AD
Francois Claded7c62a2020-04-03 11:33:02 +0200248
249 :param tx_pkt: transmitted packet
250 :param rx_pkt: received packet
251 """
252
253 # get first (outer) IPv6 header of rx'ed packet
254 rx_ip = rx_pkt.getlayer(IPv6)
255 # received ip.src should be equal to SR Policy source
256 self.assertEqual(rx_ip.src, self.src_addr)
257 # received ip.dst should be equal to expected sidlist next segment
258 self.assertEqual(rx_ip.dst, self.sid_list[self.test_sid_index + 1])
259
260 # rx'ed packet should have SRH
261 self.assertTrue(rx_pkt.haslayer(IPv6ExtHdrSegmentRouting))
262
263 # get SRH
264 rx_srh = rx_pkt.getlayer(IPv6ExtHdrSegmentRouting)
265 # rx'ed seglist should be equal to SID-list in reversed order
266 self.assertEqual(rx_srh.addresses, self.sid_list[::-1])
267 # segleft should be equal to previous segleft value minus 1
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200268 self.assertEqual(rx_srh.segleft, len(self.sid_list) - self.test_sid_index - 2)
Francois Claded7c62a2020-04-03 11:33:02 +0200269 # lastentry should be equal to the SID-list length minus 1
270 self.assertEqual(rx_srh.lastentry, len(self.sid_list) - 1)
271
272 # the whole rx'ed pkt beyond SRH should be equal to tx'ed pkt
273 # except for the hop-limit field
274 tx_ip = tx_pkt.getlayer(IPv6)
275 # -> update tx'ed hlim to the expected hlim
276 tx_ip.hlim -= 1
277 # -> check payload
278 self.assertEqual(rx_srh.payload, tx_ip)
279
280 self.logger.debug("packet verification: SUCCESS")
281
282 def test_SRv6_End_AD_IPv4(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200283 """Test SRv6 End.AD behavior with IPv4 traffic."""
284 self.src_addr = "a0::"
285 self.sid_list = ["a1::", "a2::a4", "a3::"]
Francois Claded7c62a2020-04-03 11:33:02 +0200286 self.test_sid_index = 1
287
288 # send traffic to one destination interface
289 # source and destination interfaces are IPv6 only
290 self.setup_interfaces(ipv6=[True, False], ipv4=[False, True])
291
292 # configure route to next segment
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200293 route = VppIpRoute(
294 self,
295 self.sid_list[self.test_sid_index + 1],
296 128,
297 [
298 VppRoutePath(
299 self.pg0.remote_ip6,
300 self.pg0.sw_if_index,
301 proto=DpoProto.DPO_PROTO_IP6,
302 )
303 ],
304 )
Francois Claded7c62a2020-04-03 11:33:02 +0200305 route.add_vpp_config()
306
307 # configure SRv6 localSID behavior
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200308 cli_str = (
309 "sr localsid address "
310 + self.sid_list[self.test_sid_index]
311 + " behavior end.ad.flow"
312 + " nh "
313 + self.pg1.remote_ip4
314 + " oif "
315 + self.pg1.name
316 + " iif "
317 + self.pg1.name
318 )
Francois Claded7c62a2020-04-03 11:33:02 +0200319 self.vapi.cli(cli_str)
320
321 # log the localsids
322 self.logger.debug(self.vapi.cli("show sr localsid"))
323
324 # send one packet per packet size
325 count = len(self.pg_packet_sizes)
326
327 # prepare IPv4 in SRv6 headers
328 packet_header1 = self.create_packet_header_IPv6_SRH_IPv4(
329 srcaddr=self.src_addr,
330 sidlist=self.sid_list[::-1],
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200331 segleft=len(self.sid_list) - self.test_sid_index - 1,
332 )
Francois Claded7c62a2020-04-03 11:33:02 +0200333
334 # generate packets (pg0->pg1)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200335 pkts1 = self.create_stream(
336 self.pg0, self.pg1, packet_header1, self.pg_packet_sizes, count
337 )
Francois Claded7c62a2020-04-03 11:33:02 +0200338
339 # send packets and verify received packets
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200340 self.send_and_verify_pkts(
341 self.pg0, pkts1, self.pg1, self.compare_rx_tx_packet_End_AD_IPv4_out
342 )
Francois Claded7c62a2020-04-03 11:33:02 +0200343
344 # log the localsid counters
345 self.logger.info(self.vapi.cli("show sr localsid"))
346
347 # prepare IPv6 header for returning packets
348 packet_header2 = self.create_packet_header_IPv4()
349
350 # generate returning packets (pg1->pg0)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200351 pkts2 = self.create_stream(
352 self.pg1, self.pg0, packet_header2, self.pg_packet_sizes, count
353 )
Francois Claded7c62a2020-04-03 11:33:02 +0200354
355 # send packets and verify received packets
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200356 self.send_and_verify_pkts(
357 self.pg1, pkts2, self.pg0, self.compare_rx_tx_packet_End_AD_IPv4_in
358 )
Francois Claded7c62a2020-04-03 11:33:02 +0200359
360 # log the localsid counters
361 self.logger.info(self.vapi.cli("show sr localsid"))
362
363 # remove SRv6 localSIDs
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200364 cli_str = "sr localsid del address " + self.sid_list[self.test_sid_index]
Francois Claded7c62a2020-04-03 11:33:02 +0200365 self.vapi.cli(cli_str)
366
367 # cleanup interfaces
368 self.teardown_interfaces()
369
370 def compare_rx_tx_packet_End_AD_IPv4_out(self, tx_pkt, rx_pkt):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200371 """Compare input and output packet after passing End.AD with IPv4
Francois Claded7c62a2020-04-03 11:33:02 +0200372
373 :param tx_pkt: transmitted packet
374 :param rx_pkt: received packet
375 """
376
377 # get IPv4 header of rx'ed packet
378 rx_ip = rx_pkt.getlayer(IP)
379
380 tx_ip = tx_pkt.getlayer(IPv6)
381 tx_ip2 = tx_pkt.getlayer(IP)
382
383 # verify if rx'ed packet has no SRH
384 self.assertFalse(rx_pkt.haslayer(IPv6ExtHdrSegmentRouting))
385
386 # the whole rx_ip pkt should be equal to tx_ip2
387 # except for the ttl field and ip checksum
388 # -> adjust tx'ed ttl to expected ttl
389 tx_ip2.ttl = tx_ip2.ttl - 1
390 # -> set tx'ed ip checksum to None and let scapy recompute
391 tx_ip2.chksum = None
392 # read back the pkt (with str()) to force computing these fields
393 # probably other ways to accomplish this are possible
394 tx_ip2 = IP(scapy.compat.raw(tx_ip2))
395
396 self.assertEqual(rx_ip, tx_ip2)
397
398 self.logger.debug("packet verification: SUCCESS")
399
400 def compare_rx_tx_packet_End_AD_IPv4_in(self, tx_pkt, rx_pkt):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200401 """Compare input and output packet after passing End.AD
Francois Claded7c62a2020-04-03 11:33:02 +0200402
403 :param tx_pkt: transmitted packet
404 :param rx_pkt: received packet
405 """
406
407 # get first (outer) IPv6 header of rx'ed packet
408 rx_ip = rx_pkt.getlayer(IPv6)
409 # received ip.src should be equal to SR Policy source
410 self.assertEqual(rx_ip.src, self.src_addr)
411 # received ip.dst should be equal to expected sidlist next segment
412 self.assertEqual(rx_ip.dst, self.sid_list[self.test_sid_index + 1])
413
414 # rx'ed packet should have SRH
415 self.assertTrue(rx_pkt.haslayer(IPv6ExtHdrSegmentRouting))
416
417 # get SRH
418 rx_srh = rx_pkt.getlayer(IPv6ExtHdrSegmentRouting)
419 # rx'ed seglist should be equal to SID-list in reversed order
420 self.assertEqual(rx_srh.addresses, self.sid_list[::-1])
421 # segleft should be equal to previous segleft value minus 1
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200422 self.assertEqual(rx_srh.segleft, len(self.sid_list) - self.test_sid_index - 2)
Francois Claded7c62a2020-04-03 11:33:02 +0200423 # lastentry should be equal to the SID-list length minus 1
424 self.assertEqual(rx_srh.lastentry, len(self.sid_list) - 1)
425
426 # the whole rx'ed pkt beyond SRH should be equal to tx'ed pkt
427 # except for the ttl field and ip checksum
428 tx_ip = tx_pkt.getlayer(IP)
429 # -> adjust tx'ed ttl to expected ttl
430 tx_ip.ttl = tx_ip.ttl - 1
431 # -> set tx'ed ip checksum to None and let scapy recompute
432 tx_ip.chksum = None
433 # -> read back the pkt (with str()) to force computing these fields
434 # probably other ways to accomplish this are possible
435 self.assertEqual(rx_srh.payload, IP(scapy.compat.raw(tx_ip)))
436
437 self.logger.debug("packet verification: SUCCESS")
438
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200439 def create_stream(self, src_if, dst_if, packet_header, packet_sizes, count):
Francois Claded7c62a2020-04-03 11:33:02 +0200440 """Create SRv6 input packet stream for defined interface.
441
442 :param VppInterface src_if: Interface to create packet stream for
443 :param VppInterface dst_if: destination interface of packet stream
444 :param packet_header: Layer3 scapy packet headers,
445 L2 is added when not provided,
446 Raw(payload) with packet_info is added
447 :param list packet_sizes: packet stream pckt sizes,sequentially applied
448 to packets in stream have
449 :param int count: number of packets in packet stream
450 :return: list of packets
451 """
452 self.logger.info("Creating packets")
453 pkts = []
454 for i in range(0, count - 1):
455 payload_info = self.create_packet_info(src_if, dst_if)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200456 self.logger.debug("Creating packet with index %d" % (payload_info.index))
Francois Claded7c62a2020-04-03 11:33:02 +0200457 payload = self.info_to_payload(payload_info)
458 # add L2 header if not yet provided in packet_header
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200459 if packet_header.getlayer(0).name == "Ethernet":
Francois Claded7c62a2020-04-03 11:33:02 +0200460 p = packet_header / Raw(payload)
461 else:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200462 p = (
463 Ether(dst=src_if.local_mac, src=src_if.remote_mac)
464 / packet_header
465 / Raw(payload)
466 )
Francois Claded7c62a2020-04-03 11:33:02 +0200467 size = packet_sizes[i % len(packet_sizes)]
468 self.logger.debug("Packet size %d" % (size))
469 self.extend_packet(p, size)
470 # we need to store the packet with the automatic fields computed
471 # read back the dumped packet (with str())
472 # to force computing these fields
473 # probably other ways are possible
474 p = Ether(scapy.compat.raw(p))
475 payload_info.data = p.copy()
476 self.logger.debug(ppp("Created packet:", p))
477 pkts.append(p)
478 self.logger.info("Done creating packets")
479 return pkts
480
481 def send_and_verify_pkts(self, input, pkts, output, compare_func):
482 """Send packets and verify received packets using compare_func
483
484 :param input: ingress interface of DUT
485 :param pkts: list of packets to transmit
486 :param output: egress interface of DUT
487 :param compare_func: function to compare in and out packets
488 """
489 # add traffic stream to input interface
490 input.add_stream(pkts)
491
492 # enable capture on all interfaces
493 self.pg_enable_capture(self.pg_interfaces)
494
495 # start traffic
496 self.logger.info("Starting traffic")
497 self.pg_start()
498
499 # get output capture
500 self.logger.info("Getting packet capture")
501 capture = output.get_capture()
502
503 # assert nothing was captured on input interface
504 # input.assert_nothing_captured()
505
506 # verify captured packets
507 self.verify_captured_pkts(output, capture, compare_func)
508
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200509 def create_packet_header_IPv6(
510 self, saddr="1234::1", daddr="4321::1", sport=1234, dport=1234
511 ):
Francois Claded7c62a2020-04-03 11:33:02 +0200512 """Create packet header: IPv6 header, UDP header
513
514 :param dst: IPv6 destination address
515
516 IPv6 source address is 1234::1
517 IPv6 destination address is 4321::1
518 UDP source port and destination port are 1234
519 """
520
521 p = IPv6(src=saddr, dst=daddr) / UDP(sport=sport, dport=dport)
522 return p
523
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200524 def create_packet_header_IPv6_SRH_IPv6(
525 self,
526 srcaddr,
527 sidlist,
528 segleft,
529 insrc="1234::1",
530 indst="4321::1",
531 sport=1234,
532 dport=1234,
533 ):
Francois Claded7c62a2020-04-03 11:33:02 +0200534 """Create packet header: IPv6 encapsulated in SRv6:
535 IPv6 header with SRH, IPv6 header, UDP header
536
537 :param int srcaddr: outer source address
538 :param list sidlist: segment list of outer IPv6 SRH
539 :param int segleft: segments-left field of outer IPv6 SRH
540
541 Outer IPv6 source address is set to srcaddr
542 Outer IPv6 destination address is set to sidlist[segleft]
543 Inner IPv6 source addresses is 1234::1
544 Inner IPv6 destination address is 4321::1
545 UDP source port and destination port are 1234
546 """
547
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200548 p = (
549 IPv6(src=srcaddr, dst=sidlist[segleft])
550 / IPv6ExtHdrSegmentRouting(addresses=sidlist, segleft=segleft, nh=41)
551 / IPv6(src=insrc, dst=indst)
552 / UDP(sport=sport, dport=dport)
553 )
Francois Claded7c62a2020-04-03 11:33:02 +0200554 return p
555
556 def create_packet_header_IPv4(self):
557 """Create packet header: IPv4 header, UDP header
558
559 :param dst: IPv4 destination address
560
561 IPv4 source address is 123.1.1.1
562 IPv4 destination address is 124.1.1.1
563 UDP source port and destination port are 1234
564 """
565
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200566 p = IP(src="123.1.1.1", dst="124.1.1.1") / UDP(sport=1234, dport=1234)
Francois Claded7c62a2020-04-03 11:33:02 +0200567 return p
568
569 def create_packet_header_IPv6_SRH_IPv4(self, srcaddr, sidlist, segleft):
570 """Create packet header: IPv4 encapsulated in SRv6:
571 IPv6 header with SRH, IPv4 header, UDP header
572
573 :param int srcaddr: outer source address
574 :param list sidlist: segment list of outer IPv6 SRH
575 :param int segleft: segments-left field of outer IPv6 SRH
576
577 Outer IPv6 source address is set to srcaddr
578 Outer IPv6 destination address is set to sidlist[segleft]
579 Inner IPv4 source address is 123.1.1.1
580 Inner IPv4 destination address is 124.1.1.1
581 UDP source port and destination port are 1234
582 """
583
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200584 p = (
585 IPv6(src=srcaddr, dst=sidlist[segleft])
586 / IPv6ExtHdrSegmentRouting(addresses=sidlist, segleft=segleft, nh=4)
587 / IP(src="123.1.1.1", dst="124.1.1.1")
588 / UDP(sport=1234, dport=1234)
589 )
Francois Claded7c62a2020-04-03 11:33:02 +0200590 return p
591
592 def get_payload_info(self, packet):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200593 """Extract the payload_info from the packet"""
Francois Claded7c62a2020-04-03 11:33:02 +0200594 # in most cases, payload_info is in packet[Raw]
595 # but packet[Raw] gives the complete payload
596 # (incl L2 header) for the T.Encaps L2 case
597 try:
598 payload_info = self.payload_to_info(packet[Raw])
599
600 except:
601 # remote L2 header from packet[Raw]:
602 # take packet[Raw], convert it to an Ether layer
603 # and then extract Raw from it
604 payload_info = self.payload_to_info(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200605 Ether(scapy.compat.raw(packet[Raw]))[Raw]
606 )
Francois Claded7c62a2020-04-03 11:33:02 +0200607
608 return payload_info
609
610 def verify_captured_pkts(self, dst_if, capture, compare_func):
611 """
612 Verify captured packet stream for specified interface.
613 Compare ingress with egress packets using the specified compare fn
614
615 :param dst_if: egress interface of DUT
616 :param capture: captured packets
617 :param compare_func: function to compare in and out packet
618 """
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200619 self.logger.info(
620 "Verifying capture on interface %s using function %s"
621 % (dst_if.name, compare_func.__name__)
622 )
Francois Claded7c62a2020-04-03 11:33:02 +0200623
624 last_info = dict()
625 for i in self.pg_interfaces:
626 last_info[i.sw_if_index] = None
627 dst_sw_if_index = dst_if.sw_if_index
628
629 for packet in capture:
630 try:
631 # extract payload_info from packet's payload
632 payload_info = self.get_payload_info(packet)
633 packet_index = payload_info.index
634
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200635 self.logger.debug("Verifying packet with index %d" % (packet_index))
Francois Claded7c62a2020-04-03 11:33:02 +0200636 # packet should have arrived on the expected interface
637 self.assertEqual(payload_info.dst, dst_sw_if_index)
638 self.logger.debug(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200639 "Got packet on interface %s: src=%u (idx=%u)"
640 % (dst_if.name, payload_info.src, packet_index)
641 )
Francois Claded7c62a2020-04-03 11:33:02 +0200642
643 # search for payload_info with same src and dst if_index
644 # this will give us the transmitted packet
645 next_info = self.get_next_packet_info_for_interface2(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200646 payload_info.src, dst_sw_if_index, last_info[payload_info.src]
647 )
Francois Claded7c62a2020-04-03 11:33:02 +0200648 last_info[payload_info.src] = next_info
649 # next_info should not be None
650 self.assertTrue(next_info is not None)
651 # index of tx and rx packets should be equal
652 self.assertEqual(packet_index, next_info.index)
653 # data field of next_info contains the tx packet
654 txed_packet = next_info.data
655
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200656 self.logger.debug(
657 ppp("Transmitted packet:", txed_packet)
658 ) # ppp=Pretty Print Packet
Francois Claded7c62a2020-04-03 11:33:02 +0200659
660 self.logger.debug(ppp("Received packet:", packet))
661
662 # compare rcvd packet with expected packet using compare_func
663 compare_func(txed_packet, packet)
664
665 except:
666 self.logger.error(ppp("Unexpected or invalid packet:", packet))
667 raise
668
669 # have all expected packets arrived?
670 for i in self.pg_interfaces:
671 remaining_packet = self.get_next_packet_info_for_interface2(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200672 i.sw_if_index, dst_sw_if_index, last_info[i.sw_if_index]
673 )
674 self.assertTrue(
675 remaining_packet is None,
676 "Interface %s: Packet expected from interface %s "
677 "didn't arrive" % (dst_if.name, i.name),
678 )
Francois Claded7c62a2020-04-03 11:33:02 +0200679
680
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200681if __name__ == "__main__":
Francois Claded7c62a2020-04-03 11:33:02 +0200682 unittest.main(testRunner=VppTestRunner)