summaryrefslogtreecommitdiffstats
path: root/test/test_p2p_ethernet.py
blob: 75c390144179f8226cfd4b3e01c5ccf1103d6192 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
#!/usr/bin/env python
import random
import unittest
import datetime
import re

from scapy.packet import Raw
from scapy.layers.l2 import Ether
from scapy.layers.inet import IP, UDP
from scapy.layers.inet6 import IPv6

from framework import VppTestCase, VppTestRunner
from vpp_sub_interface import VppP2PSubint
from vpp_ip import DpoProto
from vpp_ip_route import VppIpRoute, VppRoutePath
from vpp_papi import mac_pton


class P2PEthernetAPI(VppTestCase):
    """P2P Ethernet tests"""

    p2p_sub_ifs = []

    @classmethod
    def setUpClass(cls):
        super(P2PEthernetAPI, cls).setUpClass()

        # Create pg interfaces
        cls.create_pg_interfaces(range(4))

        # Set up all interfaces
        for i in cls.pg_interfaces:
            i.admin_up()

    def create_p2p_ethernet(self, parent_if, sub_id, remote_mac):
        p2p = VppP2PSubint(self, parent_if, sub_id, mac_pton(remote_mac))
        self.p2p_sub_ifs.append(p2p)

    def delete_p2p_ethernet(self, parent_if, remote_mac):
        self.vapi.p2p_ethernet_del(parent_if.sw_if_index,
                                   mac_pton(remote_mac))

    def test_api(self):
        """delete/create p2p subif"""
        self.logger.info("FFP_TEST_START_0000")

        self.create_p2p_ethernet(self.pg0, 1, "de:ad:00:00:00:01")
        self.create_p2p_ethernet(self.pg0, 2, "de:ad:00:00:00:02")
        intfs = self.vapi.cli("show interface")

        self.assertNotEqual(intfs.find('pg0.1'), -1)
        self.assertNotEqual(intfs.find('pg0.2'), -1)
        self.assertEqual(intfs.find('pg0.5'), -1)

        # create pg2.5 subif
        self.create_p2p_ethernet(self.pg0, 5, "de:ad:00:00:00:ff")
        intfs = self.vapi.cli("show interface")
        self.assertNotEqual(intfs.find('pg0.5'), -1)
        # delete pg2.5 subif
        self.delete_p2p_ethernet(self.pg0, "de:ad:00:00:00:ff")

        intfs = self.vapi.cli("show interface")

        self.assertNotEqual(intfs.find('pg0.1'), -1)
        self.assertNotEqual(intfs.find('pg0.2'), -1)
        self.assertEqual(intfs.find('pg0.5'), -1)

        self.logger.info("FFP_TEST_FINISH_0000")

    def test_p2p_subif_creation_1k(self):
        """create 1k of p2p subifs"""
        self.logger.info("FFP_TEST_START_0001")

        macs = []
        clients = 1000
        mac = int("dead00000000", 16)

        for i in range(1, clients+1):
            try:
                macs.append(':'.join(re.findall('..', '{:02x}'.format(mac+i))))
                self.vapi.p2p_ethernet_add(self.pg2.sw_if_index,
                                           mac_pton(macs[i-1]),
                                           i)
            except Exception:
                self.logger.info("Failed to create subif %d %s" % (
                    i, macs[i-1]))
                raise

        intfs = self.vapi.cli("show interface").split("\n")
        count = 0
        for intf in intfs:
            if intf.startswith('pg2.'):
                count += 1
        self.assertEqual(count, clients)

        self.logger.info("FFP_TEST_FINISH_0001")


