LCOV - code coverage report
Current view: top level - plugins/dhcp - client.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 415 538 77.1 %
Date: 2023-10-26 01:39:38 Functions: 27 28 96.4 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2015 Cisco and/or its affiliates.
       3             :  * Licensed under the Apache License, Version 2.0 (the "License");
       4             :  * you may not use this file except in compliance with the License.
       5             :  * You may obtain a copy of the License at:
       6             :  *
       7             :  *     http://www.apache.org/licenses/LICENSE-2.0
       8             :  *
       9             :  * Unless required by applicable law or agreed to in writing, software
      10             :  * distributed under the License is distributed on an "AS IS" BASIS,
      11             :  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      12             :  * See the License for the specific language governing permissions and
      13             :  * limitations under the License.
      14             :  */
      15             : #include <vlib/vlib.h>
      16             : #include <vlibmemory/api.h>
      17             : #include <dhcp/client.h>
      18             : #include <dhcp/dhcp_proxy.h>
      19             : #include <vnet/fib/fib_table.h>
      20             : #include <vnet/qos/qos_types.h>
      21             : 
      22             : vlib_log_class_t dhcp_logger;
      23             : 
      24             : dhcp_client_main_t dhcp_client_main;
      25             : static vlib_node_registration_t dhcp_client_process_node;
      26             : 
      27             : #define DHCP_DBG(...)                           \
      28             :     vlib_log_debug (dhcp_logger, __VA_ARGS__)
      29             : 
      30             : #define DHCP_INFO(...)                          \
      31             :     vlib_log_notice (dhcp_logger, __VA_ARGS__)
      32             : 
      33             : #define foreach_dhcp_sent_packet_stat           \
      34             : _(DISCOVER, "DHCP discover packets sent")       \
      35             : _(OFFER, "DHCP offer packets sent")             \
      36             : _(REQUEST, "DHCP request packets sent")         \
      37             : _(ACK, "DHCP ack packets sent")
      38             : 
      39             : #define foreach_dhcp_error_counter                                      \
      40             : _(NOT_FOR_US, "DHCP packets for other hosts, dropped")                  \
      41             : _(NAK, "DHCP nak packets received")                                     \
      42             : _(NON_OFFER_DISCOVER, "DHCP non-offer packets in discover state")       \
      43             : _(ODDBALL, "DHCP non-ack, non-offer packets received")                  \
      44             : _(BOUND, "DHCP bind success")
      45             : 
      46             : typedef enum
      47             : {
      48             : #define _(sym,str) DHCP_STAT_##sym,
      49             :   foreach_dhcp_sent_packet_stat foreach_dhcp_error_counter
      50             : #undef _
      51             :     DHCP_STAT_UNKNOWN,
      52             :   DHCP_STAT_N_STAT,
      53             : } sample_error_t;
      54             : 
      55             : static char *dhcp_client_process_stat_strings[] = {
      56             : #define _(sym,string) string,
      57             :   foreach_dhcp_sent_packet_stat foreach_dhcp_error_counter
      58             : #undef _
      59             :     "DHCP unknown packets sent",
      60             : };
      61             : 
      62             : static u8 *
      63          33 : format_dhcp_client_state (u8 * s, va_list * va)
      64             : {
      65          33 :   dhcp_client_state_t state = va_arg (*va, dhcp_client_state_t);
      66          33 :   char *str = "BOGUS!";
      67             : 
      68          33 :   switch (state)
      69             :     {
      70             : #define _(a)                                    \
      71             :     case a:                                     \
      72             :       str = #a;                                 \
      73             :         break;
      74          33 :       foreach_dhcp_client_state;
      75             : #undef _
      76           0 :     default:
      77           0 :       break;
      78             :     }
      79             : 
      80          33 :   s = format (s, "%s", str);
      81          33 :   return s;
      82             : }
      83             : 
      84             : static u8 *
      85          33 : format_dhcp_client (u8 * s, va_list * va)
      86             : {
      87          33 :   dhcp_client_main_t *dcm = va_arg (*va, dhcp_client_main_t *);
      88          33 :   dhcp_client_t *c = va_arg (*va, dhcp_client_t *);
      89          33 :   int verbose = va_arg (*va, int);
      90             :   ip4_address_t *addr;
      91             : 
      92          33 :   s = format (s, "[%d] %U state %U installed %d", c - dcm->clients,
      93             :               format_vnet_sw_if_index_name, dcm->vnet_main, c->sw_if_index,
      94          33 :               format_dhcp_client_state, c->state, c->addresses_installed);
      95             : 
      96          33 :   if (0 != c->dscp)
      97           8 :     s = format (s, " dscp %d", c->dscp);
      98             : 
      99          33 :   if (c->installed.leased_address.as_u32)
     100             :     {
     101          13 :       s = format (s, " addr %U/%d gw %U server %U",
     102             :                   format_ip4_address, &c->installed.leased_address,
     103             :                   c->installed.subnet_mask_width,
     104             :                   format_ip4_address, &c->installed.router_address,
     105             :                   format_ip4_address, &c->installed.dhcp_server);
     106             : 
     107          13 :       vec_foreach (addr, c->domain_server_address)
     108           0 :         s = format (s, " dns %U", format_ip4_address, addr);
     109             :     }
     110             :   else
     111             :     {
     112          20 :       s = format (s, " no address");
     113             :     }
     114             : 
     115          33 :   if (verbose)
     116             :     {
     117             :       s =
     118          33 :         format (s,
     119             :                 "\n lease: lifetime:%d renewal-interval:%d expires:%.2f (now:%.2f)",
     120             :                 c->lease_lifetime, c->lease_renewal_interval,
     121             :                 c->lease_expires, vlib_time_now (dcm->vlib_main));
     122             :       s =
     123          33 :         format (s, "\n retry-count:%d, next-xmt:%.2f", c->retry_count,
     124             :                 c->next_transmit);
     125          33 :       s = format (s, "\n broadcast adjacency:%d", c->ai_bcast);
     126             :     }
     127          33 :   return s;
     128             : }
     129             : 
     130             : static void
     131           5 : dhcp_client_acquire_address (dhcp_client_main_t * dcm, dhcp_client_t * c)
     132             : {
     133             :   /*
     134             :    * Install any/all info gleaned from dhcp, right here
     135             :    */
     136           5 :   if (!c->addresses_installed)
     137             :     {
     138           5 :       ip4_add_del_interface_address (dcm->vlib_main, c->sw_if_index,
     139             :                                      (void *) &c->learned.leased_address,
     140             :                                      c->learned.subnet_mask_width,
     141             :                                      0 /*is_del */ );
     142           5 :       if (c->learned.router_address.as_u32)
     143             :         {
     144           5 :           fib_prefix_t all_0s = {
     145             :             .fp_len = 0,
     146             :             .fp_proto = FIB_PROTOCOL_IP4,
     147             :           };
     148           5 :           ip46_address_t nh = {
     149             :             .ip4 = c->learned.router_address,
     150             :           };
     151             : 
     152             :           /* *INDENT-OFF* */
     153           5 :           fib_table_entry_path_add (
     154             :               fib_table_get_index_for_sw_if_index (
     155             :                   FIB_PROTOCOL_IP4,
     156             :                   c->sw_if_index),
     157             :               &all_0s,
     158             :               FIB_SOURCE_DHCP,
     159             :               FIB_ENTRY_FLAG_NONE,
     160             :               DPO_PROTO_IP4,
     161             :               &nh, c->sw_if_index,
     162             :               ~0, 1, NULL,      // no label stack
     163             :               FIB_ROUTE_PATH_FLAG_NONE);
     164             :           /* *INDENT-ON* */
     165             :         }
     166             :     }
     167           5 :   clib_memcpy (&c->installed, &c->learned, sizeof (c->installed));
     168           5 :   c->addresses_installed = 1;
     169           5 : }
     170             : 
     171             : static void
     172          11 : dhcp_client_release_address (dhcp_client_main_t * dcm, dhcp_client_t * c)
     173             : {
     174             :   /*
     175             :    * Remove any/all info gleaned from dhcp, right here. Caller(s)
     176             :    * have not wiped out the info yet.
     177             :    */
     178          11 :   if (c->addresses_installed)
     179             :     {
     180           5 :       ip4_add_del_interface_address (dcm->vlib_main, c->sw_if_index,
     181             :                                      (void *) &c->installed.leased_address,
     182             :                                      c->installed.subnet_mask_width,
     183             :                                      1 /*is_del */ );
     184             : 
     185             :       /* Remove the default route */
     186           5 :       if (c->installed.router_address.as_u32)
     187             :         {
     188           5 :           fib_prefix_t all_0s = {
     189             :             .fp_len = 0,
     190             :             .fp_proto = FIB_PROTOCOL_IP4,
     191             :           };
     192           5 :           ip46_address_t nh = {
     193             :             .ip4 = c->installed.router_address,
     194             :           };
     195             : 
     196           5 :           fib_table_entry_path_remove (fib_table_get_index_for_sw_if_index
     197             :                                        (FIB_PROTOCOL_IP4, c->sw_if_index),
     198             :                                        &all_0s, FIB_SOURCE_DHCP,
     199             :                                        DPO_PROTO_IP4, &nh, c->sw_if_index, ~0,
     200             :                                        1, FIB_ROUTE_PATH_FLAG_NONE);
     201             :         }
     202             :     }
     203          11 :   clib_memset (&c->installed, 0, sizeof (c->installed));
     204          11 :   c->addresses_installed = 0;
     205          11 : }
     206             : 
     207             : static void
     208           4 : dhcp_client_proc_callback (uword * client_index)
     209             : {
     210           4 :   vlib_main_t *vm = vlib_get_main ();
     211           4 :   ASSERT (vlib_get_thread_index () == 0);
     212           4 :   vlib_process_signal_event (vm, dhcp_client_process_node.index,
     213             :                              EVENT_DHCP_CLIENT_WAKEUP, *client_index);
     214           4 : }
     215             : 
     216             : static void
     217           5 : dhcp_client_addr_callback (u32 * cindex)
     218             : {
     219           5 :   dhcp_client_main_t *dcm = &dhcp_client_main;
     220             :   dhcp_client_t *c;
     221             : 
     222           5 :   c = pool_elt_at_index (dcm->clients, *cindex);
     223             : 
     224             :   /* disable the feature */
     225           5 :   vnet_feature_enable_disable ("ip4-unicast",
     226             :                                "ip4-dhcp-client-detect",
     227             :                                c->sw_if_index, 0 /* disable */ , 0, 0);
     228           5 :   c->client_detect_feature_enabled = 0;
     229             : 
     230             :   /* add the address to the interface if they've changed since the last time */
     231           5 :   if (0 != clib_memcmp (&c->installed, &c->learned, sizeof (c->learned)))
     232             :     {
     233           5 :       dhcp_client_release_address (dcm, c);
     234           5 :       dhcp_client_acquire_address (dcm, c);
     235             :     }
     236             : 
     237             :   /*
     238             :    * Call the user's event callback to report DHCP information
     239             :    */
     240           5 :   if (c->event_callback)
     241           0 :     c->event_callback (c->client_index, c);
     242             : 
     243           5 :   DHCP_INFO ("update: %U", format_dhcp_client, dcm, c, 1 /* verbose */ );
     244           5 : }
     245             : 
     246             : static void
     247           6 : dhcp_client_reset (dhcp_client_main_t * dcm, dhcp_client_t * c)
     248             : {
     249           6 :   vlib_worker_thread_barrier_sync (dcm->vlib_main);
     250           6 :   if (c->client_detect_feature_enabled == 1)
     251             :     {
     252           3 :       vnet_feature_enable_disable ("ip4-unicast",
     253             :                                    "ip4-dhcp-client-detect",
     254             :                                    c->sw_if_index, 0, 0, 0);
     255           3 :       c->client_detect_feature_enabled = 0;
     256             :     }
     257           6 :   dhcp_client_release_address (dcm, c);
     258           6 :   vlib_worker_thread_barrier_release (dcm->vlib_main);
     259             : 
     260           6 :   clib_memset (&c->learned, 0, sizeof (c->installed));
     261           6 :   c->state = DHCP_DISCOVER;
     262           6 :   c->next_transmit = vlib_time_now (dcm->vlib_main);
     263           6 :   c->retry_count = 0;
     264           6 :   c->lease_renewal_interval = 0;
     265           6 :   vec_free (c->domain_server_address);
     266           6 : }
     267             : 
     268             : /*
     269             :  * dhcp_client_for_us - server-to-client callback.
     270             :  * Called from proxy_node.c:dhcp_proxy_to_client_input().
     271             :  * This function first decides that the packet in question is
     272             :  * actually for the dhcp client code in case we're also acting as
     273             :  * a dhcp proxy. Ay caramba, what a folly!
     274             :  */
     275             : int
     276          14 : dhcp_client_for_us (u32 bi, vlib_buffer_t * b,
     277             :                     ip4_header_t * ip,
     278             :                     udp_header_t * udp, dhcp_header_t * dhcp)
     279             : {
     280          14 :   dhcp_client_main_t *dcm = &dhcp_client_main;
     281          14 :   vlib_main_t *vm = vlib_get_main ();
     282             :   dhcp_client_t *c;
     283             :   uword *p;
     284          14 :   f64 now = vlib_time_now (vm);
     285          14 :   u8 dhcp_message_type = 0;
     286             :   dhcp_option_t *o;
     287             : 
     288             :   /*
     289             :    * Doing dhcp client on this interface?
     290             :    * Presumably we will always receive dhcp clnt for-us pkts on
     291             :    * the interface that's asking for an address.
     292             :    */
     293          14 :   p = hash_get (dcm->client_by_sw_if_index,
     294             :                 vnet_buffer (b)->sw_if_index[VLIB_RX]);
     295          14 :   if (p == 0)
     296           5 :     return 0;                   /* no */
     297             : 
     298           9 :   c = pool_elt_at_index (dcm->clients, p[0]);
     299             : 
     300             :   /* Mixing dhcp relay and dhcp proxy? DGMS... */
     301           9 :   if (c->state == DHCP_BOUND && c->retry_count == 0)
     302           0 :     return 0;
     303             : 
     304             :   /* Packet not for us? Turf it... */
     305           9 :   if (memcmp (dhcp->client_hardware_address, c->client_hardware_address,
     306             :               sizeof (c->client_hardware_address)))
     307             :     {
     308           0 :       vlib_node_increment_counter (vm, dhcp_client_process_node.index,
     309             :                                    DHCP_STAT_NOT_FOR_US, 1);
     310           0 :       return 0;
     311             :     }
     312             : 
     313             :   /* parse through the packet, learn what we can */
     314           9 :   if (dhcp->your_ip_address.as_u32)
     315           9 :     c->learned.leased_address.as_u32 = dhcp->your_ip_address.as_u32;
     316             : 
     317           9 :   c->learned.dhcp_server.as_u32 = dhcp->server_ip_address.as_u32;
     318             : 
     319           9 :   o = (dhcp_option_t *) dhcp->options;
     320             : 
     321          45 :   while (o->option != 0xFF /* end of options */  &&
     322          36 :          (u8 *) o < (b->data + b->current_data + b->current_length))
     323             :     {
     324          36 :       switch (o->option)
     325             :         {
     326           9 :         case 53:                /* dhcp message type */
     327           9 :           dhcp_message_type = o->data[0];
     328           9 :           break;
     329             : 
     330           6 :         case 51:                /* lease time */
     331             :           {
     332             :             u32 lease_time_in_seconds =
     333           6 :               clib_host_to_net_u32 (o->data_as_u32[0]);
     334             :             // for debug: lease_time_in_seconds = 20; /*$$$$*/
     335           6 :             c->lease_expires = now + (f64) lease_time_in_seconds;
     336           6 :             c->lease_lifetime = lease_time_in_seconds;
     337             :             /* Set a sensible default, in case we don't get opt 58 */
     338           6 :             c->lease_renewal_interval = lease_time_in_seconds / 2;
     339             :           }
     340           6 :           break;
     341             : 
     342           2 :         case 58:                /* lease renew time in seconds */
     343             :           {
     344             :             u32 lease_renew_time_in_seconds =
     345           2 :               clib_host_to_net_u32 (o->data_as_u32[0]);
     346           2 :             c->lease_renewal_interval = lease_renew_time_in_seconds;
     347             :           }
     348           2 :           break;
     349             : 
     350           9 :         case 54:                /* dhcp server address */
     351           9 :           c->learned.dhcp_server.as_u32 = o->data_as_u32[0];
     352           9 :           break;
     353             : 
     354           5 :         case 1:         /* subnet mask */
     355             :           {
     356           5 :             u32 subnet_mask = clib_host_to_net_u32 (o->data_as_u32[0]);
     357           5 :             c->learned.subnet_mask_width = count_set_bits (subnet_mask);
     358             :           }
     359           5 :           break;
     360           5 :         case 3:         /* router address */
     361             :           {
     362           5 :             u32 router_address = o->data_as_u32[0];
     363           5 :             c->learned.router_address.as_u32 = router_address;
     364             :           }
     365           5 :           break;
     366           0 :         case 6:         /* domain server address */
     367             :           {
     368           0 :             vec_free (c->domain_server_address);
     369           0 :             vec_validate (c->domain_server_address,
     370             :                           o->length / sizeof (ip4_address_t) - 1);
     371           0 :             clib_memcpy (c->domain_server_address, o->data, o->length);
     372             :           }
     373           0 :           break;
     374           0 :         case 12:                /* hostname */
     375             :           {
     376             :             /* Replace the existing hostname if necessary */
     377           0 :             vec_free (c->hostname);
     378           0 :             vec_validate (c->hostname, o->length - 1);
     379           0 :             clib_memcpy (c->hostname, o->data, o->length);
     380             :           }
     381           0 :           break;
     382             : 
     383             :           /* $$$$ Your message in this space, parse more options */
     384           0 :         default:
     385           0 :           break;
     386             :         }
     387             : 
     388          36 :       o = (dhcp_option_t *) (((uword) o) + (o->length + 2));
     389             :     }
     390             : 
     391           9 :   switch (c->state)
     392             :     {
     393           4 :     case DHCP_DISCOVER:
     394           4 :       if (dhcp_message_type != DHCP_PACKET_OFFER)
     395             :         {
     396           0 :           vlib_node_increment_counter (vm, dhcp_client_process_node.index,
     397             :                                        DHCP_STAT_NON_OFFER_DISCOVER, 1);
     398           0 :           c->next_transmit = now + 5.0;
     399           0 :           break;
     400             :         }
     401             : 
     402             :       /* Received an offer, go send a request */
     403           4 :       c->state = DHCP_REQUEST;
     404           4 :       c->retry_count = 0;
     405           4 :       c->next_transmit = 0;  /* send right now... */
     406             :       /* Poke the client process, which will send the request */
     407           4 :       uword client_id = c - dcm->clients;
     408           4 :       vl_api_rpc_call_main_thread (dhcp_client_proc_callback,
     409             :                                    (u8 *) & client_id, sizeof (uword));
     410           4 :       break;
     411             : 
     412           5 :     case DHCP_BOUND:
     413             :     case DHCP_REQUEST:
     414           5 :       if (dhcp_message_type == DHCP_PACKET_NAK)
     415             :         {
     416           0 :           vlib_node_increment_counter (vm, dhcp_client_process_node.index,
     417             :                                        DHCP_STAT_NAK, 1);
     418             :           /* Probably never happens in bound state, but anyhow...
     419             :              Wipe out any memory of the address we had... */
     420           0 :           dhcp_client_reset (dcm, c);
     421           0 :           break;
     422             :         }
     423             : 
     424           5 :       if (dhcp_message_type != DHCP_PACKET_ACK &&
     425             :           dhcp_message_type != DHCP_PACKET_OFFER)
     426             :         {
     427           0 :           vlib_node_increment_counter (vm, dhcp_client_process_node.index,
     428             :                                        DHCP_STAT_NON_OFFER_DISCOVER, 1);
     429           0 :           clib_warning ("sw_if_index %d state %U message type %d",
     430             :                         c->sw_if_index, format_dhcp_client_state,
     431             :                         c->state, dhcp_message_type);
     432           0 :           c->next_transmit = now + 5.0;
     433           0 :           break;
     434             :         }
     435             :       /* OK, we own the address (etc), add to the routing table(s) */
     436             :       {
     437             :         /* Send the index over to the main thread, where it can retrieve
     438             :          * the original client */
     439           5 :         u32 cindex = c - dcm->clients;
     440           5 :         vl_api_force_rpc_call_main_thread (dhcp_client_addr_callback,
     441             :                                            (u8 *) & cindex, sizeof (u32));
     442             :       }
     443             : 
     444           5 :       c->state = DHCP_BOUND;
     445           5 :       c->retry_count = 0;
     446           5 :       c->next_transmit = now + (f64) c->lease_renewal_interval;
     447           5 :       c->lease_expires = now + (f64) c->lease_lifetime;
     448           5 :       vlib_node_increment_counter (vm, dhcp_client_process_node.index,
     449             :                                    DHCP_STAT_BOUND, 1);
     450           5 :       break;
     451             : 
     452           0 :     default:
     453           0 :       clib_warning ("client %d bogus state %d", c - dcm->clients, c->state);
     454           0 :       break;
     455             :     }
     456             : 
     457             :   /* return 1 so the call disposes of this packet */
     458           9 :   return 1;
     459             : }
     460             : 
     461             : static void
     462          14 : send_dhcp_pkt (dhcp_client_main_t * dcm, dhcp_client_t * c,
     463             :                dhcp_packet_type_t type, int is_broadcast)
     464             : {
     465          14 :   vlib_main_t *vm = dcm->vlib_main;
     466          14 :   vnet_main_t *vnm = dcm->vnet_main;
     467          14 :   vnet_hw_interface_t *hw = vnet_get_sup_hw_interface (vnm, c->sw_if_index);
     468             :   vnet_sw_interface_t *sup_sw
     469          14 :     = vnet_get_sup_sw_interface (vnm, c->sw_if_index);
     470          14 :   vnet_sw_interface_t *sw = vnet_get_sw_interface (vnm, c->sw_if_index);
     471             :   vlib_buffer_t *b;
     472             :   u32 bi;
     473             :   ip4_header_t *ip;
     474             :   udp_header_t *udp;
     475             :   dhcp_header_t *dhcp;
     476             :   u32 *to_next;
     477             :   vlib_frame_t *f;
     478             :   dhcp_option_t *o;
     479             :   u16 udp_length, ip_length;
     480             :   u32 counter_index, node_index;
     481             : 
     482          14 :   DHCP_INFO ("send: type:%U bcast:%d %U",
     483             :              format_dhcp_packet_type, type,
     484             :              is_broadcast, format_dhcp_client, dcm, c, 1 /* verbose */ );
     485             : 
     486             :   /* Interface(s) down? */
     487          14 :   if ((hw->flags & VNET_HW_INTERFACE_FLAG_LINK_UP) == 0)
     488           1 :     return;
     489          13 :   if ((sup_sw->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) == 0)
     490           0 :     return;
     491          13 :   if ((sw->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) == 0)
     492           0 :     return;
     493             : 
     494          13 :   if (vlib_buffer_alloc (vm, &bi, 1) != 1)
     495             :     {
     496           0 :       clib_warning ("buffer allocation failure");
     497           0 :       c->next_transmit = 0;
     498           0 :       return;
     499             :     }
     500             : 
     501             :   /* Build a dhcpv4 pkt from whole cloth */
     502          13 :   b = vlib_get_buffer (vm, bi);
     503             : 
     504          13 :   ASSERT (b->current_data == 0);
     505             : 
     506          13 :   vnet_buffer (b)->sw_if_index[VLIB_RX] = c->sw_if_index;
     507          13 :   b->flags |= VNET_BUFFER_F_LOCALLY_ORIGINATED;
     508             : 
     509          13 :   if (is_broadcast)
     510             :     {
     511          10 :       node_index = ip4_rewrite_node.index;
     512          10 :       vnet_buffer (b)->ip.adj_index[VLIB_TX] = c->ai_bcast;
     513             :     }
     514             :   else
     515           3 :     node_index = dcm->ip4_lookup_node_index;
     516             : 
     517             :   /* Enqueue the packet right now */
     518          13 :   f = vlib_get_frame_to_node (vm, node_index);
     519          13 :   to_next = vlib_frame_vector_args (f);
     520          13 :   to_next[0] = bi;
     521          13 :   f->n_vectors = 1;
     522          13 :   vlib_put_frame_to_node (vm, node_index, f);
     523             : 
     524             :   /* build the headers */
     525          13 :   ip = vlib_buffer_get_current (b);
     526          13 :   udp = (udp_header_t *) (ip + 1);
     527          13 :   dhcp = (dhcp_header_t *) (udp + 1);
     528             : 
     529             :   /* $$$ optimize, maybe */
     530          13 :   clib_memset (ip, 0, sizeof (*ip) + sizeof (*udp) + sizeof (*dhcp));
     531             : 
     532          13 :   ip->ip_version_and_header_length = 0x45;
     533          13 :   ip->ttl = 128;
     534          13 :   ip->protocol = IP_PROTOCOL_UDP;
     535             : 
     536          13 :   ip->tos = c->dscp;
     537             : 
     538          13 :   if (ip->tos)
     539             :     {
     540             :       /*
     541             :        * Setup the buffer's QoS settings so any QoS marker on the egress
     542             :        * interface, that might set VLAN CoS bits, based on this DSCP setting
     543             :        */
     544           3 :       vnet_buffer2 (b)->qos.source = QOS_SOURCE_IP;
     545           3 :       vnet_buffer2 (b)->qos.bits = ip->tos;
     546           3 :       b->flags |= VNET_BUFFER_F_QOS_DATA_VALID;
     547             :     }
     548             : 
     549          13 :   if (is_broadcast)
     550             :     {
     551             :       /* src = 0.0.0.0, dst = 255.255.255.255 */
     552          10 :       ip->dst_address.as_u32 = ~0;
     553             :     }
     554             :   else
     555             :     {
     556             :       /* Renewing an active lease, plain old ip4 src/dst */
     557           3 :       ip->src_address.as_u32 = c->learned.leased_address.as_u32;
     558           3 :       ip->dst_address.as_u32 = c->learned.dhcp_server.as_u32;
     559             :     }
     560             : 
     561          13 :   udp->src_port = clib_host_to_net_u16 (UDP_DST_PORT_dhcp_to_client);
     562          13 :   udp->dst_port = clib_host_to_net_u16 (UDP_DST_PORT_dhcp_to_server);
     563             : 
     564             :   /* Send the interface MAC address */
     565          13 :   clib_memcpy (dhcp->client_hardware_address,
     566             :                vnet_sw_interface_get_hw_address (vnm, c->sw_if_index), 6);
     567             : 
     568             :   /* And remember it for rx-packet-for-us checking */
     569          13 :   clib_memcpy (c->client_hardware_address, dhcp->client_hardware_address,
     570             :                sizeof (c->client_hardware_address));
     571             : 
     572             :   /* Lease renewal, set up client_ip_address */
     573          13 :   if (is_broadcast == 0)
     574           3 :     dhcp->client_ip_address.as_u32 = c->learned.leased_address.as_u32;
     575             : 
     576          13 :   dhcp->opcode = 1;          /* request, all we send */
     577          13 :   dhcp->hardware_type = 1;   /* ethernet */
     578          13 :   dhcp->hardware_address_length = 6;
     579          13 :   dhcp->transaction_identifier = c->transaction_id;
     580          13 :   dhcp->flags =
     581          13 :     clib_host_to_net_u16 (is_broadcast && c->set_broadcast_flag ?
     582             :                           DHCP_FLAG_BROADCAST : 0);
     583          13 :   dhcp->magic_cookie.as_u32 = DHCP_MAGIC;
     584             : 
     585          13 :   o = (dhcp_option_t *) dhcp->options;
     586             : 
     587             :   /* Send option 53, the DHCP message type */
     588          13 :   o->option = DHCP_PACKET_OPTION_MSG_TYPE;
     589          13 :   o->length = 1;
     590          13 :   o->data[0] = type;
     591          13 :   o = (dhcp_option_t *) (((uword) o) + (o->length + 2));
     592             : 
     593             :   /* Send option 57, max msg length */
     594             :   if (0 /* not needed, apparently */ )
     595             :     {
     596             :       o->option = 57;
     597             :       o->length = 2;
     598             :       {
     599             :         u16 *o2 = (u16 *) o->data;
     600             :         *o2 = clib_host_to_net_u16 (1152);
     601             :         o = (dhcp_option_t *) (((uword) o) + (o->length + 2));
     602             :       }
     603             :     }
     604             : 
     605             :   /*
     606             :    * If server ip address is available with non-zero value,
     607             :    * option 54 (DHCP Server Identifier) is sent.
     608             :    */
     609          13 :   if (c->learned.dhcp_server.as_u32)
     610             :     {
     611           7 :       o->option = 54;
     612           7 :       o->length = 4;
     613           7 :       clib_memcpy (o->data, &c->learned.dhcp_server.as_u32, 4);
     614           7 :       o = (dhcp_option_t *) (((uword) o) + (o->length + 2));
     615             :     }
     616             : 
     617             :   /* send option 50, requested IP address */
     618          13 :   if (c->learned.leased_address.as_u32)
     619             :     {
     620           7 :       o->option = 50;
     621           7 :       o->length = 4;
     622           7 :       clib_memcpy (o->data, &c->learned.leased_address.as_u32, 4);
     623           7 :       o = (dhcp_option_t *) (((uword) o) + (o->length + 2));
     624             :     }
     625             : 
     626             :   /* send option 12, host name */
     627          13 :   if (vec_len (c->hostname))
     628             :     {
     629          13 :       o->option = 12;
     630          13 :       o->length = vec_len (c->hostname);
     631          13 :       clib_memcpy (o->data, c->hostname, vec_len (c->hostname));
     632          13 :       o = (dhcp_option_t *) (((uword) o) + (o->length + 2));
     633             :     }
     634             : 
     635             :   /* send option 61, client_id */
     636          13 :   if (vec_len (c->client_identifier))
     637             :     {
     638           2 :       o->option = 61;
     639           2 :       o->length = vec_len (c->client_identifier) + 1;
     640             :       /* Set type to zero, apparently some dhcp servers care */
     641           2 :       o->data[0] = 0;
     642           2 :       clib_memcpy (o->data + 1, c->client_identifier,
     643             :                    vec_len (c->client_identifier));
     644           2 :       o = (dhcp_option_t *) (((uword) o) + (o->length + 2));
     645             :     }
     646             : 
     647             :   /* $$ maybe send the client s/w version if anyone cares */
     648             : 
     649             :   /*
     650             :    * send option 55, parameter request list
     651             :    * The current list - see below, matches the Linux dhcp client's list
     652             :    * Any specific dhcp server config and/or dhcp server may or may
     653             :    * not yield specific options.
     654             :    */
     655          13 :   o->option = 55;
     656          13 :   o->length = vec_len (c->option_55_data);
     657          13 :   clib_memcpy (o->data, c->option_55_data, vec_len (c->option_55_data));
     658          13 :   o = (dhcp_option_t *) (((uword) o) + (o->length + 2));
     659             : 
     660             :   /* End of list */
     661          13 :   o->option = 0xff;
     662          13 :   o->length = 0;
     663          13 :   o++;
     664             : 
     665          13 :   b->current_length = ((u8 *) o) - b->data;
     666             : 
     667             :   /* fix ip length, checksum and udp length */
     668          13 :   ip_length = vlib_buffer_length_in_chain (vm, b);
     669             : 
     670          13 :   ip->length = clib_host_to_net_u16 (ip_length);
     671          13 :   ip->checksum = ip4_header_checksum (ip);
     672             : 
     673          13 :   udp_length = ip_length - (sizeof (*ip));
     674          13 :   udp->length = clib_host_to_net_u16 (udp_length);
     675             : 
     676          13 :   switch (type)
     677             :     {
     678             : #define _(a,b) case DHCP_PACKET_##a: {counter_index = DHCP_STAT_##a; break;}
     679          13 :       foreach_dhcp_sent_packet_stat
     680             : #undef _
     681           0 :     default:
     682           0 :       counter_index = DHCP_STAT_UNKNOWN;
     683           0 :       break;
     684             :     }
     685             : 
     686          13 :   vlib_node_increment_counter (vm, dhcp_client_process_node.index,
     687             :                                counter_index, 1);
     688             : }
     689             : 
     690             : static int
     691           7 : dhcp_discover_state (dhcp_client_main_t * dcm, dhcp_client_t * c, f64 now)
     692             : {
     693             :   /*
     694             :    * State machine "DISCOVER" state. Send a dhcp discover packet,
     695             :    * eventually back off the retry rate.
     696             :    */
     697             :   /*
     698             :    * In order to accept any OFFER, whether broadcasted or unicasted, we
     699             :    * need to configure the dhcp-client-detect feature as an input feature
     700             :    * so the DHCP OFFER is sent to the ip4-local node. Without this a
     701             :    * broadcasted OFFER hits the 255.255.255.255/32 address and a unicast
     702             :    * hits 0.0.0.0/0 both of which default to drop and the latter may forward
     703             :    * of box - not what we want. Nor to we want to change these route for
     704             :    * all interfaces in this table
     705             :    */
     706           7 :   if (c->client_detect_feature_enabled == 0)
     707             :     {
     708           6 :       vlib_worker_thread_barrier_sync (dcm->vlib_main);
     709           6 :       vnet_feature_enable_disable ("ip4-unicast",
     710             :                                    "ip4-dhcp-client-detect",
     711             :                                    c->sw_if_index, 1 /* enable */ , 0, 0);
     712           6 :       vlib_worker_thread_barrier_release (dcm->vlib_main);
     713           6 :       c->client_detect_feature_enabled = 1;
     714             :     }
     715             : 
     716           7 :   send_dhcp_pkt (dcm, c, DHCP_PACKET_DISCOVER, 1 /* is_broadcast */ );
     717             : 
     718           7 :   c->retry_count++;
     719           7 :   if (c->retry_count > 10)
     720           0 :     c->next_transmit = now + 5.0;
     721             :   else
     722           7 :     c->next_transmit = now + 1.0;
     723           7 :   return 0;
     724             : }
     725             : 
     726             : static int
     727           4 : dhcp_request_state (dhcp_client_main_t * dcm, dhcp_client_t * c, f64 now)
     728             : {
     729             :   /*
     730             :    * State machine "REQUEST" state. Send a dhcp request packet,
     731             :    * eventually drop back to the discover state.
     732             :    */
     733           4 :   DHCP_INFO ("enter request: %U", format_dhcp_client, dcm, c,
     734             :              1 /*verbose */ );
     735             : 
     736           4 :   send_dhcp_pkt (dcm, c, DHCP_PACKET_REQUEST, 1 /* is_broadcast */ );
     737             : 
     738           4 :   c->retry_count++;
     739           4 :   if (c->retry_count > 7 /* lucky you */ )
     740             :     {
     741           0 :       c->state = DHCP_DISCOVER;
     742           0 :       c->next_transmit = now;
     743           0 :       c->retry_count = 0;
     744           0 :       return 1;
     745             :     }
     746           4 :   c->next_transmit = now + 1.0;
     747           4 :   return 0;
     748             : }
     749             : 
     750             : static int
     751           4 : dhcp_bound_state (dhcp_client_main_t * dcm, dhcp_client_t * c, f64 now)
     752             : {
     753             :   /*
     754             :    * We disable the client detect feature when we bind a
     755             :    * DHCP address. Turn it back on again on first renew attempt.
     756             :    * Otherwise, if the DHCP server replies we'll never see it.
     757             :    */
     758           4 :   if (c->client_detect_feature_enabled == 0)
     759             :     {
     760           2 :       vlib_worker_thread_barrier_sync (dcm->vlib_main);
     761           2 :       vnet_feature_enable_disable ("ip4-unicast",
     762             :                                    "ip4-dhcp-client-detect",
     763             :                                    c->sw_if_index, 1 /* enable */ , 0, 0);
     764           2 :       vlib_worker_thread_barrier_release (dcm->vlib_main);
     765           2 :       c->client_detect_feature_enabled = 1;
     766             :     }
     767             : 
     768             :   /*
     769             :    * State machine "BOUND" state. Send a dhcp request packet to renew
     770             :    * the lease.
     771             :    * Eventually, when the lease expires, forget the dhcp data
     772             :    * and go back to the stone age.
     773             :    */
     774           4 :   if (now > c->lease_expires)
     775             :     {
     776           1 :       DHCP_INFO ("lease expired: %U", format_dhcp_client, dcm, c,
     777             :                  1 /*verbose */ );
     778             : 
     779             :       /* reset all data for the client. do not send any more messages
     780             :        * since the objects to do so have been lost */
     781           1 :       dhcp_client_reset (dcm, c);
     782           1 :       return 1;
     783             :     }
     784             : 
     785           3 :   DHCP_INFO ("enter bound: %U", format_dhcp_client, dcm, c, 1 /* verbose */ );
     786           3 :   send_dhcp_pkt (dcm, c, DHCP_PACKET_REQUEST, 0 /* is_broadcast */ );
     787             : 
     788           3 :   c->retry_count++;
     789           3 :   if (c->retry_count > 10)
     790           0 :     c->next_transmit = now + 5.0;
     791             :   else
     792           3 :     c->next_transmit = now + 1.0;
     793             : 
     794           3 :   return 0;
     795             : }
     796             : 
     797             : static f64
     798          25 : dhcp_client_sm (f64 now, f64 timeout, uword pool_index)
     799             : {
     800          25 :   dhcp_client_main_t *dcm = &dhcp_client_main;
     801             :   dhcp_client_t *c;
     802             : 
     803             :   /* deleted, pooched, yadda yadda yadda */
     804          25 :   if (pool_is_free_index (dcm->clients, pool_index))
     805           0 :     return timeout;
     806             : 
     807          25 :   c = pool_elt_at_index (dcm->clients, pool_index);
     808             : 
     809             :   /* Time for us to do something with this client? */
     810          25 :   if (now < c->next_transmit)
     811          11 :     return c->next_transmit;
     812             : 
     813          14 :   DHCP_INFO ("sm active session %d", c - dcm->clients);
     814             : 
     815          15 : again:
     816          15 :   switch (c->state)
     817             :     {
     818           7 :     case DHCP_DISCOVER: /* send a discover */
     819           7 :       if (dhcp_discover_state (dcm, c, now))
     820           0 :         goto again;
     821           7 :       break;
     822             : 
     823           4 :     case DHCP_REQUEST:          /* send a request */
     824           4 :       if (dhcp_request_state (dcm, c, now))
     825           0 :         goto again;
     826           4 :       break;
     827             : 
     828           4 :     case DHCP_BOUND:            /* bound, renew needed? */
     829           4 :       if (dhcp_bound_state (dcm, c, now))
     830           1 :         goto again;
     831           3 :       break;
     832             : 
     833           0 :     default:
     834           0 :       clib_warning ("dhcp client %d bogus state %d",
     835             :                     c - dcm->clients, c->state);
     836           0 :       break;
     837             :     }
     838             : 
     839          14 :   return c->next_transmit;
     840             : }
     841             : 
     842             : static uword
     843         575 : dhcp_client_process (vlib_main_t * vm,
     844             :                      vlib_node_runtime_t * rt, vlib_frame_t * f)
     845             : {
     846         575 :   f64 timeout = 1000.0;
     847             :   f64 next_expire_time, this_next_expire_time;
     848             :   f64 now;
     849             :   uword event_type;
     850         575 :   uword *event_data = 0;
     851         575 :   dhcp_client_main_t *dcm = &dhcp_client_main;
     852             :   dhcp_client_t *c;
     853             :   int i;
     854             : 
     855             :   while (1)
     856             :     {
     857         596 :       vlib_process_wait_for_event_or_clock (vm, timeout);
     858             : 
     859          21 :       event_type = vlib_process_get_events (vm, &event_data);
     860             : 
     861          21 :       now = vlib_time_now (vm);
     862             : 
     863          21 :       switch (event_type)
     864             :         {
     865           9 :         case EVENT_DHCP_CLIENT_WAKEUP:
     866          18 :           for (i = 0; i < vec_len (event_data); i++)
     867           9 :             (void) dhcp_client_sm (now, timeout, event_data[i]);
     868             :           /* FALLTHROUGH */
     869             : 
     870             :         case ~0:
     871          21 :           if (pool_elts (dcm->clients))
     872             :             {
     873             :               /* *INDENT-OFF* */
     874          16 :               next_expire_time = 1e70;
     875          32 :               pool_foreach (c, dcm->clients)
     876             :                {
     877          16 :                 this_next_expire_time = dhcp_client_sm
     878          16 :                   (now, timeout, (uword) (c - dcm->clients));
     879          16 :                 next_expire_time = this_next_expire_time < next_expire_time ?
     880          16 :                   this_next_expire_time : next_expire_time;
     881             :               }
     882          16 :               if (next_expire_time > now)
     883          16 :                 timeout = next_expire_time - now;
     884             :               else
     885             :                 {
     886           0 :                   clib_warning ("BUG");
     887           0 :                   timeout = 1.13;
     888             :                 }
     889             :               /* *INDENT-ON* */
     890             :             }
     891             :           else
     892           5 :             timeout = 1000.0;
     893          21 :           break;
     894             :         }
     895             : 
     896          21 :       vec_reset_length (event_data);
     897             :     }
     898             : 
     899             :   /* NOTREACHED */
     900             :   return 0;
     901             : }
     902             : 
     903             : /* *INDENT-OFF* */
     904      153836 : VLIB_REGISTER_NODE (dhcp_client_process_node,static) = {
     905             :     .function = dhcp_client_process,
     906             :     .type = VLIB_NODE_TYPE_PROCESS,
     907             :     .name = "dhcp-client-process",
     908             :     .process_log2_n_stack_bytes = 16,
     909             :     .n_errors = ARRAY_LEN(dhcp_client_process_stat_strings),
     910             :     .error_strings = dhcp_client_process_stat_strings,
     911             : };
     912             : /* *INDENT-ON* */
     913             : 
     914             : static clib_error_t *
     915           1 : show_dhcp_client_command_fn (vlib_main_t * vm,
     916             :                              unformat_input_t * input,
     917             :                              vlib_cli_command_t * cmd)
     918             : {
     919           1 :   dhcp_client_main_t *dcm = &dhcp_client_main;
     920             :   dhcp_client_t *c;
     921           1 :   int verbose = 0;
     922           1 :   u32 sw_if_index = ~0;
     923             :   uword *p;
     924             : 
     925           3 :   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
     926             :     {
     927           2 :       if (unformat (input, "intfc %U",
     928             :                     unformat_vnet_sw_interface, dcm->vnet_main, &sw_if_index))
     929             :         ;
     930           1 :       else if (unformat (input, "verbose"))
     931           1 :         verbose = 1;
     932             :       else
     933           0 :         break;
     934             :     }
     935             : 
     936           1 :   if (sw_if_index != ~0)
     937             :     {
     938           1 :       p = hash_get (dcm->client_by_sw_if_index, sw_if_index);
     939           1 :       if (p == 0)
     940           0 :         return clib_error_return (0, "dhcp client not configured");
     941           1 :       c = pool_elt_at_index (dcm->clients, p[0]);
     942           1 :       vlib_cli_output (vm, "%U", format_dhcp_client, dcm, c, verbose);
     943           1 :       return 0;
     944             :     }
     945             : 
     946             :   /* *INDENT-OFF* */
     947           0 :   pool_foreach (c, dcm->clients)
     948             :    {
     949           0 :     vlib_cli_output (vm, "%U",
     950             :                      format_dhcp_client, dcm,
     951             :                      c, verbose);
     952             :   }
     953             :   /* *INDENT-ON* */
     954             : 
     955           0 :   return 0;
     956             : }
     957             : 
     958             : /* *INDENT-OFF* */
     959      235753 : VLIB_CLI_COMMAND (show_dhcp_client_command, static) = {
     960             :   .path = "show dhcp client",
     961             :   .short_help = "show dhcp client [intfc <intfc>][verbose]",
     962             :   .function = show_dhcp_client_command_fn,
     963             : };
     964             : /* *INDENT-ON* */
     965             : 
     966             : 
     967             : int
     968          10 : dhcp_client_add_del (dhcp_client_add_del_args_t * a)
     969             : {
     970          10 :   dhcp_client_main_t *dcm = &dhcp_client_main;
     971          10 :   vlib_main_t *vm = dcm->vlib_main;
     972             :   dhcp_client_t *c;
     973             :   uword *p;
     974             : 
     975          10 :   p = hash_get (dcm->client_by_sw_if_index, a->sw_if_index);
     976             : 
     977          10 :   if ((p && a->is_add) || (!p && a->is_add == 0))
     978           0 :     return VNET_API_ERROR_INVALID_VALUE;
     979             : 
     980          10 :   if (a->is_add)
     981             :     {
     982           5 :       dhcp_maybe_register_udp_ports (DHCP_PORT_REG_CLIENT);
     983           5 :       pool_get (dcm->clients, c);
     984           5 :       clib_memset (c, 0, sizeof (*c));
     985           5 :       c->state = DHCP_DISCOVER;
     986           5 :       c->sw_if_index = a->sw_if_index;
     987           5 :       c->client_index = a->client_index;
     988           5 :       c->pid = a->pid;
     989           5 :       c->event_callback = a->event_callback;
     990           5 :       c->option_55_data = a->option_55_data;
     991           5 :       c->hostname = a->hostname;
     992           5 :       c->client_identifier = a->client_identifier;
     993           5 :       c->set_broadcast_flag = a->set_broadcast_flag;
     994           5 :       c->dscp = a->dscp;
     995           5 :       c->ai_bcast = adj_nbr_add_or_lock (FIB_PROTOCOL_IP4,
     996             :                                          VNET_LINK_IP4,
     997           5 :                                          &ADJ_BCAST_ADDR, c->sw_if_index);
     998             : 
     999             :       do
    1000             :         {
    1001           5 :           c->transaction_id = random_u32 (&dcm->seed);
    1002             :         }
    1003           5 :       while (c->transaction_id == 0);
    1004             : 
    1005           5 :       hash_set (dcm->client_by_sw_if_index, a->sw_if_index, c - dcm->clients);
    1006             : 
    1007           5 :       vlib_process_signal_event (vm, dhcp_client_process_node.index,
    1008           5 :                                  EVENT_DHCP_CLIENT_WAKEUP, c - dcm->clients);
    1009             : 
    1010           5 :       DHCP_INFO ("create: %U", format_dhcp_client, dcm, c, 1 /* verbose */ );
    1011             :     }
    1012             :   else
    1013             :     {
    1014           5 :       c = pool_elt_at_index (dcm->clients, p[0]);
    1015             : 
    1016           5 :       dhcp_client_reset (dcm, c);
    1017             : 
    1018           5 :       adj_unlock (c->ai_bcast);
    1019             : 
    1020           5 :       vec_free (c->domain_server_address);
    1021           5 :       vec_free (c->option_55_data);
    1022           5 :       vec_free (c->hostname);
    1023           5 :       vec_free (c->client_identifier);
    1024           5 :       hash_unset (dcm->client_by_sw_if_index, c->sw_if_index);
    1025           5 :       pool_put (dcm->clients, c);
    1026             :     }
    1027          10 :   return 0;
    1028             : }
    1029             : 
    1030             : int
    1031          10 : dhcp_client_config (u32 is_add,
    1032             :                     u32 client_index,
    1033             :                     vlib_main_t * vm,
    1034             :                     u32 sw_if_index,
    1035             :                     u8 * hostname,
    1036             :                     u8 * client_id,
    1037             :                     dhcp_event_cb_t event_callback,
    1038             :                     u8 set_broadcast_flag, ip_dscp_t dscp, u32 pid)
    1039             : {
    1040          10 :   dhcp_client_add_del_args_t _a, *a = &_a;
    1041             :   int rv;
    1042             : 
    1043          10 :   clib_memset (a, 0, sizeof (*a));
    1044          10 :   a->is_add = is_add;
    1045          10 :   a->sw_if_index = sw_if_index;
    1046          10 :   a->client_index = client_index;
    1047          10 :   a->pid = pid;
    1048          10 :   a->event_callback = event_callback;
    1049          10 :   a->set_broadcast_flag = set_broadcast_flag;
    1050          10 :   a->dscp = dscp;
    1051          10 :   vec_validate (a->hostname, strlen ((char *) hostname) - 1);
    1052          10 :   strncpy ((char *) a->hostname, (char *) hostname, vec_len (a->hostname));
    1053          10 :   vec_validate (a->client_identifier, strlen ((char *) client_id) - 1);
    1054          10 :   strncpy ((char *) a->client_identifier, (char *) client_id,
    1055          10 :            vec_len (a->client_identifier));
    1056             : 
    1057             :   /*
    1058             :    * Option 55 request list. These data precisely match
    1059             :    * the Ubuntu dhcp client. YMMV.
    1060             :    */
    1061             : 
    1062             :   /* Subnet Mask */
    1063          10 :   vec_add1 (a->option_55_data, 1);
    1064             :   /* Broadcast address */
    1065          10 :   vec_add1 (a->option_55_data, 28);
    1066             :   /* time offset */
    1067          10 :   vec_add1 (a->option_55_data, 2);
    1068             :   /* Router */
    1069          10 :   vec_add1 (a->option_55_data, 3);
    1070             :   /* Domain Name */
    1071          10 :   vec_add1 (a->option_55_data, 15);
    1072             :   /* DNS */
    1073          10 :   vec_add1 (a->option_55_data, 6);
    1074             :   /* Domain search */
    1075          10 :   vec_add1 (a->option_55_data, 119);
    1076             :   /* Host name */
    1077          10 :   vec_add1 (a->option_55_data, 12);
    1078             :   /* NetBIOS name server */
    1079          10 :   vec_add1 (a->option_55_data, 44);
    1080             :   /* NetBIOS Scope */
    1081          10 :   vec_add1 (a->option_55_data, 47);
    1082             :   /* MTU */
    1083          10 :   vec_add1 (a->option_55_data, 26);
    1084             :   /* Classless static route */
    1085          10 :   vec_add1 (a->option_55_data, 121);
    1086             :   /* NTP servers */
    1087          10 :   vec_add1 (a->option_55_data, 42);
    1088             : 
    1089          10 :   rv = dhcp_client_add_del (a);
    1090             : 
    1091          10 :   switch (rv)
    1092             :     {
    1093          10 :     case 0:
    1094          10 :       break;
    1095             : 
    1096           0 :     case VNET_API_ERROR_INVALID_VALUE:
    1097             : 
    1098           0 :       vec_free (a->hostname);
    1099           0 :       vec_free (a->client_identifier);
    1100           0 :       vec_free (a->option_55_data);
    1101             : 
    1102           0 :       if (is_add)
    1103           0 :         DHCP_INFO ("dhcp client already enabled on intf_idx %d", sw_if_index);
    1104             :       else
    1105           0 :         DHCP_INFO ("not enabled on on intf_idx %d", sw_if_index);
    1106           0 :       break;
    1107             : 
    1108           0 :     default:
    1109           0 :       DHCP_INFO ("dhcp_client_add_del returned %d", rv);
    1110             :     }
    1111             : 
    1112          10 :   return rv;
    1113             : }
    1114             : 
    1115             : void
    1116          10 : dhcp_client_walk (dhcp_client_walk_cb_t cb, void *ctx)
    1117             : {
    1118          10 :   dhcp_client_main_t *dcm = &dhcp_client_main;
    1119             :   dhcp_client_t *c;
    1120             : 
    1121             :   /* *INDENT-OFF* */
    1122          14 :   pool_foreach (c, dcm->clients)
    1123             :    {
    1124           4 :     if (!cb(c, ctx))
    1125           0 :       break;
    1126             :   }
    1127             :   /* *INDENT-ON* */
    1128             : 
    1129          10 : }
    1130             : 
    1131             : static clib_error_t *
    1132           0 : dhcp_client_set_command_fn (vlib_main_t * vm,
    1133             :                             unformat_input_t * input,
    1134             :                             vlib_cli_command_t * cmd)
    1135             : {
    1136             : 
    1137           0 :   dhcp_client_main_t *dcm = &dhcp_client_main;
    1138             :   u32 sw_if_index;
    1139           0 :   u8 *hostname = 0;
    1140           0 :   u8 sw_if_index_set = 0;
    1141           0 :   u8 set_broadcast_flag = 1;
    1142           0 :   int is_add = 1;
    1143           0 :   dhcp_client_add_del_args_t _a, *a = &_a;
    1144             :   int rv;
    1145             : 
    1146           0 :   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
    1147             :     {
    1148           0 :       if (unformat (input, "intfc %U",
    1149             :                     unformat_vnet_sw_interface, dcm->vnet_main, &sw_if_index))
    1150           0 :         sw_if_index_set = 1;
    1151           0 :       else if (unformat (input, "hostname %v", &hostname))
    1152             :         ;
    1153           0 :       else if (unformat (input, "del"))
    1154           0 :         is_add = 0;
    1155           0 :       else if (unformat (input, "broadcast", &set_broadcast_flag))
    1156           0 :         is_add = 0;
    1157             :       else
    1158           0 :         break;
    1159             :     }
    1160             : 
    1161           0 :   if (sw_if_index_set == 0)
    1162           0 :     return clib_error_return (0, "interface not specified");
    1163             : 
    1164           0 :   clib_memset (a, 0, sizeof (*a));
    1165           0 :   a->is_add = is_add;
    1166           0 :   a->sw_if_index = sw_if_index;
    1167           0 :   a->hostname = hostname;
    1168           0 :   a->client_identifier = format (0, "vpp 1.1%c", 0);
    1169           0 :   a->set_broadcast_flag = set_broadcast_flag;
    1170             : 
    1171             :   /*
    1172             :    * Option 55 request list. These data precisely match
    1173             :    * the Ubuntu dhcp client. YMMV.
    1174             :    */
    1175             : 
    1176             :   /* Subnet Mask */
    1177           0 :   vec_add1 (a->option_55_data, 1);
    1178             :   /* Broadcast address */
    1179           0 :   vec_add1 (a->option_55_data, 28);
    1180             :   /* time offset */
    1181           0 :   vec_add1 (a->option_55_data, 2);
    1182             :   /* Router */
    1183           0 :   vec_add1 (a->option_55_data, 3);
    1184             :   /* Domain Name */
    1185           0 :   vec_add1 (a->option_55_data, 15);
    1186             :   /* DNS */
    1187           0 :   vec_add1 (a->option_55_data, 6);
    1188             :   /* Domain search */
    1189           0 :   vec_add1 (a->option_55_data, 119);
    1190             :   /* Host name */
    1191           0 :   vec_add1 (a->option_55_data, 12);
    1192             :   /* NetBIOS name server */
    1193           0 :   vec_add1 (a->option_55_data, 44);
    1194             :   /* NetBIOS Scope */
    1195           0 :   vec_add1 (a->option_55_data, 47);
    1196             :   /* MTU */
    1197           0 :   vec_add1 (a->option_55_data, 26);
    1198             :   /* Classless static route */
    1199           0 :   vec_add1 (a->option_55_data, 121);
    1200             :   /* NTP servers */
    1201           0 :   vec_add1 (a->option_55_data, 42);
    1202             : 
    1203           0 :   rv = dhcp_client_add_del (a);
    1204             : 
    1205           0 :   switch (rv)
    1206             :     {
    1207           0 :     case 0:
    1208           0 :       break;
    1209             : 
    1210           0 :     case VNET_API_ERROR_INVALID_VALUE:
    1211             : 
    1212           0 :       vec_free (a->hostname);
    1213           0 :       vec_free (a->client_identifier);
    1214           0 :       vec_free (a->option_55_data);
    1215           0 :       if (is_add)
    1216           0 :         return clib_error_return (0, "dhcp client already enabled on %U",
    1217             :                                   format_vnet_sw_if_index_name,
    1218             :                                   dcm->vnet_main, sw_if_index);
    1219             :       else
    1220           0 :         return clib_error_return (0, "dhcp client not enabled on %U",
    1221             :                                   format_vnet_sw_if_index_name,
    1222             :                                   dcm->vnet_main, sw_if_index);
    1223             :       break;
    1224             : 
    1225           0 :     default:
    1226           0 :       vlib_cli_output (vm, "dhcp_client_add_del returned %d", rv);
    1227             :     }
    1228             : 
    1229           0 :   return 0;
    1230             : }
    1231             : 
    1232             : /* *INDENT-OFF* */
    1233      235753 : VLIB_CLI_COMMAND (dhcp_client_set_command, static) = {
    1234             :   .path = "set dhcp client",
    1235             :   .short_help = "set dhcp client [del] intfc <interface> [hostname <name>]",
    1236             :   .function = dhcp_client_set_command_fn,
    1237             : };
    1238             : /* *INDENT-ON* */
    1239             : 
    1240             : static clib_error_t *
    1241         575 : dhcp_client_init (vlib_main_t * vm)
    1242             : {
    1243         575 :   dhcp_client_main_t *dcm = &dhcp_client_main;
    1244             :   vlib_node_t *ip4_lookup_node;
    1245             : 
    1246         575 :   ip4_lookup_node = vlib_get_node_by_name (vm, (u8 *) "ip4-lookup");
    1247             : 
    1248             :   /* Should never happen... */
    1249         575 :   if (ip4_lookup_node == 0)
    1250           0 :     return clib_error_return (0, "ip4-lookup node not found");
    1251             : 
    1252         575 :   dcm->ip4_lookup_node_index = ip4_lookup_node->index;
    1253         575 :   dcm->vlib_main = vm;
    1254         575 :   dcm->vnet_main = vnet_get_main ();
    1255         575 :   dcm->seed = (u32) clib_cpu_time_now ();
    1256             : 
    1257         575 :   dhcp_logger = vlib_log_register_class ("dhcp", "client");
    1258         575 :   DHCP_DBG ("plugin initialized");
    1259             : 
    1260         575 :   return 0;
    1261             : }
    1262             : 
    1263        1151 : VLIB_INIT_FUNCTION (dhcp_client_init);
    1264             : 
    1265             : /*
    1266             :  * fd.io coding-style-patch-verification: ON
    1267             :  *
    1268             :  * Local Variables:
    1269             :  * eval: (c-set-style "gnu")
    1270             :  * End:
    1271             :  */

Generated by: LCOV version 1.14