Revert "tests: Rework vpp config generation."
[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 setUpConstants(cls):
124         cls.extra_vpp_punt_config = [
125             "punt", "{", "socket", cls.tempdir+"/socket_punt", "}"]
126         super(TestPuntSocket, cls).setUpConstants()
127
128     def setUp(self):
129         super(TestPuntSocket, self).setUp()
130         random.seed()
131
132         self.create_pg_interfaces(range(2))
133         for i in self.pg_interfaces:
134             i.admin_up()
135
136     def tearDown(self):
137         del self.sock_servers[:]
138
139     def socket_client_create(self, sock_name, id=None):
140         thread = serverSocketThread(id, sock_name, self.portsCheck)
141         self.sock_servers.append(thread)
142         thread.start()
143
144     def socket_client_close(self):
145         for thread in self.sock_servers:
146             thread.sock.close()
147
148
149 class TestIP4PuntSocket(TestPuntSocket):
150     """ Punt Socket for IPv4 """
151
152     def setUp(self):
153         super(TestIP4PuntSocket, self).setUp()
154
155         for i in self.pg_interfaces:
156             i.config_ip4()
157             i.resolve_arp()
158
159     def tearDown(self):
160         super(TestIP4PuntSocket, self).tearDown()
161         for i in self.pg_interfaces:
162             i.unconfig_ip4()
163             i.admin_down()
164
165     def test_punt_socket_dump(self):
166         """ Punt socket registration/deregistration"""
167
168         punts = self.vapi.punt_socket_dump(is_ip6=0)
169         self.assertEqual(len(punts), 0)
170
171         #
172         # configure a punt socket
173         #
174         self.vapi.punt_socket_register(1111, self.tempdir+"/socket_punt_1111")
175         self.vapi.punt_socket_register(2222, self.tempdir+"/socket_punt_2222")
176         punts = self.vapi.punt_socket_dump(is_ip6=0)
177         self.assertEqual(len(punts), 2)
178         self.assertEqual(punts[0].punt.l4_port, 1111)
179         self.assertEqual(punts[1].punt.l4_port, 2222)
180
181         #
182         # deregister a punt socket
183         #
184         self.vapi.punt_socket_deregister(1111)
185         punts = self.vapi.punt_socket_dump(is_ip6=0)
186         self.assertEqual(len(punts), 1)
187
188         #
189         # configure a punt socket again
190         #
191         self.vapi.punt_socket_register(1111, self.tempdir+"/socket_punt_1111")
192         self.vapi.punt_socket_register(3333, self.tempdir+"/socket_punt_3333")
193         punts = self.vapi.punt_socket_dump(is_ip6=0)
194         self.assertEqual(len(punts), 3)
195
196         #
197         # deregister all punt socket
198         #
199         self.vapi.punt_socket_deregister(1111)
200         self.vapi.punt_socket_deregister(2222)
201         self.vapi.punt_socket_deregister(3333)
202         punts = self.vapi.punt_socket_dump(is_ip6=0)
203         self.assertEqual(len(punts), 0)
204
205     def test_punt_socket_traffic_single_port_single_socket(self):
206         """ Punt socket traffic single port single socket"""
207
208         port = self.ports[0]
209
210         p = (Ether(src=self.pg0.remote_mac,
211                    dst=self.pg0.local_mac) /
212              IP(src=self.pg0.remote_ip4, dst=self.pg0.local_ip4) /
213              UDP(sport=9876, dport=port) /
214              Raw('\xa5' * 100))
215
216         pkts = p * self.nr_packets
217         self.portsCheck[port] = self.nr_packets
218
219         punts = self.vapi.punt_socket_dump(is_ip6=0)
220         self.assertEqual(len(punts), 0)
221
222         #
223         # expect ICMP - port unreachable for all packets
224         #
225         self.vapi.cli("clear trace")
226         self.pg0.add_stream(pkts)
227         self.pg_enable_capture(self.pg_interfaces)
228         self.pg_start()
229         # FIXME - when punt socket deregister is implemented
230         # rx = self.pg0.get_capture(self.nr_packets)
231         # for p in rx:
232         #     self.assertEqual(int(p[IP].proto), 1)   # ICMP
233         #     self.assertEqual(int(p[ICMP].code), 3)  # unreachable
234
235         #
236         # configure a punt socket
237         #
238         self.socket_client_create(self.tempdir+"/socket_" + str(port))
239         self.vapi.punt_socket_register(port, self.tempdir+"/socket_" +
240                                        str(port))
241         punts = self.vapi.punt_socket_dump(is_ip6=0)
242         self.assertEqual(len(punts), 1)
243
244         self.logger.debug("Sending %s packets to port %d",
245                           str(self.portsCheck[port]), port)
246         #
247         # expect punt socket and no packets on pg0
248         #
249         self.vapi.cli("clear errors")
250         self.vapi.cli("clear trace")
251         self.pg0.add_stream(pkts)
252         self.pg_enable_capture(self.pg_interfaces)
253         self.pg_start()
254         self.pg0.get_capture(0)
255         self.logger.info(self.vapi.cli("show trace"))
256         self.socket_client_close()
257         self.assertEqual(self.portsCheck[port], 0)
258
259         #
260         # remove punt socket. expect ICMP - port unreachable for all packets
261         #
262         self.vapi.punt_socket_deregister(port)
263         punts = self.vapi.punt_socket_dump(is_ip6=0)
264         self.assertEqual(len(punts), 0)
265         self.pg0.add_stream(pkts)
266         self.pg_enable_capture(self.pg_interfaces)
267         self.pg_start()
268         # FIXME - when punt socket deregister is implemented
269         # self.pg0.get_capture(nr_packets)
270
271     def test_punt_socket_traffic_multi_port_multi_sockets(self):
272         """ Punt socket traffic multi ports and multi sockets"""
273
274         for p in self.ports:
275             self.portsCheck[p] = 0
276
277         #
278         # create stream with random pakets count per given ports
279         #
280         pkts = list()
281         for _ in range(0, self.nr_packets):
282             # choose port from port list
283             p = random.choice(self.ports)
284             pkts.append((
285                 Ether(src=self.pg0.remote_mac,
286                       dst=self.pg0.local_mac) /
287                 IP(src=self.pg0.remote_ip4, dst=self.pg0.local_ip4) /
288                 UDP(sport=9876, dport=p) /
289                 Raw('\xa5' * 100)))
290             self.portsCheck[p] += 1
291         #
292         # no punt socket
293         #
294         punts = self.vapi.punt_socket_dump(is_ip6=0)
295         self.assertEqual(len(punts), 0)
296
297         #
298         # configure a punt socket
299         #
300         for p in self.ports:
301             self.socket_client_create(self.tempdir+"/socket_" + str(p))
302             self.vapi.punt_socket_register(p, self.tempdir+"/socket_" + str(p))
303         punts = self.vapi.punt_socket_dump(is_ip6=0)
304         self.assertEqual(len(punts), len(self.ports))
305
306         for p in self.ports:
307             self.logger.debug("Sending %s packets to port %d",
308                               str(self.portsCheck[p]), p)
309
310         #
311         # expect punt socket and no packets on pg0
312         #
313         self.vapi.cli("clear errors")
314         self.vapi.cli("clear trace")
315         self.pg0.add_stream(pkts)
316         self.pg_enable_capture(self.pg_interfaces)
317         self.pg_start()
318         self.pg0.get_capture(0)
319         self.logger.info(self.vapi.cli("show trace"))
320         self.socket_client_close()
321
322         for p in self.ports:
323             self.assertEqual(self.portsCheck[p], 0)
324             self.vapi.punt_socket_deregister(p)
325         punts = self.vapi.punt_socket_dump(is_ip6=0)
326         self.assertEqual(len(punts), 0)
327
328     def test_punt_socket_traffic_multi_ports_single_socket(self):
329         """ Punt socket traffic multi ports and single socket"""
330
331         for p in self.ports:
332             self.portsCheck[p] = 0
333
334         #
335         # create stream with random pakets count per given ports
336         #
337         pkts = list()
338         for _ in range(0, self.nr_packets):
339             # choose port from port list
340             p = random.choice(self.ports)
341             pkts.append((
342                 Ether(src=self.pg0.remote_mac,
343                       dst=self.pg0.local_mac) /
344                 IP(src=self.pg0.remote_ip4, dst=self.pg0.local_ip4) /
345                 UDP(sport=9876, dport=p) /
346                 Raw('\xa5' * 100)))
347             self.portsCheck[p] += 1
348
349         #
350         # no punt socket
351         #
352         punts = self.vapi.punt_socket_dump(is_ip6=0)
353         self.assertEqual(len(punts), 0)
354
355         # configure a punt socket
356         #
357         self.socket_client_create(self.tempdir+"/socket_multi")
358         for p in self.ports:
359             self.vapi.punt_socket_register(p, self.tempdir+"/socket_multi")
360         punts = self.vapi.punt_socket_dump(is_ip6=0)
361         self.assertEqual(len(punts), len(self.ports))
362
363         for p in self.ports:
364             self.logger.debug("Sending %s packets to port %d",
365                               str(self.portsCheck[p]), p)
366         #
367         # expect punt socket and no packets on pg0
368         #
369         self.vapi.cli("clear errors")
370         self.vapi.cli("clear trace")
371         self.pg0.add_stream(pkts)
372         self.pg_enable_capture(self.pg_interfaces)
373         self.pg_start()
374         self.pg0.get_capture(0)
375         self.logger.info(self.vapi.cli("show trace"))
376         self.socket_client_close()
377
378         for p in self.ports:
379             self.assertEqual(self.portsCheck[p], 0)
380             self.vapi.punt_socket_deregister(p)
381         punts = self.vapi.punt_socket_dump(is_ip6=0)
382         self.assertEqual(len(punts), 0)
383
384
385 class TestIP6PuntSocket(TestPuntSocket):
386     """ Punt Socket for IPv6"""
387
388     def setUp(self):
389         super(TestIP6PuntSocket, self).setUp()
390
391         for i in self.pg_interfaces:
392             i.config_ip6()
393             i.resolve_ndp()
394
395     def tearDown(self):
396         super(TestIP6PuntSocket, self).tearDown()
397         for i in self.pg_interfaces:
398             i.unconfig_ip6()
399             i.admin_down()
400
401     def test_punt_socket_dump(self):
402         """ Punt socket registration """
403
404         punts = self.vapi.punt_socket_dump(is_ip6=1)
405         self.assertEqual(len(punts), 0)
406
407         #
408         # configure a punt socket
409         #
410         self.vapi.punt_socket_register(1111, self.tempdir+"/socket_1111",
411                                        is_ip4=0)
412         self.vapi.punt_socket_register(2222, self.tempdir+"/socket_2222",
413                                        is_ip4=0)
414         punts = self.vapi.punt_socket_dump(is_ip6=1)
415         self.assertEqual(len(punts), 2)
416         self.assertEqual(punts[0].punt.l4_port, 1111)
417         self.assertEqual(punts[1].punt.l4_port, 2222)
418
419         #
420         # deregister a punt socket
421         #
422         self.vapi.punt_socket_deregister(1111, is_ip4=0)
423         punts = self.vapi.punt_socket_dump(is_ip6=1)
424         self.assertEqual(len(punts), 1)
425
426         #
427         # configure a punt socket again
428         #
429         self.vapi.punt_socket_register(1111, self.tempdir+"/socket_1111",
430                                        is_ip4=0)
431         punts = self.vapi.punt_socket_dump(is_ip6=1)
432         self.assertEqual(len(punts), 2)
433
434         #
435         # deregister all punt socket
436         #
437         self.vapi.punt_socket_deregister(1111, is_ip4=0)
438         self.vapi.punt_socket_deregister(2222, is_ip4=0)
439         self.vapi.punt_socket_deregister(3333, is_ip4=0)
440         punts = self.vapi.punt_socket_dump(is_ip6=1)
441         self.assertEqual(len(punts), 0)
442
443     def test_punt_socket_traffic_single_port_single_socket(self):
444         """ Punt socket traffic single port single socket"""
445
446         port = self.ports[0]
447
448         p = (Ether(src=self.pg0.remote_mac,
449                    dst=self.pg0.local_mac) /
450              IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6) /
451              inet6.UDP(sport=9876, dport=port) /
452              Raw('\xa5' * 100))
453
454         pkts = p * self.nr_packets
455         self.portsCheck[port] = self.nr_packets
456
457         punts = self.vapi.punt_socket_dump(is_ip6=1)
458         self.assertEqual(len(punts), 0)
459
460         #
461         # expect ICMPv6 - destination unreachable for all packets
462         #
463         self.vapi.cli("clear trace")
464         self.pg0.add_stream(pkts)
465         self.pg_enable_capture(self.pg_interfaces)
466         self.pg_start()
467         # FIXME - when punt socket deregister is implemented
468         # rx = self.pg0.get_capture(self.nr_packets)
469         # for p in rx:
470         #     self.assertEqual(int(p[IPv6].nh), 58)                # ICMPv6
471         #     self.assertEqual(int(p[ICMPv6DestUnreach].code),4)  # unreachable
472
473         #
474         # configure a punt socket
475         #
476         self.socket_client_create(self.tempdir+"/socket_" + str(port))
477         self.vapi.punt_socket_register(port, self.tempdir+"/socket_" +
478                                        str(port), is_ip4=0)
479         punts = self.vapi.punt_socket_dump(is_ip6=1)
480         self.assertEqual(len(punts), 1)
481
482         self.logger.debug("Sending %s packets to port %d",
483                           str(self.portsCheck[port]), port)
484         #
485         # expect punt socket and no packets on pg0
486         #
487         self.vapi.cli("clear errors")
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         self.pg0.get_capture(0)
493         self.logger.info(self.vapi.cli("show trace"))
494         self.socket_client_close()
495         self.assertEqual(self.portsCheck[port], 0)
496
497         #
498         # remove punt socket. expect ICMP - dest. unreachable for all packets
499         #
500         self.vapi.punt_socket_deregister(port, is_ip4=0)
501         punts = self.vapi.punt_socket_dump(is_ip6=1)
502         self.assertEqual(len(punts), 0)
503         self.pg0.add_stream(pkts)
504         self.pg_enable_capture(self.pg_interfaces)
505         self.pg_start()
506         # FIXME - when punt socket deregister is implemented
507         # self.pg0.get_capture(nr_packets)
508
509     def test_punt_socket_traffic_multi_port_multi_sockets(self):
510         """ Punt socket traffic multi ports and multi sockets"""
511
512         for p in self.ports:
513             self.portsCheck[p] = 0
514
515         #
516         # create stream with random pakets count per given ports
517         #
518         pkts = list()
519         for _ in range(0, self.nr_packets):
520             # choose port from port list
521             p = random.choice(self.ports)
522             pkts.append((
523                 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=p) /
527                 Raw('\xa5' * 100)))
528             self.portsCheck[p] += 1
529         #
530         # no punt socket
531         #
532         punts = self.vapi.punt_socket_dump(is_ip6=1)
533         self.assertEqual(len(punts), 0)
534
535         #
536         # configure a punt socket
537         #
538         for p in self.ports:
539             self.socket_client_create(self.tempdir+"/socket_" + str(p))
540             self.vapi.punt_socket_register(p, self.tempdir+"/socket_" + str(p),
541                                            is_ip4=0)
542         punts = self.vapi.punt_socket_dump(is_ip6=1)
543         self.assertEqual(len(punts), len(self.ports))
544
545         for p in self.ports:
546             self.logger.debug("Sending %s packets to port %d",
547                               str(self.portsCheck[p]), p)
548
549         #
550         # expect punt socket and no packets on pg0
551         #
552         self.vapi.cli("clear errors")
553         self.vapi.cli("clear trace")
554         self.pg0.add_stream(pkts)
555         self.pg_enable_capture(self.pg_interfaces)
556         self.pg_start()
557         self.pg0.get_capture(0)
558         self.logger.info(self.vapi.cli("show trace"))
559         self.socket_client_close()
560
561         for p in self.ports:
562             self.assertEqual(self.portsCheck[p], 0)
563             self.vapi.punt_socket_deregister(p, is_ip4=0)
564         punts = self.vapi.punt_socket_dump(is_ip6=1)
565         self.assertEqual(len(punts), 0)
566
567     def test_punt_socket_traffic_multi_ports_single_socket(self):
568         """ Punt socket traffic multi ports and single socket"""
569
570         for p in self.ports:
571             self.portsCheck[p] = 0
572
573         #
574         # create stream with random pakets count per given ports
575         #
576         pkts = list()
577         for _ in range(0, self.nr_packets):
578             # choose port from port list
579             p = random.choice(self.ports)
580             pkts.append((
581                 Ether(src=self.pg0.remote_mac,
582                       dst=self.pg0.local_mac) /
583                 IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6) /
584                 inet6.UDP(sport=9876, dport=p) /
585                 Raw('\xa5' * 100)))
586             self.portsCheck[p] += 1
587
588         #
589         # no punt socket
590         #
591         punts = self.vapi.punt_socket_dump(is_ip6=1)
592         self.assertEqual(len(punts), 0)
593
594         #
595         # configure a punt socket
596         #
597         self.socket_client_create(self.tempdir+"/socket_multi")
598         for p in self.ports:
599             self.vapi.punt_socket_register(p, self.tempdir+"/socket_multi",
600                                            is_ip4=0)
601         punts = self.vapi.punt_socket_dump(is_ip6=1)
602         self.assertEqual(len(punts), len(self.ports))
603
604         for p in self.ports:
605             self.logger.debug("Send %s packets to port %d",
606                               str(self.portsCheck[p]), p)
607         #
608         # expect punt socket and no packets on pg0
609         #
610         self.vapi.cli("clear errors")
611         self.vapi.cli("clear trace")
612         self.pg0.add_stream(pkts)
613         self.pg_enable_capture(self.pg_interfaces)
614         self.pg_start()
615         self.pg0.get_capture(0)
616         self.logger.info(self.vapi.cli("show trace"))
617         self.socket_client_close()
618
619         for p in self.ports:
620             self.assertEqual(self.portsCheck[p], 0)
621             self.vapi.punt_socket_deregister(p, is_ip4=0)
622         punts = self.vapi.punt_socket_dump(is_ip6=1)
623         self.assertEqual(len(punts), 0)
624
625 if __name__ == '__main__':
626     unittest.main(testRunner=VppTestRunner)