class P2PEthernetIPV6(VppTestCase):
    """P2P Ethernet IPv6 tests"""

    p2p_sub_ifs = []
    packets = []

    @classmethod
    def setUpClass(cls):
        super(P2PEthernetIPV6, cls).setUpClass()

        # Create pg interfaces
        cls.create_pg_interfaces(range(3))

        # Packet sizes
        cls.pg_if_packet_sizes = [64, 512, 1518, 9018]

        # Set up all interfaces
        for i in cls.pg_interfaces:
            i.admin_up()

        cls.pg0.generate_remote_hosts(3)
        cls.pg0.configure_ipv6_neighbors()

        cls.pg1.config_ip6()
        cls.pg1.generate_remote_hosts(3)
        cls.pg1.configure_ipv6_neighbors()
        cls.pg1.disable_ipv6_ra()

    def setUp(self):
        super(P2PEthernetIPV6, self).setUp()
        for p in self.packets:
            self.packets.remove(p)
        self.p2p_sub_ifs.append(
            self.create_p2p_ethernet(self.pg0, 1,
                                     self.pg0._remote_hosts[0].mac))
        self.p2p_sub_ifs.append(
            self.create_p2p_ethernet(self.pg0, 2,
                                     self.pg0._remote_hosts[1].mac))
        self.vapi.cli("trace add p2p-ethernet-input 50")

    def tearDown(self):
        while len(self.p2p_sub_ifs):
            p2p = self.p2p_sub_ifs.pop()
            self.delete_p2p_ethernet(p2p)

        super(P2PEthernetIPV6, self).tearDown()

    def create_p2p_ethernet(self, parent_if, sub_id, remote_mac):
        p2p = VppP2PSubint(self, parent_if, sub_id, mac_pton(remote_mac))
        p2p.admin_up()
        p2p.config_ip6()
        p2p.disable_ipv6_ra()
        return p2p

    def delete_p2p_ethernet(self, p2p):
        p2p.unconfig_ip6()
        p2p.admin_down()
        self.vapi.p2p_ethernet_del(p2p.parent.sw_if_index,
                                   p2p.p2p_remote_mac)

    def create_stream(self, src_mac=None, dst_mac=None,
                      src_ip=None, dst_ip=None, size=None):
        pkt_size = size
        if size is None:
            pkt_size = random.choice(self.pg_if_packet_sizes)
        p = Ether(src=src_mac, dst=dst_mac)
        p /= IPv6(src=src_ip, dst=dst_ip)
        p /= (UDP(sport=1234, dport=4321) / Raw('\xa5' * 20))
        self.extend_packet(p, pkt_size)
        return p

    def send_packets(self, src_if=None, dst_if=None, packets=None, count=None):
        self.pg_enable_capture([dst_if])
        if packets is None:
            packets = self.packets
        src_if.add_stream(packets)
        self.pg_start()
        if count is None:
            count = len(packets)
        return dst_if.get_capture(count)

    def test_no_p2p_subif(self):
        """standard routing without p2p subinterfaces"""
        self.logger.info("FFP_TEST_START_0001")

        route_8000 = VppIpRoute(self, "8000::", 64,
                                [VppRoutePath(self.pg0.remote_ip6,
                                              self.pg0.sw_if_index,
                                              proto=DpoProto.DPO_PROTO_IP6)],
                                is_ip6=1)
        route_8000.add_vpp_config()

        self.packets = [(Ether(dst=self.pg1.local_mac,
                               src=self.pg1.remote_mac) /
                         IPv6(src="3001::1", dst="8000::100") /
                         UDP(sport=1234, dport=1234) /
                         Raw('\xa5' * 100))]
        self.send_packets(self.pg1, self.pg0)

        self.logger.info("FFP_TEST_FINISH_0001")

    def test_ip6_rx_p2p_subif(self):
        """receive ipv6 packet via p2p subinterface"""
        self.logger.info("FFP_TEST_START_0002")

        route_9001 = VppIpRoute(self, "9001::", 64,
                                [VppRoutePath(self.pg1.remote_ip6,
                                              self.pg1.sw_if_index,
                                              proto=DpoProto.DPO_PROTO_IP6)],
                                is_ip6=1)
        route_9001.add_vpp_config()

        self.packets.append(
            self.create_stream(src_mac=self.pg0._remote_hosts[0].mac,
                               dst_mac=self.pg0.local_mac,
                               src_ip=self.p2p_sub_ifs[0].remote_ip6,
                               dst_ip="9001::100"))

        self.send_packets(self.pg0, self.pg1, self.packets)
        self.assert_packet_counter_equal('p2p-ethernet-input', 1)

        route_9001.remove_vpp_config()
        self.logger.info("FFP_TEST_FINISH_0002")

    def test_ip6_rx_p2p_subif_route(self):
        """route rx ip6 packet not matching p2p subinterface"""
        self.logger.info("FFP_TEST_START_0003")

        self.pg0.config_ip6()

        route_3 = VppIpRoute(self, "9000::", 64,
                             [VppRoutePath(self.pg1._remote_hosts[0].ip6,
                                           self.pg1.sw_if_index,
                                           proto=DpoProto.DPO_PROTO_IP6)],
                             is_ip6=1)
        route_3.add_vpp_config()

        self.packets.append(
            self.create_stream(src_mac="02:03:00:00:ff:ff",
                               dst_mac=self.pg0.local_mac,
                               src_ip="a000::100",
                               dst_ip="9000::100"))

        self.send_packets(self.pg0, self.pg1)

        self.pg0.unconfig_ip6()

        route_3.remove_vpp_config()

        self.logger.info("FFP_TEST_FINISH_0003")

    def test_ip6_rx_p2p_subif_drop(self):
        """drop rx packet not matching p2p subinterface"""
        self.logger.info("FFP_TEST_START_0004")

        route_9001 = VppIpRoute(self, "9000::", 64,
                                [VppRoutePath(self.pg1._remote_hosts[0].ip6,
                                              self.pg1.sw_if_index,
                                              proto=DpoProto.DPO_PROTO_IP6)],
                                is_ip6=1)
        route_9001.add_vpp_config()

        self.packets.append(
            self.create_stream(src_mac="02:03:00:00:ff:ff",
                               dst_mac=self.pg0.local_mac,
                               src_ip="a000::100",
                               dst_ip="9000::100"))

        # no packet received
        self.send_packets(self.pg0, self.pg1, count=0)
        self.logger.info("FFP_TEST_FINISH_0004")

    def test_ip6_tx_p2p_subif(self):
        """send packet via p2p subinterface"""
        self.logger.info("FFP_TEST_START_0005")

        route_8000 = VppIpRoute(self, "8000::", 64,
                                [VppRoutePath(self.pg0.remote_ip6,
                                              self.pg0.sw_if_index,
                                              proto=DpoProto.DPO_PROTO_IP6)],
                                is_ip6=1)
        route_8000.add_vpp_config()
        route_8001 = VppIpRoute(self, "8001::", 64,
                                [VppRoutePath(self.p2p_sub_ifs[0].remote_ip6,
                                              self.p2p_sub_ifs[0].sw_if_index,
                                              proto=DpoProto.DPO_PROTO_IP6)],
                                is_ip6=1)
        route_8001.add_vpp_config()
        route_8002 = VppIpRoute(self, "8002::", 64,
                                [VppRoutePath(self.p2p_sub_ifs[1].remote_ip6,
                                              self.p2p_sub_ifs[1].sw_if_index,
                                              proto=DpoProto.DPO_PROTO_IP6)],
                                is_ip6=1)
        route_8002.add_vpp_config()

        for i in range(0, 3):
            self.packets.append(
                self.create_stream(src_mac=self.pg1.remote_mac,
                                   dst_mac=self.pg1.local_mac,
                                   src_ip=self.pg1.remote_ip6,
                                   dst_ip="800%d::100" % i))

        self.send_packets(self.pg1, self.pg0, count=3)

        route_8000.remove_vpp_config()
        route_8001.remove_vpp_config()
        route_8002.remove_vpp_config()

        self.logger.info("FFP_TEST_FINISH_0005")

    def test_ip6_tx_p2p_subif_drop(self):
        """drop tx ip6 packet not matching p2p subinterface"""
        self.logger.info("FFP_TEST_START_0006")

        self.packets.append(
            self.create_stream(src_mac="02:03:00:00:ff:ff",
                               dst_mac=self.pg0.local_mac,
                               src_ip="a000::100",
                               dst_ip="9000::100"))

        # no packet received
        self.send_packets(self.pg0, self.pg1, count=0)
        self.logger.info("FFP_TEST_FINISH_0006")


