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