From 54c6dc450031443663d40b836a8b0bffdcc2bdea Mon Sep 17 00:00:00 2001 From: Neale Ranns Date: Wed, 17 Jan 2018 10:29:10 -0800 Subject: For DHCP client configuration control the setting of the broadcast flag in the DISCOVER message sent. According to RFC2131: In the case of a client using DHCP for initial configuration (before the client's TCP/IP software has been completely configured), DHCP requires creative use of the client's TCP/IP software and liberal interpretation of RFC 1122. The TCP/IP software SHOULD accept and forward to the IP layer any IP packets delivered to the client's hardware address before the IP address is configured; DHCP servers and BOOTP relay agents may not be able to deliver DHCP messages to clients that cannot accept hardware unicast datagrams before the TCP/IP software is configured. To work around some clients that cannot accept IP unicast datagrams before the TCP/IP software is configured as discussed in the previous paragraph, DHCP uses the 'flags' field [21]. The leftmost bit is defined as the BROADCAST (B) flag. The semantics of this flag are discussed in section 4.1 of this document. The remaining bits of the flags field are reserved for future use. They MUST be set to zero by clients and ignored by servers and relay agents. Figure 2 gives the format of the 'flags' field. this changes means VPP conforms to the: "SHOULD accept and forward to the IP layer any IP packets delivered to the client's hardware address before the IP address is configured" with the caveat that VPP allows DHCP packets destined to the stanard client DHCP port to be delivered. With this enhancement the control-plane is now able to choose the setting of the broadcast flag. Change-Id: Ia4eb2c9bb1e30c29f9192facc645e9533641955a Signed-off-by: Neale Ranns --- test/test_dhcp.py | 109 ++++++++++++++++++++++++++++++++++++++++++++-- test/vpp_papi_provider.py | 2 + 2 files changed, 107 insertions(+), 4 deletions(-) (limited to 'test') diff --git a/test/test_dhcp.py b/test/test_dhcp.py index db7a7cc223c..21940ca99f0 100644 --- a/test/test_dhcp.py +++ b/test/test_dhcp.py @@ -214,7 +214,8 @@ class TestDHCP(VppTestCase): 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): + def verify_orig_dhcp_discover(self, pkt, intf, hostname, client_id=None, + broadcast=1): self.verify_orig_dhcp_pkt(pkt, intf) self.verify_dhcp_msg_type(pkt, "discover") @@ -224,9 +225,13 @@ class TestDHCP(VppTestCase): bootp = pkt[BOOTP] self.assertEqual(bootp.ciaddr, "0.0.0.0") self.assertEqual(bootp.giaddr, "0.0.0.0") - self.assertEqual(bootp.flags, 0x8000) + if broadcast: + self.assertEqual(bootp.flags, 0x8000) + else: + self.assertEqual(bootp.flags, 0x0000) - def verify_orig_dhcp_request(self, pkt, intf, hostname, ip): + def verify_orig_dhcp_request(self, pkt, intf, hostname, ip, + broadcast=1): self.verify_orig_dhcp_pkt(pkt, intf) self.verify_dhcp_msg_type(pkt, "request") @@ -235,7 +240,10 @@ class TestDHCP(VppTestCase): bootp = pkt[BOOTP] self.assertEqual(bootp.ciaddr, "0.0.0.0") self.assertEqual(bootp.giaddr, "0.0.0.0") - self.assertEqual(bootp.flags, 0x8000) + if broadcast: + self.assertEqual(bootp.flags, 0x8000) + else: + self.assertEqual(bootp.flags, 0x0000) def verify_relayed_dhcp_discover(self, pkt, intf, src_intf=None, fib_id=0, oui=0, @@ -1310,6 +1318,14 @@ class TestDHCP(VppTestCase): self.pg_enable_capture(self.pg_interfaces) self.pg_start() + # + # We'll get an ARP request for the router address + # + rx = self.pg3.get_capture(1) + + self.assertEqual(rx[0][ARP].pdst, self.pg3.remote_ip4) + self.pg_enable_capture(self.pg_interfaces) + # # At the end of this procedure there should be a connected route # in the FIB @@ -1325,6 +1341,91 @@ class TestDHCP(VppTestCase): self.assertFalse(find_route(self, self.pg3.local_ip4, 32)) self.assertFalse(find_route(self, self.pg3.local_ip4, 24)) + # + # Rince and repeat, this time with VPP configured not to set + # the braodcast flag in the discover and request messages, + # and for the server to unicast the responses. + # + # Configure DHCP client on PG3 and capture the discover sent + # + self.vapi.dhcp_client(self.pg3.sw_if_index, hostname, + set_broadcast_flag=0) + + rx = self.pg3.get_capture(1) + + self.verify_orig_dhcp_discover(rx[0], self.pg3, hostname, + broadcast=0) + + # + # Send back on offer, unicasted to the offered address. + # Expect the request. + # + p_offer = (Ether(dst=self.pg3.local_mac, src=self.pg3.remote_mac) / + IP(src=self.pg3.remote_ip4, dst=self.pg3.local_ip4) / + UDP(sport=DHCP4_SERVER_PORT, dport=DHCP4_CLIENT_PORT) / + BOOTP(op=1, yiaddr=self.pg3.local_ip4) / + DHCP(options=[('message-type', 'offer'), + ('server_id', self.pg3.remote_ip4), + ('end')])) + + self.pg3.add_stream(p_offer) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + rx = self.pg3.get_capture(1) + self.verify_orig_dhcp_request(rx[0], self.pg3, hostname, + self.pg3.local_ip4, + broadcast=0) + + # + # Send an acknowloedgement + # + p_ack = (Ether(dst=self.pg3.local_mac, src=self.pg3.remote_mac) / + IP(src=self.pg3.remote_ip4, dst=self.pg3.local_ip4) / + UDP(sport=DHCP4_SERVER_PORT, dport=DHCP4_CLIENT_PORT) / + BOOTP(op=1, yiaddr=self.pg3.local_ip4) / + DHCP(options=[('message-type', 'ack'), + ('subnet_mask', "255.255.255.0"), + ('router', self.pg3.remote_ip4), + ('server_id', self.pg3.remote_ip4), + ('lease_time', 43200), + ('end')])) + + self.pg3.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.pg3.get_capture(1) + + self.assertEqual(rx[0][ARP].pdst, self.pg3.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.pg3.local_ip4, 24)) + self.assertTrue(find_route(self, self.pg3.local_ip4, 32)) + + # remove the left over ARP entry + self.vapi.ip_neighbor_add_del(self.pg3.sw_if_index, + mactobinary(self.pg3.remote_mac), + self.pg3.remote_ip4, + is_add=0) + # + # remove the DHCP config + # + self.vapi.dhcp_client(self.pg3.sw_if_index, hostname, is_add=0) + + # + # and now the route should be gone + # + self.assertFalse(find_route(self, self.pg3.local_ip4, 32)) + self.assertFalse(find_route(self, self.pg3.local_ip4, 24)) + if __name__ == '__main__': unittest.main(testRunner=VppTestRunner) diff --git a/test/vpp_papi_provider.py b/test/vpp_papi_provider.py index 13dccc9283b..db0f8e61aed 100644 --- a/test/vpp_papi_provider.py +++ b/test/vpp_papi_provider.py @@ -2132,6 +2132,7 @@ class VppPapiProvider(object): hostname, client_id='', is_add=1, + set_broadcast_flag=1, want_dhcp_events=0): return self.api( self.papi.dhcp_client_config, @@ -2141,6 +2142,7 @@ class VppPapiProvider(object): 'client_id': client_id, 'is_add': is_add, 'want_dhcp_event': want_dhcp_events, + 'set_broadcast_flag': set_broadcast_flag, 'pid': os.getpid(), }) -- cgit 1.2.3-korg