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