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