c7e97c641b809fef7bb7202c67acd6c361f93f0a
[vpp.git] / test / test_udp.py
1 #!/usr/bin/env python3
2 import unittest
3 from framework import tag_fixme_vpp_workers
4 from framework import VppTestCase, VppTestRunner
5
6 from vpp_udp_encap import find_udp_encap, VppUdpEncap
7 from vpp_udp_decap import VppUdpDecap
8 from vpp_ip_route import (
9     VppIpRoute,
10     VppRoutePath,
11     VppIpTable,
12     VppMplsLabel,
13     VppMplsTable,
14     VppMplsRoute,
15     FibPathType,
16     FibPathProto,
17 )
18 from vpp_neighbor import VppNeighbor
19 from vpp_papi import VppEnum
20
21 from scapy.packet import Raw
22 from scapy.layers.l2 import Ether
23 from scapy.layers.inet import IP, UDP, ICMP
24 from scapy.layers.inet6 import IPv6
25 from scapy.contrib.mpls import MPLS
26
27 NUM_PKTS = 67
28
29
30 @tag_fixme_vpp_workers
31 class TestUdpEncap(VppTestCase):
32     """UDP Encap Test Case"""
33
34     @classmethod
35     def setUpClass(cls):
36         super(TestUdpEncap, cls).setUpClass()
37
38     @classmethod
39     def tearDownClass(cls):
40         super(TestUdpEncap, cls).tearDownClass()
41
42     def setUp(self):
43         super(TestUdpEncap, self).setUp()
44
45         # create 2 pg interfaces
46         self.create_pg_interfaces(range(4))
47
48         # setup interfaces
49         # assign them different tables.
50         table_id = 0
51         self.tables = []
52
53         for i in self.pg_interfaces:
54             i.admin_up()
55
56             if table_id != 0:
57                 tbl = VppIpTable(self, table_id)
58                 tbl.add_vpp_config()
59                 self.tables.append(tbl)
60                 tbl = VppIpTable(self, table_id, is_ip6=1)
61                 tbl.add_vpp_config()
62                 self.tables.append(tbl)
63
64             i.set_table_ip4(table_id)
65             i.set_table_ip6(table_id)
66             i.config_ip4()
67             i.resolve_arp()
68             i.config_ip6()
69             i.resolve_ndp()
70             table_id += 1
71
72     def tearDown(self):
73         for i in self.pg_interfaces:
74             i.unconfig_ip4()
75             i.unconfig_ip6()
76             i.set_table_ip4(0)
77             i.set_table_ip6(0)
78             i.admin_down()
79         super(TestUdpEncap, self).tearDown()
80
81     def validate_outer4(self, rx, encap_obj):
82         self.assertEqual(rx[IP].src, encap_obj.src_ip_s)
83         self.assertEqual(rx[IP].dst, encap_obj.dst_ip_s)
84         self.assertEqual(rx[UDP].sport, encap_obj.src_port)
85         self.assertEqual(rx[UDP].dport, encap_obj.dst_port)
86
87     def validate_outer6(self, rx, encap_obj):
88         self.assertEqual(rx[IPv6].src, encap_obj.src_ip_s)
89         self.assertEqual(rx[IPv6].dst, encap_obj.dst_ip_s)
90         self.assertEqual(rx[UDP].sport, encap_obj.src_port)
91         self.assertEqual(rx[UDP].dport, encap_obj.dst_port)
92
93     def validate_inner4(self, rx, tx, ttl=None):
94         self.assertEqual(rx[IP].src, tx[IP].src)
95         self.assertEqual(rx[IP].dst, tx[IP].dst)
96         if ttl:
97             self.assertEqual(rx[IP].ttl, ttl)
98         else:
99             self.assertEqual(rx[IP].ttl, tx[IP].ttl)
100
101     def validate_inner6(self, rx, tx, hlim=None):
102         self.assertEqual(rx.src, tx[IPv6].src)
103         self.assertEqual(rx.dst, tx[IPv6].dst)
104         if hlim:
105             self.assertEqual(rx.hlim, hlim)
106         else:
107             self.assertEqual(rx.hlim, tx[IPv6].hlim)
108
109     def test_udp_encap(self):
110         """UDP Encap test"""
111
112         #
113         # construct a UDP encap object through each of the peers
114         # v4 through the first two peers, v6 through the second.
115         # The last encap is v4 and is used to check the codepath
116         # where 2 different udp encap objects are processed at the
117         # same time
118         #
119         udp_encap_0 = VppUdpEncap(
120             self, self.pg0.local_ip4, self.pg0.remote_ip4, 330, 440
121         )
122         udp_encap_1 = VppUdpEncap(
123             self, self.pg1.local_ip4, self.pg1.remote_ip4, 331, 441, table_id=1
124         )
125         udp_encap_2 = VppUdpEncap(
126             self, self.pg2.local_ip6, self.pg2.remote_ip6, 332, 442, table_id=2
127         )
128         udp_encap_3 = VppUdpEncap(
129             self, self.pg3.local_ip6, self.pg3.remote_ip6, 333, 443, table_id=3
130         )
131         udp_encap_4 = VppUdpEncap(
132             self, self.pg0.local_ip4, self.pg0.remote_ip4, 334, 444
133         )
134         udp_encap_0.add_vpp_config()
135         udp_encap_1.add_vpp_config()
136         udp_encap_2.add_vpp_config()
137         udp_encap_3.add_vpp_config()
138         udp_encap_4.add_vpp_config()
139
140         self.logger.info(self.vapi.cli("sh udp encap"))
141
142         self.assertTrue(find_udp_encap(self, udp_encap_2))
143         self.assertTrue(find_udp_encap(self, udp_encap_3))
144         self.assertTrue(find_udp_encap(self, udp_encap_0))
145         self.assertTrue(find_udp_encap(self, udp_encap_1))
146         self.assertTrue(find_udp_encap(self, udp_encap_4))
147
148         #
149         # Routes via each UDP encap object - all combinations of v4 and v6.
150         #
151         route_4o4 = VppIpRoute(
152             self,
153             "1.1.0.1",
154             24,
155             [
156                 VppRoutePath(
157                     "0.0.0.0",
158                     0xFFFFFFFF,
159                     type=FibPathType.FIB_PATH_TYPE_UDP_ENCAP,
160                     next_hop_id=udp_encap_0.id,
161                     proto=FibPathProto.FIB_PATH_NH_PROTO_IP4,
162                 )
163             ],
164             table_id=1,
165         )
166         # specific route to match encap4, to test encap of 2 packets using 2
167         # different encap
168         route_4o4_2 = VppIpRoute(
169             self,
170             "1.1.0.2",
171             32,
172             [
173                 VppRoutePath(
174                     "0.0.0.0",
175                     0xFFFFFFFF,
176                     type=FibPathType.FIB_PATH_TYPE_UDP_ENCAP,
177                     next_hop_id=udp_encap_4.id,
178                     proto=FibPathProto.FIB_PATH_NH_PROTO_IP4,
179                 )
180             ],
181             table_id=1,
182         )
183         route_4o6 = VppIpRoute(
184             self,
185             "1.1.2.1",
186             32,
187             [
188                 VppRoutePath(
189                     "0.0.0.0",
190                     0xFFFFFFFF,
191                     type=FibPathType.FIB_PATH_TYPE_UDP_ENCAP,
192                     next_hop_id=udp_encap_2.id,
193                     proto=FibPathProto.FIB_PATH_NH_PROTO_IP4,
194                 )
195             ],
196         )
197         route_6o4 = VppIpRoute(
198             self,
199             "2001::1",
200             128,
201             [
202                 VppRoutePath(
203                     "0.0.0.0",
204                     0xFFFFFFFF,
205                     type=FibPathType.FIB_PATH_TYPE_UDP_ENCAP,
206                     next_hop_id=udp_encap_1.id,
207                     proto=FibPathProto.FIB_PATH_NH_PROTO_IP6,
208                 )
209             ],
210         )
211         route_6o6 = VppIpRoute(
212             self,
213             "2001::3",
214             128,
215             [
216                 VppRoutePath(
217                     "0.0.0.0",
218                     0xFFFFFFFF,
219                     type=FibPathType.FIB_PATH_TYPE_UDP_ENCAP,
220                     next_hop_id=udp_encap_3.id,
221                     proto=FibPathProto.FIB_PATH_NH_PROTO_IP6,
222                 )
223             ],
224         )
225         route_4o6.add_vpp_config()
226         route_6o6.add_vpp_config()
227         route_6o4.add_vpp_config()
228         route_4o4.add_vpp_config()
229         route_4o4_2.add_vpp_config()
230
231         #
232         # 4o4 encap
233         # we add a single packet matching the last encap at the beginning of
234         # the packet vector so that we encap 2 packets with different udp
235         # encap object at the same time
236         #
237         p_4o4 = (
238             Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac)
239             / IP(src="2.2.2.2", dst="1.1.0.1")
240             / UDP(sport=1234, dport=1234)
241             / Raw(b"\xa5" * 100)
242         )
243         p_4o4_2 = (
244             Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac)
245             / IP(src="2.2.2.2", dst="1.1.0.2")
246             / UDP(sport=1234, dport=1234)
247             / Raw(b"\xa5" * 100)
248         )
249         rx = self.send_and_expect(
250             self.pg1, p_4o4_2 * 1 + p_4o4 * (NUM_PKTS - 1), self.pg0
251         )
252         # checking encap4 magic packet
253         p = rx.pop(0)
254         self.validate_outer4(p, udp_encap_4)
255         p = IP(p["UDP"].payload.load)
256         self.validate_inner4(p, p_4o4_2)
257         self.assertEqual(udp_encap_4.get_stats()["packets"], 1)
258         # checking remaining packets for encap0
259         for p in rx:
260             self.validate_outer4(p, udp_encap_0)
261             p = IP(p["UDP"].payload.load)
262             self.validate_inner4(p, p_4o4)
263         self.assertEqual(udp_encap_0.get_stats()["packets"], NUM_PKTS - 1)
264
265         #
266         # 4o6 encap
267         #
268         p_4o6 = (
269             Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
270             / IP(src="2.2.2.2", dst="1.1.2.1")
271             / UDP(sport=1234, dport=1234)
272             / Raw(b"\xa5" * 100)
273         )
274         rx = self.send_and_expect(self.pg0, p_4o6 * NUM_PKTS, self.pg2)
275         for p in rx:
276             self.validate_outer6(p, udp_encap_2)
277             p = IP(p["UDP"].payload.load)
278             self.validate_inner4(p, p_4o6)
279         self.assertEqual(udp_encap_2.get_stats()["packets"], NUM_PKTS)
280
281         #
282         # 6o4 encap
283         #
284         p_6o4 = (
285             Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
286             / IPv6(src="2001::100", dst="2001::1")
287             / UDP(sport=1234, dport=1234)
288             / Raw(b"\xa5" * 100)
289         )
290         rx = self.send_and_expect(self.pg0, p_6o4 * NUM_PKTS, self.pg1)
291         for p in rx:
292             self.validate_outer4(p, udp_encap_1)
293             p = IPv6(p["UDP"].payload.load)
294             self.validate_inner6(p, p_6o4)
295         self.assertEqual(udp_encap_1.get_stats()["packets"], NUM_PKTS)
296
297         #
298         # 6o6 encap
299         #
300         p_6o6 = (
301             Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
302             / IPv6(src="2001::100", dst="2001::3")
303             / UDP(sport=1234, dport=1234)
304             / Raw(b"\xa5" * 100)
305         )
306         rx = self.send_and_expect(self.pg0, p_6o6 * NUM_PKTS, self.pg3)
307         for p in rx:
308             self.validate_outer6(p, udp_encap_3)
309             p = IPv6(p["UDP"].payload.load)
310             self.validate_inner6(p, p_6o6)
311         self.assertEqual(udp_encap_3.get_stats()["packets"], NUM_PKTS)
312
313         #
314         # A route with an output label
315         # the TTL of the inner packet is decremented on LSP ingress
316         #
317         route_4oMPLSo4 = VppIpRoute(
318             self,
319             "1.1.2.22",
320             32,
321             [
322                 VppRoutePath(
323                     "0.0.0.0",
324                     0xFFFFFFFF,
325                     type=FibPathType.FIB_PATH_TYPE_UDP_ENCAP,
326                     next_hop_id=1,
327                     labels=[VppMplsLabel(66)],
328                 )
329             ],
330         )
331         route_4oMPLSo4.add_vpp_config()
332
333         p_4omo4 = (
334             Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
335             / IP(src="2.2.2.2", dst="1.1.2.22")
336             / UDP(sport=1234, dport=1234)
337             / Raw(b"\xa5" * 100)
338         )
339         rx = self.send_and_expect(self.pg0, p_4omo4 * NUM_PKTS, self.pg1)
340         for p in rx:
341             self.validate_outer4(p, udp_encap_1)
342             p = MPLS(p["UDP"].payload.load)
343             self.validate_inner4(p, p_4omo4, ttl=63)
344         self.assertEqual(udp_encap_1.get_stats()["packets"], 2 * NUM_PKTS)
345
346     def test_udp_decap(self):
347         """UDP Decap test"""
348         #
349         # construct a UDP decap object for each type of protocol
350         #
351
352         # IPv4
353         udp_api_proto = VppEnum.vl_api_udp_decap_next_proto_t
354         next_proto = udp_api_proto.UDP_API_DECAP_PROTO_IP4
355         udp_decap_0 = VppUdpDecap(self, 1, 220, next_proto)
356
357         # IPv6
358         next_proto = udp_api_proto.UDP_API_DECAP_PROTO_IP6
359         udp_decap_1 = VppUdpDecap(self, 0, 221, next_proto)
360
361         # MPLS
362         next_proto = udp_api_proto.UDP_API_DECAP_PROTO_MPLS
363         udp_decap_2 = VppUdpDecap(self, 1, 222, next_proto)
364
365         udp_decap_0.add_vpp_config()
366         udp_decap_1.add_vpp_config()
367         udp_decap_2.add_vpp_config()
368
369         #
370         # Routes via the corresponding pg after the UDP decap
371         #
372         route_4 = VppIpRoute(
373             self,
374             "1.1.1.1",
375             32,
376             [VppRoutePath("0.0.0.0", self.pg0.sw_if_index)],
377             table_id=0,
378         )
379
380         route_6 = VppIpRoute(
381             self, "2001::1", 128, [VppRoutePath("::", self.pg1.sw_if_index)], table_id=1
382         )
383
384         route_mo4 = VppIpRoute(
385             self,
386             "3.3.3.3",
387             32,
388             [VppRoutePath("0.0.0.0", self.pg2.sw_if_index)],
389             table_id=2,
390         )
391
392         route_4.add_vpp_config()
393         route_6.add_vpp_config()
394         route_mo4.add_vpp_config()
395
396         #
397         # Adding neighbors to route the packets
398         #
399         n_4 = VppNeighbor(self, self.pg0.sw_if_index, "00:11:22:33:44:55", "1.1.1.1")
400         n_6 = VppNeighbor(self, self.pg1.sw_if_index, "11:22:33:44:55:66", "2001::1")
401         n_mo4 = VppNeighbor(self, self.pg2.sw_if_index, "22:33:44:55:66:77", "3.3.3.3")
402
403         n_4.add_vpp_config()
404         n_6.add_vpp_config()
405         n_mo4.add_vpp_config()
406
407         #
408         # MPLS decapsulation config
409         #
410         mpls_table = VppMplsTable(self, 0)
411         mpls_table.add_vpp_config()
412         mpls_route = VppMplsRoute(
413             self,
414             77,
415             1,
416             [
417                 VppRoutePath(
418                     "0.0.0.0",
419                     0xFFFFFFFF,
420                     nh_table_id=2,
421                     proto=FibPathProto.FIB_PATH_NH_PROTO_IP4,
422                 )
423             ],
424         )
425         mpls_route.add_vpp_config()
426
427         #
428         # UDP over ipv4 decap
429         #
430         p_4 = (
431             Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
432             / IP(src=self.pg0.remote_ip4, dst=self.pg0.local_ip4)
433             / UDP(sport=1111, dport=220)
434             / IP(src="2.2.2.2", dst="1.1.1.1")
435             / UDP(sport=1234, dport=4321)
436             / Raw(b"\xa5" * 100)
437         )
438
439         rx = self.send_and_expect(self.pg0, p_4 * NUM_PKTS, self.pg0)
440         p_4 = IP(p_4["UDP"].payload)
441         for p in rx:
442             p = IP(p["Ether"].payload)
443             self.validate_inner4(p, p_4, ttl=63)
444
445         #
446         # UDP over ipv6 decap
447         #
448         p_6 = (
449             Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac)
450             / IPv6(src=self.pg1.remote_ip6, dst=self.pg1.local_ip6)
451             / UDP(sport=2222, dport=221)
452             / IPv6(src="2001::100", dst="2001::1")
453             / UDP(sport=1234, dport=4321)
454             / Raw(b"\xa5" * 100)
455         )
456
457         rx = self.send_and_expect(self.pg1, p_6 * NUM_PKTS, self.pg1)
458         p_6 = IPv6(p_6["UDP"].payload)
459         p = IPv6(rx[0]["Ether"].payload)
460         for p in rx:
461             p = IPv6(p["Ether"].payload)
462             self.validate_inner6(p, p_6, hlim=63)
463
464         #
465         # UDP over mpls decap
466         #
467         p_mo4 = (
468             Ether(src=self.pg2.remote_mac, dst=self.pg2.local_mac)
469             / IP(src=self.pg2.remote_ip4, dst=self.pg2.local_ip4)
470             / UDP(sport=3333, dport=222)
471             / MPLS(label=77, ttl=1)
472             / IP(src="4.4.4.4", dst="3.3.3.3")
473             / UDP(sport=1234, dport=4321)
474             / Raw(b"\xa5" * 100)
475         )
476
477         self.pg2.enable_mpls()
478         rx = self.send_and_expect(self.pg2, p_mo4 * NUM_PKTS, self.pg2)
479         self.pg2.disable_mpls()
480         p_mo4 = IP(MPLS(p_mo4["UDP"].payload).payload)
481         for p in rx:
482             p = IP(p["Ether"].payload)
483             self.validate_inner4(p, p_mo4, ttl=63)
484
485
486 @tag_fixme_vpp_workers
487 class TestUDP(VppTestCase):
488     """UDP Test Case"""
489
490     @classmethod
491     def setUpClass(cls):
492         super(TestUDP, cls).setUpClass()
493
494     @classmethod
495     def tearDownClass(cls):
496         super(TestUDP, cls).tearDownClass()
497
498     def setUp(self):
499         super(TestUDP, self).setUp()
500         self.vapi.session_enable_disable(is_enable=1)
501         self.create_loopback_interfaces(2)
502
503         table_id = 0
504
505         for i in self.lo_interfaces:
506             i.admin_up()
507
508             if table_id != 0:
509                 tbl = VppIpTable(self, table_id)
510                 tbl.add_vpp_config()
511
512             i.set_table_ip4(table_id)
513             i.config_ip4()
514             table_id += 1
515
516         # Configure namespaces
517         self.vapi.app_namespace_add_del(
518             namespace_id="0", sw_if_index=self.loop0.sw_if_index
519         )
520         self.vapi.app_namespace_add_del(
521             namespace_id="1", sw_if_index=self.loop1.sw_if_index
522         )
523
524     def tearDown(self):
525         for i in self.lo_interfaces:
526             i.unconfig_ip4()
527             i.set_table_ip4(0)
528             i.admin_down()
529         self.vapi.session_enable_disable(is_enable=0)
530         super(TestUDP, self).tearDown()
531
532     def test_udp_transfer(self):
533         """UDP echo client/server transfer"""
534
535         # Add inter-table routes
536         ip_t01 = VppIpRoute(
537             self,
538             self.loop1.local_ip4,
539             32,
540             [VppRoutePath("0.0.0.0", 0xFFFFFFFF, nh_table_id=1)],
541         )
542         ip_t10 = VppIpRoute(
543             self,
544             self.loop0.local_ip4,
545             32,
546             [VppRoutePath("0.0.0.0", 0xFFFFFFFF, nh_table_id=0)],
547             table_id=1,
548         )
549         ip_t01.add_vpp_config()
550         ip_t10.add_vpp_config()
551
552         # Start builtin server and client
553         uri = "udp://" + self.loop0.local_ip4 + "/1234"
554         error = self.vapi.cli(
555             "test echo server appns 0 fifo-size 4 no-echo" + "uri " + uri
556         )
557         if error:
558             self.logger.critical(error)
559             self.assertNotIn("failed", error)
560
561         error = self.vapi.cli(
562             "test echo client mbytes 10 appns 1 "
563             + "fifo-size 4 no-output test-bytes "
564             + "syn-timeout 2 no-return uri "
565             + uri
566         )
567         if error:
568             self.logger.critical(error)
569             self.assertNotIn("failed", error)
570
571         self.logger.debug(self.vapi.cli("show session verbose 2"))
572
573         # Delete inter-table routes
574         ip_t01.remove_vpp_config()
575         ip_t10.remove_vpp_config()
576
577
578 if __name__ == "__main__":
579     unittest.main(testRunner=VppTestRunner)