For DHCP client configuration control the setting of the broadcast flag in the 69/10169/2
authorNeale Ranns <nranns@cisco.com>
Wed, 17 Jan 2018 18:29:10 +0000 (10:29 -0800)
committerOle Trøan <otroan@employees.org>
Tue, 23 Jan 2018 13:36:48 +0000 (13:36 +0000)
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 <nranns@cisco.com>
12 files changed:
src/vnet.am
src/vnet/dhcp/client.c
src/vnet/dhcp/client.h
src/vnet/dhcp/dhcp.api
src/vnet/dhcp/dhcp_api.c
src/vnet/dhcp/dhcp_client_detect.c [new file with mode: 0644]
src/vpp-api/vom/dhcp_config.cpp
src/vpp-api/vom/dhcp_config.hpp
src/vpp-api/vom/dhcp_config_cmds.cpp
src/vpp-api/vom/dhcp_config_cmds.hpp
test/test_dhcp.py
test/vpp_papi_provider.py

index 84407bc..32d3167 100644 (file)
@@ -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 +=                      \
index 8043bf2..03fc268 100644 (file)
@@ -21,56 +21,6 @@ dhcp_client_main_t dhcp_client_main;
 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
index d9c7e25..1c2becb 100644 (file)
@@ -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 */
 
index 528915a..721a1be 100644 (file)
@@ -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;
 };
 
index 8e210cd..401f6b7 100644 (file)
@@ -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 (file)
index 0000000..1b916cd
--- /dev/null
@@ -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:
+ */
index 0b6e2ef..8071fb1 100644 (file)
@@ -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)
 {
 }
index db97af9..8ea608d 100644 (file)
@@ -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
@@ -202,6 +205,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.
index ff24fe2..9e803be 100644 (file)
@@ -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)
 {
 }
 
index 863cf59..726ff99 100644 (file)
@@ -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;
 };
 
 /**
index db7a7cc..21940ca 100644 (file)
@@ -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)
index 13dccc9..db0f8e6 100644 (file)
@@ -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(),
             })