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