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