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