fib: fib api updates
[vpp.git] / test / test_ipip.py
1 #!/usr/bin/env python
2 """IP{4,6} over IP{v,6} tunnel functional tests"""
3
4 import unittest
5 from scapy.layers.inet6 import IPv6, Ether, IP, UDP, IPv6ExtHdrFragment
6 from scapy.all import fragment, fragment6, RandShort, defragment6
7 from framework import VppTestCase, VppTestRunner
8 from vpp_ip import DpoProto
9 from vpp_ip_route import VppIpRoute, VppRoutePath, VppIpTable, FibPathProto
10 from socket import AF_INET, AF_INET6, inet_pton
11 from util import reassemble4
12
13 """ Testipip is a subclass of  VPPTestCase classes.
14
15 IPIP tests.
16
17 """
18
19
20 def ipip_add_tunnel(test, src, dst, table_id=0, tc_tos=0xff):
21     """ Add a IPIP tunnel """
22     return test.vapi.ipip_add_tunnel(
23         tunnel={
24             'src': src,
25             'dst': dst,
26             'table_id': table_id,
27             'instance': 0xffffffff,
28             'tc_tos': tc_tos
29         }
30     )
31
32
33 class TestIPIP(VppTestCase):
34     """ IPIP Test Case """
35
36     @classmethod
37     def setUpClass(cls):
38         super(TestIPIP, cls).setUpClass()
39         cls.create_pg_interfaces(range(2))
40         cls.interfaces = list(cls.pg_interfaces)
41
42     @classmethod
43     def tearDownClass(cls):
44         super(TestIPIP, cls).tearDownClass()
45
46     def setUp(self):
47         super(TestIPIP, self).setUp()
48         for i in self.interfaces:
49             i.admin_up()
50             i.config_ip4()
51             i.config_ip6()
52             i.disable_ipv6_ra()
53             i.resolve_arp()
54             i.resolve_ndp()
55
56     def tearDown(self):
57         super(TestIPIP, self).tearDown()
58         if not self.vpp_dead:
59             for i in self.pg_interfaces:
60                 i.unconfig_ip4()
61                 i.unconfig_ip6()
62                 i.admin_down()
63
64     def validate(self, rx, expected):
65         self.assertEqual(rx, expected.__class__(expected))
66
67     def generate_ip4_frags(self, payload_length, fragment_size):
68         p_ether = Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac)
69         p_payload = UDP(sport=1234, dport=1234) / self.payload(payload_length)
70         p_ip4 = IP(src="1.2.3.4", dst=self.pg0.remote_ip4)
71         outer_ip4 = (p_ether / IP(src=self.pg1.remote_ip4,
72                                   id=RandShort(),
73                                   dst=self.pg0.local_ip4) / p_ip4 / p_payload)
74         frags = fragment(outer_ip4, fragment_size)
75         p4_reply = (p_ip4 / p_payload)
76         p4_reply.ttl -= 1
77         return frags, p4_reply
78
79     def test_ipip4(self):
80         """ ip{v4,v6} over ip4 test """
81         p_ether = Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
82         p_ip6 = IPv6(src="1::1", dst="DEAD::1", nh='UDP', tc=42)
83         p_ip4 = IP(src="1.2.3.4", dst="130.67.0.1", tos=42)
84         p_payload = UDP(sport=1234, dport=1234)
85
86         # IPv4 transport
87         rv = ipip_add_tunnel(self,
88                              self.pg0.local_ip4,
89                              self.pg1.remote_ip4,
90                              tc_tos=0xFF)
91         sw_if_index = rv.sw_if_index
92
93         # Set interface up and enable IP on it
94         self.vapi.sw_interface_set_flags(sw_if_index, 1)
95         self.vapi.sw_interface_set_unnumbered(
96             sw_if_index=self.pg0.sw_if_index,
97             unnumbered_sw_if_index=sw_if_index)
98
99         # Add IPv4 and IPv6 routes via tunnel interface
100         ip4_via_tunnel = VppIpRoute(
101             self, "130.67.0.0", 16,
102             [VppRoutePath("0.0.0.0",
103                           sw_if_index,
104                           proto=FibPathProto.FIB_PATH_NH_PROTO_IP4)])
105         ip4_via_tunnel.add_vpp_config()
106
107         ip6_via_tunnel = VppIpRoute(
108             self, "dead::", 16,
109             [VppRoutePath("::",
110                           sw_if_index,
111                           proto=FibPathProto.FIB_PATH_NH_PROTO_IP6)])
112         ip6_via_tunnel.add_vpp_config()
113
114         # IPv6 in to IPv4 tunnel
115         p6 = (p_ether / p_ip6 / p_payload)
116         p_inner_ip6 = p_ip6
117         p_inner_ip6.hlim -= 1
118         p6_reply = (IP(src=self.pg0.local_ip4, dst=self.pg1.remote_ip4,
119                        proto='ipv6', id=0, tos=42) / p_inner_ip6 / p_payload)
120         p6_reply.ttl -= 1
121         rx = self.send_and_expect(self.pg0, p6 * 10, self.pg1)
122         for p in rx:
123             self.validate(p[1], p6_reply)
124
125         # IPv4 in to IPv4 tunnel
126         p4 = (p_ether / p_ip4 / p_payload)
127         p_ip4_inner = p_ip4
128         p_ip4_inner.ttl -= 1
129         p4_reply = (IP(src=self.pg0.local_ip4, dst=self.pg1.remote_ip4,
130                        tos=42) /
131                     p_ip4_inner / p_payload)
132         p4_reply.ttl -= 1
133         p4_reply.id = 0
134         rx = self.send_and_expect(self.pg0, p4 * 10, self.pg1)
135         for p in rx:
136             self.validate(p[1], p4_reply)
137
138         # Decapsulation
139         p_ether = Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac)
140
141         # IPv4 tunnel to IPv4
142         p_ip4 = IP(src="1.2.3.4", dst=self.pg0.remote_ip4)
143         p4 = (p_ether / IP(src=self.pg1.remote_ip4,
144                            dst=self.pg0.local_ip4) / p_ip4 / p_payload)
145         p4_reply = (p_ip4 / p_payload)
146         p4_reply.ttl -= 1
147         rx = self.send_and_expect(self.pg1, p4 * 10, self.pg0)
148         for p in rx:
149             self.validate(p[1], p4_reply)
150
151         err = self.statistics.get_err_counter(
152             '/err/ipip4-input/packets decapsulated')
153         self.assertEqual(err, 10)
154
155         # IPv4 tunnel to IPv6
156         p_ip6 = IPv6(src="1:2:3::4", dst=self.pg0.remote_ip6)
157         p6 = (p_ether / IP(src=self.pg1.remote_ip4,
158                            dst=self.pg0.local_ip4) / p_ip6 / p_payload)
159         p6_reply = (p_ip6 / p_payload)
160         p6_reply.hlim = 63
161         rx = self.send_and_expect(self.pg1, p6 * 10, self.pg0)
162         for p in rx:
163             self.validate(p[1], p6_reply)
164
165         err = self.statistics.get_err_counter(
166             '/err/ipip4-input/packets decapsulated')
167         self.assertEqual(err, 20)
168
169         #
170         # Fragmentation / Reassembly and Re-fragmentation
171         #
172         rv = self.vapi.ip_reassembly_enable_disable(
173             sw_if_index=self.pg1.sw_if_index,
174             enable_ip4=1)
175
176         self.vapi.ip_reassembly_set(timeout_ms=1000, max_reassemblies=1000,
177                                     max_reassembly_length=1000,
178                                     expire_walk_interval_ms=10000,
179                                     is_ip6=0)
180
181         # Send lots of fragments, verify reassembled packet
182         frags, p4_reply = self.generate_ip4_frags(3131, 1400)
183         f = []
184         for i in range(0, 1000):
185             f.extend(frags)
186         self.pg1.add_stream(f)
187         self.pg_enable_capture()
188         self.pg_start()
189         rx = self.pg0.get_capture(1000)
190
191         for p in rx:
192             self.validate(p[1], p4_reply)
193
194         err = self.statistics.get_err_counter(
195             '/err/ipip4-input/packets decapsulated')
196         self.assertEqual(err, 1020)
197
198         f = []
199         r = []
200         for i in range(1, 90):
201             frags, p4_reply = self.generate_ip4_frags(i * 100, 1000)
202             f.extend(frags)
203             r.extend(p4_reply)
204         self.pg_enable_capture()
205         self.pg1.add_stream(f)
206         self.pg_start()
207         rx = self.pg0.get_capture(89)
208         i = 0
209         for p in rx:
210             self.validate(p[1], r[i])
211             i += 1
212
213         # Now try with re-fragmentation
214         #
215         # Send fragments to tunnel head-end, for the tunnel head end
216         # to reassemble and then refragment
217         #
218         self.vapi.sw_interface_set_mtu(self.pg0.sw_if_index, [576, 0, 0, 0])
219         frags, p4_reply = self.generate_ip4_frags(3123, 1200)
220         self.pg_enable_capture()
221         self.pg1.add_stream(frags)
222         self.pg_start()
223         rx = self.pg0.get_capture(6)
224         reass_pkt = reassemble4(rx)
225         p4_reply.ttl -= 1
226         p4_reply.id = 256
227         self.validate(reass_pkt, p4_reply)
228
229         self.vapi.sw_interface_set_mtu(self.pg0.sw_if_index, [1600, 0, 0, 0])
230         frags, p4_reply = self.generate_ip4_frags(3123, 1200)
231         self.pg_enable_capture()
232         self.pg1.add_stream(frags)
233         self.pg_start()
234         rx = self.pg0.get_capture(2)
235         reass_pkt = reassemble4(rx)
236         p4_reply.ttl -= 1
237         p4_reply.id = 512
238         self.validate(reass_pkt, p4_reply)
239
240     def test_ipip_create(self):
241         """ ipip create / delete interface test """
242         rv = ipip_add_tunnel(self, '1.2.3.4', '2.3.4.5')
243         sw_if_index = rv.sw_if_index
244         self.vapi.ipip_del_tunnel(sw_if_index)
245
246     def test_ipip_vrf_create(self):
247         """ ipip create / delete interface VRF test """
248
249         t = VppIpTable(self, 20)
250         t.add_vpp_config()
251         rv = ipip_add_tunnel(self, '1.2.3.4', '2.3.4.5', table_id=20)
252         sw_if_index = rv.sw_if_index
253         self.vapi.ipip_del_tunnel(sw_if_index)
254
255     def payload(self, len):
256         return 'x' * len
257
258
259 class TestIPIP6(VppTestCase):
260     """ IPIP6 Test Case """
261
262     @classmethod
263     def setUpClass(cls):
264         super(TestIPIP6, cls).setUpClass()
265         cls.create_pg_interfaces(range(2))
266         cls.interfaces = list(cls.pg_interfaces)
267
268     @classmethod
269     def tearDownClass(cls):
270         super(TestIPIP6, cls).tearDownClass()
271
272     def setUp(self):
273         super(TestIPIP6, self).setUp()
274         for i in self.interfaces:
275             i.admin_up()
276             i.config_ip4()
277             i.config_ip6()
278             i.disable_ipv6_ra()
279             i.resolve_arp()
280             i.resolve_ndp()
281         self.setup_tunnel()
282
283     def tearDown(self):
284         if not self.vpp_dead:
285             self.destroy_tunnel()
286             for i in self.pg_interfaces:
287                 i.unconfig_ip4()
288                 i.unconfig_ip6()
289                 i.admin_down()
290             super(TestIPIP6, self).tearDown()
291
292     def setup_tunnel(self):
293         # IPv6 transport
294         rv = ipip_add_tunnel(self,
295                              self.pg0.local_ip6,
296                              self.pg1.remote_ip6,
297                              tc_tos=255)
298
299         sw_if_index = rv.sw_if_index
300         self.tunnel_if_index = sw_if_index
301         self.vapi.sw_interface_set_flags(sw_if_index, 1)
302         self.vapi.sw_interface_set_unnumbered(
303             sw_if_index=self.pg0.sw_if_index,
304             unnumbered_sw_if_index=sw_if_index)
305
306         # Add IPv4 and IPv6 routes via tunnel interface
307         ip4_via_tunnel = VppIpRoute(
308             self, "130.67.0.0", 16,
309             [VppRoutePath("0.0.0.0",
310                           sw_if_index,
311                           proto=FibPathProto.FIB_PATH_NH_PROTO_IP4)])
312         ip4_via_tunnel.add_vpp_config()
313
314         ip6_via_tunnel = VppIpRoute(
315             self, "dead::", 16,
316             [VppRoutePath("::",
317                           sw_if_index,
318                           proto=FibPathProto.FIB_PATH_NH_PROTO_IP6)])
319         ip6_via_tunnel.add_vpp_config()
320
321         self.tunnel_ip6_via_tunnel = ip6_via_tunnel
322         self.tunnel_ip4_via_tunnel = ip4_via_tunnel
323
324     def destroy_tunnel(self):
325         # IPv6 transport
326         self.tunnel_ip4_via_tunnel.remove_vpp_config()
327         self.tunnel_ip6_via_tunnel.remove_vpp_config()
328
329         rv = self.vapi.ipip_del_tunnel(sw_if_index=self.tunnel_if_index)
330
331     def validate(self, rx, expected):
332         self.assertEqual(rx, expected.__class__(expected))
333
334     def generate_ip6_frags(self, payload_length, fragment_size):
335         p_ether = Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac)
336         p_payload = UDP(sport=1234, dport=1234) / self.payload(payload_length)
337         p_ip6 = IPv6(src="1::1", dst=self.pg0.remote_ip6)
338         outer_ip6 = (p_ether / IPv6(src=self.pg1.remote_ip6,
339                                     dst=self.pg0.local_ip6) /
340                      IPv6ExtHdrFragment() / p_ip6 / p_payload)
341         frags = fragment6(outer_ip6, fragment_size)
342         p6_reply = (p_ip6 / p_payload)
343         p6_reply.hlim -= 1
344         return frags, p6_reply
345
346     def generate_ip6_hairpin_frags(self, payload_length, fragment_size):
347         p_ether = Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac)
348         p_payload = UDP(sport=1234, dport=1234) / self.payload(payload_length)
349         p_ip6 = IPv6(src="1::1", dst="dead::1")
350         outer_ip6 = (p_ether / IPv6(src=self.pg1.remote_ip6,
351                                     dst=self.pg0.local_ip6) /
352                      IPv6ExtHdrFragment() / p_ip6 / p_payload)
353         frags = fragment6(outer_ip6, fragment_size)
354         p_ip6.hlim -= 1
355         p6_reply = (IPv6(src=self.pg0.local_ip6, dst=self.pg1.remote_ip6,
356                          hlim=63) / p_ip6 / p_payload)
357
358         return frags, p6_reply
359
360     def test_encap(self):
361         """ ip{v4,v6} over ip6 test encap """
362         p_ether = Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
363         p_ip6 = IPv6(src="1::1", dst="DEAD::1", tc=42, nh='UDP')
364         p_ip4 = IP(src="1.2.3.4", dst="130.67.0.1", tos=42)
365         p_payload = UDP(sport=1234, dport=1234)
366
367         # Encapsulation
368         # IPv6 in to IPv6 tunnel
369         p6 = (p_ether / p_ip6 / p_payload)
370         p6_reply = (IPv6(src=self.pg0.local_ip6, dst=self.pg1.remote_ip6,
371                          hlim=64, tc=42) /
372                     p_ip6 / p_payload)
373         p6_reply[1].hlim -= 1
374         rx = self.send_and_expect(self.pg0, p6 * 11, self.pg1)
375         for p in rx:
376             self.validate(p[1], p6_reply)
377
378         # IPv4 in to IPv6 tunnel
379         p4 = (p_ether / p_ip4 / p_payload)
380         p4_reply = (IPv6(src=self.pg0.local_ip6,
381                          dst=self.pg1.remote_ip6, hlim=64, tc=42) /
382                     p_ip4 / p_payload)
383         p4_reply[1].ttl -= 1
384         rx = self.send_and_expect(self.pg0, p4 * 11, self.pg1)
385         for p in rx:
386             self.validate(p[1], p4_reply)
387
388     def test_decap(self):
389         """ ip{v4,v6} over ip6 test decap """
390
391         p_ether = Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac)
392         p_ip6 = IPv6(src="1::1", dst="DEAD::1", tc=42, nh='UDP')
393         p_ip4 = IP(src="1.2.3.4", dst=self.pg0.remote_ip4)
394         p_payload = UDP(sport=1234, dport=1234)
395
396         # Decapsulation
397         # IPv6 tunnel to IPv4
398
399         p4 = (p_ether / IPv6(src=self.pg1.remote_ip6,
400                              dst=self.pg0.local_ip6) / p_ip4 / p_payload)
401         p4_reply = (p_ip4 / p_payload)
402         p4_reply.ttl -= 1
403         rx = self.send_and_expect(self.pg1, p4 * 11, self.pg0)
404         for p in rx:
405             self.validate(p[1], p4_reply)
406
407         # IPv6 tunnel to IPv6
408         p_ip6 = IPv6(src="1:2:3::4", dst=self.pg0.remote_ip6)
409         p6 = (p_ether / IPv6(src=self.pg1.remote_ip6,
410                              dst=self.pg0.local_ip6) / p_ip6 / p_payload)
411         p6_reply = (p_ip6 / p_payload)
412         p6_reply.hlim = 63
413         rx = self.send_and_expect(self.pg1, p6 * 11, self.pg0)
414         for p in rx:
415             self.validate(p[1], p6_reply)
416
417     def test_frag(self):
418         """ ip{v4,v6} over ip6 test frag """
419
420         p_ether = Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac)
421         p_ip6 = IPv6(src="1::1", dst="DEAD::1", tc=42, nh='UDP')
422         p_ip4 = IP(src="1.2.3.4", dst=self.pg0.remote_ip4)
423         p_payload = UDP(sport=1234, dport=1234)
424
425         #
426         # Fragmentation / Reassembly and Re-fragmentation
427         #
428         rv = self.vapi.ip_reassembly_enable_disable(
429             sw_if_index=self.pg1.sw_if_index,
430             enable_ip6=1)
431
432         self.vapi.ip_reassembly_set(timeout_ms=1000, max_reassemblies=1000,
433                                     max_reassembly_length=1000,
434                                     expire_walk_interval_ms=10000,
435                                     is_ip6=1)
436
437         # Send lots of fragments, verify reassembled packet
438         before_cnt = self.statistics.get_err_counter(
439             '/err/ipip6-input/packets decapsulated')
440         frags, p6_reply = self.generate_ip6_frags(3131, 1400)
441         f = []
442         for i in range(0, 1000):
443             f.extend(frags)
444         self.pg1.add_stream(f)
445         self.pg_enable_capture()
446         self.pg_start()
447         rx = self.pg0.get_capture(1000)
448
449         for p in rx:
450             self.validate(p[1], p6_reply)
451
452         cnt = self.statistics.get_err_counter(
453             '/err/ipip6-input/packets decapsulated')
454         self.assertEqual(cnt, before_cnt + 1000)
455
456         f = []
457         r = []
458         # TODO: Check out why reassembly of atomic fragments don't work
459         for i in range(10, 90):
460             frags, p6_reply = self.generate_ip6_frags(i * 100, 1000)
461             f.extend(frags)
462             r.extend(p6_reply)
463         self.pg_enable_capture()
464         self.pg1.add_stream(f)
465         self.pg_start()
466         rx = self.pg0.get_capture(80)
467         i = 0
468         for p in rx:
469             self.validate(p[1], r[i])
470             i += 1
471
472         # Simple fragmentation
473         p_ether = Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
474         self.vapi.sw_interface_set_mtu(self.pg1.sw_if_index, [1280, 0, 0, 0])
475
476         # IPv6 in to IPv6 tunnel
477         p_payload = UDP(sport=1234, dport=1234) / self.payload(1300)
478
479         p6 = (p_ether / p_ip6 / p_payload)
480         p6_reply = (IPv6(src=self.pg0.local_ip6, dst=self.pg1.remote_ip6,
481                          hlim=63, tc=42) /
482                     p_ip6 / p_payload)
483         p6_reply[1].hlim -= 1
484         self.pg_enable_capture()
485         self.pg0.add_stream(p6)
486         self.pg_start()
487         rx = self.pg1.get_capture(2)
488
489         # Scapy defragment doesn't deal well with multiple layers
490         # of same type / Ethernet header first
491         f = [p[1] for p in rx]
492         reass_pkt = defragment6(f)
493         self.validate(reass_pkt, p6_reply)
494
495         # Now try with re-fragmentation
496         #
497         # Send large fragments to tunnel head-end, for the tunnel head end
498         # to reassemble and then refragment out the tunnel again.
499         # Hair-pinning
500         #
501         self.vapi.sw_interface_set_mtu(self.pg1.sw_if_index, [1280, 0, 0, 0])
502         frags, p6_reply = self.generate_ip6_hairpin_frags(8000, 1200)
503         self.pg_enable_capture()
504         self.pg1.add_stream(frags)
505         self.pg_start()
506         rx = self.pg1.get_capture(7)
507         f = [p[1] for p in rx]
508         reass_pkt = defragment6(f)
509         p6_reply.id = 256
510         self.validate(reass_pkt, p6_reply)
511
512     def test_ipip_create(self):
513         """ ipip create / delete interface test """
514         rv = ipip_add_tunnel(self, '1.2.3.4', '2.3.4.5')
515         sw_if_index = rv.sw_if_index
516         self.vapi.ipip_del_tunnel(sw_if_index)
517
518     def test_ipip_vrf_create(self):
519         """ ipip create / delete interface VRF test """
520
521         t = VppIpTable(self, 20)
522         t.add_vpp_config()
523         rv = ipip_add_tunnel(self, '1.2.3.4', '2.3.4.5', table_id=20)
524         sw_if_index = rv.sw_if_index
525         self.vapi.ipip_del_tunnel(sw_if_index)
526
527     def payload(self, len):
528         return 'x' * len
529
530
531 if __name__ == '__main__':
532     unittest.main(testRunner=VppTestRunner)