Add Support of DHCP VSS Type 0 where VPN-ID is ASCII
[vpp.git] / src / vnet / dhcp / dhcp6_proxy_node.c
index ed44977..3cac278 100644 (file)
@@ -19,9 +19,9 @@
 #include <vnet/pg/pg.h>
 #include <vnet/dhcp/dhcp_proxy.h>
 #include <vnet/dhcp/dhcp6_packet.h>
-#include <vnet/fib/ip6_fib.h>
 #include <vnet/mfib/mfib_table.h>
 #include <vnet/mfib/ip6_mfib.h>
+#include <vnet/fib/fib.h>
 
 static char * dhcpv6_proxy_error_strings[] = {
 #define dhcpv6_proxy_error(n,s) s,
@@ -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);
 
@@ -226,10 +225,10 @@ 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->fib_index_by_sw_if_index [rx_sw_if_index];
-          server = dhcp_get_server(dpm, rx_fib_idx, FIB_PROTOCOL_IP6);
+          rx_fib_idx = im->mfib_index_by_sw_if_index [rx_sw_if_index];
+          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;
 
@@ -306,7 +306,7 @@ dhcpv6_proxy_to_server_input (vlib_main_t * vm,
           copy_ip6_address(&r1->link_addr, ia0);
 
         link_address_set:
-          fl = vlib_buffer_get_free_list (vm, b0->free_list_index);
+          fl = vlib_buffer_get_free_list (vm, vlib_buffer_get_free_list_index (b0));
 
           if ((b0->current_length+sizeof(*id1)+sizeof(*vss1)+sizeof(*cmac))
               > fl->n_data_bytes)
@@ -330,31 +330,40 @@ dhcpv6_proxy_to_server_input (vlib_main_t * vm,
                cmac = (dhcpv6_client_mac_t *) (((uword) ip1) + b0->current_length);
                b0->current_length += (sizeof (*cmac));
                cmac->opt.length =clib_host_to_net_u16(sizeof(*cmac) -
-                                                      sizeof(cmac->opt));
+                                                     sizeof(cmac->opt));
                cmac->opt.option = clib_host_to_net_u16(DHCPV6_OPTION_CLIENT_LINK_LAYER_ADDRESS);
-               cmac->link_type = clib_host_to_net_u16(1); // ethernet
+               cmac->link_type = clib_host_to_net_u16(1); /* ethernet */
                clib_memcpy(cmac->data, client_src_mac, 6);
                u1->length += sizeof(*cmac);
             }
 
           vss = dhcp_get_vss_info(dpm, rx_fib_idx, FIB_PROTOCOL_IP6);
 
-          if (NULL != vss) {
+          if (vss)
+           {
+             u16 id_len;       /* length of VPN ID */
+             u16 type_len = sizeof (vss1->vss_type);
+
               vss1 = (dhcpv6_vss_t *) (((uword) ip1) + b0->current_length);
-              b0->current_length += (sizeof (*vss1));
-              vss1->opt.length =clib_host_to_net_u16(sizeof(*vss1) -
-                                                    sizeof(vss1->opt));
-              vss1->opt.option = clib_host_to_net_u16(DHCPV6_OPTION_VSS);
-              vss1->data[0] = 1;   // type
-              vss1->data[1] = vss->oui >>16 & 0xff;
-              vss1->data[2] = vss->oui >>8  & 0xff;
-              vss1->data[3] = vss->oui & 0xff;
-              vss1->data[4] = vss->fib_id >> 24 & 0xff;
-              vss1->data[5] = vss->fib_id >> 16 & 0xff;
-              vss1->data[6] = vss->fib_id >> 8 & 0xff;
-              vss1->data[7] = vss->fib_id & 0xff;
-              u1->length += sizeof(*vss1);
-          }
+             vss1->vss_type = vss->vss_type;
+             if (vss->vss_type == VSS_TYPE_VPN_ID)
+               {
+                 id_len = sizeof (vss->vpn_id);        /* vpn_id is 7 bytes */
+                 memcpy (vss1->data, vss->vpn_id, id_len);
+               }
+             else if (vss->vss_type == VSS_TYPE_ASCII)
+               {
+                 id_len = vec_len (vss->vpn_ascii_id);
+                 memcpy (vss1->data, vss->vpn_ascii_id, id_len);
+               }
+             else      /* must be VSS_TYPE_DEFAULT, no VPN ID */
+               id_len = 0;
+
+              vss1->opt.option = clib_host_to_net_u16 (DHCPV6_OPTION_VSS);
+             vss1->opt.length = clib_host_to_net_u16 (type_len + id_len);
+             u1->length += type_len + id_len + sizeof (vss1->opt);
+             b0->current_length += type_len + id_len + sizeof (vss1->opt);
+            }
 
           pkts_to_server++;
           u1->checksum = 0;
@@ -371,18 +380,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 +410,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 +484,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 +549,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;
 
@@ -587,10 +662,10 @@ dhcpv6_proxy_to_client_input (vlib_main_t * vm,
       //Advance buffer to start of encapsulated DHCPv6 message
       vlib_buffer_advance (b0, sizeof(*r0));
 
-      client_fib_idx = im->fib_index_by_sw_if_index[sw_if_index];
-      server = dhcp_get_server(dm, client_fib_idx, FIB_PROTOCOL_IP6);
+      client_fib_idx = im->mfib_index_by_sw_if_index[sw_if_index];
+      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 +674,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;
 
@@ -769,24 +850,24 @@ dhcp6_proxy_set_server (ip46_address_t *addr,
     return VNET_API_ERROR_INVALID_SRC_ADDRESS;
 
   rx_fib_index = mfib_table_find_or_create_and_lock(FIB_PROTOCOL_IP6,
-                                                    rx_table_id);
+                                                    rx_table_id,
+                                                    MFIB_SOURCE_DHCP);
 
   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,
                                   MFIB_SOURCE_DHCP);
