tests: replace pycodestyle with black
[vpp.git] / test / test_punt.py
1 #!/usr/bin/env python3
2 import binascii
3 import random
4 import socket
5 import os
6 import threading
7 import struct
8 import copy
9 import fcntl
10 import time
11
12 from struct import unpack, unpack_from
13
14 try:
15     import unittest2 as unittest
16 except ImportError:
17     import unittest
18
19 from util import ppp, ppc
20 from re import compile
21 import scapy.compat
22 from scapy.packet import Raw
23 from scapy.layers.l2 import Ether
24 from scapy.layers.inet import IP, UDP, ICMP
25 from scapy.layers.ipsec import ESP
26 import scapy.layers.inet6 as inet6
27 from scapy.layers.inet6 import IPv6, ICMPv6DestUnreach
28 from scapy.contrib.ospf import OSPF_Hdr, OSPFv3_Hello
29 from framework import tag_fixme_vpp_workers
30 from framework import VppTestCase, VppTestRunner
31
32 from vpp_ip import DpoProto
33 from vpp_ip_route import VppIpRoute, VppRoutePath
34 from vpp_ipsec import VppIpsecSA, VppIpsecTunProtect, VppIpsecInterface
35 from vpp_papi import VppEnum
36
37 NUM_PKTS = 67
38
39
40 class serverSocketThread(threading.Thread):
41     """Socket server thread"""
42
43     def __init__(self, threadID, sockName):
44         threading.Thread.__init__(self)
45         self.threadID = threadID
46         self.sockName = sockName
47         self.sock = None
48         self.rx_pkts = []
49         self.stop_running = False
50
51     def rx_packets(self):
52         # Wait for some packets on socket
53         while True:
54             try:
55                 data = self.sock.recv(65536)
56
57                 # punt socket metadata
58                 # packet_desc = data[0:8]
59
60                 # Ethernet
61                 self.rx_pkts.append(Ether(data[8:]))
62             except IOError as e:
63                 if e.errno == 11:
64                     # nothing to receive, stop running or sleep a little
65                     if self.stop_running:
66                         break
67                     time.sleep(0.1)
68                     pass
69                 else:
70                     raise
71
72     def run(self):
73         self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
74         try:
75             os.unlink(self.sockName)
76         except:
77             pass
78         self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 65536)
79         self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 65536)
80         fcntl.fcntl(self.sock, fcntl.F_SETFL, os.O_NONBLOCK)
81         self.sock.bind(self.sockName)
82
83         self.rx_packets()
84
85     def close(self):
86         self.stop_running = True
87         threading.Thread.join(self)
88         self.sock.close()
89         return self.rx_pkts
90
91
92 class TestPuntSocket(VppTestCase):
93     """Punt Socket"""
94
95     ports = [1111, 2222, 3333, 4444]
96     sock_servers = list()
97     # FIXME: nr_packets > 3 results in failure
98     # nr_packets = 3 makes the test unstable
99     nr_packets = 2
100
101     @classmethod
102     def setUpClass(cls):
103         super(TestPuntSocket, cls).setUpClass()
104
105     @classmethod
106     def tearDownClass(cls):
107         super(TestPuntSocket, cls).tearDownClass()
108
109     @classmethod
110     def setUpConstants(cls):
111         cls.extra_vpp_punt_config = [
112             "punt",
113             "{",
114             "socket",
115             cls.tempdir + "/socket_punt",
116             "}",
117         ]
118         super(TestPuntSocket, cls).setUpConstants()
119
120     def setUp(self):
121         super(TestPuntSocket, self).setUp()
122         random.seed()
123
124         self.create_pg_interfaces(range(2))
125         for i in self.pg_interfaces:
126             i.admin_up()
127
128     def tearDown(self):
129         del self.sock_servers[:]
130         super(TestPuntSocket, self).tearDown()
131
132     def socket_client_create(self, sock_name, id=None):
133         thread = serverSocketThread(id, sock_name)
134         self.sock_servers.append(thread)
135         thread.start()
136         return thread
137
138     def socket_client_close(self):
139         rx_pkts = []
140         for thread in self.sock_servers:
141             rx_pkts += thread.close()
142         return rx_pkts
143
144     def verify_port(self, pr, vpr):
145         self.assertEqual(vpr.punt.type, pr["type"])
146         self.assertEqual(vpr.punt.punt.l4.port, pr["punt"]["l4"]["port"])
147         self.assertEqual(vpr.punt.punt.l4.protocol, pr["punt"]["l4"]["protocol"])
148         self.assertEqual(vpr.punt.punt.l4.af, pr["punt"]["l4"]["af"])
149
150     def verify_exception(self, pr, vpr):
151         self.assertEqual(vpr.punt.type, pr["type"])
152         self.assertEqual(vpr.punt.punt.exception.id, pr["punt"]["exception"]["id"])
153
154     def verify_ip_proto(self, pr, vpr):
155         self.assertEqual(vpr.punt.type, pr["type"])
156         self.assertEqual(vpr.punt.punt.ip_proto.af, pr["punt"]["ip_proto"]["af"])
157         self.assertEqual(
158             vpr.punt.punt.ip_proto.protocol, pr["punt"]["ip_proto"]["protocol"]
159         )
160
161     def verify_udp_pkts(self, rxs, n_rx, port):
162         n_match = 0
163         for rx in rxs:
164             self.assertTrue(rx.haslayer(UDP))
165             if rx[UDP].dport == port:
166                 n_match += 1
167         self.assertEqual(n_match, n_rx)
168
169
170 def set_port(pr, port):
171     pr["punt"]["l4"]["port"] = port
172     return pr
173
174
175 def set_reason(pr, reason):
176     pr["punt"]["exception"]["id"] = reason
177     return pr
178
179
180 def mk_vpp_cfg4():
181     pt_l4 = VppEnum.vl_api_punt_type_t.PUNT_API_TYPE_L4
182     af_ip4 = VppEnum.vl_api_address_family_t.ADDRESS_IP4
183     udp_proto = VppEnum.vl_api_ip_proto_t.IP_API_PROTO_UDP
184     punt_l4 = {"type": pt_l4, "punt": {"l4": {"af": af_ip4, "protocol": udp_proto}}}
185     return punt_l4
186
187
188 def mk_vpp_cfg6():
189     pt_l4 = VppEnum.vl_api_punt_type_t.PUNT_API_TYPE_L4
190     af_ip6 = VppEnum.vl_api_address_family_t.ADDRESS_IP6
191     udp_proto = VppEnum.vl_api_ip_proto_t.IP_API_PROTO_UDP
192     punt_l4 = {"type": pt_l4, "punt": {"l4": {"af": af_ip6, "protocol": udp_proto}}}
193     return punt_l4
194
195
196 class TestIP4PuntSocket(TestPuntSocket):
197     """Punt Socket for IPv4 UDP"""
198
199     @classmethod
200     def setUpClass(cls):
201         super(TestIP4PuntSocket, cls).setUpClass()
202
203     @classmethod
204     def tearDownClass(cls):
205         super(TestIP4PuntSocket, cls).tearDownClass()
206
207     def setUp(self):
208         super(TestIP4PuntSocket, self).setUp()
209
210         for i in self.pg_interfaces:
211             i.config_ip4()
212             i.resolve_arp()
213
214     def tearDown(self):
215         super(TestIP4PuntSocket, self).tearDown()
216         for i in self.pg_interfaces:
217             i.unconfig_ip4()
218             i.admin_down()
219
220     def test_punt_socket_dump(self):
221         """Punt socket registration/deregistration"""
222
223         pt_l4 = VppEnum.vl_api_punt_type_t.PUNT_API_TYPE_L4
224         af_ip4 = VppEnum.vl_api_address_family_t.ADDRESS_IP4
225         udp_proto = VppEnum.vl_api_ip_proto_t.IP_API_PROTO_UDP
226
227         punts = self.vapi.punt_socket_dump(type=pt_l4)
228         self.assertEqual(len(punts), 0)
229
230         #
231         # configure a punt socket
232         #
233         punt_l4 = mk_vpp_cfg4()
234
235         self.vapi.punt_socket_register(
236             set_port(punt_l4, 1111), "%s/socket_punt_1111" % self.tempdir
237         )
238         self.vapi.punt_socket_register(
239             set_port(punt_l4, 2222), "%s/socket_punt_2222" % self.tempdir
240         )
241         punts = self.vapi.punt_socket_dump(type=pt_l4)
242         self.assertEqual(len(punts), 2)
243         self.verify_port(set_port(punt_l4, 1111), punts[0])
244         self.verify_port(set_port(punt_l4, 2222), punts[1])
245
246         #
247         # deregister a punt socket
248         #
249         self.vapi.punt_socket_deregister(set_port(punt_l4, 1111))
250         punts = self.vapi.punt_socket_dump(type=pt_l4)
251         self.assertEqual(len(punts), 1)
252
253         #
254         # configure a punt socket again
255         #
256         self.vapi.punt_socket_register(
257             set_port(punt_l4, 1111), "%s/socket_punt_1111" % self.tempdir
258         )
259         self.vapi.punt_socket_register(
260             set_port(punt_l4, 3333), "%s/socket_punt_3333" % self.tempdir
261         )
262         punts = self.vapi.punt_socket_dump(type=pt_l4)
263         self.assertEqual(len(punts), 3)
264
265         self.logger.info(self.vapi.cli("sh punt sock reg"))
266
267         #
268         # deregister all punt socket
269         #
270         self.vapi.punt_socket_deregister(set_port(punt_l4, 1111))
271         self.vapi.punt_socket_deregister(set_port(punt_l4, 2222))
272         self.vapi.punt_socket_deregister(set_port(punt_l4, 3333))
273         punts = self.vapi.punt_socket_dump(type=pt_l4)
274         self.assertEqual(len(punts), 0)
275
276     def test_punt_socket_traffic_single_port_single_socket(self):
277         """Punt socket traffic single port single socket"""
278
279         port = self.ports[0]
280         pt_l4 = VppEnum.vl_api_punt_type_t.PUNT_API_TYPE_L4
281         punt_l4 = set_port(mk_vpp_cfg4(), port)
282
283         p = (
284             Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
285             / IP(src=self.pg0.remote_ip4, dst=self.pg0.local_ip4)
286             / UDP(sport=9876, dport=port)
287             / Raw(b"\xa5" * 100)
288         )
289
290         pkts = p * self.nr_packets
291
292         punts = self.vapi.punt_socket_dump(type=pt_l4)
293         self.assertEqual(len(punts), 0)
294
295         #
296         # expect ICMP - port unreachable for all packets
297         #
298         rx = self.send_and_expect_some(self.pg0, pkts, self.pg0)
299
300         for p in rx:
301             self.assertEqual(int(p[IP].proto), 1)  # ICMP
302             self.assertEqual(int(p[ICMP].code), 3)  # unreachable
303
304         #
305         # configure a punt socket
306         #
307         self.socket_client_create("%s/socket_%d" % (self.tempdir, port))
308         self.vapi.punt_socket_register(punt_l4, "%s/socket_%d" % (self.tempdir, port))
309         punts = self.vapi.punt_socket_dump(type=pt_l4)
310         self.assertEqual(len(punts), 1)
311
312         #
313         # expect punt socket and no packets on pg0
314         #
315         self.send_and_assert_no_replies(self.pg0, pkts)
316         rx = self.socket_client_close()
317         self.verify_udp_pkts(rx, len(pkts), port)
318
319         #
320         # remove punt socket. expect ICMP - port unreachable for all packets
321         #
322         self.vapi.punt_socket_deregister(punt_l4)
323         punts = self.vapi.punt_socket_dump(type=pt_l4)
324         self.assertEqual(len(punts), 0)
325
326         rx = self.send_and_expect_some(self.pg0, pkts, self.pg0)
327         for p in rx:
328             self.assertEqual(int(p[IP].proto), 1)  # ICMP
329             self.assertEqual(int(p[ICMP].code), 3)  # unreachable
330
331     def test_punt_socket_traffic_multi_ports_multi_sockets(self):
332         """Punt socket traffic multi ports and multi sockets"""
333
334         punt_l4 = mk_vpp_cfg4()
335
336         # configuration for each UDP port
337         cfgs = dict()
338
339         #
340         # create stream of packets for each port
341         #
342         for port in self.ports:
343             # choose port from port list
344             cfgs[port] = {}
345
346             pkt = (
347                 Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
348                 / IP(src=self.pg0.remote_ip4, dst=self.pg0.local_ip4)
349                 / UDP(sport=9876, dport=port)
350                 / Raw(b"\xa5" * 100)
351             )
352             cfgs[port]["pkts"] = pkt * self.nr_packets
353             cfgs[port]["port"] = port
354             cfgs[port]["vpp"] = copy.deepcopy(set_port(punt_l4, port))
355
356             # configure punt sockets
357             cfgs[port]["sock"] = self.socket_client_create(
358                 "%s/socket_%d" % (self.tempdir, port)
359             )
360             self.vapi.punt_socket_register(
361                 cfgs[port]["vpp"], "%s/socket_%d" % (self.tempdir, port)
362             )
363
364         #
365         # send the packets that get punted
366         #
367         for cfg in cfgs.values():
368             self.send_and_assert_no_replies(self.pg0, cfg["pkts"])
369
370         #
371         # test that we got the excepted packets on the expected socket
372         #
373         for cfg in cfgs.values():
374             rx = cfg["sock"].close()
375             self.verify_udp_pkts(rx, len(cfg["pkts"]), cfg["port"])
376             self.vapi.punt_socket_deregister(cfg["vpp"])
377
378     def test_punt_socket_traffic_multi_ports_single_socket(self):
379         """Punt socket traffic multi ports and single socket"""
380
381         pt_l4 = VppEnum.vl_api_punt_type_t.PUNT_API_TYPE_L4
382         punt_l4 = mk_vpp_cfg4()
383
384         #
385         # create stream of packets with each port
386         #
387         pkts = []
388         for port in self.ports:
389             # choose port from port list
390             pkt = (
391                 Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
392                 / IP(src=self.pg0.remote_ip4, dst=self.pg0.local_ip4)
393                 / UDP(sport=9876, dport=port)
394                 / Raw(b"\xa5" * 100)
395             )
396             pkts += pkt * self.nr_packets
397
398         #
399         # configure a punt socket
400         #
401         self.socket_client_create("%s/socket_multi" % self.tempdir)
402         for p in self.ports:
403             self.vapi.punt_socket_register(
404                 set_port(punt_l4, p), "%s/socket_multi" % self.tempdir
405             )
406         punts = self.vapi.punt_socket_dump(type=pt_l4)
407         self.assertEqual(len(punts), len(self.ports))
408
409         #
410         # expect punt socket and no packets on pg0
411         #
412         self.send_and_assert_no_replies(self.pg0, pkts)
413         self.logger.info(self.vapi.cli("show trace"))
414         rx = self.socket_client_close()
415
416         for p in self.ports:
417             self.verify_udp_pkts(rx, self.nr_packets, p)
418             self.vapi.punt_socket_deregister(set_port(punt_l4, p))
419         punts = self.vapi.punt_socket_dump(type=pt_l4)
420         self.assertEqual(len(punts), 0)
421
422
423 class TestIP6PuntSocket(TestPuntSocket):
424     """Punt Socket for IPv6 UDP"""
425
426     @classmethod
427     def setUpClass(cls):
428         super(TestIP6PuntSocket, cls).setUpClass()
429
430     @classmethod
431     def tearDownClass(cls):
432         super(TestIP6PuntSocket, cls).tearDownClass()
433
434     def setUp(self):
435         super(TestIP6PuntSocket, self).setUp()
436
437         for i in self.pg_interfaces:
438             i.config_ip6()
439             i.resolve_ndp()
440
441     def tearDown(self):
442         super(TestIP6PuntSocket, self).tearDown()
443         for i in self.pg_interfaces:
444             i.unconfig_ip6()
445             i.admin_down()
446
447     def test_punt_socket_dump(self):
448         """Punt socket registration"""
449
450         pt_l4 = VppEnum.vl_api_punt_type_t.PUNT_API_TYPE_L4
451         af_ip6 = VppEnum.vl_api_address_family_t.ADDRESS_IP6
452         udp_proto = VppEnum.vl_api_ip_proto_t.IP_API_PROTO_UDP
453         #
454         # configure a punt socket
455         #
456         punt_l4 = {"type": pt_l4, "punt": {"l4": {"af": af_ip6, "protocol": udp_proto}}}
457
458         punts = self.vapi.punt_socket_dump(type=pt_l4)
459         self.assertEqual(len(punts), 0)
460
461         #
462         # configure a punt socket
463         #
464         self.vapi.punt_socket_register(
465             set_port(punt_l4, 1111), "%s/socket_1111" % self.tempdir
466         )
467         self.vapi.punt_socket_register(
468             set_port(punt_l4, 2222), "%s/socket_2222" % self.tempdir
469         )
470         punts = self.vapi.punt_socket_dump(type=pt_l4)
471         self.assertEqual(len(punts), 2)
472         self.verify_port(set_port(punt_l4, 1111), punts[0])
473         self.verify_port(set_port(punt_l4, 2222), punts[1])
474
475         #
476         # deregister a punt socket
477         #
478         self.vapi.punt_socket_deregister(set_port(punt_l4, 1111))
479         punts = self.vapi.punt_socket_dump(type=pt_l4)
480         self.assertEqual(len(punts), 1)
481
482         #
483         # configure a punt socket again
484         #
485         self.vapi.punt_socket_register(
486             set_port(punt_l4, 1111), "%s/socket_1111" % self.tempdir
487         )
488         punts = self.vapi.punt_socket_dump(type=pt_l4)
489         self.assertEqual(len(punts), 2)
490
491         #
492         # deregister all punt socket
493         #
494         self.vapi.punt_socket_deregister(set_port(punt_l4, 1111))
495         self.vapi.punt_socket_deregister(set_port(punt_l4, 2222))
496         self.vapi.punt_socket_deregister(set_port(punt_l4, 3333))
497         punts = self.vapi.punt_socket_dump(type=pt_l4)
498         self.assertEqual(len(punts), 0)
499
500     def test_punt_socket_traffic_single_port_single_socket(self):
501         """Punt socket traffic single port single socket"""
502
503         port = self.ports[0]
504         pt_l4 = VppEnum.vl_api_punt_type_t.PUNT_API_TYPE_L4
505         af_ip6 = VppEnum.vl_api_address_family_t.ADDRESS_IP6
506         udp_proto = VppEnum.vl_api_ip_proto_t.IP_API_PROTO_UDP
507         punt_l4 = {
508             "type": pt_l4,
509             "punt": {
510                 "l4": {
511                     "af": af_ip6,
512                     "protocol": udp_proto,
513                     "port": port,
514                 }
515             },
516         }
517
518         p = (
519             Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
520             / IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6)
521             / inet6.UDP(sport=9876, dport=port)
522             / Raw(b"\xa5" * 100)
523         )
524
525         pkts = p * self.nr_packets
526
527         punts = self.vapi.punt_socket_dump(type=pt_l4)
528         self.assertEqual(len(punts), 0)
529
530         #
531         # expect ICMPv6 - destination unreachable for all packets
532         #
533         self.vapi.cli("clear trace")
534         self.pg0.add_stream(pkts)
535         self.pg_enable_capture(self.pg_interfaces)
536         self.pg_start()
537         # FIXME - when punt socket deregister is implemented
538         # rx = self.pg0.get_capture(self.nr_packets)
539         # for p in rx:
540         #     self.assertEqual(int(p[IPv6].nh), 58)                # ICMPv6
541         #     self.assertEqual(int(p[ICMPv6DestUnreach].code),4)  # unreachable
542
543         #
544         # configure a punt socket
545         #
546         self.socket_client_create("%s/socket_%d" % (self.tempdir, port))
547         self.vapi.punt_socket_register(punt_l4, "%s/socket_%d" % (self.tempdir, port))
548         punts = self.vapi.punt_socket_dump(type=pt_l4)
549         self.assertEqual(len(punts), 1)
550
551         #
552         # expect punt socket and no packets on pg0
553         #
554         self.vapi.cli("clear errors")
555         self.vapi.cli("clear trace")
556         self.pg0.add_stream(pkts)
557         self.pg_enable_capture(self.pg_interfaces)
558         self.pg_start()
559         self.pg0.get_capture(0)
560         self.logger.info(self.vapi.cli("show trace"))
561         rx = self.socket_client_close()
562         self.verify_udp_pkts(rx, len(pkts), port)
563
564         #
565         # remove punt socket. expect ICMP - dest. unreachable for all packets
566         #
567         self.vapi.punt_socket_deregister(punt_l4)
568         punts = self.vapi.punt_socket_dump(type=pt_l4)
569         self.assertEqual(len(punts), 0)
570         self.pg0.add_stream(pkts)
571         self.pg_enable_capture(self.pg_interfaces)
572         self.pg_start()
573         # FIXME - when punt socket deregister is implemented
574         # self.pg0.get_capture(nr_packets)
575
576     def test_punt_socket_traffic_multi_ports_multi_sockets(self):
577         """Punt socket traffic multi ports and multi sockets"""
578
579         punt_l4 = mk_vpp_cfg6()
580
581         # configuration for each UDP port
582         cfgs = dict()
583
584         #
585         # create stream of packets for each port
586         #
587         for port in self.ports:
588             # choose port from port list
589             cfgs[port] = {}
590
591             pkt = (
592                 Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
593                 / IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6)
594                 / UDP(sport=9876, dport=port)
595                 / Raw(b"\xa5" * 100)
596             )
597             cfgs[port]["pkts"] = pkt * self.nr_packets
598             cfgs[port]["port"] = port
599             cfgs[port]["vpp"] = copy.deepcopy(set_port(punt_l4, port))
600
601             # configure punt sockets
602             cfgs[port]["sock"] = self.socket_client_create(
603                 "%s/socket_%d" % (self.tempdir, port)
604             )
605             self.vapi.punt_socket_register(
606                 cfgs[port]["vpp"], "%s/socket_%d" % (self.tempdir, port)
607             )
608
609         #
610         # send the packets that get punted
611         #
612         for cfg in cfgs.values():
613             self.send_and_assert_no_replies(self.pg0, cfg["pkts"])
614
615         #
616         # test that we got the excepted packets on the expected socket
617         #
618         for cfg in cfgs.values():
619             rx = cfg["sock"].close()
620             self.verify_udp_pkts(rx, len(cfg["pkts"]), cfg["port"])
621             self.vapi.punt_socket_deregister(cfg["vpp"])
622
623     def test_punt_socket_traffic_multi_ports_single_socket(self):
624         """Punt socket traffic multi ports and single socket"""
625
626         pt_l4 = VppEnum.vl_api_punt_type_t.PUNT_API_TYPE_L4
627         af_ip6 = VppEnum.vl_api_address_family_t.ADDRESS_IP6
628         udp_proto = VppEnum.vl_api_ip_proto_t.IP_API_PROTO_UDP
629         punt_l4 = {
630             "type": pt_l4,
631             "punt": {
632                 "l4": {
633                     "af": af_ip6,
634                     "protocol": udp_proto,
635                 }
636             },
637         }
638
639         #
640         # create stream of packets with each port
641         #
642         pkts = []
643         for port in self.ports:
644             # choose port from port list
645             pkt = (
646                 Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
647                 / IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6)
648                 / UDP(sport=9876, dport=port)
649                 / Raw(b"\xa5" * 100)
650             )
651             pkts += pkt * self.nr_packets
652
653         #
654         # no punt socket
655         #
656         punts = self.vapi.punt_socket_dump(type=pt_l4)
657         self.assertEqual(len(punts), 0)
658
659         #
660         # configure a punt socket
661         #
662         self.socket_client_create("%s/socket_multi" % self.tempdir)
663         for p in self.ports:
664             self.vapi.punt_socket_register(
665                 set_port(punt_l4, p), "%s/socket_multi" % self.tempdir
666             )
667         punts = self.vapi.punt_socket_dump(type=pt_l4)
668         self.assertEqual(len(punts), len(self.ports))
669
670         #
671         # expect punt socket and no packets on pg0
672         #
673         self.vapi.cli("clear errors")
674         self.vapi.cli("clear trace")
675         self.pg0.add_stream(pkts)
676         self.pg_enable_capture(self.pg_interfaces)
677         self.pg_start()
678         # give a chance to punt socket to collect all packets
679         self.sleep(1)
680         self.pg0.get_capture(0)
681         rx = self.socket_client_close()
682
683         for p in self.ports:
684             self.verify_udp_pkts(rx, self.nr_packets, p)
685             self.vapi.punt_socket_deregister(set_port(punt_l4, p))
686         punts = self.vapi.punt_socket_dump(type=pt_l4)
687         self.assertEqual(len(punts), 0)
688
689
690 class TestExceptionPuntSocket(TestPuntSocket):
691     """Punt Socket for Exceptions"""
692
693     @classmethod
694     def setUpClass(cls):
695         super(TestExceptionPuntSocket, cls).setUpClass()
696
697     @classmethod
698     def tearDownClass(cls):
699         super(TestExceptionPuntSocket, cls).tearDownClass()
700
701     def setUp(self):
702         super(TestExceptionPuntSocket, self).setUp()
703
704         self.create_pg_interfaces(range(2))
705         for i in self.pg_interfaces:
706             i.config_ip4()
707             i.resolve_arp()
708
709     def tearDown(self):
710         super(TestExceptionPuntSocket, self).tearDown()
711         for i in self.pg_interfaces:
712             i.unconfig_ip4()
713             i.admin_down()
714
715     def test_registration(self):
716         """Punt socket registration/deregistration"""
717
718         pt_ex = VppEnum.vl_api_punt_type_t.PUNT_API_TYPE_EXCEPTION
719
720         punts = self.vapi.punt_socket_dump(type=pt_ex)
721         self.assertEqual(len(punts), 0)
722
723         #
724         # configure a punt socket
725         #
726         punt_ex = {"type": pt_ex, "punt": {"exception": {}}}
727
728         self.vapi.punt_socket_register(
729             set_reason(punt_ex, 1), "%s/socket_punt_1" % self.tempdir
730         )
731         self.vapi.punt_socket_register(
732             set_reason(punt_ex, 2), "%s/socket_punt_2" % self.tempdir
733         )
734         punts = self.vapi.punt_socket_dump(type=pt_ex)
735         self.assertEqual(len(punts), 2)
736         self.verify_exception(set_reason(punt_ex, 1), punts[0])
737         self.verify_exception(set_reason(punt_ex, 2), punts[1])
738
739         #
740         # deregister a punt socket
741         #
742         self.vapi.punt_socket_deregister(set_reason(punt_ex, 1))
743         punts = self.vapi.punt_socket_dump(type=pt_ex)
744         self.assertEqual(len(punts), 1)
745
746         #
747         # configure a punt socket again
748         #
749         self.vapi.punt_socket_register(
750             set_reason(punt_ex, 1), "%s/socket_punt_1" % self.tempdir
751         )
752         self.vapi.punt_socket_register(
753             set_reason(punt_ex, 3), "%s/socket_punt_3" % self.tempdir
754         )
755         punts = self.vapi.punt_socket_dump(type=pt_ex)
756         self.assertEqual(len(punts), 3)
757
758         self.logger.info(self.vapi.cli("sh punt sock reg exception"))
759
760         #
761         # deregister all punt socket
762         #
763         self.vapi.punt_socket_deregister(set_reason(punt_ex, 1))
764         self.vapi.punt_socket_deregister(set_reason(punt_ex, 2))
765         self.vapi.punt_socket_deregister(set_reason(punt_ex, 3))
766         punts = self.vapi.punt_socket_dump(type=pt_ex)
767         self.assertEqual(len(punts), 0)
768
769     def verify_esp_pkts(self, rxs, n_sent, spi, has_udp):
770         self.assertEqual(len(rxs), n_sent)
771         for rx in rxs:
772             self.assertTrue(rx.haslayer(IP))
773             self.assertTrue(rx.haslayer(ESP))
774             self.assertEqual(rx[ESP].spi, spi)
775             if has_udp:
776                 self.assertTrue(rx.haslayer(UDP))
777
778     def test_traffic(self):
779         """Punt socket traffic"""
780
781         port = self.ports[0]
782         pt_ex = VppEnum.vl_api_punt_type_t.PUNT_API_TYPE_EXCEPTION
783         punt_ex = {"type": pt_ex, "punt": {"exception": {}}}
784
785         #
786         # we're dealing with IPSec tunnels punting for no-such-tunnel
787         # (SPI=0 goes to ikev2)
788         #
789         cfgs = dict()
790         cfgs["ipsec4-no-such-tunnel"] = {"spi": 99, "udp": False, "itf": self.pg0}
791
792         #
793         # find the VPP ID for these punt exception reasin
794         #
795         rs = self.vapi.punt_reason_dump()
796         for key in cfgs:
797             for r in rs:
798                 print(r.reason.name)
799                 print(key)
800                 if r.reason.name == key:
801                     cfgs[key]["id"] = r.reason.id
802                     cfgs[key]["vpp"] = copy.deepcopy(
803                         set_reason(punt_ex, cfgs[key]["id"])
804                     )
805                     break
806
807         #
808         # configure punt sockets
809         #
810         for cfg in cfgs.values():
811             cfg["sock"] = self.socket_client_create(
812                 "%s/socket_%d" % (self.tempdir, cfg["id"])
813             )
814             self.vapi.punt_socket_register(
815                 cfg["vpp"], "%s/socket_%d" % (self.tempdir, cfg["id"])
816             )
817
818         #
819         # create packet streams for 'no-such-tunnel' exception
820         #
821         for cfg in cfgs.values():
822             pkt = Ether(src=cfg["itf"].remote_mac, dst=cfg["itf"].local_mac) / IP(
823                 src=cfg["itf"].remote_ip4, dst=cfg["itf"].local_ip4
824             )
825             if cfg["udp"]:
826                 pkt = pkt / UDP(sport=666, dport=4500)
827             pkt = pkt / ESP(spi=cfg["spi"], seq=3) / Raw(b"\xa5" * 100)
828             cfg["pkts"] = [pkt]
829
830         #
831         # send packets for each SPI we expect to be punted
832         #
833         for cfg in cfgs.values():
834             self.send_and_assert_no_replies(cfg["itf"], cfg["pkts"])
835
836         #
837         # verify the punted packets arrived on the associated socket
838         #
839         for cfg in cfgs.values():
840             rx = cfg["sock"].close()
841             self.verify_esp_pkts(rx, len(cfg["pkts"]), cfg["spi"], cfg["udp"])
842
843         #
844         # add some tunnels, make sure it still punts
845         #
846         tun = VppIpsecInterface(self).add_vpp_config()
847         sa_in = VppIpsecSA(
848             self,
849             11,
850             11,
851             (VppEnum.vl_api_ipsec_integ_alg_t.IPSEC_API_INTEG_ALG_SHA1_96),
852             b"0123456701234567",
853             (VppEnum.vl_api_ipsec_crypto_alg_t.IPSEC_API_CRYPTO_ALG_AES_CBC_128),
854             b"0123456701234567",
855             50,
856             self.pg0.local_ip4,
857             self.pg0.remote_ip4,
858         ).add_vpp_config()
859         sa_out = VppIpsecSA(
860             self,
861             22,
862             22,
863             (VppEnum.vl_api_ipsec_integ_alg_t.IPSEC_API_INTEG_ALG_SHA1_96),
864             b"0123456701234567",
865             (VppEnum.vl_api_ipsec_crypto_alg_t.IPSEC_API_CRYPTO_ALG_AES_CBC_128),
866             b"0123456701234567",
867             50,
868             self.pg0.local_ip4,
869             self.pg0.remote_ip4,
870         ).add_vpp_config()
871         protect = VppIpsecTunProtect(self, tun, sa_out, [sa_in]).add_vpp_config()
872
873         #
874         # send packets for each SPI we expect to be punted
875         #
876         for cfg in cfgs.values():
877             self.send_and_assert_no_replies(cfg["itf"], cfg["pkts"])
878
879         #
880         # verify the punted packets arrived on the associated socket
881         #
882         for cfg in cfgs.values():
883             rx = cfg["sock"].close()
884             self.verify_esp_pkts(rx, len(cfg["pkts"]), cfg["spi"], cfg["udp"])
885         #
886         # socket deregister
887         #
888         for cfg in cfgs.values():
889             self.vapi.punt_socket_deregister(cfg["vpp"])
890
891
892 class TestIpProtoPuntSocket(TestPuntSocket):
893     """Punt Socket for IP packets"""
894
895     @classmethod
896     def setUpClass(cls):
897         super(TestIpProtoPuntSocket, cls).setUpClass()
898
899     @classmethod
900     def tearDownClass(cls):
901         super(TestIpProtoPuntSocket, cls).tearDownClass()
902
903     def setUp(self):
904         super(TestIpProtoPuntSocket, self).setUp()
905
906         for i in self.pg_interfaces:
907             i.config_ip4()
908             i.resolve_arp()
909
910     def tearDown(self):
911         super(TestIpProtoPuntSocket, self).tearDown()
912         for i in self.pg_interfaces:
913             i.unconfig_ip4()
914             i.admin_down()
915
916     def test_registration(self):
917         """Punt socket registration/deregistration"""
918
919         af_ip4 = VppEnum.vl_api_address_family_t.ADDRESS_IP4
920         pt_ip = VppEnum.vl_api_punt_type_t.PUNT_API_TYPE_IP_PROTO
921         proto_ospf = VppEnum.vl_api_ip_proto_t.IP_API_PROTO_OSPF
922         proto_eigrp = VppEnum.vl_api_ip_proto_t.IP_API_PROTO_EIGRP
923
924         punts = self.vapi.punt_socket_dump(type=pt_ip)
925         self.assertEqual(len(punts), 0)
926
927         #
928         # configure a punt socket
929         #
930         punt_ospf = {
931             "type": pt_ip,
932             "punt": {"ip_proto": {"af": af_ip4, "protocol": proto_ospf}},
933         }
934         punt_eigrp = {
935             "type": pt_ip,
936             "punt": {"ip_proto": {"af": af_ip4, "protocol": proto_eigrp}},
937         }
938
939         self.vapi.punt_socket_register(punt_ospf, "%s/socket_punt_1" % self.tempdir)
940         self.vapi.punt_socket_register(punt_eigrp, "%s/socket_punt_2" % self.tempdir)
941         self.logger.info(self.vapi.cli("sh punt sock reg ip"))
942         punts = self.vapi.punt_socket_dump(type=pt_ip)
943         self.assertEqual(len(punts), 2)
944         self.verify_ip_proto(punt_ospf, punts[0])
945         self.verify_ip_proto(punt_eigrp, punts[1])
946
947         #
948         # deregister a punt socket
949         #
950         self.vapi.punt_socket_deregister(punt_ospf)
951         punts = self.vapi.punt_socket_dump(type=pt_ip)
952         self.assertEqual(len(punts), 1)
953
954         #
955         # configure a punt socket again
956         #
957         self.vapi.punt_socket_register(punt_ospf, "%s/socket_punt_3" % self.tempdir)
958         punts = self.vapi.punt_socket_dump(type=pt_ip)
959         self.assertEqual(len(punts), 2)
960
961         self.logger.info(self.vapi.cli("sh punt sock reg exception"))
962
963         #
964         # deregister all punt socket
965         #
966         self.vapi.punt_socket_deregister(punt_eigrp)
967         self.vapi.punt_socket_deregister(punt_ospf)
968         punts = self.vapi.punt_socket_dump(type=pt_ip)
969         self.assertEqual(len(punts), 0)
970
971     def verify_ospf_pkts(self, rxs, n_sent):
972         self.assertEqual(len(rxs), n_sent)
973         for rx in rxs:
974             self.assertTrue(rx.haslayer(OSPF_Hdr))
975
976     def test_traffic(self):
977         """Punt socket traffic"""
978
979         af_ip4 = VppEnum.vl_api_address_family_t.ADDRESS_IP4
980         pt_ip = VppEnum.vl_api_punt_type_t.PUNT_API_TYPE_IP_PROTO
981         proto_ospf = VppEnum.vl_api_ip_proto_t.IP_API_PROTO_OSPF
982
983         #
984         # configure a punt socket to capture OSPF packets
985         #
986         punt_ospf = {
987             "type": pt_ip,
988             "punt": {"ip_proto": {"af": af_ip4, "protocol": proto_ospf}},
989         }
990
991         #
992         # create packet streams and configure a punt sockets
993         #
994         pkt = (
995             Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
996             / IP(src=self.pg0.remote_ip4, dst=self.pg0.local_ip4)
997             / OSPF_Hdr()
998             / OSPFv3_Hello()
999         )
1000         pkts = pkt * 7
1001
1002         sock = self.socket_client_create("%s/socket_1" % self.tempdir)
1003         self.vapi.punt_socket_register(punt_ospf, "%s/socket_1" % self.tempdir)
1004
1005         #
1006         # send packets for each SPI we expect to be punted
1007         #
1008         self.send_and_assert_no_replies(self.pg0, pkts)
1009
1010         #
1011         # verify the punted packets arrived on the associated socket
1012         #
1013         rx = sock.close()
1014         self.verify_ospf_pkts(rx, len(pkts))
1015         self.vapi.punt_socket_deregister(punt_ospf)
1016
1017
1018 @tag_fixme_vpp_workers
1019 class TestPunt(VppTestCase):
1020     """Exception Punt Test Case"""
1021
1022     @classmethod
1023     def setUpClass(cls):
1024         super(TestPunt, cls).setUpClass()
1025
1026     @classmethod
1027     def tearDownClass(cls):
1028         super(TestPunt, cls).tearDownClass()
1029
1030     def setUp(self):
1031         super(TestPunt, self).setUp()
1032
1033         self.create_pg_interfaces(range(4))
1034
1035         for i in self.pg_interfaces:
1036             i.admin_up()
1037             i.config_ip4()
1038             i.resolve_arp()
1039             i.config_ip6()
1040             i.resolve_ndp()
1041
1042     def tearDown(self):
1043         for i in self.pg_interfaces:
1044             i.unconfig_ip4()
1045             i.unconfig_ip6()
1046             i.admin_down()
1047         super(TestPunt, self).tearDown()
1048
1049     def test_punt(self):
1050         """Exception Path testing"""
1051
1052         #
1053         # dump the punt registered reasons
1054         #  search for a few we know should be there
1055         #
1056         rs = self.vapi.punt_reason_dump()
1057
1058         reasons = [
1059             "ipsec6-no-such-tunnel",
1060             "ipsec4-no-such-tunnel",
1061             "ipsec4-spi-o-udp-0",
1062         ]
1063
1064         for reason in reasons:
1065             found = False
1066             for r in rs:
1067                 if r.reason.name == reason:
1068                     found = True
1069                     break
1070             self.assertTrue(found)
1071
1072         #
1073         # Using the test CLI we will hook in a exception path to
1074         # send ACL deny packets out of pg0 and pg1.
1075         # the ACL is src,dst = 1.1.1.1,1.1.1.2
1076         #
1077         ip_1_1_1_2 = VppIpRoute(
1078             self,
1079             "1.1.1.2",
1080             32,
1081             [VppRoutePath(self.pg3.remote_ip4, self.pg3.sw_if_index)],
1082         )
1083         ip_1_1_1_2.add_vpp_config()
1084         ip_1_2 = VppIpRoute(
1085             self,
1086             "1::2",
1087             128,
1088             [
1089                 VppRoutePath(
1090                     self.pg3.remote_ip6,
1091                     self.pg3.sw_if_index,
1092                     proto=DpoProto.DPO_PROTO_IP6,
1093                 )
1094             ],
1095         )
1096         ip_1_2.add_vpp_config()
1097
1098         p4 = (
1099             Ether(src=self.pg2.remote_mac, dst=self.pg2.local_mac)
1100             / IP(src="1.1.1.1", dst="1.1.1.2")
1101             / UDP(sport=1234, dport=1234)
1102             / Raw(b"\xa5" * 100)
1103         )
1104         p6 = (
1105             Ether(src=self.pg2.remote_mac, dst=self.pg2.local_mac)
1106             / IPv6(src="1::1", dst="1::2")
1107             / UDP(sport=1234, dport=1234)
1108             / Raw(b"\xa5" * 100)
1109         )
1110         self.send_and_expect(self.pg2, p4 * 1, self.pg3)
1111         self.send_and_expect(self.pg2, p6 * 1, self.pg3)
1112
1113         #
1114         # apply the punting features
1115         #
1116         self.vapi.cli("test punt pg2")
1117
1118         #
1119         # dump the punt reasons to learn the IDs assigned
1120         #
1121         rs = self.vapi.punt_reason_dump(reason={"name": "reason-v4"})
1122         r4 = rs[0].reason.id
1123         rs = self.vapi.punt_reason_dump(reason={"name": "reason-v6"})
1124         r6 = rs[0].reason.id
1125
1126         #
1127         # pkts now dropped
1128         #
1129         self.send_and_assert_no_replies(self.pg2, p4 * NUM_PKTS)
1130         self.send_and_assert_no_replies(self.pg2, p6 * NUM_PKTS)
1131
1132         #
1133         # Check state:
1134         #  1 - node error counters
1135         #  2 - per-reason counters
1136         #    2, 3 are the index of the assigned punt reason
1137         #
1138         stats = self.statistics.get_err_counter("/err/punt-dispatch/No registrations")
1139         self.assertEqual(stats, 2 * NUM_PKTS)
1140
1141         stats = self.statistics.get_counter("/net/punt")
1142         self.assertEqual(stats[0][r4]["packets"], NUM_PKTS)
1143         self.assertEqual(stats[0][r6]["packets"], NUM_PKTS)
1144
1145         #
1146         # use the test CLI to test a client that punts exception
1147         # packets out of pg0
1148         #
1149         self.vapi.cli("test punt pg0 %s" % self.pg0.remote_ip4)
1150         self.vapi.cli("test punt pg0 %s" % self.pg0.remote_ip6)
1151
1152         rx4s = self.send_and_expect(self.pg2, p4 * NUM_PKTS, self.pg0)
1153         rx6s = self.send_and_expect(self.pg2, p6 * NUM_PKTS, self.pg0)
1154
1155         #
1156         # check the packets come out IP unmodified but destined to pg0 host
1157         #
1158         for rx in rx4s:
1159             self.assertEqual(rx[Ether].dst, self.pg0.remote_mac)
1160             self.assertEqual(rx[Ether].src, self.pg0.local_mac)
1161             self.assertEqual(p4[IP].dst, rx[IP].dst)
1162             self.assertEqual(p4[IP].ttl, rx[IP].ttl)
1163         for rx in rx6s:
1164             self.assertEqual(rx[Ether].dst, self.pg0.remote_mac)
1165             self.assertEqual(rx[Ether].src, self.pg0.local_mac)
1166             self.assertEqual(p6[IPv6].dst, rx[IPv6].dst)
1167             self.assertEqual(p6[IPv6].hlim, rx[IPv6].hlim)
1168
1169         stats = self.statistics.get_counter("/net/punt")
1170         self.assertEqual(stats[0][r4]["packets"], 2 * NUM_PKTS)
1171         self.assertEqual(stats[0][r6]["packets"], 2 * NUM_PKTS)
1172
1173         #
1174         # add another registration for the same reason to send packets
1175         # out of pg1
1176         #
1177         self.vapi.cli("test punt pg1 %s" % self.pg1.remote_ip4)
1178         self.vapi.cli("test punt pg1 %s" % self.pg1.remote_ip6)
1179
1180         self.vapi.cli("clear trace")
1181         self.pg2.add_stream(p4 * NUM_PKTS)
1182         self.pg_enable_capture(self.pg_interfaces)
1183         self.pg_start()
1184
1185         rxd = self.pg0.get_capture(NUM_PKTS)
1186         for rx in rxd:
1187             self.assertEqual(rx[Ether].dst, self.pg0.remote_mac)
1188             self.assertEqual(rx[Ether].src, self.pg0.local_mac)
1189             self.assertEqual(p4[IP].dst, rx[IP].dst)
1190             self.assertEqual(p4[IP].ttl, rx[IP].ttl)
1191         rxd = self.pg1.get_capture(NUM_PKTS)
1192         for rx in rxd:
1193             self.assertEqual(rx[Ether].dst, self.pg1.remote_mac)
1194             self.assertEqual(rx[Ether].src, self.pg1.local_mac)
1195             self.assertEqual(p4[IP].dst, rx[IP].dst)
1196             self.assertEqual(p4[IP].ttl, rx[IP].ttl)
1197
1198         self.vapi.cli("clear trace")
1199         self.pg2.add_stream(p6 * NUM_PKTS)
1200         self.pg_enable_capture(self.pg_interfaces)
1201         self.pg_start()
1202
1203         rxd = self.pg0.get_capture(NUM_PKTS)
1204         for rx in rxd:
1205             self.assertEqual(rx[Ether].dst, self.pg0.remote_mac)
1206             self.assertEqual(rx[Ether].src, self.pg0.local_mac)
1207             self.assertEqual(p6[IPv6].dst, rx[IPv6].dst)
1208             self.assertEqual(p6[IPv6].hlim, rx[IPv6].hlim)
1209         rxd = self.pg1.get_capture(NUM_PKTS)
1210         for rx in rxd:
1211             self.assertEqual(rx[Ether].dst, self.pg1.remote_mac)
1212             self.assertEqual(rx[Ether].src, self.pg1.local_mac)
1213             self.assertEqual(p6[IPv6].dst, rx[IPv6].dst)
1214             self.assertEqual(p6[IPv6].hlim, rx[IPv6].hlim)
1215
1216         stats = self.statistics.get_counter("/net/punt")
1217         self.assertEqual(stats[0][r4]["packets"], 3 * NUM_PKTS)
1218         self.assertEqual(stats[0][r6]["packets"], 3 * NUM_PKTS)
1219
1220         self.logger.info(self.vapi.cli("show vlib graph punt-dispatch"))
1221         self.logger.info(self.vapi.cli("show punt client"))
1222         self.logger.info(self.vapi.cli("show punt reason"))
1223         self.logger.info(self.vapi.cli("show punt stats"))
1224         self.logger.info(self.vapi.cli("show punt db"))
1225
1226
1227 if __name__ == "__main__":
1228     unittest.main(testRunner=VppTestRunner)