blob: 20cc537e43b2c218468f9f48124fa3fb5d5ce07d [file] [log] [blame]
Eyal Baric86e5922017-07-02 18:33:16 +03001#!/usr/bin/env python
2""" L2BD ARP term Test """
3
4import unittest
5import random
6import copy
7
Eyal Bari20197482017-09-13 12:29:08 +03008from socket import AF_INET, AF_INET6
Eyal Bari758137a2017-07-05 14:31:30 +03009
Eyal Baric86e5922017-07-02 18:33:16 +030010from scapy.packet import Raw
11from scapy.layers.l2 import Ether, ARP
12from scapy.layers.inet import IP
Eyal Bari758137a2017-07-05 14:31:30 +030013from scapy.utils import inet_pton, inet_ntop
14from scapy.utils6 import in6_getnsma, in6_getnsmac, in6_ptop, in6_islladdr, \
15 in6_mactoifaceid, in6_ismaddr
16from scapy.layers.inet6 import IPv6, UDP, ICMPv6ND_NS, ICMPv6ND_RS, \
17 ICMPv6ND_RA, ICMPv6NDOptSrcLLAddr, getmacbyip6, ICMPv6MRD_Solicitation, \
18 ICMPv6NDOptMTU, ICMPv6NDOptSrcLLAddr, ICMPv6NDOptPrefixInfo, \
19 ICMPv6ND_NA, ICMPv6NDOptDstLLAddr, ICMPv6DestUnreach, icmp6types
Eyal Baric86e5922017-07-02 18:33:16 +030020
21from framework import VppTestCase, VppTestRunner
22from util import Host, ppp, mactobinary
23
24
25class TestL2bdArpTerm(VppTestCase):
26 """ L2BD arp termination Test Case """
27
28 @classmethod
29 def setUpClass(cls):
30 """
31 Perform standard class setup (defined by class method setUpClass in
32 class VppTestCase) before running the test case, set test case related
33 variables and configure VPP.
34 """
35 super(TestL2bdArpTerm, cls).setUpClass()
36
37 try:
38 # Create pg interfaces
39 n_bd = 1
40 cls.ifs_per_bd = ifs_per_bd = 3
41 n_ifs = n_bd * ifs_per_bd
42 cls.create_pg_interfaces(range(n_ifs))
43
44 # Set up all interfaces
45 for i in cls.pg_interfaces:
46 i.admin_up()
47
48 cls.hosts = set()
49
50 except Exception:
51 super(TestL2bdArpTerm, cls).tearDownClass()
52 raise
53
54 def setUp(self):
55 """
56 Clear trace and packet infos before running each test.
57 """
58 self.reset_packet_infos()
59 super(TestL2bdArpTerm, self).setUp()
60
61 def tearDown(self):
62 """
63 Show various debug prints after each test.
64 """
65 super(TestL2bdArpTerm, self).tearDown()
66 if not self.vpp_dead:
67 self.logger.info(self.vapi.ppcli("show l2fib verbose"))
68 self.logger.info(self.vapi.ppcli("show bridge-domain 1 detail"))
69
Eyal Bari758137a2017-07-05 14:31:30 +030070 def add_del_arp_term_hosts(self, entries, bd_id=1, is_add=1, is_ipv6=0):
Eyal Baric86e5922017-07-02 18:33:16 +030071 for e in entries:
Eyal Bari758137a2017-07-05 14:31:30 +030072 ip = e.ip4n if is_ipv6 == 0 else e.ip6n
Eyal Baric86e5922017-07-02 18:33:16 +030073 self.vapi.bd_ip_mac_add_del(bd_id=bd_id,
74 mac=e.bin_mac,
Eyal Bari758137a2017-07-05 14:31:30 +030075 ip=ip,
76 is_ipv6=is_ipv6,
Eyal Baric86e5922017-07-02 18:33:16 +030077 is_add=is_add)
78
79 @classmethod
80 def mac_list(cls, b6_range):
81 return ["00:00:ca:fe:00:%02x" % b6 for b6 in b6_range]
82
83 @classmethod
84 def ip4_host(cls, subnet, host, mac):
85 return Host(mac=mac,
86 ip4="172.17.1%02u.%u" % (subnet, host))
87
88 @classmethod
89 def ip4_hosts(cls, subnet, start, mac_list):
90 return {cls.ip4_host(subnet, start + j, mac_list[j])
91 for j in range(len(mac_list))}
92
93 @classmethod
Eyal Bari758137a2017-07-05 14:31:30 +030094 def ip6_host(cls, subnet, host, mac):
95 return Host(mac=mac,
96 ip6="fd01:%x::%x" % (subnet, host))
97
98 @classmethod
99 def ip6_hosts(cls, subnet, start, mac_list):
100 return {cls.ip6_host(subnet, start + j, mac_list[j])
101 for j in range(len(mac_list))}
102
103 @classmethod
Eyal Baric86e5922017-07-02 18:33:16 +0300104 def bd_swifs(cls, b):
105 n = cls.ifs_per_bd
106 start = (b - 1) * n
107 return [cls.pg_interfaces[j] for j in range(start, start + n)]
108
109 def bd_add_del(self, bd_id=1, is_add=1):
110 if is_add:
111 self.vapi.bridge_domain_add_del(bd_id=bd_id, is_add=is_add)
112 for swif in self.bd_swifs(bd_id):
113 swif_idx = swif.sw_if_index
114 self.vapi.sw_interface_set_l2_bridge(
115 swif_idx, bd_id=bd_id, enable=is_add)
116 if not is_add:
117 self.vapi.bridge_domain_add_del(bd_id=bd_id, is_add=is_add)
118
119 @classmethod
Eyal Bari20197482017-09-13 12:29:08 +0300120 def arp_req(cls, src_host, host):
Eyal Baric86e5922017-07-02 18:33:16 +0300121 return (Ether(dst="ff:ff:ff:ff:ff:ff", src=src_host.mac) /
122 ARP(op="who-has",
123 hwsrc=src_host.bin_mac,
124 pdst=host.ip4,
125 psrc=src_host.ip4))
126
127 @classmethod
128 def arp_reqs(cls, src_host, entries):
Eyal Bari20197482017-09-13 12:29:08 +0300129 return [cls.arp_req(src_host, e) for e in entries]
130
131 @classmethod
132 def garp_req(cls, host):
133 return cls.arp_req(host, host)
134
135 @classmethod
136 def garp_reqs(cls, entries):
137 return [cls.garp_req(e) for e in entries]
Eyal Baric86e5922017-07-02 18:33:16 +0300138
Eyal Bari758137a2017-07-05 14:31:30 +0300139 def arp_resp_host(self, src_host, arp_resp):
Eyal Baric86e5922017-07-02 18:33:16 +0300140 ether = arp_resp[Ether]
141 self.assertEqual(ether.dst, src_host.mac)
142
143 arp = arp_resp[ARP]
144 self.assertEqual(arp.hwtype, 1)
145 self.assertEqual(arp.ptype, 0x800)
146 self.assertEqual(arp.hwlen, 6)
147 self.assertEqual(arp.plen, 4)
148 arp_opts = {"who-has": 1, "is-at": 2}
149 self.assertEqual(arp.op, arp_opts["is-at"])
150 self.assertEqual(arp.hwdst, src_host.mac)
151 self.assertEqual(arp.pdst, src_host.ip4)
Eyal Bari758137a2017-07-05 14:31:30 +0300152 return Host(mac=arp.hwsrc, ip4=arp.psrc)
Eyal Baric86e5922017-07-02 18:33:16 +0300153
154 def arp_resp_hosts(self, src_host, pkts):
Eyal Bari758137a2017-07-05 14:31:30 +0300155 return {self.arp_resp_host(src_host, p) for p in pkts}
156
Eyal Bari20197482017-09-13 12:29:08 +0300157 def inttoip4(self, ip):
158 o1 = int(ip / 16777216) % 256
159 o2 = int(ip / 65536) % 256
160 o3 = int(ip / 256) % 256
161 o4 = int(ip) % 256
162 return '%(o1)s.%(o2)s.%(o3)s.%(o4)s' % locals()
163
164 def arp_event_host(self, e):
165 return Host(mac=':'.join(['%02x' % ord(char) for char in e.new_mac]),
166 ip4=self.inttoip4(e.address))
167
168 def arp_event_hosts(self, evs):
169 return {self.arp_event_host(e) for e in evs}
170
Eyal Baric125ecc2017-09-20 11:29:17 +0300171 def nd_event_host(self, e):
172 return Host(mac=':'.join(['%02x' % ord(char) for char in e.new_mac]),
173 ip6=inet_ntop(AF_INET6, e.address))
174
175 def nd_event_hosts(self, evs):
176 return {self.nd_event_host(e) for e in evs}
177
Eyal Bari758137a2017-07-05 14:31:30 +0300178 @classmethod
179 def ns_req(cls, src_host, host):
180 nsma = in6_getnsma(inet_pton(AF_INET6, "fd10::ffff"))
181 d = inet_ntop(AF_INET6, nsma)
182 return (Ether(dst="ff:ff:ff:ff:ff:ff", src=src_host.mac) /
183 IPv6(dst=d, src=src_host.ip6) /
184 ICMPv6ND_NS(tgt=host.ip6) /
185 ICMPv6NDOptSrcLLAddr(lladdr=src_host.mac))
186
187 @classmethod
Eyal Baric125ecc2017-09-20 11:29:17 +0300188 def ns_reqs_dst(cls, entries, dst_host):
189 return [cls.ns_req(e, dst_host) for e in entries]
190
191 @classmethod
192 def ns_reqs_src(cls, src_host, entries):
Eyal Bari758137a2017-07-05 14:31:30 +0300193 return [cls.ns_req(src_host, e) for e in entries]
194
195 def na_resp_host(self, src_host, rx):
196 self.assertEqual(rx[Ether].dst, src_host.mac)
197 self.assertEqual(in6_ptop(rx[IPv6].dst),
198 in6_ptop(src_host.ip6))
199
200 self.assertTrue(rx.haslayer(ICMPv6ND_NA))
201 self.assertTrue(rx.haslayer(ICMPv6NDOptDstLLAddr))
202
203 na = rx[ICMPv6ND_NA]
204 return Host(mac=na.lladdr, ip6=na.tgt)
205
206 def na_resp_hosts(self, src_host, pkts):
207 return {self.na_resp_host(src_host, p) for p in pkts}
Eyal Baric86e5922017-07-02 18:33:16 +0300208
209 def set_bd_flags(self, bd_id, **args):
210 """
211 Enable/disable defined feature(s) of the bridge domain.
212
213 :param int bd_id: Bridge domain ID.
214 :param list args: List of feature/status pairs. Allowed features: \
215 learn, forward, flood, uu_flood and arp_term. Status False means \
216 disable, status True means enable the feature.
217 :raise: ValueError in case of unknown feature in the input.
218 """
219 for flag in args:
220 if flag == "learn":
221 feature_bitmap = 1 << 0
222 elif flag == "forward":
223 feature_bitmap = 1 << 1
224 elif flag == "flood":
225 feature_bitmap = 1 << 2
226 elif flag == "uu_flood":
227 feature_bitmap = 1 << 3
228 elif flag == "arp_term":
229 feature_bitmap = 1 << 4
230 else:
231 raise ValueError("Unknown feature used: %s" % flag)
232 is_set = 1 if args[flag] else 0
233 self.vapi.bridge_flags(bd_id, is_set, feature_bitmap)
234 self.logger.info("Bridge domain ID %d updated" % bd_id)
235
236 def verify_arp(self, src_host, req_hosts, resp_hosts, bd_id=1):
237 reqs = self.arp_reqs(src_host, req_hosts)
238
239 for swif in self.bd_swifs(bd_id):
240 swif.add_stream(reqs)
241
242 self.pg_enable_capture(self.pg_interfaces)
243 self.pg_start()
244
245 for swif in self.bd_swifs(bd_id):
246 resp_pkts = swif.get_capture(len(resp_hosts))
247 resps = self.arp_resp_hosts(src_host, resp_pkts)
248 self.assertEqual(len(resps ^ resp_hosts), 0)
249
Eyal Bari758137a2017-07-05 14:31:30 +0300250 def verify_nd(self, src_host, req_hosts, resp_hosts, bd_id=1):
Eyal Baric125ecc2017-09-20 11:29:17 +0300251 reqs = self.ns_reqs_src(src_host, req_hosts)
Eyal Bari758137a2017-07-05 14:31:30 +0300252
253 for swif in self.bd_swifs(bd_id):
254 swif.add_stream(reqs)
255
256 self.pg_enable_capture(self.pg_interfaces)
257 self.pg_start()
258
259 for swif in self.bd_swifs(bd_id):
260 resp_pkts = swif.get_capture(len(resp_hosts))
261 resps = self.na_resp_hosts(src_host, resp_pkts)
262 self.assertEqual(len(resps ^ resp_hosts), 0)
263
Eyal Baric86e5922017-07-02 18:33:16 +0300264 def test_l2bd_arp_term_01(self):
265 """ L2BD arp term - add 5 hosts, verify arp responses
266 """
267 src_host = self.ip4_host(50, 50, "00:00:11:22:33:44")
268 self.bd_add_del(1, is_add=1)
269 self.set_bd_flags(1, arp_term=True, flood=False,
270 uu_flood=False, learn=False)
271 macs = self.mac_list(range(1, 5))
272 hosts = self.ip4_hosts(4, 1, macs)
273 self.add_del_arp_term_hosts(hosts, is_add=1)
274 self.verify_arp(src_host, hosts, hosts)
275 type(self).hosts = hosts
276
277 def test_l2bd_arp_term_02(self):
278 """ L2BD arp term - delete 3 hosts, verify arp responses
279 """
280 src_host = self.ip4_host(50, 50, "00:00:11:22:33:44")
281 macs = self.mac_list(range(1, 3))
282 deleted = self.ip4_hosts(4, 1, macs)
283 self.add_del_arp_term_hosts(deleted, is_add=0)
284 remaining = self.hosts - deleted
285 self.verify_arp(src_host, self.hosts, remaining)
286 type(self).hosts = remaining
287 self.bd_add_del(1, is_add=0)
288
289 def test_l2bd_arp_term_03(self):
290 """ L2BD arp term - recreate BD1, readd 3 hosts, verify arp responses
291 """
292 src_host = self.ip4_host(50, 50, "00:00:11:22:33:44")
293 self.bd_add_del(1, is_add=1)
294 self.set_bd_flags(1, arp_term=True, flood=False,
295 uu_flood=False, learn=False)
296 macs = self.mac_list(range(1, 3))
297 readded = self.ip4_hosts(4, 1, macs)
298 self.add_del_arp_term_hosts(readded, is_add=1)
299 self.verify_arp(src_host, self.hosts | readded, readded)
300 type(self).hosts = readded
301
302 def test_l2bd_arp_term_04(self):
303 """ L2BD arp term - 2 IP4 addrs per host
304 """
305 src_host = self.ip4_host(50, 50, "00:00:11:22:33:44")
306 macs = self.mac_list(range(1, 3))
307 sub5_hosts = self.ip4_hosts(5, 1, macs)
308 self.add_del_arp_term_hosts(sub5_hosts, is_add=1)
309 hosts = self.hosts | sub5_hosts
310 self.verify_arp(src_host, hosts, hosts)
311 type(self).hosts = hosts
312 self.bd_add_del(1, is_add=0)
313
314 def test_l2bd_arp_term_05(self):
315 """ L2BD arp term - create and update 10 IP4-mac pairs
316 """
317 src_host = self.ip4_host(50, 50, "00:00:11:22:33:44")
318 self.bd_add_del(1, is_add=1)
319 self.set_bd_flags(1, arp_term=True, flood=False,
320 uu_flood=False, learn=False)
321 macs1 = self.mac_list(range(10, 20))
322 hosts1 = self.ip4_hosts(5, 1, macs1)
323 self.add_del_arp_term_hosts(hosts1, is_add=1)
324 self.verify_arp(src_host, hosts1, hosts1)
325 macs2 = self.mac_list(range(20, 30))
326 hosts2 = self.ip4_hosts(5, 1, macs2)
327 self.add_del_arp_term_hosts(hosts2, is_add=1)
328 self.verify_arp(src_host, hosts1, hosts2)
329 self.bd_add_del(1, is_add=0)
330
Eyal Bari758137a2017-07-05 14:31:30 +0300331 def test_l2bd_arp_term_06(self):
332 """ L2BD arp/ND term - hosts with both ip4/ip6
333 """
334 src_host4 = self.ip4_host(50, 50, "00:00:11:22:33:44")
335 src_host6 = self.ip6_host(50, 50, "00:00:11:22:33:44")
336 self.bd_add_del(1, is_add=1)
337 # enable flood to make sure requests are not flooded
338 self.set_bd_flags(1, arp_term=True, flood=True,
339 uu_flood=False, learn=False)
340 macs = self.mac_list(range(10, 20))
341 hosts6 = self.ip6_hosts(5, 1, macs)
342 hosts4 = self.ip4_hosts(5, 1, macs)
343 self.add_del_arp_term_hosts(hosts4, is_add=1)
344 self.add_del_arp_term_hosts(hosts6, is_add=1, is_ipv6=1)
345 self.verify_arp(src_host4, hosts4, hosts4)
346 self.verify_nd(src_host6, hosts6, hosts6)
347 self.bd_add_del(1, is_add=0)
348
349 def test_l2bd_arp_term_07(self):
350 """ L2BD ND term - Add and Del hosts, verify ND replies
351 """
352 src_host6 = self.ip6_host(50, 50, "00:00:11:22:33:44")
353 self.bd_add_del(1, is_add=1)
354 self.set_bd_flags(1, arp_term=True, flood=False,
355 uu_flood=False, learn=False)
356 macs = self.mac_list(range(10, 20))
357 hosts6 = self.ip6_hosts(5, 1, macs)
358 self.add_del_arp_term_hosts(hosts6, is_add=1, is_ipv6=1)
359 self.verify_nd(src_host6, hosts6, hosts6)
360 del_macs = self.mac_list(range(10, 15))
361 deleted = self.ip6_hosts(5, 1, del_macs)
362 self.add_del_arp_term_hosts(deleted, is_add=0, is_ipv6=1)
363 self.verify_nd(src_host6, hosts6, hosts6 - deleted)
364 self.bd_add_del(1, is_add=0)
365
366 def test_l2bd_arp_term_08(self):
367 """ L2BD ND term - Add and update IP+mac, verify ND replies
368 """
369 src_host = self.ip6_host(50, 50, "00:00:11:22:33:44")
370 self.bd_add_del(1, is_add=1)
371 self.set_bd_flags(1, arp_term=True, flood=False,
372 uu_flood=False, learn=False)
373 macs1 = self.mac_list(range(10, 20))
374 hosts = self.ip6_hosts(5, 1, macs1)
375 self.add_del_arp_term_hosts(hosts, is_add=1, is_ipv6=1)
376 self.verify_nd(src_host, hosts, hosts)
377 macs2 = self.mac_list(range(20, 30))
378 updated = self.ip6_hosts(5, 1, macs2)
379 self.add_del_arp_term_hosts(updated, is_add=1, is_ipv6=1)
380 self.verify_nd(src_host, hosts, updated)
381 self.bd_add_del(1, is_add=0)
382
Eyal Bari20197482017-09-13 12:29:08 +0300383 def test_l2bd_arp_term_09(self):
384 """ L2BD arp term - send garps, verify arp event reports
385 """
386 self.vapi.want_ip4_arp_events()
387 self.bd_add_del(1, is_add=1)
388 self.set_bd_flags(1, arp_term=True, flood=False,
389 uu_flood=False, learn=False)
390 macs = self.mac_list(range(90, 95))
391 hosts = self.ip4_hosts(5, 1, macs)
392
393 garps = self.garp_reqs(hosts)
394 self.bd_swifs(1)[0].add_stream(garps)
395
396 self.pg_enable_capture(self.pg_interfaces)
397 self.pg_start()
398 evs = [self.vapi.wait_for_event(1, "ip4_arp_event")
399 for i in range(len(hosts))]
400 ev_hosts = self.arp_event_hosts(evs)
401 self.assertEqual(len(ev_hosts ^ hosts), 0)
402
403 def test_l2bd_arp_term_10(self):
404 """ L2BD arp term - send duplicate garps, verify suppression
405 """
406 macs = self.mac_list(range(70, 71))
407 hosts = self.ip4_hosts(6, 1, macs)
408
409 """ send the packet 5 times expect one event
410 """
411 garps = self.garp_reqs(hosts) * 5
412 self.bd_swifs(1)[0].add_stream(garps)
413
414 self.pg_enable_capture(self.pg_interfaces)
415 self.pg_start()
416 evs = [self.vapi.wait_for_event(1, "ip4_arp_event")
417 for i in range(len(hosts))]
418 ev_hosts = self.arp_event_hosts(evs)
419 self.assertEqual(len(ev_hosts ^ hosts), 0)
420
421 def test_l2bd_arp_term_11(self):
422 """ L2BD arp term - disable ip4 arp events,send garps, verify no events
423 """
424 self.vapi.want_ip4_arp_events(enable_disable=0)
425 macs = self.mac_list(range(90, 95))
426 hosts = self.ip4_hosts(5, 1, macs)
427
428 garps = self.garp_reqs(hosts)
429 self.bd_swifs(1)[0].add_stream(garps)
430
431 self.pg_enable_capture(self.pg_interfaces)
432 self.pg_start()
433 self.sleep(1)
434 self.assertEqual(len(self.vapi.collect_events()), 0)
435 self.bd_add_del(1, is_add=0)
436
Eyal Baric125ecc2017-09-20 11:29:17 +0300437 def test_l2bd_arp_term_12(self):
438 """ L2BD ND term - send NS packets verify reports
439 """
440 self.vapi.want_ip6_nd_events(address=inet_pton(AF_INET6, "::0"))
441 dst_host = self.ip6_host(50, 50, "00:00:11:22:33:44")
442 self.bd_add_del(1, is_add=1)
443 self.set_bd_flags(1, arp_term=True, flood=False,
444 uu_flood=False, learn=False)
445 macs = self.mac_list(range(10, 15))
446 hosts = self.ip6_hosts(5, 1, macs)
447 reqs = self.ns_reqs_dst(hosts, dst_host)
448 self.bd_swifs(1)[0].add_stream(reqs)
449
450 self.pg_enable_capture(self.pg_interfaces)
451 self.pg_start()
452 evs = [self.vapi.wait_for_event(2, "ip6_nd_event")
453 for i in range(len(hosts))]
454 ev_hosts = self.nd_event_hosts(evs)
455 self.assertEqual(len(ev_hosts ^ hosts), 0)
456
457 def test_l2bd_arp_term_13(self):
458 """ L2BD ND term - send duplicate ns, verify suppression
459 """
460 dst_host = self.ip6_host(50, 50, "00:00:11:22:33:44")
461 macs = self.mac_list(range(10, 11))
462 hosts = self.ip6_hosts(5, 1, macs)
463 reqs = self.ns_reqs_dst(hosts, dst_host) * 5
464 self.bd_swifs(1)[0].add_stream(reqs)
465
466 self.pg_enable_capture(self.pg_interfaces)
467 self.pg_start()
468 evs = [self.vapi.wait_for_event(2, "ip6_nd_event")
469 for i in range(len(hosts))]
470 ev_hosts = self.nd_event_hosts(evs)
471 self.assertEqual(len(ev_hosts ^ hosts), 0)
472
473 def test_l2bd_arp_term_14(self):
474 """ L2BD ND term - disable ip4 arp events,send ns, verify no events
475 """
476 self.vapi.want_ip6_nd_events(enable_disable=0,
477 address=inet_pton(AF_INET6, "::0"))
478 dst_host = self.ip6_host(50, 50, "00:00:11:22:33:44")
479 macs = self.mac_list(range(10, 15))
480 hosts = self.ip6_hosts(5, 1, macs)
481 reqs = self.ns_reqs_dst(hosts, dst_host)
482 self.bd_swifs(1)[0].add_stream(reqs)
483
484 self.pg_enable_capture(self.pg_interfaces)
485 self.pg_start()
486 self.sleep(1)
487 self.assertEqual(len(self.vapi.collect_events()), 0)
488 self.bd_add_del(1, is_add=0)
489
Eyal Baric86e5922017-07-02 18:33:16 +0300490
491if __name__ == '__main__':
492 unittest.main(testRunner=VppTestRunner)