blob: efaaf52539eba8c19c3dc4a2eb6a2806c302585f [file] [log] [blame]
Ole Troan46c1c112018-03-14 20:39:40 +01001#!/usr/bin/env python
2"""IP{4,6} over IP{v,6} tunnel functional tests"""
Ole Troan298c6952018-03-08 12:30:43 +01003
4import unittest
Damjan Marionfe7d4a22018-04-13 19:43:39 +02005from scapy.layers.inet6 import IPv6, Ether, IP, UDP
Ole Troan7eb9d962018-08-10 14:39:48 +02006from scapy.all import fragment, RandShort
Ole Troan46c1c112018-03-14 20:39:40 +01007from framework import VppTestCase, VppTestRunner
Neale Rannsc0a93142018-09-05 15:42:26 -07008from vpp_ip import DpoProto
9from vpp_ip_route import VppIpRoute, VppRoutePath, VppIpTable
Ole Troan46c1c112018-03-14 20:39:40 +010010from socket import AF_INET, AF_INET6, inet_pton
Ole Troan4146c652018-08-08 22:23:19 +020011import StringIO
Ole Troan298c6952018-03-08 12:30:43 +010012
13""" Testipip is a subclass of VPPTestCase classes.
14
15IPIP tests.
16
17"""
18
19
Ole Troan4146c652018-08-08 22:23:19 +020020def reassemble(listoffragments):
21 buffer = StringIO.StringIO()
22 first = listoffragments[0]
23 buffer.seek(20)
24 for pkt in listoffragments:
25 buffer.seek(pkt[IP].frag*8)
26 buffer.write(pkt[IP].payload)
27 first.len = len(buffer.getvalue()) + 20
28 first.flags = 0
29 del(first.chksum)
30 header = str(first[IP])[:20]
31 return first[IP].__class__(header + buffer.getvalue())
32
33
Ole Troan298c6952018-03-08 12:30:43 +010034class TestIPIP(VppTestCase):
35 """ IPIP Test Case """
36
37 @classmethod
38 def setUpClass(cls):
39 super(TestIPIP, cls).setUpClass()
Ole Troan46c1c112018-03-14 20:39:40 +010040 cls.create_pg_interfaces(range(2))
41 cls.interfaces = list(cls.pg_interfaces)
Ole Troan298c6952018-03-08 12:30:43 +010042
43 def setUp(cls):
44 super(TestIPIP, cls).setUp()
Ole Troan46c1c112018-03-14 20:39:40 +010045 for i in cls.interfaces:
46 i.admin_up()
47 i.config_ip4()
48 i.config_ip6()
49 i.disable_ipv6_ra()
50 i.resolve_arp()
51 i.resolve_ndp()
Ole Troan298c6952018-03-08 12:30:43 +010052
53 def tearDown(self):
54 super(TestIPIP, self).tearDown()
55 if not self.vpp_dead:
Ole Troan46c1c112018-03-14 20:39:40 +010056 for i in self.pg_interfaces:
57 i.unconfig_ip4()
58 i.unconfig_ip6()
59 i.admin_down()
Ole Troan298c6952018-03-08 12:30:43 +010060
61 def validate(self, rx, expected):
Ole Troan46c1c112018-03-14 20:39:40 +010062 self.assertEqual(rx, expected.__class__(str(expected)))
Ole Troan298c6952018-03-08 12:30:43 +010063
Ole Troan7eb9d962018-08-10 14:39:48 +020064 def generate_frags(self, payload_length, fragment_size):
65 p_ether = Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac)
66 p_payload = UDP(sport=1234, dport=1234) / self.payload(payload_length)
67 p_ip4 = IP(src="1.2.3.4", dst=self.pg0.remote_ip4)
68 outer_ip4 = (p_ether / IP(src=self.pg1.remote_ip4,
69 id=RandShort(),
70 dst=self.pg0.local_ip4) / p_ip4 / p_payload)
71 frags = fragment(outer_ip4, fragment_size)
72 p4_reply = (p_ip4 / p_payload)
73 p4_reply.ttl -= 1
74 return frags, p4_reply
75
Ole Troan298c6952018-03-08 12:30:43 +010076 def test_ipip4(self):
77 """ ip{v4,v6} over ip4 test """
78 p_ether = Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
Ole Troand57f6362018-05-24 13:21:43 +020079 p_ip6 = IPv6(src="1::1", dst="DEAD::1", nh='UDP', tc=42)
80 p_ip4 = IP(src="1.2.3.4", dst="130.67.0.1", tos=42)
Damjan Marionfe7d4a22018-04-13 19:43:39 +020081 p_payload = UDP(sport=1234, dport=1234)
Ole Troan298c6952018-03-08 12:30:43 +010082
83 # IPv4 transport
84 rv = self.vapi.ipip_add_tunnel(
Ole Troan46c1c112018-03-14 20:39:40 +010085 src_address=self.pg0.local_ip4n,
86 dst_address=self.pg1.remote_ip4n,
Ole Troand57f6362018-05-24 13:21:43 +020087 is_ipv6=0, tc_tos=0xFF)
Ole Troan298c6952018-03-08 12:30:43 +010088 sw_if_index = rv.sw_if_index
89
90 # Set interface up and enable IP on it
Ole Troan46c1c112018-03-14 20:39:40 +010091 self.vapi.sw_interface_set_flags(sw_if_index, 1)
92 self.vapi.sw_interface_set_unnumbered(
Ole Troan298c6952018-03-08 12:30:43 +010093 ip_sw_if_index=self.pg0.sw_if_index,
94 sw_if_index=sw_if_index)
Ole Troan298c6952018-03-08 12:30:43 +010095
96 # Add IPv4 and IPv6 routes via tunnel interface
97 ip4_via_tunnel = VppIpRoute(
98 self, "130.67.0.0", 16,
99 [VppRoutePath("0.0.0.0",
100 sw_if_index,
101 proto=DpoProto.DPO_PROTO_IP4)], is_ip6=0)
102 ip4_via_tunnel.add_vpp_config()
103
104 ip6_via_tunnel = VppIpRoute(
105 self, "dead::", 16,
106 [VppRoutePath("::",
107 sw_if_index,
108 proto=DpoProto.DPO_PROTO_IP6)], is_ip6=1)
109 ip6_via_tunnel.add_vpp_config()
110
111 # IPv6 in to IPv4 tunnel
112 p6 = (p_ether / p_ip6 / p_payload)
113 p_inner_ip6 = p_ip6
114 p_inner_ip6.hlim -= 1
115 p6_reply = (IP(src=self.pg0.local_ip4, dst=self.pg1.remote_ip4,
Ole Troand57f6362018-05-24 13:21:43 +0200116 proto='ipv6', id=0, tos=42) / p_inner_ip6 / p_payload)
Ole Troan298c6952018-03-08 12:30:43 +0100117 p6_reply.ttl -= 1
118 rx = self.send_and_expect(self.pg0, p6*10, self.pg1)
119 for p in rx:
120 self.validate(p[1], p6_reply)
121
122 # IPv4 in to IPv4 tunnel
123 p4 = (p_ether / p_ip4 / p_payload)
124 p_ip4_inner = p_ip4
125 p_ip4_inner.ttl -= 1
Ole Troand57f6362018-05-24 13:21:43 +0200126 p4_reply = (IP(src=self.pg0.local_ip4, dst=self.pg1.remote_ip4,
127 tos=42) /
128 p_ip4_inner / p_payload)
Ole Troan298c6952018-03-08 12:30:43 +0100129 p4_reply.ttl -= 1
130 p4_reply.id = 0
Damjan Marionfe7d4a22018-04-13 19:43:39 +0200131 rx = self.send_and_expect(self.pg0, p4*10, self.pg1)
Ole Troan298c6952018-03-08 12:30:43 +0100132 for p in rx:
133 self.validate(p[1], p4_reply)
134
135 # Decapsulation
136 p_ether = Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac)
137
138 # IPv4 tunnel to IPv4
139 p_ip4 = IP(src="1.2.3.4", dst=self.pg0.remote_ip4)
140 p4 = (p_ether / IP(src=self.pg1.remote_ip4,
141 dst=self.pg0.local_ip4) / p_ip4 / p_payload)
142 p4_reply = (p_ip4 / p_payload)
143 p4_reply.ttl -= 1
144 rx = self.send_and_expect(self.pg1, p4*10, self.pg0)
145 for p in rx:
146 self.validate(p[1], p4_reply)
147
Ole Troan58492a82018-09-04 13:19:12 +0200148 err = self.statistics.get_counter(
149 '/err/ipip4-input/packets decapsulated')
150 self.assertEqual(err, 10)
Ole Troan73202102018-08-31 00:29:48 +0200151
Ole Troan298c6952018-03-08 12:30:43 +0100152 # IPv4 tunnel to IPv6
153 p_ip6 = IPv6(src="1:2:3::4", dst=self.pg0.remote_ip6)
154 p6 = (p_ether / IP(src=self.pg1.remote_ip4,
155 dst=self.pg0.local_ip4) / p_ip6 / p_payload)
156 p6_reply = (p_ip6 / p_payload)
157 p6_reply.hlim = 63
158 rx = self.send_and_expect(self.pg1, p6*10, self.pg0)
159 for p in rx:
160 self.validate(p[1], p6_reply)
161
Ole Troan58492a82018-09-04 13:19:12 +0200162 err = self.statistics.get_counter(
163 '/err/ipip4-input/packets decapsulated')
164 self.assertEqual(err, 20)
Ole Troan73202102018-08-31 00:29:48 +0200165
Ole Troan7eb9d962018-08-10 14:39:48 +0200166 #
Ole Troan4146c652018-08-08 22:23:19 +0200167 # Fragmentation / Reassembly and Re-fragmentation
Ole Troan7eb9d962018-08-10 14:39:48 +0200168 #
Ole Troan4146c652018-08-08 22:23:19 +0200169 rv = self.vapi.ip_reassembly_enable_disable(
170 sw_if_index=self.pg1.sw_if_index,
171 enable_ip4=1)
Ole Troan4146c652018-08-08 22:23:19 +0200172
Ole Troan7eb9d962018-08-10 14:39:48 +0200173 # Send lots of fragments, verify reassembled packet
174 frags, p4_reply = self.generate_frags(3131, 1400)
175 f = []
176 for i in range(0, 1000):
177 f.extend(frags)
178 self.pg1.add_stream(f)
Ole Troan4146c652018-08-08 22:23:19 +0200179 self.pg_enable_capture()
Ole Troan4146c652018-08-08 22:23:19 +0200180 self.pg_start()
Ole Troan7eb9d962018-08-10 14:39:48 +0200181 rx = self.pg0.get_capture(1000)
182
Ole Troan4146c652018-08-08 22:23:19 +0200183 for p in rx:
184 self.validate(p[1], p4_reply)
185
Ole Troan58492a82018-09-04 13:19:12 +0200186 err = self.statistics.get_counter(
187 '/err/ipip4-input/packets decapsulated')
188 self.assertEqual(err, 1020)
Ole Troan73202102018-08-31 00:29:48 +0200189
Ole Troan7eb9d962018-08-10 14:39:48 +0200190 f = []
191 r = []
192 for i in range(1, 90):
193 frags, p4_reply = self.generate_frags(i * 100, 1000)
194 f.extend(frags)
195 r.extend(p4_reply)
196 self.pg_enable_capture()
197 self.pg1.add_stream(f)
198 self.pg_start()
199 rx = self.pg0.get_capture(89)
200 i = 0
201 for p in rx:
202 self.validate(p[1], r[i])
203 i += 1
204
Ole Troan4146c652018-08-08 22:23:19 +0200205 # Now try with re-fragmentation
Ole Troan7eb9d962018-08-10 14:39:48 +0200206 #
207 # Send fragments to tunnel head-end, for the tunnel head end
208 # to reassemble and then refragment
209 #
Ole Troan4146c652018-08-08 22:23:19 +0200210 self.vapi.sw_interface_set_mtu(self.pg0.sw_if_index, [576, 0, 0, 0])
Ole Troan7eb9d962018-08-10 14:39:48 +0200211 frags, p4_reply = self.generate_frags(3123, 1200)
Ole Troan4146c652018-08-08 22:23:19 +0200212 self.pg_enable_capture()
213 self.pg1.add_stream(frags)
214 self.pg_start()
215 rx = self.pg0.get_capture(6)
216 reass_pkt = reassemble(rx)
217 p4_reply.ttl -= 1
218 p4_reply.id = 256
219 self.validate(reass_pkt, p4_reply)
220
Ole Troan7eb9d962018-08-10 14:39:48 +0200221 self.vapi.sw_interface_set_mtu(self.pg0.sw_if_index, [1600, 0, 0, 0])
222 frags, p4_reply = self.generate_frags(3123, 1200)
223 self.pg_enable_capture()
224 self.pg1.add_stream(frags)
225 self.pg_start()
226 rx = self.pg0.get_capture(2)
227 reass_pkt = reassemble(rx)
228 p4_reply.ttl -= 1
229 p4_reply.id = 512
230 self.validate(reass_pkt, p4_reply)
231
Ole Troan298c6952018-03-08 12:30:43 +0100232 def test_ipip6(self):
233 """ ip{v4,v6} over ip6 test """
234 p_ether = Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
Ole Troand57f6362018-05-24 13:21:43 +0200235 p_ip6 = IPv6(src="1::1", dst="DEAD::1", tc=42, nh='UDP')
236 p_ip4 = IP(src="1.2.3.4", dst="130.67.0.1", tos=42)
Damjan Marionfe7d4a22018-04-13 19:43:39 +0200237 p_payload = UDP(sport=1234, dport=1234)
Ole Troan298c6952018-03-08 12:30:43 +0100238
239 # IPv6 transport
240 rv = self.vapi.ipip_add_tunnel(
Ole Troan46c1c112018-03-14 20:39:40 +0100241 src_address=self.pg0.local_ip6n,
Ole Troand57f6362018-05-24 13:21:43 +0200242 dst_address=self.pg1.remote_ip6n, tc_tos=255)
Ole Troan298c6952018-03-08 12:30:43 +0100243
244 sw_if_index = rv.sw_if_index
245
Ole Troan46c1c112018-03-14 20:39:40 +0100246 self.vapi.sw_interface_set_flags(sw_if_index, 1)
247 self.vapi.sw_interface_set_unnumbered(
Ole Troan298c6952018-03-08 12:30:43 +0100248 ip_sw_if_index=self.pg0.sw_if_index, sw_if_index=sw_if_index)
Ole Troan298c6952018-03-08 12:30:43 +0100249
250 # Add IPv4 and IPv6 routes via tunnel interface
251 ip4_via_tunnel = VppIpRoute(
252 self, "130.67.0.0", 16,
253 [VppRoutePath("0.0.0.0",
254 sw_if_index,
255 proto=DpoProto.DPO_PROTO_IP4)], is_ip6=0)
256 ip4_via_tunnel.add_vpp_config()
257
258 ip6_via_tunnel = VppIpRoute(
259 self, "dead::", 16,
260 [VppRoutePath("::",
261 sw_if_index,
262 proto=DpoProto.DPO_PROTO_IP6)], is_ip6=1)
263 ip6_via_tunnel.add_vpp_config()
264
265 # Encapsulation
266
267 # IPv6 in to IPv6 tunnel
268 p6 = (p_ether / p_ip6 / p_payload)
Ole Troand57f6362018-05-24 13:21:43 +0200269 p6_reply = (IPv6(src=self.pg0.local_ip6, dst=self.pg1.remote_ip6,
270 hlim=63, tc=42) /
271 p_ip6 / p_payload)
Ole Troan298c6952018-03-08 12:30:43 +0100272 p6_reply[1].hlim -= 1
Ole Troand57f6362018-05-24 13:21:43 +0200273 rx = self.send_and_expect(self.pg0, p6*11, self.pg1)
Ole Troan298c6952018-03-08 12:30:43 +0100274 for p in rx:
275 self.validate(p[1], p6_reply)
276
277 # IPv4 in to IPv6 tunnel
278 p4 = (p_ether / p_ip4 / p_payload)
279 p4_reply = (IPv6(src=self.pg0.local_ip6,
Ole Troand57f6362018-05-24 13:21:43 +0200280 dst=self.pg1.remote_ip6, hlim=63, tc=42) /
281 p_ip4 / p_payload)
Ole Troan298c6952018-03-08 12:30:43 +0100282 p4_reply[1].ttl -= 1
Ole Troand57f6362018-05-24 13:21:43 +0200283 rx = self.send_and_expect(self.pg0, p4*11, self.pg1)
Ole Troan298c6952018-03-08 12:30:43 +0100284 for p in rx:
285 self.validate(p[1], p4_reply)
286
287 # Decapsulation
288
289 p_ether = Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac)
290
291 # IPv6 tunnel to IPv4
292 p_ip4 = IP(src="1.2.3.4", dst=self.pg0.remote_ip4)
293 p4 = (p_ether / IPv6(src=self.pg1.remote_ip6,
294 dst=self.pg0.local_ip6) / p_ip4 / p_payload)
295 p4_reply = (p_ip4 / p_payload)
296 p4_reply.ttl -= 1
Ole Troand57f6362018-05-24 13:21:43 +0200297 rx = self.send_and_expect(self.pg1, p4*11, self.pg0)
Ole Troan298c6952018-03-08 12:30:43 +0100298 for p in rx:
299 self.validate(p[1], p4_reply)
300
301 # IPv6 tunnel to IPv6
302 p_ip6 = IPv6(src="1:2:3::4", dst=self.pg0.remote_ip6)
303 p6 = (p_ether / IPv6(src=self.pg1.remote_ip6,
304 dst=self.pg0.local_ip6) / p_ip6 / p_payload)
305 p6_reply = (p_ip6 / p_payload)
306 p6_reply.hlim = 63
Ole Troand57f6362018-05-24 13:21:43 +0200307 rx = self.send_and_expect(self.pg1, p6*11, self.pg0)
Ole Troan298c6952018-03-08 12:30:43 +0100308 for p in rx:
309 self.validate(p[1], p6_reply)
310
311 def test_ipip_create(self):
312 """ ipip create / delete interface test """
313 rv = self.vapi.ipip_add_tunnel(
Ole Troan46c1c112018-03-14 20:39:40 +0100314 src_address=inet_pton(AF_INET, '1.2.3.4'),
315 dst_address=inet_pton(AF_INET, '2.3.4.5'), is_ipv6=0)
Ole Troan298c6952018-03-08 12:30:43 +0100316 sw_if_index = rv.sw_if_index
Ole Troan46c1c112018-03-14 20:39:40 +0100317 self.vapi.ipip_del_tunnel(sw_if_index)
Ole Troan298c6952018-03-08 12:30:43 +0100318
Neale Ranns61502112018-08-22 00:21:14 -0700319 def test_ipip_vrf_create(self):
320 """ ipip create / delete interface VRF test """
321
322 t = VppIpTable(self, 20)
323 t.add_vpp_config()
324 rv = self.vapi.ipip_add_tunnel(
325 src_address=inet_pton(AF_INET, '1.2.3.4'),
326 dst_address=inet_pton(AF_INET, '2.3.4.5'), is_ipv6=0,
327 table_id=20)
328 sw_if_index = rv.sw_if_index
329 self.vapi.ipip_del_tunnel(sw_if_index)
330
Ole Troan4146c652018-08-08 22:23:19 +0200331 def payload(self, len):
332 return 'x' * len
333
Ole Troan298c6952018-03-08 12:30:43 +0100334
335if __name__ == '__main__':
336 unittest.main(testRunner=VppTestRunner)