aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/vnet.am2
-rw-r--r--src/vnet/dhcp/client.c151
-rw-r--r--src/vnet/dhcp/client.h8
-rw-r--r--src/vnet/dhcp/dhcp.api3
-rw-r--r--src/vnet/dhcp/dhcp_api.c2
-rw-r--r--src/vnet/dhcp/dhcp_client_detect.c333
-rw-r--r--src/vpp-api/vom/dhcp_config.cpp10
-rw-r--r--src/vpp-api/vom/dhcp_config.hpp12
-rw-r--r--src/vpp-api/vom/dhcp_config_cmds.cpp4
-rw-r--r--src/vpp-api/vom/dhcp_config_cmds.hpp8
-rw-r--r--test/test_dhcp.py109
-rw-r--r--test/vpp_papi_provider.py2
12 files changed, 533 insertions, 111 deletions
diff --git a/src/vnet.am b/src/vnet.am
index 84407bc1861..32d3167b16e 100644
--- a/src/vnet.am
+++ b/src/vnet.am
@@ -724,7 +724,7 @@ API_FILES += vnet/lisp-gpe/lisp_gpe.api
########################################
libvnet_la_SOURCES += \
vnet/dhcp/client.c \
- vnet/dhcp/client.h \
+ vnet/dhcp/dhcp_client_detect.c \
vnet/dhcp/dhcp_api.c
nobase_include_HEADERS += \
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
diff --git a/src/vnet/dhcp/client.h b/src/vnet/dhcp/client.h
index d9c7e25d0b2..1c2becb3058 100644
--- a/src/vnet/dhcp/client.h
+++ b/src/vnet/dhcp/client.h
@@ -71,6 +71,10 @@ typedef struct
/* Information used for event callback */
u32 client_index;
u32 pid;
+
+ /* Set the broadcast Flag in the Discover/Request messages */
+ u8 set_broadcast_flag;
+
void *event_callback;
} dhcp_client_t;
@@ -90,6 +94,7 @@ typedef struct
{
int is_add;
u32 sw_if_index;
+ u8 set_broadcast_flag;
/* vectors, consumed by dhcp client code */
u8 *hostname;
@@ -118,7 +123,8 @@ int 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);
#endif /* included_dhcp_client_h */
diff --git a/src/vnet/dhcp/dhcp.api b/src/vnet/dhcp/dhcp.api
index 528915a46e9..721a1be3547 100644
--- a/src/vnet/dhcp/dhcp.api
+++ b/src/vnet/dhcp/dhcp.api
@@ -71,6 +71,8 @@ autoreply define dhcp_proxy_set_vss
@param is_add - add the config if non-zero, else delete
@param want_dhcp_event - DHCP event sent to the sender
via dhcp_compl_event API message if non-zero
+ @param set_broadcast_flag - in the DHCP Discover to control
+ how the resulting OFFER is addressed.
@param pid - sender's pid
*/
autoreply define dhcp_client_config
@@ -82,6 +84,7 @@ autoreply define dhcp_client_config
u8 client_id[64];
u8 is_add;
u8 want_dhcp_event;
+ u8 set_broadcast_flag;
u32 pid;
};
diff --git a/src/vnet/dhcp/dhcp_api.c b/src/vnet/dhcp/dhcp_api.c
index 8e210cdd5b0..401f6b75edc 100644
--- a/src/vnet/dhcp/dhcp_api.c
+++ b/src/vnet/dhcp/dhcp_api.c
@@ -248,7 +248,7 @@ static void vl_api_dhcp_client_config_t_handler
mp->hostname, mp->client_id,
mp->is_add, mp->client_index,
mp->want_dhcp_event ? dhcp_compl_event_callback :
- NULL, mp->pid);
+ NULL, mp->set_broadcast_flag, mp->pid);
BAD_SW_IF_INDEX_LABEL;
diff --git a/src/vnet/dhcp/dhcp_client_detect.c b/src/vnet/dhcp/dhcp_client_detect.c
new file mode 100644
index 00000000000..1b916cdd356
--- /dev/null
+++ b/src/vnet/dhcp/dhcp_client_detect.c
@@ -0,0 +1,333 @@
+/*
+ * DHCP feature; applied as an input feature to select DHCP packets
+ *
+ * Copyright (c) 2013 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vnet/dhcp/client.h>
+#include <vnet/udp/udp.h>
+
+#define foreach_dhcp_client_detect \
+ _(EXTRACT, "Extract")
+
+typedef enum
+{
+#define _(sym,str) DHCP_CLIENT_DETECT_ERROR_##sym,
+ foreach_dhcp_client_detect
+#undef _
+ DHCP_CLIENT_DETECT_N_ERROR,
+} dhcp_client_detect_error_t;
+
+static char *dhcp_client_detect_error_strings[] = {
+#define _(sym,string) string,
+ foreach_dhcp_client_detect
+#undef _
+};
+
+typedef enum
+{
+#define _(sym,str) DHCP_CLIENT_DETECT_NEXT_##sym,
+ foreach_dhcp_client_detect
+#undef _
+ DHCP_CLIENT_DETECT_N_NEXT,
+} dhcp_client_detect_next_t;
+
+/**
+ * per-packet trace data
+ */
+typedef struct dhcp_client_detect_trace_t_
+{
+ /* per-pkt trace data */
+ u8 extracted;
+} dhcp_client_detect_trace_t;
+
+static uword
+dhcp_client_detect_node_fn (vlib_main_t * vm,
+ vlib_node_runtime_t * node, vlib_frame_t * frame)
+{
+ dhcp_client_detect_next_t next_index;
+ u16 dhcp_client_port_network_order;
+ u32 n_left_from, *from, *to_next;
+ u32 extractions;
+
+ dhcp_client_port_network_order =
+ clib_net_to_host_u16 (UDP_DST_PORT_dhcp_to_client);
+ next_index = 0;
+ extractions = 0;
+ n_left_from = frame->n_vectors;
+ from = vlib_frame_vector_args (frame);
+
+ while (n_left_from > 0)
+ {
+ u32 n_left_to_next;
+
+ vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
+
+ /*
+ * This loop is optimised not so we can really quickly process DHCp
+ * offers... but so we can quickly sift them out when the interface
+ * is also receving 'normal' packets
+ */
+ while (n_left_from >= 8 && n_left_to_next >= 4)
+ {
+ udp_header_t *udp0, *udp1, *udp2, *udp3;
+ ip4_header_t *ip0, *ip1, *ip2, *ip3;
+ vlib_buffer_t *b0, *b1, *b2, *b3;
+ u32 next0, next1, next2, next3;
+ u32 bi0, bi1, bi2, bi3;
+
+ next0 = next1 = next2 = next3 = ~0;
+ bi0 = to_next[0] = from[0];
+ bi1 = to_next[1] = from[1];
+ bi2 = to_next[2] = from[2];
+ bi3 = to_next[3] = from[3];
+
+ /* Prefetch next iteration. */
+ {
+ vlib_buffer_t *p2, *p3, *p4, *p5;
+
+ p2 = vlib_get_buffer (vm, from[2]);
+ p3 = vlib_get_buffer (vm, from[3]);
+ p4 = vlib_get_buffer (vm, from[4]);
+ p5 = vlib_get_buffer (vm, from[5]);
+
+ vlib_prefetch_buffer_header (p2, STORE);
+ vlib_prefetch_buffer_header (p3, STORE);
+ vlib_prefetch_buffer_header (p4, STORE);
+ vlib_prefetch_buffer_header (p5, STORE);
+
+ CLIB_PREFETCH (p2->data, sizeof (ip0[0]) + sizeof (udp0[0]),
+ STORE);
+ CLIB_PREFETCH (p3->data, sizeof (ip0[0]) + sizeof (udp0[0]),
+ STORE);
+ CLIB_PREFETCH (p4->data, sizeof (ip0[0]) + sizeof (udp0[0]),
+ STORE);
+ CLIB_PREFETCH (p5->data, sizeof (ip0[0]) + sizeof (udp0[0]),
+ STORE);
+ }
+
+ from += 4;
+ to_next += 4;
+ n_left_from -= 4;
+ n_left_to_next -= 4;
+
+ b0 = vlib_get_buffer (vm, bi0);
+ b1 = vlib_get_buffer (vm, bi1);
+ b2 = vlib_get_buffer (vm, bi2);
+ b3 = vlib_get_buffer (vm, bi3);
+ ip0 = vlib_buffer_get_current (b0);
+ ip1 = vlib_buffer_get_current (b1);
+ ip2 = vlib_buffer_get_current (b2);
+ ip3 = vlib_buffer_get_current (b2);
+
+ vnet_feature_next (vnet_buffer (b0)->sw_if_index[VLIB_TX],
+ &next0, b0);
+ vnet_feature_next (vnet_buffer (b1)->sw_if_index[VLIB_TX],
+ &next1, b1);
+ vnet_feature_next (vnet_buffer (b2)->sw_if_index[VLIB_TX],
+ &next2, b2);
+ vnet_feature_next (vnet_buffer (b3)->sw_if_index[VLIB_TX],
+ &next3, b3);
+
+ if (ip0->protocol == IP_PROTOCOL_UDP)
+ {
+ udp0 = (udp_header_t *) (ip0 + 1);
+
+ if (dhcp_client_port_network_order == udp0->dst_port)
+ {
+ next0 = DHCP_CLIENT_DETECT_NEXT_EXTRACT;
+ extractions++;
+ }
+ }
+ if (ip1->protocol == IP_PROTOCOL_UDP)
+ {
+ udp1 = (udp_header_t *) (ip1 + 1);
+
+ if (dhcp_client_port_network_order == udp1->dst_port)
+ {
+ next1 = DHCP_CLIENT_DETECT_NEXT_EXTRACT;
+ extractions++;
+ }
+ }
+ if (ip2->protocol == IP_PROTOCOL_UDP)
+ {
+ udp2 = (udp_header_t *) (ip2 + 1);
+
+ if (dhcp_client_port_network_order == udp2->dst_port)
+ {
+ next2 = DHCP_CLIENT_DETECT_NEXT_EXTRACT;
+ extractions++;
+ }
+ }
+ if (ip3->protocol == IP_PROTOCOL_UDP)
+ {
+ udp3 = (udp_header_t *) (ip3 + 1);
+
+ if (dhcp_client_port_network_order == udp3->dst_port)
+ {
+ next3 = DHCP_CLIENT_DETECT_NEXT_EXTRACT;
+ extractions++;
+ }
+ }
+
+ if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
+ {
+ dhcp_client_detect_trace_t *t =
+ vlib_add_trace (vm, node, b0, sizeof (*t));
+ t->extracted = (next0 == DHCP_CLIENT_DETECT_NEXT_EXTRACT);
+ }
+ if (PREDICT_FALSE (b1->flags & VLIB_BUFFER_IS_TRACED))
+ {
+ dhcp_client_detect_trace_t *t =
+ vlib_add_trace (vm, node, b1, sizeof (*t));
+ t->extracted = (next1 == DHCP_CLIENT_DETECT_NEXT_EXTRACT);
+ }
+ if (PREDICT_FALSE (b2->flags & VLIB_BUFFER_IS_TRACED))
+ {
+ dhcp_client_detect_trace_t *t =
+ vlib_add_trace (vm, node, b2, sizeof (*t));
+ t->extracted = (next2 == DHCP_CLIENT_DETECT_NEXT_EXTRACT);
+ }
+ if (PREDICT_FALSE (b3->flags & VLIB_BUFFER_IS_TRACED))
+ {
+ dhcp_client_detect_trace_t *t =
+ vlib_add_trace (vm, node, b3, sizeof (*t));
+ t->extracted = (next3 == DHCP_CLIENT_DETECT_NEXT_EXTRACT);
+ }
+
+ /* verify speculative enqueue, maybe switch current next frame */
+ vlib_validate_buffer_enqueue_x4 (vm, node, next_index,
+ to_next, n_left_to_next,
+ bi0, bi1, bi2, bi3,
+ next0, next1, next2, next3);
+ }
+
+ while (n_left_from > 0 && n_left_to_next > 0)
+ {
+ udp_header_t *udp0;
+ vlib_buffer_t *b0;
+ ip4_header_t *ip0;
+ u32 next0 = ~0;
+ u32 bi0;
+
+ bi0 = from[0];
+ to_next[0] = bi0;
+ from += 1;
+ to_next += 1;
+ n_left_from -= 1;
+ n_left_to_next -= 1;
+
+ b0 = vlib_get_buffer (vm, bi0);
+ ip0 = vlib_buffer_get_current (b0);
+
+ /*
+ * when this feature is applied on an interface that is already
+ * accepting packets (because e.g. the interface has other addresses
+ * assigned) we are looking for the preverbial needle in the haystack
+ * so assume the packet is not the one we are looking for.
+ */
+ vnet_feature_next (vnet_buffer (b0)->sw_if_index[VLIB_TX],
+ &next0, b0);
+
+ /*
+ * all we are looking for here is DHCP/BOOTP packet-to-client
+ * UDO port.
+ */
+ if (ip0->protocol == IP_PROTOCOL_UDP)
+ {
+ udp0 = (udp_header_t *) (ip0 + 1);
+
+ if (dhcp_client_port_network_order == udp0->dst_port)
+ {
+ next0 = DHCP_CLIENT_DETECT_NEXT_EXTRACT;
+ extractions++;
+ }
+ }
+
+ if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
+ {
+ dhcp_client_detect_trace_t *t =
+ vlib_add_trace (vm, node, b0, sizeof (*t));
+ t->extracted = (next0 == DHCP_CLIENT_DETECT_NEXT_EXTRACT);
+ }
+
+ /* verify speculative enqueue, maybe switch current next frame */
+ vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
+ to_next, n_left_to_next,
+ bi0, next0);
+ }
+
+ vlib_put_next_frame (vm, node, next_index, n_left_to_next);
+ }
+
+ vlib_node_increment_counter (vm, node->node_index,
+ DHCP_CLIENT_DETECT_ERROR_EXTRACT, extractions);
+
+ return frame->n_vectors;
+}
+
+/* packet trace format function */
+static u8 *
+format_dhcp_client_detect_trace (u8 * s, va_list * args)
+{
+ CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
+ CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
+ dhcp_client_detect_trace_t *t =
+ va_arg (*args, dhcp_client_detect_trace_t *);
+
+ s = format (s, "dhcp-client-detect: %s", (t->extracted ? "yes" : "no"));
+
+ return s;
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (dhcp_client_detect_node) = {
+ .function = dhcp_client_detect_node_fn,
+ .name = "ip4-dhcp-client-detect",
+ .vector_size = sizeof (u32),
+ .format_trace = format_dhcp_client_detect_trace,
+ .type = VLIB_NODE_TYPE_INTERNAL,
+
+ .n_errors = ARRAY_LEN(dhcp_client_detect_error_strings),
+ .error_strings = dhcp_client_detect_error_strings,
+
+ .n_next_nodes = DHCP_CLIENT_DETECT_N_NEXT,
+ .next_nodes = {
+ /*
+ * Jump straight to the UDP dispatch node thus avoiding
+ * the RPF checks in ip4-local that will fail
+ */
+ [DHCP_CLIENT_DETECT_NEXT_EXTRACT] = "ip4-udp-lookup",
+ },
+};
+
+VLIB_NODE_FUNCTION_MULTIARCH (dhcp_client_detect_node,
+ dhcp_client_detect_node_fn);
+
+VNET_FEATURE_INIT (ip4_dvr_reinject_feat_node, static) =
+{
+ .arc_name = "ip4-unicast",
+ .node_name = "ip4-dhcp-client-detect",
+ .runs_before = VNET_FEATURES ("ip4-drop"),
+};
+
+/* *INDENT-ON* */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vpp-api/vom/dhcp_config.cpp b/src/vpp-api/vom/dhcp_config.cpp
index 0b6e2eff0de..8071fb15c57 100644
--- a/src/vpp-api/vom/dhcp_config.cpp
+++ b/src/vpp-api/vom/dhcp_config.cpp
@@ -24,20 +24,25 @@ singular_db<interface::key_t, dhcp_config> dhcp_config::m_db;
dhcp_config::event_handler dhcp_config::m_evh;
-dhcp_config::dhcp_config(const interface& itf, const std::string& hostname)
+dhcp_config::dhcp_config(const interface& itf,
+ const std::string& hostname,
+ bool set_broadcast_flag)
: m_itf(itf.singular())
, m_hostname(hostname)
, m_client_id(l2_address_t::ZERO)
+ , m_set_broadcast_flag(set_broadcast_flag)
, m_binding(0)
{
}
dhcp_config::dhcp_config(const interface& itf,
const std::string& hostname,
- const l2_address_t& client_id)
+ const l2_address_t& client_id,
+ bool set_broadcast_flag)
: m_itf(itf.singular())
, m_hostname(hostname)
, m_client_id(client_id)
+ , m_set_broadcast_flag(set_broadcast_flag)
, m_binding(0)
{
}
@@ -46,6 +51,7 @@ dhcp_config::dhcp_config(const dhcp_config& o)
: m_itf(o.m_itf)
, m_hostname(o.m_hostname)
, m_client_id(o.m_client_id)
+ , m_set_broadcast_flag(o.m_set_broadcast_flag)
, m_binding(0)
{
}
diff --git a/src/vpp-api/vom/dhcp_config.hpp b/src/vpp-api/vom/dhcp_config.hpp
index db97af98c19..8ea608d809d 100644
--- a/src/vpp-api/vom/dhcp_config.hpp
+++ b/src/vpp-api/vom/dhcp_config.hpp
@@ -41,14 +41,17 @@ public:
/**
* Construct a new object matching the desried state
*/
- dhcp_config(const interface& itf, const std::string& hostname);
+ dhcp_config(const interface& itf,
+ const std::string& hostname,
+ bool set_broadcast_flag = true);
/**
* Construct a new object matching the desried state
*/
dhcp_config(const interface& itf,
const std::string& hostname,
- const l2_address_t& client_id);
+ const l2_address_t& client_id,
+ bool set_broadcast_flag = true);
/**
* Copy Constructor
@@ -203,6 +206,11 @@ private:
const l2_address_t m_client_id;
/**
+ * Flag to control the setting the of DHCP discover's broadcast flag
+ */
+ const bool m_set_broadcast_flag;
+
+ /**
* HW configuration for the binding. The bool representing the
* do/don't bind.
*/
diff --git a/src/vpp-api/vom/dhcp_config_cmds.cpp b/src/vpp-api/vom/dhcp_config_cmds.cpp
index ff24fe2f463..9e803be7b8d 100644
--- a/src/vpp-api/vom/dhcp_config_cmds.cpp
+++ b/src/vpp-api/vom/dhcp_config_cmds.cpp
@@ -23,11 +23,13 @@ namespace dhcp_config_cmds {
bind_cmd::bind_cmd(HW::item<bool>& item,
const handle_t& itf,
const std::string& hostname,
- const l2_address_t& client_id)
+ const l2_address_t& client_id,
+ bool set_broadcast_flag)
: rpc_cmd(item)
, m_itf(itf)
, m_hostname(hostname)
, m_client_id(client_id)
+ , m_set_broadcast_flag(set_broadcast_flag)
{
}
diff --git a/src/vpp-api/vom/dhcp_config_cmds.hpp b/src/vpp-api/vom/dhcp_config_cmds.hpp
index 863cf599b74..726ff992577 100644
--- a/src/vpp-api/vom/dhcp_config_cmds.hpp
+++ b/src/vpp-api/vom/dhcp_config_cmds.hpp
@@ -37,7 +37,8 @@ public:
bind_cmd(HW::item<bool>& item,
const handle_t& itf,
const std::string& hostname,
- const l2_address_t& client_id);
+ const l2_address_t& client_id,
+ bool set_braodcast_flag = false);
/**
* Issue the command to VPP/HW
@@ -68,6 +69,11 @@ private:
* The DHCP client's ID
*/
const l2_address_t m_client_id;
+
+ /**
+ * Flag to control the setting the of DHCP discover's broadcast flag
+ */
+ const bool m_set_broadcast_flag;
};
/**
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,
@@ -1311,6 +1319,14 @@ class TestDHCP(VppTestCase):
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(),
})