make test: improve robustness and performance
[vpp.git] / test / test_gre.py
1 #!/usr/bin/env python
2
3 import unittest
4 from logging import *
5
6 from framework import VppTestCase, VppTestRunner
7 from vpp_sub_interface import VppDot1QSubint
8 from vpp_gre_interface import VppGreInterface
9 from vpp_ip_route import IpRoute, RoutePath
10 from vpp_papi_provider import L2_VTR_OP
11
12 from scapy.packet import Raw
13 from scapy.layers.l2 import Ether, Dot1Q, GRE
14 from scapy.layers.inet import IP, UDP
15 from scapy.layers.inet6 import ICMPv6ND_RA, IPv6
16 from scapy.volatile import RandMAC, RandIP
17
18 from util import ppp, ppc
19
20
21 class TestGRE(VppTestCase):
22     """ GRE Test Case """
23
24     @classmethod
25     def setUpClass(cls):
26         super(TestGRE, cls).setUpClass()
27
28     def setUp(self):
29         super(TestGRE, self).setUp()
30
31         # create 2 pg interfaces - set one in a non-default table.
32         self.create_pg_interfaces(range(2))
33
34         self.pg1.set_table_ip4(1)
35         for i in self.pg_interfaces:
36             i.admin_up()
37             i.config_ip4()
38             i.resolve_arp()
39
40     def tearDown(self):
41         super(TestGRE, self).tearDown()
42
43     def create_stream_ip4(self, src_if, src_ip, dst_ip):
44         pkts = []
45         for i in range(0, 257):
46             info = self.create_packet_info(src_if.sw_if_index,
47                                            src_if.sw_if_index)
48             payload = self.info_to_payload(info)
49             p = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) /
50                  IP(src=src_ip, dst=dst_ip) /
51                  UDP(sport=1234, dport=1234) /
52                  Raw(payload))
53             info.data = p.copy()
54             pkts.append(p)
55         return pkts
56
57     def create_tunnel_stream_4o4(self, src_if,
58                                  tunnel_src, tunnel_dst,
59                                  src_ip, dst_ip):
60         pkts = []
61         for i in range(0, 257):
62             info = self.create_packet_info(src_if.sw_if_index,
63                                            src_if.sw_if_index)
64             payload = self.info_to_payload(info)
65             p = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) /
66                  IP(src=tunnel_src, dst=tunnel_dst) /
67                  GRE() /
68                  IP(src=src_ip, dst=dst_ip) /
69                  UDP(sport=1234, dport=1234) /
70                  Raw(payload))
71             info.data = p.copy()
72             pkts.append(p)
73         return pkts
74
75     def create_tunnel_stream_6o4(self, src_if,
76                                  tunnel_src, tunnel_dst,
77                                  src_ip, dst_ip):
78         pkts = []
79         for i in range(0, 257):
80             info = self.create_packet_info(src_if.sw_if_index,
81                                            src_if.sw_if_index)
82             payload = self.info_to_payload(info)
83             p = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) /
84                  IP(src=tunnel_src, dst=tunnel_dst) /
85                  GRE() /
86                  IPv6(src=src_ip, dst=dst_ip) /
87                  UDP(sport=1234, dport=1234) /
88                  Raw(payload))
89             info.data = p.copy()
90             pkts.append(p)
91         return pkts
92
93     def create_tunnel_stream_l2o4(self, src_if,
94                                   tunnel_src, tunnel_dst):
95         pkts = []
96         for i in range(0, 257):
97             info = self.create_packet_info(src_if.sw_if_index,
98                                            src_if.sw_if_index)
99             payload = self.info_to_payload(info)
100             p = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) /
101                  IP(src=tunnel_src, dst=tunnel_dst) /
102                  GRE() /
103                  Ether(dst=RandMAC('*:*:*:*:*:*'),
104                        src=RandMAC('*:*:*:*:*:*')) /
105                  IP(src=str(RandIP()), dst=str(RandIP())) /
106                  UDP(sport=1234, dport=1234) /
107                  Raw(payload))
108             info.data = p.copy()
109             pkts.append(p)
110         return pkts
111
112     def create_tunnel_stream_vlano4(self, src_if,
113                                     tunnel_src, tunnel_dst, vlan):
114         pkts = []
115         for i in range(0, 257):
116             info = self.create_packet_info(src_if.sw_if_index,
117                                            src_if.sw_if_index)
118             payload = self.info_to_payload(info)
119             p = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) /
120                  IP(src=tunnel_src, dst=tunnel_dst) /
121                  GRE() /
122                  Ether(dst=RandMAC('*:*:*:*:*:*'),
123                        src=RandMAC('*:*:*:*:*:*')) /
124                  Dot1Q(vlan=vlan) /
125                  IP(src=str(RandIP()), dst=str(RandIP())) /
126                  UDP(sport=1234, dport=1234) /
127                  Raw(payload))
128             info.data = p.copy()
129             pkts.append(p)
130         return pkts
131
132     def verify_filter(self, capture, sent):
133         if not len(capture) == len(sent):
134             # filter out any IPv6 RAs from the capture
135             for p in capture:
136                 if (p.haslayer(ICMPv6ND_RA)):
137                     capture.remove(p)
138         return capture
139
140     def verify_tunneled_4o4(self, src_if, capture, sent,
141                             tunnel_src, tunnel_dst):
142
143         capture = self.verify_filter(capture, sent)
144         self.assertEqual(len(capture), len(sent))
145
146         for i in range(len(capture)):
147             try:
148                 tx = sent[i]
149                 rx = capture[i]
150
151                 tx_ip = tx[IP]
152                 rx_ip = rx[IP]
153
154                 self.assertEqual(rx_ip.src, tunnel_src)
155                 self.assertEqual(rx_ip.dst, tunnel_dst)
156
157                 rx_gre = rx[GRE]
158                 rx_ip = rx_gre[IP]
159
160                 self.assertEqual(rx_ip.src, tx_ip.src)
161                 self.assertEqual(rx_ip.dst, tx_ip.dst)
162                 # IP processing post pop has decremented the TTL
163                 self.assertEqual(rx_ip.ttl + 1, tx_ip.ttl)
164
165             except:
166                 self.logger.error(ppp("Rx:", rx))
167                 self.logger.error(ppp("Tx:", tx))
168                 raise
169
170     def verify_tunneled_l2o4(self, src_if, capture, sent,
171                              tunnel_src, tunnel_dst):
172         capture = self.verify_filter(capture, sent)
173         self.assertEqual(len(capture), len(sent))
174
175         for i in range(len(capture)):
176             try:
177                 tx = sent[i]
178                 rx = capture[i]
179
180                 tx_ip = tx[IP]
181                 rx_ip = rx[IP]
182
183                 self.assertEqual(rx_ip.src, tunnel_src)
184                 self.assertEqual(rx_ip.dst, tunnel_dst)
185
186                 rx_gre = rx[GRE]
187                 rx_l2 = rx_gre[Ether]
188                 rx_ip = rx_l2[IP]
189                 tx_gre = tx[GRE]
190                 tx_l2 = tx_gre[Ether]
191                 tx_ip = tx_l2[IP]
192
193                 self.assertEqual(rx_ip.src, tx_ip.src)
194                 self.assertEqual(rx_ip.dst, tx_ip.dst)
195                 # bridged, not L3 forwarded, so no TTL decrement
196                 self.assertEqual(rx_ip.ttl, tx_ip.ttl)
197
198             except:
199                 self.logger.error(ppp("Rx:", rx))
200                 self.logger.error(ppp("Tx:", tx))
201                 raise
202
203     def verify_tunneled_vlano4(self, src_if, capture, sent,
204                                tunnel_src, tunnel_dst, vlan):
205         try:
206             capture = self.verify_filter(capture, sent)
207             self.assertEqual(len(capture), len(sent))
208         except:
209             ppc("Unexpected packets captured:", capture)
210             raise
211
212         for i in range(len(capture)):
213             try:
214                 tx = sent[i]
215                 rx = capture[i]
216
217                 tx_ip = tx[IP]
218                 rx_ip = rx[IP]
219
220                 self.assertEqual(rx_ip.src, tunnel_src)
221                 self.assertEqual(rx_ip.dst, tunnel_dst)
222
223                 rx_gre = rx[GRE]
224                 rx_l2 = rx_gre[Ether]
225                 rx_vlan = rx_l2[Dot1Q]
226                 rx_ip = rx_l2[IP]
227
228                 self.assertEqual(rx_vlan.vlan, vlan)
229
230                 tx_gre = tx[GRE]
231                 tx_l2 = tx_gre[Ether]
232                 tx_ip = tx_l2[IP]
233
234                 self.assertEqual(rx_ip.src, tx_ip.src)
235                 self.assertEqual(rx_ip.dst, tx_ip.dst)
236                 # bridged, not L3 forwarded, so no TTL decrement
237                 self.assertEqual(rx_ip.ttl, tx_ip.ttl)
238
239             except:
240                 self.logger.error(ppp("Rx:", rx))
241                 self.logger.error(ppp("Tx:", tx))
242                 raise
243
244     def verify_decapped_4o4(self, src_if, capture, sent):
245         capture = self.verify_filter(capture, sent)
246         self.assertEqual(len(capture), len(sent))
247
248         for i in range(len(capture)):
249             try:
250                 tx = sent[i]
251                 rx = capture[i]
252
253                 tx_ip = tx[IP]
254                 rx_ip = rx[IP]
255                 tx_gre = tx[GRE]
256                 tx_ip = tx_gre[IP]
257
258                 self.assertEqual(rx_ip.src, tx_ip.src)
259                 self.assertEqual(rx_ip.dst, tx_ip.dst)
260                 # IP processing post pop has decremented the TTL
261                 self.assertEqual(rx_ip.ttl + 1, tx_ip.ttl)
262
263             except:
264                 self.logger.error(ppp("Rx:", rx))
265                 self.logger.error(ppp("Tx:", tx))
266                 raise
267
268     def verify_decapped_6o4(self, src_if, capture, sent):
269         capture = self.verify_filter(capture, sent)
270         self.assertEqual(len(capture), len(sent))
271
272         for i in range(len(capture)):
273             try:
274                 tx = sent[i]
275                 rx = capture[i]
276
277                 tx_ip = tx[IP]
278                 rx_ip = rx[IPv6]
279                 tx_gre = tx[GRE]
280                 tx_ip = tx_gre[IPv6]
281
282                 self.assertEqual(rx_ip.src, tx_ip.src)
283                 self.assertEqual(rx_ip.dst, tx_ip.dst)
284                 self.assertEqual(rx_ip.hlim + 1, tx_ip.hlim)
285
286             except:
287                 self.logger.error(ppp("Rx:", rx))
288                 self.logger.error(ppp("Tx:", tx))
289                 raise
290
291     def test_gre(self):
292         """ GRE tunnel Tests """
293
294         #
295         # Create an L3 GRE tunnel.
296         #  - set it admin up
297         #  - assign an IP Addres
298         #  - Add a route via the tunnel
299         #
300         gre_if = VppGreInterface(self,
301                                  self.pg0.local_ip4,
302                                  "1.1.1.2")
303         gre_if.add_vpp_config()
304
305         #
306         # The double create (create the same tunnel twice) should fail,
307         # and we should still be able to use the original
308         #
309         try:
310             gre_if.add_vpp_config()
311         except Exception:
312             pass
313         else:
314             self.fail("Double GRE tunnel add does not fail")
315
316         gre_if.admin_up()
317         gre_if.config_ip4()
318
319         route_via_tun = IpRoute(self, "4.4.4.4", 32,
320                                 [RoutePath("0.0.0.0", gre_if.sw_if_index)])
321
322         route_via_tun.add_vpp_config()
323
324         #
325         # Send a packet stream that is routed into the tunnel
326         #  - they are all dropped since the tunnel's desintation IP
327         #    is unresolved - or resolves via the default route - which
328         #    which is a drop.
329         #
330         tx = self.create_stream_ip4(self.pg0, "5.5.5.5", "4.4.4.4")
331         self.pg0.add_stream(tx)
332
333         self.pg_enable_capture(self.pg_interfaces)
334         self.pg_start()
335
336         self.pg0.assert_nothing_captured(
337             remark="GRE packets forwarded without DIP resolved")
338
339         #
340         # Add a route that resolves the tunnel's destination
341         #
342         route_tun_dst = IpRoute(self, "1.1.1.2", 32,
343                                 [RoutePath(self.pg0.remote_ip4,
344                                            self.pg0.sw_if_index)])
345         route_tun_dst.add_vpp_config()
346
347         #
348         # Send a packet stream that is routed into the tunnel
349         #  - packets are GRE encapped
350         #
351         self.vapi.cli("clear trace")
352         tx = self.create_stream_ip4(self.pg0, "5.5.5.5", "4.4.4.4")
353         self.pg0.add_stream(tx)
354
355         self.pg_enable_capture(self.pg_interfaces)
356         self.pg_start()
357
358         rx = self.pg0.get_capture()
359         self.verify_tunneled_4o4(self.pg0, rx, tx,
360                                  self.pg0.local_ip4, "1.1.1.2")
361
362         #
363         # Send tunneled packets that match the created tunnel and
364         # are decapped and forwarded
365         #
366         self.vapi.cli("clear trace")
367         tx = self.create_tunnel_stream_4o4(self.pg0,
368                                            "1.1.1.2",
369                                            self.pg0.local_ip4,
370                                            self.pg0.local_ip4,
371                                            self.pg0.remote_ip4)
372         self.pg0.add_stream(tx)
373
374         self.pg_enable_capture(self.pg_interfaces)
375         self.pg_start()
376
377         rx = self.pg0.get_capture()
378         self.verify_decapped_4o4(self.pg0, rx, tx)
379
380         #
381         # Send tunneled packets that do not match the tunnel's src
382         #
383         self.vapi.cli("clear trace")
384         tx = self.create_tunnel_stream_4o4(self.pg0,
385                                            "1.1.1.3",
386                                            self.pg0.local_ip4,
387                                            self.pg0.local_ip4,
388                                            self.pg0.remote_ip4)
389         self.pg0.add_stream(tx)
390
391         self.pg_enable_capture(self.pg_interfaces)
392         self.pg_start()
393
394         self.pg0.assert_nothing_captured(
395             remark="GRE packets forwarded despite no SRC address match")
396
397         #
398         # Configure IPv6 on the PG interface so we can route IPv6
399         # packets
400         #
401         self.pg0.config_ip6()
402         self.pg0.resolve_ndp()
403
404         #
405         # Send IPv6 tunnel encapslated packets
406         #  - dropped since IPv6 is not enabled on the tunnel
407         #
408         self.vapi.cli("clear trace")
409         tx = self.create_tunnel_stream_6o4(self.pg0,
410                                            "1.1.1.2",
411                                            self.pg0.local_ip4,
412                                            self.pg0.local_ip6,
413                                            self.pg0.remote_ip6)
414         self.pg0.add_stream(tx)
415
416         self.pg_enable_capture(self.pg_interfaces)
417         self.pg_start()
418
419         self.pg0.assert_nothing_captured(remark="IPv6 GRE packets forwarded "
420                                          "despite IPv6 not enabled on tunnel")
421
422         #
423         # Enable IPv6 on the tunnel
424         #
425         gre_if.config_ip6()
426
427         #
428         # Send IPv6 tunnel encapslated packets
429         #  - forwarded since IPv6 is enabled on the tunnel
430         #
431         self.vapi.cli("clear trace")
432         tx = self.create_tunnel_stream_6o4(self.pg0,
433                                            "1.1.1.2",
434                                            self.pg0.local_ip4,
435                                            self.pg0.local_ip6,
436                                            self.pg0.remote_ip6)
437         self.pg0.add_stream(tx)
438
439         self.pg_enable_capture(self.pg_interfaces)
440         self.pg_start()
441
442         rx = self.pg0.get_capture()
443         self.verify_decapped_6o4(self.pg0, rx, tx)
444
445         #
446         # test case cleanup
447         #
448         route_tun_dst.remove_vpp_config()
449         route_via_tun.remove_vpp_config()
450         gre_if.remove_vpp_config()
451
452         self.pg0.unconfig_ip6()
453
454     def test_gre_vrf(self):
455         """ GRE tunnel VRF Tests """
456
457         #
458         # Create an L3 GRE tunnel whose destination is in the non-default
459         # table. The underlay is thus non-default - the overlay is still
460         # the default.
461         #  - set it admin up
462         #  - assign an IP Addres
463         #
464         gre_if = VppGreInterface(self, self.pg1.local_ip4,
465                                  "2.2.2.2",
466                                  outer_fib_id=1)
467         gre_if.add_vpp_config()
468         gre_if.admin_up()
469         gre_if.config_ip4()
470
471         #
472         # Add a route via the tunnel - in the overlay
473         #
474         route_via_tun = IpRoute(self, "9.9.9.9", 32,
475                                 [RoutePath("0.0.0.0", gre_if.sw_if_index)])
476         route_via_tun.add_vpp_config()
477
478         #
479         # Add a route that resolves the tunnel's destination - in the
480         # underlay table
481         #
482         route_tun_dst = IpRoute(self, "2.2.2.2", 32, table_id=1,
483                                 paths=[RoutePath(self.pg1.remote_ip4,
484                                                  self.pg1.sw_if_index)])
485         route_tun_dst.add_vpp_config()
486
487         #
488         # Send a packet stream that is routed into the tunnel
489         # packets are sent in on pg0 which is in the default table
490         #  - packets are GRE encapped
491         #
492         self.vapi.cli("clear trace")
493         tx = self.create_stream_ip4(self.pg0, "5.5.5.5", "9.9.9.9")
494         self.pg0.add_stream(tx)
495
496         self.pg_enable_capture(self.pg_interfaces)
497         self.pg_start()
498
499         rx = self.pg1.get_capture()
500         self.verify_tunneled_4o4(self.pg1, rx, tx,
501                                  self.pg1.local_ip4, "2.2.2.2")
502
503         #
504         # Send tunneled packets that match the created tunnel and
505         # are decapped and forwarded. This tests the decap lookup
506         # does not happen in the encap table
507         #
508         self.vapi.cli("clear trace")
509         tx = self.create_tunnel_stream_4o4(self.pg1,
510                                            "2.2.2.2",
511                                            self.pg1.local_ip4,
512                                            self.pg0.local_ip4,
513                                            self.pg0.remote_ip4)
514         self.pg1.add_stream(tx)
515
516         self.pg_enable_capture(self.pg_interfaces)
517         self.pg_start()
518
519         rx = self.pg0.get_capture()
520         self.verify_decapped_4o4(self.pg0, rx, tx)
521
522         #
523         # test case cleanup
524         #
525         route_tun_dst.remove_vpp_config()
526         route_via_tun.remove_vpp_config()
527         gre_if.remove_vpp_config()
528
529     def test_gre_l2(self):
530         """ GRE tunnel L2 Tests """
531
532         #
533         # Add routes to resolve the tunnel destinations
534         #
535         route_tun1_dst = IpRoute(self, "2.2.2.2", 32,
536                                  [RoutePath(self.pg0.remote_ip4,
537                                             self.pg0.sw_if_index)])
538         route_tun2_dst = IpRoute(self, "2.2.2.3", 32,
539                                  [RoutePath(self.pg0.remote_ip4,
540                                             self.pg0.sw_if_index)])
541
542         route_tun1_dst.add_vpp_config()
543         route_tun2_dst.add_vpp_config()
544
545         #
546         # Create 2 L2 GRE tunnels and x-connect them
547         #
548         gre_if1 = VppGreInterface(self, self.pg0.local_ip4,
549                                   "2.2.2.2",
550                                   is_teb=1)
551         gre_if2 = VppGreInterface(self, self.pg0.local_ip4,
552                                   "2.2.2.3",
553                                   is_teb=1)
554         gre_if1.add_vpp_config()
555         gre_if2.add_vpp_config()
556
557         gre_if1.admin_up()
558         gre_if2.admin_up()
559
560         self.vapi.sw_interface_set_l2_xconnect(gre_if1.sw_if_index,
561                                                gre_if2.sw_if_index,
562                                                enable=1)
563         self.vapi.sw_interface_set_l2_xconnect(gre_if2.sw_if_index,
564                                                gre_if1.sw_if_index,
565                                                enable=1)
566
567         #
568         # Send in tunnel encapped L2. expect out tunnel encapped L2
569         # in both directions
570         #
571         self.vapi.cli("clear trace")
572         tx = self.create_tunnel_stream_l2o4(self.pg0,
573                                             "2.2.2.2",
574                                             self.pg0.local_ip4)
575         self.pg0.add_stream(tx)
576
577         self.pg_enable_capture(self.pg_interfaces)
578         self.pg_start()
579
580         rx = self.pg0.get_capture()
581         self.verify_tunneled_l2o4(self.pg0, rx, tx,
582                                   self.pg0.local_ip4,
583                                   "2.2.2.3")
584
585         self.vapi.cli("clear trace")
586         tx = self.create_tunnel_stream_l2o4(self.pg0,
587                                             "2.2.2.3",
588                                             self.pg0.local_ip4)
589         self.pg0.add_stream(tx)
590
591         self.pg_enable_capture(self.pg_interfaces)
592         self.pg_start()
593
594         rx = self.pg0.get_capture()
595         self.verify_tunneled_l2o4(self.pg0, rx, tx,
596                                   self.pg0.local_ip4,
597                                   "2.2.2.2")
598
599         self.vapi.sw_interface_set_l2_xconnect(gre_if1.sw_if_index,
600                                                gre_if2.sw_if_index,
601                                                enable=0)
602         self.vapi.sw_interface_set_l2_xconnect(gre_if2.sw_if_index,
603                                                gre_if1.sw_if_index,
604                                                enable=0)
605
606         #
607         # Create a VLAN sub-interfaces on the GRE TEB interfaces
608         # then x-connect them
609         #
610         gre_if_11 = VppDot1QSubint(self, gre_if1, 11)
611         gre_if_12 = VppDot1QSubint(self, gre_if2, 12)
612
613         # gre_if_11.add_vpp_config()
614         # gre_if_12.add_vpp_config()
615
616         gre_if_11.admin_up()
617         gre_if_12.admin_up()
618
619         self.vapi.sw_interface_set_l2_xconnect(gre_if_11.sw_if_index,
620                                                gre_if_12.sw_if_index,
621                                                enable=1)
622         self.vapi.sw_interface_set_l2_xconnect(gre_if_12.sw_if_index,
623                                                gre_if_11.sw_if_index,
624                                                enable=1)
625
626         #
627         # Configure both to pop thier respective VLAN tags,
628         # so that during the x-coonect they will subsequently push
629         #
630         self.vapi.sw_interface_set_l2_tag_rewrite(gre_if_12.sw_if_index,
631                                                   L2_VTR_OP.L2_POP_1,
632                                                   12)
633         self.vapi.sw_interface_set_l2_tag_rewrite(gre_if_11.sw_if_index,
634                                                   L2_VTR_OP.L2_POP_1,
635                                                   11)
636
637         #
638         # Send traffic in both directiond - expect the VLAN tags to
639         # be swapped.
640         #
641         self.vapi.cli("clear trace")
642         tx = self.create_tunnel_stream_vlano4(self.pg0,
643                                               "2.2.2.2",
644                                               self.pg0.local_ip4,
645                                               11)
646         self.pg0.add_stream(tx)
647
648         self.pg_enable_capture(self.pg_interfaces)
649         self.pg_start()
650
651         rx = self.pg0.get_capture()
652         self.verify_tunneled_vlano4(self.pg0, rx, tx,
653                                     self.pg0.local_ip4,
654                                     "2.2.2.3",
655                                     12)
656
657         self.vapi.cli("clear trace")
658         tx = self.create_tunnel_stream_vlano4(self.pg0,
659                                               "2.2.2.3",
660                                               self.pg0.local_ip4,
661                                               12)
662         self.pg0.add_stream(tx)
663
664         self.pg_enable_capture(self.pg_interfaces)
665         self.pg_start()
666
667         rx = self.pg0.get_capture()
668         self.verify_tunneled_vlano4(self.pg0, rx, tx,
669                                     self.pg0.local_ip4,
670                                     "2.2.2.2",
671                                     11)
672
673         #
674         # Cleanup Test resources
675         #
676         gre_if_11.remove_vpp_config()
677         gre_if_12.remove_vpp_config()
678         gre_if1.remove_vpp_config()
679         gre_if2.remove_vpp_config()
680         route_tun1_dst.add_vpp_config()
681         route_tun2_dst.add_vpp_config()
682
683
684 if __name__ == '__main__':
685     unittest.main(testRunner=VppTestRunner)