Add Support of DHCP VSS Type 0 where VPN-ID is ASCII
[vpp.git] / src / vnet / dhcp / dhcp4_proxy_node.c
index 88a9924..cd52be8 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,19 +220,28 @@ 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);
+          fl = vlib_buffer_get_free_list (vm, vlib_buffer_get_free_list_index (b0));
           // start write at (option*)o, some packets have padding
           if (((u8 *)o - (u8 *)b0->data + VPP_DHCP_OPTION82_SIZE) > fl->n_data_bytes)
-          {
+            {
               next0 = DHCP_PROXY_TO_SERVER_INPUT_NEXT_DROP;
               pkts_too_big++;
               goto do_trace;
-          }
+           }
 
           if ((o->option == 0xFF)  && ((u8 *)o <= end))
-          {  
+            {  
               vnet_main_t *vnm = vnet_get_main();   
               u16 old_l0, new_l0;
               ip4_address_t _ia0, * ia0 = &_ia0;
@@ -255,65 +264,53 @@ dhcp_proxy_to_server_input (vlib_main_t * vm,
               ia0 = ip4_interface_first_address(&ip4_main, sw_if_index, 0);
                   
               if (ia0 == 0)
-              {
+                {
                   error0 = DHCP_PROXY_ERROR_NO_INTERFACE_ADDRESS;
                   next0 = DHCP_PROXY_TO_SERVER_INPUT_NEXT_DROP;
                   pkts_no_interface_address++;
                   goto do_trace;
-              }
+                }
 
               /* Add option 82 */
               o->option = 82;   /* option 82 */
               o->length = 12;   /* 12 octets to follow */
               o->data[0] = 1;   /* suboption 1, circuit ID (=FIB id) */
               o->data[1] = 4;   /* length of suboption */
-              o->data[2] = (original_sw_if_index >> 24) & 0xFF;
-              o->data[3] = (original_sw_if_index >> 16) & 0xFF;
-              o->data[4] = (original_sw_if_index >> 8)  & 0xFF;
-              o->data[5] = (original_sw_if_index >> 0)  & 0xFF;
+             u32 *o_ifid = (u32 *) &o->data[2];
+             *o_ifid = clib_host_to_net_u32 (original_sw_if_index);
               o->data[6] = 5; /* suboption 5 (client RX intfc address) */
               o->data[7] = 4; /* length 4 */
-              o->data[8] = ia0->as_u8[0];
-              o->data[9] = ia0->as_u8[1];
-              o->data[10] = ia0->as_u8[2];
-              o->data[11] = ia0->as_u8[3];
+             u32 *o_addr = (u32 *) &o->data[8];
+             *o_addr = ia0->as_u32;
               o->data[12] = 0xFF;
 
               vss = dhcp_get_vss_info (dpm, fib_index, FIB_PROTOCOL_IP4);
-              if (NULL != vss)
-              {
-                  u32 opt82_fib_id=0, opt82_oui=0;
-
-                  opt82_oui =  vss->oui;
-                  opt82_fib_id =  vss->fib_id;
-
-                  o->data[12] = 151; /* vss suboption */
-                  if (255 == opt82_fib_id) {
-                      o->data[13] = 1;   /* length */
-                      o->data[14] = 255;   /* vss option type */
-                      o->data[15] = 152; /* vss control suboption */
-                      o->data[16] = 0;   /* length */
-                      /* and a new "end-of-options" option (0xff) */
-                      o->data[17] = 0xFF;
-                      o->length += 5;
-                  } else {
-                      o->data[13] = 8;   /* length */
-                      o->data[14] = 1;   /* vss option type */
-                      o->data[15] = (opt82_oui >> 16) & 0xff;
-                      o->data[16] = (opt82_oui >> 8) & 0xff;
-                      o->data[17] = (opt82_oui ) & 0xff;
-                      o->data[18] = (opt82_fib_id >> 24) & 0xff;
-                      o->data[19] = (opt82_fib_id >> 16) & 0xff;
-                      o->data[20] = (opt82_fib_id >> 8) & 0xff;
-                      o->data[21] = (opt82_fib_id) & 0xff;
-                      o->data[22] = 152; /* vss control suboption */
-                      o->data[23] = 0;   /* length */
-                          
-                      /* and a new "end-of-options" option (0xff) */
-                      o->data[24] = 0xFF;
-                      o->length += 12;
-                  }
-              }
+              if (vss)
+                {
+                 u32 id_len;                   /* length of VPN ID */
+
+                 if (vss->vss_type == VSS_TYPE_VPN_ID)
+                   {
+                     id_len = sizeof (vss->vpn_id);    /* vpn_id is 7 bytes */
+                     memcpy (&o->data[15], vss->vpn_id, id_len);
+                   }
+                 else if (vss->vss_type == VSS_TYPE_ASCII)
+                   {
+                     id_len = vec_len (vss->vpn_ascii_id);
+                     memcpy (&o->data[15], vss->vpn_ascii_id, id_len);
+                   }
+                 else  /* must be VSS_TYPE_DEFAULT, no VPN ID */
+                   id_len = 0; 
+
+                  o->data[12] = 151;           /* vss suboption */
+                 o->data[13] = id_len + 1;     /* length: vss_type + id_len */
+                 o->data[14] = vss->vss_type;  /* vss option type */
+                 o->data[15 + id_len] = 152;   /* vss control suboption */
+                 o->data[16 + id_len] = 0;     /* length */
+                 o->data[17 + id_len] = 0xFF;  /* "end-of-options" (0xFF) */
+                 /* 5 bytes for suboption headers 151+len, 152+len and 0xFF */
+                 o->length += id_len + 5; 
+                }
 
               len = o->length + 3;
               b0->current_length += len;
@@ -332,14 +329,75 @@ dhcp_proxy_to_server_input (vlib_main_t * vm,
               new_l0 = clib_net_to_host_u16 (u0->length);
               new_l0 += len;
               u0->length = clib_host_to_net_u16 (new_l0);
-          } else {
+            } 
+         else 
+           {
               vlib_node_increment_counter 
                   (vm, dhcp_proxy_to_server_node.index,
                    DHCP_PROXY_ERROR_OPTION_82_ERROR, 1);
-          }
+           }
           
           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 +408,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 +500,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 +611,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);
@@ -705,18 +775,18 @@ dhcp4_proxy_set_server (ip46_address_t *addr,
     return VNET_API_ERROR_INVALID_SRC_ADDRESS;
 
   rx_fib_index = fib_table_find_or_create_and_lock(FIB_PROTOCOL_IP4,
-                                                   rx_table_id);
+                                                   rx_table_id,
+                                                   FIB_SOURCE_DHCP);
 
   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,
                                          FIB_SOURCE_DHCP);
