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