DHCP client option 61 "client_id"
[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   /* send option 61, client_id */
418   if (vec_len (c->client_identifier))
419     {
420       o->option = 61;
421       o->length = vec_len (c->client_identifier);
422       clib_memcpy (o->data, c->client_identifier,
423                    vec_len (c->client_identifier));
424       o = (dhcp_option_t *) (((uword) o) + (o->length + 2));
425     }
426
427   /* $$ maybe send the client s/w version if anyone cares */
428
429   /* 
430    * send option 55, parameter request list
431    * The current list - see below, matches the Linux dhcp client's list
432    * Any specific dhcp server config and/or dhcp server may or may
433    * not yield specific options.
434    */
435   o->option = 55;
436   o->length = vec_len (c->option_55_data);
437   clib_memcpy (o->data, c->option_55_data, vec_len(c->option_55_data));
438   o = (dhcp_option_t *) (((uword) o) + (o->length + 2));
439
440   /* End of list */
441   o->option = 0xff;
442   o->length = 0;
443   o++;
444   
445   b->current_length = ((u8 *)o) - b->data;
446
447   /* fix ip length, checksum and udp length */
448   ip_length = vlib_buffer_length_in_chain (vm, b);
449   if (is_broadcast)
450       ip_length -= vec_len (c->l2_rewrite);
451
452   ip->length = clib_host_to_net_u16(ip_length);
453   ip->checksum = ip4_header_checksum(ip);
454
455   udp_length = ip_length - (sizeof (*ip));
456   udp->length = clib_host_to_net_u16 (udp_length);
457 }
458
459 static int 
460 dhcp_discover_state (dhcp_client_main_t * dcm, dhcp_client_t * c, f64 now)
461 {
462   /*
463    * State machine "DISCOVER" state. Send a dhcp discover packet,
464    * eventually back off the retry rate.
465    */
466   send_dhcp_pkt (dcm, c, DHCP_PACKET_DISCOVER, 1 /* is_broadcast */);
467
468   c->retry_count++;
469   if (c->retry_count > 10)
470     c->next_transmit = now + 5.0;
471   else
472     c->next_transmit = now + 1.0;
473   return 0;
474 }
475                                 
476 static int 
477 dhcp_request_state (dhcp_client_main_t * dcm, dhcp_client_t * c, f64 now)
478 {                              
479   /*
480    * State machine "REQUEST" state. Send a dhcp request packet,
481    * eventually drop back to the discover state.
482    */
483   send_dhcp_pkt (dcm, c, DHCP_PACKET_REQUEST, 1 /* is_broadcast */);
484
485   c->retry_count++;
486   if (c->retry_count > 7 /* lucky you */)
487     {
488       c->state = DHCP_DISCOVER;
489       c->next_transmit = now;
490       c->retry_count = 0;
491       return 1;
492     }
493   c->next_transmit = now + 1.0;
494   return 0;
495 }
496
497 static int 
498 dhcp_bound_state (dhcp_client_main_t * dcm, dhcp_client_t * c, f64 now)
499 {
500   /*
501    * State machine "BOUND" state. Send a dhcp request packet,
502    * eventually, when the lease expires, forget the dhcp data
503    * and go back to the stone age.
504    */
505   send_dhcp_pkt (dcm, c, DHCP_PACKET_REQUEST, 0 /* is_broadcast */);
506   
507   c->retry_count++;
508   if (c->retry_count > 10)
509     c->next_transmit = now + 5.0;
510   else
511     c->next_transmit = now + 1.0;
512   
513   if (now > c->lease_expires)
514     {
515       if (c->router_address.as_u32)
516         {
517           fib_prefix_t all_0s =
518           {
519               .fp_len = 0,
520               .fp_addr.ip4.as_u32 = 0x0,
521               .fp_proto = FIB_PROTOCOL_IP4,
522           };
523           ip46_address_t nh = {
524               .ip4 = c->router_address,
525           };
526
527           fib_table_entry_path_remove(fib_table_get_index_for_sw_if_index(
528                                           FIB_PROTOCOL_IP4,
529                                           c->sw_if_index),
530                                       &all_0s,
531                                       FIB_SOURCE_DHCP,
532                                       FIB_PROTOCOL_IP4,
533                                       &nh,
534                                       c->sw_if_index,
535                                       ~0,
536                                       1,
537                                       FIB_ROUTE_PATH_FLAG_NONE);
538         }
539
540       dhcp_client_release_address (dcm, c);
541       c->state = DHCP_DISCOVER;
542       c->next_transmit = now;
543       c->retry_count = 0;
544       /* Wipe out any memory of the address we had... */
545       c->leased_address.as_u32 = 0;
546       c->subnet_mask_width = 0;
547       c->router_address.as_u32 = 0;
548       c->lease_renewal_interval = 0;
549       c->dhcp_server.as_u32 = 0;
550       return 1;
551     }
552   return 0;
553 }
554
555 static f64 dhcp_client_sm (f64 now, f64 timeout, uword pool_index)
556 {
557   dhcp_client_main_t * dcm = &dhcp_client_main;
558   dhcp_client_t * c;
559
560   /* deleted, pooched, yadda yadda yadda */
561   if (pool_is_free_index (dcm->clients, pool_index))
562     return timeout;
563
564   c = pool_elt_at_index (dcm->clients, pool_index);
565
566   /* Time for us to do something with this client? */
567   if (now < c->next_transmit)
568     return timeout;
569
570  again:
571   switch (c->state)
572     {
573     case DHCP_DISCOVER:         /* send a discover */
574       if (dhcp_discover_state (dcm, c, now))
575         goto again;
576       break;
577
578     case DHCP_REQUEST:          /* send a request */
579       if (dhcp_request_state (dcm, c, now))
580         goto again;
581       break;
582       
583     case DHCP_BOUND:            /* bound, renew needed? */
584       if (dhcp_bound_state (dcm, c, now))
585         goto again;
586       break;
587       
588     default:
589       clib_warning ("dhcp client %d bogus state %d", 
590                     c - dcm->clients, c->state);
591       break;
592     }
593   
594   if (c->next_transmit < now + timeout)
595     return c->next_transmit - now;
596
597   return timeout;
598 }
599
600 static uword
601 dhcp_client_process (vlib_main_t * vm,
602                      vlib_node_runtime_t * rt,
603                      vlib_frame_t * f)
604 {
605   f64 timeout = 100.0;
606   f64 now;
607   uword event_type;
608   uword * event_data = 0;
609   dhcp_client_main_t * dcm = &dhcp_client_main;
610   dhcp_client_t * c;
611   int i;
612
613   while (1)
614     {
615       vlib_process_wait_for_event_or_clock (vm, timeout);
616
617       event_type = vlib_process_get_events (vm, &event_data);
618
619       now = vlib_time_now (vm);
620
621       switch (event_type)
622         {
623         case EVENT_DHCP_CLIENT_WAKEUP:
624           for (i = 0; i < vec_len (event_data); i++)
625             timeout = dhcp_client_sm (now, timeout, event_data[i]);
626           break;
627
628         case ~0:
629           pool_foreach (c, dcm->clients,
630           ({
631             timeout = dhcp_client_sm (now, timeout, 
632                                           (uword)(c - dcm->clients));
633           }));
634           if (pool_elts (dcm->clients) == 0)
635             timeout = 100.0;
636           break;
637         }
638
639       vec_reset_length (event_data);
640     }
641
642   /* NOTREACHED */
643   return 0;
644 }
645
646 VLIB_REGISTER_NODE (dhcp_client_process_node,static) = {
647     .function = dhcp_client_process,
648     .type = VLIB_NODE_TYPE_PROCESS,
649     .name = "dhcp-client-process",
650     .process_log2_n_stack_bytes = 16,
651 };
652
653 static u8 * format_dhcp_client_state (u8 * s, va_list * va)
654 {
655   dhcp_client_state_t state = va_arg (*va, dhcp_client_state_t);
656   char * str = "BOGUS!";
657
658   switch (state)
659     {
660 #define _(a)                                    \
661     case a:                                     \
662       str = #a;                                 \
663         break;
664       foreach_dhcp_client_state;
665 #undef _
666     default:
667       break;
668     }
669
670   s = format (s, "%s", str);
671   return s;
672 }
673
674 static u8 * format_dhcp_client (u8 * s, va_list * va)
675 {
676   dhcp_client_main_t * dcm = va_arg (*va, dhcp_client_main_t *);
677   dhcp_client_t * c = va_arg (*va, dhcp_client_t *);
678   int verbose = va_arg (*va, int);
679
680   s = format (s, "[%d] %U state %U ", c - dcm->clients, 
681               format_vnet_sw_if_index_name, dcm->vnet_main, c->sw_if_index,
682               format_dhcp_client_state, c->state);
683
684   if (c->leased_address.as_u32)
685     s = format (s, "addr %U/%d gw %U\n",
686                 format_ip4_address, &c->leased_address, 
687                 c->subnet_mask_width, format_ip4_address, &c->router_address);
688   else
689     s = format (s, "no address\n");
690
691   if (verbose)
692     {
693       s = format (s, "retry count %d, next xmt %.2f",
694                   c->retry_count, c->next_transmit);
695     }
696   return s;
697 }
698
699 static clib_error_t *
700 show_dhcp_client_command_fn (vlib_main_t * vm,
701                              unformat_input_t * input,
702                              vlib_cli_command_t * cmd)
703 {
704   dhcp_client_main_t * dcm = &dhcp_client_main;
705   dhcp_client_t * c;
706   int verbose = 0;
707   u32 sw_if_index = ~0;
708   uword * p;
709
710   while (unformat_check_input(input) != UNFORMAT_END_OF_INPUT) 
711     {
712       if (unformat (input, "intfc %U", 
713                     unformat_vnet_sw_interface, dcm->vnet_main, 
714                     &sw_if_index))
715         ;
716       else if (unformat (input, "verbose"))
717         verbose = 1;
718       else
719         break;
720     }
721
722   if (sw_if_index != ~0)
723     {
724       p = hash_get (dcm->client_by_sw_if_index, sw_if_index);
725       if (p == 0)
726         return clib_error_return (0, "dhcp client not configured");
727       c = pool_elt_at_index (dcm->clients, p[0]);
728       vlib_cli_output (vm, "%U", format_dhcp_client, dcm, c, verbose);
729       return 0;
730     }
731
732   pool_foreach (c, dcm->clients, 
733   ({
734     vlib_cli_output (vm, "%U", format_dhcp_client, dcm, c, verbose);
735   }));
736   
737   return 0;
738 }
739
740 VLIB_CLI_COMMAND (show_dhcp_client_command, static) = {
741   .path = "show dhcp client",
742   .short_help = "show dhcp client [intfc <intfc>][verbose]",
743   .function = show_dhcp_client_command_fn,
744 };
745
746
747 int dhcp_client_add_del (dhcp_client_add_del_args_t * a)
748 {
749   dhcp_client_main_t * dcm = &dhcp_client_main;
750   vlib_main_t * vm = dcm->vlib_main;
751   dhcp_client_t * c;
752   uword * p;
753   fib_prefix_t all_1s =
754   {
755       .fp_len = 32,
756       .fp_addr.ip4.as_u32 = 0xffffffff,
757       .fp_proto = FIB_PROTOCOL_IP4,
758   };
759   fib_prefix_t all_0s =
760   {
761       .fp_len = 0,
762       .fp_addr.ip4.as_u32 = 0x0,
763       .fp_proto = FIB_PROTOCOL_IP4,
764   };
765
766   p = hash_get (dcm->client_by_sw_if_index, a->sw_if_index);
767
768   if ((p && a->is_add) || (!p && a->is_add == 0))
769     return VNET_API_ERROR_INVALID_VALUE;
770
771   if (a->is_add)
772     {
773       pool_get (dcm->clients, c);
774       memset (c, 0, sizeof (*c));
775       c->state = DHCP_DISCOVER;
776       c->sw_if_index = a->sw_if_index;
777       c->client_index = a->client_index;
778       c->pid = a->pid;
779       c->event_callback = a->event_callback;
780       c->option_55_data = a->option_55_data;
781       c->hostname = a->hostname;
782       c->client_identifier = a->client_identifier;
783       do {
784         c->transaction_id = random_u32 (&dcm->seed);
785       } while (c->transaction_id == 0);
786       set_l2_rewrite (dcm, c);
787       hash_set (dcm->client_by_sw_if_index, a->sw_if_index, c - dcm->clients);
788
789       /* this add is ref counted by FIB so we can add for each itf */
790       fib_table_entry_special_add(fib_table_get_index_for_sw_if_index(
791                                       FIB_PROTOCOL_IP4,
792                                       c->sw_if_index),
793                                   &all_1s,
794                                   FIB_SOURCE_DHCP,
795                                   FIB_ENTRY_FLAG_LOCAL);
796
797      /*
798        * enable the interface to RX IPv4 packets
799        * this is also ref counted
800        */
801       ip4_sw_interface_enable_disable (c->sw_if_index, 1);
802
803       vlib_process_signal_event (vm, dhcp_client_process_node.index, 
804                                  EVENT_DHCP_CLIENT_WAKEUP, c - dcm->clients);
805     }
806   else
807     {
808       c = pool_elt_at_index (dcm->clients, p[0]);
809
810       fib_table_entry_special_remove(fib_table_get_index_for_sw_if_index(
811                                          FIB_PROTOCOL_IP4,
812                                          c->sw_if_index),
813                                      &all_1s,
814                                      FIB_SOURCE_DHCP);
815
816       if (c->router_address.as_u32)
817       {
818           ip46_address_t nh = {
819               .ip4 = c->router_address,
820           };
821
822           fib_table_entry_path_remove(fib_table_get_index_for_sw_if_index(
823                                           FIB_PROTOCOL_IP4,
824                                           c->sw_if_index),
825                                       &all_0s,
826                                       FIB_SOURCE_DHCP,
827                                       FIB_PROTOCOL_IP4,
828                                       &nh,
829                                       c->sw_if_index,
830                                       ~0,
831                                       1,
832                                       FIB_ROUTE_PATH_FLAG_NONE);
833       }
834       dhcp_client_release_address (dcm, c);
835       ip4_sw_interface_enable_disable (c->sw_if_index, 0);
836
837       vec_free (c->option_55_data);
838       vec_free (c->hostname);
839       vec_free (c->client_identifier);
840       vec_free (c->l2_rewrite);
841       hash_unset (dcm->client_by_sw_if_index, c->sw_if_index);
842       pool_put (dcm->clients, c);
843     }
844   return 0;
845 }
846
847 int
848 dhcp_client_config (vlib_main_t * vm,
849                     u32 sw_if_index,
850                     u8 * hostname,
851                     u8 * client_id,
852                     u32 is_add,
853                     u32 client_index,
854                     void * event_callback,
855                     u32 pid)
856 {
857   dhcp_client_add_del_args_t _a, *a = &_a;
858   int rv;
859
860   memset (a, 0, sizeof (*a));
861   a->is_add = is_add;
862   a->sw_if_index = sw_if_index;
863   a->client_index = client_index;
864   a->pid = pid;
865   a->event_callback = event_callback;
866   vec_validate(a->hostname, strlen((char *)hostname) - 1);
867   strncpy((char *)a->hostname, (char *)hostname, vec_len(a->hostname));
868   vec_validate(a->client_identifier, strlen((char *)client_id) - 1);
869   strncpy((char *)a->client_identifier, (char *)client_id, vec_len(a->client_identifier));
870
871   /* 
872    * Option 55 request list. These data precisely match
873    * the Ubuntu dhcp client. YMMV.
874    */
875
876   /* Subnet Mask */
877   vec_add1 (a->option_55_data, 1);
878   /* Broadcast address */
879   vec_add1 (a->option_55_data, 28);
880   /* time offset */
881   vec_add1 (a->option_55_data, 2);
882   /* Router */
883   vec_add1 (a->option_55_data, 3);
884   /* Domain Name */
885   vec_add1 (a->option_55_data, 15);
886   /* DNS */
887   vec_add1 (a->option_55_data, 6);
888   /* Domain search */
889   vec_add1 (a->option_55_data, 119);
890   /* Host name */
891   vec_add1 (a->option_55_data, 12);
892   /* NetBIOS name server */
893   vec_add1 (a->option_55_data, 44);
894   /* NetBIOS Scope */
895   vec_add1 (a->option_55_data, 47);
896   /* MTU */
897   vec_add1 (a->option_55_data, 26);
898   /* Classless static route */
899   vec_add1 (a->option_55_data, 121);
900   /* NTP servers */
901   vec_add1 (a->option_55_data, 42);
902
903   rv = dhcp_client_add_del (a);
904
905   switch (rv)
906     {
907     case 0:
908       break;
909
910     case VNET_API_ERROR_INVALID_VALUE:
911
912       vec_free (a->hostname);
913       vec_free (a->client_identifier);
914       vec_free (a->option_55_data);
915
916       if (is_add)
917         clib_warning ("dhcp client already enabled on intf_idx %d",
918                       sw_if_index);
919       else
920         clib_warning ("dhcp client not enabled on on intf_idx %d",
921                       sw_if_index);
922       break;
923
924     default:
925       clib_warning ("dhcp_client_add_del returned %d", rv);
926     }
927
928   return rv;
929 }
930
931 static clib_error_t *
932 dhcp_client_set_command_fn (vlib_main_t * vm,
933                            unformat_input_t * input,
934                            vlib_cli_command_t * cmd)
935 {
936
937   dhcp_client_main_t * dcm = &dhcp_client_main;
938   u32 sw_if_index;
939   u8 * hostname = 0;
940   u8 sw_if_index_set = 0;
941   int is_add = 1;
942   dhcp_client_add_del_args_t _a, *a = &_a;
943   int rv;
944
945   while (unformat_check_input(input) != UNFORMAT_END_OF_INPUT) 
946     {
947       if (unformat (input, "intfc %U", 
948                    unformat_vnet_sw_interface, dcm->vnet_main, 
949                    &sw_if_index))
950         sw_if_index_set = 1;
951       else if (unformat (input, "hostname %v", &hostname))
952         ;
953       else if (unformat (input, "del"))
954         is_add = 0;
955       else
956         break;
957     }
958
959   if (sw_if_index_set == 0)
960     return clib_error_return (0, "interface not specified");
961
962   memset (a, 0, sizeof (*a));
963   a->is_add = is_add;
964   a->sw_if_index = sw_if_index;
965   a->hostname = hostname;
966   a->client_identifier = format (0, "vpe 1.0%c", 0);
967
968   /* 
969    * Option 55 request list. These data precisely match
970    * the Ubuntu dhcp client. YMMV.
971    */
972
973   /* Subnet Mask */
974   vec_add1 (a->option_55_data, 1);
975   /* Broadcast address */
976   vec_add1 (a->option_55_data, 28);
977   /* time offset */
978   vec_add1 (a->option_55_data, 2);
979   /* Router */
980   vec_add1 (a->option_55_data, 3);
981   /* Domain Name */
982   vec_add1 (a->option_55_data, 15);
983   /* DNS */
984   vec_add1 (a->option_55_data, 6);
985   /* Domain search */
986   vec_add1 (a->option_55_data, 119);
987   /* Host name */
988   vec_add1 (a->option_55_data, 12);
989   /* NetBIOS name server */
990   vec_add1 (a->option_55_data, 44);
991   /* NetBIOS Scope */
992   vec_add1 (a->option_55_data, 47);
993   /* MTU */
994   vec_add1 (a->option_55_data, 26);
995   /* Classless static route */
996   vec_add1 (a->option_55_data, 121);
997   /* NTP servers */
998   vec_add1 (a->option_55_data, 42);
999
1000   rv = dhcp_client_add_del (a);
1001
1002   switch (rv)
1003     {
1004     case 0:
1005       break;
1006
1007     case VNET_API_ERROR_INVALID_VALUE:
1008
1009       vec_free (a->hostname);
1010       vec_free (a->client_identifier);
1011       vec_free (a->option_55_data);
1012       if (is_add)
1013         return clib_error_return (0, "dhcp client already enabled on %U", 
1014                                   format_vnet_sw_if_index_name, 
1015                                   dcm->vnet_main, sw_if_index);
1016       else
1017         return clib_error_return (0, "dhcp client not enabled on %U", 
1018                                   format_vnet_sw_if_index_name, 
1019                                   dcm->vnet_main, sw_if_index);
1020       break;
1021
1022     default:
1023       vlib_cli_output (vm, "dhcp_client_add_del returned %d", rv);
1024     }
1025
1026   return 0;
1027 }
1028
1029 VLIB_CLI_COMMAND (dhcp_client_set_command, static) = {
1030   .path = "set dhcp client",
1031   .short_help = "set dhcp client [del] intfc <interface> [hostname <name>]",
1032   .function = dhcp_client_set_command_fn,
1033 };
1034
1035 static clib_error_t *
1036 dhcp_client_init (vlib_main_t * vm)
1037 {
1038   dhcp_client_main_t * dcm = &dhcp_client_main;
1039
1040   dcm->vlib_main = vm;
1041   dcm->vnet_main = vnet_get_main();
1042   dcm->seed = 0xdeaddabe;
1043   return 0;
1044 }
1045
1046 VLIB_INIT_FUNCTION (dhcp_client_init);