FD.io VPP  v18.07-34-g55fbdb9
Vector Packet Processing
client.c
Go to the documentation of this file.
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 
21 static u8 *format_dhcp_client_state (u8 * s, va_list * va);
23 
24 #define foreach_dhcp_sent_packet_stat \
25 _(DISCOVER, "DHCP discover packets sent") \
26 _(OFFER, "DHCP offer packets sent") \
27 _(REQUEST, "DHCP request packets sent") \
28 _(ACK, "DHCP ack packets sent")
29 
30 #define foreach_dhcp_error_counter \
31 _(NOT_FOR_US, "DHCP packets for other hosts, dropped") \
32 _(NAK, "DHCP nak packets received") \
33 _(NON_OFFER_DISCOVER, "DHCP non-offer packets in discover state") \
34 _(ODDBALL, "DHCP non-ack, non-offer packets received") \
35 _(BOUND, "DHCP bind success")
36 
37 typedef enum
38 {
39 #define _(sym,str) DHCP_STAT_##sym,
41 #undef _
45 
47 #define _(sym,string) string,
49 #undef _
50  "DHCP unknown packets sent",
51 };
52 
53 
54 static void
56 {
57  /*
58  * Install any/all info gleaned from dhcp, right here
59  */
61  (void *) &c->leased_address,
62  c->subnet_mask_width, 0 /*is_del */ );
63 }
64 
65 static void
67 {
68  /*
69  * Remove any/all info gleaned from dhcp, right here. Caller(s)
70  * have not wiped out the info yet.
71  */
72 
74  (void *) &c->leased_address,
75  c->subnet_mask_width, 1 /*is_del */ );
76 }
77 
78 static void
80 {
81  /* Acquire the L2 rewrite string for the indicated sw_if_index */
83  c->sw_if_index,
85  0 /* broadcast */ );
86 }
87 
88 void vl_api_rpc_call_main_thread (void *fp, u8 * data, u32 data_length);
89 
90 static void
92 {
94  ASSERT (vlib_get_thread_index () == 0);
96  EVENT_DHCP_CLIENT_WAKEUP, *client_index);
97 }
98 
99 static void
101 {
103 
104  /* disable the feature */
105  vnet_feature_enable_disable ("ip4-unicast",
106  "ip4-dhcp-client-detect",
107  c->sw_if_index, 0 /* disable */ , 0, 0);
109 
110  /* if renewing the lease, the address and route have already been added */
111  if (c->state == DHCP_BOUND)
112  return;
113 
114  /* add the address to the interface */
116 
117  /*
118  * Configure default IP route:
119  */
120  if (c->router_address.as_u32)
121  {
122  fib_prefix_t all_0s = {
123  .fp_len = 0,
124  .fp_addr.ip4.as_u32 = 0x0,
125  .fp_proto = FIB_PROTOCOL_IP4,
126  };
127  ip46_address_t nh = {
128  .ip4 = c->router_address,
129  };
130 
131  /* *INDENT-OFF* */
135  c->sw_if_index),
136  &all_0s,
140  &nh, c->sw_if_index,
141  ~0, 1, NULL, // no label stack
143  /* *INDENT-ON* */
144  }
145 
146  /*
147  * Call the user's event callback to report DHCP information
148  */
149  if (c->event_callback)
150  c->event_callback (c->client_index, c);
151 }
152 
153 /*
154  * dhcp_client_for_us - server-to-client callback.
155  * Called from proxy_node.c:dhcp_proxy_to_client_input().
156  * This function first decides that the packet in question is
157  * actually for the dhcp client code in case we're also acting as
158  * a dhcp proxy. Ay caramba, what a folly!
159  */
160 int
162  ip4_header_t * ip,
163  udp_header_t * udp, dhcp_header_t * dhcp)
164 {
166  vlib_main_t *vm = dcm->vlib_main;
167  dhcp_client_t *c;
168  uword *p;
169  f64 now = vlib_time_now (dcm->vlib_main);
170  u8 dhcp_message_type = 0;
171  dhcp_option_t *o;
172 
173  /*
174  * Doing dhcp client on this interface?
175  * Presumably we will always receive dhcp clnt for-us pkts on
176  * the interface that's asking for an address.
177  */
179  vnet_buffer (b)->sw_if_index[VLIB_RX]);
180  if (p == 0)
181  return 0; /* no */
182 
183  c = pool_elt_at_index (dcm->clients, p[0]);
184 
185  /* Mixing dhcp relay and dhcp proxy? DGMS... */
186  if (c->state == DHCP_BOUND && c->retry_count == 0)
187  return 0;
188 
189  /* Packet not for us? Turf it... */
190  if (memcmp (dhcp->client_hardware_address, c->client_hardware_address,
191  sizeof (c->client_hardware_address)))
192  {
194  DHCP_STAT_NOT_FOR_US, 1);
195  return 0;
196  }
197 
198  /* parse through the packet, learn what we can */
199  if (dhcp->your_ip_address.as_u32)
201 
203 
204  o = (dhcp_option_t *) dhcp->options;
205 
206  while (o->option != 0xFF /* end of options */ &&
207  (u8 *) o < (b->data + b->current_data + b->current_length))
208  {
209  switch (o->option)
210  {
211  case 53: /* dhcp message type */
212  dhcp_message_type = o->data[0];
213  break;
214 
215  case 51: /* lease time */
216  {
217  u32 lease_time_in_seconds =
218  clib_host_to_net_u32 (o->data_as_u32[0]);
219  // for debug: lease_time_in_seconds = 20; /*$$$$*/
220  c->lease_expires = now + (f64) lease_time_in_seconds;
221  c->lease_lifetime = lease_time_in_seconds;
222  /* Set a sensible default, in case we don't get opt 58 */
223  c->lease_renewal_interval = lease_time_in_seconds / 2;
224  }
225  break;
226 
227  case 58: /* lease renew time in seconds */
228  {
229  u32 lease_renew_time_in_seconds =
230  clib_host_to_net_u32 (o->data_as_u32[0]);
231  c->lease_renewal_interval = lease_renew_time_in_seconds;
232  }
233  break;
234 
235  case 54: /* dhcp server address */
236  c->dhcp_server.as_u32 = o->data_as_u32[0];
237  break;
238 
239  case 1: /* subnet mask */
240  {
241  u32 subnet_mask = clib_host_to_net_u32 (o->data_as_u32[0]);
242  c->subnet_mask_width = count_set_bits (subnet_mask);
243  }
244  break;
245  case 3: /* router address */
246  {
247  u32 router_address = o->data_as_u32[0];
248  c->router_address.as_u32 = router_address;
249  }
250  break;
251 
252  case 12: /* hostname */
253  {
254  /* Replace the existing hostname if necessary */
255  vec_free (c->hostname);
256  vec_validate (c->hostname, o->length - 1);
257  clib_memcpy (c->hostname, o->data, o->length);
258  }
259  break;
260 
261  /* $$$$ Your message in this space, parse more options */
262  default:
263  break;
264  }
265 
266  o = (dhcp_option_t *) (((uword) o) + (o->length + 2));
267  }
268 
269  switch (c->state)
270  {
271  case DHCP_DISCOVER:
272  if (dhcp_message_type != DHCP_PACKET_OFFER)
273  {
275  DHCP_STAT_NON_OFFER_DISCOVER, 1);
276  c->next_transmit = now + 5.0;
277  break;
278  }
279 
280  /* Received an offer, go send a request */
281  c->state = DHCP_REQUEST;
282  c->retry_count = 0;
283  c->next_transmit = 0; /* send right now... */
284  /* Poke the client process, which will send the request */
285  uword client_id = c - dcm->clients;
287  (u8 *) & client_id, sizeof (uword));
288  break;
289 
290  case DHCP_BOUND:
291  case DHCP_REQUEST:
292  if (dhcp_message_type == DHCP_PACKET_NAK)
293  {
295  DHCP_STAT_NAK, 1);
296  /* Probably never happens in bound state, but anyhow... */
297  if (c->state == DHCP_BOUND)
298  {
300  (void *) &c->leased_address,
302  1 /*is_del */ );
303  vnet_feature_enable_disable ("ip4-unicast",
304  "ip4-dhcp-client-detect",
305  c->sw_if_index, 1 /* enable */ ,
306  0, 0);
308  }
309  /* Wipe out any memory of the address we had... */
310  c->state = DHCP_DISCOVER;
311  c->next_transmit = now;
312  c->retry_count = 0;
313  c->leased_address.as_u32 = 0;
314  c->subnet_mask_width = 0;
315  c->router_address.as_u32 = 0;
316  c->lease_renewal_interval = 0;
317  c->dhcp_server.as_u32 = 0;
318  break;
319  }
320 
321  if (dhcp_message_type != DHCP_PACKET_ACK &&
322  dhcp_message_type != DHCP_PACKET_OFFER)
323  {
325  DHCP_STAT_NON_OFFER_DISCOVER, 1);
326  clib_warning ("sw_if_index %d state %U message type %d",
328  c->state, dhcp_message_type);
329  c->next_transmit = now + 5.0;
330  break;
331  }
332  /* OK, we own the address (etc), add to the routing table(s) */
334  (u8 *) c, sizeof (*c));
335 
336  c->state = DHCP_BOUND;
337  c->retry_count = 0;
338  c->next_transmit = now + (f64) c->lease_renewal_interval;
339  c->lease_expires = now + (f64) c->lease_lifetime;
341  DHCP_STAT_BOUND, 1);
342  break;
343 
344  default:
345  clib_warning ("client %d bogus state %d", c - dcm->clients, c->state);
346  break;
347  }
348 
349  /* drop the pkt, return 1 */
350  vlib_buffer_free (vm, &bi, 1);
351  return 1;
352 }
353 
354 static void
356  dhcp_packet_type_t type, int is_broadcast)
357 {
358  vlib_main_t *vm = dcm->vlib_main;
359  vnet_main_t *vnm = dcm->vnet_main;
361  vnet_sw_interface_t *sup_sw
364  vlib_buffer_t *b;
365  u32 bi;
366  ip4_header_t *ip;
367  udp_header_t *udp;
368  dhcp_header_t *dhcp;
369  u32 *to_next;
370  vlib_frame_t *f;
371  dhcp_option_t *o;
372  u16 udp_length, ip_length;
374 
375  /* Interface(s) down? */
376  if ((hw->flags & VNET_HW_INTERFACE_FLAG_LINK_UP) == 0)
377  return;
378  if ((sup_sw->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) == 0)
379  return;
380  if ((sw->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) == 0)
381  return;
382 
383  if (vlib_buffer_alloc (vm, &bi, 1) != 1)
384  {
385  clib_warning ("buffer allocation failure");
386  c->next_transmit = 0;
387  return;
388  }
389 
390  /* Build a dhcpv4 pkt from whole cloth */
391  b = vlib_get_buffer (vm, bi);
392 
393  ASSERT (b->current_data == 0);
394 
395  vnet_buffer (b)->sw_if_index[VLIB_RX] = c->sw_if_index;
396  if (is_broadcast)
397  {
399  vnet_buffer (b)->sw_if_index[VLIB_TX] = c->sw_if_index;
401  ip = (void *)
402  (((u8 *) vlib_buffer_get_current (b)) + vec_len (c->l2_rewrite));
403  }
404  else
405  {
406  f = vlib_get_frame_to_node (vm, ip4_lookup_node.index);
407  vnet_buffer (b)->sw_if_index[VLIB_TX] = ~0; /* use interface VRF */
408  ip = vlib_buffer_get_current (b);
409  }
410 
411  /* Enqueue the packet right now */
412  to_next = vlib_frame_vector_args (f);
413  to_next[0] = bi;
414  f->n_vectors = 1;
415 
416  if (is_broadcast)
418  else
420 
421  udp = (udp_header_t *) (ip + 1);
422  dhcp = (dhcp_header_t *) (udp + 1);
423 
424  /* $$$ optimize, maybe */
425  memset (ip, 0, sizeof (*ip) + sizeof (*udp) + sizeof (*dhcp));
426 
427  ip->ip_version_and_header_length = 0x45;
428  ip->ttl = 128;
429  ip->protocol = IP_PROTOCOL_UDP;
430 
431  if (is_broadcast)
432  {
433  /* src = 0.0.0.0, dst = 255.255.255.255 */
434  ip->dst_address.as_u32 = ~0;
435  }
436  else
437  {
438  /* Renewing an active lease, plain old ip4 src/dst */
441  }
442 
443  udp->src_port = clib_host_to_net_u16 (UDP_DST_PORT_dhcp_to_client);
444  udp->dst_port = clib_host_to_net_u16 (UDP_DST_PORT_dhcp_to_server);
445 
446  /* Send the interface MAC address */
448 
449  /* And remember it for rx-packet-for-us checking */
451  sizeof (c->client_hardware_address));
452 
453  /* Lease renewal, set up client_ip_address */
454  if (is_broadcast == 0)
456 
457  dhcp->opcode = 1; /* request, all we send */
458  dhcp->hardware_type = 1; /* ethernet */
459  dhcp->hardware_address_length = 6;
461  dhcp->flags =
462  clib_host_to_net_u16 (is_broadcast && c->set_broadcast_flag ?
463  DHCP_FLAG_BROADCAST : 0);
465 
466  o = (dhcp_option_t *) dhcp->options;
467 
468  /* Send option 53, the DHCP message type */
470  o->length = 1;
471  o->data[0] = type;
472  o = (dhcp_option_t *) (((uword) o) + (o->length + 2));
473 
474  /* Send option 57, max msg length */
475  if (0 /* not needed, apparently */ )
476  {
477  o->option = 57;
478  o->length = 2;
479  {
480  u16 *o2 = (u16 *) o->data;
481  *o2 = clib_host_to_net_u16 (1152);
482  o = (dhcp_option_t *) (((uword) o) + (o->length + 2));
483  }
484  }
485 
486  /*
487  * If server ip address is available with non-zero value,
488  * option 54 (DHCP Server Identifier) is sent.
489  */
490  if (c->dhcp_server.as_u32)
491  {
492  o->option = 54;
493  o->length = 4;
494  clib_memcpy (o->data, &c->dhcp_server.as_u32, 4);
495  o = (dhcp_option_t *) (((uword) o) + (o->length + 2));
496  }
497 
498  /* send option 50, requested IP address */
499  if (c->leased_address.as_u32)
500  {
501  o->option = 50;
502  o->length = 4;
503  clib_memcpy (o->data, &c->leased_address.as_u32, 4);
504  o = (dhcp_option_t *) (((uword) o) + (o->length + 2));
505  }
506 
507  /* send option 12, host name */
508  if (vec_len (c->hostname))
509  {
510  o->option = 12;
511  o->length = vec_len (c->hostname);
512  clib_memcpy (o->data, c->hostname, vec_len (c->hostname));
513  o = (dhcp_option_t *) (((uword) o) + (o->length + 2));
514  }
515 
516  /* send option 61, client_id */
517  if (vec_len (c->client_identifier))
518  {
519  o->option = 61;
520  o->length = vec_len (c->client_identifier);
523  o = (dhcp_option_t *) (((uword) o) + (o->length + 2));
524  }
525 
526  /* $$ maybe send the client s/w version if anyone cares */
527 
528  /*
529  * send option 55, parameter request list
530  * The current list - see below, matches the Linux dhcp client's list
531  * Any specific dhcp server config and/or dhcp server may or may
532  * not yield specific options.
533  */
534  o->option = 55;
535  o->length = vec_len (c->option_55_data);
537  o = (dhcp_option_t *) (((uword) o) + (o->length + 2));
538 
539  /* End of list */
540  o->option = 0xff;
541  o->length = 0;
542  o++;
543 
544  b->current_length = ((u8 *) o) - b->data;
545 
546  /* fix ip length, checksum and udp length */
547  ip_length = vlib_buffer_length_in_chain (vm, b);
548  if (is_broadcast)
549  ip_length -= vec_len (c->l2_rewrite);
550 
551  ip->length = clib_host_to_net_u16 (ip_length);
552  ip->checksum = ip4_header_checksum (ip);
553 
554  udp_length = ip_length - (sizeof (*ip));
555  udp->length = clib_host_to_net_u16 (udp_length);
556 
557  switch (type)
558  {
559 #define _(a,b) case DHCP_PACKET_##a: {counter_index = DHCP_STAT_##a; break;}
561 #undef _
562  default:
563  counter_index = DHCP_STAT_UNKNOWN;
564  break;
565  }
566 
568  counter_index, 1);
569 }
570 
571 static int
573 {
574  /*
575  * State machine "DISCOVER" state. Send a dhcp discover packet,
576  * eventually back off the retry rate.
577  */
578 
579  if (c->client_detect_feature_enabled == 0)
580  {
581  vnet_feature_enable_disable ("ip4-unicast",
582  "ip4-dhcp-client-detect",
583  c->sw_if_index, 1 /* enable */ , 0, 0);
585  }
586 
587  send_dhcp_pkt (dcm, c, DHCP_PACKET_DISCOVER, 1 /* is_broadcast */ );
588 
589  c->retry_count++;
590  if (c->retry_count > 10)
591  c->next_transmit = now + 5.0;
592  else
593  c->next_transmit = now + 1.0;
594  return 0;
595 }
596 
597 static int
599 {
600  /*
601  * State machine "REQUEST" state. Send a dhcp request packet,
602  * eventually drop back to the discover state.
603  */
604  send_dhcp_pkt (dcm, c, DHCP_PACKET_REQUEST, 1 /* is_broadcast */ );
605 
606  c->retry_count++;
607  if (c->retry_count > 7 /* lucky you */ )
608  {
609  c->state = DHCP_DISCOVER;
610  c->next_transmit = now;
611  c->retry_count = 0;
612  return 1;
613  }
614  c->next_transmit = now + 1.0;
615  return 0;
616 }
617 
618 static int
620 {
621  /*
622  * State machine "BOUND" state. Send a dhcp request packet to renew
623  * the lease.
624  * Eventually, when the lease expires, forget the dhcp data
625  * and go back to the stone age.
626  */
627 
628  /*
629  * We disable the client detect feature when we bind a
630  * DHCP address. Turn it back on again on first renew attempt.
631  * Otherwise, if the DHCP server replies we'll never see it.
632  */
633  if (c->client_detect_feature_enabled == 0)
634  {
635  vnet_feature_enable_disable ("ip4-unicast",
636  "ip4-dhcp-client-detect",
637  c->sw_if_index, 1 /* enable */ , 0, 0);
639  }
640 
641  send_dhcp_pkt (dcm, c, DHCP_PACKET_REQUEST, 0 /* is_broadcast */ );
642 
643  c->retry_count++;
644  if (c->retry_count > 10)
645  c->next_transmit = now + 5.0;
646  else
647  c->next_transmit = now + 1.0;
648 
649  if (now > c->lease_expires)
650  {
651  /* Remove the default route */
652  if (c->router_address.as_u32)
653  {
654  fib_prefix_t all_0s = {
655  .fp_len = 0,
656  .fp_addr.ip4.as_u32 = 0x0,
657  .fp_proto = FIB_PROTOCOL_IP4,
658  };
659  ip46_address_t nh = {
660  .ip4 = c->router_address,
661  };
662 
665  &all_0s, FIB_SOURCE_DHCP,
666  DPO_PROTO_IP4, &nh, c->sw_if_index, ~0,
668  }
669  /* Remove the interface address */
671  c->state = DHCP_DISCOVER;
672  c->next_transmit = now;
673  c->retry_count = 0;
674  /* Wipe out any memory of the address we had... */
675  c->leased_address.as_u32 = 0;
676  c->subnet_mask_width = 0;
677  c->router_address.as_u32 = 0;
678  c->lease_renewal_interval = 0;
679  c->dhcp_server.as_u32 = 0;
680  return 1;
681  }
682  return 0;
683 }
684 
685 static f64
686 dhcp_client_sm (f64 now, f64 timeout, uword pool_index)
687 {
689  dhcp_client_t *c;
690 
691  /* deleted, pooched, yadda yadda yadda */
692  if (pool_is_free_index (dcm->clients, pool_index))
693  return timeout;
694 
695  c = pool_elt_at_index (dcm->clients, pool_index);
696 
697  /* Time for us to do something with this client? */
698  if (now < c->next_transmit)
699  return timeout;
700 
701 again:
702  switch (c->state)
703  {
704  case DHCP_DISCOVER: /* send a discover */
705  if (dhcp_discover_state (dcm, c, now))
706  goto again;
707  break;
708 
709  case DHCP_REQUEST: /* send a request */
710  if (dhcp_request_state (dcm, c, now))
711  goto again;
712  break;
713 
714  case DHCP_BOUND: /* bound, renew needed? */
715  if (dhcp_bound_state (dcm, c, now))
716  goto again;
717  break;
718 
719  default:
720  clib_warning ("dhcp client %d bogus state %d",
721  c - dcm->clients, c->state);
722  break;
723  }
724 
725  if (c->next_transmit < now + timeout)
726  return c->next_transmit - now;
727 
728  return timeout;
729 }
730 
731 static uword
734 {
735  f64 timeout = 100.0;
736  f64 now;
737  uword event_type;
738  uword *event_data = 0;
740  dhcp_client_t *c;
741  int i;
742 
743  while (1)
744  {
746 
747  event_type = vlib_process_get_events (vm, &event_data);
748 
749  now = vlib_time_now (vm);
750 
751  switch (event_type)
752  {
754  for (i = 0; i < vec_len (event_data); i++)
755  timeout = dhcp_client_sm (now, timeout, event_data[i]);
756  break;
757 
758  case ~0:
759  /* *INDENT-OFF* */
760  pool_foreach (c, dcm->clients,
761  ({
762  timeout = dhcp_client_sm (now, timeout,
763  (uword) (c - dcm->clients));
764  }));
765  /* *INDENT-ON* */
766  if (pool_elts (dcm->clients) == 0)
767  timeout = 100.0;
768  break;
769  }
770 
771  vec_reset_length (event_data);
772  }
773 
774  /* NOTREACHED */
775  return 0;
776 }
777 
778 /* *INDENT-OFF* */
780  .function = dhcp_client_process,
781  .type = VLIB_NODE_TYPE_PROCESS,
782  .name = "dhcp-client-process",
783  .process_log2_n_stack_bytes = 16,
785  .error_strings = dhcp_client_process_stat_strings,
786 };
787 /* *INDENT-ON* */
788 
789 static u8 *
790 format_dhcp_client_state (u8 * s, va_list * va)
791 {
793  char *str = "BOGUS!";
794 
795  switch (state)
796  {
797 #define _(a) \
798  case a: \
799  str = #a; \
800  break;
802 #undef _
803  default:
804  break;
805  }
806 
807  s = format (s, "%s", str);
808  return s;
809 }
810 
811 static u8 *
812 format_dhcp_client (u8 * s, va_list * va)
813 {
814  dhcp_client_main_t *dcm = va_arg (*va, dhcp_client_main_t *);
815  dhcp_client_t *c = va_arg (*va, dhcp_client_t *);
816  int verbose = va_arg (*va, int);
817 
818  s = format (s, "[%d] %U state %U ", c - dcm->clients,
821 
822  if (c->leased_address.as_u32)
823  s = format (s, "addr %U/%d gw %U\n",
826  else
827  s = format (s, "no address\n");
828 
829  if (verbose)
830  {
831  s = format (s, "retry count %d, next xmt %.2f",
832  c->retry_count, c->next_transmit);
833  }
834  return s;
835 }
836 
837 static clib_error_t *
839  unformat_input_t * input,
840  vlib_cli_command_t * cmd)
841 {
843  dhcp_client_t *c;
844  int verbose = 0;
845  u32 sw_if_index = ~0;
846  uword *p;
847 
849  {
850  if (unformat (input, "intfc %U",
851  unformat_vnet_sw_interface, dcm->vnet_main, &sw_if_index))
852  ;
853  else if (unformat (input, "verbose"))
854  verbose = 1;
855  else
856  break;
857  }
858 
859  if (sw_if_index != ~0)
860  {
861  p = hash_get (dcm->client_by_sw_if_index, sw_if_index);
862  if (p == 0)
863  return clib_error_return (0, "dhcp client not configured");
864  c = pool_elt_at_index (dcm->clients, p[0]);
865  vlib_cli_output (vm, "%U", format_dhcp_client, dcm, c, verbose);
866  return 0;
867  }
868 
869  /* *INDENT-OFF* */
870  pool_foreach (c, dcm->clients,
871  ({
872  vlib_cli_output (vm, "%U",
873  format_dhcp_client, dcm,
874  c, verbose);
875  }));
876  /* *INDENT-ON* */
877 
878  return 0;
879 }
880 
881 /* *INDENT-OFF* */
882 VLIB_CLI_COMMAND (show_dhcp_client_command, static) = {
883  .path = "show dhcp client",
884  .short_help = "show dhcp client [intfc <intfc>][verbose]",
885  .function = show_dhcp_client_command_fn,
886 };
887 /* *INDENT-ON* */
888 
889 
890 int
892 {
894  vlib_main_t *vm = dcm->vlib_main;
895  dhcp_client_t *c;
896  uword *p;
897  fib_prefix_t all_0s = {
898  .fp_len = 0,
899  .fp_addr.ip4.as_u32 = 0x0,
900  .fp_proto = FIB_PROTOCOL_IP4,
901  };
902 
904 
905  if ((p && a->is_add) || (!p && a->is_add == 0))
906  return VNET_API_ERROR_INVALID_VALUE;
907 
908  if (a->is_add)
909  {
910  pool_get (dcm->clients, c);
911  memset (c, 0, sizeof (*c));
912  c->state = DHCP_DISCOVER;
913  c->sw_if_index = a->sw_if_index;
914  c->client_index = a->client_index;
915  c->pid = a->pid;
918  c->hostname = a->hostname;
921  do
922  {
923  c->transaction_id = random_u32 (&dcm->seed);
924  }
925  while (c->transaction_id == 0);
926  set_l2_rewrite (dcm, c);
927  hash_set (dcm->client_by_sw_if_index, a->sw_if_index, c - dcm->clients);
928 
929  /*
930  * In order to accept any OFFER, whether broadcasted or unicasted, we
931  * need to configure the dhcp-client-detect feature as an input feature
932  * so the DHCP OFFER is sent to the ip4-local node. Without this a
933  * broadcasted OFFER hits the 255.255.255.255/32 address and a unicast
934  * hits 0.0.0.0/0 both of which default to drop and the latter may forward
935  * of box - not what we want. Nor to we want to change these route for
936  * all interfaces in this table
937  */
938  vnet_feature_enable_disable ("ip4-unicast",
939  "ip4-dhcp-client-detect",
940  c->sw_if_index, 1 /* enable */ , 0, 0);
942 
945  }
946  else
947  {
948  c = pool_elt_at_index (dcm->clients, p[0]);
949 
950  if (c->router_address.as_u32)
951  {
952  ip46_address_t nh = {
953  .ip4 = c->router_address,
954  };
955 
958  &all_0s, FIB_SOURCE_DHCP,
959  DPO_PROTO_IP4, &nh, c->sw_if_index, ~0,
961  }
963 
965  vec_free (c->hostname);
967  vec_free (c->l2_rewrite);
969  pool_put (dcm->clients, c);
970  }
971  return 0;
972 }
973 
974 int
976  u32 client_index,
977  vlib_main_t * vm,
978  u32 sw_if_index,
979  u8 * hostname,
980  u8 * client_id,
981  dhcp_event_cb_t event_callback,
982  u8 set_broadcast_flag, u32 pid)
983 {
984  dhcp_client_add_del_args_t _a, *a = &_a;
985  int rv;
986 
987  memset (a, 0, sizeof (*a));
988  a->is_add = is_add;
989  a->sw_if_index = sw_if_index;
990  a->client_index = client_index;
991  a->pid = pid;
992  a->event_callback = event_callback;
993  a->set_broadcast_flag = set_broadcast_flag;
994  vec_validate (a->hostname, strlen ((char *) hostname) - 1);
995  strncpy ((char *) a->hostname, (char *) hostname, vec_len (a->hostname));
996  vec_validate (a->client_identifier, strlen ((char *) client_id) - 1);
997  strncpy ((char *) a->client_identifier, (char *) client_id,
999 
1000  /*
1001  * Option 55 request list. These data precisely match
1002  * the Ubuntu dhcp client. YMMV.
1003  */
1004 
1005  /* Subnet Mask */
1006  vec_add1 (a->option_55_data, 1);
1007  /* Broadcast address */
1008  vec_add1 (a->option_55_data, 28);
1009  /* time offset */
1010  vec_add1 (a->option_55_data, 2);
1011  /* Router */
1012  vec_add1 (a->option_55_data, 3);
1013  /* Domain Name */
1014  vec_add1 (a->option_55_data, 15);
1015  /* DNS */
1016  vec_add1 (a->option_55_data, 6);
1017  /* Domain search */
1018  vec_add1 (a->option_55_data, 119);
1019  /* Host name */
1020  vec_add1 (a->option_55_data, 12);
1021  /* NetBIOS name server */
1022  vec_add1 (a->option_55_data, 44);
1023  /* NetBIOS Scope */
1024  vec_add1 (a->option_55_data, 47);
1025  /* MTU */
1026  vec_add1 (a->option_55_data, 26);
1027  /* Classless static route */
1028  vec_add1 (a->option_55_data, 121);
1029  /* NTP servers */
1030  vec_add1 (a->option_55_data, 42);
1031 
1032  rv = dhcp_client_add_del (a);
1033 
1034  switch (rv)
1035  {
1036  case 0:
1037  break;
1038 
1039  case VNET_API_ERROR_INVALID_VALUE:
1040 
1041  vec_free (a->hostname);
1043  vec_free (a->option_55_data);
1044 
1045  if (is_add)
1046  clib_warning ("dhcp client already enabled on intf_idx %d",
1047  sw_if_index);
1048  else
1049  clib_warning ("dhcp client not enabled on on intf_idx %d",
1050  sw_if_index);
1051  break;
1052 
1053  default:
1054  clib_warning ("dhcp_client_add_del returned %d", rv);
1055  }
1056 
1057  return rv;
1058 }
1059 
1060 void
1062 {
1064  dhcp_client_t *c;
1065 
1066  /* *INDENT-OFF* */
1067  pool_foreach (c, dcm->clients,
1068  ({
1069  if (!cb(c, ctx))
1070  break;
1071  }));
1072  /* *INDENT-ON* */
1073 
1074 }
1075 
1076 static clib_error_t *
1078  unformat_input_t * input,
1079  vlib_cli_command_t * cmd)
1080 {
1081 
1083  u32 sw_if_index;
1084  u8 *hostname = 0;
1085  u8 sw_if_index_set = 0;
1086  u8 set_broadcast_flag = 1;
1087  int is_add = 1;
1088  dhcp_client_add_del_args_t _a, *a = &_a;
1089  int rv;
1090 
1091  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1092  {
1093  if (unformat (input, "intfc %U",
1094  unformat_vnet_sw_interface, dcm->vnet_main, &sw_if_index))
1095  sw_if_index_set = 1;
1096  else if (unformat (input, "hostname %v", &hostname))
1097  ;
1098  else if (unformat (input, "del"))
1099  is_add = 0;
1100  else if (unformat (input, "broadcast", &set_broadcast_flag))
1101  is_add = 0;
1102  else
1103  break;
1104  }
1105 
1106  if (sw_if_index_set == 0)
1107  return clib_error_return (0, "interface not specified");
1108 
1109  memset (a, 0, sizeof (*a));
1110  a->is_add = is_add;
1111  a->sw_if_index = sw_if_index;
1112  a->hostname = hostname;
1113  a->client_identifier = format (0, "vpe 1.0%c", 0);
1114  a->set_broadcast_flag = set_broadcast_flag;
1115 
1116  /*
1117  * Option 55 request list. These data precisely match
1118  * the Ubuntu dhcp client. YMMV.
1119  */
1120 
1121  /* Subnet Mask */
1122  vec_add1 (a->option_55_data, 1);
1123  /* Broadcast address */
1124  vec_add1 (a->option_55_data, 28);
1125  /* time offset */
1126  vec_add1 (a->option_55_data, 2);
1127  /* Router */
1128  vec_add1 (a->option_55_data, 3);
1129  /* Domain Name */
1130  vec_add1 (a->option_55_data, 15);
1131  /* DNS */
1132  vec_add1 (a->option_55_data, 6);
1133  /* Domain search */
1134  vec_add1 (a->option_55_data, 119);
1135  /* Host name */
1136  vec_add1 (a->option_55_data, 12);
1137  /* NetBIOS name server */
1138  vec_add1 (a->option_55_data, 44);
1139  /* NetBIOS Scope */
1140  vec_add1 (a->option_55_data, 47);
1141  /* MTU */
1142  vec_add1 (a->option_55_data, 26);
1143  /* Classless static route */
1144  vec_add1 (a->option_55_data, 121);
1145  /* NTP servers */
1146  vec_add1 (a->option_55_data, 42);
1147 
1148  rv = dhcp_client_add_del (a);
1149 
1150  switch (rv)
1151  {
1152  case 0:
1153  break;
1154 
1155  case VNET_API_ERROR_INVALID_VALUE:
1156 
1157  vec_free (a->hostname);
1159  vec_free (a->option_55_data);
1160  if (is_add)
1161  return clib_error_return (0, "dhcp client already enabled on %U",
1163  dcm->vnet_main, sw_if_index);
1164  else
1165  return clib_error_return (0, "dhcp client not enabled on %U",
1167  dcm->vnet_main, sw_if_index);
1168  break;
1169 
1170  default:
1171  vlib_cli_output (vm, "dhcp_client_add_del returned %d", rv);
1172  }
1173 
1174  return 0;
1175 }
1176 
1177 /* *INDENT-OFF* */
1178 VLIB_CLI_COMMAND (dhcp_client_set_command, static) = {
1179  .path = "set dhcp client",
1180  .short_help = "set dhcp client [del] intfc <interface> [hostname <name>]",
1181  .function = dhcp_client_set_command_fn,
1182 };
1183 /* *INDENT-ON* */
1184 
1185 static clib_error_t *
1187 {
1189 
1190  dcm->vlib_main = vm;
1191  dcm->vnet_main = vnet_get_main ();
1192  dcm->seed = 0xdeaddabe;
1193  return 0;
1194 }
1195 
1197 
1198 /*
1199  * fd.io coding-style-patch-verification: ON
1200  *
1201  * Local Variables:
1202  * eval: (c-set-style "gnu")
1203  * End:
1204  */
static u8 * format_dhcp_client(u8 *s, va_list *va)
Definition: client.c:812
u8 client_detect_feature_enabled
Definition: client.h:87
#define vec_validate(V, I)
Make sure vector is long enough for given index (no header, unspecified alignment) ...
Definition: vec.h:437
u8 client_hardware_address[16]
Definition: dhcp4_packet.h:36
fib_node_index_t fib_table_entry_path_add(u32 fib_index, const fib_prefix_t *prefix, fib_source_t source, fib_entry_flag_t flags, dpo_proto_t next_hop_proto, const ip46_address_t *next_hop, u32 next_hop_sw_if_index, u32 next_hop_fib_index, u32 next_hop_weight, fib_mpls_label_t *next_hop_labels, fib_route_path_flags_t path_flags)
Add one path to an entry (aka route) in the FIB.
Definition: fib_table.c:522
#define hash_set(h, key, value)
Definition: hash.h:255
static void dhcp_client_addr_callback(dhcp_client_t *c)
Definition: client.c:100
static f64 vlib_process_wait_for_event_or_clock(vlib_main_t *vm, f64 dt)
Suspend a cooperative multi-tasking thread Waits for an event, or for the indicated number of seconds...
Definition: node_funcs.h:699
#define hash_unset(h, key)
Definition: hash.h:261
a
Definition: bitmap.h:538
static void vlib_buffer_free(vlib_main_t *vm, u32 *buffers, u32 n_buffers)
Free buffers Frees the entire buffer chain for each buffer.
Definition: buffer_funcs.h:534
u32 retry_count
Definition: client.h:53
ip4_address_t src_address
Definition: ip4_packet.h:169
vnet_main_t * vnet_get_main(void)
Definition: misc.c:47
static vnet_hw_interface_t * vnet_get_sup_hw_interface(vnet_main_t *vnm, u32 sw_if_index)
u32 lease_lifetime
Definition: client.h:68
static void send_dhcp_pkt(dhcp_client_main_t *dcm, dhcp_client_t *c, dhcp_packet_type_t type, int is_broadcast)
Definition: client.c:355
#define NULL
Definition: clib.h:55
static f64 vlib_time_now(vlib_main_t *vm)
Definition: main.h:225
u32 fib_table_get_index_for_sw_if_index(fib_protocol_t proto, u32 sw_if_index)
Get the index of the FIB bound to the interface.
Definition: fib_table.c:948
u8 * option_55_data
Definition: client.h:71
static void dhcp_client_release_address(dhcp_client_main_t *dcm, dhcp_client_t *c)
Definition: client.c:66
static int dhcp_discover_state(dhcp_client_main_t *dcm, dhcp_client_t *c, f64 now)
Definition: client.c:572
#define vec_add1(V, E)
Add 1 element to end of vector (unspecified alignment).
Definition: vec.h:523
dhcp_client_t * clients
Definition: client.h:95
int dhcp_client_for_us(u32 bi, vlib_buffer_t *b, ip4_header_t *ip, udp_header_t *udp, dhcp_header_t *dhcp)
Definition: client.c:161
int i
u32 sw_if_index
Definition: client.h:50
#define DHCP_MAGIC
Definition: dhcp4_packet.h:69
static vnet_sw_interface_t * vnet_get_sw_interface(vnet_main_t *vnm, u32 sw_if_index)
void dhcp_client_walk(dhcp_client_walk_cb_t cb, void *ctx)
Walk (visit each) DHCP client configuration.
Definition: client.c:1061
void vlib_cli_output(struct vlib_main_t *vm, char *fmt,...)
Definition: client.c:135
u8 * format(u8 *s, const char *fmt,...)
Definition: format.c:419
unformat_function_t unformat_vnet_sw_interface
static int dhcp_request_state(dhcp_client_main_t *dcm, dhcp_client_t *c, f64 now)
Definition: client.c:598
ip4_address_t server_ip_address
Definition: dhcp4_packet.h:34
static u8 * format_dhcp_client_state(u8 *s, va_list *va)
Definition: client.c:790
#define VNET_HW_INTERFACE_FLAG_LINK_UP
Definition: interface.h:458
dhcp_event_cb_t event_callback
Definition: client.h:120
dhcp_client_state_t state
Definition: client.h:47
static uword vlib_buffer_length_in_chain(vlib_main_t *vm, vlib_buffer_t *b)
Get length in bytes of the buffer chain.
Definition: buffer_funcs.h:250
#define pool_get(P, E)
Allocate an object E from a pool P (unspecified alignment).
Definition: pool.h:228
static char * dhcp_client_process_stat_strings[]
Definition: client.c:46
format_function_t format_vnet_sw_if_index_name
unsigned char u8
Definition: types.h:56
u32 transaction_id
Definition: client.h:60
u32 data_as_u32[0]
Definition: dhcp4_packet.h:50
#define vec_reset_length(v)
Reset vector length to zero NULL-pointer tolerant.
double f64
Definition: types.h:142
vlib_node_registration_t ip4_lookup_node
(constructor) VLIB_REGISTER_NODE (ip4_lookup_node)
Definition: ip4_forward.c:104
format_function_t format_ip4_address
Definition: format.h:81
static vlib_node_registration_t dhcp_client_process_node
(constructor) VLIB_REGISTER_NODE (dhcp_client_process_node)
Definition: client.c:22
u32 lease_renewal_interval
Definition: client.h:67
i16 current_data
signed offset in data[], pre_data[] that we are currently processing.
Definition: buffer.h:104
#define pool_foreach(VAR, POOL, BODY)
Iterate through pool.
Definition: pool.h:443
#define VLIB_INIT_FUNCTION(x)
Definition: init.h:156
static uword vlib_process_get_events(vlib_main_t *vm, uword **data_vector)
Return the first event type which has occurred and a vector of per-event data of that type...
Definition: node_funcs.h:542
ip4_address_t dst_address
Definition: ip4_packet.h:169
dhcp_event_cb_t event_callback
Definition: client.h:89
static u32 counter_index(vlib_main_t *vm, vlib_error_t e)
static uword dhcp_client_process(vlib_main_t *vm, vlib_node_runtime_t *rt, vlib_frame_t *f)
Definition: client.c:732
static vnet_sw_interface_t * vnet_get_sup_sw_interface(vnet_main_t *vnm, u32 sw_if_index)
Aggregrate type for a prefix.
Definition: fib_types.h:188
vlib_frame_t * vlib_get_frame_to_node(vlib_main_t *vm, u32 to_node_index)
Definition: main.c:182
#define clib_error_return(e, args...)
Definition: error.h:99
static int dhcp_bound_state(dhcp_client_main_t *dcm, dhcp_client_t *c, f64 now)
Definition: client.c:619
vhost_vring_state_t state
Definition: vhost_user.h:115
unsigned int u32
Definition: types.h:88
u16 fp_len
The mask length.
Definition: fib_types.h:192
Definition: fib_entry.h:275
u8 set_broadcast_flag
Definition: client.h:84
clib_error_t * ip4_add_del_interface_address(vlib_main_t *vm, u32 sw_if_index, ip4_address_t *address, u32 address_length, u32 is_del)
Definition: ip4_forward.c:631
ip4_address_t leased_address
Definition: client.h:63
static void dhcp_client_acquire_address(dhcp_client_main_t *dcm, dhcp_client_t *c)
Definition: client.c:55
#define hash_get(h, key)
Definition: hash.h:249
dhcp_client_state_t
Definition: client.h:30
#define pool_elt_at_index(p, i)
Returns pointer to element at given index.
Definition: pool.h:464
u16 current_length
Nbytes between current data and the end of this buffer.
Definition: buffer.h:108
static void vlib_process_signal_event(vlib_main_t *vm, uword node_index, uword type_opaque, uword data)
Definition: node_funcs.h:952
vlib_main_t * vlib_main
Definition: client.h:100
static clib_error_t * dhcp_client_init(vlib_main_t *vm)
Definition: client.c:1186
int dhcp_client_config(u32 is_add, u32 client_index, vlib_main_t *vm, u32 sw_if_index, u8 *hostname, u8 *client_id, dhcp_event_cb_t event_callback, u8 set_broadcast_flag, u32 pid)
Add/Delete DHCP clients.
Definition: client.c:975
struct _unformat_input_t unformat_input_t
u8 * l2_rewrite
Definition: client.h:73
unsigned short u16
Definition: types.h:57
void vlib_put_frame_to_node(vlib_main_t *vm, u32 to_node_index, vlib_frame_t *f)
Definition: main.c:191
static void * vlib_buffer_get_current(vlib_buffer_t *b)
Get pointer to current data to process.
Definition: buffer.h:202
static f64 dhcp_client_sm(f64 now, f64 timeout, uword pool_index)
Definition: client.c:686
void(* dhcp_event_cb_t)(u32 client_index, const struct dhcp_client_t_ *client)
Callback function for DHCP complete events.
Definition: client.h:42
#define pool_put(P, E)
Free an object E in pool P.
Definition: pool.h:274
u8 * vnet_build_rewrite_for_sw_interface(vnet_main_t *vnm, u32 sw_if_index, vnet_link_t link_type, const void *dst_address)
Definition: rewrite.c:213
dhcp_client_main_t dhcp_client_main
Definition: client.c:20
u8 client_hardware_address[6]
Definition: client.h:86
static void vlib_node_increment_counter(vlib_main_t *vm, u32 node_index, u32 counter_index, u64 increment)
Definition: node_funcs.h:1168
static clib_error_t * show_dhcp_client_command_fn(vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd)
Definition: client.c:838
ip4_address_t client_ip_address
Definition: dhcp4_packet.h:32
#define VLIB_REGISTER_NODE(x,...)
Definition: node.h:153
#define UNFORMAT_END_OF_INPUT
Definition: format.h:144
svmdb_client_t * c
u16 n_vectors
Definition: node.h:380
static_always_inline uword vlib_get_thread_index(void)
Definition: threads.h:221
vlib_main_t * vm
Definition: buffer.c:294
f64 next_transmit
Definition: client.h:56
#define vec_free(V)
Free vector&#39;s memory (no header).
Definition: vec.h:339
#define clib_warning(format, args...)
Definition: error.h:59
#define clib_memcpy(a, b, c)
Definition: string.h:75
static void dhcp_client_proc_callback(uword *client_index)
Definition: client.c:91
#define pool_is_free_index(P, I)
Use free bitmap to query whether given index is free.
Definition: pool.h:271
#define ARRAY_LEN(x)
Definition: clib.h:59
u32 client_index
Definition: client.h:80
#define VLIB_CLI_COMMAND(x,...)
Definition: cli.h:154
#define EVENT_DHCP_CLIENT_WAKEUP
Definition: client.h:125
#define VNET_SW_INTERFACE_FLAG_ADMIN_UP
Definition: interface.h:661
#define ASSERT(truth)
long ctx[MAX_CONNS]
Definition: main.c:126
u32 transaction_identifier
Definition: dhcp4_packet.h:28
u8 hardware_address_length
Definition: dhcp4_packet.h:26
u32 subnet_mask_width
Definition: client.h:65
void fib_table_entry_path_remove(u32 fib_index, const fib_prefix_t *prefix, fib_source_t source, dpo_proto_t next_hop_proto, const ip46_address_t *next_hop, u32 next_hop_sw_if_index, u32 next_hop_fib_index, u32 next_hop_weight, fib_route_path_flags_t path_flags)
remove one path to an entry (aka route) in the FIB.
Definition: fib_table.c:682
#define DHCP_FLAG_BROADCAST
Definition: dhcp4_packet.h:31
ip4_address_t router_address
Definition: client.h:66
void vl_api_rpc_call_main_thread(void *fp, u8 *data, u32 data_length)
Definition: vlib_api.c:638
static vlib_main_t * vlib_get_main(void)
Definition: global_funcs.h:23
struct _vlib_node_registration vlib_node_registration_t
Definition: defs.h:47
u8 * client_identifier
Definition: client.h:77
ip4_address_t magic_cookie
Definition: dhcp4_packet.h:39
int(* dhcp_client_walk_cb_t)(const dhcp_client_t *client, void *data)
callback function for clients walking the DHCP client configurations
Definition: client.h:151
#define vec_len(v)
Number of elements in vector (rvalue-only, NULL tolerant)
DHCP.
Definition: fib_entry.h:94
u64 uword
Definition: types.h:112
static void * vlib_frame_vector_args(vlib_frame_t *f)
Get pointer to frame vector data.
Definition: node_funcs.h:267
uword * client_by_sw_if_index
Definition: client.h:96
sample_error_t
Definition: client.c:37
int dhcp_client_add_del(dhcp_client_add_del_args_t *a)
Definition: client.c:891
#define vnet_buffer(b)
Definition: buffer.h:360
u8 * hostname
Definition: client.h:76
static u32 random_u32(u32 *seed)
32-bit random number generator
Definition: random.h:69
u8 data[0]
Packet data.
Definition: buffer.h:172
static uword count_set_bits(uword x)
Definition: bitops.h:45
static clib_error_t * dhcp_client_set_command_fn(vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd)
Definition: client.c:1077
f64 lease_expires
Definition: client.h:57
vnet_main_t * vnet_main
Definition: client.h:101
u8 ip_version_and_header_length
Definition: ip4_packet.h:137
dhcp_packet_type_t
Definition: dhcp4_packet.h:54
static u32 vlib_buffer_alloc(vlib_main_t *vm, u32 *buffers, u32 n_buffers)
Allocate buffers into supplied array.
Definition: buffer_funcs.h:490
ip4_address_t dhcp_server
Definition: client.h:64
#define foreach_dhcp_sent_packet_stat
Definition: client.c:24
static vlib_buffer_t * vlib_get_buffer(vlib_main_t *vm, u32 buffer_index)
Translate buffer index into buffer pointer.
Definition: buffer_funcs.h:57
static u16 ip4_header_checksum(ip4_header_t *i)
Definition: ip4_packet.h:246
uword unformat(unformat_input_t *i, const char *fmt,...)
Definition: unformat.c:972
Definition: defs.h:46
ip4_address_t your_ip_address
Definition: dhcp4_packet.h:33
#define foreach_dhcp_error_counter
Definition: client.c:30
int vnet_feature_enable_disable(const char *arc_name, const char *node_name, u32 sw_if_index, int enable_disable, void *feature_config, u32 n_feature_config_bytes)
Definition: feature.c:233
static uword unformat_check_input(unformat_input_t *i)
Definition: format.h:170
static void set_l2_rewrite(dhcp_client_main_t *dcm, dhcp_client_t *c)
Definition: client.c:79
static uword pool_elts(void *v)
Number of active elements in a pool.
Definition: pool.h:128