class P2PEthernetIPV4(VppTestCase):
    """P2P Ethernet IPv4 tests"""

    p2p_sub_ifs = []
    packets = []

    @classmethod
    def setUpClass(cls):
        super(P2PEthernetIPV4, cls).setUpClass()

        # Create pg interfaces
        cls.create_pg_interfaces(range(3))

        # Packet sizes
        cls.pg_if_packet_sizes = [64, 512, 1518, 9018]

        # Set up all interfaces
        for i in cls.pg_interfaces:
            i.admin_up()

        cls.pg0.config_ip4()
        cls.pg0.generate_remote_hosts(5)
        cls.pg0.configure_ipv4_neighbors()

        cls.pg1.config_ip4()
        cls.pg1.generate_remote_hosts(5)
        cls.pg1.configure_ipv4_neighbors()

    def setUp(self):
        super(P2PEthernetIPV4, self).setUp()
        for p in self.packets:
            self.packets.remove(p)
        self.p2p_sub_ifs.append(
            self.create_p2p_ethernet(self.pg0, 1,
                                     self.pg0._remote_hosts[0].mac))
        self.p2p_sub_ifs.append(
            self.create_p2p_ethernet(self.pg0, 2,
                                     self.pg0._remote_hosts[1].mac))
        self.vapi.cli("trace add p2p-ethernet-input 50")

    def tearDown(self):
        while len(self.p2p_sub_ifs):
            p2p = self.p2p_sub_ifs.pop()
            self.delete_p2p_ethernet(p2p)
        super(P2PEthernetIPV4, self).tearDown()

    def create_stream(self, src_mac=None, dst_mac=None,
                      src_ip=None, dst_ip=None, size=None):
        pkt_size = size
        if size is None:
            pkt_size = random.choice(self.pg_if_packet_sizes)
        p = Ether(src=src_mac, dst=dst_mac)
        p /= IP(src=src_ip, dst=dst_ip)
        p /= (UDP(sport=1234, dport=4321) / Raw('\xa5' * 20))
        self.extend_packet(p, pkt_size)
        return p

    def send_packets(self, src_if=None, dst_if=None, packets=None, count=None):
        self.pg_enable_capture([dst_if])
        if packets is None:
            packets = self.packets
        src_if.add_stream(packets)
        self.pg_start()
        if count is None:
            count = len(packets)
        return dst_if.get_capture(count)

    def create_p2p_ethernet(self, parent_if, sub_id, remote_mac):
        p2p = VppP2PSubint(self, parent_if, sub_id, mac_pton(remote_mac))
        p2p.admin_up()
        p2p.config_ip4()
        return p2p

    def delete_p2p_ethernet(self, p2p):
        p2p.unconfig_ip4()
        p2p.admin_down()
        self.vapi.p2p_ethernet_del(p2p.parent.sw_if_index,
                                   p2p.p2p_remote_mac)

    def test_ip4_rx_p2p_subif(self):
        """receive ipv4 packet via p2p subinterface"""
        self.logger.info("FFP_TEST_START_0002")

        route_9000 = VppIpRoute(self, "9.0.0.0", 16,
                                [VppRoutePath(self.pg1.remote_ip4,
                                              self.pg1.sw_if_index)])
        route_9000.add_vpp_config()

        self.packets.append(
            self.create_stream(src_mac=self.pg0._remote_hosts[0].mac,
                               dst_mac=self.pg0.local_mac,
                               src_ip=self.p2p_sub_ifs[0].remote_ip4,
                               dst_ip="9.0.0.100"))

        self.send_packets(self.pg0, self.pg1, self.packets)

        self.assert_packet_counter_equal('p2p-ethernet-input', 1)

        route_9000.remove_vpp_config()
        self.logger.info("FFP_TEST_FINISH_0002")

    def test_ip4_rx_p2p_subif_route(self):
        """route rx packet not matching p2p subinterface"""
        self.logger.info("FFP_TEST_START_0003")

        route_9001 = VppIpRoute(self, "9.0.0.0", 24,
                                [VppRoutePath(self.pg1.remote_ip4,
                                              self.pg1.sw_if_index)])
        route_9001.add_vpp_config()

        self.packets.append(
            self.create_stream(src_mac="02:01:00:00:ff:ff",
                               dst_mac=self.pg0.local_mac,
                               src_ip="8.0.0.100",
                               dst_ip="9.0.0.100"))

        self.send_packets(self.pg0, self.pg1)

        route_9001.remove_vpp_config()

        self.logger.info("FFP_TEST_FINISH_0003")

    def test_ip4_tx_p2p_subif(self):
        """send ip4 packet via p2p subinterface"""
        self.logger.info("FFP_TEST_START_0005")

        route_9100 = VppIpRoute(self, "9.1.0.100", 24,
                                [VppRoutePath(self.pg0.remote_ip4,
                                              self.pg0.sw_if_index,
                                              )])
        route_9100.add_vpp_config()
        route_9200 = VppIpRoute(self, "9.2.0.100", 24,
                                [VppRoutePath(self.p2p_sub_ifs[0].remote_ip4,
                                              self.p2p_sub_ifs[0].sw_if_index,
                                              )])
        route_9200.add_vpp_config()
        route_9300 = VppIpRoute(self, "9.3.0.100", 24,
                                [VppRoutePath(self.p2p_sub_ifs[1].remote_ip4,
                                              self.p2p_sub_ifs[1].sw_if_index
                                              )])
        route_9300.add_vpp_config()

        for i in range(0, 3):
            self.packets.append(
                self.create_stream(src_mac=self.pg1.remote_mac,
                                   dst_mac=self.pg1.local_mac,
                                   src_ip=self.pg1.remote_ip4,
                                   dst_ip="9.%d.0.100" % (i+1)))

        self.send_packets(self.pg1, self.pg0)

        # route_7000.remove_vpp_config()
        route_9100.remove_vpp_config()
        route_9200.remove_vpp_config()
        route_9300.remove_vpp_config()

        self.logger.info("FFP_TEST_FINISH_0005")

    def test_ip4_tx_p2p_subif_drop(self):
        """drop tx ip4 packet not matching p2p subinterface"""
        self.logger.info("FFP_TEST_START_0006")

        self.packets.append(
            self.create_stream(src_mac="02:01:00:00:ff:ff",
                               dst_mac=self.pg0.local_mac,
                               src_ip="8.0.0.100",
                               dst_ip="9.0.0.100"))

        # no packet received
        self.send_packets(self.pg0, self.pg1, count=0)
        self.logger.info("FFP_TEST_FINISH_0006")


