3 from framework import tag_fixme_vpp_workers
4 from framework import VppTestCase, VppTestRunner
6 from vpp_udp_encap import find_udp_encap, VppUdpEncap
7 from vpp_udp_decap import VppUdpDecap
8 from vpp_ip_route import (
18 from vpp_neighbor import VppNeighbor
19 from vpp_papi import VppEnum
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
28 ENTROPY_PORT_MIN = 0x3 << 14
29 ENTROPY_PORT_MAX = 0xFFFF
32 @tag_fixme_vpp_workers
33 class TestUdpEncap(VppTestCase):
34 """UDP Encap Test Case"""
38 super(TestUdpEncap, cls).setUpClass()
41 def tearDownClass(cls):
42 super(TestUdpEncap, cls).tearDownClass()
45 super(TestUdpEncap, self).setUp()
47 # create 2 pg interfaces
48 self.create_pg_interfaces(range(4))
51 # assign them different tables.
55 for i in self.pg_interfaces:
59 tbl = VppIpTable(self, table_id)
61 self.tables.append(tbl)
62 tbl = VppIpTable(self, table_id, is_ip6=1)
64 self.tables.append(tbl)
66 i.set_table_ip4(table_id)
67 i.set_table_ip6(table_id)
75 for i in self.pg_interfaces:
81 super(TestUdpEncap, self).tearDown()
83 def validate_outer4(self, rx, encap_obj, sport_entropy=False):
84 self.assertEqual(rx[IP].src, encap_obj.src_ip_s)
85 self.assertEqual(rx[IP].dst, encap_obj.dst_ip_s)
87 self.assert_in_range(rx[UDP].sport, ENTROPY_PORT_MIN, ENTROPY_PORT_MAX)
89 self.assertEqual(rx[UDP].sport, encap_obj.src_port)
90 self.assertEqual(rx[UDP].dport, encap_obj.dst_port)
92 def validate_outer6(self, rx, encap_obj, sport_entropy=False):
93 self.assertEqual(rx[IPv6].src, encap_obj.src_ip_s)
94 self.assertEqual(rx[IPv6].dst, encap_obj.dst_ip_s)
96 self.assert_in_range(rx[UDP].sport, ENTROPY_PORT_MIN, ENTROPY_PORT_MAX)
98 self.assertEqual(rx[UDP].sport, encap_obj.src_port)
99 self.assertEqual(rx[UDP].dport, encap_obj.dst_port)
101 def validate_inner4(self, rx, tx, ttl=None):
102 self.assertEqual(rx[IP].src, tx[IP].src)
103 self.assertEqual(rx[IP].dst, tx[IP].dst)
105 self.assertEqual(rx[IP].ttl, ttl)
107 self.assertEqual(rx[IP].ttl, tx[IP].ttl)
109 def validate_inner6(self, rx, tx, hlim=None):
110 self.assertEqual(rx.src, tx[IPv6].src)
111 self.assertEqual(rx.dst, tx[IPv6].dst)
113 self.assertEqual(rx.hlim, hlim)
115 self.assertEqual(rx.hlim, tx[IPv6].hlim)
117 def test_udp_encap(self):
121 # construct a UDP encap object through each of the peers
122 # v4 through the first two peers, v6 through the second.
123 # The last encap is v4 and is used to check the codepath
124 # where 2 different udp encap objects are processed at the
127 udp_encap_0 = VppUdpEncap(
128 self, self.pg0.local_ip4, self.pg0.remote_ip4, 330, 440
130 udp_encap_1 = VppUdpEncap(
131 self, self.pg1.local_ip4, self.pg1.remote_ip4, 331, 441, table_id=1
133 udp_encap_2 = VppUdpEncap(
134 self, self.pg2.local_ip6, self.pg2.remote_ip6, 332, 442, table_id=2
136 udp_encap_3 = VppUdpEncap(
137 self, self.pg3.local_ip6, self.pg3.remote_ip6, 333, 443, table_id=3
139 udp_encap_4 = VppUdpEncap(
140 self, self.pg0.local_ip4, self.pg0.remote_ip4, 334, 444
142 udp_encap_0.add_vpp_config()
143 udp_encap_1.add_vpp_config()
144 udp_encap_2.add_vpp_config()
145 udp_encap_3.add_vpp_config()
146 udp_encap_4.add_vpp_config()
148 self.logger.info(self.vapi.cli("sh udp encap"))
150 self.assertTrue(find_udp_encap(self, udp_encap_2))
151 self.assertTrue(find_udp_encap(self, udp_encap_3))
152 self.assertTrue(find_udp_encap(self, udp_encap_0))
153 self.assertTrue(find_udp_encap(self, udp_encap_1))
154 self.assertTrue(find_udp_encap(self, udp_encap_4))
157 # Routes via each UDP encap object - all combinations of v4 and v6.
159 route_4o4 = VppIpRoute(
167 type=FibPathType.FIB_PATH_TYPE_UDP_ENCAP,
168 next_hop_id=udp_encap_0.id,
169 proto=FibPathProto.FIB_PATH_NH_PROTO_IP4,
174 # specific route to match encap4, to test encap of 2 packets using 2
176 route_4o4_2 = VppIpRoute(
184 type=FibPathType.FIB_PATH_TYPE_UDP_ENCAP,
185 next_hop_id=udp_encap_4.id,
186 proto=FibPathProto.FIB_PATH_NH_PROTO_IP4,
191 route_4o6 = VppIpRoute(
199 type=FibPathType.FIB_PATH_TYPE_UDP_ENCAP,
200 next_hop_id=udp_encap_2.id,
201 proto=FibPathProto.FIB_PATH_NH_PROTO_IP4,
205 route_6o4 = VppIpRoute(
213 type=FibPathType.FIB_PATH_TYPE_UDP_ENCAP,
214 next_hop_id=udp_encap_1.id,
215 proto=FibPathProto.FIB_PATH_NH_PROTO_IP6,
219 route_6o6 = VppIpRoute(
227 type=FibPathType.FIB_PATH_TYPE_UDP_ENCAP,
228 next_hop_id=udp_encap_3.id,
229 proto=FibPathProto.FIB_PATH_NH_PROTO_IP6,
233 route_4o6.add_vpp_config()
234 route_6o6.add_vpp_config()
235 route_6o4.add_vpp_config()
236 route_4o4.add_vpp_config()
237 route_4o4_2.add_vpp_config()
241 # we add a single packet matching the last encap at the beginning of
242 # the packet vector so that we encap 2 packets with different udp
243 # encap object at the same time
246 Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac)
247 / IP(src="2.2.2.2", dst="1.1.0.1")
248 / UDP(sport=1234, dport=1234)
252 Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac)
253 / IP(src="2.2.2.2", dst="1.1.0.2")
254 / UDP(sport=1234, dport=1234)
257 rx = self.send_and_expect(
258 self.pg1, p_4o4_2 * 1 + p_4o4 * (NUM_PKTS - 1), self.pg0
260 # checking encap4 magic packet
262 self.validate_outer4(p, udp_encap_4)
263 p = IP(p["UDP"].payload.load)
264 self.validate_inner4(p, p_4o4_2)
265 self.assertEqual(udp_encap_4.get_stats()["packets"], 1)
266 # checking remaining packets for encap0
268 self.validate_outer4(p, udp_encap_0)
269 p = IP(p["UDP"].payload.load)
270 self.validate_inner4(p, p_4o4)
271 self.assertEqual(udp_encap_0.get_stats()["packets"], NUM_PKTS - 1)
277 Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
278 / IP(src="2.2.2.2", dst="1.1.2.1")
279 / UDP(sport=1234, dport=1234)
282 rx = self.send_and_expect(self.pg0, p_4o6 * NUM_PKTS, self.pg2)
284 self.validate_outer6(p, udp_encap_2)
285 p = IP(p["UDP"].payload.load)
286 self.validate_inner4(p, p_4o6)
287 self.assertEqual(udp_encap_2.get_stats()["packets"], NUM_PKTS)
293 Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
294 / IPv6(src="2001::100", dst="2001::1")
295 / UDP(sport=1234, dport=1234)
298 rx = self.send_and_expect(self.pg0, p_6o4 * NUM_PKTS, self.pg1)
300 self.validate_outer4(p, udp_encap_1)
301 p = IPv6(p["UDP"].payload.load)
302 self.validate_inner6(p, p_6o4)
303 self.assertEqual(udp_encap_1.get_stats()["packets"], NUM_PKTS)
309 Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
310 / IPv6(src="2001::100", dst="2001::3")
311 / UDP(sport=1234, dport=1234)
314 rx = self.send_and_expect(self.pg0, p_6o6 * NUM_PKTS, self.pg3)
316 self.validate_outer6(p, udp_encap_3)
317 p = IPv6(p["UDP"].payload.load)
318 self.validate_inner6(p, p_6o6)
319 self.assertEqual(udp_encap_3.get_stats()["packets"], NUM_PKTS)
322 # A route with an output label
323 # the TTL of the inner packet is decremented on LSP ingress
325 route_4oMPLSo4 = VppIpRoute(
333 type=FibPathType.FIB_PATH_TYPE_UDP_ENCAP,
335 labels=[VppMplsLabel(66)],
339 route_4oMPLSo4.add_vpp_config()
342 Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
343 / IP(src="2.2.2.2", dst="1.1.2.22")
344 / UDP(sport=1234, dport=1234)
347 rx = self.send_and_expect(self.pg0, p_4omo4 * NUM_PKTS, self.pg1)
349 self.validate_outer4(p, udp_encap_1)
350 p = MPLS(p["UDP"].payload.load)
351 self.validate_inner4(p, p_4omo4, ttl=63)
352 self.assertEqual(udp_encap_1.get_stats()["packets"], 2 * NUM_PKTS)
354 def test_udp_encap_entropy(self):
355 """UDP Encap src port entropy test"""
358 # construct a UDP encap object through each of the peers
359 # v4 through the first two peers, v6 through the second.
360 # use zero source port to enable entropy per rfc7510.
362 udp_encap_0 = VppUdpEncap(self, self.pg0.local_ip4, self.pg0.remote_ip4, 0, 440)
363 udp_encap_1 = VppUdpEncap(
364 self, self.pg1.local_ip4, self.pg1.remote_ip4, 0, 441, table_id=1
366 udp_encap_2 = VppUdpEncap(
367 self, self.pg2.local_ip6, self.pg2.remote_ip6, 0, 442, table_id=2
369 udp_encap_3 = VppUdpEncap(
370 self, self.pg3.local_ip6, self.pg3.remote_ip6, 0, 443, table_id=3
372 udp_encap_0.add_vpp_config()
373 udp_encap_1.add_vpp_config()
374 udp_encap_2.add_vpp_config()
375 udp_encap_3.add_vpp_config()
377 self.logger.info(self.vapi.cli("sh udp encap"))
379 self.assertTrue(find_udp_encap(self, udp_encap_0))
380 self.assertTrue(find_udp_encap(self, udp_encap_1))
381 self.assertTrue(find_udp_encap(self, udp_encap_2))
382 self.assertTrue(find_udp_encap(self, udp_encap_3))
385 # Routes via each UDP encap object - all combinations of v4 and v6.
387 route_4o4 = VppIpRoute(
395 type=FibPathType.FIB_PATH_TYPE_UDP_ENCAP,
396 next_hop_id=udp_encap_0.id,
397 proto=FibPathProto.FIB_PATH_NH_PROTO_IP4,
402 route_4o6 = VppIpRoute(
410 type=FibPathType.FIB_PATH_TYPE_UDP_ENCAP,
411 next_hop_id=udp_encap_2.id,
412 proto=FibPathProto.FIB_PATH_NH_PROTO_IP4,
416 route_6o4 = VppIpRoute(
424 type=FibPathType.FIB_PATH_TYPE_UDP_ENCAP,
425 next_hop_id=udp_encap_1.id,
426 proto=FibPathProto.FIB_PATH_NH_PROTO_IP6,
430 route_6o6 = VppIpRoute(
438 type=FibPathType.FIB_PATH_TYPE_UDP_ENCAP,
439 next_hop_id=udp_encap_3.id,
440 proto=FibPathProto.FIB_PATH_NH_PROTO_IP6,
444 route_4o4.add_vpp_config()
445 route_4o6.add_vpp_config()
446 route_6o6.add_vpp_config()
447 route_6o4.add_vpp_config()
453 for i in range(NUM_PKTS):
455 Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac)
456 / IP(src="2.2.2.2", dst="1.1.0.1")
457 / UDP(sport=1234 + i, dport=1234)
460 rx = self.send_and_expect(self.pg1, p_4o4, self.pg0)
462 for i, p in enumerate(rx):
463 self.validate_outer4(p, udp_encap_0, True)
464 sports.add(p["UDP"].sport)
465 p = IP(p["UDP"].payload.load)
466 self.validate_inner4(p, p_4o4[i])
467 self.assertEqual(udp_encap_0.get_stats()["packets"], NUM_PKTS)
469 len(sports), 1, "source port {} is not an entropy value".format(sports)
476 for i in range(NUM_PKTS):
478 Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
479 / IP(src="2.2.2.2", dst="1.1.2.1")
480 / UDP(sport=1234 + i, dport=1234)
483 rx = self.send_and_expect(self.pg0, p_4o6, self.pg2)
486 self.validate_outer6(p, udp_encap_2, True)
487 sports.add(p["UDP"].sport)
488 p = IP(p["UDP"].payload.load)
489 self.validate_inner4(p, p_4o6[i])
490 self.assertEqual(udp_encap_2.get_stats()["packets"], NUM_PKTS)
492 len(sports), 1, "source port {} is not an entropy value".format(sports)
499 for i in range(NUM_PKTS):
501 Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
502 / IPv6(src="2001::100", dst="2001::1")
503 / UDP(sport=1234 + i, dport=1234)
506 rx = self.send_and_expect(self.pg0, p_6o4, self.pg1)
509 self.validate_outer4(p, udp_encap_1, True)
510 sports.add(p["UDP"].sport)
511 p = IPv6(p["UDP"].payload.load)
512 self.validate_inner6(p, p_6o4[i])
513 self.assertEqual(udp_encap_1.get_stats()["packets"], NUM_PKTS)
515 len(sports), 1, "source port {} is not an entropy value".format(sports)
522 for i in range(NUM_PKTS):
524 Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
525 / IPv6(src="2001::100", dst="2001::3")
526 / UDP(sport=1234 + i, dport=1234)
529 rx = self.send_and_expect(self.pg0, p_6o6, self.pg3)
532 self.validate_outer6(p, udp_encap_3, True)
533 sports.add(p["UDP"].sport)
534 p = IPv6(p["UDP"].payload.load)
535 self.validate_inner6(p, p_6o6[i])
536 self.assertEqual(udp_encap_3.get_stats()["packets"], NUM_PKTS)
538 len(sports), 1, "source port {} is not an entropy value".format(sports)
541 def test_udp_decap(self):
544 # construct a UDP decap object for each type of protocol
548 udp_api_proto = VppEnum.vl_api_udp_decap_next_proto_t
549 next_proto = udp_api_proto.UDP_API_DECAP_PROTO_IP4
550 udp_decap_0 = VppUdpDecap(self, 1, 220, next_proto)
553 next_proto = udp_api_proto.UDP_API_DECAP_PROTO_IP6
554 udp_decap_1 = VppUdpDecap(self, 0, 221, next_proto)
557 next_proto = udp_api_proto.UDP_API_DECAP_PROTO_MPLS
558 udp_decap_2 = VppUdpDecap(self, 1, 222, next_proto)
560 udp_decap_0.add_vpp_config()
561 udp_decap_1.add_vpp_config()
562 udp_decap_2.add_vpp_config()
565 # Routes via the corresponding pg after the UDP decap
567 route_4 = VppIpRoute(
571 [VppRoutePath("0.0.0.0", self.pg0.sw_if_index)],
575 route_6 = VppIpRoute(
576 self, "2001::1", 128, [VppRoutePath("::", self.pg1.sw_if_index)], table_id=1
579 route_mo4 = VppIpRoute(
583 [VppRoutePath("0.0.0.0", self.pg2.sw_if_index)],
587 route_4.add_vpp_config()
588 route_6.add_vpp_config()
589 route_mo4.add_vpp_config()
592 # Adding neighbors to route the packets
594 n_4 = VppNeighbor(self, self.pg0.sw_if_index, "00:11:22:33:44:55", "1.1.1.1")
595 n_6 = VppNeighbor(self, self.pg1.sw_if_index, "11:22:33:44:55:66", "2001::1")
596 n_mo4 = VppNeighbor(self, self.pg2.sw_if_index, "22:33:44:55:66:77", "3.3.3.3")
600 n_mo4.add_vpp_config()
603 # MPLS decapsulation config
605 mpls_table = VppMplsTable(self, 0)
606 mpls_table.add_vpp_config()
607 mpls_route = VppMplsRoute(
616 proto=FibPathProto.FIB_PATH_NH_PROTO_IP4,
620 mpls_route.add_vpp_config()
623 # UDP over ipv4 decap
626 Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
627 / IP(src=self.pg0.remote_ip4, dst=self.pg0.local_ip4)
628 / UDP(sport=1111, dport=220)
629 / IP(src="2.2.2.2", dst="1.1.1.1")
630 / UDP(sport=1234, dport=4321)
634 rx = self.send_and_expect(self.pg0, p_4 * NUM_PKTS, self.pg0)
635 p_4 = IP(p_4["UDP"].payload)
637 p = IP(p["Ether"].payload)
638 self.validate_inner4(p, p_4, ttl=63)
641 # UDP over ipv6 decap
644 Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac)
645 / IPv6(src=self.pg1.remote_ip6, dst=self.pg1.local_ip6)
646 / UDP(sport=2222, dport=221)
647 / IPv6(src="2001::100", dst="2001::1")
648 / UDP(sport=1234, dport=4321)
652 rx = self.send_and_expect(self.pg1, p_6 * NUM_PKTS, self.pg1)
653 p_6 = IPv6(p_6["UDP"].payload)
654 p = IPv6(rx[0]["Ether"].payload)
656 p = IPv6(p["Ether"].payload)
657 self.validate_inner6(p, p_6, hlim=63)
660 # UDP over mpls decap
663 Ether(src=self.pg2.remote_mac, dst=self.pg2.local_mac)
664 / IP(src=self.pg2.remote_ip4, dst=self.pg2.local_ip4)
665 / UDP(sport=3333, dport=222)
666 / MPLS(label=77, ttl=1)
667 / IP(src="4.4.4.4", dst="3.3.3.3")
668 / UDP(sport=1234, dport=4321)
672 self.pg2.enable_mpls()
673 rx = self.send_and_expect(self.pg2, p_mo4 * NUM_PKTS, self.pg2)
674 self.pg2.disable_mpls()
675 p_mo4 = IP(MPLS(p_mo4["UDP"].payload).payload)
677 p = IP(p["Ether"].payload)
678 self.validate_inner4(p, p_mo4, ttl=63)
681 @tag_fixme_vpp_workers
682 class TestUDP(VppTestCase):
687 super(TestUDP, cls).setUpClass()
690 def tearDownClass(cls):
691 super(TestUDP, cls).tearDownClass()
694 super(TestUDP, self).setUp()
695 self.vapi.session_enable_disable(is_enable=1)
696 self.create_loopback_interfaces(2)
700 for i in self.lo_interfaces:
704 tbl = VppIpTable(self, table_id)
707 i.set_table_ip4(table_id)
711 # Configure namespaces
712 self.vapi.app_namespace_add_del_v4(
713 namespace_id="0", sw_if_index=self.loop0.sw_if_index
715 self.vapi.app_namespace_add_del_v4(
716 namespace_id="1", sw_if_index=self.loop1.sw_if_index
720 for i in self.lo_interfaces:
724 self.vapi.session_enable_disable(is_enable=0)
725 super(TestUDP, self).tearDown()
727 def test_udp_transfer(self):
728 """UDP echo client/server transfer"""
730 # Add inter-table routes
733 self.loop1.local_ip4,
735 [VppRoutePath("0.0.0.0", 0xFFFFFFFF, nh_table_id=1)],
739 self.loop0.local_ip4,
741 [VppRoutePath("0.0.0.0", 0xFFFFFFFF, nh_table_id=0)],
744 ip_t01.add_vpp_config()
745 ip_t10.add_vpp_config()
747 # Start builtin server and client
748 uri = "udp://" + self.loop0.local_ip4 + "/1234"
749 error = self.vapi.cli(
750 "test echo server appns 0 fifo-size 4 no-echo" + "uri " + uri
753 self.logger.critical(error)
754 self.assertNotIn("failed", error)
756 error = self.vapi.cli(
757 "test echo client mbytes 10 appns 1 "
758 + "fifo-size 4 no-output test-bytes "
759 + "syn-timeout 2 no-return uri "
763 self.logger.critical(error)
764 self.assertNotIn("failed", error)
766 self.logger.debug(self.vapi.cli("show session verbose 2"))
768 # Delete inter-table routes
769 ip_t01.remove_vpp_config()
770 ip_t10.remove_vpp_config()
773 if __name__ == "__main__":
774 unittest.main(testRunner=VppTestRunner)