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