if __name__ == '__main__':
    unittest.main(testRunner=VppTestRunner)
class="p">(scapy.compat.raw(tx_ip)) self.assertEqual(payload, tx_ip) self.logger.debug("packet verification: SUCCESS") def compare_rx_tx_packet_End_AS_L2_in(self, tx_pkt, rx_pkt): """Compare input and output packet after passing End.AS :param tx_pkt: transmitted packet :param rx_pkt: received packet """ # get first (outer) IPv6 header of rx'ed packet rx_ip = rx_pkt.getlayer(IPv6) rx_srh = None tx_ether = tx_pkt.getlayer(Ether) # expected segment-list (SRH order) tx_seglist = self.rewrite_sid_list[::-1] # received ip.src should be equal to SR Policy source self.assertEqual(rx_ip.src, self.rewrite_src_addr) # received ip.dst should be equal to expected sidlist[lastentry] self.assertEqual(rx_ip.dst, tx_seglist[-1]) if len(tx_seglist) > 1: # rx'ed packet should have SRH self.assertTrue(rx_pkt.haslayer(IPv6ExtHdrSegmentRouting)) # get SRH rx_srh = rx_pkt.getlayer(IPv6ExtHdrSegmentRouting) # rx'ed seglist should be equal to seglist self.assertEqual(rx_srh.addresses, tx_seglist) # segleft should be equal to size seglist-1 self.assertEqual(rx_srh.segleft, len(tx_seglist) - 1) # segleft should be equal to lastentry self.assertEqual(rx_srh.segleft, rx_srh.lastentry) # nh should be "No Next Header" (143) self.assertEqual(rx_srh.nh, 143) # get payload payload = rx_srh.payload else: # rx'ed packet should NOT have SRH self.assertFalse(rx_pkt.haslayer(IPv6ExtHdrSegmentRouting)) # get payload payload = rx_ip.payload # the whole rx'ed pkt beyond SRH should be equal to tx'ed pkt self.assertEqual(Ether(scapy.compat.raw(payload)), tx_ether) self.logger.debug("packet verification: SUCCESS") def compare_rx_tx_packet_End_AS_IPv6_out(self, tx_pkt, rx_pkt): """Compare input and output packet after passing End.AS with IPv6 :param tx_pkt: transmitted packet :param rx_pkt: received packet """ # get first (outer) IPv6 header of rx'ed packet rx_ip = rx_pkt.getlayer(IPv6) tx_ip = tx_pkt.getlayer(IPv6) tx_ip2 = tx_pkt.getlayer(IPv6, 2) # verify if rx'ed packet has no SRH self.assertFalse(rx_pkt.haslayer(IPv6ExtHdrSegmentRouting)) # the whole rx_ip pkt should be equal to tx_ip2 # except for the hlim field # -> adjust tx'ed hlim to expected hlim tx_ip2.hlim = tx_ip2.hlim - 1 self.assertEqual(rx_ip, tx_ip2) self.logger.debug("packet verification: SUCCESS") def compare_rx_tx_packet_End_AS_IPv4_out(self, tx_pkt, rx_pkt): """Compare input and output packet after passing End.AS with IPv4 :param tx_pkt: transmitted packet :param rx_pkt: received packet """ # get IPv4 header of rx'ed packet rx_ip = rx_pkt.getlayer(IP) tx_ip = tx_pkt.getlayer(IPv6) tx_ip2 = tx_pkt.getlayer(IP) # verify if rx'ed packet has no SRH self.assertFalse(rx_pkt.haslayer(IPv6ExtHdrSegmentRouting)) # the whole rx_ip pkt should be equal to tx_ip2 # except for the ttl field and ip checksum # -> adjust tx'ed ttl to expected ttl tx_ip2.ttl = tx_ip2.ttl - 1 # -> set tx'ed ip checksum to None and let scapy recompute tx_ip2.chksum = None # read back the pkt (with str()) to force computing these fields # probably other ways to accomplish this are possible tx_ip2 = IP(scapy.compat.raw(tx_ip2)) self.assertEqual(rx_ip, tx_ip2) self.logger.debug("packet verification: SUCCESS") def compare_rx_tx_packet_End_AS_L2_out(self, tx_pkt, rx_pkt): """Compare input and output packet after passing End.AS with L2 :param tx_pkt: transmitted packet :param rx_pkt: received packet """ # get IPv4 header of rx'ed packet rx_eth = rx_pkt.getlayer(Ether) tx_ip = tx_pkt.getlayer(IPv6) # we can't just get the 2nd Ether layer # get the Raw content and dissect it as Ether tx_eth1 = Ether(scapy.compat.raw(tx_pkt[Raw])) # verify if rx'ed packet has no SRH self.assertFalse(rx_pkt.haslayer(IPv6ExtHdrSegmentRouting)) # the whole rx_eth pkt should be equal to tx_eth1 self.assertEqual(rx_eth, tx_eth1) self.logger.debug("packet verification: SUCCESS") def create_stream(self, src_if, dst_if, packet_header, packet_sizes, count): """Create SRv6 input packet stream for defined interface. :param VppInterface src_if: Interface to create packet stream for :param VppInterface dst_if: destination interface of packet stream :param packet_header: Layer3 scapy packet headers, L2 is added when not provided, Raw(payload) with packet_info is added :param list packet_sizes: packet stream pckt sizes,sequentially applied to packets in stream have :param int count: number of packets in packet stream :return: list of packets """ self.logger.info("Creating packets") pkts = [] for i in range(0, count - 1): payload_info = self.create_packet_info(src_if, dst_if) self.logger.debug("Creating packet with index %d" % (payload_info.index)) payload = self.info_to_payload(payload_info) # add L2 header if not yet provided in packet_header if packet_header.getlayer(0).name == "Ethernet": p = packet_header / Raw(payload) else: p = ( Ether(dst=src_if.local_mac, src=src_if.remote_mac) / packet_header / Raw(payload) ) size = packet_sizes[i % len(packet_sizes)] self.logger.debug("Packet size %d" % (size)) self.extend_packet(p, size) # we need to store the packet with the automatic fields computed # read back the dumped packet (with str()) # to force computing these fields # probably other ways are possible p = Ether(scapy.compat.raw(p)) payload_info.data = p.copy() self.logger.debug(ppp("Created packet:", p)) pkts.append(p) self.logger.info("Done creating packets") return pkts def send_and_verify_pkts(self, input, pkts, output, compare_func): """Send packets and verify received packets using compare_func :param input: ingress interface of DUT :param pkts: list of packets to transmit :param output: egress interface of DUT :param compare_func: function to compare in and out packets """ # add traffic stream to input interface input.add_stream(pkts) # enable capture on all interfaces self.pg_enable_capture(self.pg_interfaces) # start traffic self.logger.info("Starting traffic") self.pg_start() # get output capture self.logger.info("Getting packet capture") capture = output.get_capture() # assert nothing was captured on input interface # input.assert_nothing_captured() # verify captured packets self.verify_captured_pkts(output, capture, compare_func) def create_packet_header_IPv6(self): """Create packet header: IPv6 header, UDP header :param dst: IPv6 destination address IPv6 source address is 1234::1 IPv6 destination address is 4321::1 UDP source port and destination port are 1234 """ p = IPv6(src="1234::1", dst="4321::1") / UDP(sport=1234, dport=1234) return p def create_packet_header_IPv6_SRH_IPv6(self, sidlist, segleft): """Create packet header: IPv6 encapsulated in SRv6: IPv6 header with SRH, IPv6 header, UDP header :param list sidlist: segment list of outer IPv6 SRH :param int segleft: segments-left field of outer IPv6 SRH Outer IPv6 source address is set to 5678::1 Outer IPv6 destination address is set to sidlist[segleft] IPv6 source addresses is 1234::1 IPv6 destination address is 4321::1 UDP source port and destination port are 1234 """ p = ( IPv6(src="5678::1", dst=sidlist[segleft]) / IPv6ExtHdrSegmentRouting(addresses=sidlist, segleft=segleft, nh=41) / IPv6(src="1234::1", dst="4321::1") / UDP(sport=1234, dport=1234) ) return p def create_packet_header_IPv4(self): """Create packet header: IPv4 header, UDP header :param dst: IPv4 destination address IPv4 source address is 123.1.1.1 IPv4 destination address is 124.1.1.1 UDP source port and destination port are 1234 """ p = IP(src="123.1.1.1", dst="124.1.1.1") / UDP(sport=1234, dport=1234) return p def create_packet_header_IPv6_SRH_IPv4(self, sidlist, segleft): """Create packet header: IPv4 encapsulated in SRv6: IPv6 header with SRH, IPv4 header, UDP header :param ipv4address dst: inner IPv4 destination address :param list sidlist: segment list of outer IPv6 SRH :param int segleft: segments-left field of outer IPv6 SRH Outer IPv6 destination address is set to sidlist[segleft] IPv6 source address is 1234::1 IPv4 source address is 123.1.1.1 IPv4 destination address is 124.1.1.1 UDP source port and destination port are 1234 """ p = ( IPv6(src="1234::1", dst=sidlist[segleft]) / IPv6ExtHdrSegmentRouting(addresses=sidlist, segleft=segleft, nh=4) / IP(src="123.1.1.1", dst="124.1.1.1") / UDP(sport=1234, dport=1234) ) return p def create_packet_header_L2(self, vlan=0): """Create packet header: L2 header :param vlan: if vlan!=0 then add 802.1q header """ # Note: the dst addr ('00:55:44:33:22:11') is used in # the compare function compare_rx_tx_packet_T_Encaps_L2 # to detect presence of L2 in SRH payload p = Ether(src="00:11:22:33:44:55", dst="00:55:44:33:22:11") etype = 0x8137 # IPX if vlan: # add 802.1q layer p /= Dot1Q(vlan=vlan, type=etype) else: p.type = etype return p def create_packet_header_IPv6_SRH_L2(self, sidlist, segleft, vlan=0): """Create packet header: L2 encapsulated in SRv6: IPv6 header with SRH, L2 :param list sidlist: segment list of outer IPv6 SRH :param int segleft: segments-left field of outer IPv6 SRH :param vlan: L2 vlan; if vlan!=0 then add 802.1q header Outer IPv6 destination address is set to sidlist[segleft] IPv6 source address is 1234::1 """ eth = Ether(src="00:11:22:33:44:55", dst="00:55:44:33:22:11") etype = 0x8137 # IPX if vlan: # add 802.1q layer eth /= Dot1Q(vlan=vlan, type=etype) else: eth.type = etype p = ( IPv6(src="1234::1", dst=sidlist[segleft]) / IPv6ExtHdrSegmentRouting(addresses=sidlist, segleft=segleft, nh=143) / eth ) return p def get_payload_info(self, packet): """Extract the payload_info from the packet""" # in most cases, payload_info is in packet[Raw] # but packet[Raw] gives the complete payload # (incl L2 header) for the T.Encaps L2 case try: payload_info = self.payload_to_info(packet[Raw]) except: # remote L2 header from packet[Raw]: # take packet[Raw], convert it to an Ether layer # and then extract Raw from it payload_info = self.payload_to_info( Ether(scapy.compat.raw(packet[Raw]))[Raw] ) return payload_info def verify_captured_pkts(self, dst_if, capture, compare_func): """ Verify captured packet stream for specified interface. Compare ingress with egress packets using the specified compare fn :param dst_if: egress interface of DUT :param capture: captured packets :param compare_func: function to compare in and out packet """ self.logger.info( "Verifying capture on interface %s using function %s" % (dst_if.name, compare_func.__name__) ) last_info = dict() for i in self.pg_interfaces: last_info[i.sw_if_index] = None dst_sw_if_index = dst_if.sw_if_index for packet in capture: try: # extract payload_info from packet's payload payload_info = self.get_payload_info(packet) packet_index = payload_info.index self.logger.debug("Verifying packet with index %d" % (packet_index)) # packet should have arrived on the expected interface self.assertEqual(payload_info.dst, dst_sw_if_index) self.logger.debug( "Got packet on interface %s: src=%u (idx=%u)" % (dst_if.name, payload_info.src, packet_index) ) # search for payload_info with same src and dst if_index # this will give us the transmitted packet next_info = self.get_next_packet_info_for_interface2( payload_info.src, dst_sw_if_index, last_info[payload_info.src] ) last_info[payload_info.src] = next_info # next_info should not be None self.assertTrue(next_info is not None) # index of tx and rx packets should be equal self.assertEqual(packet_index, next_info.index) # data field of next_info contains the tx packet txed_packet = next_info.data self.logger.debug( ppp("Transmitted packet:", txed_packet) ) # ppp=Pretty Print Packet self.logger.debug(ppp("Received packet:", packet)) # compare rcvd packet with expected packet using compare_func compare_func(txed_packet, packet) except: self.logger.error(ppp("Unexpected or invalid packet:", packet)) raise # have all expected packets arrived? for i in self.pg_interfaces: remaining_packet = self.get_next_packet_info_for_interface2( i.sw_if_index, dst_sw_if_index, last_info[i.sw_if_index] ) self.assertTrue( remaining_packet is None, "Interface %s: Packet expected from interface %s " "didn't arrive" % (dst_if.name, i.name), ) if __name__ == "__main__": unittest.main(testRunner=VppTestRunner)