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 """
35 super(TestDHCP, self).setUp()
37 # create 6 pg interfaces for pg0 to pg5
38 self.create_pg_interfaces(range(6))
41 # pg0 to 2 are IP configured in VRF 0, 1 and 2.
42 # pg3 to 5 are non IP-configured in VRF 0, 1 and 2.
44 for table_id in range(1, 4):
45 tbl4 = VppIpTable(self, table_id)
47 self.tables.append(tbl4)
48 tbl6 = VppIpTable(self, table_id, is_ip6=1)
50 self.tables.append(tbl6)
53 for i in self.pg_interfaces[:3]:
55 i.set_table_ip4(table_id)
56 i.set_table_ip6(table_id)
64 for i in self.pg_interfaces[3:]:
66 i.set_table_ip4(table_id)
67 i.set_table_ip6(table_id)
71 for i in self.pg_interfaces[:3]:
75 for i in self.pg_interfaces:
79 super(TestDHCP, self).tearDown()
81 def verify_dhcp_has_option(self, pkt, option, value):
85 for i in dhcp.options:
88 self.assertEqual(i[1], value)
91 self.assertTrue(found)
93 def validate_relay_options(self, pkt, intf, ip_addr, vpn_id, fib_id, oui):
99 for i in dhcp.options:
101 if i[0] == "relay_agent_Information":
103 # There are two sb-options present - each of length 6.
107 self.assertEqual(len(data), 24)
108 elif len(vpn_id) > 0:
109 self.assertEqual(len(data), len(vpn_id)+17)
111 self.assertEqual(len(data), 12)
114 # First sub-option is ID 1, len 4, then encoded
115 # sw_if_index. This test uses low valued indicies
117 # The ID space is VPP internal - so no matching value
120 self.assertEqual(ord(data[0]), 1)
121 self.assertEqual(ord(data[1]), 4)
122 self.assertEqual(ord(data[2]), 0)
123 self.assertEqual(ord(data[3]), 0)
124 self.assertEqual(ord(data[4]), 0)
125 self.assertEqual(ord(data[5]), intf._sw_if_index)
128 # next sub-option is the IP address of the client side
130 # sub-option ID=5, length (of a v4 address)=4
132 claddr = socket.inet_pton(AF_INET, ip_addr)
134 self.assertEqual(ord(data[6]), 5)
135 self.assertEqual(ord(data[7]), 4)
136 self.assertEqual(data[8], claddr[0])
137 self.assertEqual(data[9], claddr[1])
138 self.assertEqual(data[10], claddr[2])
139 self.assertEqual(data[11], claddr[3])
142 # sub-option 151 encodes vss_type 1,
143 # the 3 byte oui and the 4 byte fib_id
144 self.assertEqual(id_len, 0)
145 self.assertEqual(ord(data[12]), 151)
146 self.assertEqual(ord(data[13]), 8)
147 self.assertEqual(ord(data[14]), 1)
148 self.assertEqual(ord(data[15]), 0)
149 self.assertEqual(ord(data[16]), 0)
150 self.assertEqual(ord(data[17]), oui)
151 self.assertEqual(ord(data[18]), 0)
152 self.assertEqual(ord(data[19]), 0)
153 self.assertEqual(ord(data[20]), 0)
154 self.assertEqual(ord(data[21]), fib_id)
156 # VSS control sub-option
157 self.assertEqual(ord(data[22]), 152)
158 self.assertEqual(ord(data[23]), 0)
161 # sub-option 151 encode vss_type of 0
162 # followerd by vpn_id in ascii
163 self.assertEqual(oui, 0)
164 self.assertEqual(ord(data[12]), 151)
165 self.assertEqual(ord(data[13]), id_len+1)
166 self.assertEqual(ord(data[14]), 0)
167 self.assertEqual(data[15:15+id_len], vpn_id)
169 # VSS control sub-option
170 self.assertEqual(ord(data[15+len(vpn_id)]), 152)
171 self.assertEqual(ord(data[16+len(vpn_id)]), 0)
174 self.assertTrue(found)
178 def verify_dhcp_msg_type(self, pkt, name):
181 for o in dhcp.options:
183 if o[0] == "message-type" \
184 and DHCPTypes[o[1]] == name:
186 self.assertTrue(found)
188 def verify_dhcp_offer(self, pkt, intf, vpn_id="", fib_id=0, oui=0):
190 self.assertEqual(ether.dst, "ff:ff:ff:ff:ff:ff")
191 self.assertEqual(ether.src, intf.local_mac)
194 self.assertEqual(ip.dst, "255.255.255.255")
195 self.assertEqual(ip.src, intf.local_ip4)
198 self.assertEqual(udp.dport, DHCP4_CLIENT_PORT)
199 self.assertEqual(udp.sport, DHCP4_SERVER_PORT)
201 self.verify_dhcp_msg_type(pkt, "offer")
202 data = self.validate_relay_options(pkt, intf, intf.local_ip4,
205 def verify_orig_dhcp_pkt(self, pkt, intf):
207 self.assertEqual(ether.dst, "ff:ff:ff:ff:ff:ff")
208 self.assertEqual(ether.src, intf.local_mac)
211 self.assertEqual(ip.dst, "255.255.255.255")
212 self.assertEqual(ip.src, "0.0.0.0")
215 self.assertEqual(udp.dport, DHCP4_SERVER_PORT)
216 self.assertEqual(udp.sport, DHCP4_CLIENT_PORT)
218 def verify_orig_dhcp_discover(self, pkt, intf, hostname, client_id=None,
220 self.verify_orig_dhcp_pkt(pkt, intf)
222 self.verify_dhcp_msg_type(pkt, "discover")
223 self.verify_dhcp_has_option(pkt, "hostname", hostname)
225 self.verify_dhcp_has_option(pkt, "client_id", client_id)
227 self.assertEqual(bootp.ciaddr, "0.0.0.0")
228 self.assertEqual(bootp.giaddr, "0.0.0.0")
230 self.assertEqual(bootp.flags, 0x8000)
232 self.assertEqual(bootp.flags, 0x0000)
234 def verify_orig_dhcp_request(self, pkt, intf, hostname, ip,
236 self.verify_orig_dhcp_pkt(pkt, intf)
238 self.verify_dhcp_msg_type(pkt, "request")
239 self.verify_dhcp_has_option(pkt, "hostname", hostname)
240 self.verify_dhcp_has_option(pkt, "requested_addr", ip)
242 self.assertEqual(bootp.ciaddr, "0.0.0.0")
243 self.assertEqual(bootp.giaddr, "0.0.0.0")
245 self.assertEqual(bootp.flags, 0x8000)
247 self.assertEqual(bootp.flags, 0x0000)
249 def verify_relayed_dhcp_discover(self, pkt, intf, src_intf=None,
252 dst_mac=None, dst_ip=None):
254 dst_mac = intf.remote_mac
256 dst_ip = intf.remote_ip4
259 self.assertEqual(ether.dst, dst_mac)
260 self.assertEqual(ether.src, intf.local_mac)
263 self.assertEqual(ip.dst, dst_ip)
264 self.assertEqual(ip.src, intf.local_ip4)
267 self.assertEqual(udp.dport, DHCP4_SERVER_PORT)
268 self.assertEqual(udp.sport, DHCP4_CLIENT_PORT)
273 for o in dhcp.options:
275 if o[0] == "message-type" \
276 and DHCPTypes[o[1]] == "discover":
278 self.assertTrue(is_discover)
280 data = self.validate_relay_options(pkt, src_intf,
286 def verify_dhcp6_solicit(self, pkt, intf,
294 dst_mac = intf.remote_mac
296 dst_ip = in6_ptop(intf.remote_ip6)
299 self.assertEqual(ether.dst, dst_mac)
300 self.assertEqual(ether.src, intf.local_mac)
303 self.assertEqual(in6_ptop(ip.dst), dst_ip)
304 self.assertEqual(in6_ptop(ip.src), in6_ptop(intf.local_ip6))
307 self.assertEqual(udp.dport, DHCP6_CLIENT_PORT)
308 self.assertEqual(udp.sport, DHCP6_SERVER_PORT)
310 relay = pkt[DHCP6_RelayForward]
311 self.assertEqual(in6_ptop(relay.peeraddr), in6_ptop(peer_ip))
312 oid = pkt[DHCP6OptIfaceId]
313 cll = pkt[DHCP6OptClientLinkLayerAddr]
314 self.assertEqual(cll.optlen, 8)
315 self.assertEqual(cll.lltype, 1)
316 self.assertEqual(cll.clladdr, peer_mac)
321 self.assertEqual(id_len, 0)
322 vss = pkt[DHCP6OptVSS]
323 self.assertEqual(vss.optlen, 8)
324 self.assertEqual(vss.type, 1)
325 # the OUI and FIB-id are really 3 and 4 bytes resp.
326 # but the tested range is small
327 self.assertEqual(ord(vss.data[0]), 0)
328 self.assertEqual(ord(vss.data[1]), 0)
329 self.assertEqual(ord(vss.data[2]), oui)
330 self.assertEqual(ord(vss.data[3]), 0)
331 self.assertEqual(ord(vss.data[4]), 0)
332 self.assertEqual(ord(vss.data[5]), 0)
333 self.assertEqual(ord(vss.data[6]), fib_id)
336 self.assertEqual(oui, 0)
337 vss = pkt[DHCP6OptVSS]
338 self.assertEqual(vss.optlen, id_len+1)
339 self.assertEqual(vss.type, 0)
340 self.assertEqual(vss.data[0:id_len], vpn_id)
342 # the relay message should be an encoded Solicit
343 msg = pkt[DHCP6OptRelayMsg]
344 sol = DHCP6_Solicit()
345 self.assertEqual(msg.optlen, len(str(sol)))
346 self.assertEqual(str(sol), (str(msg[1]))[:msg.optlen])
348 def verify_dhcp6_advert(self, pkt, intf, peer):
350 self.assertEqual(ether.dst, "ff:ff:ff:ff:ff:ff")
351 self.assertEqual(ether.src, intf.local_mac)
354 self.assertEqual(in6_ptop(ip.dst), in6_ptop(peer))
355 self.assertEqual(in6_ptop(ip.src), in6_ptop(intf.local_ip6))
358 self.assertEqual(udp.dport, DHCP6_SERVER_PORT)
359 self.assertEqual(udp.sport, DHCP6_CLIENT_PORT)
361 # not sure why this is not decoding
362 # adv = pkt[DHCP6_Advertise]
364 def wait_for_no_route(self, address, length,
365 n_tries=50, s_time=1):
367 if not find_route(self, address, length):
369 n_tries = n_tries - 1
374 def test_dhcp_proxy(self):
378 # Verify no response to DHCP request without DHCP config
380 p_disc_vrf0 = (Ether(dst="ff:ff:ff:ff:ff:ff",
381 src=self.pg3.remote_mac) /
382 IP(src="0.0.0.0", dst="255.255.255.255") /
383 UDP(sport=DHCP4_CLIENT_PORT,
384 dport=DHCP4_SERVER_PORT) /
386 DHCP(options=[('message-type', 'discover'), ('end')]))
387 pkts_disc_vrf0 = [p_disc_vrf0]
388 p_disc_vrf1 = (Ether(dst="ff:ff:ff:ff:ff:ff",
389 src=self.pg4.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_vrf1 = [p_disc_vrf1]
396 p_disc_vrf2 = (Ether(dst="ff:ff:ff:ff:ff:ff",
397 src=self.pg5.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_vrf2 = [p_disc_vrf2]
405 self.send_and_assert_no_replies(self.pg3, pkts_disc_vrf0,
406 "DHCP with no configuration")
407 self.send_and_assert_no_replies(self.pg4, pkts_disc_vrf1,
408 "DHCP with no configuration")
409 self.send_and_assert_no_replies(self.pg5, pkts_disc_vrf2,
410 "DHCP with no configuration")
413 # Enable DHCP proxy in VRF 0
415 server_addr = self.pg0.remote_ip4n
416 src_addr = self.pg0.local_ip4n
418 self.vapi.dhcp_proxy_config(server_addr,
423 # Discover packets from the client are dropped because there is no
424 # IP address configured on the client facing interface
426 self.send_and_assert_no_replies(self.pg3, pkts_disc_vrf0,
427 "Discover DHCP no relay address")
430 # Inject a response from the server
431 # dropped, because there is no IP addrees on the
432 # client interfce to fill in the option.
434 p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
435 IP(src=self.pg0.remote_ip4, dst=self.pg0.local_ip4) /
436 UDP(sport=DHCP4_SERVER_PORT, dport=DHCP4_SERVER_PORT) /
438 DHCP(options=[('message-type', 'offer'), ('end')]))
441 self.send_and_assert_no_replies(self.pg3, pkts,
442 "Offer DHCP no relay address")
445 # configure an IP address on the client facing interface
447 self.pg3.config_ip4()
450 # Try again with a discover packet
451 # Rx'd packet should be to the server address and from the configured
453 # UDP source ports are unchanged
454 # we've no option 82 config so that should be absent
456 self.pg3.add_stream(pkts_disc_vrf0)
457 self.pg_enable_capture(self.pg_interfaces)
460 rx = self.pg0.get_capture(1)
463 option_82 = self.verify_relayed_dhcp_discover(rx, self.pg0,
467 # Create an DHCP offer reply from the server with a correctly formatted
468 # option 82. i.e. send back what we just captured
469 # The offer, sent mcast to the client, still has option 82.
471 p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
472 IP(src=self.pg0.remote_ip4, dst=self.pg0.local_ip4) /
473 UDP(sport=DHCP4_SERVER_PORT, dport=DHCP4_SERVER_PORT) /
475 DHCP(options=[('message-type', 'offer'),
476 ('relay_agent_Information', option_82),
480 self.pg0.add_stream(pkts)
481 self.pg_enable_capture(self.pg_interfaces)
484 rx = self.pg3.get_capture(1)
487 self.verify_dhcp_offer(rx, self.pg3)
492 # 1. not our IP address = not checked by VPP? so offer is replayed
494 bad_ip = option_82[0:8] + scapy.compat.chb(33) + option_82[9:]
496 p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
497 IP(src=self.pg0.remote_ip4, dst=self.pg0.local_ip4) /
498 UDP(sport=DHCP4_SERVER_PORT, dport=DHCP4_SERVER_PORT) /
500 DHCP(options=[('message-type', 'offer'),
501 ('relay_agent_Information', bad_ip),
504 self.send_and_assert_no_replies(self.pg0, pkts,
505 "DHCP offer option 82 bad address")
507 # 2. Not a sw_if_index VPP knows
508 bad_if_index = option_82[0:2] + scapy.compat.chb(33) + option_82[3:]
510 p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
511 IP(src=self.pg0.remote_ip4, dst=self.pg0.local_ip4) /
512 UDP(sport=DHCP4_SERVER_PORT, dport=DHCP4_SERVER_PORT) /
514 DHCP(options=[('message-type', 'offer'),
515 ('relay_agent_Information', bad_if_index),
518 self.send_and_assert_no_replies(self.pg0, pkts,
519 "DHCP offer option 82 bad if index")
522 # Send a DHCP request in VRF 1. should be dropped.
524 self.send_and_assert_no_replies(self.pg4, pkts_disc_vrf1,
525 "DHCP with no configuration VRF 1")
528 # Delete the DHCP config in VRF 0
529 # Should now drop requests.
531 self.vapi.dhcp_proxy_config(server_addr,
536 self.send_and_assert_no_replies(self.pg3, pkts_disc_vrf0,
537 "DHCP config removed VRF 0")
538 self.send_and_assert_no_replies(self.pg4, pkts_disc_vrf1,
539 "DHCP config removed VRF 1")
542 # Add DHCP config for VRF 1 & 2
544 server_addr1 = self.pg1.remote_ip4n
545 src_addr1 = self.pg1.local_ip4n
546 self.vapi.dhcp_proxy_config(server_addr1,
550 server_addr2 = self.pg2.remote_ip4n
551 src_addr2 = self.pg2.local_ip4n
552 self.vapi.dhcp_proxy_config(server_addr2,
558 # Confim DHCP requests ok in VRF 1 & 2.
559 # - dropped on IP config on client interface
561 self.send_and_assert_no_replies(self.pg4, pkts_disc_vrf1,
562 "DHCP config removed VRF 1")
563 self.send_and_assert_no_replies(self.pg5, pkts_disc_vrf2,
564 "DHCP config removed VRF 2")
567 # configure an IP address on the client facing interface
569 self.pg4.config_ip4()
570 self.pg4.add_stream(pkts_disc_vrf1)
571 self.pg_enable_capture(self.pg_interfaces)
573 rx = self.pg1.get_capture(1)
575 self.verify_relayed_dhcp_discover(rx, self.pg1, src_intf=self.pg4)
577 self.pg5.config_ip4()
578 self.pg5.add_stream(pkts_disc_vrf2)
579 self.pg_enable_capture(self.pg_interfaces)
581 rx = self.pg2.get_capture(1)
583 self.verify_relayed_dhcp_discover(rx, self.pg2, src_intf=self.pg5)
587 # table=1, vss_type=1, vpn_index=1, oui=4
588 # table=2, vss_type=0, vpn_id = "ip4-table-2"
589 self.vapi.dhcp_proxy_set_vss(1, 1, vpn_index=1, oui=4, is_add=1)
590 self.vapi.dhcp_proxy_set_vss(2, 0, "ip4-table-2", is_add=1)
592 self.pg4.add_stream(pkts_disc_vrf1)
593 self.pg_enable_capture(self.pg_interfaces)
596 rx = self.pg1.get_capture(1)
598 self.verify_relayed_dhcp_discover(rx, self.pg1,
602 self.pg5.add_stream(pkts_disc_vrf2)
603 self.pg_enable_capture(self.pg_interfaces)
606 rx = self.pg2.get_capture(1)
608 self.verify_relayed_dhcp_discover(rx, self.pg2,
610 vpn_id="ip4-table-2")
613 # Add a second DHCP server in VRF 1
614 # expect clients messages to be relay to both configured servers
616 self.pg1.generate_remote_hosts(2)
617 server_addr12 = socket.inet_pton(AF_INET, self.pg1.remote_hosts[1].ip4)
619 self.vapi.dhcp_proxy_config(server_addr12,
626 # We'll need an ARP entry for the server to send it packets
628 arp_entry = VppNeighbor(self,
629 self.pg1.sw_if_index,
630 self.pg1.remote_hosts[1].mac,
631 self.pg1.remote_hosts[1].ip4)
632 arp_entry.add_vpp_config()
635 # Send a discover from the client. expect two relayed messages
636 # The frist packet is sent to the second server
637 # We're not enforcing that here, it's just the way it is.
639 self.pg4.add_stream(pkts_disc_vrf1)
640 self.pg_enable_capture(self.pg_interfaces)
643 rx = self.pg1.get_capture(2)
645 option_82 = self.verify_relayed_dhcp_discover(
648 dst_mac=self.pg1.remote_hosts[1].mac,
649 dst_ip=self.pg1.remote_hosts[1].ip4,
651 self.verify_relayed_dhcp_discover(rx[1], self.pg1,
656 # Send both packets back. Client gets both.
658 p1 = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) /
659 IP(src=self.pg1.remote_ip4, dst=self.pg1.local_ip4) /
660 UDP(sport=DHCP4_SERVER_PORT, dport=DHCP4_SERVER_PORT) /
662 DHCP(options=[('message-type', 'offer'),
663 ('relay_agent_Information', option_82),
665 p2 = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) /
666 IP(src=self.pg1.remote_hosts[1].ip4, dst=self.pg1.local_ip4) /
667 UDP(sport=DHCP4_SERVER_PORT, dport=DHCP4_SERVER_PORT) /
669 DHCP(options=[('message-type', 'offer'),
670 ('relay_agent_Information', option_82),
674 self.pg1.add_stream(pkts)
675 self.pg_enable_capture(self.pg_interfaces)
678 rx = self.pg4.get_capture(2)
680 self.verify_dhcp_offer(rx[0], self.pg4, fib_id=1, oui=4)
681 self.verify_dhcp_offer(rx[1], self.pg4, fib_id=1, oui=4)
684 # Ensure offers from non-servers are dropeed
686 p2 = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) /
687 IP(src="8.8.8.8", dst=self.pg1.local_ip4) /
688 UDP(sport=DHCP4_SERVER_PORT, dport=DHCP4_SERVER_PORT) /
690 DHCP(options=[('message-type', 'offer'),
691 ('relay_agent_Information', option_82),
693 self.send_and_assert_no_replies(self.pg1, p2,
694 "DHCP offer from non-server")
697 # Ensure only the discover is sent to multiple servers
699 p_req_vrf1 = (Ether(dst="ff:ff:ff:ff:ff:ff",
700 src=self.pg4.remote_mac) /
701 IP(src="0.0.0.0", dst="255.255.255.255") /
702 UDP(sport=DHCP4_CLIENT_PORT,
703 dport=DHCP4_SERVER_PORT) /
705 DHCP(options=[('message-type', 'request'),
708 self.pg4.add_stream(p_req_vrf1)
709 self.pg_enable_capture(self.pg_interfaces)
712 rx = self.pg1.get_capture(1)
715 # Remove the second DHCP server
717 self.vapi.dhcp_proxy_config(server_addr12,
724 # Test we can still relay with the first
726 self.pg4.add_stream(pkts_disc_vrf1)
727 self.pg_enable_capture(self.pg_interfaces)
730 rx = self.pg1.get_capture(1)
732 self.verify_relayed_dhcp_discover(rx, self.pg1,
737 # Remove the VSS config
738 # relayed DHCP has default vlaues in the option.
740 self.vapi.dhcp_proxy_set_vss(1, is_add=0)
741 self.vapi.dhcp_proxy_set_vss(2, is_add=0)
743 self.pg4.add_stream(pkts_disc_vrf1)
744 self.pg_enable_capture(self.pg_interfaces)
747 rx = self.pg1.get_capture(1)
749 self.verify_relayed_dhcp_discover(rx, self.pg1, src_intf=self.pg4)
752 # remove DHCP config to cleanup
754 self.vapi.dhcp_proxy_config(server_addr1,
759 self.vapi.dhcp_proxy_config(server_addr2,
765 self.send_and_assert_no_replies(self.pg3, pkts_disc_vrf0,
766 "DHCP cleanup VRF 0")
767 self.send_and_assert_no_replies(self.pg4, pkts_disc_vrf1,
768 "DHCP cleanup VRF 1")
769 self.send_and_assert_no_replies(self.pg5, pkts_disc_vrf2,
770 "DHCP cleanup VRF 2")
772 self.pg3.unconfig_ip4()
773 self.pg4.unconfig_ip4()
774 self.pg5.unconfig_ip4()
776 def test_dhcp6_proxy(self):
779 # Verify no response to DHCP request without DHCP config
781 dhcp_solicit_dst = "ff02::1:2"
782 dhcp_solicit_src_vrf0 = mk_ll_addr(self.pg3.remote_mac)
783 dhcp_solicit_src_vrf1 = mk_ll_addr(self.pg4.remote_mac)
784 dhcp_solicit_src_vrf2 = mk_ll_addr(self.pg5.remote_mac)
785 server_addr_vrf0 = self.pg0.remote_ip6n
786 src_addr_vrf0 = self.pg0.local_ip6n
787 server_addr_vrf1 = self.pg1.remote_ip6n
788 src_addr_vrf1 = self.pg1.local_ip6n
789 server_addr_vrf2 = self.pg2.remote_ip6n
790 src_addr_vrf2 = self.pg2.local_ip6n
792 dmac = in6_getnsmac(inet_pton(socket.AF_INET6, dhcp_solicit_dst))
793 p_solicit_vrf0 = (Ether(dst=dmac, src=self.pg3.remote_mac) /
794 IPv6(src=dhcp_solicit_src_vrf0,
795 dst=dhcp_solicit_dst) /
796 UDP(sport=DHCP6_SERVER_PORT,
797 dport=DHCP6_CLIENT_PORT) /
799 p_solicit_vrf1 = (Ether(dst=dmac, src=self.pg4.remote_mac) /
800 IPv6(src=dhcp_solicit_src_vrf1,
801 dst=dhcp_solicit_dst) /
802 UDP(sport=DHCP6_SERVER_PORT,
803 dport=DHCP6_CLIENT_PORT) /
805 p_solicit_vrf2 = (Ether(dst=dmac, src=self.pg5.remote_mac) /
806 IPv6(src=dhcp_solicit_src_vrf2,
807 dst=dhcp_solicit_dst) /
808 UDP(sport=DHCP6_SERVER_PORT,
809 dport=DHCP6_CLIENT_PORT) /
812 self.send_and_assert_no_replies(self.pg3, p_solicit_vrf0,
813 "DHCP with no configuration")
814 self.send_and_assert_no_replies(self.pg4, p_solicit_vrf1,
815 "DHCP with no configuration")
816 self.send_and_assert_no_replies(self.pg5, p_solicit_vrf2,
817 "DHCP with no configuration")
820 # DHCPv6 config in VRF 0.
821 # Packets still dropped because the client facing interface has no
824 self.vapi.dhcp_proxy_config(server_addr_vrf0,
830 self.send_and_assert_no_replies(self.pg3, p_solicit_vrf0,
831 "DHCP with no configuration")
832 self.send_and_assert_no_replies(self.pg4, p_solicit_vrf1,
833 "DHCP with no configuration")
836 # configure an IP address on the client facing interface
838 self.pg3.config_ip6()
841 # Now the DHCP requests are relayed to the server
843 self.pg3.add_stream(p_solicit_vrf0)
844 self.pg_enable_capture(self.pg_interfaces)
847 rx = self.pg0.get_capture(1)
849 self.verify_dhcp6_solicit(rx[0], self.pg0,
850 dhcp_solicit_src_vrf0,
854 # Exception cases for rejected relay responses
857 # 1 - not a relay reply
858 p_adv_vrf0 = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
859 IPv6(dst=self.pg0.local_ip6, src=self.pg0.remote_ip6) /
860 UDP(sport=DHCP6_SERVER_PORT, dport=DHCP6_SERVER_PORT) /
862 self.send_and_assert_no_replies(self.pg3, p_adv_vrf0,
863 "DHCP6 not a relay reply")
865 # 2 - no relay message option
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) /
871 self.send_and_assert_no_replies(self.pg3, p_adv_vrf0,
872 "DHCP not a relay message")
875 p_adv_vrf0 = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
876 IPv6(dst=self.pg0.local_ip6, src=self.pg0.remote_ip6) /
877 UDP(sport=DHCP6_SERVER_PORT, dport=DHCP6_SERVER_PORT) /
879 DHCP6OptRelayMsg(optlen=0) /
881 self.send_and_assert_no_replies(self.pg3, p_adv_vrf0,
882 "DHCP6 no circuit ID")
883 # 4 - wrong circuit ID
884 p_adv_vrf0 = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
885 IPv6(dst=self.pg0.local_ip6, src=self.pg0.remote_ip6) /
886 UDP(sport=DHCP6_SERVER_PORT, dport=DHCP6_SERVER_PORT) /
888 DHCP6OptIfaceId(optlen=4, ifaceid='\x00\x00\x00\x05') /
889 DHCP6OptRelayMsg(optlen=0) /
891 self.send_and_assert_no_replies(self.pg3, p_adv_vrf0,
892 "DHCP6 wrong circuit ID")
895 # Send the relay response (the advertisement)
897 p_adv_vrf0 = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
898 IPv6(dst=self.pg0.local_ip6, src=self.pg0.remote_ip6) /
899 UDP(sport=DHCP6_SERVER_PORT, dport=DHCP6_SERVER_PORT) /
901 DHCP6OptIfaceId(optlen=4, ifaceid='\x00\x00\x00\x04') /
902 DHCP6OptRelayMsg(optlen=0) /
903 DHCP6_Advertise(trid=1) /
904 DHCP6OptStatusCode(statuscode=0))
905 pkts_adv_vrf0 = [p_adv_vrf0]
907 self.pg0.add_stream(pkts_adv_vrf0)
908 self.pg_enable_capture(self.pg_interfaces)
911 rx = self.pg3.get_capture(1)
913 self.verify_dhcp6_advert(rx[0], self.pg3, "::")
916 # Send the relay response (the advertisement)
917 # - with peer address
918 p_adv_vrf0 = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
919 IPv6(dst=self.pg0.local_ip6, src=self.pg0.remote_ip6) /
920 UDP(sport=DHCP6_SERVER_PORT, dport=DHCP6_SERVER_PORT) /
921 DHCP6_RelayReply(peeraddr=dhcp_solicit_src_vrf0) /
922 DHCP6OptIfaceId(optlen=4, ifaceid='\x00\x00\x00\x04') /
923 DHCP6OptRelayMsg(optlen=0) /
924 DHCP6_Advertise(trid=1) /
925 DHCP6OptStatusCode(statuscode=0))
926 pkts_adv_vrf0 = [p_adv_vrf0]
928 self.pg0.add_stream(pkts_adv_vrf0)
929 self.pg_enable_capture(self.pg_interfaces)
932 rx = self.pg3.get_capture(1)
934 self.verify_dhcp6_advert(rx[0], self.pg3, dhcp_solicit_src_vrf0)
937 # Add all the config for VRF 1 & 2
939 self.vapi.dhcp_proxy_config(server_addr_vrf1,
944 self.pg4.config_ip6()
946 self.vapi.dhcp_proxy_config(server_addr_vrf2,
951 self.pg5.config_ip6()
956 self.pg4.add_stream(p_solicit_vrf1)
957 self.pg_enable_capture(self.pg_interfaces)
960 rx = self.pg1.get_capture(1)
962 self.verify_dhcp6_solicit(rx[0], self.pg1,
963 dhcp_solicit_src_vrf1,
969 self.pg5.add_stream(p_solicit_vrf2)
970 self.pg_enable_capture(self.pg_interfaces)
973 rx = self.pg2.get_capture(1)
975 self.verify_dhcp6_solicit(rx[0], self.pg2,
976 dhcp_solicit_src_vrf2,
982 p_adv_vrf1 = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) /
983 IPv6(dst=self.pg1.local_ip6, src=self.pg1.remote_ip6) /
984 UDP(sport=DHCP6_SERVER_PORT, dport=DHCP6_SERVER_PORT) /
985 DHCP6_RelayReply(peeraddr=dhcp_solicit_src_vrf1) /
986 DHCP6OptIfaceId(optlen=4, ifaceid='\x00\x00\x00\x05') /
987 DHCP6OptRelayMsg(optlen=0) /
988 DHCP6_Advertise(trid=1) /
989 DHCP6OptStatusCode(statuscode=0))
990 pkts_adv_vrf1 = [p_adv_vrf1]
992 self.pg1.add_stream(pkts_adv_vrf1)
993 self.pg_enable_capture(self.pg_interfaces)
996 rx = self.pg4.get_capture(1)
998 self.verify_dhcp6_advert(rx[0], self.pg4, dhcp_solicit_src_vrf1)
1002 # table=1, vss_type=1, vpn_index=1, oui=4
1003 # table=2, vss_type=0, vpn_id = "ip6-table-2"
1004 self.vapi.dhcp_proxy_set_vss(1, 1, oui=4, vpn_index=1, is_ip6=1)
1005 self.vapi.dhcp_proxy_set_vss(2, 0, "IPv6-table-2", is_ip6=1)
1007 self.pg4.add_stream(p_solicit_vrf1)
1008 self.pg_enable_capture(self.pg_interfaces)
1011 rx = self.pg1.get_capture(1)
1013 self.verify_dhcp6_solicit(rx[0], self.pg1,
1014 dhcp_solicit_src_vrf1,
1015 self.pg4.remote_mac,
1019 self.pg5.add_stream(p_solicit_vrf2)
1020 self.pg_enable_capture(self.pg_interfaces)
1023 rx = self.pg2.get_capture(1)
1025 self.verify_dhcp6_solicit(rx[0], self.pg2,
1026 dhcp_solicit_src_vrf2,
1027 self.pg5.remote_mac,
1028 vpn_id="IPv6-table-2")
1031 # Remove the VSS config
1032 # relayed DHCP has default vlaues in the option.
1034 self.vapi.dhcp_proxy_set_vss(1, is_ip6=1, is_add=0)
1036 self.pg4.add_stream(p_solicit_vrf1)
1037 self.pg_enable_capture(self.pg_interfaces)
1040 rx = self.pg1.get_capture(1)
1042 self.verify_dhcp6_solicit(rx[0], self.pg1,
1043 dhcp_solicit_src_vrf1,
1044 self.pg4.remote_mac)
1047 # Add a second DHCP server in VRF 1
1048 # expect clients messages to be relay to both configured servers
1050 self.pg1.generate_remote_hosts(2)
1051 server_addr12 = socket.inet_pton(AF_INET6,
1052 self.pg1.remote_hosts[1].ip6)
1054 self.vapi.dhcp_proxy_config(server_addr12,
1061 # We'll need an ND entry for the server to send it packets
1063 nd_entry = VppNeighbor(self,
1064 self.pg1.sw_if_index,
1065 self.pg1.remote_hosts[1].mac,
1066 self.pg1.remote_hosts[1].ip6)
1067 nd_entry.add_vpp_config()
1070 # Send a discover from the client. expect two relayed messages
1071 # The frist packet is sent to the second server
1072 # We're not enforcing that here, it's just the way it is.
1074 self.pg4.add_stream(p_solicit_vrf1)
1075 self.pg_enable_capture(self.pg_interfaces)
1078 rx = self.pg1.get_capture(2)
1080 self.verify_dhcp6_solicit(rx[0], self.pg1,
1081 dhcp_solicit_src_vrf1,
1082 self.pg4.remote_mac)
1083 self.verify_dhcp6_solicit(rx[1], self.pg1,
1084 dhcp_solicit_src_vrf1,
1085 self.pg4.remote_mac,
1086 dst_mac=self.pg1.remote_hosts[1].mac,
1087 dst_ip=self.pg1.remote_hosts[1].ip6)
1090 # Send both packets back. Client gets both.
1092 p1 = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) /
1093 IPv6(dst=self.pg1.local_ip6, src=self.pg1.remote_ip6) /
1094 UDP(sport=DHCP6_SERVER_PORT, dport=DHCP6_SERVER_PORT) /
1095 DHCP6_RelayReply(peeraddr=dhcp_solicit_src_vrf1) /
1096 DHCP6OptIfaceId(optlen=4, ifaceid='\x00\x00\x00\x05') /
1097 DHCP6OptRelayMsg(optlen=0) /
1098 DHCP6_Advertise(trid=1) /
1099 DHCP6OptStatusCode(statuscode=0))
1100 p2 = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_hosts[1].mac) /
1101 IPv6(dst=self.pg1.local_ip6, src=self.pg1._remote_hosts[1].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))
1111 self.pg1.add_stream(pkts)
1112 self.pg_enable_capture(self.pg_interfaces)
1115 rx = self.pg4.get_capture(2)
1117 self.verify_dhcp6_advert(rx[0], self.pg4, dhcp_solicit_src_vrf1)
1118 self.verify_dhcp6_advert(rx[1], self.pg4, dhcp_solicit_src_vrf1)
1121 # Ensure only solicit messages are duplicated
1123 p_request_vrf1 = (Ether(dst=dmac, src=self.pg4.remote_mac) /
1124 IPv6(src=dhcp_solicit_src_vrf1,
1125 dst=dhcp_solicit_dst) /
1126 UDP(sport=DHCP6_SERVER_PORT,
1127 dport=DHCP6_CLIENT_PORT) /
1130 self.pg4.add_stream(p_request_vrf1)
1131 self.pg_enable_capture(self.pg_interfaces)
1134 rx = self.pg1.get_capture(1)
1137 # Test we drop DHCP packets from addresses that are not configured as
1140 p2 = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_hosts[1].mac) /
1141 IPv6(dst=self.pg1.local_ip6, src="3001::1") /
1142 UDP(sport=DHCP6_SERVER_PORT, dport=DHCP6_SERVER_PORT) /
1143 DHCP6_RelayReply(peeraddr=dhcp_solicit_src_vrf1) /
1144 DHCP6OptIfaceId(optlen=4, ifaceid='\x00\x00\x00\x05') /
1145 DHCP6OptRelayMsg(optlen=0) /
1146 DHCP6_Advertise(trid=1) /
1147 DHCP6OptStatusCode(statuscode=0))
1148 self.send_and_assert_no_replies(self.pg1, p2,
1149 "DHCP6 not from server")
1152 # Remove the second DHCP server
1154 self.vapi.dhcp_proxy_config(server_addr12,
1162 # Test we can still relay with the first
1164 self.pg4.add_stream(p_solicit_vrf1)
1165 self.pg_enable_capture(self.pg_interfaces)
1168 rx = self.pg1.get_capture(1)
1170 self.verify_dhcp6_solicit(rx[0], self.pg1,
1171 dhcp_solicit_src_vrf1,
1172 self.pg4.remote_mac)
1177 self.vapi.dhcp_proxy_config(server_addr_vrf2,
1183 self.vapi.dhcp_proxy_config(server_addr_vrf1,
1189 self.vapi.dhcp_proxy_config(server_addr_vrf0,
1197 self.vapi.dhcp_proxy_config(server_addr_vrf0,
1203 self.pg3.unconfig_ip6()
1204 self.pg4.unconfig_ip6()
1205 self.pg5.unconfig_ip6()
1207 def test_dhcp_client(self):
1210 hostname = 'universal-dp'
1212 self.pg_enable_capture(self.pg_interfaces)
1215 # Configure DHCP client on PG3 and capture the discover sent
1217 self.vapi.dhcp_client_config(self.pg3.sw_if_index, hostname)
1219 rx = self.pg3.get_capture(1)
1221 self.verify_orig_dhcp_discover(rx[0], self.pg3, hostname)
1224 # Send back on offer, expect the request
1226 p_offer = (Ether(dst=self.pg3.local_mac, src=self.pg3.remote_mac) /
1227 IP(src=self.pg3.remote_ip4, dst="255.255.255.255") /
1228 UDP(sport=DHCP4_SERVER_PORT, dport=DHCP4_CLIENT_PORT) /
1230 yiaddr=self.pg3.local_ip4,
1231 chaddr=mac_pton(self.pg3.local_mac)) /
1232 DHCP(options=[('message-type', 'offer'),
1233 ('server_id', self.pg3.remote_ip4),
1236 self.pg3.add_stream(p_offer)
1237 self.pg_enable_capture(self.pg_interfaces)
1240 rx = self.pg3.get_capture(1)
1241 self.verify_orig_dhcp_request(rx[0], self.pg3, hostname,
1245 # Send an acknowledgment
1247 p_ack = (Ether(dst=self.pg3.local_mac, src=self.pg3.remote_mac) /
1248 IP(src=self.pg3.remote_ip4, dst="255.255.255.255") /
1249 UDP(sport=DHCP4_SERVER_PORT, dport=DHCP4_CLIENT_PORT) /
1250 BOOTP(op=1, yiaddr=self.pg3.local_ip4,
1251 chaddr=mac_pton(self.pg3.local_mac)) /
1252 DHCP(options=[('message-type', 'ack'),
1253 ('subnet_mask', "255.255.255.0"),
1254 ('router', self.pg3.remote_ip4),
1255 ('server_id', self.pg3.remote_ip4),
1256 ('lease_time', 43200),
1259 self.pg3.add_stream(p_ack)
1260 self.pg_enable_capture(self.pg_interfaces)
1264 # We'll get an ARP request for the router address
1266 rx = self.pg3.get_capture(1)
1268 self.assertEqual(rx[0][ARP].pdst, self.pg3.remote_ip4)
1269 self.pg_enable_capture(self.pg_interfaces)
1272 # At the end of this procedure there should be a connected route
1275 self.assertTrue(find_route(self, self.pg3.local_ip4, 24))
1276 self.assertTrue(find_route(self, self.pg3.local_ip4, 32))
1278 # remove the left over ARP entry
1279 self.vapi.ip_neighbor_add_del(self.pg3.sw_if_index,
1280 self.pg3.remote_mac,
1281 self.pg3.remote_ip4,
1285 # remove the DHCP config
1287 self.vapi.dhcp_client_config(self.pg3.sw_if_index, hostname, is_add=0)
1290 # and now the route should be gone
1292 self.assertFalse(find_route(self, self.pg3.local_ip4, 32))
1293 self.assertFalse(find_route(self, self.pg3.local_ip4, 24))
1296 # Start the procedure again. this time have VPP send the client-ID
1298 self.pg3.admin_down()
1301 self.vapi.dhcp_client_config(self.pg3.sw_if_index, hostname,
1302 client_id=self.pg3.local_mac)
1304 rx = self.pg3.get_capture(1)
1306 self.verify_orig_dhcp_discover(rx[0], self.pg3, hostname,
1309 # TODO: VPP DHCP client should not accept DHCP OFFER message with
1310 # the XID (Transaction ID) not matching the XID of the most recent
1311 # DHCP DISCOVERY message.
1312 # Such DHCP OFFER message must be silently discarded - RFC2131.
1313 # Reported in Jira ticket: VPP-99
1314 self.pg3.add_stream(p_offer)
1315 self.pg_enable_capture(self.pg_interfaces)
1318 rx = self.pg3.get_capture(1)
1319 self.verify_orig_dhcp_request(rx[0], self.pg3, hostname,
1323 # unicast the ack to the offered address
1325 p_ack = (Ether(dst=self.pg3.local_mac, src=self.pg3.remote_mac) /
1326 IP(src=self.pg3.remote_ip4, dst=self.pg3.local_ip4) /
1327 UDP(sport=DHCP4_SERVER_PORT, dport=DHCP4_CLIENT_PORT) /
1328 BOOTP(op=1, yiaddr=self.pg3.local_ip4,
1329 chaddr=mac_pton(self.pg3.local_mac)) /
1330 DHCP(options=[('message-type', 'ack'),
1331 ('subnet_mask', "255.255.255.0"),
1332 ('router', self.pg3.remote_ip4),
1333 ('server_id', self.pg3.remote_ip4),
1334 ('lease_time', 43200),
1337 self.pg3.add_stream(p_ack)
1338 self.pg_enable_capture(self.pg_interfaces)
1342 # We'll get an ARP request for the router address
1344 rx = self.pg3.get_capture(1)
1346 self.assertEqual(rx[0][ARP].pdst, self.pg3.remote_ip4)
1347 self.pg_enable_capture(self.pg_interfaces)
1350 # At the end of this procedure there should be a connected route
1353 self.assertTrue(find_route(self, self.pg3.local_ip4, 32))
1354 self.assertTrue(find_route(self, self.pg3.local_ip4, 24))
1357 # remove the DHCP config
1359 self.vapi.dhcp_client_config(self.pg3.sw_if_index, hostname, is_add=0)
1361 self.assertFalse(find_route(self, self.pg3.local_ip4, 32))
1362 self.assertFalse(find_route(self, self.pg3.local_ip4, 24))
1365 # Rince and repeat, this time with VPP configured not to set
1366 # the braodcast flag in the discover and request messages,
1367 # and for the server to unicast the responses.
1369 # Configure DHCP client on PG3 and capture the discover sent
1371 self.vapi.dhcp_client_config(self.pg3.sw_if_index, hostname,
1372 set_broadcast_flag=0)
1374 rx = self.pg3.get_capture(1)
1376 self.verify_orig_dhcp_discover(rx[0], self.pg3, hostname,
1380 # Send back on offer, unicasted to the offered address.
1381 # Expect the request.
1383 p_offer = (Ether(dst=self.pg3.local_mac, src=self.pg3.remote_mac) /
1384 IP(src=self.pg3.remote_ip4, dst=self.pg3.local_ip4) /
1385 UDP(sport=DHCP4_SERVER_PORT, dport=DHCP4_CLIENT_PORT) /
1386 BOOTP(op=1, yiaddr=self.pg3.local_ip4,
1387 chaddr=mac_pton(self.pg3.local_mac)) /
1388 DHCP(options=[('message-type', 'offer'),
1389 ('server_id', self.pg3.remote_ip4),
1392 self.pg3.add_stream(p_offer)
1393 self.pg_enable_capture(self.pg_interfaces)
1396 rx = self.pg3.get_capture(1)
1397 self.verify_orig_dhcp_request(rx[0], self.pg3, hostname,
1402 # Send an acknowledgment
1404 p_ack = (Ether(dst=self.pg3.local_mac, src=self.pg3.remote_mac) /
1405 IP(src=self.pg3.remote_ip4, dst=self.pg3.local_ip4) /
1406 UDP(sport=DHCP4_SERVER_PORT, dport=DHCP4_CLIENT_PORT) /
1407 BOOTP(op=1, yiaddr=self.pg3.local_ip4,
1408 chaddr=mac_pton(self.pg3.local_mac)) /
1409 DHCP(options=[('message-type', 'ack'),
1410 ('subnet_mask', "255.255.255.0"),
1411 ('router', self.pg3.remote_ip4),
1412 ('server_id', self.pg3.remote_ip4),
1413 ('lease_time', 43200),
1416 self.pg3.add_stream(p_ack)
1417 self.pg_enable_capture(self.pg_interfaces)
1421 # We'll get an ARP request for the router address
1423 rx = self.pg3.get_capture(1)
1425 self.assertEqual(rx[0][ARP].pdst, self.pg3.remote_ip4)
1426 self.pg_enable_capture(self.pg_interfaces)
1429 # At the end of this procedure there should be a connected route
1432 self.assertTrue(find_route(self, self.pg3.local_ip4, 24))
1433 self.assertTrue(find_route(self, self.pg3.local_ip4, 32))
1435 # remove the left over ARP entry
1436 self.vapi.ip_neighbor_add_del(self.pg3.sw_if_index,
1437 self.pg3.remote_mac,
1438 self.pg3.remote_ip4,
1442 # read the DHCP client details from a dump
1444 clients = self.vapi.dhcp_client_dump()
1446 self.assertEqual(clients[0].client.sw_if_index,
1447 self.pg3.sw_if_index)
1448 self.assertEqual(clients[0].lease.sw_if_index,
1449 self.pg3.sw_if_index)
1450 self.assertEqual(clients[0].client.hostname.rstrip('\0'),
1452 self.assertEqual(clients[0].lease.hostname.rstrip('\0'),
1454 self.assertEqual(clients[0].lease.is_ipv6, 0)
1455 # 0 = DISCOVER, 1 = REQUEST, 2 = BOUND
1456 self.assertEqual(clients[0].lease.state, 2)
1457 self.assertEqual(clients[0].lease.mask_width, 24)
1458 self.assertEqual(clients[0].lease.router_address.rstrip('\0'),
1459 self.pg3.remote_ip4n)
1460 self.assertEqual(clients[0].lease.host_address.rstrip('\0'),
1461 self.pg3.local_ip4n)
1464 # remove the DHCP config
1466 self.vapi.dhcp_client_config(self.pg3.sw_if_index, hostname, is_add=0)
1469 # and now the route should be gone
1471 self.assertFalse(find_route(self, self.pg3.local_ip4, 32))
1472 self.assertFalse(find_route(self, self.pg3.local_ip4, 24))
1475 # Start the procedure again. Use requested lease time option.
1477 self.pg3.admin_down()
1480 self.vapi.dhcp_client_config(self.pg3.sw_if_index, hostname)
1482 rx = self.pg3.get_capture(1)
1484 self.verify_orig_dhcp_discover(rx[0], self.pg3, hostname)
1487 # Send back on offer with requested lease time, expect the request
1490 p_offer = (Ether(dst=self.pg3.local_mac, src=self.pg3.remote_mac) /
1491 IP(src=self.pg3.remote_ip4, dst='255.255.255.255') /
1492 UDP(sport=DHCP4_SERVER_PORT, dport=DHCP4_CLIENT_PORT) /
1494 yiaddr=self.pg3.local_ip4,
1495 chaddr=mac_pton(self.pg3.local_mac)) /
1496 DHCP(options=[('message-type', 'offer'),
1497 ('server_id', self.pg3.remote_ip4),
1498 ('lease_time', lease_time),
1501 self.pg3.add_stream(p_offer)
1502 self.pg_enable_capture(self.pg_interfaces)
1505 rx = self.pg3.get_capture(1)
1506 self.verify_orig_dhcp_request(rx[0], self.pg3, hostname,
1510 # Send an acknowledgment
1512 p_ack = (Ether(dst=self.pg3.local_mac, src=self.pg3.remote_mac) /
1513 IP(src=self.pg3.remote_ip4, dst='255.255.255.255') /
1514 UDP(sport=DHCP4_SERVER_PORT, dport=DHCP4_CLIENT_PORT) /
1515 BOOTP(op=1, yiaddr=self.pg3.local_ip4,
1516 chaddr=mac_pton(self.pg3.local_mac)) /
1517 DHCP(options=[('message-type', 'ack'),
1518 ('subnet_mask', '255.255.255.0'),
1519 ('router', self.pg3.remote_ip4),
1520 ('server_id', self.pg3.remote_ip4),
1521 ('lease_time', lease_time),
1524 self.pg3.add_stream(p_ack)
1525 self.pg_enable_capture(self.pg_interfaces)
1529 # We'll get an ARP request for the router address
1531 rx = self.pg3.get_capture(1)
1533 self.assertEqual(rx[0][ARP].pdst, self.pg3.remote_ip4)
1536 # At the end of this procedure there should be a connected route
1539 self.assertTrue(find_route(self, self.pg3.local_ip4, 32))
1540 self.assertTrue(find_route(self, self.pg3.local_ip4, 24))
1542 # remove the left over ARP entry
1543 self.vapi.ip_neighbor_add_del(self.pg3.sw_if_index,
1544 self.pg3.remote_mac,
1545 self.pg3.remote_ip4,
1549 # the route should be gone after the lease expires
1551 self.assertTrue(self.wait_for_no_route(self.pg3.local_ip4, 32))
1552 self.assertTrue(self.wait_for_no_route(self.pg3.local_ip4, 24))
1555 # remove the DHCP config
1557 self.vapi.dhcp_client_config(self.pg3.sw_if_index, hostname, is_add=0)
1560 if __name__ == '__main__':
1561 unittest.main(testRunner=VppTestRunner)