7 from framework import VppTestCase, VppTestRunner, running_extended_tests
8 from vpp_neighbor import VppNeighbor
9 from vpp_ip_route import find_route, VppIpTable
10 from util import mk_ll_addr
12 from scapy.layers.l2 import Ether, getmacbyip, ARP
13 from scapy.layers.inet import IP, UDP, ICMP
14 from scapy.layers.inet6 import IPv6, in6_getnsmac
15 from scapy.utils6 import in6_mactoifaceid
16 from scapy.layers.dhcp import DHCP, BOOTP, DHCPTypes
17 from scapy.layers.dhcp6 import DHCP6, DHCP6_Solicit, DHCP6_RelayForward, \
18 DHCP6_RelayReply, DHCP6_Advertise, DHCP6OptRelayMsg, DHCP6OptIfaceId, \
19 DHCP6OptStatusCode, DHCP6OptVSS, DHCP6OptClientLinkLayerAddr, DHCP6_Request
20 from socket import AF_INET, AF_INET6
21 from scapy.utils import inet_pton, inet_ntop
22 from scapy.utils6 import in6_ptop
23 from vpp_papi import mac_pton
25 DHCP4_CLIENT_PORT = 68
26 DHCP4_SERVER_PORT = 67
27 DHCP6_CLIENT_PORT = 547
28 DHCP6_SERVER_PORT = 546
31 class TestDHCP(VppTestCase):
32 """ DHCP Test Case """
36 super(TestDHCP, cls).setUpClass()
39 def tearDownClass(cls):
40 super(TestDHCP, cls).tearDownClass()
43 super(TestDHCP, self).setUp()
45 # create 6 pg interfaces for pg0 to pg5
46 self.create_pg_interfaces(range(6))
49 # pg0 to 2 are IP configured in VRF 0, 1 and 2.
50 # pg3 to 5 are non IP-configured in VRF 0, 1 and 2.
52 for table_id in range(1, 4):
53 tbl4 = VppIpTable(self, table_id)
55 self.tables.append(tbl4)
56 tbl6 = VppIpTable(self, table_id, is_ip6=1)
58 self.tables.append(tbl6)
61 for i in self.pg_interfaces[:3]:
63 i.set_table_ip4(table_id)
64 i.set_table_ip6(table_id)
72 for i in self.pg_interfaces[3:]:
74 i.set_table_ip4(table_id)
75 i.set_table_ip6(table_id)
79 for i in self.pg_interfaces[:3]:
83 for i in self.pg_interfaces:
87 super(TestDHCP, self).tearDown()
89 def verify_dhcp_has_option(self, pkt, option, value):
93 for i in dhcp.options:
96 self.assertEqual(i[1], value)
99 self.assertTrue(found)
101 def validate_relay_options(self, pkt, intf, ip_addr, vpn_id, fib_id, oui):
107 for i in dhcp.options:
109 if i[0] == "relay_agent_Information":
111 # There are two sb-options present - each of length 6.
115 self.assertEqual(len(data), 24)
116 elif len(vpn_id) > 0:
117 self.assertEqual(len(data), len(vpn_id)+17)
119 self.assertEqual(len(data), 12)
122 # First sub-option is ID 1, len 4, then encoded
123 # sw_if_index. This test uses low valued indicies
125 # The ID space is VPP internal - so no matching value
128 self.assertEqual(ord(data[0]), 1)
129 self.assertEqual(ord(data[1]), 4)
130 self.assertEqual(ord(data[2]), 0)
131 self.assertEqual(ord(data[3]), 0)
132 self.assertEqual(ord(data[4]), 0)
133 self.assertEqual(ord(data[5]), intf._sw_if_index)
136 # next sub-option is the IP address of the client side
138 # sub-option ID=5, length (of a v4 address)=4
140 claddr = socket.inet_pton(AF_INET, ip_addr)
142 self.assertEqual(ord(data[6]), 5)
143 self.assertEqual(ord(data[7]), 4)
144 self.assertEqual(data[8], claddr[0])
145 self.assertEqual(data[9], claddr[1])
146 self.assertEqual(data[10], claddr[2])
147 self.assertEqual(data[11], claddr[3])
150 # sub-option 151 encodes vss_type 1,
151 # the 3 byte oui and the 4 byte fib_id
152 self.assertEqual(id_len, 0)
153 self.assertEqual(ord(data[12]), 151)
154 self.assertEqual(ord(data[13]), 8)
155 self.assertEqual(ord(data[14]), 1)
156 self.assertEqual(ord(data[15]), 0)
157 self.assertEqual(ord(data[16]), 0)
158 self.assertEqual(ord(data[17]), oui)
159 self.assertEqual(ord(data[18]), 0)
160 self.assertEqual(ord(data[19]), 0)
161 self.assertEqual(ord(data[20]), 0)
162 self.assertEqual(ord(data[21]), fib_id)
164 # VSS control sub-option
165 self.assertEqual(ord(data[22]), 152)
166 self.assertEqual(ord(data[23]), 0)
169 # sub-option 151 encode vss_type of 0
170 # followerd by vpn_id in ascii
171 self.assertEqual(oui, 0)
172 self.assertEqual(ord(data[12]), 151)
173 self.assertEqual(ord(data[13]), id_len+1)
174 self.assertEqual(ord(data[14]), 0)
175 self.assertEqual(data[15:15+id_len], vpn_id)
177 # VSS control sub-option
178 self.assertEqual(ord(data[15+len(vpn_id)]), 152)
179 self.assertEqual(ord(data[16+len(vpn_id)]), 0)
182 self.assertTrue(found)
186 def verify_dhcp_msg_type(self, pkt, name):
189 for o in dhcp.options:
191 if o[0] == "message-type" \
192 and DHCPTypes[o[1]] == name:
194 self.assertTrue(found)
196 def verify_dhcp_offer(self, pkt, intf, vpn_id="", fib_id=0, oui=0):
198 self.assertEqual(ether.dst, "ff:ff:ff:ff:ff:ff")
199 self.assertEqual(ether.src, intf.local_mac)
202 self.assertEqual(ip.dst, "255.255.255.255")
203 self.assertEqual(ip.src, intf.local_ip4)
206 self.assertEqual(udp.dport, DHCP4_CLIENT_PORT)
207 self.assertEqual(udp.sport, DHCP4_SERVER_PORT)
209 self.verify_dhcp_msg_type(pkt, "offer")
210 data = self.validate_relay_options(pkt, intf, intf.local_ip4,
213 def verify_orig_dhcp_pkt(self, pkt, intf):
215 self.assertEqual(ether.dst, "ff:ff:ff:ff:ff:ff")
216 self.assertEqual(ether.src, intf.local_mac)
219 self.assertEqual(ip.dst, "255.255.255.255")
220 self.assertEqual(ip.src, "0.0.0.0")
223 self.assertEqual(udp.dport, DHCP4_SERVER_PORT)
224 self.assertEqual(udp.sport, DHCP4_CLIENT_PORT)
226 def verify_orig_dhcp_discover(self, pkt, intf, hostname, client_id=None,
228 self.verify_orig_dhcp_pkt(pkt, intf)
230 self.verify_dhcp_msg_type(pkt, "discover")
231 self.verify_dhcp_has_option(pkt, "hostname", hostname)
233 self.verify_dhcp_has_option(pkt, "client_id", client_id)
235 self.assertEqual(bootp.ciaddr, "0.0.0.0")
236 self.assertEqual(bootp.giaddr, "0.0.0.0")
238 self.assertEqual(bootp.flags, 0x8000)
240 self.assertEqual(bootp.flags, 0x0000)
242 def verify_orig_dhcp_request(self, pkt, intf, hostname, ip,
244 self.verify_orig_dhcp_pkt(pkt, intf)
246 self.verify_dhcp_msg_type(pkt, "request")
247 self.verify_dhcp_has_option(pkt, "hostname", hostname)
248 self.verify_dhcp_has_option(pkt, "requested_addr", ip)
250 self.assertEqual(bootp.ciaddr, "0.0.0.0")
251 self.assertEqual(bootp.giaddr, "0.0.0.0")
253 self.assertEqual(bootp.flags, 0x8000)
255 self.assertEqual(bootp.flags, 0x0000)
257 def verify_relayed_dhcp_discover(self, pkt, intf, src_intf=None,
260 dst_mac=None, dst_ip=None):
262 dst_mac = intf.remote_mac
264 dst_ip = intf.remote_ip4
267 self.assertEqual(ether.dst, dst_mac)
268 self.assertEqual(ether.src, intf.local_mac)
271 self.assertEqual(ip.dst, dst_ip)
272 self.assertEqual(ip.src, intf.local_ip4)
275 self.assertEqual(udp.dport, DHCP4_SERVER_PORT)
276 self.assertEqual(udp.sport, DHCP4_CLIENT_PORT)
281 for o in dhcp.options:
283 if o[0] == "message-type" \
284 and DHCPTypes[o[1]] == "discover":
286 self.assertTrue(is_discover)
288 data = self.validate_relay_options(pkt, src_intf,
294 def verify_dhcp6_solicit(self, pkt, intf,
302 dst_mac = intf.remote_mac
304 dst_ip = in6_ptop(intf.remote_ip6)
307 self.assertEqual(ether.dst, dst_mac)
308 self.assertEqual(ether.src, intf.local_mac)
311 self.assertEqual(in6_ptop(ip.dst), dst_ip)
312 self.assertEqual(in6_ptop(ip.src), in6_ptop(intf.local_ip6))
315 self.assertEqual(udp.dport, DHCP6_CLIENT_PORT)
316 self.assertEqual(udp.sport, DHCP6_SERVER_PORT)
318 relay = pkt[DHCP6_RelayForward]
319 self.assertEqual(in6_ptop(relay.peeraddr), in6_ptop(peer_ip))
320 oid = pkt[DHCP6OptIfaceId]
321 cll = pkt[DHCP6OptClientLinkLayerAddr]
322 self.assertEqual(cll.optlen, 8)
323 self.assertEqual(cll.lltype, 1)
324 self.assertEqual(cll.clladdr, peer_mac)
329 self.assertEqual(id_len, 0)
330 vss = pkt[DHCP6OptVSS]
331 self.assertEqual(vss.optlen, 8)
332 self.assertEqual(vss.type, 1)
333 # the OUI and FIB-id are really 3 and 4 bytes resp.
334 # but the tested range is small
335 self.assertEqual(ord(vss.data[0]), 0)
336 self.assertEqual(ord(vss.data[1]), 0)
337 self.assertEqual(ord(vss.data[2]), oui)
338 self.assertEqual(ord(vss.data[3]), 0)
339 self.assertEqual(ord(vss.data[4]), 0)
340 self.assertEqual(ord(vss.data[5]), 0)
341 self.assertEqual(ord(vss.data[6]), fib_id)
344 self.assertEqual(oui, 0)
345 vss = pkt[DHCP6OptVSS]
346 self.assertEqual(vss.optlen, id_len+1)
347 self.assertEqual(vss.type, 0)
348 self.assertEqual(vss.data[0:id_len], vpn_id)
350 # the relay message should be an encoded Solicit
351 msg = pkt[DHCP6OptRelayMsg]
352 sol = DHCP6_Solicit()
353 self.assertEqual(msg.optlen, len(str(sol)))
354 self.assertEqual(str(sol), (str(msg[1]))[:msg.optlen])
356 def verify_dhcp6_advert(self, pkt, intf, peer):
358 self.assertEqual(ether.dst, "ff:ff:ff:ff:ff:ff")
359 self.assertEqual(ether.src, intf.local_mac)
362 self.assertEqual(in6_ptop(ip.dst), in6_ptop(peer))
363 self.assertEqual(in6_ptop(ip.src), in6_ptop(intf.local_ip6))
366 self.assertEqual(udp.dport, DHCP6_SERVER_PORT)
367 self.assertEqual(udp.sport, DHCP6_CLIENT_PORT)
369 # not sure why this is not decoding
370 # adv = pkt[DHCP6_Advertise]
372 def wait_for_no_route(self, address, length,
373 n_tries=50, s_time=1):
375 if not find_route(self, address, length):
377 n_tries = n_tries - 1
382 def test_dhcp_proxy(self):
386 # Verify no response to DHCP request without DHCP config
388 p_disc_vrf0 = (Ether(dst="ff:ff:ff:ff:ff:ff",
389 src=self.pg3.remote_mac) /
390 IP(src="0.0.0.0", dst="255.255.255.255") /
391 UDP(sport=DHCP4_CLIENT_PORT,
392 dport=DHCP4_SERVER_PORT) /
394 DHCP(options=[('message-type', 'discover'), ('end')]))
395 pkts_disc_vrf0 = [p_disc_vrf0]
396 p_disc_vrf1 = (Ether(dst="ff:ff:ff:ff:ff:ff",
397 src=self.pg4.remote_mac) /
398 IP(src="0.0.0.0", dst="255.255.255.255") /
399 UDP(sport=DHCP4_CLIENT_PORT,
400 dport=DHCP4_SERVER_PORT) /
402 DHCP(options=[('message-type', 'discover'), ('end')]))
403 pkts_disc_vrf1 = [p_disc_vrf1]
404 p_disc_vrf2 = (Ether(dst="ff:ff:ff:ff:ff:ff",
405 src=self.pg5.remote_mac) /
406 IP(src="0.0.0.0", dst="255.255.255.255") /
407 UDP(sport=DHCP4_CLIENT_PORT,
408 dport=DHCP4_SERVER_PORT) /
410 DHCP(options=[('message-type', 'discover'), ('end')]))
411 pkts_disc_vrf2 = [p_disc_vrf2]
413 self.send_and_assert_no_replies(self.pg3, pkts_disc_vrf0,
414 "DHCP with no configuration")
415 self.send_and_assert_no_replies(self.pg4, pkts_disc_vrf1,
416 "DHCP with no configuration")
417 self.send_and_assert_no_replies(self.pg5, pkts_disc_vrf2,
418 "DHCP with no configuration")
421 # Enable DHCP proxy in VRF 0
423 server_addr = self.pg0.remote_ip4n
424 src_addr = self.pg0.local_ip4n
426 self.vapi.dhcp_proxy_config(server_addr,
431 # Discover packets from the client are dropped because there is no
432 # IP address configured on the client facing interface
434 self.send_and_assert_no_replies(self.pg3, pkts_disc_vrf0,
435 "Discover DHCP no relay address")
438 # Inject a response from the server
439 # dropped, because there is no IP addrees on the
440 # client interfce to fill in the option.
442 p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
443 IP(src=self.pg0.remote_ip4, dst=self.pg0.local_ip4) /
444 UDP(sport=DHCP4_SERVER_PORT, dport=DHCP4_SERVER_PORT) /
446 DHCP(options=[('message-type', 'offer'), ('end')]))
449 self.send_and_assert_no_replies(self.pg3, pkts,
450 "Offer DHCP no relay address")
453 # configure an IP address on the client facing interface
455 self.pg3.config_ip4()
458 # Try again with a discover packet
459 # Rx'd packet should be to the server address and from the configured
461 # UDP source ports are unchanged
462 # we've no option 82 config so that should be absent
464 self.pg3.add_stream(pkts_disc_vrf0)
465 self.pg_enable_capture(self.pg_interfaces)
468 rx = self.pg0.get_capture(1)
471 option_82 = self.verify_relayed_dhcp_discover(rx, self.pg0,
475 # Create an DHCP offer reply from the server with a correctly formatted
476 # option 82. i.e. send back what we just captured
477 # The offer, sent mcast to the client, still has option 82.
479 p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
480 IP(src=self.pg0.remote_ip4, dst=self.pg0.local_ip4) /
481 UDP(sport=DHCP4_SERVER_PORT, dport=DHCP4_SERVER_PORT) /
483 DHCP(options=[('message-type', 'offer'),
484 ('relay_agent_Information', option_82),
488 self.pg0.add_stream(pkts)
489 self.pg_enable_capture(self.pg_interfaces)
492 rx = self.pg3.get_capture(1)
495 self.verify_dhcp_offer(rx, self.pg3)
500 # 1. not our IP address = not checked by VPP? so offer is replayed
502 bad_ip = option_82[0:8] + scapy.compat.chb(33) + option_82[9:]
504 p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
505 IP(src=self.pg0.remote_ip4, dst=self.pg0.local_ip4) /
506 UDP(sport=DHCP4_SERVER_PORT, dport=DHCP4_SERVER_PORT) /
508 DHCP(options=[('message-type', 'offer'),
509 ('relay_agent_Information', bad_ip),
512 self.send_and_assert_no_replies(self.pg0, pkts,
513 "DHCP offer option 82 bad address")
515 # 2. Not a sw_if_index VPP knows
516 bad_if_index = option_82[0:2] + scapy.compat.chb(33) + option_82[3:]
518 p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
519 IP(src=self.pg0.remote_ip4, dst=self.pg0.local_ip4) /
520 UDP(sport=DHCP4_SERVER_PORT, dport=DHCP4_SERVER_PORT) /
522 DHCP(options=[('message-type', 'offer'),
523 ('relay_agent_Information', bad_if_index),
526 self.send_and_assert_no_replies(self.pg0, pkts,
527 "DHCP offer option 82 bad if index")
530 # Send a DHCP request in VRF 1. should be dropped.
532 self.send_and_assert_no_replies(self.pg4, pkts_disc_vrf1,
533 "DHCP with no configuration VRF 1")
536 # Delete the DHCP config in VRF 0
537 # Should now drop requests.
539 self.vapi.dhcp_proxy_config(server_addr,
544 self.send_and_assert_no_replies(self.pg3, pkts_disc_vrf0,
545 "DHCP config removed VRF 0")
546 self.send_and_assert_no_replies(self.pg4, pkts_disc_vrf1,
547 "DHCP config removed VRF 1")
550 # Add DHCP config for VRF 1 & 2
552 server_addr1 = self.pg1.remote_ip4n
553 src_addr1 = self.pg1.local_ip4n
554 self.vapi.dhcp_proxy_config(server_addr1,
558 server_addr2 = self.pg2.remote_ip4n
559 src_addr2 = self.pg2.local_ip4n
560 self.vapi.dhcp_proxy_config(server_addr2,
566 # Confim DHCP requests ok in VRF 1 & 2.
567 # - dropped on IP config on client interface
569 self.send_and_assert_no_replies(self.pg4, pkts_disc_vrf1,
570 "DHCP config removed VRF 1")
571 self.send_and_assert_no_replies(self.pg5, pkts_disc_vrf2,
572 "DHCP config removed VRF 2")
575 # configure an IP address on the client facing interface
577 self.pg4.config_ip4()
578 self.pg4.add_stream(pkts_disc_vrf1)
579 self.pg_enable_capture(self.pg_interfaces)
581 rx = self.pg1.get_capture(1)
583 self.verify_relayed_dhcp_discover(rx, self.pg1, src_intf=self.pg4)
585 self.pg5.config_ip4()
586 self.pg5.add_stream(pkts_disc_vrf2)
587 self.pg_enable_capture(self.pg_interfaces)
589 rx = self.pg2.get_capture(1)
591 self.verify_relayed_dhcp_discover(rx, self.pg2, src_intf=self.pg5)
595 # table=1, vss_type=1, vpn_index=1, oui=4
596 # table=2, vss_type=0, vpn_id = "ip4-table-2"
597 self.vapi.dhcp_proxy_set_vss(1, 1, vpn_index=1, oui=4, is_add=1)
598 self.vapi.dhcp_proxy_set_vss(2, 0, "ip4-table-2", is_add=1)
600 self.pg4.add_stream(pkts_disc_vrf1)
601 self.pg_enable_capture(self.pg_interfaces)
604 rx = self.pg1.get_capture(1)
606 self.verify_relayed_dhcp_discover(rx, self.pg1,
610 self.pg5.add_stream(pkts_disc_vrf2)
611 self.pg_enable_capture(self.pg_interfaces)
614 rx = self.pg2.get_capture(1)
616 self.verify_relayed_dhcp_discover(rx, self.pg2,
618 vpn_id="ip4-table-2")
621 # Add a second DHCP server in VRF 1
622 # expect clients messages to be relay to both configured servers
624 self.pg1.generate_remote_hosts(2)
625 server_addr12 = socket.inet_pton(AF_INET, self.pg1.remote_hosts[1].ip4)
627 self.vapi.dhcp_proxy_config(server_addr12,
634 # We'll need an ARP entry for the server to send it packets
636 arp_entry = VppNeighbor(self,
637 self.pg1.sw_if_index,
638 self.pg1.remote_hosts[1].mac,
639 self.pg1.remote_hosts[1].ip4)
640 arp_entry.add_vpp_config()
643 # Send a discover from the client. expect two relayed messages
644 # The frist packet is sent to the second server
645 # We're not enforcing that here, it's just the way it is.
647 self.pg4.add_stream(pkts_disc_vrf1)
648 self.pg_enable_capture(self.pg_interfaces)
651 rx = self.pg1.get_capture(2)
653 option_82 = self.verify_relayed_dhcp_discover(
656 dst_mac=self.pg1.remote_hosts[1].mac,
657 dst_ip=self.pg1.remote_hosts[1].ip4,
659 self.verify_relayed_dhcp_discover(rx[1], self.pg1,
664 # Send both packets back. Client gets both.
666 p1 = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) /
667 IP(src=self.pg1.remote_ip4, dst=self.pg1.local_ip4) /
668 UDP(sport=DHCP4_SERVER_PORT, dport=DHCP4_SERVER_PORT) /
670 DHCP(options=[('message-type', 'offer'),
671 ('relay_agent_Information', option_82),
673 p2 = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) /
674 IP(src=self.pg1.remote_hosts[1].ip4, dst=self.pg1.local_ip4) /
675 UDP(sport=DHCP4_SERVER_PORT, dport=DHCP4_SERVER_PORT) /
677 DHCP(options=[('message-type', 'offer'),
678 ('relay_agent_Information', option_82),
682 self.pg1.add_stream(pkts)
683 self.pg_enable_capture(self.pg_interfaces)
686 rx = self.pg4.get_capture(2)
688 self.verify_dhcp_offer(rx[0], self.pg4, fib_id=1, oui=4)
689 self.verify_dhcp_offer(rx[1], self.pg4, fib_id=1, oui=4)
692 # Ensure offers from non-servers are dropeed
694 p2 = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) /
695 IP(src="8.8.8.8", dst=self.pg1.local_ip4) /
696 UDP(sport=DHCP4_SERVER_PORT, dport=DHCP4_SERVER_PORT) /
698 DHCP(options=[('message-type', 'offer'),
699 ('relay_agent_Information', option_82),
701 self.send_and_assert_no_replies(self.pg1, p2,
702 "DHCP offer from non-server")
705 # Ensure only the discover is sent to multiple servers
707 p_req_vrf1 = (Ether(dst="ff:ff:ff:ff:ff:ff",
708 src=self.pg4.remote_mac) /
709 IP(src="0.0.0.0", dst="255.255.255.255") /
710 UDP(sport=DHCP4_CLIENT_PORT,
711 dport=DHCP4_SERVER_PORT) /
713 DHCP(options=[('message-type', 'request'),
716 self.pg4.add_stream(p_req_vrf1)
717 self.pg_enable_capture(self.pg_interfaces)
720 rx = self.pg1.get_capture(1)
723 # Remove the second DHCP server
725 self.vapi.dhcp_proxy_config(server_addr12,
732 # Test we can still relay with the first
734 self.pg4.add_stream(pkts_disc_vrf1)
735 self.pg_enable_capture(self.pg_interfaces)
738 rx = self.pg1.get_capture(1)
740 self.verify_relayed_dhcp_discover(rx, self.pg1,
745 # Remove the VSS config
746 # relayed DHCP has default vlaues in the option.
748 self.vapi.dhcp_proxy_set_vss(1, is_add=0)
749 self.vapi.dhcp_proxy_set_vss(2, is_add=0)
751 self.pg4.add_stream(pkts_disc_vrf1)
752 self.pg_enable_capture(self.pg_interfaces)
755 rx = self.pg1.get_capture(1)
757 self.verify_relayed_dhcp_discover(rx, self.pg1, src_intf=self.pg4)
760 # remove DHCP config to cleanup
762 self.vapi.dhcp_proxy_config(server_addr1,
767 self.vapi.dhcp_proxy_config(server_addr2,
773 self.send_and_assert_no_replies(self.pg3, pkts_disc_vrf0,
774 "DHCP cleanup VRF 0")
775 self.send_and_assert_no_replies(self.pg4, pkts_disc_vrf1,
776 "DHCP cleanup VRF 1")
777 self.send_and_assert_no_replies(self.pg5, pkts_disc_vrf2,
778 "DHCP cleanup VRF 2")
780 self.pg3.unconfig_ip4()
781 self.pg4.unconfig_ip4()
782 self.pg5.unconfig_ip4()
784 def test_dhcp6_proxy(self):
787 # Verify no response to DHCP request without DHCP config
789 dhcp_solicit_dst = "ff02::1:2"
790 dhcp_solicit_src_vrf0 = mk_ll_addr(self.pg3.remote_mac)
791 dhcp_solicit_src_vrf1 = mk_ll_addr(self.pg4.remote_mac)
792 dhcp_solicit_src_vrf2 = mk_ll_addr(self.pg5.remote_mac)
793 server_addr_vrf0 = self.pg0.remote_ip6n
794 src_addr_vrf0 = self.pg0.local_ip6n
795 server_addr_vrf1 = self.pg1.remote_ip6n
796 src_addr_vrf1 = self.pg1.local_ip6n
797 server_addr_vrf2 = self.pg2.remote_ip6n
798 src_addr_vrf2 = self.pg2.local_ip6n
800 dmac = in6_getnsmac(inet_pton(socket.AF_INET6, dhcp_solicit_dst))
801 p_solicit_vrf0 = (Ether(dst=dmac, src=self.pg3.remote_mac) /
802 IPv6(src=dhcp_solicit_src_vrf0,
803 dst=dhcp_solicit_dst) /
804 UDP(sport=DHCP6_SERVER_PORT,
805 dport=DHCP6_CLIENT_PORT) /
807 p_solicit_vrf1 = (Ether(dst=dmac, src=self.pg4.remote_mac) /
808 IPv6(src=dhcp_solicit_src_vrf1,
809 dst=dhcp_solicit_dst) /
810 UDP(sport=DHCP6_SERVER_PORT,
811 dport=DHCP6_CLIENT_PORT) /
813 p_solicit_vrf2 = (Ether(dst=dmac, src=self.pg5.remote_mac) /
814 IPv6(src=dhcp_solicit_src_vrf2,
815 dst=dhcp_solicit_dst) /
816 UDP(sport=DHCP6_SERVER_PORT,
817 dport=DHCP6_CLIENT_PORT) /
820 self.send_and_assert_no_replies(self.pg3, p_solicit_vrf0,
821 "DHCP with no configuration")
822 self.send_and_assert_no_replies(self.pg4, p_solicit_vrf1,
823 "DHCP with no configuration")
824 self.send_and_assert_no_replies(self.pg5, p_solicit_vrf2,
825 "DHCP with no configuration")
828 # DHCPv6 config in VRF 0.
829 # Packets still dropped because the client facing interface has no
832 self.vapi.dhcp_proxy_config(server_addr_vrf0,
838 self.send_and_assert_no_replies(self.pg3, p_solicit_vrf0,
839 "DHCP with no configuration")
840 self.send_and_assert_no_replies(self.pg4, p_solicit_vrf1,
841 "DHCP with no configuration")
844 # configure an IP address on the client facing interface
846 self.pg3.config_ip6()
849 # Now the DHCP requests are relayed to the server
851 self.pg3.add_stream(p_solicit_vrf0)
852 self.pg_enable_capture(self.pg_interfaces)
855 rx = self.pg0.get_capture(1)
857 self.verify_dhcp6_solicit(rx[0], self.pg0,
858 dhcp_solicit_src_vrf0,
862 # Exception cases for rejected relay responses
865 # 1 - not a relay reply
866 p_adv_vrf0 = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
867 IPv6(dst=self.pg0.local_ip6, src=self.pg0.remote_ip6) /
868 UDP(sport=DHCP6_SERVER_PORT, dport=DHCP6_SERVER_PORT) /
870 self.send_and_assert_no_replies(self.pg3, p_adv_vrf0,
871 "DHCP6 not a relay reply")
873 # 2 - no relay message option
874 p_adv_vrf0 = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
875 IPv6(dst=self.pg0.local_ip6, src=self.pg0.remote_ip6) /
876 UDP(sport=DHCP6_SERVER_PORT, dport=DHCP6_SERVER_PORT) /
879 self.send_and_assert_no_replies(self.pg3, p_adv_vrf0,
880 "DHCP not a relay message")
883 p_adv_vrf0 = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
884 IPv6(dst=self.pg0.local_ip6, src=self.pg0.remote_ip6) /
885 UDP(sport=DHCP6_SERVER_PORT, dport=DHCP6_SERVER_PORT) /
887 DHCP6OptRelayMsg(optlen=0) /
889 self.send_and_assert_no_replies(self.pg3, p_adv_vrf0,
890 "DHCP6 no circuit ID")
891 # 4 - wrong circuit ID
892 p_adv_vrf0 = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
893 IPv6(dst=self.pg0.local_ip6, src=self.pg0.remote_ip6) /
894 UDP(sport=DHCP6_SERVER_PORT, dport=DHCP6_SERVER_PORT) /
896 DHCP6OptIfaceId(optlen=4, ifaceid='\x00\x00\x00\x05') /
897 DHCP6OptRelayMsg(optlen=0) /
899 self.send_and_assert_no_replies(self.pg3, p_adv_vrf0,
900 "DHCP6 wrong circuit ID")
903 # Send the relay response (the advertisement)
905 p_adv_vrf0 = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
906 IPv6(dst=self.pg0.local_ip6, src=self.pg0.remote_ip6) /
907 UDP(sport=DHCP6_SERVER_PORT, dport=DHCP6_SERVER_PORT) /
909 DHCP6OptIfaceId(optlen=4, ifaceid='\x00\x00\x00\x04') /
910 DHCP6OptRelayMsg(optlen=0) /
911 DHCP6_Advertise(trid=1) /
912 DHCP6OptStatusCode(statuscode=0))
913 pkts_adv_vrf0 = [p_adv_vrf0]
915 self.pg0.add_stream(pkts_adv_vrf0)
916 self.pg_enable_capture(self.pg_interfaces)
919 rx = self.pg3.get_capture(1)
921 self.verify_dhcp6_advert(rx[0], self.pg3, "::")
924 # Send the relay response (the advertisement)
925 # - with peer address
926 p_adv_vrf0 = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
927 IPv6(dst=self.pg0.local_ip6, src=self.pg0.remote_ip6) /
928 UDP(sport=DHCP6_SERVER_PORT, dport=DHCP6_SERVER_PORT) /
929 DHCP6_RelayReply(peeraddr=dhcp_solicit_src_vrf0) /
930 DHCP6OptIfaceId(optlen=4, ifaceid='\x00\x00\x00\x04') /
931 DHCP6OptRelayMsg(optlen=0) /
932 DHCP6_Advertise(trid=1) /
933 DHCP6OptStatusCode(statuscode=0))
934 pkts_adv_vrf0 = [p_adv_vrf0]
936 self.pg0.add_stream(pkts_adv_vrf0)
937 self.pg_enable_capture(self.pg_interfaces)
940 rx = self.pg3.get_capture(1)
942 self.verify_dhcp6_advert(rx[0], self.pg3, dhcp_solicit_src_vrf0)
945 # Add all the config for VRF 1 & 2
947 self.vapi.dhcp_proxy_config(server_addr_vrf1,
952 self.pg4.config_ip6()
954 self.vapi.dhcp_proxy_config(server_addr_vrf2,
959 self.pg5.config_ip6()
964 self.pg4.add_stream(p_solicit_vrf1)
965 self.pg_enable_capture(self.pg_interfaces)
968 rx = self.pg1.get_capture(1)
970 self.verify_dhcp6_solicit(rx[0], self.pg1,
971 dhcp_solicit_src_vrf1,
977 self.pg5.add_stream(p_solicit_vrf2)
978 self.pg_enable_capture(self.pg_interfaces)
981 rx = self.pg2.get_capture(1)
983 self.verify_dhcp6_solicit(rx[0], self.pg2,
984 dhcp_solicit_src_vrf2,
990 p_adv_vrf1 = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) /
991 IPv6(dst=self.pg1.local_ip6, src=self.pg1.remote_ip6) /
992 UDP(sport=DHCP6_SERVER_PORT, dport=DHCP6_SERVER_PORT) /
993 DHCP6_RelayReply(peeraddr=dhcp_solicit_src_vrf1) /
994 DHCP6OptIfaceId(optlen=4, ifaceid='\x00\x00\x00\x05') /
995 DHCP6OptRelayMsg(optlen=0) /
996 DHCP6_Advertise(trid=1) /
997 DHCP6OptStatusCode(statuscode=0))
998 pkts_adv_vrf1 = [p_adv_vrf1]
1000 self.pg1.add_stream(pkts_adv_vrf1)
1001 self.pg_enable_capture(self.pg_interfaces)
1004 rx = self.pg4.get_capture(1)
1006 self.verify_dhcp6_advert(rx[0], self.pg4, dhcp_solicit_src_vrf1)
1010 # table=1, vss_type=1, vpn_index=1, oui=4
1011 # table=2, vss_type=0, vpn_id = "ip6-table-2"
1012 self.vapi.dhcp_proxy_set_vss(1, 1, oui=4, vpn_index=1, is_ip6=1)
1013 self.vapi.dhcp_proxy_set_vss(2, 0, "IPv6-table-2", is_ip6=1)
1015 self.pg4.add_stream(p_solicit_vrf1)
1016 self.pg_enable_capture(self.pg_interfaces)
1019 rx = self.pg1.get_capture(1)
1021 self.verify_dhcp6_solicit(rx[0], self.pg1,
1022 dhcp_solicit_src_vrf1,
1023 self.pg4.remote_mac,
1027 self.pg5.add_stream(p_solicit_vrf2)
1028 self.pg_enable_capture(self.pg_interfaces)
1031 rx = self.pg2.get_capture(1)
1033 self.verify_dhcp6_solicit(rx[0], self.pg2,
1034 dhcp_solicit_src_vrf2,
1035 self.pg5.remote_mac,
1036 vpn_id="IPv6-table-2")
1039 # Remove the VSS config
1040 # relayed DHCP has default vlaues in the option.
1042 self.vapi.dhcp_proxy_set_vss(1, is_ip6=1, is_add=0)
1044 self.pg4.add_stream(p_solicit_vrf1)
1045 self.pg_enable_capture(self.pg_interfaces)
1048 rx = self.pg1.get_capture(1)
1050 self.verify_dhcp6_solicit(rx[0], self.pg1,
1051 dhcp_solicit_src_vrf1,
1052 self.pg4.remote_mac)
1055 # Add a second DHCP server in VRF 1
1056 # expect clients messages to be relay to both configured servers
1058 self.pg1.generate_remote_hosts(2)
1059 server_addr12 = socket.inet_pton(AF_INET6,
1060 self.pg1.remote_hosts[1].ip6)
1062 self.vapi.dhcp_proxy_config(server_addr12,
1069 # We'll need an ND entry for the server to send it packets
1071 nd_entry = VppNeighbor(self,
1072 self.pg1.sw_if_index,
1073 self.pg1.remote_hosts[1].mac,
1074 self.pg1.remote_hosts[1].ip6)
1075 nd_entry.add_vpp_config()
1078 # Send a discover from the client. expect two relayed messages
1079 # The frist packet is sent to the second server
1080 # We're not enforcing that here, it's just the way it is.
1082 self.pg4.add_stream(p_solicit_vrf1)
1083 self.pg_enable_capture(self.pg_interfaces)
1086 rx = self.pg1.get_capture(2)
1088 self.verify_dhcp6_solicit(rx[0], self.pg1,
1089 dhcp_solicit_src_vrf1,
1090 self.pg4.remote_mac)
1091 self.verify_dhcp6_solicit(rx[1], self.pg1,
1092 dhcp_solicit_src_vrf1,
1093 self.pg4.remote_mac,
1094 dst_mac=self.pg1.remote_hosts[1].mac,
1095 dst_ip=self.pg1.remote_hosts[1].ip6)
1098 # Send both packets back. Client gets both.
1100 p1 = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) /
1101 IPv6(dst=self.pg1.local_ip6, src=self.pg1.remote_ip6) /
1102 UDP(sport=DHCP6_SERVER_PORT, dport=DHCP6_SERVER_PORT) /
1103 DHCP6_RelayReply(peeraddr=dhcp_solicit_src_vrf1) /
1104 DHCP6OptIfaceId(optlen=4, ifaceid='\x00\x00\x00\x05') /
1105 DHCP6OptRelayMsg(optlen=0) /
1106 DHCP6_Advertise(trid=1) /
1107 DHCP6OptStatusCode(statuscode=0))
1108 p2 = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_hosts[1].mac) /
1109 IPv6(dst=self.pg1.local_ip6, src=self.pg1._remote_hosts[1].ip6) /
1110 UDP(sport=DHCP6_SERVER_PORT, dport=DHCP6_SERVER_PORT) /
1111 DHCP6_RelayReply(peeraddr=dhcp_solicit_src_vrf1) /
1112 DHCP6OptIfaceId(optlen=4, ifaceid='\x00\x00\x00\x05') /
1113 DHCP6OptRelayMsg(optlen=0) /
1114 DHCP6_Advertise(trid=1) /
1115 DHCP6OptStatusCode(statuscode=0))
1119 self.pg1.add_stream(pkts)
1120 self.pg_enable_capture(self.pg_interfaces)
1123 rx = self.pg4.get_capture(2)
1125 self.verify_dhcp6_advert(rx[0], self.pg4, dhcp_solicit_src_vrf1)
1126 self.verify_dhcp6_advert(rx[1], self.pg4, dhcp_solicit_src_vrf1)
1129 # Ensure only solicit messages are duplicated
1131 p_request_vrf1 = (Ether(dst=dmac, src=self.pg4.remote_mac) /
1132 IPv6(src=dhcp_solicit_src_vrf1,
1133 dst=dhcp_solicit_dst) /
1134 UDP(sport=DHCP6_SERVER_PORT,
1135 dport=DHCP6_CLIENT_PORT) /
1138 self.pg4.add_stream(p_request_vrf1)
1139 self.pg_enable_capture(self.pg_interfaces)
1142 rx = self.pg1.get_capture(1)
1145 # Test we drop DHCP packets from addresses that are not configured as
1148 p2 = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_hosts[1].mac) /
1149 IPv6(dst=self.pg1.local_ip6, src="3001::1") /
1150 UDP(sport=DHCP6_SERVER_PORT, dport=DHCP6_SERVER_PORT) /
1151 DHCP6_RelayReply(peeraddr=dhcp_solicit_src_vrf1) /
1152 DHCP6OptIfaceId(optlen=4, ifaceid='\x00\x00\x00\x05') /
1153 DHCP6OptRelayMsg(optlen=0) /
1154 DHCP6_Advertise(trid=1) /
1155 DHCP6OptStatusCode(statuscode=0))
1156 self.send_and_assert_no_replies(self.pg1, p2,
1157 "DHCP6 not from server")
1160 # Remove the second DHCP server
1162 self.vapi.dhcp_proxy_config(server_addr12,
1170 # Test we can still relay with the first
1172 self.pg4.add_stream(p_solicit_vrf1)
1173 self.pg_enable_capture(self.pg_interfaces)
1176 rx = self.pg1.get_capture(1)
1178 self.verify_dhcp6_solicit(rx[0], self.pg1,
1179 dhcp_solicit_src_vrf1,
1180 self.pg4.remote_mac)
1185 self.vapi.dhcp_proxy_config(server_addr_vrf2,
1191 self.vapi.dhcp_proxy_config(server_addr_vrf1,
1197 self.vapi.dhcp_proxy_config(server_addr_vrf0,
1205 self.vapi.dhcp_proxy_config(server_addr_vrf0,
1211 self.pg3.unconfig_ip6()
1212 self.pg4.unconfig_ip6()
1213 self.pg5.unconfig_ip6()
1215 def test_dhcp_client(self):
1218 hostname = 'universal-dp'
1220 self.pg_enable_capture(self.pg_interfaces)
1223 # Configure DHCP client on PG3 and capture the discover sent
1225 self.vapi.dhcp_client_config(self.pg3.sw_if_index, hostname)
1227 rx = self.pg3.get_capture(1)
1229 self.verify_orig_dhcp_discover(rx[0], self.pg3, hostname)
1232 # Send back on offer, expect the request
1234 p_offer = (Ether(dst=self.pg3.local_mac, src=self.pg3.remote_mac) /
1235 IP(src=self.pg3.remote_ip4, dst="255.255.255.255") /
1236 UDP(sport=DHCP4_SERVER_PORT, dport=DHCP4_CLIENT_PORT) /
1238 yiaddr=self.pg3.local_ip4,
1239 chaddr=mac_pton(self.pg3.local_mac)) /
1240 DHCP(options=[('message-type', 'offer'),
1241 ('server_id', self.pg3.remote_ip4),
1244 self.pg3.add_stream(p_offer)
1245 self.pg_enable_capture(self.pg_interfaces)
1248 rx = self.pg3.get_capture(1)
1249 self.verify_orig_dhcp_request(rx[0], self.pg3, hostname,
1253 # Send an acknowledgment
1255 p_ack = (Ether(dst=self.pg3.local_mac, src=self.pg3.remote_mac) /
1256 IP(src=self.pg3.remote_ip4, dst="255.255.255.255") /
1257 UDP(sport=DHCP4_SERVER_PORT, dport=DHCP4_CLIENT_PORT) /
1258 BOOTP(op=1, yiaddr=self.pg3.local_ip4,
1259 chaddr=mac_pton(self.pg3.local_mac)) /
1260 DHCP(options=[('message-type', 'ack'),
1261 ('subnet_mask', "255.255.255.0"),
1262 ('router', self.pg3.remote_ip4),
1263 ('server_id', self.pg3.remote_ip4),
1264 ('lease_time', 43200),
1267 self.pg3.add_stream(p_ack)
1268 self.pg_enable_capture(self.pg_interfaces)
1272 # We'll get an ARP request for the router address
1274 rx = self.pg3.get_capture(1)
1276 self.assertEqual(rx[0][ARP].pdst, self.pg3.remote_ip4)
1277 self.pg_enable_capture(self.pg_interfaces)
1280 # At the end of this procedure there should be a connected route
1283 self.assertTrue(find_route(self, self.pg3.local_ip4, 24))
1284 self.assertTrue(find_route(self, self.pg3.local_ip4, 32))
1286 # remove the left over ARP entry
1287 self.vapi.ip_neighbor_add_del(self.pg3.sw_if_index,
1288 self.pg3.remote_mac,
1289 self.pg3.remote_ip4,
1293 # remove the DHCP config
1295 self.vapi.dhcp_client_config(self.pg3.sw_if_index, hostname, is_add=0)
1298 # and now the route should be gone
1300 self.assertFalse(find_route(self, self.pg3.local_ip4, 32))
1301 self.assertFalse(find_route(self, self.pg3.local_ip4, 24))
1304 # Start the procedure again. this time have VPP send the client-ID
1306 self.pg3.admin_down()
1309 self.vapi.dhcp_client_config(self.pg3.sw_if_index, hostname,
1310 client_id=self.pg3.local_mac)
1312 rx = self.pg3.get_capture(1)
1314 self.verify_orig_dhcp_discover(rx[0], self.pg3, hostname,
1317 # TODO: VPP DHCP client should not accept DHCP OFFER message with
1318 # the XID (Transaction ID) not matching the XID of the most recent
1319 # DHCP DISCOVERY message.
1320 # Such DHCP OFFER message must be silently discarded - RFC2131.
1321 # Reported in Jira ticket: VPP-99
1322 self.pg3.add_stream(p_offer)
1323 self.pg_enable_capture(self.pg_interfaces)
1326 rx = self.pg3.get_capture(1)
1327 self.verify_orig_dhcp_request(rx[0], self.pg3, hostname,
1331 # unicast the ack to the offered address
1333 p_ack = (Ether(dst=self.pg3.local_mac, src=self.pg3.remote_mac) /
1334 IP(src=self.pg3.remote_ip4, dst=self.pg3.local_ip4) /
1335 UDP(sport=DHCP4_SERVER_PORT, dport=DHCP4_CLIENT_PORT) /
1336 BOOTP(op=1, yiaddr=self.pg3.local_ip4,
1337 chaddr=mac_pton(self.pg3.local_mac)) /
1338 DHCP(options=[('message-type', 'ack'),
1339 ('subnet_mask', "255.255.255.0"),
1340 ('router', self.pg3.remote_ip4),
1341 ('server_id', self.pg3.remote_ip4),
1342 ('lease_time', 43200),
1345 self.pg3.add_stream(p_ack)
1346 self.pg_enable_capture(self.pg_interfaces)
1350 # We'll get an ARP request for the router address
1352 rx = self.pg3.get_capture(1)
1354 self.assertEqual(rx[0][ARP].pdst, self.pg3.remote_ip4)
1355 self.pg_enable_capture(self.pg_interfaces)
1358 # At the end of this procedure there should be a connected route
1361 self.assertTrue(find_route(self, self.pg3.local_ip4, 32))
1362 self.assertTrue(find_route(self, self.pg3.local_ip4, 24))
1365 # remove the DHCP config
1367 self.vapi.dhcp_client_config(self.pg3.sw_if_index, hostname, is_add=0)
1369 self.assertFalse(find_route(self, self.pg3.local_ip4, 32))
1370 self.assertFalse(find_route(self, self.pg3.local_ip4, 24))
1373 # Rince and repeat, this time with VPP configured not to set
1374 # the braodcast flag in the discover and request messages,
1375 # and for the server to unicast the responses.
1377 # Configure DHCP client on PG3 and capture the discover sent
1379 self.vapi.dhcp_client_config(self.pg3.sw_if_index, hostname,
1380 set_broadcast_flag=0)
1382 rx = self.pg3.get_capture(1)
1384 self.verify_orig_dhcp_discover(rx[0], self.pg3, hostname,
1388 # Send back on offer, unicasted to the offered address.
1389 # Expect the request.
1391 p_offer = (Ether(dst=self.pg3.local_mac, src=self.pg3.remote_mac) /
1392 IP(src=self.pg3.remote_ip4, dst=self.pg3.local_ip4) /
1393 UDP(sport=DHCP4_SERVER_PORT, dport=DHCP4_CLIENT_PORT) /
1394 BOOTP(op=1, yiaddr=self.pg3.local_ip4,
1395 chaddr=mac_pton(self.pg3.local_mac)) /
1396 DHCP(options=[('message-type', 'offer'),
1397 ('server_id', self.pg3.remote_ip4),
1400 self.pg3.add_stream(p_offer)
1401 self.pg_enable_capture(self.pg_interfaces)
1404 rx = self.pg3.get_capture(1)
1405 self.verify_orig_dhcp_request(rx[0], self.pg3, hostname,
1410 # Send an acknowledgment
1412 p_ack = (Ether(dst=self.pg3.local_mac, src=self.pg3.remote_mac) /
1413 IP(src=self.pg3.remote_ip4, dst=self.pg3.local_ip4) /
1414 UDP(sport=DHCP4_SERVER_PORT, dport=DHCP4_CLIENT_PORT) /
1415 BOOTP(op=1, yiaddr=self.pg3.local_ip4,
1416 chaddr=mac_pton(self.pg3.local_mac)) /
1417 DHCP(options=[('message-type', 'ack'),
1418 ('subnet_mask', "255.255.255.0"),
1419 ('router', self.pg3.remote_ip4),
1420 ('server_id', self.pg3.remote_ip4),
1421 ('lease_time', 43200),
1424 self.pg3.add_stream(p_ack)
1425 self.pg_enable_capture(self.pg_interfaces)
1429 # We'll get an ARP request for the router address
1431 rx = self.pg3.get_capture(1)
1433 self.assertEqual(rx[0][ARP].pdst, self.pg3.remote_ip4)
1434 self.pg_enable_capture(self.pg_interfaces)
1437 # At the end of this procedure there should be a connected route
1440 self.assertTrue(find_route(self, self.pg3.local_ip4, 24))
1441 self.assertTrue(find_route(self, self.pg3.local_ip4, 32))
1443 # remove the left over ARP entry
1444 self.vapi.ip_neighbor_add_del(self.pg3.sw_if_index,
1445 self.pg3.remote_mac,
1446 self.pg3.remote_ip4,
1450 # read the DHCP client details from a dump
1452 clients = self.vapi.dhcp_client_dump()
1454 self.assertEqual(clients[0].client.sw_if_index,
1455 self.pg3.sw_if_index)
1456 self.assertEqual(clients[0].lease.sw_if_index,
1457 self.pg3.sw_if_index)
1458 self.assertEqual(clients[0].client.hostname.rstrip('\0'),
1460 self.assertEqual(clients[0].lease.hostname.rstrip('\0'),
1462 self.assertEqual(clients[0].lease.is_ipv6, 0)
1463 # 0 = DISCOVER, 1 = REQUEST, 2 = BOUND
1464 self.assertEqual(clients[0].lease.state, 2)
1465 self.assertEqual(clients[0].lease.mask_width, 24)
1466 self.assertEqual(clients[0].lease.router_address.rstrip('\0'),
1467 self.pg3.remote_ip4n)
1468 self.assertEqual(clients[0].lease.host_address.rstrip('\0'),
1469 self.pg3.local_ip4n)
1472 # remove the DHCP config
1474 self.vapi.dhcp_client_config(self.pg3.sw_if_index, hostname, is_add=0)
1477 # and now the route should be gone
1479 self.assertFalse(find_route(self, self.pg3.local_ip4, 32))
1480 self.assertFalse(find_route(self, self.pg3.local_ip4, 24))
1483 # Start the procedure again. Use requested lease time option.
1485 self.pg3.admin_down()
1488 self.vapi.dhcp_client_config(self.pg3.sw_if_index, hostname)
1490 rx = self.pg3.get_capture(1)
1492 self.verify_orig_dhcp_discover(rx[0], self.pg3, hostname)
1495 # Send back on offer with requested lease time, expect the request
1498 p_offer = (Ether(dst=self.pg3.local_mac, src=self.pg3.remote_mac) /
1499 IP(src=self.pg3.remote_ip4, dst='255.255.255.255') /
1500 UDP(sport=DHCP4_SERVER_PORT, dport=DHCP4_CLIENT_PORT) /
1502 yiaddr=self.pg3.local_ip4,
1503 chaddr=mac_pton(self.pg3.local_mac)) /
1504 DHCP(options=[('message-type', 'offer'),
1505 ('server_id', self.pg3.remote_ip4),
1506 ('lease_time', lease_time),
1509 self.pg3.add_stream(p_offer)
1510 self.pg_enable_capture(self.pg_interfaces)
1513 rx = self.pg3.get_capture(1)
1514 self.verify_orig_dhcp_request(rx[0], self.pg3, hostname,
1518 # Send an acknowledgment
1520 p_ack = (Ether(dst=self.pg3.local_mac, src=self.pg3.remote_mac) /
1521 IP(src=self.pg3.remote_ip4, dst='255.255.255.255') /
1522 UDP(sport=DHCP4_SERVER_PORT, dport=DHCP4_CLIENT_PORT) /
1523 BOOTP(op=1, yiaddr=self.pg3.local_ip4,
1524 chaddr=mac_pton(self.pg3.local_mac)) /
1525 DHCP(options=[('message-type', 'ack'),
1526 ('subnet_mask', '255.255.255.0'),
1527 ('router', self.pg3.remote_ip4),
1528 ('server_id', self.pg3.remote_ip4),
1529 ('lease_time', lease_time),
1532 self.pg3.add_stream(p_ack)
1533 self.pg_enable_capture(self.pg_interfaces)
1537 # We'll get an ARP request for the router address
1539 rx = self.pg3.get_capture(1)
1541 self.assertEqual(rx[0][ARP].pdst, self.pg3.remote_ip4)
1544 # At the end of this procedure there should be a connected route
1547 self.assertTrue(find_route(self, self.pg3.local_ip4, 32))
1548 self.assertTrue(find_route(self, self.pg3.local_ip4, 24))
1550 # remove the left over ARP entry
1551 self.vapi.ip_neighbor_add_del(self.pg3.sw_if_index,
1552 self.pg3.remote_mac,
1553 self.pg3.remote_ip4,
1557 # the route should be gone after the lease expires
1559 self.assertTrue(self.wait_for_no_route(self.pg3.local_ip4, 32))
1560 self.assertTrue(self.wait_for_no_route(self.pg3.local_ip4, 24))
1563 # remove the DHCP config
1565 self.vapi.dhcp_client_config(self.pg3.sw_if_index, hostname, is_add=0)
1568 if __name__ == '__main__':
1569 unittest.main(testRunner=VppTestRunner)