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