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