blob: 0f78159dccf374d24d907b4b8c4962c04347d62d [file] [log] [blame]
Renato Botelho do Coutoead1e532019-10-31 13:31:07 -05001#!/usr/bin/env python3
Francois Clad0928da92018-07-09 16:45:23 +02002
3import unittest
Francois Clad0928da92018-07-09 16:45:23 +02004
Dave Wallace8800f732023-08-31 00:47:44 -04005from framework import VppTestCase
6from asfframework import VppTestRunner
7from vpp_ip_route import VppIpRoute, VppRoutePath
Francois Clad0928da92018-07-09 16:45:23 +02008
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07009import scapy.compat
Francois Clad0928da92018-07-09 16:45:23 +020010from scapy.packet import Raw
11from scapy.layers.l2 import Ether, Dot1Q
12from scapy.layers.inet6 import IPv6, UDP, IPv6ExtHdrSegmentRouting
13from scapy.layers.inet import IP, UDP
Dmitry Valter34fa0ce2024-03-11 10:38:46 +000014from config import config
Francois Clad0928da92018-07-09 16:45:23 +020015
Francois Clad0928da92018-07-09 16:45:23 +020016from util import ppp
17
18
Dmitry Valter34fa0ce2024-03-11 10:38:46 +000019@unittest.skipIf("srv6-as" in config.excluded_plugins, "Exclude srv6-as plugin tests")
Tianyu Li6d95f8c2022-02-25 05:51:10 +000020class TestSRv6As(VppTestCase):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020021 """SRv6 Static Proxy plugin Test Case"""
Francois Clad0928da92018-07-09 16:45:23 +020022
23 @classmethod
24 def setUpClass(self):
Tianyu Li6d95f8c2022-02-25 05:51:10 +000025 super(TestSRv6As, self).setUpClass()
Francois Clad0928da92018-07-09 16:45:23 +020026
Paul Vinciguerra7f9b7f92019-03-12 19:23:27 -070027 @classmethod
28 def tearDownClass(cls):
Tianyu Li6d95f8c2022-02-25 05:51:10 +000029 super(TestSRv6As, cls).tearDownClass()
Paul Vinciguerra7f9b7f92019-03-12 19:23:27 -070030
Francois Clad0928da92018-07-09 16:45:23 +020031 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(TestSRv6As, self).setUp()
Francois Clad0928da92018-07-09 16:45:23 +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 Clad0928da92018-07-09 16:45:23 +020043 self.teardown_interfaces()
44
Tianyu Li6d95f8c2022-02-25 05:51:10 +000045 super(TestSRv6As, self).tearDown()
Francois Clad0928da92018-07-09 16:45:23 +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 Clad0928da92018-07-09 16:45:23 +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 Clad0928da92018-07-09 16:45:23 +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 Clad0928da92018-07-09 16:45:23 +0200108
109 if any(ipv6):
110 self.logger.debug(self.vapi.cli("show ip6 neighbors"))
111 if any(ipv4):
Neale Rannscbe25aa2019-09-30 10:53:31 +0000112 self.logger.debug(self.vapi.cli("show ip4 neighbors"))
Francois Clad0928da92018-07-09 16:45:23 +0200113 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 Clad0928da92018-07-09 16:45:23 +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_AS_IPv6_noSRH(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200131 """Test SRv6 End.AS behavior with IPv6 traffic and no SRH rewrite."""
Francois Clad0928da92018-07-09 16:45:23 +0200132 self.run_SRv6_End_AS_IPv6(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200133 sid_list=["a1::", "a2::a6", "a3::"],
Francois Clad0928da92018-07-09 16:45:23 +0200134 test_sid_index=1,
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200135 rewrite_src_addr="a2::",
136 )
Francois Clad0928da92018-07-09 16:45:23 +0200137
138 def test_SRv6_End_AS_IPv6_SRH(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200139 """Test SRv6 End.AS behavior with IPv6 traffic and SRH rewrite."""
Francois Clad0928da92018-07-09 16:45:23 +0200140 self.run_SRv6_End_AS_IPv6(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200141 sid_list=["a1::a6", "a2::", "a3::"],
Francois Clad0928da92018-07-09 16:45:23 +0200142 test_sid_index=0,
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200143 rewrite_src_addr="a1::",
144 )
Francois Clad0928da92018-07-09 16:45:23 +0200145
146 def test_SRv6_End_AS_IPv4_noSRH(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200147 """Test SRv6 End.AS behavior with IPv4 traffic and no SRH rewrite."""
Francois Clad0928da92018-07-09 16:45:23 +0200148 self.run_SRv6_End_AS_IPv4(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200149 sid_list=["a1::", "a2::a6", "a3::"],
Francois Clad0928da92018-07-09 16:45:23 +0200150 test_sid_index=1,
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200151 rewrite_src_addr="a2::",
152 )
Francois Clad0928da92018-07-09 16:45:23 +0200153
154 def test_SRv6_End_AS_IPv4_SRH(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200155 """Test SRv6 End.AS behavior with IPv4 traffic and SRH rewrite."""
Francois Clad0928da92018-07-09 16:45:23 +0200156 self.run_SRv6_End_AS_IPv4(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200157 sid_list=["a1::a6", "a2::", "a3::"],
Francois Clad0928da92018-07-09 16:45:23 +0200158 test_sid_index=0,
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200159 rewrite_src_addr="a1::",
160 )
Francois Clad0928da92018-07-09 16:45:23 +0200161
Francois Cladb02f3b72018-07-04 11:47:21 +0200162 def test_SRv6_End_AS_L2_noSRH(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200163 """Test SRv6 End.AS behavior with L2 traffic and no SRH rewrite."""
Francois Cladb02f3b72018-07-04 11:47:21 +0200164 self.run_SRv6_End_AS_L2(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200165 sid_list=["a1::", "a2::a6", "a3::"],
Francois Cladb02f3b72018-07-04 11:47:21 +0200166 test_sid_index=1,
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200167 rewrite_src_addr="a2::",
168 )
Francois Cladb02f3b72018-07-04 11:47:21 +0200169
170 def test_SRv6_End_AS_L2_SRH(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200171 """Test SRv6 End.AS behavior with L2 traffic and SRH rewrite."""
Francois Cladb02f3b72018-07-04 11:47:21 +0200172 self.run_SRv6_End_AS_L2(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200173 sid_list=["a1::a6", "a2::", "a3::"],
Francois Cladb02f3b72018-07-04 11:47:21 +0200174 test_sid_index=0,
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200175 rewrite_src_addr="a1::",
176 )
Francois Cladb02f3b72018-07-04 11:47:21 +0200177
178 def run_SRv6_End_AS_L2(self, sid_list, test_sid_index, rewrite_src_addr):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200179 """Run SRv6 End.AS test with L2 traffic."""
Francois Cladb02f3b72018-07-04 11:47:21 +0200180 self.rewrite_src_addr = rewrite_src_addr
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200181 self.rewrite_sid_list = sid_list[test_sid_index + 1 : :]
Francois Cladb02f3b72018-07-04 11:47:21 +0200182
183 # send traffic to one destination interface
184 # source and destination interfaces are IPv6 only
185 self.setup_interfaces(ipv6=[True, False])
186
187 # configure route to next segment
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200188 route = VppIpRoute(
189 self,
190 sid_list[test_sid_index + 1],
191 128,
192 [VppRoutePath(self.pg0.remote_ip6, self.pg0.sw_if_index)],
193 )
Francois Cladb02f3b72018-07-04 11:47:21 +0200194 route.add_vpp_config()
195
196 # configure SRv6 localSID behavior
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200197 cli_str = (
198 "sr localsid address "
199 + sid_list[test_sid_index]
200 + " behavior end.as"
201 + " oif "
202 + self.pg1.name
203 + " iif "
204 + self.pg1.name
205 + " src "
206 + self.rewrite_src_addr
207 )
Francois Cladb02f3b72018-07-04 11:47:21 +0200208 for s in self.rewrite_sid_list:
209 cli_str += " next " + s
210 self.vapi.cli(cli_str)
211
212 # log the localsids
213 self.logger.debug(self.vapi.cli("show sr localsid"))
214
215 # send one packet per packet size
216 count = len(self.pg_packet_sizes)
217
218 # prepare L2 in SRv6 headers
219 packet_header1 = self.create_packet_header_IPv6_SRH_L2(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200220 sidlist=sid_list[::-1], segleft=len(sid_list) - test_sid_index - 1, vlan=0
221 )
Francois Cladb02f3b72018-07-04 11:47:21 +0200222
223 # generate packets (pg0->pg1)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200224 pkts1 = self.create_stream(
225 self.pg0, self.pg1, packet_header1, self.pg_packet_sizes, count
226 )
Francois Cladb02f3b72018-07-04 11:47:21 +0200227
228 # send packets and verify received packets
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200229 self.send_and_verify_pkts(
230 self.pg0, pkts1, self.pg1, self.compare_rx_tx_packet_End_AS_L2_out
231 )
Francois Cladb02f3b72018-07-04 11:47:21 +0200232
233 # log the localsid counters
234 self.logger.info(self.vapi.cli("show sr localsid"))
235
236 # prepare L2 header for returning packets
237 packet_header2 = self.create_packet_header_L2()
238
239 # generate returning packets (pg1->pg0)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200240 pkts2 = self.create_stream(
241 self.pg1, self.pg0, packet_header2, self.pg_packet_sizes, count
242 )
Francois Cladb02f3b72018-07-04 11:47:21 +0200243
244 # send packets and verify received packets
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200245 self.send_and_verify_pkts(
246 self.pg1, pkts2, self.pg0, self.compare_rx_tx_packet_End_AS_L2_in
247 )
Francois Cladb02f3b72018-07-04 11:47:21 +0200248
249 # log the localsid counters
250 self.logger.info(self.vapi.cli("show sr localsid"))
251
252 # remove SRv6 localSIDs
253 self.vapi.cli("sr localsid del address " + sid_list[test_sid_index])
254
255 # cleanup interfaces
256 self.teardown_interfaces()
257
Francois Clad0928da92018-07-09 16:45:23 +0200258 def run_SRv6_End_AS_IPv6(self, sid_list, test_sid_index, rewrite_src_addr):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200259 """Run SRv6 End.AS test with IPv6 traffic."""
Francois Clad0928da92018-07-09 16:45:23 +0200260 self.rewrite_src_addr = rewrite_src_addr
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200261 self.rewrite_sid_list = sid_list[test_sid_index + 1 : :]
Francois Clad0928da92018-07-09 16:45:23 +0200262
263 # send traffic to one destination interface
264 # source and destination interfaces are IPv6 only
265 self.setup_interfaces(ipv6=[True, True])
266
267 # configure route to next segment
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200268 route = VppIpRoute(
269 self,
270 sid_list[test_sid_index + 1],
271 128,
272 [VppRoutePath(self.pg0.remote_ip6, self.pg0.sw_if_index)],
273 )
Francois Clad0928da92018-07-09 16:45:23 +0200274 route.add_vpp_config()
275
276 # configure SRv6 localSID behavior
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200277 cli_str = (
278 "sr localsid address "
279 + sid_list[test_sid_index]
280 + " behavior end.as"
281 + " nh "
282 + self.pg1.remote_ip6
283 + " oif "
284 + self.pg1.name
285 + " iif "
286 + self.pg1.name
287 + " src "
288 + self.rewrite_src_addr
289 )
Francois Clad0928da92018-07-09 16:45:23 +0200290 for s in self.rewrite_sid_list:
291 cli_str += " next " + s
292 self.vapi.cli(cli_str)
293
294 # log the localsids
295 self.logger.debug(self.vapi.cli("show sr localsid"))
296
297 # send one packet per packet size
298 count = len(self.pg_packet_sizes)
299
300 # prepare IPv6 in SRv6 headers
301 packet_header1 = self.create_packet_header_IPv6_SRH_IPv6(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200302 sidlist=sid_list[::-1], segleft=len(sid_list) - test_sid_index - 1
303 )
Francois Clad0928da92018-07-09 16:45:23 +0200304
305 # generate packets (pg0->pg1)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200306 pkts1 = self.create_stream(
307 self.pg0, self.pg1, packet_header1, self.pg_packet_sizes, count
308 )
Francois Clad0928da92018-07-09 16:45:23 +0200309
310 # send packets and verify received packets
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200311 self.send_and_verify_pkts(
312 self.pg0, pkts1, self.pg1, self.compare_rx_tx_packet_End_AS_IPv6_out
313 )
Francois Clad0928da92018-07-09 16:45:23 +0200314
315 # log the localsid counters
316 self.logger.info(self.vapi.cli("show sr localsid"))
317
318 # prepare IPv6 header for returning packets
319 packet_header2 = self.create_packet_header_IPv6()
320
321 # generate returning packets (pg1->pg0)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200322 pkts2 = self.create_stream(
323 self.pg1, self.pg0, packet_header2, self.pg_packet_sizes, count
324 )
Francois Clad0928da92018-07-09 16:45:23 +0200325
326 # send packets and verify received packets
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200327 self.send_and_verify_pkts(
328 self.pg1, pkts2, self.pg0, self.compare_rx_tx_packet_End_AS_IPv6_in
329 )
Francois Clad0928da92018-07-09 16:45:23 +0200330
331 # log the localsid counters
332 self.logger.info(self.vapi.cli("show sr localsid"))
333
334 # remove SRv6 localSIDs
335 self.vapi.cli("sr localsid del address " + sid_list[test_sid_index])
336
337 # cleanup interfaces
338 self.teardown_interfaces()
339
340 def run_SRv6_End_AS_IPv4(self, sid_list, test_sid_index, rewrite_src_addr):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200341 """Run SRv6 End.AS test with IPv4 traffic."""
Francois Clad0928da92018-07-09 16:45:23 +0200342 self.rewrite_src_addr = rewrite_src_addr
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200343 self.rewrite_sid_list = sid_list[test_sid_index + 1 : :]
Francois Clad0928da92018-07-09 16:45:23 +0200344
345 # send traffic to one destination interface
346 # source and destination interfaces are IPv6 only
347 self.setup_interfaces(ipv6=[True, False], ipv4=[True, True])
348
349 # configure route to next segment
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200350 route = VppIpRoute(
351 self,
352 sid_list[test_sid_index + 1],
353 128,
354 [VppRoutePath(self.pg0.remote_ip6, self.pg0.sw_if_index)],
355 )
Francois Clad0928da92018-07-09 16:45:23 +0200356 route.add_vpp_config()
357
358 # configure SRv6 localSID behavior
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200359 cli_str = (
360 "sr localsid address "
361 + sid_list[test_sid_index]
362 + " behavior end.as"
363 + " nh "
364 + self.pg1.remote_ip4
365 + " oif "
366 + self.pg1.name
367 + " iif "
368 + self.pg1.name
369 + " src "
370 + self.rewrite_src_addr
371 )
Francois Clad0928da92018-07-09 16:45:23 +0200372 for s in self.rewrite_sid_list:
373 cli_str += " next " + s
374 self.vapi.cli(cli_str)
375
376 # log the localsids
377 self.logger.debug(self.vapi.cli("show sr localsid"))
378
379 # send one packet per packet size
380 count = len(self.pg_packet_sizes)
381
382 # prepare IPv4 in SRv6 headers
383 packet_header1 = self.create_packet_header_IPv6_SRH_IPv4(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200384 sidlist=sid_list[::-1], segleft=len(sid_list) - test_sid_index - 1
385 )
Francois Clad0928da92018-07-09 16:45:23 +0200386
387 # generate packets (pg0->pg1)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200388 pkts1 = self.create_stream(
389 self.pg0, self.pg1, packet_header1, self.pg_packet_sizes, count
390 )
Francois Clad0928da92018-07-09 16:45:23 +0200391
392 # send packets and verify received packets
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200393 self.send_and_verify_pkts(
394 self.pg0, pkts1, self.pg1, self.compare_rx_tx_packet_End_AS_IPv4_out
395 )
Francois Clad0928da92018-07-09 16:45:23 +0200396
397 # log the localsid counters
398 self.logger.info(self.vapi.cli("show sr localsid"))
399
400 # prepare IPv6 header for returning packets
401 packet_header2 = self.create_packet_header_IPv4()
402
403 # generate returning packets (pg1->pg0)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200404 pkts2 = self.create_stream(
405 self.pg1, self.pg0, packet_header2, self.pg_packet_sizes, count
406 )
Francois Clad0928da92018-07-09 16:45:23 +0200407
408 # send packets and verify received packets
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200409 self.send_and_verify_pkts(
410 self.pg1, pkts2, self.pg0, self.compare_rx_tx_packet_End_AS_IPv4_in
411 )
Francois Clad0928da92018-07-09 16:45:23 +0200412
413 # log the localsid counters
414 self.logger.info(self.vapi.cli("show sr localsid"))
415
416 # remove SRv6 localSIDs
417 self.vapi.cli("sr localsid del address " + sid_list[test_sid_index])
418
419 # cleanup interfaces
420 self.teardown_interfaces()
421
422 def compare_rx_tx_packet_End_AS_IPv6_in(self, tx_pkt, rx_pkt):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200423 """Compare input and output packet after passing End.AS
Francois Clad0928da92018-07-09 16:45:23 +0200424
425 :param tx_pkt: transmitted packet
426 :param rx_pkt: received packet
427 """
428
429 # get first (outer) IPv6 header of rx'ed packet
430 rx_ip = rx_pkt.getlayer(IPv6)
431 rx_srh = None
432
433 tx_ip = tx_pkt.getlayer(IPv6)
434
435 # expected segment-list (SRH order)
436 tx_seglist = self.rewrite_sid_list[::-1]
437
438 # received ip.src should be equal to SR Policy source
439 self.assertEqual(rx_ip.src, self.rewrite_src_addr)
440 # received ip.dst should be equal to expected sidlist[lastentry]
441 self.assertEqual(rx_ip.dst, tx_seglist[-1])
442
443 if len(tx_seglist) > 1:
444 # rx'ed packet should have SRH
445 self.assertTrue(rx_pkt.haslayer(IPv6ExtHdrSegmentRouting))
446 # get SRH
447 rx_srh = rx_pkt.getlayer(IPv6ExtHdrSegmentRouting)
448 # rx'ed seglist should be equal to expected seglist
449 self.assertEqual(rx_srh.addresses, tx_seglist)
450 # segleft should be equal to size expected seglist-1
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200451 self.assertEqual(rx_srh.segleft, len(tx_seglist) - 1)
Francois Clad0928da92018-07-09 16:45:23 +0200452 # segleft should be equal to lastentry
453 self.assertEqual(rx_srh.segleft, rx_srh.lastentry)
454 # get payload
455 payload = rx_srh.payload
456 else:
457 # rx'ed packet should NOT have SRH
458 self.assertFalse(rx_pkt.haslayer(IPv6ExtHdrSegmentRouting))
459 # get payload
460 payload = rx_ip.payload
461
462 # the whole rx'ed pkt beyond SRH should be equal to tx'ed pkt
463 # except for the hop-limit field
464 # -> update tx'ed hlim to the expected hlim
465 tx_ip.hlim = tx_ip.hlim - 1
466
467 self.assertEqual(payload, tx_ip)
468
469 self.logger.debug("packet verification: SUCCESS")
470
471 def compare_rx_tx_packet_End_AS_IPv4_in(self, tx_pkt, rx_pkt):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200472 """Compare input and output packet after passing End.AS
Francois Clad0928da92018-07-09 16:45:23 +0200473
474 :param tx_pkt: transmitted packet
475 :param rx_pkt: received packet
476 """
477
478 # get first (outer) IPv6 header of rx'ed packet
479 rx_ip = rx_pkt.getlayer(IPv6)
480 rx_srh = None
481
482 tx_ip = tx_pkt.getlayer(IP)
483
484 # expected segment-list (SRH order)
485 tx_seglist = self.rewrite_sid_list[::-1]
486
487 # received ip.src should be equal to SR Policy source
488 self.assertEqual(rx_ip.src, self.rewrite_src_addr)
489 # received ip.dst should be equal to expected sidlist[lastentry]
490 self.assertEqual(rx_ip.dst, tx_seglist[-1])
491
492 if len(tx_seglist) > 1:
493 # rx'ed packet should have SRH and IPv4 header
494 self.assertTrue(rx_pkt.haslayer(IPv6ExtHdrSegmentRouting))
495 self.assertTrue(rx_ip.payload.haslayer(IP))
496 # get SRH
497 rx_srh = rx_pkt.getlayer(IPv6ExtHdrSegmentRouting)
498 # rx'ed seglist should be equal to seglist
499 self.assertEqual(rx_srh.addresses, tx_seglist)
500 # segleft should be equal to size seglist-1
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200501 self.assertEqual(rx_srh.segleft, len(tx_seglist) - 1)
Francois Clad0928da92018-07-09 16:45:23 +0200502 # segleft should be equal to lastentry
503 self.assertEqual(rx_srh.segleft, rx_srh.lastentry)
504 payload = rx_srh.payload
505 else:
506 # rx'ed packet should NOT have SRH
507 self.assertFalse(rx_pkt.haslayer(IPv6ExtHdrSegmentRouting))
508 # get payload
509 payload = rx_ip.payload
510
511 # the whole rx'ed pkt beyond SRH should be equal to tx'ed pkt
512 # except for the ttl field and ip checksum
513 # -> adjust tx'ed ttl to expected ttl
514 tx_ip.ttl = tx_ip.ttl - 1
515 # -> set tx'ed ip checksum to None and let scapy recompute
516 tx_ip.chksum = None
517 # read back the pkt (with str()) to force computing these fields
518 # probably other ways to accomplish this are possible
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700519 tx_ip = IP(scapy.compat.raw(tx_ip))
Francois Clad0928da92018-07-09 16:45:23 +0200520
521 self.assertEqual(payload, tx_ip)
522
523 self.logger.debug("packet verification: SUCCESS")
524
Francois Cladb02f3b72018-07-04 11:47:21 +0200525 def compare_rx_tx_packet_End_AS_L2_in(self, tx_pkt, rx_pkt):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200526 """Compare input and output packet after passing End.AS
Francois Cladb02f3b72018-07-04 11:47:21 +0200527
528 :param tx_pkt: transmitted packet
529 :param rx_pkt: received packet
530 """
531
532 # get first (outer) IPv6 header of rx'ed packet
533 rx_ip = rx_pkt.getlayer(IPv6)
534 rx_srh = None
535
536 tx_ether = tx_pkt.getlayer(Ether)
537
538 # expected segment-list (SRH order)
539 tx_seglist = self.rewrite_sid_list[::-1]
540
541 # received ip.src should be equal to SR Policy source
542 self.assertEqual(rx_ip.src, self.rewrite_src_addr)
543 # received ip.dst should be equal to expected sidlist[lastentry]
544 self.assertEqual(rx_ip.dst, tx_seglist[-1])
545
546 if len(tx_seglist) > 1:
547 # rx'ed packet should have SRH
548 self.assertTrue(rx_pkt.haslayer(IPv6ExtHdrSegmentRouting))
549 # get SRH
550 rx_srh = rx_pkt.getlayer(IPv6ExtHdrSegmentRouting)
551 # rx'ed seglist should be equal to seglist
552 self.assertEqual(rx_srh.addresses, tx_seglist)
553 # segleft should be equal to size seglist-1
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200554 self.assertEqual(rx_srh.segleft, len(tx_seglist) - 1)
Francois Cladb02f3b72018-07-04 11:47:21 +0200555 # segleft should be equal to lastentry
556 self.assertEqual(rx_srh.segleft, rx_srh.lastentry)
pcamaril30e76712020-02-04 08:36:51 +0100557 # nh should be "No Next Header" (143)
558 self.assertEqual(rx_srh.nh, 143)
Francois Cladb02f3b72018-07-04 11:47:21 +0200559 # get payload
560 payload = rx_srh.payload
561 else:
562 # rx'ed packet should NOT have SRH
563 self.assertFalse(rx_pkt.haslayer(IPv6ExtHdrSegmentRouting))
564 # get payload
565 payload = rx_ip.payload
566
567 # the whole rx'ed pkt beyond SRH should be equal to tx'ed pkt
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700568 self.assertEqual(Ether(scapy.compat.raw(payload)), tx_ether)
Francois Cladb02f3b72018-07-04 11:47:21 +0200569
570 self.logger.debug("packet verification: SUCCESS")
571
Francois Clad0928da92018-07-09 16:45:23 +0200572 def compare_rx_tx_packet_End_AS_IPv6_out(self, tx_pkt, rx_pkt):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200573 """Compare input and output packet after passing End.AS with IPv6
Francois Clad0928da92018-07-09 16:45:23 +0200574
575 :param tx_pkt: transmitted packet
576 :param rx_pkt: received packet
577 """
578
579 # get first (outer) IPv6 header of rx'ed packet
580 rx_ip = rx_pkt.getlayer(IPv6)
581
582 tx_ip = tx_pkt.getlayer(IPv6)
583 tx_ip2 = tx_pkt.getlayer(IPv6, 2)
584
585 # verify if rx'ed packet has no SRH
586 self.assertFalse(rx_pkt.haslayer(IPv6ExtHdrSegmentRouting))
587
588 # the whole rx_ip pkt should be equal to tx_ip2
589 # except for the hlim field
590 # -> adjust tx'ed hlim to expected hlim
591 tx_ip2.hlim = tx_ip2.hlim - 1
592
593 self.assertEqual(rx_ip, tx_ip2)
594
595 self.logger.debug("packet verification: SUCCESS")
596
597 def compare_rx_tx_packet_End_AS_IPv4_out(self, tx_pkt, rx_pkt):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200598 """Compare input and output packet after passing End.AS with IPv4
Francois Clad0928da92018-07-09 16:45:23 +0200599
600 :param tx_pkt: transmitted packet
601 :param rx_pkt: received packet
602 """
603
604 # get IPv4 header of rx'ed packet
605 rx_ip = rx_pkt.getlayer(IP)
606
607 tx_ip = tx_pkt.getlayer(IPv6)
608 tx_ip2 = tx_pkt.getlayer(IP)
609
610 # verify if rx'ed packet has no SRH
611 self.assertFalse(rx_pkt.haslayer(IPv6ExtHdrSegmentRouting))
612
613 # the whole rx_ip pkt should be equal to tx_ip2
614 # except for the ttl field and ip checksum
615 # -> adjust tx'ed ttl to expected ttl
616 tx_ip2.ttl = tx_ip2.ttl - 1
617 # -> set tx'ed ip checksum to None and let scapy recompute
618 tx_ip2.chksum = None
619 # read back the pkt (with str()) to force computing these fields
620 # probably other ways to accomplish this are possible
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700621 tx_ip2 = IP(scapy.compat.raw(tx_ip2))
Francois Clad0928da92018-07-09 16:45:23 +0200622
623 self.assertEqual(rx_ip, tx_ip2)
624
625 self.logger.debug("packet verification: SUCCESS")
626
Francois Cladb02f3b72018-07-04 11:47:21 +0200627 def compare_rx_tx_packet_End_AS_L2_out(self, tx_pkt, rx_pkt):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200628 """Compare input and output packet after passing End.AS with L2
Francois Cladb02f3b72018-07-04 11:47:21 +0200629
630 :param tx_pkt: transmitted packet
631 :param rx_pkt: received packet
632 """
633
634 # get IPv4 header of rx'ed packet
635 rx_eth = rx_pkt.getlayer(Ether)
636
637 tx_ip = tx_pkt.getlayer(IPv6)
638 # we can't just get the 2nd Ether layer
639 # get the Raw content and dissect it as Ether
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700640 tx_eth1 = Ether(scapy.compat.raw(tx_pkt[Raw]))
Francois Cladb02f3b72018-07-04 11:47:21 +0200641
642 # verify if rx'ed packet has no SRH
643 self.assertFalse(rx_pkt.haslayer(IPv6ExtHdrSegmentRouting))
644
645 # the whole rx_eth pkt should be equal to tx_eth1
646 self.assertEqual(rx_eth, tx_eth1)
647
648 self.logger.debug("packet verification: SUCCESS")
649
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200650 def create_stream(self, src_if, dst_if, packet_header, packet_sizes, count):
Francois Clad0928da92018-07-09 16:45:23 +0200651 """Create SRv6 input packet stream for defined interface.
652
653 :param VppInterface src_if: Interface to create packet stream for
654 :param VppInterface dst_if: destination interface of packet stream
655 :param packet_header: Layer3 scapy packet headers,
656 L2 is added when not provided,
657 Raw(payload) with packet_info is added
658 :param list packet_sizes: packet stream pckt sizes,sequentially applied
659 to packets in stream have
660 :param int count: number of packets in packet stream
661 :return: list of packets
662 """
663 self.logger.info("Creating packets")
664 pkts = []
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200665 for i in range(0, count - 1):
Francois Clad0928da92018-07-09 16:45:23 +0200666 payload_info = self.create_packet_info(src_if, dst_if)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200667 self.logger.debug("Creating packet with index %d" % (payload_info.index))
Francois Clad0928da92018-07-09 16:45:23 +0200668 payload = self.info_to_payload(payload_info)
669 # add L2 header if not yet provided in packet_header
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200670 if packet_header.getlayer(0).name == "Ethernet":
671 p = packet_header / Raw(payload)
Francois Clad0928da92018-07-09 16:45:23 +0200672 else:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200673 p = (
674 Ether(dst=src_if.local_mac, src=src_if.remote_mac)
675 / packet_header
676 / Raw(payload)
677 )
Francois Clad0928da92018-07-09 16:45:23 +0200678 size = packet_sizes[i % len(packet_sizes)]
679 self.logger.debug("Packet size %d" % (size))
680 self.extend_packet(p, size)
681 # we need to store the packet with the automatic fields computed
682 # read back the dumped packet (with str())
683 # to force computing these fields
684 # probably other ways are possible
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700685 p = Ether(scapy.compat.raw(p))
Francois Clad0928da92018-07-09 16:45:23 +0200686 payload_info.data = p.copy()
687 self.logger.debug(ppp("Created packet:", p))
688 pkts.append(p)
689 self.logger.info("Done creating packets")
690 return pkts
691
692 def send_and_verify_pkts(self, input, pkts, output, compare_func):
693 """Send packets and verify received packets using compare_func
694
695 :param input: ingress interface of DUT
696 :param pkts: list of packets to transmit
697 :param output: egress interface of DUT
698 :param compare_func: function to compare in and out packets
699 """
700 # add traffic stream to input interface
701 input.add_stream(pkts)
702
703 # enable capture on all interfaces
704 self.pg_enable_capture(self.pg_interfaces)
705
706 # start traffic
707 self.logger.info("Starting traffic")
708 self.pg_start()
709
710 # get output capture
711 self.logger.info("Getting packet capture")
712 capture = output.get_capture()
713
714 # assert nothing was captured on input interface
715 # input.assert_nothing_captured()
716
717 # verify captured packets
718 self.verify_captured_pkts(output, capture, compare_func)
719
720 def create_packet_header_IPv6(self):
721 """Create packet header: IPv6 header, UDP header
722
723 :param dst: IPv6 destination address
724
725 IPv6 source address is 1234::1
726 IPv6 destination address is 4321::1
727 UDP source port and destination port are 1234
728 """
729
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200730 p = IPv6(src="1234::1", dst="4321::1") / UDP(sport=1234, dport=1234)
Francois Clad0928da92018-07-09 16:45:23 +0200731 return p
732
733 def create_packet_header_IPv6_SRH_IPv6(self, sidlist, segleft):
734 """Create packet header: IPv6 encapsulated in SRv6:
735 IPv6 header with SRH, IPv6 header, UDP header
736
737 :param list sidlist: segment list of outer IPv6 SRH
738 :param int segleft: segments-left field of outer IPv6 SRH
739
740 Outer IPv6 source address is set to 5678::1
741 Outer IPv6 destination address is set to sidlist[segleft]
742 IPv6 source addresses is 1234::1
743 IPv6 destination address is 4321::1
744 UDP source port and destination port are 1234
745 """
746
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200747 p = (
748 IPv6(src="5678::1", dst=sidlist[segleft])
749 / IPv6ExtHdrSegmentRouting(addresses=sidlist, segleft=segleft, nh=41)
750 / IPv6(src="1234::1", dst="4321::1")
751 / UDP(sport=1234, dport=1234)
752 )
Francois Clad0928da92018-07-09 16:45:23 +0200753 return p
754
755 def create_packet_header_IPv4(self):
756 """Create packet header: IPv4 header, UDP header
757
758 :param dst: IPv4 destination address
759
760 IPv4 source address is 123.1.1.1
761 IPv4 destination address is 124.1.1.1
762 UDP source port and destination port are 1234
763 """
764
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200765 p = IP(src="123.1.1.1", dst="124.1.1.1") / UDP(sport=1234, dport=1234)
Francois Clad0928da92018-07-09 16:45:23 +0200766 return p
767
768 def create_packet_header_IPv6_SRH_IPv4(self, sidlist, segleft):
769 """Create packet header: IPv4 encapsulated in SRv6:
770 IPv6 header with SRH, IPv4 header, UDP header
771
772 :param ipv4address dst: inner IPv4 destination address
773 :param list sidlist: segment list of outer IPv6 SRH
774 :param int segleft: segments-left field of outer IPv6 SRH
775
776 Outer IPv6 destination address is set to sidlist[segleft]
777 IPv6 source address is 1234::1
778 IPv4 source address is 123.1.1.1
779 IPv4 destination address is 124.1.1.1
780 UDP source port and destination port are 1234
781 """
782
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200783 p = (
784 IPv6(src="1234::1", dst=sidlist[segleft])
785 / IPv6ExtHdrSegmentRouting(addresses=sidlist, segleft=segleft, nh=4)
786 / IP(src="123.1.1.1", dst="124.1.1.1")
787 / UDP(sport=1234, dport=1234)
788 )
Francois Clad0928da92018-07-09 16:45:23 +0200789 return p
790
Francois Cladb02f3b72018-07-04 11:47:21 +0200791 def create_packet_header_L2(self, vlan=0):
792 """Create packet header: L2 header
793
794 :param vlan: if vlan!=0 then add 802.1q header
795 """
796 # Note: the dst addr ('00:55:44:33:22:11') is used in
797 # the compare function compare_rx_tx_packet_T_Encaps_L2
798 # to detect presence of L2 in SRH payload
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200799 p = Ether(src="00:11:22:33:44:55", dst="00:55:44:33:22:11")
Francois Cladb02f3b72018-07-04 11:47:21 +0200800 etype = 0x8137 # IPX
801 if vlan:
802 # add 802.1q layer
803 p /= Dot1Q(vlan=vlan, type=etype)
804 else:
805 p.type = etype
806 return p
807
808 def create_packet_header_IPv6_SRH_L2(self, sidlist, segleft, vlan=0):
809 """Create packet header: L2 encapsulated in SRv6:
810 IPv6 header with SRH, L2
811
812 :param list sidlist: segment list of outer IPv6 SRH
813 :param int segleft: segments-left field of outer IPv6 SRH
814 :param vlan: L2 vlan; if vlan!=0 then add 802.1q header
815
816 Outer IPv6 destination address is set to sidlist[segleft]
817 IPv6 source address is 1234::1
818 """
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200819 eth = Ether(src="00:11:22:33:44:55", dst="00:55:44:33:22:11")
Francois Cladb02f3b72018-07-04 11:47:21 +0200820 etype = 0x8137 # IPX
821 if vlan:
822 # add 802.1q layer
823 eth /= Dot1Q(vlan=vlan, type=etype)
824 else:
825 eth.type = etype
826
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200827 p = (
828 IPv6(src="1234::1", dst=sidlist[segleft])
829 / IPv6ExtHdrSegmentRouting(addresses=sidlist, segleft=segleft, nh=143)
830 / eth
831 )
Francois Cladb02f3b72018-07-04 11:47:21 +0200832 return p
833
Francois Clad0928da92018-07-09 16:45:23 +0200834 def get_payload_info(self, packet):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200835 """Extract the payload_info from the packet"""
Francois Clad0928da92018-07-09 16:45:23 +0200836 # in most cases, payload_info is in packet[Raw]
837 # but packet[Raw] gives the complete payload
838 # (incl L2 header) for the T.Encaps L2 case
839 try:
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800840 payload_info = self.payload_to_info(packet[Raw])
Francois Clad0928da92018-07-09 16:45:23 +0200841
842 except:
843 # remote L2 header from packet[Raw]:
844 # take packet[Raw], convert it to an Ether layer
845 # and then extract Raw from it
846 payload_info = self.payload_to_info(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200847 Ether(scapy.compat.raw(packet[Raw]))[Raw]
848 )
Francois Clad0928da92018-07-09 16:45:23 +0200849
850 return payload_info
851
852 def verify_captured_pkts(self, dst_if, capture, compare_func):
853 """
854 Verify captured packet stream for specified interface.
855 Compare ingress with egress packets using the specified compare fn
856
857 :param dst_if: egress interface of DUT
858 :param capture: captured packets
859 :param compare_func: function to compare in and out packet
860 """
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200861 self.logger.info(
862 "Verifying capture on interface %s using function %s"
863 % (dst_if.name, compare_func.__name__)
864 )
Francois Clad0928da92018-07-09 16:45:23 +0200865
866 last_info = dict()
867 for i in self.pg_interfaces:
868 last_info[i.sw_if_index] = None
869 dst_sw_if_index = dst_if.sw_if_index
870
871 for packet in capture:
872 try:
873 # extract payload_info from packet's payload
874 payload_info = self.get_payload_info(packet)
875 packet_index = payload_info.index
876
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200877 self.logger.debug("Verifying packet with index %d" % (packet_index))
Francois Clad0928da92018-07-09 16:45:23 +0200878 # packet should have arrived on the expected interface
879 self.assertEqual(payload_info.dst, dst_sw_if_index)
880 self.logger.debug(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200881 "Got packet on interface %s: src=%u (idx=%u)"
882 % (dst_if.name, payload_info.src, packet_index)
883 )
Francois Clad0928da92018-07-09 16:45:23 +0200884
885 # search for payload_info with same src and dst if_index
886 # this will give us the transmitted packet
887 next_info = self.get_next_packet_info_for_interface2(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200888 payload_info.src, dst_sw_if_index, last_info[payload_info.src]
889 )
Francois Clad0928da92018-07-09 16:45:23 +0200890 last_info[payload_info.src] = next_info
891 # next_info should not be None
892 self.assertTrue(next_info is not None)
893 # index of tx and rx packets should be equal
894 self.assertEqual(packet_index, next_info.index)
895 # data field of next_info contains the tx packet
896 txed_packet = next_info.data
897
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200898 self.logger.debug(
899 ppp("Transmitted packet:", txed_packet)
900 ) # ppp=Pretty Print Packet
Francois Clad0928da92018-07-09 16:45:23 +0200901
902 self.logger.debug(ppp("Received packet:", packet))
903
904 # compare rcvd packet with expected packet using compare_func
905 compare_func(txed_packet, packet)
906
907 except:
Francois Clad0928da92018-07-09 16:45:23 +0200908 self.logger.error(ppp("Unexpected or invalid packet:", packet))
909 raise
910
911 # have all expected packets arrived?
912 for i in self.pg_interfaces:
913 remaining_packet = self.get_next_packet_info_for_interface2(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200914 i.sw_if_index, dst_sw_if_index, last_info[i.sw_if_index]
915 )
916 self.assertTrue(
917 remaining_packet is None,
918 "Interface %s: Packet expected from interface %s "
919 "didn't arrive" % (dst_if.name, i.name),
920 )
Francois Clad0928da92018-07-09 16:45:23 +0200921
922
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200923if __name__ == "__main__":
Francois Clad0928da92018-07-09 16:45:23 +0200924 unittest.main(testRunner=VppTestRunner)