4 from socket import AF_INET, AF_INET6, inet_pton
6 from framework import VppTestCase, VppTestRunner
7 from vpp_neighbor import VppNeighbor, find_nbr
9 from scapy.packet import Raw
10 from scapy.layers.l2 import Ether, ARP
11 from scapy.layers.inet import IP, UDP
13 # not exported by scapy, so redefined here
14 arp_opts = {"who-has": 1, "is-at": 2}
17 class ARPTestCase(VppTestCase):
21 super(ARPTestCase, self).setUp()
23 # create 3 pg interfaces
24 self.create_pg_interfaces(range(4))
26 # pg0 configured with ip4 and 6 addresses used for input
27 # pg1 configured with ip4 and 6 addresses used for output
28 # pg2 is unnumbered to pg0
29 for i in self.pg_interfaces:
34 self.pg0.resolve_arp()
39 # pg3 in a different VRF
40 self.pg3.set_table_ip4(1)
43 def verify_arp_req(self, rx, smac, sip, dip):
45 self.assertEqual(ether.dst, "ff:ff:ff:ff:ff:ff")
46 self.assertEqual(ether.src, smac)
49 self.assertEqual(arp.hwtype, 1)
50 self.assertEqual(arp.ptype, 0x800)
51 self.assertEqual(arp.hwlen, 6)
52 self.assertEqual(arp.plen, 4)
53 self.assertEqual(arp.op, arp_opts["who-has"])
54 self.assertEqual(arp.hwsrc, smac)
55 self.assertEqual(arp.hwdst, "00:00:00:00:00:00")
56 self.assertEqual(arp.psrc, sip)
57 self.assertEqual(arp.pdst, dip)
59 def verify_arp_resp(self, rx, smac, dmac, sip, dip):
61 self.assertEqual(ether.dst, dmac)
62 self.assertEqual(ether.src, smac)
65 self.assertEqual(arp.hwtype, 1)
66 self.assertEqual(arp.ptype, 0x800)
67 self.assertEqual(arp.hwlen, 6)
68 self.assertEqual(arp.plen, 4)
69 self.assertEqual(arp.op, arp_opts["is-at"])
70 self.assertEqual(arp.hwsrc, smac)
71 self.assertEqual(arp.hwdst, dmac)
72 self.assertEqual(arp.psrc, sip)
73 self.assertEqual(arp.pdst, dip)
75 def verify_ip(self, rx, smac, dmac, sip, dip):
77 self.assertEqual(ether.dst, dmac)
78 self.assertEqual(ether.src, smac)
81 self.assertEqual(ip.src, sip)
82 self.assertEqual(ip.dst, dip)
84 def send_and_assert_no_replies(self, intf, pkts, remark):
86 self.pg_enable_capture(self.pg_interfaces)
88 for i in self.pg_interfaces:
89 i.assert_nothing_captured(remark=remark)
95 # Generate some hosts on the LAN
97 self.pg1.generate_remote_hosts(4)
100 # Send IP traffic to one of these unresolved hosts.
101 # expect the generation of an ARP request
103 p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
104 IP(src=self.pg0.remote_ip4, dst=self.pg1._remote_hosts[1].ip4) /
105 UDP(sport=1234, dport=1234) /
108 self.pg0.add_stream(p)
109 self.pg_enable_capture(self.pg_interfaces)
112 rx = self.pg1.get_capture(1)
114 self.verify_arp_req(rx[0],
117 self.pg1._remote_hosts[1].ip4)
120 # And a dynamic ARP entry for host 1
122 dyn_arp = VppNeighbor(self,
123 self.pg1.sw_if_index,
124 self.pg1.remote_hosts[1].mac,
125 self.pg1.remote_hosts[1].ip4)
126 dyn_arp.add_vpp_config()
129 # now we expect IP traffic forwarded
131 dyn_p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
132 IP(src=self.pg0.remote_ip4,
133 dst=self.pg1._remote_hosts[1].ip4) /
134 UDP(sport=1234, dport=1234) /
137 self.pg0.add_stream(dyn_p)
138 self.pg_enable_capture(self.pg_interfaces)
141 rx = self.pg1.get_capture(1)
143 self.verify_ip(rx[0],
145 self.pg1.remote_hosts[1].mac,
147 self.pg1._remote_hosts[1].ip4)
150 # And a Static ARP entry for host 2
152 static_arp = VppNeighbor(self,
153 self.pg1.sw_if_index,
154 self.pg1.remote_hosts[2].mac,
155 self.pg1.remote_hosts[2].ip4,
157 static_arp.add_vpp_config()
159 static_p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
160 IP(src=self.pg0.remote_ip4,
161 dst=self.pg1._remote_hosts[2].ip4) /
162 UDP(sport=1234, dport=1234) /
165 self.pg0.add_stream(static_p)
166 self.pg_enable_capture(self.pg_interfaces)
169 rx = self.pg1.get_capture(1)
171 self.verify_ip(rx[0],
173 self.pg1.remote_hosts[2].mac,
175 self.pg1._remote_hosts[2].ip4)
178 # flap the link. dynamic ARPs get flush, statics don't
180 self.pg1.admin_down()
183 self.pg0.add_stream(static_p)
184 self.pg_enable_capture(self.pg_interfaces)
186 rx = self.pg1.get_capture(1)
188 self.verify_ip(rx[0],
190 self.pg1.remote_hosts[2].mac,
192 self.pg1._remote_hosts[2].ip4)
194 self.pg0.add_stream(dyn_p)
195 self.pg_enable_capture(self.pg_interfaces)
198 rx = self.pg1.get_capture(1)
199 self.verify_arp_req(rx[0],
202 self.pg1._remote_hosts[1].ip4)
205 # Send an ARP request from one of the so-far unlearned remote hosts
207 p = (Ether(dst="ff:ff:ff:ff:ff:ff",
208 src=self.pg1._remote_hosts[3].mac) /
210 hwsrc=self.pg1._remote_hosts[3].mac,
211 pdst=self.pg1.local_ip4,
212 psrc=self.pg1._remote_hosts[3].ip4))
214 self.pg1.add_stream(p)
215 self.pg_enable_capture(self.pg_interfaces)
218 rx = self.pg1.get_capture(1)
219 self.verify_arp_resp(rx[0],
221 self.pg1._remote_hosts[3].mac,
223 self.pg1._remote_hosts[3].ip4)
226 # VPP should have learned the mapping for the remote host
228 self.assertTrue(find_nbr(self,
229 self.pg1.sw_if_index,
230 self.pg1._remote_hosts[3].ip4))
234 # 1 - don't respond to ARP request for address not within the
235 # interface's sub-net
237 p = (Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg0.remote_mac) /
239 hwsrc=self.pg0.remote_mac,
241 psrc=self.pg0.remote_ip4))
242 self.send_and_assert_no_replies(self.pg0, p,
243 "ARP req for non-local destination")
246 # 2 - don't respond to ARP request from an address not within the
247 # interface's sub-net
249 p = (Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg0.remote_mac) /
251 hwsrc=self.pg0.remote_mac,
253 pdst=self.pg0.local_ip4))
254 self.send_and_assert_no_replies(self.pg0, p,
255 "ARP req for non-local source")
258 # 3 - don't respond to ARP request from an address that belongs to
261 p = (Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg0.remote_mac) /
263 hwsrc=self.pg0.remote_mac,
264 psrc=self.pg0.local_ip4,
265 pdst=self.pg0.local_ip4))
266 self.send_and_assert_no_replies(self.pg0, p,
267 "ARP req for non-local source")
270 # 4 - don't respond to ARP requests that has mac source different
271 # from ARP request HW source
274 p = (Ether(dst="ff:ff:ff:ff:ff:ff", src=self.pg0.remote_mac) /
276 hwsrc="00:00:00:DE:AD:BE",
277 psrc=self.pg0.remote_ip4,
278 pdst=self.pg0.local_ip4))
279 self.send_and_assert_no_replies(self.pg0, p,
280 "ARP req for non-local source")
285 dyn_arp.remove_vpp_config()
286 static_arp.remove_vpp_config()
288 def test_proxy_arp(self):
292 # Proxy ARP rewquest packets for each interface
294 arp_req_pg2 = (Ether(src=self.pg2.remote_mac,
295 dst="ff:ff:ff:ff:ff:ff") /
297 hwsrc=self.pg2.remote_mac,
299 psrc=self.pg1.remote_ip4))
300 arp_req_pg0 = (Ether(src=self.pg0.remote_mac,
301 dst="ff:ff:ff:ff:ff:ff") /
303 hwsrc=self.pg0.remote_mac,
305 psrc=self.pg0.remote_ip4))
306 arp_req_pg1 = (Ether(src=self.pg1.remote_mac,
307 dst="ff:ff:ff:ff:ff:ff") /
309 hwsrc=self.pg1.remote_mac,
311 psrc=self.pg1.remote_ip4))
312 arp_req_pg3 = (Ether(src=self.pg3.remote_mac,
313 dst="ff:ff:ff:ff:ff:ff") /
315 hwsrc=self.pg3.remote_mac,
317 psrc=self.pg3.remote_ip4))
320 # Configure Proxy ARP for 10.10.10.0 -> 10.10.10.124
322 self.vapi.proxy_arp_add_del(inet_pton(AF_INET, "10.10.10.2"),
323 inet_pton(AF_INET, "10.10.10.124"))
326 # No responses are sent when the interfaces are not enabled for proxy
329 self.send_and_assert_no_replies(self.pg0, arp_req_pg0,
330 "ARP req from unconfigured interface")
331 self.send_and_assert_no_replies(self.pg2, arp_req_pg2,
332 "ARP req from unconfigured interface")
335 # Make pg2 un-numbered to pg1
338 self.pg2.set_unnumbered(self.pg1.sw_if_index)
340 self.send_and_assert_no_replies(self.pg2, arp_req_pg2,
341 "ARP req from unnumbered interface")
344 # Enable each interface to reply to proxy ARPs
346 for i in self.pg_interfaces:
350 # Now each of the interfaces should reply to a request to a proxied
353 self.pg0.add_stream(arp_req_pg0)
354 self.pg_enable_capture(self.pg_interfaces)
357 rx = self.pg0.get_capture(1)
358 self.verify_arp_resp(rx[0],
364 self.pg1.add_stream(arp_req_pg1)
365 self.pg_enable_capture(self.pg_interfaces)
368 rx = self.pg1.get_capture(1)
369 self.verify_arp_resp(rx[0],
375 self.pg2.add_stream(arp_req_pg2)
376 self.pg_enable_capture(self.pg_interfaces)
379 rx = self.pg2.get_capture(1)
380 self.verify_arp_resp(rx[0],
387 # A request for an address out of the configured range
389 arp_req_pg1_hi = (Ether(src=self.pg1.remote_mac,
390 dst="ff:ff:ff:ff:ff:ff") /
392 hwsrc=self.pg1.remote_mac,
394 psrc=self.pg1.remote_ip4))
395 self.send_and_assert_no_replies(self.pg1, arp_req_pg1_hi,
396 "ARP req out of range HI")
397 arp_req_pg1_low = (Ether(src=self.pg1.remote_mac,
398 dst="ff:ff:ff:ff:ff:ff") /
400 hwsrc=self.pg1.remote_mac,
402 psrc=self.pg1.remote_ip4))
403 self.send_and_assert_no_replies(self.pg1, arp_req_pg1_low,
404 "ARP req out of range Low")
407 # Request for an address in the proxy range but from an interface
410 self.send_and_assert_no_replies(self.pg3, arp_req_pg3,
411 "ARP req from different VRF")
414 # Disable Each interface for proxy ARP
415 # - expect none to respond
417 for i in self.pg_interfaces:
420 self.send_and_assert_no_replies(self.pg0, arp_req_pg0,
421 "ARP req from disable")
422 self.send_and_assert_no_replies(self.pg1, arp_req_pg1,
423 "ARP req from disable")
424 self.send_and_assert_no_replies(self.pg2, arp_req_pg2,
425 "ARP req from disable")