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