X-Git-Url: https://gerrit.fd.io/r/gitweb?a=blobdiff_plain;f=test%2Ftest_dhcp.py;h=fe97f6c9a2b5c031c7b25ecdbc8ff6a323ad352f;hb=bb7f0f644ac861cf8f57d2445ce516ed378ced4c;hp=fbfb8a0c0df8e87b3af5af3e94da9ba88a1b332d;hpb=20a175a18414c67e38b5ce0709b33fb1df8069c9;p=vpp.git diff --git a/test/test_dhcp.py b/test/test_dhcp.py index fbfb8a0c0df..fe97f6c9a2b 100644 --- a/test/test_dhcp.py +++ b/test/test_dhcp.py @@ -2,16 +2,20 @@ import unittest import socket +import struct from framework import VppTestCase, VppTestRunner +from vpp_neighbor import VppNeighbor +from vpp_ip_route import find_route, VppIpTable +from util import mk_ll_addr -from scapy.layers.l2 import Ether, getmacbyip +from scapy.layers.l2 import Ether, getmacbyip, ARP from scapy.layers.inet import IP, UDP, ICMP from scapy.layers.inet6 import IPv6, in6_getnsmac, in6_mactoifaceid from scapy.layers.dhcp import DHCP, BOOTP, DHCPTypes from scapy.layers.dhcp6 import DHCP6, DHCP6_Solicit, DHCP6_RelayForward, \ DHCP6_RelayReply, DHCP6_Advertise, DHCP6OptRelayMsg, DHCP6OptIfaceId, \ - DHCP6OptStatusCode, DHCP6OptVSS, DHCP6OptClientLinkLayerAddr + DHCP6OptStatusCode, DHCP6OptVSS, DHCP6OptClientLinkLayerAddr, DHCP6_Request from socket import AF_INET, AF_INET6 from scapy.utils import inet_pton, inet_ntop from scapy.utils6 import in6_ptop @@ -22,13 +26,6 @@ DHCP6_CLIENT_PORT = 547 DHCP6_SERVER_PORT = 546 -def mk_ll_addr(mac): - - euid = in6_mactoifaceid(mac) - addr = "fe80::" + euid - return addr - - class TestDHCP(VppTestCase): """ DHCP Test Case """ @@ -37,9 +34,19 @@ class TestDHCP(VppTestCase): # create 3 pg interfaces self.create_pg_interfaces(range(4)) + self.tables = [] # pg0 and 1 are IP configured in VRF 0 and 1. # pg2 and 3 are non IP-configured in VRF 0 and 1 + table_id = 0 + for table_id in range(1, 4): + tbl4 = VppIpTable(self, table_id) + tbl4.add_vpp_config() + self.tables.append(tbl4) + tbl6 = VppIpTable(self, table_id, is_ip6=1) + tbl6.add_vpp_config() + self.tables.append(tbl6) + table_id = 0 for i in self.pg_interfaces[:2]: i.admin_up() @@ -58,6 +65,17 @@ class TestDHCP(VppTestCase): i.set_table_ip6(table_id) table_id += 1 + def tearDown(self): + for i in self.pg_interfaces[:2]: + i.unconfig_ip4() + i.unconfig_ip6() + + for i in self.pg_interfaces: + i.set_table_ip4(0) + i.set_table_ip6(0) + i.admin_down() + super(TestDHCP, self).tearDown() + def send_and_assert_no_replies(self, intf, pkts, remark): intf.add_stream(pkts) self.pg_enable_capture(self.pg_interfaces) @@ -65,6 +83,18 @@ class TestDHCP(VppTestCase): for i in self.pg_interfaces: i.assert_nothing_captured(remark=remark) + def verify_dhcp_has_option(self, pkt, option, value): + dhcp = pkt[DHCP] + found = False + + for i in dhcp.options: + if type(i) is tuple: + if i[0] == option: + self.assertEqual(i[1], value) + found = True + + self.assertTrue(found) + def validate_relay_options(self, pkt, intf, ip_addr, fib_id, oui): dhcp = pkt[DHCP] found = 0 @@ -133,7 +163,17 @@ class TestDHCP(VppTestCase): return data - def verify_dhcp_offer(self, pkt, intf): + def verify_dhcp_msg_type(self, pkt, name): + dhcp = pkt[DHCP] + found = False + for o in dhcp.options: + if type(o) is tuple: + if o[0] == "message-type" \ + and DHCPTypes[o[1]] == name: + found = True + self.assertTrue(found) + + def verify_dhcp_offer(self, pkt, intf, fib_id=0, oui=0): ether = pkt[Ether] self.assertEqual(ether.dst, "ff:ff:ff:ff:ff:ff") self.assertEqual(ether.src, intf.local_mac) @@ -146,24 +186,60 @@ class TestDHCP(VppTestCase): self.assertEqual(udp.dport, DHCP4_CLIENT_PORT) self.assertEqual(udp.sport, DHCP4_SERVER_PORT) - dhcp = pkt[DHCP] - is_offer = False - for o in dhcp.options: - if type(o) is tuple: - if o[0] == "message-type" \ - and DHCPTypes[o[1]] == "offer": - is_offer = True - self.assertTrue(is_offer) + self.verify_dhcp_msg_type(pkt, "offer") + data = self.validate_relay_options(pkt, intf, intf.local_ip4, + fib_id, oui) - data = self.validate_relay_options(pkt, intf, intf.local_ip4, 0, 0) + def verify_orig_dhcp_pkt(self, pkt, intf): + ether = pkt[Ether] + self.assertEqual(ether.dst, "ff:ff:ff:ff:ff:ff") + self.assertEqual(ether.src, intf.local_mac) + + ip = pkt[IP] + self.assertEqual(ip.dst, "255.255.255.255") + self.assertEqual(ip.src, "0.0.0.0") + + udp = pkt[UDP] + self.assertEqual(udp.dport, DHCP4_SERVER_PORT) + self.assertEqual(udp.sport, DHCP4_CLIENT_PORT) + + def verify_orig_dhcp_discover(self, pkt, intf, hostname, client_id=None): + self.verify_orig_dhcp_pkt(pkt, intf) + + self.verify_dhcp_msg_type(pkt, "discover") + self.verify_dhcp_has_option(pkt, "hostname", hostname) + if client_id: + self.verify_dhcp_has_option(pkt, "client_id", client_id) + bootp = pkt[BOOTP] + self.assertEqual(bootp.ciaddr, "0.0.0.0") + self.assertEqual(bootp.giaddr, "0.0.0.0") + self.assertEqual(bootp.flags, 0x8000) + + def verify_orig_dhcp_request(self, pkt, intf, hostname, ip): + self.verify_orig_dhcp_pkt(pkt, intf) + + self.verify_dhcp_msg_type(pkt, "request") + self.verify_dhcp_has_option(pkt, "hostname", hostname) + self.verify_dhcp_has_option(pkt, "requested_addr", ip) + bootp = pkt[BOOTP] + self.assertEqual(bootp.ciaddr, "0.0.0.0") + self.assertEqual(bootp.giaddr, "0.0.0.0") + self.assertEqual(bootp.flags, 0x8000) + + def verify_relayed_dhcp_discover(self, pkt, intf, src_intf=None, + fib_id=0, oui=0, + dst_mac=None, dst_ip=None): + if not dst_mac: + dst_mac = intf.remote_mac + if not dst_ip: + dst_ip = intf.remote_ip4 - def verify_dhcp_discover(self, pkt, intf, src_intf=None, fib_id=0, oui=0): ether = pkt[Ether] - self.assertEqual(ether.dst, intf.remote_mac) + self.assertEqual(ether.dst, dst_mac) self.assertEqual(ether.src, intf.local_mac) ip = pkt[IP] - self.assertEqual(ip.dst, intf.remote_ip4) + self.assertEqual(ip.dst, dst_ip) self.assertEqual(ip.src, intf.local_ip4) udp = pkt[UDP] @@ -188,13 +264,20 @@ class TestDHCP(VppTestCase): def verify_dhcp6_solicit(self, pkt, intf, peer_ip, peer_mac, fib_id=0, - oui=0): + oui=0, + dst_mac=None, + dst_ip=None): + if not dst_mac: + dst_mac = intf.remote_mac + if not dst_ip: + dst_ip = in6_ptop(intf.remote_ip6) + ether = pkt[Ether] - self.assertEqual(ether.dst, intf.remote_mac) + self.assertEqual(ether.dst, dst_mac) self.assertEqual(ether.src, intf.local_mac) ip = pkt[IPv6] - self.assertEqual(in6_ptop(ip.dst), in6_ptop(intf.remote_ip6)) + self.assertEqual(in6_ptop(ip.dst), dst_ip) self.assertEqual(in6_ptop(ip.src), in6_ptop(intf.local_ip6)) udp = pkt[UDP] @@ -293,7 +376,7 @@ class TestDHCP(VppTestCase): # # Inject a response from the server # dropped, because there is no IP addrees on the - # clinet interfce to fill in the option. + # client interfce to fill in the option. # p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / IP(src=self.pg0.remote_ip4, dst=self.pg0.local_ip4) / @@ -324,7 +407,8 @@ class TestDHCP(VppTestCase): rx = self.pg0.get_capture(1) rx = rx[0] - option_82 = self.verify_dhcp_discover(rx, self.pg0, src_intf=self.pg2) + option_82 = self.verify_relayed_dhcp_discover(rx, self.pg0, + src_intf=self.pg2) # # Create an DHCP offer reply from the server with a correctly formatted @@ -429,7 +513,7 @@ class TestDHCP(VppTestCase): rx = self.pg1.get_capture(1) rx = rx[0] - self.verify_dhcp_discover(rx, self.pg1, src_intf=self.pg3) + self.verify_relayed_dhcp_discover(rx, self.pg1, src_intf=self.pg3) # # Add VSS config @@ -442,8 +526,133 @@ class TestDHCP(VppTestCase): rx = self.pg1.get_capture(1) rx = rx[0] - self.verify_dhcp_discover(rx, self.pg1, src_intf=self.pg3, - fib_id=1, oui=4) + self.verify_relayed_dhcp_discover(rx, self.pg1, + src_intf=self.pg3, + fib_id=1, oui=4) + + # + # Add a second DHCP server in VRF 1 + # expect clients messages to be relay to both configured servers + # + self.pg1.generate_remote_hosts(2) + server_addr2 = socket.inet_pton(AF_INET, self.pg1.remote_hosts[1].ip4) + + self.vapi.dhcp_proxy_config(server_addr2, + src_addr, + rx_table_id=1, + server_table_id=1, + is_add=1) + + # + # We'll need an ARP entry for the server to send it packets + # + arp_entry = VppNeighbor(self, + self.pg1.sw_if_index, + self.pg1.remote_hosts[1].mac, + self.pg1.remote_hosts[1].ip4) + arp_entry.add_vpp_config() + + # + # Send a discover from the client. expect two relayed messages + # The frist packet is sent to the second server + # We're not enforcing that here, it's just the way it is. + # + self.pg3.add_stream(pkts_disc_vrf1) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + rx = self.pg1.get_capture(2) + + option_82 = self.verify_relayed_dhcp_discover( + rx[0], self.pg1, + src_intf=self.pg3, + dst_mac=self.pg1.remote_hosts[1].mac, + dst_ip=self.pg1.remote_hosts[1].ip4, + fib_id=1, oui=4) + self.verify_relayed_dhcp_discover(rx[1], self.pg1, + src_intf=self.pg3, + fib_id=1, oui=4) + + # + # Send both packets back. Client gets both. + # + p1 = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) / + IP(src=self.pg1.remote_ip4, dst=self.pg1.local_ip4) / + UDP(sport=DHCP4_SERVER_PORT, dport=DHCP4_SERVER_PORT) / + BOOTP(op=1) / + DHCP(options=[('message-type', 'offer'), + ('relay_agent_Information', option_82), + ('end')])) + p2 = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) / + IP(src=self.pg1.remote_hosts[1].ip4, dst=self.pg1.local_ip4) / + UDP(sport=DHCP4_SERVER_PORT, dport=DHCP4_SERVER_PORT) / + BOOTP(op=1) / + DHCP(options=[('message-type', 'offer'), + ('relay_agent_Information', option_82), + ('end')])) + pkts = [p1, p2] + + self.pg1.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + rx = self.pg3.get_capture(2) + + self.verify_dhcp_offer(rx[0], self.pg3, fib_id=1, oui=4) + self.verify_dhcp_offer(rx[1], self.pg3, fib_id=1, oui=4) + + # + # Ensure offers from non-servers are dropeed + # + p2 = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) / + IP(src="8.8.8.8", dst=self.pg1.local_ip4) / + UDP(sport=DHCP4_SERVER_PORT, dport=DHCP4_SERVER_PORT) / + BOOTP(op=1) / + DHCP(options=[('message-type', 'offer'), + ('relay_agent_Information', option_82), + ('end')])) + self.send_and_assert_no_replies(self.pg1, p2, + "DHCP offer from non-server") + + # + # Ensure only the discover is sent to multiple servers + # + p_req_vrf1 = (Ether(dst="ff:ff:ff:ff:ff:ff", + src=self.pg3.remote_mac) / + IP(src="0.0.0.0", dst="255.255.255.255") / + UDP(sport=DHCP4_CLIENT_PORT, + dport=DHCP4_SERVER_PORT) / + BOOTP(op=1) / + DHCP(options=[('message-type', 'request'), + ('end')])) + + self.pg3.add_stream(p_req_vrf1) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + rx = self.pg1.get_capture(1) + + # + # Remove the second DHCP server + # + self.vapi.dhcp_proxy_config(server_addr2, + src_addr, + rx_table_id=1, + server_table_id=1, + is_add=0) + + # + # Test we can still relay with the first + # + self.pg3.add_stream(pkts_disc_vrf1) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + rx = self.pg1.get_capture(1) + rx = rx[0] + self.verify_relayed_dhcp_discover(rx, self.pg1, + src_intf=self.pg3, + fib_id=1, oui=4) # # Remove the VSS config @@ -457,7 +666,7 @@ class TestDHCP(VppTestCase): rx = self.pg1.get_capture(1) rx = rx[0] - self.verify_dhcp_discover(rx, self.pg1, src_intf=self.pg3) + self.verify_relayed_dhcp_discover(rx, self.pg1, src_intf=self.pg3) # # remove DHCP config to cleanup @@ -465,13 +674,15 @@ class TestDHCP(VppTestCase): self.vapi.dhcp_proxy_config(server_addr, src_addr, rx_table_id=1, - server_table_id=11, + server_table_id=1, is_add=0) self.send_and_assert_no_replies(self.pg2, pkts_disc_vrf0, "DHCP cleanup VRF 0") self.send_and_assert_no_replies(self.pg3, pkts_disc_vrf1, "DHCP cleanup VRF 1") + self.pg2.unconfig_ip4() + self.pg3.unconfig_ip4() def test_dhcp6_proxy(self): """ DHCPv6 Proxy""" @@ -493,18 +704,16 @@ class TestDHCP(VppTestCase): UDP(sport=DHCP6_SERVER_PORT, dport=DHCP6_CLIENT_PORT) / DHCP6_Solicit()) - pkts_solicit_vrf0 = [p_solicit_vrf0] p_solicit_vrf1 = (Ether(dst=dmac, src=self.pg3.remote_mac) / IPv6(src=dhcp_solicit_src_vrf1, dst=dhcp_solicit_dst) / UDP(sport=DHCP6_SERVER_PORT, dport=DHCP6_CLIENT_PORT) / DHCP6_Solicit()) - pkts_solicit_vrf1 = [p_solicit_vrf1] - self.send_and_assert_no_replies(self.pg2, pkts_solicit_vrf0, + self.send_and_assert_no_replies(self.pg2, p_solicit_vrf0, "DHCP with no configuration") - self.send_and_assert_no_replies(self.pg3, pkts_solicit_vrf1, + self.send_and_assert_no_replies(self.pg3, p_solicit_vrf1, "DHCP with no configuration") # @@ -518,9 +727,9 @@ class TestDHCP(VppTestCase): server_table_id=0, is_ipv6=1) - self.send_and_assert_no_replies(self.pg2, pkts_solicit_vrf0, + self.send_and_assert_no_replies(self.pg2, p_solicit_vrf0, "DHCP with no configuration") - self.send_and_assert_no_replies(self.pg3, pkts_solicit_vrf1, + self.send_and_assert_no_replies(self.pg3, p_solicit_vrf1, "DHCP with no configuration") # @@ -531,13 +740,13 @@ class TestDHCP(VppTestCase): # # Now the DHCP requests are relayed to the server # - self.pg2.add_stream(pkts_solicit_vrf0) + self.pg2.add_stream(p_solicit_vrf0) self.pg_enable_capture(self.pg_interfaces) self.pg_start() rx = self.pg0.get_capture(1) - rx = rx[0] - self.verify_dhcp6_solicit(rx, self.pg0, + + self.verify_dhcp6_solicit(rx[0], self.pg0, dhcp_solicit_src_vrf0, self.pg2.remote_mac) @@ -550,8 +759,7 @@ class TestDHCP(VppTestCase): IPv6(dst=self.pg0.local_ip6, src=self.pg0.remote_ip6) / UDP(sport=DHCP6_SERVER_PORT, dport=DHCP6_SERVER_PORT) / DHCP6_Advertise()) - pkts_adv_vrf0 = [p_adv_vrf0] - self.send_and_assert_no_replies(self.pg2, pkts_adv_vrf0, + self.send_and_assert_no_replies(self.pg2, p_adv_vrf0, "DHCP6 not a relay reply") # 2 - no relay message option @@ -560,8 +768,7 @@ class TestDHCP(VppTestCase): UDP(sport=DHCP6_SERVER_PORT, dport=DHCP6_SERVER_PORT) / DHCP6_RelayReply() / DHCP6_Advertise()) - pkts_adv_vrf0 = [p_adv_vrf0] - self.send_and_assert_no_replies(self.pg2, pkts_adv_vrf0, + self.send_and_assert_no_replies(self.pg2, p_adv_vrf0, "DHCP not a relay message") # 3 - no circuit ID @@ -571,8 +778,7 @@ class TestDHCP(VppTestCase): DHCP6_RelayReply() / DHCP6OptRelayMsg(optlen=0) / DHCP6_Advertise()) - pkts_adv_vrf0 = [p_adv_vrf0] - self.send_and_assert_no_replies(self.pg2, pkts_adv_vrf0, + self.send_and_assert_no_replies(self.pg2, p_adv_vrf0, "DHCP6 no circuit ID") # 4 - wrong circuit ID p_adv_vrf0 = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / @@ -582,8 +788,7 @@ class TestDHCP(VppTestCase): DHCP6OptIfaceId(optlen=4, ifaceid='\x00\x00\x00\x05') / DHCP6OptRelayMsg(optlen=0) / DHCP6_Advertise()) - pkts_adv_vrf0 = [p_adv_vrf0] - self.send_and_assert_no_replies(self.pg2, pkts_adv_vrf0, + self.send_and_assert_no_replies(self.pg2, p_adv_vrf0, "DHCP6 wrong circuit ID") # @@ -604,8 +809,8 @@ class TestDHCP(VppTestCase): self.pg_start() rx = self.pg2.get_capture(1) - rx = rx[0] - self.verify_dhcp6_advert(rx, self.pg2, "::") + + self.verify_dhcp6_advert(rx[0], self.pg2, "::") # # Send the relay response (the advertisement) @@ -625,8 +830,8 @@ class TestDHCP(VppTestCase): self.pg_start() rx = self.pg2.get_capture(1) - rx = rx[0] - self.verify_dhcp6_advert(rx, self.pg2, dhcp_solicit_src_vrf0) + + self.verify_dhcp6_advert(rx[0], self.pg2, dhcp_solicit_src_vrf0) # # Add all the config for VRF 1 @@ -641,13 +846,13 @@ class TestDHCP(VppTestCase): # # VRF 1 solicit # - self.pg3.add_stream(pkts_solicit_vrf1) + self.pg3.add_stream(p_solicit_vrf1) self.pg_enable_capture(self.pg_interfaces) self.pg_start() rx = self.pg1.get_capture(1) - rx = rx[0] - self.verify_dhcp6_solicit(rx, self.pg1, + + self.verify_dhcp6_solicit(rx[0], self.pg1, dhcp_solicit_src_vrf1, self.pg3.remote_mac) @@ -669,21 +874,21 @@ class TestDHCP(VppTestCase): self.pg_start() rx = self.pg3.get_capture(1) - rx = rx[0] - self.verify_dhcp6_advert(rx, self.pg3, dhcp_solicit_src_vrf1) + + self.verify_dhcp6_advert(rx[0], self.pg3, dhcp_solicit_src_vrf1) # # Add VSS config # table=1, fib=id=1, oui=4 self.vapi.dhcp_proxy_set_vss(1, 1, 4, is_ip6=1) - self.pg3.add_stream(pkts_solicit_vrf1) + self.pg3.add_stream(p_solicit_vrf1) self.pg_enable_capture(self.pg_interfaces) self.pg_start() rx = self.pg1.get_capture(1) - rx = rx[0] - self.verify_dhcp6_solicit(rx, self.pg1, + + self.verify_dhcp6_solicit(rx[0], self.pg1, dhcp_solicit_src_vrf1, self.pg3.remote_mac, fib_id=1, @@ -695,31 +900,309 @@ class TestDHCP(VppTestCase): # self.vapi.dhcp_proxy_set_vss(1, 1, 4, is_ip6=1, is_add=0) - self.pg3.add_stream(pkts_solicit_vrf1) + self.pg3.add_stream(p_solicit_vrf1) self.pg_enable_capture(self.pg_interfaces) self.pg_start() rx = self.pg1.get_capture(1) - rx = rx[0] - self.verify_dhcp6_solicit(rx, self.pg1, + + self.verify_dhcp6_solicit(rx[0], self.pg1, dhcp_solicit_src_vrf1, self.pg3.remote_mac) # - # Cleanup + # Add a second DHCP server in VRF 1 + # expect clients messages to be relay to both configured servers # - self.vapi.dhcp_proxy_config(server_addr_vrf1, + self.pg1.generate_remote_hosts(2) + server_addr2 = socket.inet_pton(AF_INET6, self.pg1.remote_hosts[1].ip6) + + self.vapi.dhcp_proxy_config(server_addr2, + src_addr_vrf1, + rx_table_id=1, + server_table_id=1, + is_ipv6=1) + + # + # We'll need an ND entry for the server to send it packets + # + nd_entry = VppNeighbor(self, + self.pg1.sw_if_index, + self.pg1.remote_hosts[1].mac, + self.pg1.remote_hosts[1].ip6, + af=AF_INET6) + nd_entry.add_vpp_config() + + # + # Send a discover from the client. expect two relayed messages + # The frist packet is sent to the second server + # We're not enforcing that here, it's just the way it is. + # + self.pg3.add_stream(p_solicit_vrf1) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + rx = self.pg1.get_capture(2) + + self.verify_dhcp6_solicit(rx[0], self.pg1, + dhcp_solicit_src_vrf1, + self.pg3.remote_mac) + self.verify_dhcp6_solicit(rx[1], self.pg1, + dhcp_solicit_src_vrf1, + self.pg3.remote_mac, + dst_mac=self.pg1.remote_hosts[1].mac, + dst_ip=self.pg1.remote_hosts[1].ip6) + + # + # Send both packets back. Client gets both. + # + p1 = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) / + IPv6(dst=self.pg1.local_ip6, src=self.pg1.remote_ip6) / + UDP(sport=DHCP6_SERVER_PORT, dport=DHCP6_SERVER_PORT) / + DHCP6_RelayReply(peeraddr=dhcp_solicit_src_vrf1) / + DHCP6OptIfaceId(optlen=4, ifaceid='\x00\x00\x00\x04') / + DHCP6OptRelayMsg(optlen=0) / + DHCP6_Advertise(trid=1) / + DHCP6OptStatusCode(statuscode=0)) + p2 = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_hosts[1].mac) / + IPv6(dst=self.pg1.local_ip6, src=self.pg1._remote_hosts[1].ip6) / + UDP(sport=DHCP6_SERVER_PORT, dport=DHCP6_SERVER_PORT) / + DHCP6_RelayReply(peeraddr=dhcp_solicit_src_vrf1) / + DHCP6OptIfaceId(optlen=4, ifaceid='\x00\x00\x00\x04') / + DHCP6OptRelayMsg(optlen=0) / + DHCP6_Advertise(trid=1) / + DHCP6OptStatusCode(statuscode=0)) + + pkts = [p1, p2] + + self.pg1.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + rx = self.pg3.get_capture(2) + + self.verify_dhcp6_advert(rx[0], self.pg3, dhcp_solicit_src_vrf1) + self.verify_dhcp6_advert(rx[1], self.pg3, dhcp_solicit_src_vrf1) + + # + # Ensure only solicit messages are duplicated + # + p_request_vrf1 = (Ether(dst=dmac, src=self.pg3.remote_mac) / + IPv6(src=dhcp_solicit_src_vrf1, + dst=dhcp_solicit_dst) / + UDP(sport=DHCP6_SERVER_PORT, + dport=DHCP6_CLIENT_PORT) / + DHCP6_Request()) + + self.pg3.add_stream(p_request_vrf1) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + rx = self.pg1.get_capture(1) + + # + # Test we drop DHCP packets from addresses that are not configured as + # DHCP servers + # + p2 = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_hosts[1].mac) / + IPv6(dst=self.pg1.local_ip6, src="3001::1") / + UDP(sport=DHCP6_SERVER_PORT, dport=DHCP6_SERVER_PORT) / + DHCP6_RelayReply(peeraddr=dhcp_solicit_src_vrf1) / + DHCP6OptIfaceId(optlen=4, ifaceid='\x00\x00\x00\x04') / + DHCP6OptRelayMsg(optlen=0) / + DHCP6_Advertise(trid=1) / + DHCP6OptStatusCode(statuscode=0)) + self.send_and_assert_no_replies(self.pg1, p2, + "DHCP6 not from server") + + # + # Remove the second DHCP server + # + self.vapi.dhcp_proxy_config(server_addr2, src_addr_vrf1, rx_table_id=1, server_table_id=1, is_ipv6=1, is_add=0) + + # + # Test we can still relay with the first + # + self.pg3.add_stream(p_solicit_vrf1) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + rx = self.pg1.get_capture(1) + + self.verify_dhcp6_solicit(rx[0], self.pg1, + dhcp_solicit_src_vrf1, + self.pg3.remote_mac) + + # + # Cleanup + # self.vapi.dhcp_proxy_config(server_addr_vrf1, src_addr_vrf1, + rx_table_id=1, + server_table_id=1, + is_ipv6=1, + is_add=0) + self.vapi.dhcp_proxy_config(server_addr_vrf0, + src_addr_vrf0, + rx_table_id=0, + server_table_id=0, + is_ipv6=1, + is_add=0) + + # duplicate delete + self.vapi.dhcp_proxy_config(server_addr_vrf0, + src_addr_vrf0, rx_table_id=0, server_table_id=0, is_ipv6=1, is_add=0) + self.pg2.unconfig_ip6() + self.pg3.unconfig_ip6() + + def test_dhcp_client(self): + """ DHCP Client""" + + hostname = 'universal-dp' + + self.pg_enable_capture(self.pg_interfaces) + + # + # Configure DHCP client on PG2 and capture the discover sent + # + self.vapi.dhcp_client(self.pg2.sw_if_index, hostname) + + rx = self.pg2.get_capture(1) + + self.verify_orig_dhcp_discover(rx[0], self.pg2, hostname) + + # + # Sned back on offer, expect the request + # + p_offer = (Ether(dst=self.pg2.local_mac, src=self.pg2.remote_mac) / + IP(src=self.pg2.remote_ip4, dst="255.255.255.255") / + UDP(sport=DHCP4_SERVER_PORT, dport=DHCP4_CLIENT_PORT) / + BOOTP(op=1, yiaddr=self.pg2.local_ip4) / + DHCP(options=[('message-type', 'offer'), + ('server_id', self.pg2.remote_ip4), + ('end')])) + + self.pg2.add_stream(p_offer) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + rx = self.pg2.get_capture(1) + self.verify_orig_dhcp_request(rx[0], self.pg2, hostname, + self.pg2.local_ip4) + + # + # Send an acknowloedgement + # + p_ack = (Ether(dst=self.pg2.local_mac, src=self.pg2.remote_mac) / + IP(src=self.pg2.remote_ip4, dst="255.255.255.255") / + UDP(sport=DHCP4_SERVER_PORT, dport=DHCP4_CLIENT_PORT) / + BOOTP(op=1, yiaddr=self.pg2.local_ip4) / + DHCP(options=[('message-type', 'ack'), + ('subnet_mask', "255.255.255.0"), + ('router', self.pg2.remote_ip4), + ('server_id', self.pg2.remote_ip4), + ('lease_time', 43200), + ('end')])) + + self.pg2.add_stream(p_ack) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + # + # We'll get an ARP request for the router address + # + rx = self.pg2.get_capture(1) + + self.assertEqual(rx[0][ARP].pdst, self.pg2.remote_ip4) + self.pg_enable_capture(self.pg_interfaces) + + # + # At the end of this procedure there should be a connected route + # in the FIB + # + self.assertTrue(find_route(self, self.pg2.local_ip4, 24)) + self.assertTrue(find_route(self, self.pg2.local_ip4, 32)) + + # remove the left over ARP entry + self.vapi.ip_neighbor_add_del(self.pg2.sw_if_index, + self.pg2.remote_mac, + self.pg2.remote_ip4, + is_add=0) + # + # remove the DHCP config + # + self.vapi.dhcp_client(self.pg2.sw_if_index, hostname, is_add=0) + + # + # and now the route should be gone + # + self.assertFalse(find_route(self, self.pg2.local_ip4, 32)) + self.assertFalse(find_route(self, self.pg2.local_ip4, 24)) + + # + # Start the procedure again. this time have VPP send the client-ID + # + self.pg2.admin_down() + self.sleep(1) + self.pg2.admin_up() + self.vapi.dhcp_client(self.pg2.sw_if_index, hostname, + client_id=self.pg2.local_mac) + + rx = self.pg2.get_capture(1) + + self.verify_orig_dhcp_discover(rx[0], self.pg2, hostname, + self.pg2.local_mac) + + self.pg2.add_stream(p_offer) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + rx = self.pg2.get_capture(1) + self.verify_orig_dhcp_request(rx[0], self.pg2, hostname, + self.pg2.local_ip4) + + # + # unicast the ack to the offered address + # + p_ack = (Ether(dst=self.pg2.local_mac, src=self.pg2.remote_mac) / + IP(src=self.pg2.remote_ip4, dst=self.pg2.local_ip4) / + UDP(sport=DHCP4_SERVER_PORT, dport=DHCP4_CLIENT_PORT) / + BOOTP(op=1, yiaddr=self.pg2.local_ip4) / + DHCP(options=[('message-type', 'ack'), + ('subnet_mask', "255.255.255.0"), + ('router', self.pg2.remote_ip4), + ('server_id', self.pg2.remote_ip4), + ('lease_time', 43200), + ('end')])) + + self.pg2.add_stream(p_ack) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + # + # At the end of this procedure there should be a connected route + # in the FIB + # + self.assertTrue(find_route(self, self.pg2.local_ip4, 32)) + self.assertTrue(find_route(self, self.pg2.local_ip4, 24)) + + # + # remove the DHCP config + # + self.vapi.dhcp_client(self.pg2.sw_if_index, hostname, is_add=0) + + self.assertFalse(find_route(self, self.pg2.local_ip4, 32)) + self.assertFalse(find_route(self, self.pg2.local_ip4, 24)) + if __name__ == '__main__': unittest.main(testRunner=VppTestRunner)