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