FD.io VPP  v18.01.1-37-g7ea3975
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_client_process_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 typedef enum
31 {
32 #define _(sym,str) DHCP_STAT_##sym,
34 #undef _
38 
40 #define _(sym,string) string,
42 #undef _
43  "DHCP unknown packets sent",
44 };
45 
46 
47 static void
49 {
50  /* Install a local entry for the offered address */
51  fib_prefix_t rx = {
52  .fp_len = 32,
53  .fp_addr.ip4 = c->leased_address,
54  .fp_proto = FIB_PROTOCOL_IP4,
55  };
56 
58  (FIB_PROTOCOL_IP4, c->sw_if_index), &rx,
60 
61  /* And add the server's address as uRPF exempt so we can accept
62  * local packets from it */
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  fib_prefix_t rx = {
78  .fp_len = 32,
79  .fp_addr.ip4 = c->leased_address,
80  .fp_proto = FIB_PROTOCOL_IP4,
81  };
82 
84  (FIB_PROTOCOL_IP4, c->sw_if_index), &rx,
86  fib_prefix_t server = {
87  .fp_len = 32,
88  .fp_addr.ip4 = c->dhcp_server,
89  .fp_proto = FIB_PROTOCOL_IP4,
90  };
91 
93  (FIB_PROTOCOL_IP4, c->sw_if_index), &server,
95 }
96 
97 static void
99 {
100  /*
101  * Install any/all info gleaned from dhcp, right here
102  */
104  (void *) &c->leased_address,
105  c->subnet_mask_width, 0 /*is_del */ );
106 }
107 
108 static void
110 {
111  /*
112  * Remove any/all info gleaned from dhcp, right here. Caller(s)
113  * have not wiped out the info yet.
114  */
115 
117  (void *) &c->leased_address,
118  c->subnet_mask_width, 1 /*is_del */ );
119 }
120 
121 static void
123 {
124  /* Acquire the L2 rewrite string for the indicated sw_if_index */
126  c->sw_if_index,
128  0 /* broadcast */ );
129 }
130 
131 void vl_api_rpc_call_main_thread (void *fp, u8 * data, u32 data_length);
132 
133 static void
135 {
137  ASSERT (vlib_get_thread_index () == 0);
139  EVENT_DHCP_CLIENT_WAKEUP, *client_index);
140 }
141 
142 static void
144 {
146  void (*fp) (u32, u32, u8 *, u8, u8, u8 *, u8 *, u8 *) = c->event_callback;
147 
148  /* replace the temporary RX address with the correct subnet */
151 
152  /*
153  * Configure default IP route:
154  */
155  if (c->router_address.as_u32)
156  {
157  fib_prefix_t all_0s = {
158  .fp_len = 0,
159  .fp_addr.ip4.as_u32 = 0x0,
160  .fp_proto = FIB_PROTOCOL_IP4,
161  };
162  ip46_address_t nh = {
163  .ip4 = c->router_address,
164  };
165 
166  /* *INDENT-OFF* */
170  c->sw_if_index),
171  &all_0s,
175  &nh, c->sw_if_index,
176  ~0, 1, NULL, // no label stack
178  /* *INDENT-ON* */
179  }
180 
181  /*
182  * Call the user's event callback to report DHCP information
183  */
184  if (fp)
185  (*fp) (c->client_index, /* clinet index */
186  c->pid, c->hostname, c->subnet_mask_width, 0, /* is_ipv6 */
187  (u8 *) & c->leased_address, /* host IP address */
188  (u8 *) & c->router_address, /* router IP address */
189  (u8 *) (c->l2_rewrite + 6)); /* host MAC address */
190 }
191 
192 /*
193  * dhcp_client_for_us - server-to-client callback.
194  * Called from proxy_node.c:dhcp_proxy_to_client_input().
195  * This function first decides that the packet in question is
196  * actually for the dhcp client code in case we're also acting as
197  * a dhcp proxy. Ay caramba, what a folly!
198  */
199 int
201  ip4_header_t * ip,
202  udp_header_t * udp, dhcp_header_t * dhcp)
203 {
205  vlib_main_t *vm = dcm->vlib_main;
206  dhcp_client_t *c;
207  uword *p;
208  f64 now = vlib_time_now (dcm->vlib_main);
209  u8 dhcp_message_type = 0;
210  dhcp_option_t *o;
211 
212  /*
213  * Doing dhcp client on this interface?
214  * Presumably we will always receive dhcp clnt for-us pkts on
215  * the interface that's asking for an address.
216  */
218  vnet_buffer (b)->sw_if_index[VLIB_RX]);
219  if (p == 0)
220  return 0; /* no */
221 
222  c = pool_elt_at_index (dcm->clients, p[0]);
223 
224  /* Mixing dhcp relay and dhcp proxy? DGMS... */
225  if (c->state == DHCP_BOUND && c->retry_count == 0)
226  return 0;
227 
228  /* parse through the packet, learn what we can */
229  if (dhcp->your_ip_address.as_u32)
231 
233 
234  o = (dhcp_option_t *) dhcp->options;
235 
236  while (o->option != 0xFF /* end of options */ &&
237  (u8 *) o < (b->data + b->current_data + b->current_length))
238  {
239  switch (o->option)
240  {
241  case 53: /* dhcp message type */
242  dhcp_message_type = o->data[0];
243  break;
244 
245  case 51: /* lease time */
246  {
247  u32 lease_time_in_seconds =
248  clib_host_to_net_u32 (o->data_as_u32[0]);
249  c->lease_expires = now + (f64) lease_time_in_seconds;
250  c->lease_lifetime = lease_time_in_seconds;
251  /* Set a sensible default, in case we don't get opt 58 */
252  c->lease_renewal_interval = lease_time_in_seconds / 2;
253  }
254  break;
255 
256  case 58: /* lease renew time in seconds */
257  {
258  u32 lease_renew_time_in_seconds =
259  clib_host_to_net_u32 (o->data_as_u32[0]);
260  c->lease_renewal_interval = lease_renew_time_in_seconds;
261  }
262  break;
263 
264  case 54: /* dhcp server address */
265  c->dhcp_server.as_u32 = o->data_as_u32[0];
266  break;
267 
268  case 1: /* subnet mask */
269  {
270  u32 subnet_mask = clib_host_to_net_u32 (o->data_as_u32[0]);
271  c->subnet_mask_width = count_set_bits (subnet_mask);
272  }
273  break;
274  case 3: /* router address */
275  {
276  u32 router_address = o->data_as_u32[0];
277  c->router_address.as_u32 = router_address;
278  }
279  break;
280 
281  case 12: /* hostname */
282  {
283  /* Replace the existing hostname if necessary */
284  vec_free (c->hostname);
285  vec_validate (c->hostname, o->length - 1);
286  clib_memcpy (c->hostname, o->data, o->length);
287  }
288  break;
289 
290  /* $$$$ Your message in this space, parse more options */
291  default:
292  break;
293  }
294 
295  o = (dhcp_option_t *) (((uword) o) + (o->length + 2));
296  }
297 
298  switch (c->state)
299  {
300  case DHCP_DISCOVER:
301  if (dhcp_message_type != DHCP_PACKET_OFFER)
302  {
303  clib_warning ("sw_if_index %d state %U message type %d",
305  c->state, dhcp_message_type);
306  c->next_transmit = now + 5.0;
307  break;
308  }
309  /*
310  * in order to accept unicasted ACKs we need to configure the offered
311  * address on the interface. However, at this point we may not know the
312  * subnet-mask (an OFFER may not contain it). So add a temporary receice
313  * and uRPF excempt entry
314  */
316 
317  /* Received an offer, go send a request */
318  c->state = DHCP_REQUEST;
319  c->retry_count = 0;
320  c->next_transmit = 0; /* send right now... */
321  /* Poke the client process, which will send the request */
322  uword client_id = c - dcm->clients;
324  (u8 *) & client_id, sizeof (uword));
325  break;
326 
327  case DHCP_BOUND:
328  case DHCP_REQUEST:
329  if (dhcp_message_type != DHCP_PACKET_ACK)
330  {
331  clib_warning ("sw_if_index %d state %U message type %d",
333  c->state, dhcp_message_type);
334  c->next_transmit = now + 5.0;
335  break;
336  }
337  /* OK, we own the address (etc), add to the routing table(s) */
338  if (c->state == DHCP_REQUEST)
340  (u8 *) c, sizeof (*c));
341 
342  c->state = DHCP_BOUND;
343  c->retry_count = 0;
344  c->next_transmit = now + (f64) c->lease_renewal_interval;
345  c->lease_expires = now + (f64) c->lease_lifetime;
346  break;
347 
348  default:
349  clib_warning ("client %d bogus state %d", c - dcm->clients, c->state);
350  break;
351  }
352 
353  /* drop the pkt, return 1 */
354  vlib_buffer_free (vm, &bi, 1);
355  return 1;
356 }
357 
358 static void
360  dhcp_packet_type_t type, int is_broadcast)
361 {
362  vlib_main_t *vm = dcm->vlib_main;
363  vnet_main_t *vnm = dcm->vnet_main;
365  vnet_sw_interface_t *sup_sw
368  vlib_buffer_t *b;
369  u32 bi;
370  ip4_header_t *ip;
371  udp_header_t *udp;
372  dhcp_header_t *dhcp;
373  u32 *to_next;
374  vlib_frame_t *f;
375  dhcp_option_t *o;
376  u16 udp_length, ip_length;
378 
379  /* Interface(s) down? */
380  if ((hw->flags & VNET_HW_INTERFACE_FLAG_LINK_UP) == 0)
381  return;
382  if ((sup_sw->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) == 0)
383  return;
384  if ((sw->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) == 0)
385  return;
386 
387  if (vlib_buffer_alloc (vm, &bi, 1) != 1)
388  {
389  clib_warning ("buffer allocation failure");
390  c->next_transmit = 0;
391  return;
392  }
393 
394  /* Build a dhcpv4 pkt from whole cloth */
395  b = vlib_get_buffer (vm, bi);
396 
397  ASSERT (b->current_data == 0);
398 
399  vnet_buffer (b)->sw_if_index[VLIB_RX] = c->sw_if_index;
400  if (is_broadcast)
401  {
403  vnet_buffer (b)->sw_if_index[VLIB_TX] = c->sw_if_index;
405  ip = (void *)
406  (((u8 *) vlib_buffer_get_current (b)) + vec_len (c->l2_rewrite));
407  }
408  else
409  {
410  f = vlib_get_frame_to_node (vm, ip4_lookup_node.index);
411  vnet_buffer (b)->sw_if_index[VLIB_TX] = ~0; /* use interface VRF */
412  ip = vlib_buffer_get_current (b);
413  }
414 
415  /* Enqueue the packet right now */
416  to_next = vlib_frame_vector_args (f);
417  to_next[0] = bi;
418  f->n_vectors = 1;
419 
420  if (is_broadcast)
422  else
424 
425  udp = (udp_header_t *) (ip + 1);
426  dhcp = (dhcp_header_t *) (udp + 1);
427 
428  /* $$$ optimize, maybe */
429  memset (ip, 0, sizeof (*ip) + sizeof (*udp) + sizeof (*dhcp));
430 
431  ip->ip_version_and_header_length = 0x45;
432  ip->ttl = 128;
433  ip->protocol = IP_PROTOCOL_UDP;
434 
435  if (is_broadcast)
436  {
437  /* src = 0.0.0.0, dst = 255.255.255.255 */
438  ip->dst_address.as_u32 = ~0;
439  }
440  else
441  {
442  /* Renewing an active lease, plain old ip4 src/dst */
445  }
446 
447  udp->src_port = clib_host_to_net_u16 (UDP_DST_PORT_dhcp_to_client);
448  udp->dst_port = clib_host_to_net_u16 (UDP_DST_PORT_dhcp_to_server);
449 
450  /* Send the interface MAC 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 = clib_host_to_net_u16 (is_broadcast ? DHCP_FLAG_BROADCAST : 0);
463 
464  o = (dhcp_option_t *) dhcp->options;
465 
466  /* Send option 53, the DHCP message type */
468  o->length = 1;
469  o->data[0] = type;
470  o = (dhcp_option_t *) (((uword) o) + (o->length + 2));
471 
472  /* Send option 57, max msg length */
473  if (0 /* not needed, apparently */ )
474  {
475  o->option = 57;
476  o->length = 2;
477  {
478  u16 *o2 = (u16 *) o->data;
479  *o2 = clib_host_to_net_u16 (1152);
480  o = (dhcp_option_t *) (((uword) o) + (o->length + 2));
481  }
482  }
483 
484  /*
485  * If server ip address is available with non-zero value,
486  * option 54 (DHCP Server Identifier) is sent.
487  */
488  if (c->dhcp_server.as_u32)
489  {
490  o->option = 54;
491  o->length = 4;
492  clib_memcpy (o->data, &c->dhcp_server.as_u32, 4);
493  o = (dhcp_option_t *) (((uword) o) + (o->length + 2));
494  }
495 
496  /* send option 50, requested IP address */
497  if (c->leased_address.as_u32)
498  {
499  o->option = 50;
500  o->length = 4;
501  clib_memcpy (o->data, &c->leased_address.as_u32, 4);
502  o = (dhcp_option_t *) (((uword) o) + (o->length + 2));
503  }
504 
505  /* send option 12, host name */
506  if (vec_len (c->hostname))
507  {
508  o->option = 12;
509  o->length = vec_len (c->hostname);
510  clib_memcpy (o->data, c->hostname, vec_len (c->hostname));
511  o = (dhcp_option_t *) (((uword) o) + (o->length + 2));
512  }
513 
514  /* send option 61, client_id */
515  if (vec_len (c->client_identifier))
516  {
517  o->option = 61;
518  o->length = vec_len (c->client_identifier);
521  o = (dhcp_option_t *) (((uword) o) + (o->length + 2));
522  }
523 
524  /* $$ maybe send the client s/w version if anyone cares */
525 
526  /*
527  * send option 55, parameter request list
528  * The current list - see below, matches the Linux dhcp client's list
529  * Any specific dhcp server config and/or dhcp server may or may
530  * not yield specific options.
531  */
532  o->option = 55;
533  o->length = vec_len (c->option_55_data);
535  o = (dhcp_option_t *) (((uword) o) + (o->length + 2));
536 
537  /* End of list */
538  o->option = 0xff;
539  o->length = 0;
540  o++;
541 
542  b->current_length = ((u8 *) o) - b->data;
543 
544  /* fix ip length, checksum and udp length */
545  ip_length = vlib_buffer_length_in_chain (vm, b);
546  if (is_broadcast)
547  ip_length -= vec_len (c->l2_rewrite);
548 
549  ip->length = clib_host_to_net_u16 (ip_length);
550  ip->checksum = ip4_header_checksum (ip);
551 
552  udp_length = ip_length - (sizeof (*ip));
553  udp->length = clib_host_to_net_u16 (udp_length);
554 
555  switch (type)
556  {
557 #define _(a,b) case DHCP_PACKET_##a: {counter_index = DHCP_STAT_##a; break;}
559 #undef _
560  default:
561  counter_index = DHCP_STAT_UNKNOWN;
562  break;
563  }
564 
566  counter_index, 1);
567 }
568 
569 static int
571 {
572  /*
573  * State machine "DISCOVER" state. Send a dhcp discover packet,
574  * eventually back off the retry rate.
575  */
576  send_dhcp_pkt (dcm, c, DHCP_PACKET_DISCOVER, 1 /* is_broadcast */ );
577 
578  c->retry_count++;
579  if (c->retry_count > 10)
580  c->next_transmit = now + 5.0;
581  else
582  c->next_transmit = now + 1.0;
583  return 0;
584 }
585 
586 static int
588 {
589  /*
590  * State machine "REQUEST" state. Send a dhcp request packet,
591  * eventually drop back to the discover state.
592  */
593  send_dhcp_pkt (dcm, c, DHCP_PACKET_REQUEST, 1 /* is_broadcast */ );
594 
595  c->retry_count++;
596  if (c->retry_count > 7 /* lucky you */ )
597  {
598  c->state = DHCP_DISCOVER;
599  c->next_transmit = now;
600  c->retry_count = 0;
601  return 1;
602  }
603  c->next_transmit = now + 1.0;
604  return 0;
605 }
606 
607 static int
609 {
610  /*
611  * State machine "BOUND" state. Send a dhcp request packet,
612  * eventually, when the lease expires, forget the dhcp data
613  * and go back to the stone age.
614  */
615  send_dhcp_pkt (dcm, c, DHCP_PACKET_REQUEST, 0 /* is_broadcast */ );
616 
617  c->retry_count++;
618  if (c->retry_count > 10)
619  c->next_transmit = now + 5.0;
620  else
621  c->next_transmit = now + 1.0;
622 
623  if (now > c->lease_expires)
624  {
625  /* Remove the default route */
626  if (c->router_address.as_u32)
627  {
628  fib_prefix_t all_0s = {
629  .fp_len = 0,
630  .fp_addr.ip4.as_u32 = 0x0,
631  .fp_proto = FIB_PROTOCOL_IP4,
632  };
633  ip46_address_t nh = {
634  .ip4 = c->router_address,
635  };
636 
639  &all_0s, FIB_SOURCE_DHCP,
640  DPO_PROTO_IP4, &nh, c->sw_if_index, ~0,
642  }
643  /* Remove the interface address */
645  c->state = DHCP_DISCOVER;
646  c->next_transmit = now;
647  c->retry_count = 0;
648  /* Wipe out any memory of the address we had... */
649  c->leased_address.as_u32 = 0;
650  c->subnet_mask_width = 0;
651  c->router_address.as_u32 = 0;
652  c->lease_renewal_interval = 0;
653  c->dhcp_server.as_u32 = 0;
654  /*
655  * We disable the client detect feature when we bind a
656  * DHCP address. Turn it back on again here.
657  * Otherwise, if the DHCP server replies after an outage,
658  * we'll never see it.
659  */
660  vnet_feature_enable_disable ("ip4-unicast",
661  "ip4-dhcp-client-detect",
662  c->sw_if_index, 1, 0, 0);
663  return 1;
664  }
665  return 0;
666 }
667 
668 static f64
669 dhcp_client_sm (f64 now, f64 timeout, uword pool_index)
670 {
672  dhcp_client_t *c;
673 
674  /* deleted, pooched, yadda yadda yadda */
675  if (pool_is_free_index (dcm->clients, pool_index))
676  return timeout;
677 
678  c = pool_elt_at_index (dcm->clients, pool_index);
679 
680  /* Time for us to do something with this client? */
681  if (now < c->next_transmit)
682  return timeout;
683 
684 again:
685  switch (c->state)
686  {
687  case DHCP_DISCOVER: /* send a discover */
688  if (dhcp_discover_state (dcm, c, now))
689  goto again;
690  break;
691 
692  case DHCP_REQUEST: /* send a request */
693  if (dhcp_request_state (dcm, c, now))
694  goto again;
695  break;
696 
697  case DHCP_BOUND: /* bound, renew needed? */
698  if (dhcp_bound_state (dcm, c, now))
699  goto again;
700  break;
701 
702  default:
703  clib_warning ("dhcp client %d bogus state %d",
704  c - dcm->clients, c->state);
705  break;
706  }
707 
708  if (c->next_transmit < now + timeout)
709  return c->next_transmit - now;
710 
711  return timeout;
712 }
713 
714 static uword
717 {
718  f64 timeout = 100.0;
719  f64 now;
720  uword event_type;
721  uword *event_data = 0;
723  dhcp_client_t *c;
724  int i;
725 
726  while (1)
727  {
729 
730  event_type = vlib_process_get_events (vm, &event_data);
731 
732  now = vlib_time_now (vm);
733 
734  switch (event_type)
735  {
737  for (i = 0; i < vec_len (event_data); i++)
738  timeout = dhcp_client_sm (now, timeout, event_data[i]);
739  break;
740 
741  case ~0:
742  pool_foreach (c, dcm->clients, (
743  {
744  timeout =
745  dhcp_client_sm (now, timeout,
746  (uword) (c -
747  dcm->clients));
748  }
749  ));
750  if (pool_elts (dcm->clients) == 0)
751  timeout = 100.0;
752  break;
753  }
754 
755  vec_reset_length (event_data);
756  }
757 
758  /* NOTREACHED */
759  return 0;
760 }
761 
762 /* *INDENT-OFF* */
764  .function = dhcp_client_process,
765  .type = VLIB_NODE_TYPE_PROCESS,
766  .name = "dhcp-client-process",
767  .process_log2_n_stack_bytes = 16,
769  .error_strings = dhcp_client_process_stat_strings,
770 };
771 /* *INDENT-ON* */
772 
773 static u8 *
774 format_dhcp_client_state (u8 * s, va_list * va)
775 {
777  char *str = "BOGUS!";
778 
779  switch (state)
780  {
781 #define _(a) \
782  case a: \
783  str = #a; \
784  break;
786 #undef _
787  default:
788  break;
789  }
790 
791  s = format (s, "%s", str);
792  return s;
793 }
794 
795 static u8 *
796 format_dhcp_client (u8 * s, va_list * va)
797 {
798  dhcp_client_main_t *dcm = va_arg (*va, dhcp_client_main_t *);
799  dhcp_client_t *c = va_arg (*va, dhcp_client_t *);
800  int verbose = va_arg (*va, int);
801 
802  s = format (s, "[%d] %U state %U ", c - dcm->clients,
805 
806  if (c->leased_address.as_u32)
807  s = format (s, "addr %U/%d gw %U\n",
810  else
811  s = format (s, "no address\n");
812 
813  if (verbose)
814  {
815  s = format (s, "retry count %d, next xmt %.2f",
816  c->retry_count, c->next_transmit);
817  }
818  return s;
819 }
820 
821 static clib_error_t *
823  unformat_input_t * input,
824  vlib_cli_command_t * cmd)
825 {
827  dhcp_client_t *c;
828  int verbose = 0;
829  u32 sw_if_index = ~0;
830  uword *p;
831 
833  {
834  if (unformat (input, "intfc %U",
835  unformat_vnet_sw_interface, dcm->vnet_main, &sw_if_index))
836  ;
837  else if (unformat (input, "verbose"))
838  verbose = 1;
839  else
840  break;
841  }
842 
843  if (sw_if_index != ~0)
844  {
845  p = hash_get (dcm->client_by_sw_if_index, sw_if_index);
846  if (p == 0)
847  return clib_error_return (0, "dhcp client not configured");
848  c = pool_elt_at_index (dcm->clients, p[0]);
849  vlib_cli_output (vm, "%U", format_dhcp_client, dcm, c, verbose);
850  return 0;
851  }
852 
853  pool_foreach (c, dcm->clients, (
854  {
855  vlib_cli_output (vm, "%U",
856  format_dhcp_client, dcm,
857  c, verbose);
858  }
859  ));
860 
861  return 0;
862 }
863 
864 /* *INDENT-OFF* */
865 VLIB_CLI_COMMAND (show_dhcp_client_command, static) = {
866  .path = "show dhcp client",
867  .short_help = "show dhcp client [intfc <intfc>][verbose]",
868  .function = show_dhcp_client_command_fn,
869 };
870 /* *INDENT-ON* */
871 
872 
873 int
875 {
877  vlib_main_t *vm = dcm->vlib_main;
878  dhcp_client_t *c;
879  uword *p;
880  fib_prefix_t all_1s = {
881  .fp_len = 32,
882  .fp_addr.ip4.as_u32 = 0xffffffff,
883  .fp_proto = FIB_PROTOCOL_IP4,
884  };
885  fib_prefix_t all_0s = {
886  .fp_len = 0,
887  .fp_addr.ip4.as_u32 = 0x0,
888  .fp_proto = FIB_PROTOCOL_IP4,
889  };
890 
892 
893  if ((p && a->is_add) || (!p && a->is_add == 0))
894  return VNET_API_ERROR_INVALID_VALUE;
895 
896  if (a->is_add)
897  {
898  pool_get (dcm->clients, c);
899  memset (c, 0, sizeof (*c));
900  c->state = DHCP_DISCOVER;
901  c->sw_if_index = a->sw_if_index;
902  c->client_index = a->client_index;
903  c->pid = a->pid;
906  c->hostname = a->hostname;
908  do
909  {
910  c->transaction_id = random_u32 (&dcm->seed);
911  }
912  while (c->transaction_id == 0);
913  set_l2_rewrite (dcm, c);
914  hash_set (dcm->client_by_sw_if_index, a->sw_if_index, c - dcm->clients);
915 
916  /* this add is ref counted by FIB so we can add for each itf */
919  &all_1s, FIB_SOURCE_DHCP,
921 
922  /*
923  * enable the interface to RX IPv4 packets
924  * this is also ref counted
925  */
927 
930  }
931  else
932  {
933  c = pool_elt_at_index (dcm->clients, p[0]);
934 
937  &all_1s, FIB_SOURCE_DHCP);
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  }
954 
956  vec_free (c->hostname);
958  vec_free (c->l2_rewrite);
960  pool_put (dcm->clients, c);
961  }
962  return 0;
963 }
964 
965 int
967  u32 sw_if_index,
968  u8 * hostname,
969  u8 * client_id,
970  u32 is_add,
971  u32 client_index, void *event_callback, u32 pid)
972 {
973  dhcp_client_add_del_args_t _a, *a = &_a;
974  int rv;
975 
976  memset (a, 0, sizeof (*a));
977  a->is_add = is_add;
978  a->sw_if_index = sw_if_index;
979  a->client_index = client_index;
980  a->pid = pid;
981  a->event_callback = event_callback;
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  int is_add = 1;
1059  dhcp_client_add_del_args_t _a, *a = &_a;
1060  int rv;
1061 
1062  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1063  {
1064  if (unformat (input, "intfc %U",
1065  unformat_vnet_sw_interface, dcm->vnet_main, &sw_if_index))
1066  sw_if_index_set = 1;
1067  else if (unformat (input, "hostname %v", &hostname))
1068  ;
1069  else if (unformat (input, "del"))
1070  is_add = 0;
1071  else
1072  break;
1073  }
1074 
1075  if (sw_if_index_set == 0)
1076  return clib_error_return (0, "interface not specified");
1077 
1078  memset (a, 0, sizeof (*a));
1079  a->is_add = is_add;
1080  a->sw_if_index = sw_if_index;
1081  a->hostname = hostname;
1082  a->client_identifier = format (0, "vpe 1.0%c", 0);
1083 
1084  /*
1085  * Option 55 request list. These data precisely match
1086  * the Ubuntu dhcp client. YMMV.
1087  */
1088 
1089  /* Subnet Mask */
1090  vec_add1 (a->option_55_data, 1);
1091  /* Broadcast address */
1092  vec_add1 (a->option_55_data, 28);
1093  /* time offset */
1094  vec_add1 (a->option_55_data, 2);
1095  /* Router */
1096  vec_add1 (a->option_55_data, 3);
1097  /* Domain Name */
1098  vec_add1 (a->option_55_data, 15);
1099  /* DNS */
1100  vec_add1 (a->option_55_data, 6);
1101  /* Domain search */
1102  vec_add1 (a->option_55_data, 119);
1103  /* Host name */
1104  vec_add1 (a->option_55_data, 12);
1105  /* NetBIOS name server */
1106  vec_add1 (a->option_55_data, 44);
1107  /* NetBIOS Scope */
1108  vec_add1 (a->option_55_data, 47);
1109  /* MTU */
1110  vec_add1 (a->option_55_data, 26);
1111  /* Classless static route */
1112  vec_add1 (a->option_55_data, 121);
1113  /* NTP servers */
1114  vec_add1 (a->option_55_data, 42);
1115 
1116  rv = dhcp_client_add_del (a);
1117 
1118  switch (rv)
1119  {
1120  case 0:
1121  break;
1122 
1123  case VNET_API_ERROR_INVALID_VALUE:
1124 
1125  vec_free (a->hostname);
1127  vec_free (a->option_55_data);
1128  if (is_add)
1129  return clib_error_return (0, "dhcp client already enabled on %U",
1131  dcm->vnet_main, sw_if_index);
1132  else
1133  return clib_error_return (0, "dhcp client not enabled on %U",
1135  dcm->vnet_main, sw_if_index);
1136  break;
1137 
1138  default:
1139  vlib_cli_output (vm, "dhcp_client_add_del returned %d", rv);
1140  }
1141 
1142  return 0;
1143 }
1144 
1145 /* *INDENT-OFF* */
1146 VLIB_CLI_COMMAND (dhcp_client_set_command, static) = {
1147  .path = "set dhcp client",
1148  .short_help = "set dhcp client [del] intfc <interface> [hostname <name>]",
1149  .function = dhcp_client_set_command_fn,
1150 };
1151 /* *INDENT-ON* */
1152 
1153 static clib_error_t *
1155 {
1157 
1158  dcm->vlib_main = vm;
1159  dcm->vnet_main = vnet_get_main ();
1160  dcm->seed = 0xdeaddabe;
1161  return 0;
1162 }
1163 
1165 
1166 /*
1167  * fd.io coding-style-patch-verification: ON
1168  *
1169  * Local Variables:
1170  * eval: (c-set-style "gnu")
1171  * End:
1172  */
static u8 * format_dhcp_client(u8 *s, va_list *va)
Definition: client.c:796
#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:966
#define hash_set(h, key, value)
Definition: hash.h:254
sll srl srl sll sra u16x4 i
Definition: vector_sse2.h:337
static void dhcp_client_addr_callback(dhcp_client_t *c)
Definition: client.c:143
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:359
#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:109
static int dhcp_discover_state(dhcp_client_main_t *dcm, dhcp_client_t *c, f64 now)
Definition: client.c:570
#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:200
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:48
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:587
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:774
#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
static char * dhcp_client_process_stat_strings[]
Definition: client.c:39
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
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:715
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:608
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:98
#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:75
static clib_error_t * dhcp_client_init(vlib_main_t *vm)
Definition: client.c:1154
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:669
#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 void vlib_node_increment_counter(vlib_main_t *vm, u32 node_index, u32 counter_index, u64 increment)
Definition: node_funcs.h:1158
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:822
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:134
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
#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
#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
#define foreach_dhcp_client_process_stat
Definition: client.c:24
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
sample_error_t
Definition: client.c:30
int dhcp_client_add_del(dhcp_client_add_del_args_t *a)
Definition: client.c:874
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:1049
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
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:229
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:122
static uword pool_elts(void *v)
Number of active elements in a pool.
Definition: pool.h:128