DHCP Multiple Servers (VPP-602, VPP-605) 15/5515/4
authorNeale Ranns <nranns@cisco.com>
Thu, 16 Feb 2017 15:45:03 +0000 (07:45 -0800)
committerDamjan Marion <dmarion.lists@gmail.com>
Tue, 7 Mar 2017 21:21:41 +0000 (21:21 +0000)
Multiple DHCP (4 and/or 6) servers can be added and removed through multiple calls to the 'set dhcp server' API.
All 4/6/ discover/solicit messages will then be replicated to all servers in the list. The expectation is that the servers/system is configured in such a way that this is viable.
If VSS information is providied for the clinet VRF which also has multiple servers configured, then the same VSS information is sent to each server. Likewise the source address of packets sent to from VPP to each server is the same.

Change-Id: I3287cb084c84b3f612b78bc69cfcb5b9c1f8934d
Signed-off-by: Neale Ranns <nranns@cisco.com>
13 files changed:
src/scripts/vnet/dhcp/proxy
src/vat/api_format.c
src/vnet/dhcp/client.c
src/vnet/dhcp/dhcp.api
src/vnet/dhcp/dhcp4_packet.h
src/vnet/dhcp/dhcp4_proxy_node.c
src/vnet/dhcp/dhcp6_proxy_node.c
src/vnet/dhcp/dhcp_api.c
src/vnet/dhcp/dhcp_proxy.c
src/vnet/dhcp/dhcp_proxy.h
src/vnet/ip/ip6_packet.h
src/vnet/pg/input.c
test/test_dhcp.py

index c709d87..42dff2a 100644 (file)
@@ -14,7 +14,8 @@ set int ip addr loop0 2001::1/64
 set int ip addr loop0 2001:1::1/64
 
 set dhcp proxy server 10.255.0.1 src-address 10.0.0.1 server-fib-id 0 rx-fib-id 0
-set dhcp proxy server 10.255.0.2 src-address 10.0.1.1 server-fib-id 1 rx-fib-id 1
+set dhcp proxy server 10.255.0.2 src-address 10.0.0.1 server-fib-id 0 rx-fib-id 0
+set dhcp proxy server 10.255.1.2 src-address 10.0.1.1 server-fib-id 1 rx-fib-id 1
 
 set dhcpv6 proxy server 3001::1 src-address 2001::1 server-fib-id 0 rx-fib-id 0
 set dhcpv6 proxy server 3002::1 src-address 2001:1::1 server-fib-id 1 rx-fib-id 1
index 0b60b91..b5943f0 100644 (file)
@@ -7573,23 +7573,35 @@ static void
 vl_api_dhcp_proxy_details_t_handler (vl_api_dhcp_proxy_details_t * mp)
 {
   vat_main_t *vam = &vat_main;
+  u32 i, count = mp->count;
+  vl_api_dhcp_server_t *s;
 
   if (mp->is_ipv6)
     print (vam->ofp,
-          "RX Table-ID %d, Server Table-ID %d, Server Address %U, Source Address %U, VSS FIB-ID %d, VSS OUI %d",
+          "RX Table-ID %d, Source Address %U, VSS FIB-ID %d, VSS OUI %d",
           ntohl (mp->rx_vrf_id),
-          ntohl (mp->server_vrf_id),
-          format_ip6_address, mp->dhcp_server,
           format_ip6_address, mp->dhcp_src_address,
           ntohl (mp->vss_oui), ntohl (mp->vss_fib_id));
   else
     print (vam->ofp,
-          "RX Table-ID %d, Server Table-ID %d, Server Address %U, Source Address %U, VSS FIB-ID %d, VSS OUI %d",
+          "RX Table-ID %d, Source Address %U, VSS FIB-ID %d, VSS OUI %d",
           ntohl (mp->rx_vrf_id),
-          ntohl (mp->server_vrf_id),
-          format_ip4_address, mp->dhcp_server,
           format_ip4_address, mp->dhcp_src_address,
           ntohl (mp->vss_oui), ntohl (mp->vss_fib_id));
+
+  for (i = 0; i < count; i++)
+    {
+      s = &mp->servers[i];
+
+      if (mp->is_ipv6)
+       print (vam->ofp,
+              " Server Table-ID %d, Server Address %U",
+              ntohl (s->server_vrf_id), format_ip6_address, s->dhcp_server);
+      else
+       print (vam->ofp,
+              " Server Table-ID %d, Server Address %U",
+              ntohl (s->server_vrf_id), format_ip4_address, s->dhcp_server);
+    }
 }
 
 static void vl_api_dhcp_proxy_details_t_handler_json
