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