-          fib_table_unlock (rx_fib_index, FIB_PROTOCOL_IP4);
+          fib_table_unlock (rx_fib_index, FIB_PROTOCOL_IP4, FIB_SOURCE_DHCP);
       }
     }
   else
@@ -728,12 +798,11 @@ dhcp4_proxy_set_server (ip46_address_t *addr,
           fib_table_entry_special_add(rx_fib_index,
                                       &all_1s,
                                       FIB_SOURCE_DHCP,
-                                      FIB_ENTRY_FLAG_LOCAL,
-                                      ADJ_INDEX_INVALID);
-          fib_table_lock (rx_fib_index, FIB_PROTOCOL_IP4);
+                                      FIB_ENTRY_FLAG_LOCAL);
+          fib_table_lock (rx_fib_index, FIB_PROTOCOL_IP4, FIB_SOURCE_DHCP);
       }
   }
-  fib_table_unlock (rx_fib_index, FIB_PROTOCOL_IP4);
+  fib_table_unlock (rx_fib_index, FIB_PROTOCOL_IP4, FIB_SOURCE_DHCP);
 
   return (rc);
 }
@@ -809,29 +878,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;
@@ -864,54 +939,47 @@ dhcp_option_82_vss_fn (vlib_main_t * vm,
                         unformat_input_t * input,
                         vlib_cli_command_t * cmd)
 {
-  int is_del = 0, got_new_vpn_id=0;
-  u32 oui=0, fib_id=0, tbl_id=~0;
+  u8 is_del = 0, vss_type = VSS_TYPE_DEFAULT;
+  u32 oui = 0, fib_id = 0, tbl_id = ~0;
+  u8 *vpn_ascii_id = 0;
 
   while (unformat_check_input(input) != UNFORMAT_END_OF_INPUT) 
     {
-
-      if (unformat(input, "delete") || unformat(input, "del"))
-          is_del = 1;    
+      if (unformat (input, "table %d", &tbl_id))
+         ;
       else if (unformat (input, "oui %d", &oui))
-          got_new_vpn_id = 1;
+         vss_type = VSS_TYPE_VPN_ID;
       else if (unformat (input, "vpn-id %d", &fib_id))
-          got_new_vpn_id = 1;
-      else if (unformat (input, "table %d", &tbl_id))
-          got_new_vpn_id = 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
-          break;
-  }
+        break;
+    }
+
   if (tbl_id == ~0)
       return clib_error_return (0, "no table ID specified.");
   
-  if (is_del || got_new_vpn_id)
+  int rv = dhcp_proxy_set_vss (FIB_PROTOCOL_IP4, tbl_id, vss_type, 
+                              vpn_ascii_id, oui, fib_id, is_del);
+  switch (rv)
     {
-      int rv;
-      rv = dhcp_proxy_set_vss(FIB_PROTOCOL_IP4, 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, "option 82 vss(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, "option 82 vss for table %d not found in 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, "option 82 vss for table %d not found in 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 (dhcp_proxy_vss_command,static) = {
   .path = "set dhcp option-82 vss",
-  .short_help = "set dhcp option-82 vss [del] table <table id> oui <oui> vpn-id <vpn-id>",
+  .short_help = "set dhcp option-82 vss [del] table <table id> [oui <n> vpn-id <n> | vpn-ascii-id <text>]",
   .function = dhcp_option_82_vss_fn,
 };