Fix VPP-1528 get the same IP address from DHCP server for two VPP DHCP clients
[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",
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       vec_add1 (s, '\n');
841     }
842   else
843     {
844       s = format (s, "no address\n");
845     }
846
847   if (verbose)
848     {
849       s = format (s, "retry count %d, next xmt %.2f",
850                   c->retry_count, c->next_transmit);
851     }
852   return s;
853 }
854
855 static clib_error_t *
856 show_dhcp_client_command_fn (vlib_main_t * vm,
857                              unformat_input_t * input,
858                              vlib_cli_command_t * cmd)
859 {
860   dhcp_client_main_t *dcm = &dhcp_client_main;
861   dhcp_client_t *c;
862   int verbose = 0;
863   u32 sw_if_index = ~0;
864   uword *p;
865
866   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
867     {
868       if (unformat (input, "intfc %U",
869                     unformat_vnet_sw_interface, dcm->vnet_main, &sw_if_index))
870         ;
871       else if (unformat (input, "verbose"))
872         verbose = 1;
873       else
874         break;
875     }
876
877   if (sw_if_index != ~0)
878     {
879       p = hash_get (dcm->client_by_sw_if_index, sw_if_index);
880       if (p == 0)
881         return clib_error_return (0, "dhcp client not configured");
882       c = pool_elt_at_index (dcm->clients, p[0]);
883       vlib_cli_output (vm, "%U", format_dhcp_client, dcm, c, verbose);
884       return 0;
885     }
886
887   /* *INDENT-OFF* */
888   pool_foreach (c, dcm->clients,
889   ({
890     vlib_cli_output (vm, "%U",
891                      format_dhcp_client, dcm,
892                      c, verbose);
893   }));
894   /* *INDENT-ON* */
895
896   return 0;
897 }
898
899 /* *INDENT-OFF* */
900 VLIB_CLI_COMMAND (show_dhcp_client_command, static) = {
901   .path = "show dhcp client",
902   .short_help = "show dhcp client [intfc <intfc>][verbose]",
903   .function = show_dhcp_client_command_fn,
904 };
905 /* *INDENT-ON* */
906
907
908 int
909 dhcp_client_add_del (dhcp_client_add_del_args_t * a)
910 {
911   dhcp_client_main_t *dcm = &dhcp_client_main;
912   vlib_main_t *vm = dcm->vlib_main;
913   dhcp_client_t *c;
914   uword *p;
915   fib_prefix_t all_0s = {
916     .fp_len = 0,
917     .fp_addr.ip4.as_u32 = 0x0,
918     .fp_proto = FIB_PROTOCOL_IP4,
919   };
920
921   p = hash_get (dcm->client_by_sw_if_index, a->sw_if_index);
922
923   if ((p && a->is_add) || (!p && a->is_add == 0))
924     return VNET_API_ERROR_INVALID_VALUE;
925
926   if (a->is_add)
927     {
928       dhcp_maybe_register_udp_ports (DHCP_PORT_REG_CLIENT);
929       pool_get (dcm->clients, c);
930       clib_memset (c, 0, sizeof (*c));
931       c->state = DHCP_DISCOVER;
932       c->sw_if_index = a->sw_if_index;
933       c->client_index = a->client_index;
934       c->pid = a->pid;
935       c->event_callback = a->event_callback;
936       c->option_55_data = a->option_55_data;
937       c->hostname = a->hostname;
938       c->client_identifier = a->client_identifier;
939       c->set_broadcast_flag = a->set_broadcast_flag;
940       do
941         {
942           c->transaction_id = random_u32 (&dcm->seed);
943         }
944       while (c->transaction_id == 0);
945       set_l2_rewrite (dcm, c);
946       hash_set (dcm->client_by_sw_if_index, a->sw_if_index, c - dcm->clients);
947
948       /*
949        * In order to accept any OFFER, whether broadcasted or unicasted, we
950        * need to configure the dhcp-client-detect feature as an input feature
951        * so the DHCP OFFER is sent to the ip4-local node. Without this a
952        * broadcasted OFFER hits the 255.255.255.255/32 address and a unicast
953        * hits 0.0.0.0/0 both of which default to drop and the latter may forward
954        * of box - not what we want. Nor to we want to change these route for
955        * all interfaces in this table
956        */
957       vnet_feature_enable_disable ("ip4-unicast",
958                                    "ip4-dhcp-client-detect",
959                                    c->sw_if_index, 1 /* enable */ , 0, 0);
960       c->client_detect_feature_enabled = 1;
961
962       vlib_process_signal_event (vm, dhcp_client_process_node.index,
963                                  EVENT_DHCP_CLIENT_WAKEUP, c - dcm->clients);
964     }
965   else
966     {
967       c = pool_elt_at_index (dcm->clients, p[0]);
968
969       if (c->router_address.as_u32)
970         {
971           ip46_address_t nh = {
972             .ip4 = c->router_address,
973           };
974
975           fib_table_entry_path_remove (fib_table_get_index_for_sw_if_index
976                                        (FIB_PROTOCOL_IP4, c->sw_if_index),
977                                        &all_0s, FIB_SOURCE_DHCP,
978                                        DPO_PROTO_IP4, &nh, c->sw_if_index, ~0,
979                                        1, FIB_ROUTE_PATH_FLAG_NONE);
980         }
981       dhcp_client_release_address (dcm, c);
982
983       vec_free (c->domain_server_address);
984       vec_free (c->option_55_data);
985       vec_free (c->hostname);
986       vec_free (c->client_identifier);
987       vec_free (c->l2_rewrite);
988       hash_unset (dcm->client_by_sw_if_index, c->sw_if_index);
989       pool_put (dcm->clients, c);
990     }
991   return 0;
992 }
993
994 int
995 dhcp_client_config (u32 is_add,
996                     u32 client_index,
997                     vlib_main_t * vm,
998                     u32 sw_if_index,
999                     u8 * hostname,
1000                     u8 * client_id,
1001                     dhcp_event_cb_t event_callback,
1002                     u8 set_broadcast_flag, u32 pid)
1003 {
1004   dhcp_client_add_del_args_t _a, *a = &_a;
1005   int rv;
1006
1007   clib_memset (a, 0, sizeof (*a));
1008   a->is_add = is_add;
1009   a->sw_if_index = sw_if_index;
1010   a->client_index = client_index;
1011   a->pid = pid;
1012   a->event_callback = event_callback;
1013   a->set_broadcast_flag = set_broadcast_flag;
1014   vec_validate (a->hostname, strlen ((char *) hostname) - 1);
1015   strncpy ((char *) a->hostname, (char *) hostname, vec_len (a->hostname));
1016   vec_validate (a->client_identifier, strlen ((char *) client_id) - 1);
1017   strncpy ((char *) a->client_identifier, (char *) client_id,
1018            vec_len (a->client_identifier));
1019
1020   /*
1021    * Option 55 request list. These data precisely match
1022    * the Ubuntu dhcp client. YMMV.
1023    */
1024
1025   /* Subnet Mask */
1026   vec_add1 (a->option_55_data, 1);
1027   /* Broadcast address */
1028   vec_add1 (a->option_55_data, 28);
1029   /* time offset */
1030   vec_add1 (a->option_55_data, 2);
1031   /* Router */
1032   vec_add1 (a->option_55_data, 3);
1033   /* Domain Name */
1034   vec_add1 (a->option_55_data, 15);
1035   /* DNS */
1036   vec_add1 (a->option_55_data, 6);
1037   /* Domain search */
1038   vec_add1 (a->option_55_data, 119);
1039   /* Host name */
1040   vec_add1 (a->option_55_data, 12);
1041   /* NetBIOS name server */
1042   vec_add1 (a->option_55_data, 44);
1043   /* NetBIOS Scope */
1044   vec_add1 (a->option_55_data, 47);
1045   /* MTU */
1046   vec_add1 (a->option_55_data, 26);
1047   /* Classless static route */
1048   vec_add1 (a->option_55_data, 121);
1049   /* NTP servers */
1050   vec_add1 (a->option_55_data, 42);
1051
1052   rv = dhcp_client_add_del (a);
1053
1054   switch (rv)
1055     {
1056     case 0:
1057       break;
1058
1059     case VNET_API_ERROR_INVALID_VALUE:
1060
1061       vec_free (a->hostname);
1062       vec_free (a->client_identifier);
1063       vec_free (a->option_55_data);
1064
1065       if (is_add)
1066         clib_warning ("dhcp client already enabled on intf_idx %d",
1067                       sw_if_index);
1068       else
1069         clib_warning ("dhcp client not enabled on on intf_idx %d",
1070                       sw_if_index);
1071       break;
1072
1073     default:
1074       clib_warning ("dhcp_client_add_del returned %d", rv);
1075     }
1076
1077   return rv;
1078 }
1079
1080 void
1081 dhcp_client_walk (dhcp_client_walk_cb_t cb, void *ctx)
1082 {
1083   dhcp_client_main_t *dcm = &dhcp_client_main;
1084   dhcp_client_t *c;
1085
1086   /* *INDENT-OFF* */
1087   pool_foreach (c, dcm->clients,
1088   ({
1089     if (!cb(c, ctx))
1090       break;
1091   }));
1092   /* *INDENT-ON* */
1093
1094 }
1095
1096 static clib_error_t *
1097 dhcp_client_set_command_fn (vlib_main_t * vm,
1098                             unformat_input_t * input,
1099                             vlib_cli_command_t * cmd)
1100 {
1101
1102   dhcp_client_main_t *dcm = &dhcp_client_main;
1103   u32 sw_if_index;
1104   u8 *hostname = 0;
1105   u8 sw_if_index_set = 0;
1106   u8 set_broadcast_flag = 1;
1107   int is_add = 1;
1108   dhcp_client_add_del_args_t _a, *a = &_a;
1109   int rv;
1110
1111   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1112     {
1113       if (unformat (input, "intfc %U",
1114                     unformat_vnet_sw_interface, dcm->vnet_main, &sw_if_index))
1115         sw_if_index_set = 1;
1116       else if (unformat (input, "hostname %v", &hostname))
1117         ;
1118       else if (unformat (input, "del"))
1119         is_add = 0;
1120       else if (unformat (input, "broadcast", &set_broadcast_flag))
1121         is_add = 0;
1122       else
1123         break;
1124     }
1125
1126   if (sw_if_index_set == 0)
1127     return clib_error_return (0, "interface not specified");
1128
1129   clib_memset (a, 0, sizeof (*a));
1130   a->is_add = is_add;
1131   a->sw_if_index = sw_if_index;
1132   a->hostname = hostname;
1133   a->client_identifier = format (0, "vpe 1.0%c", 0);
1134   a->set_broadcast_flag = set_broadcast_flag;
1135
1136   /*
1137    * Option 55 request list. These data precisely match
1138    * the Ubuntu dhcp client. YMMV.
1139    */
1140
1141   /* Subnet Mask */
1142   vec_add1 (a->option_55_data, 1);
1143   /* Broadcast address */
1144   vec_add1 (a->option_55_data, 28);
1145   /* time offset */
1146   vec_add1 (a->option_55_data, 2);
1147   /* Router */
1148   vec_add1 (a->option_55_data, 3);
1149   /* Domain Name */
1150   vec_add1 (a->option_55_data, 15);
1151   /* DNS */
1152   vec_add1 (a->option_55_data, 6);
1153   /* Domain search */
1154   vec_add1 (a->option_55_data, 119);
1155   /* Host name */
1156   vec_add1 (a->option_55_data, 12);
1157   /* NetBIOS name server */
1158   vec_add1 (a->option_55_data, 44);
1159   /* NetBIOS Scope */
1160   vec_add1 (a->option_55_data, 47);
1161   /* MTU */
1162   vec_add1 (a->option_55_data, 26);
1163   /* Classless static route */
1164   vec_add1 (a->option_55_data, 121);
1165   /* NTP servers */
1166   vec_add1 (a->option_55_data, 42);
1167
1168   rv = dhcp_client_add_del (a);
1169
1170   switch (rv)
1171     {
1172     case 0:
1173       break;
1174
1175     case VNET_API_ERROR_INVALID_VALUE:
1176
1177       vec_free (a->hostname);
1178       vec_free (a->client_identifier);
1179       vec_free (a->option_55_data);
1180       if (is_add)
1181         return clib_error_return (0, "dhcp client already enabled on %U",
1182                                   format_vnet_sw_if_index_name,
1183                                   dcm->vnet_main, sw_if_index);
1184       else
1185         return clib_error_return (0, "dhcp client not enabled on %U",
1186                                   format_vnet_sw_if_index_name,
1187                                   dcm->vnet_main, sw_if_index);
1188       break;
1189
1190     default:
1191       vlib_cli_output (vm, "dhcp_client_add_del returned %d", rv);
1192     }
1193
1194   return 0;
1195 }
1196
1197 /* *INDENT-OFF* */
1198 VLIB_CLI_COMMAND (dhcp_client_set_command, static) = {
1199   .path = "set dhcp client",
1200   .short_help = "set dhcp client [del] intfc <interface> [hostname <name>]",
1201   .function = dhcp_client_set_command_fn,
1202 };
1203 /* *INDENT-ON* */
1204
1205 static clib_error_t *
1206 dhcp_client_init (vlib_main_t * vm)
1207 {
1208   dhcp_client_main_t *dcm = &dhcp_client_main;
1209
1210   dcm->vlib_main = vm;
1211   dcm->vnet_main = vnet_get_main ();
1212   dcm->seed = (u32) clib_cpu_time_now ();
1213   return 0;
1214 }
1215
1216 VLIB_INIT_FUNCTION (dhcp_client_init);
1217
1218 /*
1219  * fd.io coding-style-patch-verification: ON
1220  *
1221  * Local Variables:
1222  * eval: (c-set-style "gnu")
1223  * End:
1224  */