FD.io VPP  v18.04-17-g3a0d853
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  void (*fp) (u32, u32, u8 *, u8, u8, u8 *, u8 *, u8 *) = c->event_callback;
104 
105  /* disable the feature */
106  vnet_feature_enable_disable ("ip4-unicast",
107  "ip4-dhcp-client-detect",
108  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 (fp)
150  (*fp) (c->client_index, /* clinet index */
151  c->pid, c->hostname, c->subnet_mask_width, 0, /* is_ipv6 */
152  (u8 *) & c->leased_address, /* host IP address */
153  (u8 *) & c->router_address, /* router IP address */
154  (u8 *) (c->l2_rewrite + 6)); /* host MAC address */
155 }
156 
157 /*
158  * dhcp_client_for_us - server-to-client callback.
159  * Called from proxy_node.c:dhcp_proxy_to_client_input().
160  * This function first decides that the packet in question is
161  * actually for the dhcp client code in case we're also acting as
162  * a dhcp proxy. Ay caramba, what a folly!
163  */
164 int
166  ip4_header_t * ip,
167  udp_header_t * udp, dhcp_header_t * dhcp)
168 {
170  vlib_main_t *vm = dcm->vlib_main;
171  dhcp_client_t *c;
172  uword *p;
173  f64 now = vlib_time_now (dcm->vlib_main);
174  u8 dhcp_message_type = 0;
175  dhcp_option_t *o;
176 
177  /*
178  * Doing dhcp client on this interface?
179  * Presumably we will always receive dhcp clnt for-us pkts on
180  * the interface that's asking for an address.
181  */
183  vnet_buffer (b)->sw_if_index[VLIB_RX]);
184  if (p == 0)
185  return 0; /* no */
186 
187  c = pool_elt_at_index (dcm->clients, p[0]);
188 
189  /* Mixing dhcp relay and dhcp proxy? DGMS... */
190  if (c->state == DHCP_BOUND && c->retry_count == 0)
191  return 0;
192 
193  /* Packet not for us? Turf it... */
194  if (memcmp (dhcp->client_hardware_address, c->client_hardware_address,
195  sizeof (c->client_hardware_address)))
196  {
198  DHCP_STAT_NOT_FOR_US, 1);
199  return 0;
200  }
201 
202  /* parse through the packet, learn what we can */
203  if (dhcp->your_ip_address.as_u32)
205 
207 
208  o = (dhcp_option_t *) dhcp->options;
209 
210  while (o->option != 0xFF /* end of options */ &&
211  (u8 *) o < (b->data + b->current_data + b->current_length))
212  {
213  switch (o->option)
214  {
215  case 53: /* dhcp message type */
216  dhcp_message_type = o->data[0];
217  break;
218 
219  case 51: /* lease time */
220  {
221  u32 lease_time_in_seconds =
222  clib_host_to_net_u32 (o->data_as_u32[0]);
223  c->lease_expires = now + (f64) lease_time_in_seconds;
224  c->lease_lifetime = lease_time_in_seconds;
225  /* Set a sensible default, in case we don't get opt 58 */
226  c->lease_renewal_interval = lease_time_in_seconds / 2;
227  }
228  break;
229 
230  case 58: /* lease renew time in seconds */
231  {
232  u32 lease_renew_time_in_seconds =
233  clib_host_to_net_u32 (o->data_as_u32[0]);
234  c->lease_renewal_interval = lease_renew_time_in_seconds;
235  }
236  break;
237 
238  case 54: /* dhcp server address */
239  c->dhcp_server.as_u32 = o->data_as_u32[0];
240  break;
241 
242  case 1: /* subnet mask */
243  {
244  u32 subnet_mask = clib_host_to_net_u32 (o->data_as_u32[0]);
245  c->subnet_mask_width = count_set_bits (subnet_mask);
246  }
247  break;
248  case 3: /* router address */
249  {
250  u32 router_address = o->data_as_u32[0];
251  c->router_address.as_u32 = router_address;
252  }
253  break;
254 
255  case 12: /* hostname */
256  {
257  /* Replace the existing hostname if necessary */
258  vec_free (c->hostname);
259  vec_validate (c->hostname, o->length - 1);
260  clib_memcpy (c->hostname, o->data, o->length);
261  }
262  break;
263 
264  /* $$$$ Your message in this space, parse more options */
265  default:
266  break;
267  }
268 
269  o = (dhcp_option_t *) (((uword) o) + (o->length + 2));
270  }
271 
272  switch (c->state)
273  {
274  case DHCP_DISCOVER:
275  if (dhcp_message_type != DHCP_PACKET_OFFER)
276  {
278  DHCP_STAT_NON_OFFER_DISCOVER, 1);
279  c->next_transmit = now + 5.0;
280  break;
281  }
282 
283  /* Received an offer, go send a request */
284  c->state = DHCP_REQUEST;
285  c->retry_count = 0;
286  c->next_transmit = 0; /* send right now... */
287  /* Poke the client process, which will send the request */
288  uword client_id = c - dcm->clients;
290  (u8 *) & client_id, sizeof (uword));
291  break;
292 
293  case DHCP_BOUND:
294  case DHCP_REQUEST:
295  if (dhcp_message_type == DHCP_PACKET_NAK)
296  {
298  DHCP_STAT_NAK, 1);
299  /* Probably never happens in bound state, but anyhow... */
300  if (c->state == DHCP_BOUND)
301  {
303  (void *) &c->leased_address,
305  1 /*is_del */ );
306  vnet_feature_enable_disable ("ip4-unicast",
307  "ip4-dhcp-client-detect",
308  c->sw_if_index, 1 /* enable */ ,
309  0, 0);
310  }
311  /* Wipe out any memory of the address we had... */
312  c->state = DHCP_DISCOVER;
313  c->next_transmit = now;
314  c->retry_count = 0;
315  c->leased_address.as_u32 = 0;
316  c->subnet_mask_width = 0;
317  c->router_address.as_u32 = 0;
318  c->lease_renewal_interval = 0;
319  c->dhcp_server.as_u32 = 0;
320  break;
321  }
322 
323  if (dhcp_message_type != DHCP_PACKET_ACK &&
324  dhcp_message_type != DHCP_PACKET_OFFER)
325  {
327  DHCP_STAT_NON_OFFER_DISCOVER, 1);
328  clib_warning ("sw_if_index %d state %U message type %d",
330  c->state, dhcp_message_type);
331  c->next_transmit = now + 5.0;
332  break;
333  }
334  /* OK, we own the address (etc), add to the routing table(s) */
336  (u8 *) c, sizeof (*c));
337 
338  c->state = DHCP_BOUND;
339  c->retry_count = 0;
340  c->next_transmit = now + (f64) c->lease_renewal_interval;
341  c->lease_expires = now + (f64) c->lease_lifetime;
343  DHCP_STAT_BOUND, 1);
344  break;
345 
346  default:
347  clib_warning ("client %d bogus state %d", c - dcm->clients, c->state);
348  break;
349  }
350 
351  /* drop the pkt, return 1 */
352  vlib_buffer_free (vm, &bi, 1);
353  return 1;
354 }
355 
356 static void
358  dhcp_packet_type_t type, int is_broadcast)
359 {
360  vlib_main_t *vm = dcm->vlib_main;
361  vnet_main_t *vnm = dcm->vnet_main;
363  vnet_sw_interface_t *sup_sw
366  vlib_buffer_t *b;
367  u32 bi;
368  ip4_header_t *ip;
369  udp_header_t *udp;
370  dhcp_header_t *dhcp;
371  u32 *to_next;
372  vlib_frame_t *f;
373  dhcp_option_t *o;
374  u16 udp_length, ip_length;
376 
377  /* Interface(s) down? */
378  if ((hw->flags & VNET_HW_INTERFACE_FLAG_LINK_UP) == 0)
379  return;
380  if ((sup_sw->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) == 0)
381  return;
382  if ((sw->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) == 0)
383  return;
384 
385  if (vlib_buffer_alloc (vm, &bi, 1) != 1)
386  {
387  clib_warning ("buffer allocation failure");
388  c->next_transmit = 0;
389  return;
390  }
391 
392  /* Build a dhcpv4 pkt from whole cloth */
393  b = vlib_get_buffer (vm, bi);
394 
395  ASSERT (b->current_data == 0);
396 
397  vnet_buffer (b)->sw_if_index[VLIB_RX] = c->sw_if_index;
398  if (is_broadcast)
399  {
401  vnet_buffer (b)->sw_if_index[VLIB_TX] = c->sw_if_index;
403  ip = (void *)
404  (((u8 *) vlib_buffer_get_current (b)) + vec_len (c->l2_rewrite));
405  }
406  else
407  {
408  f = vlib_get_frame_to_node (vm, ip4_lookup_node.index);
409  vnet_buffer (b)->sw_if_index[VLIB_TX] = ~0; /* use interface VRF */
410  ip = vlib_buffer_get_current (b);
411  }
412 
413  /* Enqueue the packet right now */
414  to_next = vlib_frame_vector_args (f);
415  to_next[0] = bi;
416  f->n_vectors = 1;
417 
418  if (is_broadcast)
420  else
422 
423  udp = (udp_header_t *) (ip + 1);
424  dhcp = (dhcp_header_t *) (udp + 1);
425 
426  /* $$$ optimize, maybe */
427  memset (ip, 0, sizeof (*ip) + sizeof (*udp) + sizeof (*dhcp));
428 
429  ip->ip_version_and_header_length = 0x45;
430  ip->ttl = 128;
431  ip->protocol = IP_PROTOCOL_UDP;
432 
433  if (is_broadcast)
434  {
435  /* src = 0.0.0.0, dst = 255.255.255.255 */
436  ip->dst_address.as_u32 = ~0;
437  }
438  else
439  {
440  /* Renewing an active lease, plain old ip4 src/dst */
443  }
444 
445  udp->src_port = clib_host_to_net_u16 (UDP_DST_PORT_dhcp_to_client);
446  udp->dst_port = clib_host_to_net_u16 (UDP_DST_PORT_dhcp_to_server);
447 
448  /* Send the interface MAC address */
450 
451  /* And remember it for rx-packet-for-us checking */
453  sizeof (c->client_hardware_address));
454 
455  /* Lease renewal, set up client_ip_address */
456  if (is_broadcast == 0)
458 
459  dhcp->opcode = 1; /* request, all we send */
460  dhcp->hardware_type = 1; /* ethernet */
461  dhcp->hardware_address_length = 6;
463  dhcp->flags =
464  clib_host_to_net_u16 (is_broadcast && c->set_broadcast_flag ?
465  DHCP_FLAG_BROADCAST : 0);
467 
468  o = (dhcp_option_t *) dhcp->options;
469 
470  /* Send option 53, the DHCP message type */
472  o->length = 1;
473  o->data[0] = type;
474  o = (dhcp_option_t *) (((uword) o) + (o->length + 2));
475 
476  /* Send option 57, max msg length */
477  if (0 /* not needed, apparently */ )
478  {
479  o->option = 57;
480  o->length = 2;
481  {
482  u16 *o2 = (u16 *) o->data;
483  *o2 = clib_host_to_net_u16 (1152);
484  o = (dhcp_option_t *) (((uword) o) + (o->length + 2));
485  }
486  }
487 
488  /*
489  * If server ip address is available with non-zero value,
490  * option 54 (DHCP Server Identifier) is sent.
491  */
492  if (c->dhcp_server.as_u32)
493  {
494  o->option = 54;
495  o->length = 4;
496  clib_memcpy (o->data, &c->dhcp_server.as_u32, 4);
497  o = (dhcp_option_t *) (((uword) o) + (o->length + 2));
498  }
499 
500  /* send option 50, requested IP address */
501  if (c->leased_address.as_u32)
502  {
503  o->option = 50;
504  o->length = 4;
505  clib_memcpy (o->data, &c->leased_address.as_u32, 4);
506  o = (dhcp_option_t *) (((uword) o) + (o->length + 2));
507  }
508 
509  /* send option 12, host name */
510  if (vec_len (c->hostname))
511  {
512  o->option = 12;
513  o->length = vec_len (c->hostname);
514  clib_memcpy (o->data, c->hostname, vec_len (c->hostname));
515  o = (dhcp_option_t *) (((uword) o) + (o->length + 2));
516  }
517 
518  /* send option 61, client_id */
519  if (vec_len (c->client_identifier))
520  {
521  o->option = 61;
522  o->length = vec_len (c->client_identifier);
525  o = (dhcp_option_t *) (((uword) o) + (o->length + 2));
526  }
527 
528  /* $$ maybe send the client s/w version if anyone cares */
529 
530  /*
531  * send option 55, parameter request list
532  * The current list - see below, matches the Linux dhcp client's list
533  * Any specific dhcp server config and/or dhcp server may or may
534  * not yield specific options.
535  */
536  o->option = 55;
537  o->length = vec_len (c->option_55_data);
539  o = (dhcp_option_t *) (((uword) o) + (o->length + 2));
540 
541  /* End of list */
542  o->option = 0xff;
543  o->length = 0;
544  o++;
545 
546  b->current_length = ((u8 *) o) - b->data;
547 
548  /* fix ip length, checksum and udp length */
549  ip_length = vlib_buffer_length_in_chain (vm, b);
550  if (is_broadcast)
551  ip_length -= vec_len (c->l2_rewrite);
552 
553  ip->length = clib_host_to_net_u16 (ip_length);
554  ip->checksum = ip4_header_checksum (ip);
555 
556  udp_length = ip_length - (sizeof (*ip));
557  udp->length = clib_host_to_net_u16 (udp_length);
558 
559  switch (type)
560  {
561 #define _(a,b) case DHCP_PACKET_##a: {counter_index = DHCP_STAT_##a; break;}
563 #undef _
564  default:
565  counter_index = DHCP_STAT_UNKNOWN;
566  break;
567  }
568 
570  counter_index, 1);
571 }
572 
573 static int
575 {
576  /*
577  * State machine "DISCOVER" state. Send a dhcp discover packet,
578  * eventually back off the retry rate.
579  */
580  send_dhcp_pkt (dcm, c, DHCP_PACKET_DISCOVER, 1 /* is_broadcast */ );
581 
582  c->retry_count++;
583  if (c->retry_count > 10)
584  c->next_transmit = now + 5.0;
585  else
586  c->next_transmit = now + 1.0;
587  return 0;
588 }
589 
590 static int
592 {
593  /*
594  * State machine "REQUEST" state. Send a dhcp request packet,
595  * eventually drop back to the discover state.
596  */
597  send_dhcp_pkt (dcm, c, DHCP_PACKET_REQUEST, 1 /* is_broadcast */ );
598 
599  c->retry_count++;
600  if (c->retry_count > 7 /* lucky you */ )
601  {
602  c->state = DHCP_DISCOVER;
603  c->next_transmit = now;
604  c->retry_count = 0;
605  return 1;
606  }
607  c->next_transmit = now + 1.0;
608  return 0;
609 }
610 
611 static int
613 {
614  /*
615  * State machine "BOUND" state. Send a dhcp request packet to renew
616  * the lease.
617  * Eventually, when the lease expires, forget the dhcp data
618  * and go back to the stone age.
619  */
620 
621  /*
622  * We disable the client detect feature when we bind a
623  * DHCP address. Turn it back on again on first renew attempt.
624  * Otherwise, if the DHCP server replies we'll never see it.
625  */
626  if (!c->retry_count)
627  vnet_feature_enable_disable ("ip4-unicast",
628  "ip4-dhcp-client-detect",
629  c->sw_if_index, 1 /* enable */ , 0, 0);
630 
631  send_dhcp_pkt (dcm, c, DHCP_PACKET_REQUEST, 0 /* is_broadcast */ );
632 
633  c->retry_count++;
634  if (c->retry_count > 10)
635  c->next_transmit = now + 5.0;
636  else
637  c->next_transmit = now + 1.0;
638 
639  if (now > c->lease_expires)
640  {
641  /* Remove the default route */
642  if (c->router_address.as_u32)
643  {
644  fib_prefix_t all_0s = {
645  .fp_len = 0,
646  .fp_addr.ip4.as_u32 = 0x0,
647  .fp_proto = FIB_PROTOCOL_IP4,
648  };
649  ip46_address_t nh = {
650  .ip4 = c->router_address,
651  };
652 
655  &all_0s, FIB_SOURCE_DHCP,
656  DPO_PROTO_IP4, &nh, c->sw_if_index, ~0,
658  }
659  /* Remove the interface address */
661  c->state = DHCP_DISCOVER;
662  c->next_transmit = now;
663  c->retry_count = 0;
664  /* Wipe out any memory of the address we had... */
665  c->leased_address.as_u32 = 0;
666  c->subnet_mask_width = 0;
667  c->router_address.as_u32 = 0;
668  c->lease_renewal_interval = 0;
669  c->dhcp_server.as_u32 = 0;
670  return 1;
671  }
672  return 0;
673 }
674 
675 static f64
676 dhcp_client_sm (f64 now, f64 timeout, uword pool_index)
677 {
679  dhcp_client_t *c;
680 
681  /* deleted, pooched, yadda yadda yadda */
682  if (pool_is_free_index (dcm->clients, pool_index))
683  return timeout;
684 
685  c = pool_elt_at_index (dcm->clients, pool_index);
686 
687  /* Time for us to do something with this client? */
688  if (now < c->next_transmit)
689  return timeout;
690 
691 again:
692  switch (c->state)
693  {
694  case DHCP_DISCOVER: /* send a discover */
695  if (dhcp_discover_state (dcm, c, now))
696  goto again;
697  break;
698 
699  case DHCP_REQUEST: /* send a request */
700  if (dhcp_request_state (dcm, c, now))
701  goto again;
702  break;
703 
704  case DHCP_BOUND: /* bound, renew needed? */
705  if (dhcp_bound_state (dcm, c, now))
706  goto again;
707  break;
708 
709  default:
710  clib_warning ("dhcp client %d bogus state %d",
711  c - dcm->clients, c->state);
712  break;
713  }
714 
715  if (c->next_transmit < now + timeout)
716  return c->next_transmit - now;
717 
718  return timeout;
719 }
720 
721 static uword
724 {
725  f64 timeout = 100.0;
726  f64 now;
727  uword event_type;
728  uword *event_data = 0;
730  dhcp_client_t *c;
731  int i;
732 
733  while (1)
734  {
736 
737  event_type = vlib_process_get_events (vm, &event_data);
738 
739  now = vlib_time_now (vm);
740 
741  switch (event_type)
742  {
744  for (i = 0; i < vec_len (event_data); i++)
745  timeout = dhcp_client_sm (now, timeout, event_data[i]);
746  break;
747 
748  case ~0:
749  /* *INDENT-OFF* */
750  pool_foreach (c, dcm->clients,
751  ({
752  timeout = dhcp_client_sm (now, timeout,
753  (uword) (c - dcm->clients));
754  }));
755  /* *INDENT-ON* */
756  if (pool_elts (dcm->clients) == 0)
757  timeout = 100.0;
758  break;
759  }
760 
761  vec_reset_length (event_data);
762  }
763 
764  /* NOTREACHED */
765  return 0;
766 }
767 
768 /* *INDENT-OFF* */
770  .function = dhcp_client_process,
771  .type = VLIB_NODE_TYPE_PROCESS,
772  .name = "dhcp-client-process",
773  .process_log2_n_stack_bytes = 16,
775  .error_strings = dhcp_client_process_stat_strings,
776 };
777 /* *INDENT-ON* */
778 
779 static u8 *
780 format_dhcp_client_state (u8 * s, va_list * va)
781 {
783  char *str = "BOGUS!";
784 
785  switch (state)
786  {
787 #define _(a) \
788  case a: \
789  str = #a; \
790  break;
792 #undef _
793  default:
794  break;
795  }
796 
797  s = format (s, "%s", str);
798  return s;
799 }
800 
801 static u8 *
802 format_dhcp_client (u8 * s, va_list * va)
803 {
804  dhcp_client_main_t *dcm = va_arg (*va, dhcp_client_main_t *);
805  dhcp_client_t *c = va_arg (*va, dhcp_client_t *);
806  int verbose = va_arg (*va, int);
807 
808  s = format (s, "[%d] %U state %U ", c - dcm->clients,
811 
812  if (c->leased_address.as_u32)
813  s = format (s, "addr %U/%d gw %U\n",
816  else
817  s = format (s, "no address\n");
818 
819  if (verbose)
820  {
821  s = format (s, "retry count %d, next xmt %.2f",
822  c->retry_count, c->next_transmit);
823  }
824  return s;
825 }
826 
827 static clib_error_t *
829  unformat_input_t * input,
830  vlib_cli_command_t * cmd)
831 {
833  dhcp_client_t *c;
834  int verbose = 0;
835  u32 sw_if_index = ~0;
836  uword *p;
837 
839  {
840  if (unformat (input, "intfc %U",
841  unformat_vnet_sw_interface, dcm->vnet_main, &sw_if_index))
842  ;
843  else if (unformat (input, "verbose"))
844  verbose = 1;
845  else
846  break;
847  }
848 
849  if (sw_if_index != ~0)
850  {
851  p = hash_get (dcm->client_by_sw_if_index, sw_if_index);
852  if (p == 0)
853  return clib_error_return (0, "dhcp client not configured");
854  c = pool_elt_at_index (dcm->clients, p[0]);
855  vlib_cli_output (vm, "%U", format_dhcp_client, dcm, c, verbose);
856  return 0;
857  }
858 
859  /* *INDENT-OFF* */
860  pool_foreach (c, dcm->clients,
861  ({
862  vlib_cli_output (vm, "%U",
863  format_dhcp_client, dcm,
864  c, verbose);
865  }));
866  /* *INDENT-ON* */
867 
868  return 0;
869 }
870 
871 /* *INDENT-OFF* */
872 VLIB_CLI_COMMAND (show_dhcp_client_command, static) = {
873  .path = "show dhcp client",
874  .short_help = "show dhcp client [intfc <intfc>][verbose]",
875  .function = show_dhcp_client_command_fn,
876 };
877 /* *INDENT-ON* */
878 
879 
880 int
882 {
884  vlib_main_t *vm = dcm->vlib_main;
885  dhcp_client_t *c;
886  uword *p;
887  fib_prefix_t all_0s = {
888  .fp_len = 0,
889  .fp_addr.ip4.as_u32 = 0x0,
890  .fp_proto = FIB_PROTOCOL_IP4,
891  };
892 
894 
895  if ((p && a->is_add) || (!p && a->is_add == 0))
896  return VNET_API_ERROR_INVALID_VALUE;
897 
898  if (a->is_add)
899  {
900  pool_get (dcm->clients, c);
901  memset (c, 0, sizeof (*c));
902  c->state = DHCP_DISCOVER;
903  c->sw_if_index = a->sw_if_index;
904  c->client_index = a->client_index;
905  c->pid = a->pid;
908  c->hostname = a->hostname;
911  do
912  {
913  c->transaction_id = random_u32 (&dcm->seed);
914  }
915  while (c->transaction_id == 0);
916  set_l2_rewrite (dcm, c);
917  hash_set (dcm->client_by_sw_if_index, a->sw_if_index, c - dcm->clients);
918 
919  /*
920  * In order to accept any OFFER, whether broadcasted or unicasted, we
921  * need to configure the dhcp-client-detect feature as an input feature
922  * so the DHCP OFFER is sent to the ip4-local node. Without this a
923  * broadcasted OFFER hits the 255.255.255.255/32 address and a unicast
924  * hits 0.0.0.0/0 both of which default to drop and the latter may forward
925  * of box - not what we want. Nor to we want to change these route for
926  * all interfaces in this table
927  */
928  vnet_feature_enable_disable ("ip4-unicast",
929  "ip4-dhcp-client-detect",
930  c->sw_if_index, 1 /* enable */ , 0, 0);
931 
934  }
935  else
936  {
937  c = pool_elt_at_index (dcm->clients, p[0]);
938 
939  if (c->router_address.as_u32)
940  {
941  ip46_address_t nh = {
942  .ip4 = c->router_address,
943  };
944 
947  &all_0s, FIB_SOURCE_DHCP,
948  DPO_PROTO_IP4, &nh, c->sw_if_index, ~0,
950  }
952 
954  vec_free (c->hostname);
956  vec_free (c->l2_rewrite);
958  pool_put (dcm->clients, c);
959  }
960  return 0;
961 }
962 
963 int
965  u32 sw_if_index,
966  u8 * hostname,
967  u8 * client_id,
968  u32 is_add,
969  u32 client_index,
970  void *event_callback, u8 set_broadcast_flag, u32 pid)
971 {
972  dhcp_client_add_del_args_t _a, *a = &_a;
973  int rv;
974 
975  memset (a, 0, sizeof (*a));
976  a->is_add = is_add;
977  a->sw_if_index = sw_if_index;
978  a->client_index = client_index;
979  a->pid = pid;
980  a->event_callback = event_callback;
981  a->set_broadcast_flag = set_broadcast_flag;
982  vec_validate (a->hostname, strlen ((char *) hostname) - 1);
983  strncpy ((char *) a->hostname, (char *) hostname, vec_len (a->hostname));
984  vec_validate (a->client_identifier, strlen ((char *) client_id) - 1);
985  strncpy ((char *) a->client_identifier, (char *) client_id,
987 
988  /*
989  * Option 55 request list. These data precisely match
990  * the Ubuntu dhcp client. YMMV.
991  */
992 
993  /* Subnet Mask */
994  vec_add1 (a->option_55_data, 1);
995  /* Broadcast address */
996  vec_add1 (a->option_55_data, 28);
997  /* time offset */
998  vec_add1 (a->option_55_data, 2);
999  /* Router */
1000  vec_add1 (a->option_55_data, 3);
1001  /* Domain Name */
1002  vec_add1 (a->option_55_data, 15);
1003  /* DNS */
1004  vec_add1 (a->option_55_data, 6);
1005  /* Domain search */
1006  vec_add1 (a->option_55_data, 119);
1007  /* Host name */
1008  vec_add1 (a->option_55_data, 12);
1009  /* NetBIOS name server */
1010  vec_add1 (a->option_55_data, 44);
1011  /* NetBIOS Scope */
1012  vec_add1 (a->option_55_data, 47);
1013  /* MTU */
1014  vec_add1 (a->option_55_data, 26);
1015  /* Classless static route */
1016  vec_add1 (a->option_55_data, 121);
1017  /* NTP servers */
1018  vec_add1 (a->option_55_data, 42);
1019 
1020  rv = dhcp_client_add_del (a);
1021 
1022  switch (rv)
1023  {
1024  case 0:
1025  break;
1026 
1027  case VNET_API_ERROR_INVALID_VALUE:
1028 
1029  vec_free (a->hostname);
1031  vec_free (a->option_55_data);
1032 
1033  if (is_add)
1034  clib_warning ("dhcp client already enabled on intf_idx %d",
1035  sw_if_index);
1036  else
1037  clib_warning ("dhcp client not enabled on on intf_idx %d",
1038  sw_if_index);
1039  break;
1040 
1041  default:
1042  clib_warning ("dhcp_client_add_del returned %d", rv);
1043  }
1044 
1045  return rv;
1046 }
1047 
1048 static clib_error_t *
1050  unformat_input_t * input,
1051  vlib_cli_command_t * cmd)
1052 {
1053 
1055  u32 sw_if_index;
1056  u8 *hostname = 0;
1057  u8 sw_if_index_set = 0;
1058  u8 set_broadcast_flag = 1;
1059  int is_add = 1;
1060  dhcp_client_add_del_args_t _a, *a = &_a;
1061  int rv;
1062 
1063  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1064  {
1065  if (unformat (input, "intfc %U",
1066  unformat_vnet_sw_interface, dcm->vnet_main, &sw_if_index))
1067  sw_if_index_set = 1;
1068  else if (unformat (input, "hostname %v", &hostname))
1069  ;
1070  else if (unformat (input, "del"))
1071  is_add = 0;
1072  else if (unformat (input, "broadcast", &set_broadcast_flag))
1073  is_add = 0;
1074  else
1075  break;
1076  }
1077 
1078  if (sw_if_index_set == 0)
1079  return clib_error_return (0, "interface not specified");
1080 
1081  memset (a, 0, sizeof (*a));
1082  a->is_add = is_add;
1083  a->sw_if_index = sw_if_index;
1084  a->hostname = hostname;
1085  a->client_identifier = format (0, "vpe 1.0%c", 0);
1086  a->set_broadcast_flag = set_broadcast_flag;
1087 
1088  /*
1089  * Option 55 request list. These data precisely match
1090  * the Ubuntu dhcp client. YMMV.
1091  */
1092 
1093  /* Subnet Mask */
1094  vec_add1 (a->option_55_data, 1);
1095  /* Broadcast address */
1096  vec_add1 (a->option_55_data, 28);
1097  /* time offset */
1098  vec_add1 (a->option_55_data, 2);
1099  /* Router */
1100  vec_add1 (a->option_55_data, 3);
1101  /* Domain Name */
1102  vec_add1 (a->option_55_data, 15);
1103  /* DNS */
1104  vec_add1 (a->option_55_data, 6);
1105  /* Domain search */
1106  vec_add1 (a->option_55_data, 119);
1107  /* Host name */
1108  vec_add1 (a->option_55_data, 12);
1109  /* NetBIOS name server */
1110  vec_add1 (a->option_55_data, 44);
1111  /* NetBIOS Scope */
1112  vec_add1 (a->option_55_data, 47);
1113  /* MTU */
1114  vec_add1 (a->option_55_data, 26);
1115  /* Classless static route */
1116  vec_add1 (a->option_55_data, 121);
1117  /* NTP servers */
1118  vec_add1 (a->option_55_data, 42);
1119 
1120  rv = dhcp_client_add_del (a);
1121 
1122  switch (rv)
1123  {
1124  case 0:
1125  break;
1126 
1127  case VNET_API_ERROR_INVALID_VALUE:
1128 
1129  vec_free (a->hostname);
1131  vec_free (a->option_55_data);
1132  if (is_add)
1133  return clib_error_return (0, "dhcp client already enabled on %U",
1135  dcm->vnet_main, sw_if_index);
1136  else
1137  return clib_error_return (0, "dhcp client not enabled on %U",
1139  dcm->vnet_main, sw_if_index);
1140  break;
1141 
1142  default:
1143  vlib_cli_output (vm, "dhcp_client_add_del returned %d", rv);
1144  }
1145 
1146  return 0;
1147 }
1148 
1149 /* *INDENT-OFF* */
1150 VLIB_CLI_COMMAND (dhcp_client_set_command, static) = {
1151  .path = "set dhcp client",
1152  .short_help = "set dhcp client [del] intfc <interface> [hostname <name>]",
1153  .function = dhcp_client_set_command_fn,
1154 };
1155 /* *INDENT-ON* */
1156 
1157 static clib_error_t *
1159 {
1161 
1162  dcm->vlib_main = vm;
1163  dcm->vnet_main = vnet_get_main ();
1164  dcm->seed = 0xdeaddabe;
1165  return 0;
1166 }
1167 
1169 
1170 /*
1171  * fd.io coding-style-patch-verification: ON
1172  *
1173  * Local Variables:
1174  * eval: (c-set-style "gnu")
1175  * End:
1176  */
static u8 * format_dhcp_client(u8 *s, va_list *va)
Definition: client.c:802
#define vec_validate(V, I)
Make sure vector is long enough for given index (no header, unspecified alignment) ...
Definition: vec.h:434
u32 retry_count
Definition: client.h:45
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:514
#define hash_set(h, key, value)
Definition: hash.h:254
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:260
a
Definition: bitmap.h:516
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:391
ip4_address_t src_address
Definition: ip4_packet.h:164
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)
f64 next_transmit
Definition: client.h:48
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:357
#define NULL
Definition: clib.h:55
static f64 vlib_time_now(vlib_main_t *vm)
Definition: main.h:227
u32 transaction_id
Definition: client.h:52
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:940
u8 * hostname
Definition: client.h:68
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:574
#define vec_add1(V, E)
Add 1 element to end of vector (unspecified alignment).
Definition: vec.h:520
dhcp_client_t * clients
Definition: client.h:87
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:165
int i
#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 vlib_cli_output(struct vlib_main_t *vm, char *fmt,...)
Definition: client.c:106
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:591
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:780
#define VNET_HW_INTERFACE_FLAG_LINK_UP
Definition: interface.h:390
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:107
#define pool_get(P, E)
Allocate an object E from a pool P (unspecified alignment).
Definition: pool.h:227
static char * dhcp_client_process_stat_strings[]
Definition: client.c:46
format_function_t format_vnet_sw_if_index_name
u32 data_as_u32[0]
Definition: dhcp4_packet.h:50
#define vec_reset_length(v)
Reset vector length to zero NULL-pointer tolerant.
vlib_node_registration_t ip4_lookup_node
(constructor) VLIB_REGISTER_NODE (ip4_lookup_node)
Definition: ip4_forward.c:103
format_function_t format_ip4_address
Definition: format.h:79
static vlib_node_registration_t dhcp_client_process_node
(constructor) VLIB_REGISTER_NODE (dhcp_client_process_node)
Definition: client.c:22
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:440
#define VLIB_INIT_FUNCTION(x)
Definition: init.h:111
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:164
static u32 counter_index(vlib_main_t *vm, vlib_error_t e)
u32 lease_lifetime
Definition: client.h:60
static uword dhcp_client_process(vlib_main_t *vm, vlib_node_runtime_t *rt, vlib_frame_t *f)
Definition: client.c:722
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:612
u8 * l2_rewrite
Definition: client.h:65
u16 fp_len
The mask length.
Definition: fib_types.h:192
u32 client_index
Definition: client.h:72
u8 set_broadcast_flag
Definition: client.h:76
Definition: fib_entry.h:270
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:617
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:248
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:461
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:92
u32 subnet_mask_width
Definition: client.h:57
static clib_error_t * dhcp_client_init(vlib_main_t *vm)
Definition: client.c:1158
struct _unformat_input_t unformat_input_t
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:209
static f64 dhcp_client_sm(f64 now, f64 timeout, uword pool_index)
Definition: client.c:676
#define pool_put(P, E)
Free an object E in pool P.
Definition: pool.h:273
int dhcp_client_config(vlib_main_t *vm, u32 sw_if_index, u8 *hostname, u8 *client_id, u32 is_add, u32 client_index, void *event_callback, u8 set_broadcast_flag, u32 pid)
Definition: client.c:964
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:210
dhcp_client_main_t dhcp_client_main
Definition: client.c:20
static void vlib_node_increment_counter(vlib_main_t *vm, u32 node_index, u32 counter_index, u64 increment)
Definition: node_funcs.h:1166
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:828
ip4_address_t client_ip_address
Definition: dhcp4_packet.h:32
#define VLIB_REGISTER_NODE(x,...)
Definition: node.h:143
#define UNFORMAT_END_OF_INPUT
Definition: format.h:143
svmdb_client_t * c
u16 n_vectors
Definition: node.h:344
static_always_inline uword vlib_get_thread_index(void)
Definition: threads.h:221
vlib_main_t * vm
Definition: buffer.c:294
#define vec_free(V)
Free vector&#39;s memory (no header).
Definition: vec.h:336
#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
dhcp_client_state_t state
Definition: client.h:39
#define pool_is_free_index(P, I)
Use free bitmap to query whether given index is free.
Definition: pool.h:270
#define ARRAY_LEN(x)
Definition: clib.h:59
u8 * option_55_data
Definition: client.h:63
#define VLIB_CLI_COMMAND(x,...)
Definition: cli.h:154
u8 client_hardware_address[6]
Definition: client.h:78
#define EVENT_DHCP_CLIENT_WAKEUP
Definition: client.h:117
#define VNET_SW_INTERFACE_FLAG_ADMIN_UP
Definition: interface.h:588
#define ASSERT(truth)
unsigned int u32
Definition: types.h:88
u32 lease_renewal_interval
Definition: client.h:59
u32 sw_if_index
Definition: client.h:42
vhost_vring_state_t state
Definition: vhost-user.h:82
u32 transaction_identifier
Definition: dhcp4_packet.h:28
u8 hardware_address_length
Definition: dhcp4_packet.h:26
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:674
#define DHCP_FLAG_BROADCAST
Definition: dhcp4_packet.h:31
void vl_api_rpc_call_main_thread(void *fp, u8 *data, u32 data_length)
Definition: vlib_api.c:644
static vlib_main_t * vlib_get_main(void)
Definition: global_funcs.h:23
u64 uword
Definition: types.h:112
f64 lease_expires
Definition: client.h:49
struct _vlib_node_registration vlib_node_registration_t
Definition: defs.h:47
unsigned short u16
Definition: types.h:57
ip4_address_t magic_cookie
Definition: dhcp4_packet.h:39
#define vec_len(v)
Number of elements in vector (rvalue-only, NULL tolerant)
double f64
Definition: types.h:142
unsigned char u8
Definition: types.h:56
DHCP.
Definition: fib_entry.h:90
ip4_address_t dhcp_server
Definition: client.h:56
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:88
sample_error_t
Definition: client.c:37
int dhcp_client_add_del(dhcp_client_add_del_args_t *a)
Definition: client.c:881
void * event_callback
Definition: client.h:81
#define vnet_buffer(b)
Definition: buffer.h:372
static u32 random_u32(u32 *seed)
32-bit random number generator
Definition: random.h:69
u8 data[0]
Packet data.
Definition: buffer.h:179
static uword count_set_bits(uword x)
Definition: bitops.h:45
ip4_address_t router_address
Definition: client.h:58
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:1049
vnet_main_t * vnet_main
Definition: client.h:93
u8 ip_version_and_header_length
Definition: ip4_packet.h:132
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:347
#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
ip4_address_t leased_address
Definition: client.h:55
static u16 ip4_header_checksum(ip4_header_t *i)
Definition: ip4_packet.h:239
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:169
u8 * client_identifier
Definition: client.h:69
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