@@ -7597,8 +7609,10 @@ static void vl_api_dhcp_proxy_details_t_handler_json
 {
   vat_main_t *vam = &vat_main;
   vat_json_node_t *node = NULL;
+  u32 i, count = mp->count;
   struct in_addr ip4;
   struct in6_addr ip6;
+  vl_api_dhcp_server_t *s;
 
   if (VAT_JSON_ARRAY != vam->json_tree.type)
     {
@@ -7609,24 +7623,38 @@ static void vl_api_dhcp_proxy_details_t_handler_json
 
   vat_json_init_object (node);
   vat_json_object_add_uint (node, "rx-table-id", ntohl (mp->rx_vrf_id));
-  vat_json_object_add_uint (node, "server-table-id",
-                           ntohl (mp->server_vrf_id));
+  vat_json_object_add_uint (node, "vss-fib-id", ntohl (mp->vss_fib_id));
+  vat_json_object_add_uint (node, "vss-oui", ntohl (mp->vss_oui));
+
   if (mp->is_ipv6)
     {
-      clib_memcpy (&ip6, &mp->dhcp_server, sizeof (ip6));
-      vat_json_object_add_ip6 (node, "server_address", ip6);
       clib_memcpy (&ip6, &mp->dhcp_src_address, sizeof (ip6));
       vat_json_object_add_ip6 (node, "src_address", ip6);
     }
   else
     {
-      clib_memcpy (&ip4, &mp->dhcp_server, sizeof (ip4));
-      vat_json_object_add_ip4 (node, "server_address", ip4);
       clib_memcpy (&ip4, &mp->dhcp_src_address, sizeof (ip4));
       vat_json_object_add_ip4 (node, "src_address", ip4);
     }
-  vat_json_object_add_uint (node, "vss-fib-id", ntohl (mp->vss_fib_id));
-  vat_json_object_add_uint (node, "vss-oui", ntohl (mp->vss_oui));
+
+  for (i = 0; i < count; i++)
+    {
+      s = &mp->servers[i];
+
+      vat_json_object_add_uint (node, "server-table-id",
+                               ntohl (s->server_vrf_id));
+
+      if (mp->is_ipv6)
+       {
+         clib_memcpy (&ip4, &s->dhcp_server, sizeof (ip4));
+         vat_json_object_add_ip4 (node, "src_address", ip4);
+       }
+      else
+       {
+         clib_memcpy (&ip6, &s->dhcp_server, sizeof (ip6));
+         vat_json_object_add_ip6 (node, "server_address", ip6);
+       }
+    }
 }
 
 static int
index d34c5a6..29749a3 100644 (file)
@@ -366,7 +366,7 @@ send_dhcp_pkt (dhcp_client_main_t * dcm, dhcp_client_t * c,
   o = (dhcp_option_t * )dhcp->options;
 
   /* Send option 53, the DHCP message type */
-  o->option = 53;
+  o->option = DHCP_PACKET_OPTION_MSG_TYPE;
   o->length = 1;
   o->data[0] = type;
   o = (dhcp_option_t *) (((uword) o) + (o->length + 2));
index 8daadd8..2db85a7 100644 (file)
@@ -137,19 +137,25 @@ define dhcp_proxy_dump
   u8  is_ip6;
 };
 
+typeonly manual_print manual_endian define dhcp_server
+{
+  u32 server_vrf_id;
+  u8 dhcp_server[16];
+};
+
 /** \brief Tell client about a DHCP completion event
     @param client_index - opaque cookie to identify the sender
 */
-define dhcp_proxy_details
+manual_endian manual_print define dhcp_proxy_details
 {
   u32 context;
   u32 rx_vrf_id;
-  u32 server_vrf_id;
   u32 vss_oui;
   u32 vss_fib_id;
   u8 is_ipv6;
-  u8 dhcp_server[16];
   u8 dhcp_src_address[16];
+  u8 count;
+  vl_api_dhcp_server_t servers[count];
 };
 
 /*
index 28c4b15..07829f4 100644 (file)
@@ -55,6 +55,11 @@ typedef enum {
   DHCP_PACKET_ACK=5,
 } dhcp_packet_type_t;
 
+typedef enum dhcp_packet_option_t_
+{
+    DHCP_PACKET_OPTION_MSG_TYPE = 53,
+} dhcp_packet_option_t;
+
 /* charming antique: 99.130.83.99 is the dhcp magic cookie */
 #define DHCP_MAGIC (clib_host_to_net_u32(0x63825363))
 
index 88a9924..1c84881 100644 (file)
@@ -135,18 +135,17 @@ dhcp_proxy_to_server_input (vlib_main_t * vm,
           u32 original_sw_if_index = 0;
           u8  *end = NULL;
           u32 fib_index;
-          dhcp_server_t * server;
+          dhcp_proxy_t *proxy;
+          dhcp_server_t *server;
           u32 rx_sw_if_index;
           dhcp_option_t *o;
           u32 len = 0;
           vlib_buffer_free_list_t *fl;
+          u8 is_discover = 0;
 
          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);
 
@@ -172,16 +171,17 @@ dhcp_proxy_to_server_input (vlib_main_t * vm,
           rx_sw_if_index = vnet_buffer(b0)->sw_if_index[VLIB_RX];
 
           fib_index = im->fib_index_by_sw_if_index [rx_sw_if_index];
-          server = dhcp_get_server(dpm, fib_index, FIB_PROTOCOL_IP4);
-          
-          if (PREDICT_FALSE (NULL == server))
+          proxy = dhcp_get_proxy(dpm, fib_index, FIB_PROTOCOL_IP4);
+
+          if (PREDICT_FALSE (NULL == proxy))
             {
               error0 = DHCP_PROXY_ERROR_NO_SERVER;
               next0 = DHCP_PROXY_TO_SERVER_INPUT_NEXT_DROP;
               pkts_no_server++;
               goto do_trace;
             }
-          
+
+          server = &proxy->dhcp_servers[0];
           vlib_buffer_advance (b0, -(sizeof(*ip0)));
           ip0 = vlib_buffer_get_current (b0);
 
@@ -198,7 +198,7 @@ dhcp_proxy_to_server_input (vlib_main_t * vm,
 
           sum0 = ip0->checksum;
           old0 = ip0->src_address.as_u32;
-          new0 = server->dhcp_src_address.ip4.as_u32;
+          new0 = proxy->dhcp_src_address.ip4.as_u32;
           ip0->src_address.as_u32 = new0;
           sum0 = ip_csum_update (sum0, old0, new0, 
                                 ip4_header_t /* structure */, 
@@ -209,7 +209,7 @@ dhcp_proxy_to_server_input (vlib_main_t * vm,
           vnet_buffer(b0)->sw_if_index[VLIB_TX] =
             server->server_fib_index;
 
-          h0->gateway_ip_address.as_u32 = server->dhcp_src_address.ip4.as_u32;
+          h0->gateway_ip_address.as_u32 = proxy->dhcp_src_address.ip4.as_u32;
           pkts_to_server++;
 
           o = (dhcp_option_t *) h0->options;
@@ -220,7 +220,16 @@ dhcp_proxy_to_server_input (vlib_main_t * vm,
           end = b0->data + b0->current_data + b0->current_length;
           /* TLVs are not performance-friendly... */
           while  (o->option != 0xFF /* end of options */ && (u8 *)o < end) 
+            {
+              if (DHCP_PACKET_OPTION_MSG_TYPE == o->option)
+                {
+                  if (DHCP_PACKET_DISCOVER == o->data[0])
+                    {
+                      is_discover = 1;
+                    }
+                }
               o = (dhcp_option_t *) (((uword) o) + (o->length + 2));
+          }
 
           fl = vlib_buffer_get_free_list (vm, b0->free_list_index);
           // start write at (option*)o, some packets have padding
@@ -340,6 +349,65 @@ dhcp_proxy_to_server_input (vlib_main_t * vm,
           
           next0 = DHCP_PROXY_TO_SERVER_INPUT_NEXT_LOOKUP;
 
+          /*
+           * If we have multiple servers configured and this is the
+           * client's discover message, then send copies to each of
+           * those servers
+           */
+          if (is_discover && vec_len(proxy->dhcp_servers) > 1)
+          {
+              u32 ii;
+
+              for (ii = 1; ii < vec_len(proxy->dhcp_servers); ii++)
+              {
+                  vlib_buffer_t *c0;
+                  u32 ci0;
+              
+                  c0 = vlib_buffer_copy(vm, b0);
+                  ci0 = vlib_get_buffer_index(vm, c0);
+                  server = &proxy->dhcp_servers[ii];
+
+                  ip0 = vlib_buffer_get_current (c0);
+
+                  sum0 = ip0->checksum;
+                  old0 = ip0->dst_address.as_u32;
+                  new0 = server->dhcp_server.ip4.as_u32;
+                  ip0->dst_address.as_u32 = server->dhcp_server.ip4.as_u32;
+                  sum0 = ip_csum_update (sum0, old0, new0, 
+                                         ip4_header_t /* structure */, 
+                                         dst_address /* changed member */);
+                  ip0->checksum = ip_csum_fold (sum0);
+
+                  to_next[0] = ci0;
+                  to_next += 1;
+                  n_left_to_next -= 1;
+
+                  vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
+                                                   to_next, n_left_to_next,
+                                                   ci0, next0);
+
+                  if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED)) 
+                  {
+                      dhcp_proxy_trace_t *tr;
+
+                      tr = vlib_add_trace (vm, node, c0, sizeof (*tr));
+                      tr->which = 0; /* to server */
+                      tr->error = error0;
+                      tr->original_sw_if_index = original_sw_if_index;
+                      tr->sw_if_index = sw_if_index;
+                      if (next0 == DHCP_PROXY_TO_SERVER_INPUT_NEXT_LOOKUP)
+                          tr->trace_ip4_address.as_u32 = server->dhcp_server.ip4.as_u32;
+                  }
+
+                  if (PREDICT_FALSE(0 == n_left_to_next))
+                  {
+                      vlib_put_next_frame (vm, node, next_index,
+                                           n_left_to_next);
+                      vlib_get_next_frame (vm, node, next_index,
+                                           to_next, n_left_to_next);
+                  }
+              }
+          }
         do_trace:
           if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED)) 
             {
@@ -350,10 +418,15 @@ dhcp_proxy_to_server_input (vlib_main_t * vm,
                tr->original_sw_if_index = original_sw_if_index;
                tr->sw_if_index = sw_if_index;
                if (next0 == DHCP_PROXY_TO_SERVER_INPUT_NEXT_LOOKUP)
-                 tr->trace_ip4_address.as_u32 = server->dhcp_server.ip4.as_u32;
+                 tr->trace_ip4_address.as_u32 =
+                     proxy->dhcp_servers[0].dhcp_server.ip4.as_u32;
             }
 
         do_enqueue:
+         to_next[0] = bi0;
+         to_next += 1;
+         n_left_to_next -= 1;
+
          vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
                                           to_next, n_left_to_next,
                                           bi0, next0);
@@ -437,7 +510,8 @@ dhcp_proxy_to_client_input (vlib_main_t * vm,
       u32 error0 = (u32)~0;
       vnet_sw_interface_t *swif;
       u32 fib_index;
-      dhcp_server_t * server;
+      dhcp_proxy_t *proxy;
+      dhcp_server_t *server;
       u32 original_sw_if_index = (u32) ~0;
       ip4_address_t relay_addr = {
           .as_u32 = 0,
@@ -547,20 +621,26 @@ dhcp_proxy_to_client_input (vlib_main_t * vm,
         }
 
       fib_index = im->fib_index_by_sw_if_index [sw_if_index];
-      server = dhcp_get_server(dpm, fib_index, FIB_PROTOCOL_IP4);
+      proxy = dhcp_get_proxy(dpm, fib_index, FIB_PROTOCOL_IP4);
 
-      if (PREDICT_FALSE (NULL == server))
+      if (PREDICT_FALSE (NULL == proxy))
         {
           error0 = DHCP_PROXY_ERROR_NO_SERVER;
           goto drop_packet;
         }
       
-      if (ip0->src_address.as_u32 != server->dhcp_server.ip4.as_u32)
-        {             
-          error0 = DHCP_PROXY_ERROR_BAD_SVR_FIB_OR_ADDRESS;
-          goto drop_packet;
+      vec_foreach(server, proxy->dhcp_servers)
+        {
+          if (ip0->src_address.as_u32 == server->dhcp_server.ip4.as_u32)
+            {
+              goto server_found;
+            }
         }
 
+      error0 = DHCP_PROXY_ERROR_BAD_SVR_FIB_OR_ADDRESS;
+      goto drop_packet;
+
+    server_found:
       vnet_buffer (b0)->sw_if_index[VLIB_TX] = sw_if_index;
 
       swif = vnet_get_sw_interface (vnm, sw_if_index);
@@ -709,9 +789,8 @@ dhcp4_proxy_set_server (ip46_address_t *addr,
 
   if (is_del)
     {
-      rc = dhcp_proxy_server_del (FIB_PROTOCOL_IP4, rx_fib_index);
-
-      if (0 == rc)
+      if (dhcp_proxy_server_del (FIB_PROTOCOL_IP4, rx_fib_index,
+                                 addr, server_table_id))
       {
           fib_table_entry_special_remove(rx_fib_index,
                                          &all_1s,
@@ -809,29 +888,35 @@ VLIB_CLI_COMMAND (dhcp_proxy_set_command, static) = {
 static u8 *
 format_dhcp4_proxy_server (u8 * s, va_list * args)
 {
-  dhcp_server_t * server = va_arg (*args, dhcp_server_t *);
+  dhcp_proxy_t *proxy = va_arg (*args, dhcp_proxy_t *);
   ip4_fib_t * rx_fib, * server_fib;
+  dhcp_server_t *server;
 
-  if (server == 0)
+  if (proxy == 0)
     {
-      s = format (s, "%=16s%=16s%=14s%=14s", "Server", "Src Address", 
-                  "Server FIB", "RX FIB");
+        s = format (s, "%=14s%=16s%s", "RX FIB", "Src Address", 
+                    "Servers FIB,Address");
       return s;
     }
 
-  server_fib = ip4_fib_get(server->server_fib_index);
-  rx_fib = ip4_fib_get(server->rx_fib_index);
+  rx_fib = ip4_fib_get(proxy->rx_fib_index);
+
+  s = format (s, "%=14u%=16U",
+              rx_fib->table_id,
+              format_ip46_address, &proxy->dhcp_src_address, IP46_TYPE_ANY);
 
-  s = format (s, "%=16U%=16U%=14u%=14u",
-              format_ip46_address, &server->dhcp_server, IP46_TYPE_ANY,
-              format_ip46_address, &server->dhcp_src_address, IP46_TYPE_ANY,
-              server_fib->table_id,
-              rx_fib->table_id);
+  vec_foreach(server, proxy->dhcp_servers)
+  {
+      server_fib = ip4_fib_get(server->server_fib_index);
+      s = format (s, "%u,%U  ",
+                  server_fib->table_id,
+                  format_ip46_address, &server->dhcp_server, IP46_TYPE_ANY);
+  }
   return s;
 }
 
 static int
-dhcp4_proxy_show_walk (dhcp_server_t *server,
+dhcp4_proxy_show_walk (dhcp_proxy_t *server,
                        void *ctx)
 {
     vlib_main_t * vm = ctx;
index 5867420..524cb09 100644 (file)
@@ -140,7 +140,8 @@ dhcpv6_proxy_to_server_input (vlib_main_t * vm,
   ip6_main_t * im = &ip6_main;
   ip6_address_t * src;
   int bogus_length;
-  dhcp_server_t * server;
+  dhcp_proxy_t *proxy;
+  dhcp_server_t *server;
   u32  rx_fib_idx = 0, server_fib_idx = 0;
 
   next_index = node->cached_next_index;
@@ -176,13 +177,11 @@ dhcpv6_proxy_to_server_input (vlib_main_t * vm,
           u8 client_src_mac[6];
           vlib_buffer_free_list_t *fl;
           dhcp_vss_t *vss;
+          u8 is_solicit = 0;
 
          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);
 
@@ -227,9 +226,9 @@ dhcpv6_proxy_to_server_input (vlib_main_t * vm,
           /* Send to DHCPV6 server via the configured FIB */
           rx_sw_if_index = sw_if_index =  vnet_buffer(b0)->sw_if_index[VLIB_RX];
           rx_fib_idx = im->mfib_index_by_sw_if_index [rx_sw_if_index];
-          server = dhcp_get_server(dpm, rx_fib_idx, FIB_PROTOCOL_IP6);
+          proxy = dhcp_get_proxy(dpm, rx_fib_idx, FIB_PROTOCOL_IP6);
 
-          if (PREDICT_FALSE (NULL == server))
+          if (PREDICT_FALSE (NULL == proxy))
           {
               error0 = DHCPV6_PROXY_ERROR_NO_SERVER;
               next0 = DHCPV6_PROXY_TO_SERVER_INPUT_NEXT_DROP;
@@ -237,6 +236,7 @@ dhcpv6_proxy_to_server_input (vlib_main_t * vm,
               goto do_trace;
           }
 
+          server = &proxy->dhcp_servers[0];
           server_fib_idx = server->server_fib_index;
           vnet_buffer(b0)->sw_if_index[VLIB_TX] = server_fib_idx;
 
@@ -371,18 +371,19 @@ dhcpv6_proxy_to_server_input (vlib_main_t * vm,
           ip1->payload_length =  u1->length;
           ip1->protocol = PROTO_UDP;
           ip1->hop_limit = HOP_COUNT_LIMIT;
-             src = (server->dhcp_server.ip6.as_u64[0] ||
-                     server->dhcp_server.ip6.as_u64[1]) ?
-               &server->dhcp_server.ip6 : &all_dhcpv6_server_address;
+          src = ((server->dhcp_server.ip6.as_u64[0] ||
+                  server->dhcp_server.ip6.as_u64[1]) ?
+                 &server->dhcp_server.ip6 :
+                 &all_dhcpv6_server_address);
           copy_ip6_address(&ip1->dst_address, src);
 
 
           ia0 = ip6_interface_first_global_or_site_address
               (&ip6_main, vnet_buffer(b0)->sw_if_index[VLIB_RX]);
 
-             src = (server->dhcp_src_address.ip6.as_u64[0] ||
-                     server->dhcp_src_address.ip6.as_u64[1]) ?
-               &server->dhcp_src_address.ip6 : ia0;
+             src = (proxy->dhcp_src_address.ip6.as_u64[0] ||
+                     proxy->dhcp_src_address.ip6.as_u64[1]) ?
+               &proxy->dhcp_src_address.ip6 : ia0;
           if (ia0 == 0)
             {
               error0 = DHCPV6_PROXY_ERROR_NO_SRC_ADDRESS;
@@ -400,6 +401,66 @@ dhcpv6_proxy_to_server_input (vlib_main_t * vm,
 
           next0 = DHCPV6_PROXY_TO_SERVER_INPUT_NEXT_LOOKUP;
 
+          is_solicit = (DHCPV6_MSG_SOLICIT == h0->u.msg_type);
+
+          /*
+           * If we have multiple servers configured and this is the
+           * client's discover message, then send copies to each of
+           * those servers
+           */
+          if (is_solicit && vec_len(proxy->dhcp_servers) > 1)
+          {
+              u32 ii;
+
+              for (ii = 1; ii < vec_len(proxy->dhcp_servers); ii++)
+              {
+                  vlib_buffer_t *c0;
+                  u32 ci0;
+              
+                  c0 = vlib_buffer_copy(vm, b0);
+                  ci0 = vlib_get_buffer_index(vm, c0);
+                  server = &proxy->dhcp_servers[ii];
+
+                  ip0 = vlib_buffer_get_current (c0);
+
+                  src = ((server->dhcp_server.ip6.as_u64[0] ||
+                          server->dhcp_server.ip6.as_u64[1]) ?
+                         &server->dhcp_server.ip6 :
+                         &all_dhcpv6_server_address);
+                  copy_ip6_address(&ip1->dst_address, src);
+
+                  to_next[0] = ci0;
+                  to_next += 1;
+                  n_left_to_next -= 1;
+
+                  vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
+                                                   to_next, n_left_to_next,
+                                                   ci0, next0);
+
+                  if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED)) 
+                  {
+                      dhcpv6_proxy_trace_t *tr;
+
+                      tr = vlib_add_trace (vm, node, c0, sizeof (*tr));
+                      tr->which = 0; /* to server */
+                      tr->error = error0;
+                      tr->original_sw_if_index = rx_sw_if_index;
+                      tr->sw_if_index = sw_if_index;
+                      if (next0 == DHCPV6_PROXY_TO_SERVER_INPUT_NEXT_LOOKUP)
+                          copy_ip6_address((ip6_address_t *)&tr->packet_data[0],
+                                           &server->dhcp_server.ip6);
+                  }
+
+                  if (PREDICT_FALSE(0 == n_left_to_next))
+                  {
+                      vlib_put_next_frame (vm, node, next_index,
+                                           n_left_to_next);
+                      vlib_get_next_frame (vm, node, next_index,
+                                           to_next, n_left_to_next);
+                  }
+              }
+          }
+
         do_trace:
           if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED))
             {
@@ -414,6 +475,10 @@ dhcpv6_proxy_to_server_input (vlib_main_t * vm,
             }
 
         do_enqueue:
+         to_next[0] = bi0;
+         to_next += 1;
+         n_left_to_next -= 1;
+
          vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
                                           to_next, n_left_to_next,
                                           bi0, next0);
@@ -475,7 +540,8 @@ dhcpv6_proxy_to_client_input (vlib_main_t * vm,
   u32 n_left_from, * from;
   ethernet_main_t *em = ethernet_get_main (vm);
   dhcp_proxy_main_t * dm = &dhcp_proxy_main;
-  dhcp_server_t * server;
+  dhcp_proxy_t *proxy;
+  dhcp_server_t *server;
   vnet_main_t * vnm = vnet_get_main();
   int bogus_length;
 
@@ -588,9 +654,9 @@ dhcpv6_proxy_to_client_input (vlib_main_t * vm,
       vlib_buffer_advance (b0, sizeof(*r0));
 
       client_fib_idx = im->mfib_index_by_sw_if_index[sw_if_index];
-      server = dhcp_get_server(dm, client_fib_idx, FIB_PROTOCOL_IP6);
+      proxy = dhcp_get_proxy(dm, client_fib_idx, FIB_PROTOCOL_IP6);
 
-      if (NULL == server)
+      if (NULL == proxy)
       {
          error0 = DHCPV6_PROXY_ERROR_NO_SERVER;
           goto drop_packet;
@@ -599,15 +665,21 @@ dhcpv6_proxy_to_client_input (vlib_main_t * vm,
       server_fib_idx = im->fib_index_by_sw_if_index
           [vnet_buffer(b0)->sw_if_index[VLIB_RX]];
 
-      if (server_fib_idx != server->server_fib_index ||
-          ip0->src_address.as_u64[0] != server->dhcp_server.ip6.as_u64[0] ||
-          ip0->src_address.as_u64[1] != server->dhcp_server.ip6.as_u64[1])
-        {
-          //drop packet if not from server with configured address or FIB
-          error0 = DHCPV6_PROXY_ERROR_BAD_SVR_FIB_OR_ADDRESS;
-          goto drop_packet;
-        }
+      vec_foreach(server, proxy->dhcp_servers)
+      {
+          if (server_fib_idx == server->server_fib_index &&
+              ip0->src_address.as_u64[0] == server->dhcp_server.ip6.as_u64[0] &&
+              ip0->src_address.as_u64[1] == server->dhcp_server.ip6.as_u64[1])
+          {
+              goto server_found;
+          }
+      }
+
+      //drop packet if not from server with configured address or FIB
+      error0 = DHCPV6_PROXY_ERROR_BAD_SVR_FIB_OR_ADDRESS;
+      goto drop_packet;
 
+    server_found:
       vnet_buffer (b0)->sw_if_index[VLIB_TX] = original_sw_if_index
           = sw_if_index;
 
@@ -773,9 +845,8 @@ dhcp6_proxy_set_server (ip46_address_t *addr,
 
   if (is_del)
     {
-      rc = dhcp_proxy_server_del (FIB_PROTOCOL_IP6, rx_fib_index);
-
-      if (0 == rc)
+      if (dhcp_proxy_server_del (FIB_PROTOCOL_IP6, rx_fib_index,
+                                 addr, server_table_id))
       {
           mfib_table_entry_delete(rx_fib_index,
                                   &all_dhcp_servers,
@@ -893,43 +964,50 @@ VLIB_CLI_COMMAND (dhcpv6_proxy_set_command, static) = {
 static u8 *
 format_dhcp6_proxy_server (u8 * s, va_list * args)
 {
-  dhcp_server_t * server = va_arg (*args, dhcp_server_t *);
+  dhcp_proxy_t * proxy = va_arg (*args, dhcp_proxy_t *);
   ip6_fib_t *server_fib;
+  dhcp_server_t *server;
   ip6_mfib_t *rx_fib;
 
-  if (NULL == server)
+  if (proxy == 0)
     {
-      s = format (s, "%=40s%=40s%=14s%=14s", "Server Address", "Source Address",
-                  "Server FIB", "RX FIB");
+        s = format (s, "%=14s%=16s%s", "RX FIB", "Src Address", 
+                    "Servers FIB,Address");
       return s;
     }
 
-  server_fib = ip6_fib_get(server->server_fib_index);
-  rx_fib = ip6_mfib_get(server->rx_fib_index);
+  rx_fib = ip6_mfib_get(proxy->rx_fib_index);
+
+  s = format (s, "%=14u%=16U",
+              rx_fib->table_id,
+              format_ip46_address, &proxy->dhcp_src_address, IP46_TYPE_ANY);
 
+  vec_foreach(server, proxy->dhcp_servers)
+  {
+      server_fib = ip6_fib_get(server->server_fib_index);
+      s = format (s, "%u,%U  ",
+                  server_fib->table_id,
+                  format_ip46_address, &server->dhcp_server, IP46_TYPE_ANY);
+  }
 
-  s = format (s, "%=40U%=40U%=14u%=14u",
-              format_ip46_address, &server->dhcp_server, IP46_TYPE_ANY,
-              format_ip46_address, &server->dhcp_src_address, IP46_TYPE_ANY,
-              server_fib->table_id, rx_fib->table_id);
   return s;
 }
 
 static int
-dhcp6_proxy_show_walk (dhcp_server_t *server,
+dhcp6_proxy_show_walk (dhcp_proxy_t *proxy,
                        void *ctx)
 {
     vlib_main_t * vm = ctx;
 
-    vlib_cli_output (vm, "%U", format_dhcp6_proxy_server, server);
+    vlib_cli_output (vm, "%U", format_dhcp6_proxy_server, proxy);
 
     return (1);
 }
 
 static clib_error_t *
 dhcpv6_proxy_show_command_fn (vlib_main_t * vm,
-                            unformat_input_t * input,
-                            vlib_cli_command_t * cmd)
+                              unformat_input_t * input,
+                              vlib_cli_command_t * cmd)
 {
   vlib_cli_output (vm, "%U", format_dhcp6_proxy_server, NULL /* header line */);
 
index ce34f6a..e9c757e 100644 (file)
@@ -24,6 +24,7 @@
 #include <vnet/api_errno.h>
 #include <vnet/dhcp/dhcp_proxy.h>
 #include <vnet/dhcp/client.h>
+#include <vnet/fib/fib_table.h>
 
 #include <vnet/vnet_msg_enum.h>
 
@@ -113,46 +114,73 @@ vl_api_dhcp_proxy_dump_t_handler (vl_api_dhcp_proxy_dump_t * mp)
   if (q == 0)
     return;
 
-  dhcp_proxy_dump ((mp->is_ip6 == 0 ?
+  dhcp_proxy_dump ((mp->is_ip6 == 1 ?
                    FIB_PROTOCOL_IP6 : FIB_PROTOCOL_IP4), q, mp->context);
 }
 
 void
 dhcp_send_details (fib_protocol_t proto,
-                  void *opaque,
-                  u32 context,
-                  const ip46_address_t * server,
-                  const ip46_address_t * src,
-                  u32 server_fib_id,
-                  u32 rx_fib_id, u32 vss_fib_id, u32 vss_oui)
+                  void *opaque, u32 context, dhcp_proxy_t * proxy)
 {
   vl_api_dhcp_proxy_details_t *mp;
   unix_shared_memory_queue_t *q = opaque;
-
-  mp = vl_msg_api_alloc (sizeof (*mp));
+  vl_api_dhcp_server_t *v_server;
+  dhcp_server_t *server;
+  fib_table_t *s_fib;
+  dhcp_vss_t *vss;
+  u32 count;
+  size_t n;
+
+  count = vec_len (proxy->dhcp_servers);
+  n = sizeof (*mp) + (count * sizeof (vl_api_dhcp_server_t));
+  mp = vl_msg_api_alloc (n);
   if (!mp)
     return;
-  memset (mp, 0, sizeof (*mp));
+  memset (mp, 0, n);
   mp->_vl_msg_id = ntohs (VL_API_DHCP_PROXY_DETAILS);
   mp->context = context;
-
-  mp->rx_vrf_id = htonl (rx_fib_id);
-  mp->server_vrf_id = htonl (server_fib_id);
-  mp->vss_oui = htonl (vss_oui);
-  mp->vss_fib_id = htonl (vss_fib_id);
+  mp->count = count;
 
   mp->is_ipv6 = (proto == FIB_PROTOCOL_IP6);
+  mp->rx_vrf_id =
+    htonl (dhcp_proxy_rx_table_get_table_id (proto, proxy->rx_fib_index));
+
+  vss = dhcp_get_vss_info (&dhcp_proxy_main, proxy->rx_fib_index, proto);
+
+  if (NULL != vss)
+    {
+      mp->vss_oui = htonl (vss->oui);
+      mp->vss_fib_id = htonl (vss->fib_id);
+    }
+
+  vec_foreach_index (count, proxy->dhcp_servers)
+  {
+    server = &proxy->dhcp_servers[count];
+    v_server = &mp->servers[count];
+
+    s_fib = fib_table_get (server->server_fib_index, proto);
+
+    v_server->server_vrf_id = htonl (s_fib->ft_table_id);
+
+    if (mp->is_ipv6)
+      {
+       memcpy (v_server->dhcp_server, &server->dhcp_server.ip6, 16);
+      }
+    else
+      {
+       /* put the address in the first bytes */
+       memcpy (v_server->dhcp_server, &server->dhcp_server.ip4, 4);
+      }
+  }
 
   if (mp->is_ipv6)
     {
-      memcpy (mp->dhcp_server, server, 16);
-      memcpy (mp->dhcp_src_address, src, 16);
+      memcpy (mp->dhcp_src_address, &proxy->dhcp_src_address.ip6, 16);
     }
   else
     {
       /* put the address in the first bytes */
-      memcpy (mp->dhcp_server, &server->ip4, 4);
-      memcpy (mp->dhcp_src_address, &src->ip4, 4);
+      memcpy (mp->dhcp_src_address, &proxy->dhcp_src_address.ip4, 4);
     }
   vl_msg_api_send_shmem (q, (u8 *) & mp);
 }
index 8e31c3d..ba7f354 100644 (file)
@@ -44,7 +44,7 @@ dhcp_proxy_rx_table_unlock (fib_protocol_t proto,
         mfib_table_unlock(fib_index, proto);
 }
 
-static u32
+ u32
 dhcp_proxy_rx_table_get_table_id (fib_protocol_t proto,
                                   u32 fib_index)
 {
@@ -72,7 +72,7 @@ dhcp_proxy_walk (fib_protocol_t proto,
                  void *ctx)
 {
   dhcp_proxy_main_t * dpm = &dhcp_proxy_main;
-  dhcp_server_t * server;
+  dhcp_proxy_t * server;
   u32 server_index, i;
 
   vec_foreach_index (i, dpm->dhcp_server_index_by_rx_fib_index[proto])
@@ -124,31 +124,68 @@ dhcp_vss_walk (fib_protocol_t proto,
     }
 }
 
+static u32
+dhcp_proxy_server_find (dhcp_proxy_t *proxy,
+                        fib_protocol_t proto,
+                        ip46_address_t *addr,
+                        u32 server_table_id)
+{
+    dhcp_server_t *server;
+    u32 ii, fib_index;
+
+    vec_foreach_index(ii, proxy->dhcp_servers)
+    {
+        server = &proxy->dhcp_servers[ii];
+        fib_index = fib_table_find(proto, server_table_id);
+
+        if (ip46_address_is_equal(&server->dhcp_server,
+                                  addr) &&
+            (server->server_fib_index == fib_index))
+        {
+            return (ii);
+        }
+    }
+    return (~0);
+}
+
 int
 dhcp_proxy_server_del (fib_protocol_t proto,
-                       u32 rx_fib_index)
+                       u32 rx_fib_index,
+                       ip46_address_t *addr,
+                       u32 server_table_id)
 {
   dhcp_proxy_main_t * dpm = &dhcp_proxy_main;
-  dhcp_server_t * server = 0;
-  int rc = 0;
+  dhcp_proxy_t *proxy = 0;
 
-  server = dhcp_get_server(dpm, rx_fib_index, proto);
+  proxy = dhcp_get_proxy(dpm, rx_fib_index, proto);
 
-  if (NULL == server)
-  {
-      rc = VNET_API_ERROR_NO_SUCH_ENTRY;
-  }
-  else
+  if (NULL != proxy)
   {
-      /* Use the default server again.  */
-      dpm->dhcp_server_index_by_rx_fib_index[proto][rx_fib_index] = ~0;
+      dhcp_server_t *server;
+      u32 index;
 
-      fib_table_unlock (server->server_fib_index, proto);
+      index = dhcp_proxy_server_find(proxy, proto, addr, server_table_id);
 
-      pool_put (dpm->dhcp_servers[proto], server);
+      if (~0 != index)
+      {
+          server = &proxy->dhcp_servers[index];
+          fib_table_unlock (server->server_fib_index, proto);
+
+          vec_del1(proxy->dhcp_servers, index);
+
+          if (0 == vec_len(proxy->dhcp_servers))
+          {
+              /* no servers left, delete the proxy config */
+              dpm->dhcp_server_index_by_rx_fib_index[proto][rx_fib_index] = ~0;
+              vec_free(proxy->dhcp_servers);
+              pool_put (dpm->dhcp_servers[proto], proxy);
+              return (1);
+          }
+      }
   }
 
-  return (rc);
+  /* the proxy still exists */
+  return (0);
 }
 
 int
@@ -159,48 +196,42 @@ dhcp_proxy_server_add (fib_protocol_t proto,
                        u32 server_table_id)
 {
   dhcp_proxy_main_t * dpm = &dhcp_proxy_main;
-  dhcp_server_t * server = 0;
+  dhcp_proxy_t * proxy = 0;
   int new = 0;
 
-  server = dhcp_get_server(dpm, rx_fib_index, proto);
+  proxy = dhcp_get_proxy(dpm, rx_fib_index, proto);
 
-  if (NULL == server)
+  if (NULL == proxy)
   {
       vec_validate_init_empty(dpm->dhcp_server_index_by_rx_fib_index[proto],
                               rx_fib_index,
                               ~0);
 
-      pool_get (dpm->dhcp_servers[proto], server);
-      memset (server, 0, sizeof (*server));
+      pool_get (dpm->dhcp_servers[proto], proxy);
+      memset (proxy, 0, sizeof (*proxy));
       new = 1;
 
       dpm->dhcp_server_index_by_rx_fib_index[proto][rx_fib_index] =
-          server - dpm->dhcp_servers[proto];
+          proxy - dpm->dhcp_servers[proto];
 
-      server->rx_fib_index = rx_fib_index;
-      server->server_fib_index = 
-          fib_table_find_or_create_and_lock(proto, server_table_id);
+      proxy->dhcp_src_address = *src_address;
+      proxy->rx_fib_index = rx_fib_index;
   }
   else
   {
-      /* modify, may need to swap server FIBs */
-      u32 tmp_index;
-
-      tmp_index = fib_table_find(proto, server_table_id);
-
-      if (tmp_index != server->server_fib_index)
+      if (~0 != dhcp_proxy_server_find(proxy, proto, addr, server_table_id))
       {
-          tmp_index = server->server_fib_index;
-
-          /* certainly swapping if the fib doesn't exist */
-          server->server_fib_index = 
-              fib_table_find_or_create_and_lock(proto, server_table_id);
-          fib_table_unlock (tmp_index, proto);
+          return (new);
       }
   }
 
-  server->dhcp_server = *addr;
-  server->dhcp_src_address = *src_address;
+  dhcp_server_t server = {
+      .dhcp_server = *addr,
+      .server_fib_index = fib_table_find_or_create_and_lock(proto,
+                                                            server_table_id),
+  };
+
+  vec_add1(proxy->dhcp_servers, server);
 
   return (new);
 }
@@ -213,31 +244,15 @@ typedef struct dhcp4_proxy_dump_walk_ctx_t_
 } dhcp_proxy_dump_walk_cxt_t;
 
 static int
-dhcp_proxy_dump_walk (dhcp_server_t *server,
+dhcp_proxy_dump_walk (dhcp_proxy_t *proxy,
                       void *arg)
 {
   dhcp_proxy_dump_walk_cxt_t *ctx = arg;
-  fib_table_t *s_fib;
-  u32 rx_table_id;
-  dhcp_vss_t *v;
-
-  v = dhcp_get_vss_info(&dhcp_proxy_main,
-                        server->rx_fib_index,
-                        ctx->proto);
-
-  s_fib = fib_table_get(server->server_fib_index, ctx->proto);
-  rx_table_id = dhcp_proxy_rx_table_get_table_id(server->rx_fib_index,
-                                                 ctx->proto);
 
   dhcp_send_details(ctx->proto,
                     ctx->opaque,
                     ctx->context,
-                    &server->dhcp_server,
-                    &server->dhcp_src_address,
-                    s_fib->ft_table_id,
-                    rx_table_id,
-                    (v ? v->fib_id : 0),
-                    (v ? v->oui : 0));
+                    proxy);
 
   return (1);
 }
index 708e92f..ef2bc0a 100644 (file)
@@ -58,32 +58,58 @@ typedef struct dhcp_vss_t_ {
 } dhcp_vss_t;
 
 /**
- * @brief A DHCP proxy server represenation
+ * @brief A representation of a single DHCP Server within a given VRF config
  */
-typedef struct dhcp_server_t_ {
+typedef struct dhcp_server_t_
+{
     /**
      * @brief The address of the DHCP server to which to relay the client's
      *        messages
      */
     ip46_address_t dhcp_server;
 
-    /**
-     * @brief The source address to use in relayed messaes
-     */
-    ip46_address_t dhcp_src_address;
-
     /**
      * @brief The FIB index (not the external Table-ID) in which the server
      *        is reachable.
      */
     u32 server_fib_index;
+} dhcp_server_t;
+
+/**
+ * @brief A DHCP proxy represenation fpr per-client VRF config
+ */
+typedef struct dhcp_proxy_t_ {
+    /**
+     * @brief The set of DHCP servers to which messages are relayed.
+     *  If multiple servers are configured then discover/solict messages
+     * are relayed to each. A cookie is maintained for the relay, and only
+     * one message is replayed to the client, based on the presence of the
+     * cookie.
+     * The expectation is there are only 1 or 2 servers, hence no fancy DB.
+     */
+    dhcp_server_t *dhcp_servers;
+
+    /**
+     * @brief Hash table of pending requets key'd on the clients MAC address
+     */
+    uword *dhcp_pending;
+
+    /**
+     * @brief A lock for the pending request DB.
+     */
+    int lock;
+
+    /**
+     * @brief The source address to use in relayed messaes
+     */
+    ip46_address_t dhcp_src_address;
 
     /**
      * @brief The FIB index (not the external Table-ID) in which the client
      *        is resides.
      */
     u32 rx_fib_index;
-} dhcp_server_t;
+} dhcp_proxy_t;
 
 #define DHCP_N_PROTOS (FIB_PROTOCOL_IP6 + 1)
 
@@ -92,7 +118,7 @@ typedef struct dhcp_server_t_ {
  */
 typedef struct {
   /* Pool of DHCP servers */
-  dhcp_server_t *dhcp_servers[DHCP_N_PROTOS];
+  dhcp_proxy_t *dhcp_servers[DHCP_N_PROTOS];
 
   /* Pool of selected DHCP server. Zero is the default server */
   u32 * dhcp_server_index_by_rx_fib_index[DHCP_N_PROTOS];
@@ -114,12 +140,7 @@ extern dhcp_proxy_main_t dhcp_proxy_main;
 void dhcp_send_details (fib_protocol_t proto,
                         void *opaque,
                         u32 context,
-                        const ip46_address_t *server,
-                        const ip46_address_t *src,
-                        u32 server_fib_id,
-                        u32 rx_fib_id,
-                        u32 vss_fib_id,
-                        u32 vss_oui);
+                        dhcp_proxy_t *proxy);
 
 /**
  * @brief Show (on CLI) a VSS config during a show walk
@@ -157,16 +178,22 @@ int dhcp_proxy_server_add(fib_protocol_t proto,
 
 /**
  * @brief Delete a DHCP proxy config
- * @return 0 is deleted, otherwise an error code
+ * @return 1 if the proxy is deleted, 0 otherwise
  */
 int dhcp_proxy_server_del(fib_protocol_t proto,
-                          u32 rx_fib_index);
+                          u32 rx_fib_index,
+                          ip46_address_t *addr,
+                          u32 server_table_id);
+
+u32
+dhcp_proxy_rx_table_get_table_id (fib_protocol_t proto,
+                                  u32 fib_index);
 
 /**
  * @brief Callback function invoked for each DHCP proxy entry
  *  return 0 to break the walk, non-zero otherwise.
  */
-typedef int (*dhcp_proxy_walk_fn_t)(dhcp_server_t *server,
+typedef int (*dhcp_proxy_walk_fn_t)(dhcp_proxy_t *server,
                                     void *ctx);
 
 /**
@@ -191,6 +218,18 @@ void dhcp_vss_walk(fib_protocol_t proto,
                    dhcp_vss_walk_fn_t fn,
                    void *ctx);
 
+/**
+ * @brief Lock a proxy object to prevent simultaneous access of its
+ *  pending store
+ */
+void dhcp_proxy_lock (dhcp_proxy_t *server);
+
+/**
+ * @brief Lock a proxy object to prevent simultaneous access of its
+ *  pending store
+ */
+void dhcp_proxy_unlock (dhcp_proxy_t *server);
+
 /**
  * @brief Get the VSS data for the FIB index
  */
@@ -215,12 +254,12 @@ dhcp_get_vss_info (dhcp_proxy_main_t *dm,
 /**
  * @brief Get the DHCP proxy server data for the FIB index
  */
-static inline dhcp_server_t *
-dhcp_get_server (dhcp_proxy_main_t *dm,
-                 u32 rx_fib_index,
-                 fib_protocol_t proto)
+static inline dhcp_proxy_t *
+dhcp_get_proxy (dhcp_proxy_main_t *dm,
+                u32 rx_fib_index,
+                fib_protocol_t proto)
 {
-  dhcp_server_t *s = NULL;
+  dhcp_proxy_t *s = NULL;
 
   if (vec_len(dm->dhcp_server_index_by_rx_fib_index[proto]) > rx_fib_index &&
       dm->dhcp_server_index_by_rx_fib_index[proto][rx_fib_index] != ~0)
index 6eabeef..9bf19ed 100644 (file)
@@ -79,6 +79,8 @@ typedef CLIB_PACKED (union {
 #define ip46_address_reset(ip46)       ((ip46)->as_u64[0] = (ip46)->as_u64[1] = 0)
 #define ip46_address_cmp(ip46_1, ip46_2) (memcmp(ip46_1, ip46_2, sizeof(*ip46_1)))
 #define ip46_address_is_zero(ip46)     (((ip46)->as_u64[0] == 0) && ((ip46)->as_u64[1] == 0))
+#define ip46_address_is_equal(a1, a2)  (((a1)->as_u64[0] == (a2)->as_u64[0]) \
+                                         && ((a1)->as_u64[1] == (a2)->as_u64[1]))
 
 always_inline void
 ip46_from_addr_buf (u32 is_ipv6, u8 * buf, ip46_address_t * ip)
index 4a65b02..2649798 100644 (file)
@@ -1373,6 +1373,7 @@ typedef struct
   u32 stream_index;
 
   u32 packet_length;
+  u32 sw_if_index;
 
   /* Use pre data for packet data. */
   vlib_buffer_t buffer;
@@ -1399,6 +1400,7 @@ format_pg_input_trace (u8 * s, va_list * va)
     s = format (s, "stream %d", t->stream_index);
 
   s = format (s, ", %d bytes", t->packet_length);
+  s = format (s, ", %d sw_if_index", t->sw_if_index);
 
   s = format (s, "\n%U%U",
              format_white_space, indent, format_vlib_buffer, &t->buffer);
@@ -1458,6 +1460,9 @@ pg_input_trace (pg_main_t * pg,
       t0->packet_length = vlib_buffer_length_in_chain (vm, b0);
       t1->packet_length = vlib_buffer_length_in_chain (vm, b1);
 
+      t0->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];
+      t1->sw_if_index = vnet_buffer (b1)->sw_if_index[VLIB_RX];
+
       clib_memcpy (&t0->buffer, b0, sizeof (b0[0]) - sizeof (b0->pre_data));
       clib_memcpy (&t1->buffer, b1, sizeof (b1[0]) - sizeof (b1->pre_data));
 
@@ -1484,6 +1489,7 @@ pg_input_trace (pg_main_t * pg,
 
       t0->stream_index = stream_index;
       t0->packet_length = vlib_buffer_length_in_chain (vm, b0);
+      t0->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];
       clib_memcpy (&t0->buffer, b0, sizeof (b0[0]) - sizeof (b0->pre_data));
       clib_memcpy (t0->buffer.pre_data, b0->data,
                   sizeof (t0->buffer.pre_data));
index a09c9bd..89667d3 100644 (file)
@@ -2,8 +2,10 @@
 
 import unittest
 import socket
+import struct
 
 from framework import VppTestCase, VppTestRunner
+from vpp_neighbor import VppNeighbor
 
 from scapy.layers.l2 import Ether, getmacbyip
 from scapy.layers.inet import IP, UDP, ICMP
@@ -11,7 +13,7 @@ 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
@@ -140,7 +142,7 @@ class TestDHCP(VppTestCase):
 
         return data
 
-    def verify_dhcp_offer(self, pkt, intf):
+    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)
@@ -162,15 +164,22 @@ class TestDHCP(VppTestCase):
                     is_offer = True
         self.assertTrue(is_offer)
 
-        data = self.validate_relay_options(pkt, intf, intf.local_ip4, 0, 0)
+        data = self.validate_relay_options(pkt, intf, intf.local_ip4,
+                                           fib_id, oui)
+
+    def verify_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]
@@ -195,13 +204,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]
@@ -447,6 +463,128 @@ class TestDHCP(VppTestCase):
         self.pg_enable_capture(self.pg_interfaces)
         self.pg_start()
 
+        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)
+
+        #
+        # 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_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_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_dhcp_discover(rx, self.pg1, src_intf=self.pg3,
@@ -472,7 +610,7 @@ 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,
@@ -500,18 +638,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")
 
         #
@@ -525,9 +661,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")
 
         #
@@ -538,13 +674,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)
 
@@ -557,8 +693,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
@@ -567,8 +702,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
@@ -578,8 +712,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) /
@@ -589,8 +722,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")
 
         #
@@ -611,8 +743,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)
@@ -632,8 +764,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
@@ -648,13 +780,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)
 
@@ -676,21 +808,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,
@@ -702,27 +834,163 @@ 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,