VTL Cleanup: Fix missing calls to setUpClass/tearDownClass, fix numerous TypeErrors.
[vpp.git] / test / test_punt.py
1 #!/usr/bin/env python
2 import binascii
3 import random
4 import socket
5 import unittest
6 import os
7 import scapy.layers.inet6 as inet6
8 import threading
9 import struct
10
11 from struct import unpack, unpack_from
12 from util import ppp, ppc
13 from re import compile
14 from scapy.packet import Raw
15 from scapy.layers.l2 import Ether
16 from scapy.layers.inet import IP, UDP, ICMP
17 from scapy.layers.inet6 import IPv6, ICMPv6DestUnreach
18 from framework import VppTestCase, VppTestRunner
19
20
21 # Format MAC Address
22 def get_mac_addr(bytes_addr):
23     return ':'.join('%02x' % ord(b) for b in bytes_addr)
24
25
26 # Format IP Address
27 def ipv4(bytes_addr):
28     return '.'.join('%d' % ord(b) for b in bytes_addr)
29
30
31 # Unpack Ethernet Frame
32 def ethernet_frame(data):
33     dest_mac, src_mac, proto = struct.unpack('! 6s 6s H', data[:14])
34     return dest_mac, src_mac, socket.htons(proto), data[14:]
35
36
37 # Unpack IPv4 Packets
38 def ipv4_packet(data):
39     proto, src, target = struct.unpack('! 8x 1x B 2x 4s 4s', data[:20])
40     return proto, src, target, data[20:]
41
42
43 # Unpack IPv6 Packets
44 def ipv6_packet(data):
45     nh, src, target = struct.unpack('! 6x B 1x 16s 16s', data[:40])
46     return nh, src, target, data[40:]
47
48
49 # Unpacks any UDP Packet
50 def udp_seg(data):
51     src_port, dest_port, size = struct.unpack('! H H 2x H', data[:8])
52     return src_port, dest_port, size, data[8:]
53
54
55 # Unpacks any TCP Packet
56 def tcp_seg(data):
57     src_port, dest_port, seq, flag = struct.unpack('! H H L 4x H', data[:14])
58     return src_port, dest_port, seq, data[((flag >> 12) * 4):]
59
60
61 def receivePackets(sock, counters):
62     # Wait for some packets on socket
63     while True:
64         data = sock.recv(65536)
65
66         # punt socket metadata
67         # packet_desc = data[0:8]
68
69         # Ethernet
70         _, _, eth_proto, data = ethernet_frame(data[8:])
71         # Ipv4
72         if eth_proto == 8:
73             proto, _, _, data = ipv4_packet(data)
74             # TCP
75             if proto == 6:
76                 _, dst_port, _, data = udp_seg(data)
77             # UDP
78             elif proto == 17:
79                 _, dst_port, _, data = udp_seg(data)
80                 counters[dst_port] = 0
81         # Ipv6
82         elif eth_proto == 0xdd86:
83             nh, _, _, data = ipv6_packet(data)
84             # TCP
85             if nh == 6:
86                 _, dst_port, _, data = udp_seg(data)
87             # UDP
88             elif nh == 17:
89                 _, dst_port, _, data = udp_seg(data)
90                 counters[dst_port] = 0
91
92
93 class serverSocketThread(threading.Thread):
94     """ Socket server thread"""
95
96     def __init__(self, threadID, sockName, counters):
97         threading.Thread.__init__(self)
98         self.threadID = threadID
99         self.sockName = sockName
100         self.sock = None
101         self.counters = counters
102
103     def run(self):
104         self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
105         try:
106             os.unlink(self.sockName)
107         except:
108             pass
109         self.sock.bind(self.sockName)
110
111         receivePackets(self.sock, self.counters)
112
113
114 class TestPuntSocket(VppTestCase):
115     """ Punt Socket """
116
117     ports = [1111, 2222, 3333, 4444]
118     sock_servers = list()
119     portsCheck = dict()
120     nr_packets = 256
121
122     @classmethod
123     def setUpClass(cls):
124         super(TestPuntSocket, cls).setUpClass()
125
126     @classmethod
127     def tearDownClass(cls):
128         super(TestPuntSocket, cls).tearDownClass()
129
130     @classmethod
131     def setUpConstants(cls):
132         cls.extra_vpp_punt_config = [
133             "punt", "{", "socket", cls.tempdir+"/socket_punt", "}"]
134         super(TestPuntSocket, cls).setUpConstants()
135
136     def setUp(self):
137         super(TestPuntSocket, self).setUp()
138         random.seed()
139
140         self.create_pg_interfaces(range(2))
141         for i in self.pg_interfaces:
142             i.admin_up()
143
144     def tearDown(self):
145         del self.sock_servers[:]
146         super(TestPuntSocket, self).tearDown()
147
148     def socket_client_create(self, sock_name, id=None):
149         thread = serverSocketThread(id, sock_name, self.portsCheck)
150         self.sock_servers.append(thread)
151         thread.start()
152
153     def socket_client_close(self):
154         for thread in self.sock_servers:
155             thread.sock.close()
156
157
158 class TestIP4PuntSocket(TestPuntSocket):
159     """ Punt Socket for IPv4 """
160
161     @classmethod
162     def setUpClass(cls):
163         super(TestIP4PuntSocket, cls).setUpClass()
164
165     @classmethod
166     def tearDownClass(cls):
167         super(TestIP4PuntSocket, cls).tearDownClass()
168
169     def setUp(self):
170         super(TestIP4PuntSocket, self).setUp()
171
172         for i in self.pg_interfaces:
173             i.config_ip4()
174             i.resolve_arp()
175
176     def tearDown(self):
177         super(TestIP4PuntSocket, self).tearDown()
178         for i in self.pg_interfaces:
179             i.unconfig_ip4()
180             i.admin_down()
181
182     def test_punt_socket_dump(self):
183         """ Punt socket registration/deregistration"""
184
185         punts = self.vapi.punt_socket_dump(is_ip6=0)
186         self.assertEqual(len(punts), 0)
187
188         #
189         # configure a punt socket
190         #
191         self.vapi.punt_socket_register(1111, self.tempdir+"/socket_punt_1111")
192         self.vapi.punt_socket_register(2222, self.tempdir+"/socket_punt_2222")
193         punts = self.vapi.punt_socket_dump(is_ip6=0)
194         self.assertEqual(len(punts), 2)
195         self.assertEqual(punts[0].punt.l4_port, 1111)
196         self.assertEqual(punts[1].punt.l4_port, 2222)
197
198         #
199         # deregister a punt socket
200         #
201         self.vapi.punt_socket_deregister(1111)
202         punts = self.vapi.punt_socket_dump(is_ip6=0)
203         self.assertEqual(len(punts), 1)
204
205         #
206         # configure a punt socket again
207         #
208         self.vapi.punt_socket_register(1111, self.tempdir+"/socket_punt_1111")
209         self.vapi.punt_socket_register(3333, self.tempdir+"/socket_punt_3333")
210         punts = self.vapi.punt_socket_dump(is_ip6=0)
211         self.assertEqual(len(punts), 3)
212
213         #
214         # deregister all punt socket
215         #
216         self.vapi.punt_socket_deregister(1111)
217         self.vapi.punt_socket_deregister(2222)
218         self.vapi.punt_socket_deregister(3333)
219         punts = self.vapi.punt_socket_dump(is_ip6=0)
220         self.assertEqual(len(punts), 0)
221
222     def test_punt_socket_traffic_single_port_single_socket(self):
223         """ Punt socket traffic single port single socket"""
224
225         port = self.ports[0]
226
227         p = (Ether(src=self.pg0.remote_mac,
228                    dst=self.pg0.local_mac) /
229              IP(src=self.pg0.remote_ip4, dst=self.pg0.local_ip4) /
230              UDP(sport=9876, dport=port) /
231              Raw('\xa5' * 100))
232
233         pkts = p * self.nr_packets
234         self.portsCheck[port] = self.nr_packets
235
236         punts = self.vapi.punt_socket_dump(is_ip6=0)
237         self.assertEqual(len(punts), 0)
238
239         #
240         # expect ICMP - port unreachable for all packets
241         #
242         self.vapi.cli("clear trace")
243         self.pg0.add_stream(pkts)
244         self.pg_enable_capture(self.pg_interfaces)
245         self.pg_start()
246         # FIXME - when punt socket deregister is implemented
247         # rx = self.pg0.get_capture(self.nr_packets)
248         # for p in rx:
249         #     self.assertEqual(int(p[IP].proto), 1)   # ICMP
250         #     self.assertEqual(int(p[ICMP].code), 3)  # unreachable
251
252         #
253         # configure a punt socket
254         #
255         self.socket_client_create(self.tempdir+"/socket_" + str(port))
256         self.vapi.punt_socket_register(port, self.tempdir+"/socket_" +
257                                        str(port))
258         punts = self.vapi.punt_socket_dump(is_ip6=0)
259         self.assertEqual(len(punts), 1)
260
261         self.logger.debug("Sending %s packets to port %d",
262                           str(self.portsCheck[port]), port)
263         #
264         # expect punt socket and no packets on pg0
265         #
266         self.vapi.cli("clear errors")
267         self.vapi.cli("clear trace")
268         self.pg0.add_stream(pkts)
269         self.pg_enable_capture(self.pg_interfaces)
270         self.pg_start()
271         self.pg0.get_capture(0)
272         self.logger.info(self.vapi.cli("show trace"))
273         self.socket_client_close()
274         self.assertEqual(self.portsCheck[port], 0)
275
276         #
277         # remove punt socket. expect ICMP - port unreachable for all packets
278         #
279         self.vapi.punt_socket_deregister(port)
280         punts = self.vapi.punt_socket_dump(is_ip6=0)
281         self.assertEqual(len(punts), 0)
282         self.pg0.add_stream(pkts)
283         self.pg_enable_capture(self.pg_interfaces)
284         self.pg_start()
285         # FIXME - when punt socket deregister is implemented
286         # self.pg0.get_capture(nr_packets)
287
288     def test_punt_socket_traffic_multi_port_multi_sockets(self):
289         """ Punt socket traffic multi ports and multi sockets"""
290
291         for p in self.ports:
292             self.portsCheck[p] = 0
293
294         #
295         # create stream with random pakets count per given ports
296         #
297         pkts = list()
298         for _ in range(0, self.nr_packets):
299             # choose port from port list
300             p = random.choice(self.ports)
301             pkts.append((
302                 Ether(src=self.pg0.remote_mac,
303                       dst=self.pg0.local_mac) /
304                 IP(src=self.pg0.remote_ip4, dst=self.pg0.local_ip4) /
305                 UDP(sport=9876, dport=p) /
306                 Raw('\xa5' * 100)))
307             self.portsCheck[p] += 1
308         #
309         # no punt socket
310         #
311         punts = self.vapi.punt_socket_dump(is_ip6=0)
312         self.assertEqual(len(punts), 0)
313
314         #
315         # configure a punt socket
316         #
317         for p in self.ports:
318             self.socket_client_create(self.tempdir+"/socket_" + str(p))
319             self.vapi.punt_socket_register(p, self.tempdir+"/socket_" + str(p))
320         punts = self.vapi.punt_socket_dump(is_ip6=0)
321         self.assertEqual(len(punts), len(self.ports))
322
323         for p in self.ports:
324             self.logger.debug("Sending %s packets to port %d",
325                               str(self.portsCheck[p]), p)
326
327         #
328         # expect punt socket and no packets on pg0
329         #
330         self.vapi.cli("clear errors")
331         self.vapi.cli("clear trace")
332         self.pg0.add_stream(pkts)
333         self.pg_enable_capture(self.pg_interfaces)
334         self.pg_start()
335         self.pg0.get_capture(0)
336         self.logger.info(self.vapi.cli("show trace"))
337         self.socket_client_close()
338
339         for p in self.ports:
340             self.assertEqual(self.portsCheck[p], 0)
341             self.vapi.punt_socket_deregister(p)
342         punts = self.vapi.punt_socket_dump(is_ip6=0)
343         self.assertEqual(len(punts), 0)
344
345     def test_punt_socket_traffic_multi_ports_single_socket(self):
346         """ Punt socket traffic multi ports and single socket"""
347
348         for p in self.ports:
349             self.portsCheck[p] = 0
350
351         #
352         # create stream with random pakets count per given ports
353         #
354         pkts = list()
355         for _ in range(0, self.nr_packets):
356             # choose port from port list
357             p = random.choice(self.ports)
358             pkts.append((
359                 Ether(src=self.pg0.remote_mac,
360                       dst=self.pg0.local_mac) /
361                 IP(src=self.pg0.remote_ip4, dst=self.pg0.local_ip4) /
362                 UDP(sport=9876, dport=p) /
363                 Raw('\xa5' * 100)))
364             self.portsCheck[p] += 1
365
366         #
367         # no punt socket
368         #
369         punts = self.vapi.punt_socket_dump(is_ip6=0)
370         self.assertEqual(len(punts), 0)
371
372         # configure a punt socket
373         #
374         self.socket_client_create(self.tempdir+"/socket_multi")
375         for p in self.ports:
376             self.vapi.punt_socket_register(p, self.tempdir+"/socket_multi")
377         punts = self.vapi.punt_socket_dump(is_ip6=0)
378         self.assertEqual(len(punts), len(self.ports))
379
380         for p in self.ports:
381             self.logger.debug("Sending %s packets to port %d",
382                               str(self.portsCheck[p]), p)
383         #
384         # expect punt socket and no packets on pg0
385         #
386         self.vapi.cli("clear errors")
387         self.vapi.cli("clear trace")
388         self.pg0.add_stream(pkts)
389         self.pg_enable_capture(self.pg_interfaces)
390         self.pg_start()
391         self.pg0.get_capture(0)
392         self.logger.info(self.vapi.cli("show trace"))
393         self.socket_client_close()
394
395         for p in self.ports:
396             self.assertEqual(self.portsCheck[p], 0)
397             self.vapi.punt_socket_deregister(p)
398         punts = self.vapi.punt_socket_dump(is_ip6=0)
399         self.assertEqual(len(punts), 0)
400
401
402 class TestIP6PuntSocket(TestPuntSocket):
403     """ Punt Socket for IPv6"""
404
405     @classmethod
406     def setUpClass(cls):
407         super(TestIP6PuntSocket, cls).setUpClass()
408
409     @classmethod
410     def tearDownClass(cls):
411         super(TestIP6PuntSocket, cls).tearDownClass()
412
413     def setUp(self):
414         super(TestIP6PuntSocket, self).setUp()
415
416         for i in self.pg_interfaces:
417             i.config_ip6()
418             i.resolve_ndp()
419
420     def tearDown(self):
421         super(TestIP6PuntSocket, self).tearDown()
422         for i in self.pg_interfaces:
423             i.unconfig_ip6()
424             i.admin_down()
425
426     def test_punt_socket_dump(self):
427         """ Punt socket registration """
428
429         punts = self.vapi.punt_socket_dump(is_ip6=1)
430         self.assertEqual(len(punts), 0)
431
432         #
433         # configure a punt socket
434         #
435         self.vapi.punt_socket_register(1111, self.tempdir+"/socket_1111",
436                                        is_ip4=0)
437         self.vapi.punt_socket_register(2222, self.tempdir+"/socket_2222",
438                                        is_ip4=0)
439         punts = self.vapi.punt_socket_dump(is_ip6=1)
440         self.assertEqual(len(punts), 2)
441         self.assertEqual(punts[0].punt.l4_port, 1111)
442         self.assertEqual(punts[1].punt.l4_port, 2222)
443
444         #
445         # deregister a punt socket
446         #
447         self.vapi.punt_socket_deregister(1111, is_ip4=0)
448         punts = self.vapi.punt_socket_dump(is_ip6=1)
449         self.assertEqual(len(punts), 1)
450
451         #
452         # configure a punt socket again
453         #
454         self.vapi.punt_socket_register(1111, self.tempdir+"/socket_1111",
455                                        is_ip4=0)
456         punts = self.vapi.punt_socket_dump(is_ip6=1)
457         self.assertEqual(len(punts), 2)
458
459         #
460         # deregister all punt socket
461         #
462         self.vapi.punt_socket_deregister(1111, is_ip4=0)
463         self.vapi.punt_socket_deregister(2222, is_ip4=0)
464         self.vapi.punt_socket_deregister(3333, is_ip4=0)
465         punts = self.vapi.punt_socket_dump(is_ip6=1)
466         self.assertEqual(len(punts), 0)
467
468     def test_punt_socket_traffic_single_port_single_socket(self):
469         """ Punt socket traffic single port single socket"""
470
471         port = self.ports[0]
472
473         p = (Ether(src=self.pg0.remote_mac,
474                    dst=self.pg0.local_mac) /
475              IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6) /
476              inet6.UDP(sport=9876, dport=port) /
477              Raw('\xa5' * 100))
478
479         pkts = p * self.nr_packets
480         self.portsCheck[port] = self.nr_packets
481
482         punts = self.vapi.punt_socket_dump(is_ip6=1)
483         self.assertEqual(len(punts), 0)
484
485         #
486         # expect ICMPv6 - destination unreachable for all packets
487         #
488         self.vapi.cli("clear trace")
489         self.pg0.add_stream(pkts)
490         self.pg_enable_capture(self.pg_interfaces)
491         self.pg_start()
492         # FIXME - when punt socket deregister is implemented
493         # rx = self.pg0.get_capture(self.nr_packets)
494         # for p in rx:
495         #     self.assertEqual(int(p[IPv6].nh), 58)                # ICMPv6
496         #     self.assertEqual(int(p[ICMPv6DestUnreach].code),4)  # unreachable
497
498         #
499         # configure a punt socket
500         #
501         self.socket_client_create(self.tempdir+"/socket_" + str(port))
502         self.vapi.punt_socket_register(port, self.tempdir+"/socket_" +
503                                        str(port), is_ip4=0)
504         punts = self.vapi.punt_socket_dump(is_ip6=1)
505         self.assertEqual(len(punts), 1)
506
507         self.logger.debug("Sending %s packets to port %d",
508                           str(self.portsCheck[port]), port)
509         #
510         # expect punt socket and no packets on pg0
511         #
512         self.vapi.cli("clear errors")
513         self.vapi.cli("clear trace")
514         self.pg0.add_stream(pkts)
515         self.pg_enable_capture(self.pg_interfaces)
516         self.pg_start()
517         self.pg0.get_capture(0)
518         self.logger.info(self.vapi.cli("show trace"))
519         self.socket_client_close()
520         self.assertEqual(self.portsCheck[port], 0)
521
522         #
523         # remove punt socket. expect ICMP - dest. unreachable for all packets
524         #
525         self.vapi.punt_socket_deregister(port, is_ip4=0)
526         punts = self.vapi.punt_socket_dump(is_ip6=1)
527         self.assertEqual(len(punts), 0)
528         self.pg0.add_stream(pkts)
529         self.pg_enable_capture(self.pg_interfaces)
530         self.pg_start()
531         # FIXME - when punt socket deregister is implemented
532         # self.pg0.get_capture(nr_packets)
533
534     def test_punt_socket_traffic_multi_port_multi_sockets(self):
535         """ Punt socket traffic multi ports and multi sockets"""
536
537         for p in self.ports:
538             self.portsCheck[p] = 0
539
540         #
541         # create stream with random pakets count per given ports
542         #
543         pkts = list()
544         for _ in range(0, self.nr_packets):
545             # choose port from port list
546             p = random.choice(self.ports)
547             pkts.append((
548                 Ether(src=self.pg0.remote_mac,
549                       dst=self.pg0.local_mac) /
550                 IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6) /
551                 inet6.UDP(sport=9876, dport=p) /
552                 Raw('\xa5' * 100)))
553             self.portsCheck[p] += 1
554         #
555         # no punt socket
556         #
557         punts = self.vapi.punt_socket_dump(is_ip6=1)
558         self.assertEqual(len(punts), 0)
559
560         #
561         # configure a punt socket
562         #
563         for p in self.ports:
564             self.socket_client_create(self.tempdir+"/socket_" + str(p))
565             self.vapi.punt_socket_register(p, self.tempdir+"/socket_" + str(p),
566                                            is_ip4=0)
567         punts = self.vapi.punt_socket_dump(is_ip6=1)
568         self.assertEqual(len(punts), len(self.ports))
569
570         for p in self.ports:
571             self.logger.debug("Sending %s packets to port %d",
572                               str(self.portsCheck[p]), p)
573
574         #
575         # expect punt socket and no packets on pg0
576         #
577         self.vapi.cli("clear errors")
578         self.vapi.cli("clear trace")
579         self.pg0.add_stream(pkts)
580         self.pg_enable_capture(self.pg_interfaces)
581         self.pg_start()
582         self.pg0.get_capture(0)
583         self.logger.info(self.vapi.cli("show trace"))
584         self.socket_client_close()
585
586         for p in self.ports:
587             self.assertEqual(self.portsCheck[p], 0)
588             self.vapi.punt_socket_deregister(p, is_ip4=0)
589         punts = self.vapi.punt_socket_dump(is_ip6=1)
590         self.assertEqual(len(punts), 0)
591
592     def test_punt_socket_traffic_multi_ports_single_socket(self):
593         """ Punt socket traffic multi ports and single socket"""
594
595         for p in self.ports:
596             self.portsCheck[p] = 0
597
598         #
599         # create stream with random pakets count per given ports
600         #
601         pkts = list()
602         for _ in range(0, self.nr_packets):
603             # choose port from port list
604             p = random.choice(self.ports)
605             pkts.append((
606                 Ether(src=self.pg0.remote_mac,
607                       dst=self.pg0.local_mac) /
608                 IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6) /
609                 inet6.UDP(sport=9876, dport=p) /
610                 Raw('\xa5' * 100)))
611             self.portsCheck[p] += 1
612
613         #
614         # no punt socket
615         #
616         punts = self.vapi.punt_socket_dump(is_ip6=1)
617         self.assertEqual(len(punts), 0)
618
619         #
620         # configure a punt socket
621         #
622         self.socket_client_create(self.tempdir+"/socket_multi")
623         for p in self.ports:
624             self.vapi.punt_socket_register(p, self.tempdir+"/socket_multi",
625                                            is_ip4=0)
626         punts = self.vapi.punt_socket_dump(is_ip6=1)
627         self.assertEqual(len(punts), len(self.ports))
628
629         for p in self.ports:
630             self.logger.debug("Send %s packets to port %d",
631                               str(self.portsCheck[p]), p)
632         #
633         # expect punt socket and no packets on pg0
634         #
635         self.vapi.cli("clear errors")
636         self.vapi.cli("clear trace")
637         self.pg0.add_stream(pkts)
638         self.pg_enable_capture(self.pg_interfaces)
639         self.pg_start()
640         self.pg0.get_capture(0)
641         self.logger.info(self.vapi.cli("show trace"))
642         self.socket_client_close()
643
644         for p in self.ports:
645             self.assertEqual(self.portsCheck[p], 0)
646             self.vapi.punt_socket_deregister(p, is_ip4=0)
647         punts = self.vapi.punt_socket_dump(is_ip6=1)
648         self.assertEqual(len(punts), 0)
649
650 if __name__ == '__main__':
651     unittest.main(testRunner=VppTestRunner)