-          mfib_table_unlock(rx_fib_index, FIB_PROTOCOL_IP6);
+          mfib_table_unlock(rx_fib_index, FIB_PROTOCOL_IP6, MFIB_SOURCE_DHCP);
       }
     }
   else
     {
      const fib_route_path_t path_for_us = {
-          .frp_proto = FIB_PROTOCOL_IP6,
+          .frp_proto = DPO_PROTO_IP6,
           .frp_addr = zero_addr,
           .frp_sw_if_index = 0xffffffff,
           .frp_fib_index = ~0,
@@ -812,12 +893,13 @@ dhcp6_proxy_set_server (ip46_address_t *addr,
          mfib_table_entry_update(rx_fib_index,
                                  &all_dhcp_servers,
                                  MFIB_SOURCE_DHCP,
+                                 MFIB_RPF_ID_NONE,
                                  MFIB_ENTRY_FLAG_ACCEPT_ALL_ITF);
-         mfib_table_lock(rx_fib_index, FIB_PROTOCOL_IP6);
+         mfib_table_lock(rx_fib_index, FIB_PROTOCOL_IP6, MFIB_SOURCE_DHCP);
      }
     }
 
-  mfib_table_unlock(rx_fib_index, FIB_PROTOCOL_IP6);
+  mfib_table_unlock(rx_fib_index, FIB_PROTOCOL_IP6, MFIB_SOURCE_DHCP);
 
   return (rc);
 }
@@ -893,42 +975,51 @@ 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 *);
-  ip6_fib_t * rx_fib, * server_fib;
+  dhcp_proxy_t * proxy = va_arg (*args, dhcp_proxy_t *);
+  fib_table_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_fib_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 = fib_table_get(server->server_fib_index,
+                                 FIB_PROTOCOL_IP6);
+      s = format (s, "%u,%U  ",
+                  server_fib->ft_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 */);
 
@@ -948,18 +1039,20 @@ dhcpv6_vss_command_fn (vlib_main_t * vm,
                        unformat_input_t * input,
                        vlib_cli_command_t * cmd)
 {
-  int is_del = 0, got_new_vss=0;
-  u32 oui=0;
-  u32 fib_id=0, tbl_id=~0;
+  u8 is_del = 0, vss_type = VSS_TYPE_DEFAULT;
+  u8 *vpn_ascii_id = 0;
+  u32 oui = 0, fib_id = 0, tbl_id = ~0;
 
   while (unformat_check_input(input) != UNFORMAT_END_OF_INPUT)
     {
-      if (unformat (input, "oui %d", &oui))
-          got_new_vss = 1;
+      if (unformat (input, "table %d", &tbl_id))
+          ;
+      else if (unformat (input, "oui %d", &oui))
+         vss_type = VSS_TYPE_VPN_ID;
       else if (unformat (input, "vpn-id %d", &fib_id))
-          got_new_vss = 1;
-      else if (unformat (input, "table %d", &tbl_id))
-          got_new_vss = 1;
+         vss_type = VSS_TYPE_VPN_ID;
+      else if (unformat (input, "vpn-ascii-id %s", &vpn_ascii_id))
+         vss_type = VSS_TYPE_ASCII;
       else if (unformat(input, "delete") || unformat(input, "del"))
           is_del = 1;
       else
@@ -969,37 +1062,23 @@ dhcpv6_vss_command_fn (vlib_main_t * vm,
   if (tbl_id ==~0)
       return clib_error_return (0, "no table ID specified.");
 
-  if (is_del || got_new_vss)
+  int rv = dhcp_proxy_set_vss(FIB_PROTOCOL_IP6, tbl_id, vss_type,
+                             vpn_ascii_id, oui, fib_id, is_del);
+  switch (rv)
     {
-      int rv;
-
-      rv = dhcp_proxy_set_vss(FIB_PROTOCOL_IP6, tbl_id, oui, fib_id, is_del);
-      switch (rv)
-        {
-        case 0:
-          return 0;
-
-        case VNET_API_ERROR_NO_SUCH_FIB:
-            return clib_error_return (0, "vss info (oui:%d, vpn-id:%d)  not found in table %d.",
-                                      oui, fib_id, tbl_id);
-
-        case VNET_API_ERROR_NO_SUCH_ENTRY:
-            return clib_error_return (0, "vss for table %d not found in pool.",
-                                      tbl_id);
-
-        default:
-          return clib_error_return (0, "BUG: rv %d", rv);
-        }
+    case 0:
+       return 0;
+    case VNET_API_ERROR_NO_SUCH_ENTRY:
+       return clib_error_return (0, "vss for table %d not found in pool.",
+                                 tbl_id);
+    default:
+       return clib_error_return (0, "BUG: rv %d", rv);
     }
-  else
-      return clib_error_return (0, "parse error`%U'",
-                                format_unformat_error, input);
-
 }
 
 VLIB_CLI_COMMAND (dhcpv6_proxy_vss_command, static) = {
   .path = "set dhcpv6 vss",
-  .short_help = "set dhcpv6 vss table <table-id> oui <oui> vpn-idx <vpn-idx>",
+  .short_help = "set dhcpv6 vss table <table-id> [oui <n> vpn-id <n> | vpn-ascii-id <text>]",
   .function = dhcpv6_vss_command_fn,
 };