summaryrefslogtreecommitdiffstats
path: root/src/vnet/dhcp/client.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/vnet/dhcp/client.c')
-rw-r--r--src/vnet/dhcp/client.c151
1 files changed, 53 insertions, 98 deletions
diff --git a/src/vnet/dhcp/client.c b/src/vnet/dhcp/client.c
index 8043bf22d43..03fc2689abf 100644
--- a/src/vnet/dhcp/client.c
+++ b/src/vnet/dhcp/client.c
@@ -22,56 +22,6 @@ static u8 *format_dhcp_client_state (u8 * s, va_list * va);
static vlib_node_registration_t dhcp_client_process_node;
static void
-dhcp_client_add_rx_address (dhcp_client_main_t * dcm, dhcp_client_t * c)
-{
- /* Install a local entry for the offered address */
- fib_prefix_t rx = {
- .fp_len = 32,
- .fp_addr.ip4 = c->leased_address,
- .fp_proto = FIB_PROTOCOL_IP4,
- };
-
- fib_table_entry_special_add (fib_table_get_index_for_sw_if_index
- (FIB_PROTOCOL_IP4, c->sw_if_index), &rx,
- FIB_SOURCE_DHCP, (FIB_ENTRY_FLAG_LOCAL));
-
- /* And add the server's address as uRPF exempt so we can accept
- * local packets from it */
- fib_prefix_t server = {
- .fp_len = 32,
- .fp_addr.ip4 = c->dhcp_server,
- .fp_proto = FIB_PROTOCOL_IP4,
- };
-
- fib_table_entry_special_add (fib_table_get_index_for_sw_if_index
- (FIB_PROTOCOL_IP4, c->sw_if_index), &server,
- FIB_SOURCE_URPF_EXEMPT, (FIB_ENTRY_FLAG_DROP));
-}
-
-static void
-dhcp_client_remove_rx_address (dhcp_client_main_t * dcm, dhcp_client_t * c)
-{
- fib_prefix_t rx = {
- .fp_len = 32,
- .fp_addr.ip4 = c->leased_address,
- .fp_proto = FIB_PROTOCOL_IP4,
- };
-
- fib_table_entry_special_remove (fib_table_get_index_for_sw_if_index
- (FIB_PROTOCOL_IP4, c->sw_if_index), &rx,
- FIB_SOURCE_DHCP);
- fib_prefix_t server = {
- .fp_len = 32,
- .fp_addr.ip4 = c->dhcp_server,
- .fp_proto = FIB_PROTOCOL_IP4,
- };
-
- fib_table_entry_special_remove (fib_table_get_index_for_sw_if_index
- (FIB_PROTOCOL_IP4, c->sw_if_index), &server,
- FIB_SOURCE_URPF_EXEMPT);
-}
-
-static void
dhcp_client_acquire_address (dhcp_client_main_t * dcm, dhcp_client_t * c)
{
/*
@@ -233,13 +183,6 @@ dhcp_client_for_us (u32 bi, vlib_buffer_t * b,
c->next_transmit = now + 5.0;
break;
}
- /*
- * in order to accept unicasted ACKs we need to configure the offered
- * address on the interface. However, at this point we may not know the
- * subnet-mask (an OFFER may not contain it). So add a temporary receice
- * and uRPF excempt entry
- */
- dhcp_client_add_rx_address (dcm, c);
/* Received an offer, go send a request */
c->state = DHCP_REQUEST;
@@ -267,9 +210,11 @@ dhcp_client_for_us (u32 bi, vlib_buffer_t * b,
void (*fp) (u32, u32, u8 *, u8, u8, u8 *, u8 *, u8 *) =
c->event_callback;
- /* replace the temporary RX address with the correct subnet */
- dhcp_client_remove_rx_address (dcm, c);
+ /* add the advertised subnet and disable the feature */
dhcp_client_acquire_address (dcm, c);
+ vnet_feature_enable_disable ("ip4-unicast",
+ "ip4-dhcp-client-detect",
+ c->sw_if_index, 0, 0, 0);
/*
* Configure default IP route:
@@ -285,8 +230,19 @@ dhcp_client_for_us (u32 bi, vlib_buffer_t * b,
.ip4 = c->router_address,
};
- fib_table_entry_path_add (fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4, c->sw_if_index), &all_0s, FIB_SOURCE_DHCP, FIB_ENTRY_FLAG_NONE, DPO_PROTO_IP4, &nh, c->sw_if_index, ~0, 1, NULL, // no label stack
- FIB_ROUTE_PATH_FLAG_NONE);
+ /* *INDENT-OFF* */
+ fib_table_entry_path_add (
+ fib_table_get_index_for_sw_if_index (
+ FIB_PROTOCOL_IP4,
+ c->sw_if_index),
+ &all_0s,
+ FIB_SOURCE_DHCP,
+ FIB_ENTRY_FLAG_NONE,
+ DPO_PROTO_IP4,
+ &nh, c->sw_if_index,
+ ~0, 1, NULL, // no label stack
+ FIB_ROUTE_PATH_FLAG_NONE);
+ /* *INDENT-ON* */
}
/*
@@ -418,7 +374,9 @@ send_dhcp_pkt (dhcp_client_main_t * dcm, dhcp_client_t * c,
dhcp->hardware_type = 1; /* ethernet */
dhcp->hardware_address_length = 6;
dhcp->transaction_identifier = c->transaction_id;
- dhcp->flags = clib_host_to_net_u16 (is_broadcast ? DHCP_FLAG_BROADCAST : 0);
+ dhcp->flags =
+ clib_host_to_net_u16 (is_broadcast && c->set_broadcast_flag ?
+ DHCP_FLAG_BROADCAST : 0);
dhcp->magic_cookie.as_u32 = DHCP_MAGIC;
o = (dhcp_option_t *) dhcp->options;
@@ -676,14 +634,13 @@ dhcp_client_process (vlib_main_t * vm,
break;
case ~0:
- pool_foreach (c, dcm->clients, (
- {
- timeout =
- dhcp_client_sm (now, timeout,
- (uword) (c -
- dcm->clients));
- }
- ));
+ /* *INDENT-OFF* */
+ pool_foreach (c, dcm->clients,
+ ({
+ timeout = dhcp_client_sm (now, timeout,
+ (uword) (c - dcm->clients));
+ }));
+ /* *INDENT-ON* */
if (pool_elts (dcm->clients) == 0)
timeout = 100.0;
break;
@@ -785,13 +742,14 @@ show_dhcp_client_command_fn (vlib_main_t * vm,
return 0;
}
- pool_foreach (c, dcm->clients, (
- {
- vlib_cli_output (vm, "%U",
- format_dhcp_client, dcm,
- c, verbose);
- }
- ));
+ /* *INDENT-OFF* */
+ pool_foreach (c, dcm->clients,
+ ({
+ vlib_cli_output (vm, "%U",
+ format_dhcp_client, dcm,
+ c, verbose);
+ }));
+ /* *INDENT-ON* */
return 0;
}
@@ -812,11 +770,6 @@ dhcp_client_add_del (dhcp_client_add_del_args_t * a)
vlib_main_t *vm = dcm->vlib_main;
dhcp_client_t *c;
uword *p;
- fib_prefix_t all_1s = {
- .fp_len = 32,
- .fp_addr.ip4.as_u32 = 0xffffffff,
- .fp_proto = FIB_PROTOCOL_IP4,
- };
fib_prefix_t all_0s = {
.fp_len = 0,
.fp_addr.ip4.as_u32 = 0x0,
@@ -840,6 +793,7 @@ dhcp_client_add_del (dhcp_client_add_del_args_t * a)
c->option_55_data = a->option_55_data;
c->hostname = a->hostname;
c->client_identifier = a->client_identifier;
+ c->set_broadcast_flag = a->set_broadcast_flag;
do
{
c->transaction_id = random_u32 (&dcm->seed);
@@ -848,17 +802,18 @@ dhcp_client_add_del (dhcp_client_add_del_args_t * a)
set_l2_rewrite (dcm, c);
hash_set (dcm->client_by_sw_if_index, a->sw_if_index, c - dcm->clients);
- /* this add is ref counted by FIB so we can add for each itf */
- fib_table_entry_special_add (fib_table_get_index_for_sw_if_index
- (FIB_PROTOCOL_IP4, c->sw_if_index),
- &all_1s, FIB_SOURCE_DHCP,
- FIB_ENTRY_FLAG_LOCAL);
-
/*
- * enable the interface to RX IPv4 packets
- * this is also ref counted
+ * In order to accept any OFFER, whether broadcasted or unicasted, we
+ * need to configure the dhcp-client-detect feature as an input feature
+ * so the DHCP OFFER is sent to the ip4-local node. Without this a
+ * broadcasted OFFER hits the 255.255.255.255/32 address and a unicast
+ * hits 0.0.0.0/0 both of which default to drop and the latter may forward
+ * of box - not what we want. Nor to we want to change these route for
+ * all interfaces in this table
*/
- ip4_sw_interface_enable_disable (c->sw_if_index, 1);
+ vnet_feature_enable_disable ("ip4-unicast",
+ "ip4-dhcp-client-detect",
+ c->sw_if_index, 1, 0, 0);
vlib_process_signal_event (vm, dhcp_client_process_node.index,
EVENT_DHCP_CLIENT_WAKEUP, c - dcm->clients);
@@ -867,10 +822,6 @@ dhcp_client_add_del (dhcp_client_add_del_args_t * a)
{
c = pool_elt_at_index (dcm->clients, p[0]);
- fib_table_entry_special_remove (fib_table_get_index_for_sw_if_index
- (FIB_PROTOCOL_IP4, c->sw_if_index),
- &all_1s, FIB_SOURCE_DHCP);
-
if (c->router_address.as_u32)
{
ip46_address_t nh = {
@@ -883,9 +834,7 @@ dhcp_client_add_del (dhcp_client_add_del_args_t * a)
DPO_PROTO_IP4, &nh, c->sw_if_index, ~0,
1, FIB_ROUTE_PATH_FLAG_NONE);
}
- dhcp_client_remove_rx_address (dcm, c);
dhcp_client_release_address (dcm, c);
- ip4_sw_interface_enable_disable (c->sw_if_index, 0);
vec_free (c->option_55_data);
vec_free (c->hostname);
@@ -903,7 +852,8 @@ dhcp_client_config (vlib_main_t * vm,
u8 * hostname,
u8 * client_id,
u32 is_add,
- u32 client_index, void *event_callback, u32 pid)
+ u32 client_index,
+ void *event_callback, u8 set_broadcast_flag, u32 pid)
{
dhcp_client_add_del_args_t _a, *a = &_a;
int rv;
@@ -914,6 +864,7 @@ dhcp_client_config (vlib_main_t * vm,
a->client_index = client_index;
a->pid = pid;
a->event_callback = event_callback;
+ a->set_broadcast_flag = set_broadcast_flag;
vec_validate (a->hostname, strlen ((char *) hostname) - 1);
strncpy ((char *) a->hostname, (char *) hostname, vec_len (a->hostname));
vec_validate (a->client_identifier, strlen ((char *) client_id) - 1);
@@ -990,6 +941,7 @@ dhcp_client_set_command_fn (vlib_main_t * vm,
u32 sw_if_index;
u8 *hostname = 0;
u8 sw_if_index_set = 0;
+ u8 set_broadcast_flag = 1;
int is_add = 1;
dhcp_client_add_del_args_t _a, *a = &_a;
int rv;
@@ -1003,6 +955,8 @@ dhcp_client_set_command_fn (vlib_main_t * vm,
;
else if (unformat (input, "del"))
is_add = 0;
+ else if (unformat (input, "broadcast", &set_broadcast_flag))
+ is_add = 0;
else
break;
}
@@ -1015,6 +969,7 @@ dhcp_client_set_command_fn (vlib_main_t * vm,
a->sw_if_index = sw_if_index;
a->hostname = hostname;
a->client_identifier = format (0, "vpe 1.0%c", 0);
+ a->set_broadcast_flag = set_broadcast_flag;
/*
* Option 55 request list. These data precisely match
c">TestBondInterface(VppTestCase): """Bond Test Case """ @classmethod def setUpClass(cls): super(TestBondInterface, cls).setUpClass() # Test variables cls.pkts_per_burst = 257 # Number of packets per burst # create 3 pg interfaces cls.create_pg_interfaces(range(4)) # packet sizes cls.pg_if_packet_sizes = [64, 512, 1518] # , 9018] # setup all interfaces for i in cls.pg_interfaces: i.admin_up() @classmethod def tearDownClass(cls): super(TestBondInterface, cls).tearDownClass() def setUp(self): super(TestBondInterface, self).setUp() def tearDown(self): super(TestBondInterface, self).tearDown() def show_commands_at_teardown(self): self.logger.info(self.vapi.ppcli("show interface")) def test_bond_traffic(self): """ Bond traffic test """ # topology # # RX-> TX-> # # pg2 ------+ +------pg0 (slave) # | | # BondEthernet0 (10.10.10.1) # | | # pg3 ------+ +------pg1 (slave) # # create interface (BondEthernet0) # self.logger.info("create bond") bond0_mac = "02:fe:38:30:59:3c" mac = MACAddress(bond0_mac).packed bond0 = VppBondInterface(self, mode=3, lb=1, numa_only=0, use_custom_mac=1, mac_address=mac) bond0.add_vpp_config() bond0.admin_up() bond0_addr = socket.inet_pton(socket.AF_INET, "10.10.10.1") self.vapi.sw_interface_add_del_address(sw_if_index=bond0.sw_if_index, address=bond0_addr, address_length=24) self.pg2.config_ip4() self.pg2.resolve_arp() self.pg3.config_ip4() self.pg3.resolve_arp() self.logger.info(self.vapi.cli("show interface")) self.logger.info(self.vapi.cli("show interface address")) self.logger.info(self.vapi.cli("show ip arp")) # enslave pg0 and pg1 to BondEthernet0 self.logger.info("bond enslave interface pg0 to BondEthernet0") bond0.enslave_vpp_bond_interface(sw_if_index=self.pg0.sw_if_index, is_passive=0, is_long_timeout=0) self.logger.info("bond enslave interface pg1 to BondEthernet0") bond0.enslave_vpp_bond_interface(sw_if_index=self.pg1.sw_if_index, is_passive=0, is_long_timeout=0) # verify both slaves in BondEthernet0 if_dump = self.vapi.sw_interface_slave_dump(bond0.sw_if_index) self.assertTrue(self.pg0.is_interface_config_in_dump(if_dump)) self.assertTrue(self.pg1.is_interface_config_in_dump(if_dump)) # generate a packet from pg2 -> BondEthernet0 -> pg1 # BondEthernet0 TX hashes this packet to pg1 p2 = (Ether(src=bond0_mac, dst=self.pg2.local_mac) / IP(src=self.pg2.local_ip4, dst="10.10.10.12") / UDP(sport=1235, dport=1235) / Raw('\xa5' * 100)) self.pg2.add_stream(p2) # generate a packet from pg3 -> BondEthernet0 -> pg0 # BondEthernet0 TX hashes this packet to pg0 # notice the ip address and ports are different than p2 packet p3 = (Ether(src=bond0_mac, dst=self.pg3.local_mac) / IP(src=self.pg3.local_ip4, dst="10.10.10.11") / UDP(sport=1234, dport=1234) / Raw('\xa5' * 100)) self.pg3.add_stream(p3) self.pg_enable_capture(self.pg_interfaces) # set up the static arp entries pointing to the BondEthernet0 interface # so that it does not try to resolve the ip address self.logger.info(self.vapi.cli( "set ip arp static BondEthernet0 10.10.10.12 abcd.abcd.0002")) self.logger.info(self.vapi.cli( "set ip arp static BondEthernet0 10.10.10.11 abcd.abcd.0004")) # clear the interface counters self.logger.info(self.vapi.cli("clear interfaces")) self.pg_start() self.logger.info("check the interface counters") # verify counters # BondEthernet0 tx bytes = 284 intfs = self.vapi.cli("show interface BondEthernet0").split("\n") found = 0 for intf in intfs: if "tx bytes" in intf and "284" in intf: found = 1 self.assertEqual(found, 1) # BondEthernet0 tx bytes = 284 intfs = self.vapi.cli("show interface BondEthernet0").split("\n") found = 0 for intf in intfs: if "tx bytes" in intf and "284" in intf: found = 1 self.assertEqual(found, 1) # pg2 rx bytes = 142 intfs = self.vapi.cli("show interface pg2").split("\n") found = 0 for intf in intfs: if "rx bytes" in intf and "142" in intf: found = 1 self.assertEqual(found, 1) # pg3 rx bytes = 142 intfs = self.vapi.cli("show interface pg3").split("\n") found = 0 for intf in intfs: if "rx bytes" in intf and "142" in intf: found = 1 self.assertEqual(found, 1) bond0.remove_vpp_config() def test_bond_enslave(self): """ Bond enslave/detach slave test """ # create interface (BondEthernet0) self.logger.info("create bond") bond0 = VppBondInterface(self, mode=3) bond0.add_vpp_config() bond0.admin_up() # verify pg0 and pg1 not in BondEthernet0 if_dump = self.vapi.sw_interface_slave_dump(bond0.sw_if_index) self.assertFalse(self.pg0.is_interface_config_in_dump(if_dump)) self.assertFalse(self.pg1.is_interface_config_in_dump(if_dump)) # enslave pg0 and pg1 to BondEthernet0 self.logger.info("bond enslave interface pg0 to BondEthernet0") bond0.enslave_vpp_bond_interface(sw_if_index=self.pg0.sw_if_index, is_passive=0, is_long_timeout=0) self.logger.info("bond enslave interface pg1 to BondEthernet0") bond0.enslave_vpp_bond_interface(sw_if_index=self.pg1.sw_if_index, is_passive=0, is_long_timeout=0) # verify both slaves in BondEthernet0 if_dump = self.vapi.sw_interface_slave_dump(bond0.sw_if_index) self.assertTrue(self.pg0.is_interface_config_in_dump(if_dump)) self.assertTrue(self.pg1.is_interface_config_in_dump(if_dump)) # detach interface pg0 self.logger.info("detach interface pg0") bond0.detach_vpp_bond_interface(sw_if_index=self.pg0.sw_if_index) # verify pg0 is not in BondEthernet0, but pg1 is if_dump = self.vapi.sw_interface_slave_dump(bond0.sw_if_index) self.assertFalse(self.pg0.is_interface_config_in_dump(if_dump)) self.assertTrue(self.pg1.is_interface_config_in_dump(if_dump)) # detach interface pg1 self.logger.info("detach interface pg1") bond0.detach_vpp_bond_interface(sw_if_index=self.pg1.sw_if_index) # verify pg0 and pg1 not in BondEthernet0 if_dump = self.vapi.sw_interface_slave_dump(bond0.sw_if_index) self.assertFalse(self.pg0.is_interface_config_in_dump(if_dump)) self.assertFalse(self.pg1.is_interface_config_in_dump(if_dump)) bond0.remove_vpp_config() def test_bond(self): """ Bond add/delete interface test """ self.logger.info("Bond add interfaces") # create interface 1 (BondEthernet0) bond0 = VppBondInterface(self, mode=5) bond0.add_vpp_config() bond0.admin_up() # create interface 2 (BondEthernet1) bond1 = VppBondInterface(self, mode=3) bond1.add_vpp_config() bond1.admin_up() # verify both interfaces in the show ifs = self.vapi.cli("show interface") self.assertIn('BondEthernet0', ifs) self.assertIn('BondEthernet1', ifs) # verify they are in the dump also if_dump = self.vapi.sw_interface_bond_dump() self.assertTrue(bond0.is_interface_config_in_dump(if_dump)) self.assertTrue(bond1.is_interface_config_in_dump(if_dump)) # delete BondEthernet1 self.logger.info("Deleting BondEthernet1") bond1.remove_vpp_config() self.logger.info("Verifying BondEthernet1 is deleted") ifs = self.vapi.cli("show interface") # verify BondEthernet0 still in the show self.assertIn('BondEthernet0', ifs) # verify BondEthernet1 not in the show self.assertNotIn('BondEthernet1', ifs) # verify BondEthernet1 is not in the dump if_dump = self.vapi.sw_interface_bond_dump() self.assertFalse(bond1.is_interface_config_in_dump(if_dump)) # verify BondEthernet0 is still in the dump self.assertTrue(bond0.is_interface_config_in_dump(if_dump)) # delete BondEthernet0 self.logger.info("Deleting BondEthernet0") bond0.remove_vpp_config() self.logger.info("Verifying BondEthernet0 is deleted") # verify BondEthernet0 not in the show ifs = self.vapi.cli("show interface") self.assertNotIn('BondEthernet0', ifs) # verify BondEthernet0 is not in the dump if_dump = self.vapi.sw_interface_bond_dump() self.assertFalse(bond0.is_interface_config_in_dump(if_dump)) if __name__ == '__main__': unittest.main(testRunner=VppTestRunner)