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