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