blob: 5986438b85f6ce2a80e889b0168da0784c70de25 [file] [log] [blame]
Ed Warnickecb9cada2015-12-08 15:45:58 -07001/*
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>
Neale Ranns20a175a2017-02-14 07:28:41 -080016#include <vnet/dhcp/client.h>
Neale Ranns2dd68522017-02-16 03:38:59 -080017#include <vnet/dhcp/dhcp_proxy.h>
Neale Ranns0bfe5d82016-08-25 15:29:12 +010018#include <vnet/fib/fib_table.h>
Ed Warnickecb9cada2015-12-08 15:45:58 -070019
20dhcp_client_main_t dhcp_client_main;
21static u8 * format_dhcp_client_state (u8 * s, va_list * va);
22static vlib_node_registration_t dhcp_client_process_node;
23
Ed Warnickecb9cada2015-12-08 15:45:58 -070024static void
Neale Ranns808c5b22017-08-02 05:15:07 -070025dhcp_client_add_rx_address (dhcp_client_main_t * dcm, dhcp_client_t * c)
26{
27 /* Install a local entry for the offered address */
28 fib_prefix_t rx =
29 {
30 .fp_len = 32,
31 .fp_addr.ip4 = c->leased_address,
32 .fp_proto = FIB_PROTOCOL_IP4,
33 };
34
35 fib_table_entry_special_add(fib_table_get_index_for_sw_if_index(
36 FIB_PROTOCOL_IP4,
37 c->sw_if_index),
38 &rx,
39 FIB_SOURCE_DHCP,
40 (FIB_ENTRY_FLAG_LOCAL));
41
42 /* And add the server's address as uRPF exempt so we can accept
43 * local packets from it */
44 fib_prefix_t server =
45 {
46 .fp_len = 32,
47 .fp_addr.ip4 = c->dhcp_server,
48 .fp_proto = FIB_PROTOCOL_IP4,
49 };
50
51 fib_table_entry_special_add(fib_table_get_index_for_sw_if_index(
52 FIB_PROTOCOL_IP4,
53 c->sw_if_index),
54 &server,
55 FIB_SOURCE_URPF_EXEMPT,
56 (FIB_ENTRY_FLAG_DROP));
57}
58
59static void
60dhcp_client_remove_rx_address (dhcp_client_main_t * dcm, dhcp_client_t * c)
61{
62 fib_prefix_t rx =
63 {
64 .fp_len = 32,
65 .fp_addr.ip4 = c->leased_address,
66 .fp_proto = FIB_PROTOCOL_IP4,
67 };
68
69 fib_table_entry_special_remove(fib_table_get_index_for_sw_if_index(
70 FIB_PROTOCOL_IP4,
71 c->sw_if_index),
72 &rx,
73 FIB_SOURCE_DHCP);
74 fib_prefix_t server =
75 {
76 .fp_len = 32,
77 .fp_addr.ip4 = c->dhcp_server,
78 .fp_proto = FIB_PROTOCOL_IP4,
79 };
80
81 fib_table_entry_special_remove(fib_table_get_index_for_sw_if_index(
82 FIB_PROTOCOL_IP4,
83 c->sw_if_index),
84 &server,
85 FIB_SOURCE_URPF_EXEMPT);
86}
87
88static void
Ed Warnickecb9cada2015-12-08 15:45:58 -070089dhcp_client_acquire_address (dhcp_client_main_t * dcm, dhcp_client_t * c)
90{
91 /*
92 * Install any/all info gleaned from dhcp, right here
93 */
94 ip4_add_del_interface_address (dcm->vlib_main, c->sw_if_index,
95 (void *) &c->leased_address,
96 c->subnet_mask_width, 0 /*is_del*/);
97}
98
99static void
100dhcp_client_release_address (dhcp_client_main_t * dcm, dhcp_client_t * c)
101{
102 /*
103 * Remove any/all info gleaned from dhcp, right here. Caller(s)
104 * have not wiped out the info yet.
105 */
106
107 ip4_add_del_interface_address (dcm->vlib_main, c->sw_if_index,
108 (void *) &c->leased_address,
109 c->subnet_mask_width, 1 /*is_del*/);
110}
111
Neale Rannsb80c5362016-10-08 13:03:40 +0100112static void
113set_l2_rewrite (dhcp_client_main_t * dcm, dhcp_client_t * c)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700114{
Ed Warnickecb9cada2015-12-08 15:45:58 -0700115 /* Acquire the L2 rewrite string for the indicated sw_if_index */
Neale Rannsb80c5362016-10-08 13:03:40 +0100116 c->l2_rewrite = vnet_build_rewrite_for_sw_interface(
117 dcm->vnet_main,
118 c->sw_if_index,
119 VNET_LINK_IP4,
120 0 /* broadcast */);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700121}
122
John Lo609707e2017-09-19 21:45:10 -0400123void vl_api_rpc_call_main_thread (void *fp, u8 * data, u32 data_length);
124
125static void
126dhcp_client_proc_callback (uword * client_index)
127{
128 vlib_main_t *vm = vlib_get_main ();
129 ASSERT (vlib_get_thread_index () == 0);
130 vlib_process_signal_event (vm, dhcp_client_process_node.index,
131 EVENT_DHCP_CLIENT_WAKEUP, *client_index);
132}
133
Ed Warnickecb9cada2015-12-08 15:45:58 -0700134/*
135 * dhcp_client_for_us - server-to-client callback.
136 * Called from proxy_node.c:dhcp_proxy_to_client_input().
137 * This function first decides that the packet in question is
138 * actually for the dhcp client code in case we're also acting as
139 * a dhcp proxy. Ay caramba, what a folly!
140 */
141int dhcp_client_for_us (u32 bi, vlib_buffer_t * b,
142 ip4_header_t * ip,
143 udp_header_t * udp,
144 dhcp_header_t * dhcp)
145{
146 dhcp_client_main_t * dcm = &dhcp_client_main;
147 vlib_main_t * vm = dcm->vlib_main;
148 dhcp_client_t * c;
149 uword * p;
150 f64 now = vlib_time_now (dcm->vlib_main);
151 u8 dhcp_message_type = 0;
152 dhcp_option_t * o;
153
154 /*
155 * Doing dhcp client on this interface?
156 * Presumably we will always receive dhcp clnt for-us pkts on
157 * the interface that's asking for an address.
158 */
159 p = hash_get (dcm->client_by_sw_if_index,
160 vnet_buffer(b)->sw_if_index [VLIB_RX]);
161 if (p == 0)
162 return 0; /* no */
163
164 c = pool_elt_at_index (dcm->clients, p[0]);
165
166 /* Mixing dhcp relay and dhcp proxy? DGMS... */
167 if (c->state == DHCP_BOUND && c->retry_count == 0)
168 return 0;
169
170 /* parse through the packet, learn what we can */
171 if (dhcp->your_ip_address.as_u32)
172 c->leased_address.as_u32 = dhcp->your_ip_address.as_u32;
Neale Ranns808c5b22017-08-02 05:15:07 -0700173
174 c->dhcp_server.as_u32 = dhcp->server_ip_address.as_u32;
175
Ed Warnickecb9cada2015-12-08 15:45:58 -0700176 o = (dhcp_option_t *) dhcp->options;
177
178 while (o->option != 0xFF /* end of options */ &&
179 (u8 *) o < (b->data + b->current_data + b->current_length))
180 {
181 switch (o->option)
182 {
183 case 53: /* dhcp message type */
184 dhcp_message_type = o->data[0];
185 break;
186
187 case 51: /* lease time */
188 {
189 u32 lease_time_in_seconds =
190 clib_host_to_net_u32 (o->data_as_u32[0]);
191 c->lease_expires = now + (f64) lease_time_in_seconds;
192 c->lease_lifetime = lease_time_in_seconds;
193 /* Set a sensible default, in case we don't get opt 58 */
194 c->lease_renewal_interval = lease_time_in_seconds / 2;
195 }
196 break;
197
198 case 58: /* lease renew time in seconds */
199 {
200 u32 lease_renew_time_in_seconds =
201 clib_host_to_net_u32 (o->data_as_u32[0]);
202 c->lease_renewal_interval = lease_renew_time_in_seconds;
203 }
204 break;
205
206 case 54: /* dhcp server address */
207 c->dhcp_server.as_u32 = o->data_as_u32[0];
208 break;
209
210 case 1: /* subnet mask */
211 {
212 u32 subnet_mask =
213 clib_host_to_net_u32 (o->data_as_u32[0]);
214 c->subnet_mask_width = count_set_bits (subnet_mask);
215 }
216 break;
217 case 3: /* router address */
218 {
219 u32 router_address = o->data_as_u32[0];
220 c->router_address.as_u32 = router_address;
221 }
222 break;
223
224 case 12: /* hostname */
225 {
226 /* Replace the existing hostname if necessary */
227 vec_free (c->hostname);
228 vec_validate (c->hostname, o->length - 1);
Damjan Marionf1213b82016-03-13 02:22:06 +0100229 clib_memcpy (c->hostname, o->data, o->length);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700230 }
231 break;
232
233 /* $$$$ Your message in this space, parse more options */
234 default:
235 break;
236 }
237
238 o = (dhcp_option_t *) (((uword) o) + (o->length + 2));
239 }
240
241 switch (c->state)
242 {
243 case DHCP_DISCOVER:
244 if (dhcp_message_type != DHCP_PACKET_OFFER)
245 {
246 clib_warning ("sw_if_index %d state %U message type %d",
247 c->sw_if_index, format_dhcp_client_state,
248 c->state, dhcp_message_type);
249 c->next_transmit = now + 5.0;
250 break;
251 }
Neale Ranns808c5b22017-08-02 05:15:07 -0700252 /*
253 * in order to accept unicasted ACKs we need to configure the offered
254 * address on the interface. However, at this point we may not know the
255 * subnet-mask (an OFFER may not contain it). So add a temporary receice
256 * and uRPF excempt entry
257 */
258 dhcp_client_add_rx_address (dcm, c);
259
Ed Warnickecb9cada2015-12-08 15:45:58 -0700260 /* Received an offer, go send a request */
261 c->state = DHCP_REQUEST;
262 c->retry_count = 0;
263 c->next_transmit = 0; /* send right now... */
264 /* Poke the client process, which will send the request */
John Lo609707e2017-09-19 21:45:10 -0400265 uword client_id = c - dcm->clients;
266 vl_api_rpc_call_main_thread (dhcp_client_proc_callback,
267 (u8 *) &client_id, sizeof (uword));
Ed Warnickecb9cada2015-12-08 15:45:58 -0700268 break;
269
270 case DHCP_BOUND:
271 case DHCP_REQUEST:
272 if (dhcp_message_type != DHCP_PACKET_ACK)
273 {
274 clib_warning ("sw_if_index %d state %U message type %d",
275 c->sw_if_index, format_dhcp_client_state,
276 c->state, dhcp_message_type);
277 c->next_transmit = now + 5.0;
278 break;
279 }
280 /* OK, we own the address (etc), add to the routing table(s) */
281 if (c->state == DHCP_REQUEST)
282 {
Neale Ranns4729b1e2017-07-06 01:39:05 -0700283 void (*fp)(u32, u32, u8 *, u8, u8, u8 *, u8 *, u8 *) = c->event_callback;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700284
Neale Ranns808c5b22017-08-02 05:15:07 -0700285 /* replace the temporary RX address with the correct subnet */
286 dhcp_client_remove_rx_address (dcm, c);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700287 dhcp_client_acquire_address (dcm, c);
288
289 /*
290 * Configure default IP route:
Ed Warnickecb9cada2015-12-08 15:45:58 -0700291 */
292 if (c->router_address.as_u32)
Neale Ranns0bfe5d82016-08-25 15:29:12 +0100293 {
294 fib_prefix_t all_0s =
295 {
296 .fp_len = 0,
297 .fp_addr.ip4.as_u32 = 0x0,
298 .fp_proto = FIB_PROTOCOL_IP4,
299 };
300 ip46_address_t nh =
301 {
302 .ip4 = c->router_address,
303 };
304
305 fib_table_entry_path_add (fib_table_get_index_for_sw_if_index(
306 FIB_PROTOCOL_IP4,
307 c->sw_if_index),
308 &all_0s,
309 FIB_SOURCE_DHCP,
310 FIB_ENTRY_FLAG_NONE,
Neale Rannsda78f952017-05-24 09:15:43 -0700311 DPO_PROTO_IP4,
Neale Ranns0bfe5d82016-08-25 15:29:12 +0100312 &nh,
313 c->sw_if_index,
314 ~0,
315 1,
Neale Rannsad422ed2016-11-02 14:20:04 +0000316 NULL, // no label stack
Neale Ranns0bfe5d82016-08-25 15:29:12 +0100317 FIB_ROUTE_PATH_FLAG_NONE);
318 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700319
320 /*
321 * Call the user's event callback to report DHCP information
322 */
323 if (fp)
324 (*fp) (c->client_index, /* clinet index */
325 c->pid,
326 c->hostname,
Neale Ranns4729b1e2017-07-06 01:39:05 -0700327 c->subnet_mask_width,
Ed Warnickecb9cada2015-12-08 15:45:58 -0700328 0, /* is_ipv6 */
329 (u8 *)&c->leased_address, /* host IP address */
330 (u8 *)&c->router_address, /* router IP address */
331 (u8 *)(c->l2_rewrite + 6));/* host MAC address */
332 }
333
334 c->state = DHCP_BOUND;
335 c->retry_count = 0;
336 c->next_transmit = now + (f64) c->lease_renewal_interval;
337 c->lease_expires = now + (f64) c->lease_lifetime;
338 break;
339
340 default:
341 clib_warning ("client %d bogus state %d",
342 c - dcm->clients, c->state);
343 break;
344 }
345
346 /* drop the pkt, return 1 */
347 vlib_buffer_free (vm, &bi, 1);
348 return 1;
349}
350
351static void
352send_dhcp_pkt (dhcp_client_main_t * dcm, dhcp_client_t * c,
353 dhcp_packet_type_t type, int is_broadcast)
354{
355 vlib_main_t * vm = dcm->vlib_main;
356 vnet_main_t * vnm = dcm->vnet_main;
357 vnet_hw_interface_t * hw = vnet_get_sup_hw_interface (vnm, c->sw_if_index);
358 vnet_sw_interface_t * sup_sw
359 = vnet_get_sup_sw_interface (vnm, c->sw_if_index);
360 vnet_sw_interface_t * sw = vnet_get_sw_interface (vnm, c->sw_if_index);
361 vlib_buffer_t * b;
362 u32 bi;
363 ip4_header_t * ip;
364 udp_header_t * udp;
365 dhcp_header_t * dhcp;
366 u32 * to_next;
367 vlib_frame_t * f;
368 dhcp_option_t * o;
369 u16 udp_length, ip_length;
370
371 /* Interface(s) down? */
372 if ((hw->flags & VNET_HW_INTERFACE_FLAG_LINK_UP) == 0)
373 return;
374 if ((sup_sw->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) == 0)
375 return;
376 if ((sw->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) == 0)
377 return;
378
379 if (vlib_buffer_alloc (vm, &bi, 1) != 1) {
380 clib_warning ("buffer allocation failure");
381 c->next_transmit = 0;
382 return;
383 }
384
385 /* Build a dhcpv4 pkt from whole cloth */
386 b = vlib_get_buffer (vm, bi);
387
388 ASSERT (b->current_data == 0);
389
390 vnet_buffer(b)->sw_if_index[VLIB_RX] = c->sw_if_index;
391 if (is_broadcast)
392 {
393 f = vlib_get_frame_to_node (vm, hw->output_node_index);
394 vnet_buffer(b)->sw_if_index[VLIB_TX] = c->sw_if_index;
Damjan Marionf1213b82016-03-13 02:22:06 +0100395 clib_memcpy (b->data, c->l2_rewrite, vec_len(c->l2_rewrite));
Ed Warnickecb9cada2015-12-08 15:45:58 -0700396 ip = (void *)
397 (((u8 *)vlib_buffer_get_current (b)) + vec_len (c->l2_rewrite));
398 }
399 else
400 {
401 f = vlib_get_frame_to_node (vm, ip4_lookup_node.index);
402 vnet_buffer(b)->sw_if_index[VLIB_TX] = ~0; /* use interface VRF */
403 ip = vlib_buffer_get_current (b);
404 }
405
406 /* Enqueue the packet right now */
407 to_next = vlib_frame_vector_args (f);
408 to_next[0] = bi;
409 f->n_vectors = 1;
410
411 if (is_broadcast)
412 vlib_put_frame_to_node (vm, hw->output_node_index, f);
413 else
414 vlib_put_frame_to_node (vm, ip4_lookup_node.index, f);
415
416 udp = (udp_header_t *)(ip+1);
417 dhcp = (dhcp_header_t *)(udp+1);
418
419 /* $$$ optimize, maybe */
420 memset (ip, 0, sizeof (*ip) + sizeof (*udp) + sizeof (*dhcp));
421
422 ip->ip_version_and_header_length = 0x45;
423 ip->ttl = 128;
424 ip->protocol = IP_PROTOCOL_UDP;
425
426 if (is_broadcast)
427 {
428 /* src = 0.0.0.0, dst = 255.255.255.255 */
429 ip->dst_address.as_u32 = ~0;
430 }
431 else
432 {
433 /* Renewing an active lease, plain old ip4 src/dst */
434 ip->src_address.as_u32 = c->leased_address.as_u32;
435 ip->dst_address.as_u32 = c->dhcp_server.as_u32;
436 }
437
438 udp->src_port = clib_host_to_net_u16 (UDP_DST_PORT_dhcp_to_client);
439 udp->dst_port = clib_host_to_net_u16 (UDP_DST_PORT_dhcp_to_server);
440
441 /* Send the interface MAC address */
Damjan Marionf1213b82016-03-13 02:22:06 +0100442 clib_memcpy (dhcp->client_hardware_address, c->l2_rewrite + 6, 6);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700443
444 /* Lease renewal, set up client_ip_address */
445 if (is_broadcast == 0)
446 dhcp->client_ip_address.as_u32 = c->leased_address.as_u32;
447
448 dhcp->opcode = 1; /* request, all we send */
449 dhcp->hardware_type = 1; /* ethernet */
450 dhcp->hardware_address_length = 6;
451 dhcp->transaction_identifier = c->transaction_id;
452 dhcp->flags = clib_host_to_net_u16(is_broadcast ? DHCP_FLAG_BROADCAST : 0);
453 dhcp->magic_cookie.as_u32 = DHCP_MAGIC;
454
455 o = (dhcp_option_t * )dhcp->options;
456
457 /* Send option 53, the DHCP message type */
Neale Ranns3466c302017-02-16 07:45:03 -0800458 o->option = DHCP_PACKET_OPTION_MSG_TYPE;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700459 o->length = 1;
460 o->data[0] = type;
461 o = (dhcp_option_t *) (((uword) o) + (o->length + 2));
462
463 /* Send option 57, max msg length */
464 if (0 /* not needed, apparently */)
465 {
466 o->option = 57;
467 o->length = 2;
468 {
469 u16 *o2 = (u16 *) o->data;
470 *o2 = clib_host_to_net_u16 (1152);
471 o = (dhcp_option_t *) (((uword) o) + (o->length + 2));
472 }
473 }
474
475 /*
476 * If server ip address is available with non-zero value,
477 * option 54 (DHCP Server Identifier) is sent.
478 */
479 if (c->dhcp_server.as_u32)
480 {
481 o->option = 54;
482 o->length = 4;
Damjan Marionf1213b82016-03-13 02:22:06 +0100483 clib_memcpy (o->data, &c->dhcp_server.as_u32, 4);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700484 o = (dhcp_option_t *) (((uword) o) + (o->length + 2));
485 }
486
487 /* send option 50, requested IP address */
488 if (c->leased_address.as_u32)
489 {
490 o->option = 50;
491 o->length = 4;
Damjan Marionf1213b82016-03-13 02:22:06 +0100492 clib_memcpy (o->data, &c->leased_address.as_u32, 4);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700493 o = (dhcp_option_t *) (((uword) o) + (o->length + 2));
494 }
495
496 /* send option 12, host name */
497 if (vec_len (c->hostname))
498 {
499 o->option = 12;
500 o->length = vec_len (c->hostname);
Damjan Marionf1213b82016-03-13 02:22:06 +0100501 clib_memcpy (o->data, c->hostname, vec_len (c->hostname));
Ed Warnickecb9cada2015-12-08 15:45:58 -0700502 o = (dhcp_option_t *) (((uword) o) + (o->length + 2));
503 }
504
Neale Ranns51822bf2017-07-18 09:26:53 -0700505 /* send option 61, client_id */
506 if (vec_len (c->client_identifier))
507 {
508 o->option = 61;
509 o->length = vec_len (c->client_identifier);
510 clib_memcpy (o->data, c->client_identifier,
511 vec_len (c->client_identifier));
512 o = (dhcp_option_t *) (((uword) o) + (o->length + 2));
513 }
514
Ed Warnickecb9cada2015-12-08 15:45:58 -0700515 /* $$ maybe send the client s/w version if anyone cares */
516
517 /*
518 * send option 55, parameter request list
519 * The current list - see below, matches the Linux dhcp client's list
520 * Any specific dhcp server config and/or dhcp server may or may
521 * not yield specific options.
522 */
523 o->option = 55;
524 o->length = vec_len (c->option_55_data);
Damjan Marionf1213b82016-03-13 02:22:06 +0100525 clib_memcpy (o->data, c->option_55_data, vec_len(c->option_55_data));
Ed Warnickecb9cada2015-12-08 15:45:58 -0700526 o = (dhcp_option_t *) (((uword) o) + (o->length + 2));
527
528 /* End of list */
529 o->option = 0xff;
530 o->length = 0;
531 o++;
532
533 b->current_length = ((u8 *)o) - b->data;
534
535 /* fix ip length, checksum and udp length */
536 ip_length = vlib_buffer_length_in_chain (vm, b);
537 if (is_broadcast)
538 ip_length -= vec_len (c->l2_rewrite);
539
540 ip->length = clib_host_to_net_u16(ip_length);
541 ip->checksum = ip4_header_checksum(ip);
542
543 udp_length = ip_length - (sizeof (*ip));
544 udp->length = clib_host_to_net_u16 (udp_length);
545}
546
547static int
548dhcp_discover_state (dhcp_client_main_t * dcm, dhcp_client_t * c, f64 now)
549{
550 /*
551 * State machine "DISCOVER" state. Send a dhcp discover packet,
552 * eventually back off the retry rate.
553 */
554 send_dhcp_pkt (dcm, c, DHCP_PACKET_DISCOVER, 1 /* is_broadcast */);
555
556 c->retry_count++;
557 if (c->retry_count > 10)
558 c->next_transmit = now + 5.0;
559 else
560 c->next_transmit = now + 1.0;
561 return 0;
562}
563
564static int
565dhcp_request_state (dhcp_client_main_t * dcm, dhcp_client_t * c, f64 now)
566{
567 /*
568 * State machine "REQUEST" state. Send a dhcp request packet,
569 * eventually drop back to the discover state.
570 */
571 send_dhcp_pkt (dcm, c, DHCP_PACKET_REQUEST, 1 /* is_broadcast */);
572
573 c->retry_count++;
574 if (c->retry_count > 7 /* lucky you */)
575 {
576 c->state = DHCP_DISCOVER;
577 c->next_transmit = now;
578 c->retry_count = 0;
579 return 1;
580 }
581 c->next_transmit = now + 1.0;
582 return 0;
583}
584
585static int
586dhcp_bound_state (dhcp_client_main_t * dcm, dhcp_client_t * c, f64 now)
587{
588 /*
589 * State machine "BOUND" state. Send a dhcp request packet,
590 * eventually, when the lease expires, forget the dhcp data
591 * and go back to the stone age.
592 */
593 send_dhcp_pkt (dcm, c, DHCP_PACKET_REQUEST, 0 /* is_broadcast */);
594
595 c->retry_count++;
596 if (c->retry_count > 10)
597 c->next_transmit = now + 5.0;
598 else
599 c->next_transmit = now + 1.0;
600
601 if (now > c->lease_expires)
602 {
603 if (c->router_address.as_u32)
Neale Ranns0bfe5d82016-08-25 15:29:12 +0100604 {
605 fib_prefix_t all_0s =
606 {
607 .fp_len = 0,
608 .fp_addr.ip4.as_u32 = 0x0,
609 .fp_proto = FIB_PROTOCOL_IP4,
610 };
611 ip46_address_t nh = {
612 .ip4 = c->router_address,
613 };
614
615 fib_table_entry_path_remove(fib_table_get_index_for_sw_if_index(
616 FIB_PROTOCOL_IP4,
617 c->sw_if_index),
618 &all_0s,
619 FIB_SOURCE_DHCP,
Neale Rannsda78f952017-05-24 09:15:43 -0700620 DPO_PROTO_IP4,
Neale Ranns0bfe5d82016-08-25 15:29:12 +0100621 &nh,
622 c->sw_if_index,
623 ~0,
624 1,
625 FIB_ROUTE_PATH_FLAG_NONE);
626 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700627
628 dhcp_client_release_address (dcm, c);
629 c->state = DHCP_DISCOVER;
630 c->next_transmit = now;
631 c->retry_count = 0;
632 /* Wipe out any memory of the address we had... */
633 c->leased_address.as_u32 = 0;
634 c->subnet_mask_width = 0;
635 c->router_address.as_u32 = 0;
636 c->lease_renewal_interval = 0;
637 c->dhcp_server.as_u32 = 0;
638 return 1;
639 }
640 return 0;
641}
642
643static f64 dhcp_client_sm (f64 now, f64 timeout, uword pool_index)
644{
645 dhcp_client_main_t * dcm = &dhcp_client_main;
646 dhcp_client_t * c;
647
648 /* deleted, pooched, yadda yadda yadda */
649 if (pool_is_free_index (dcm->clients, pool_index))
650 return timeout;
651
652 c = pool_elt_at_index (dcm->clients, pool_index);
653
654 /* Time for us to do something with this client? */
655 if (now < c->next_transmit)
656 return timeout;
657
658 again:
659 switch (c->state)
660 {
661 case DHCP_DISCOVER: /* send a discover */
662 if (dhcp_discover_state (dcm, c, now))
663 goto again;
664 break;
665
666 case DHCP_REQUEST: /* send a request */
667 if (dhcp_request_state (dcm, c, now))
668 goto again;
669 break;
670
671 case DHCP_BOUND: /* bound, renew needed? */
672 if (dhcp_bound_state (dcm, c, now))
673 goto again;
674 break;
675
676 default:
677 clib_warning ("dhcp client %d bogus state %d",
678 c - dcm->clients, c->state);
679 break;
680 }
681
682 if (c->next_transmit < now + timeout)
683 return c->next_transmit - now;
684
685 return timeout;
686}
687
688static uword
689dhcp_client_process (vlib_main_t * vm,
690 vlib_node_runtime_t * rt,
691 vlib_frame_t * f)
692{
693 f64 timeout = 100.0;
694 f64 now;
695 uword event_type;
696 uword * event_data = 0;
697 dhcp_client_main_t * dcm = &dhcp_client_main;
698 dhcp_client_t * c;
699 int i;
700
701 while (1)
702 {
703 vlib_process_wait_for_event_or_clock (vm, timeout);
704
705 event_type = vlib_process_get_events (vm, &event_data);
706
707 now = vlib_time_now (vm);
708
709 switch (event_type)
710 {
711 case EVENT_DHCP_CLIENT_WAKEUP:
712 for (i = 0; i < vec_len (event_data); i++)
713 timeout = dhcp_client_sm (now, timeout, event_data[i]);
714 break;
715
716 case ~0:
717 pool_foreach (c, dcm->clients,
718 ({
719 timeout = dhcp_client_sm (now, timeout,
720 (uword)(c - dcm->clients));
721 }));
722 if (pool_elts (dcm->clients) == 0)
723 timeout = 100.0;
724 break;
725 }
726
727 vec_reset_length (event_data);
728 }
729
730 /* NOTREACHED */
731 return 0;
732}
733
734VLIB_REGISTER_NODE (dhcp_client_process_node,static) = {
735 .function = dhcp_client_process,
736 .type = VLIB_NODE_TYPE_PROCESS,
737 .name = "dhcp-client-process",
738 .process_log2_n_stack_bytes = 16,
739};
740
741static u8 * format_dhcp_client_state (u8 * s, va_list * va)
742{
743 dhcp_client_state_t state = va_arg (*va, dhcp_client_state_t);
744 char * str = "BOGUS!";
745
746 switch (state)
747 {
748#define _(a) \
749 case a: \
750 str = #a; \
751 break;
752 foreach_dhcp_client_state;
753#undef _
754 default:
755 break;
756 }
757
758 s = format (s, "%s", str);
759 return s;
760}
761
762static u8 * format_dhcp_client (u8 * s, va_list * va)
763{
764 dhcp_client_main_t * dcm = va_arg (*va, dhcp_client_main_t *);
765 dhcp_client_t * c = va_arg (*va, dhcp_client_t *);
766 int verbose = va_arg (*va, int);
767
768 s = format (s, "[%d] %U state %U ", c - dcm->clients,
769 format_vnet_sw_if_index_name, dcm->vnet_main, c->sw_if_index,
770 format_dhcp_client_state, c->state);
771
772 if (c->leased_address.as_u32)
773 s = format (s, "addr %U/%d gw %U\n",
774 format_ip4_address, &c->leased_address,
775 c->subnet_mask_width, format_ip4_address, &c->router_address);
776 else
777 s = format (s, "no address\n");
778
779 if (verbose)
780 {
781 s = format (s, "retry count %d, next xmt %.2f",
782 c->retry_count, c->next_transmit);
783 }
784 return s;
785}
786
787static clib_error_t *
788show_dhcp_client_command_fn (vlib_main_t * vm,
789 unformat_input_t * input,
790 vlib_cli_command_t * cmd)
791{
792 dhcp_client_main_t * dcm = &dhcp_client_main;
793 dhcp_client_t * c;
794 int verbose = 0;
795 u32 sw_if_index = ~0;
796 uword * p;
797
798 while (unformat_check_input(input) != UNFORMAT_END_OF_INPUT)
799 {
800 if (unformat (input, "intfc %U",
801 unformat_vnet_sw_interface, dcm->vnet_main,
802 &sw_if_index))
803 ;
804 else if (unformat (input, "verbose"))
805 verbose = 1;
806 else
807 break;
808 }
809
810 if (sw_if_index != ~0)
811 {
812 p = hash_get (dcm->client_by_sw_if_index, sw_if_index);
813 if (p == 0)
814 return clib_error_return (0, "dhcp client not configured");
Neale Ranns0bfe5d82016-08-25 15:29:12 +0100815 c = pool_elt_at_index (dcm->clients, p[0]);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700816 vlib_cli_output (vm, "%U", format_dhcp_client, dcm, c, verbose);
817 return 0;
818 }
819
820 pool_foreach (c, dcm->clients,
821 ({
822 vlib_cli_output (vm, "%U", format_dhcp_client, dcm, c, verbose);
823 }));
824
825 return 0;
826}
827
828VLIB_CLI_COMMAND (show_dhcp_client_command, static) = {
829 .path = "show dhcp client",
830 .short_help = "show dhcp client [intfc <intfc>][verbose]",
831 .function = show_dhcp_client_command_fn,
832};
833
834
835int dhcp_client_add_del (dhcp_client_add_del_args_t * a)
836{
837 dhcp_client_main_t * dcm = &dhcp_client_main;
838 vlib_main_t * vm = dcm->vlib_main;
839 dhcp_client_t * c;
840 uword * p;
Neale Ranns0bfe5d82016-08-25 15:29:12 +0100841 fib_prefix_t all_1s =
842 {
843 .fp_len = 32,
844 .fp_addr.ip4.as_u32 = 0xffffffff,
845 .fp_proto = FIB_PROTOCOL_IP4,
846 };
847 fib_prefix_t all_0s =
848 {
849 .fp_len = 0,
850 .fp_addr.ip4.as_u32 = 0x0,
851 .fp_proto = FIB_PROTOCOL_IP4,
852 };
Ed Warnickecb9cada2015-12-08 15:45:58 -0700853
854 p = hash_get (dcm->client_by_sw_if_index, a->sw_if_index);
855
856 if ((p && a->is_add) || (!p && a->is_add == 0))
857 return VNET_API_ERROR_INVALID_VALUE;
858
859 if (a->is_add)
860 {
861 pool_get (dcm->clients, c);
862 memset (c, 0, sizeof (*c));
863 c->state = DHCP_DISCOVER;
864 c->sw_if_index = a->sw_if_index;
865 c->client_index = a->client_index;
866 c->pid = a->pid;
867 c->event_callback = a->event_callback;
868 c->option_55_data = a->option_55_data;
869 c->hostname = a->hostname;
870 c->client_identifier = a->client_identifier;
871 do {
872 c->transaction_id = random_u32 (&dcm->seed);
873 } while (c->transaction_id == 0);
874 set_l2_rewrite (dcm, c);
875 hash_set (dcm->client_by_sw_if_index, a->sw_if_index, c - dcm->clients);
Neale Ranns0bfe5d82016-08-25 15:29:12 +0100876
877 /* this add is ref counted by FIB so we can add for each itf */
878 fib_table_entry_special_add(fib_table_get_index_for_sw_if_index(
879 FIB_PROTOCOL_IP4,
880 c->sw_if_index),
881 &all_1s,
882 FIB_SOURCE_DHCP,
Neale Rannsa0558302017-04-13 00:44:52 -0700883 FIB_ENTRY_FLAG_LOCAL);
Neale Ranns0bfe5d82016-08-25 15:29:12 +0100884
885 /*
886 * enable the interface to RX IPv4 packets
887 * this is also ref counted
888 */
889 ip4_sw_interface_enable_disable (c->sw_if_index, 1);
890
Ed Warnickecb9cada2015-12-08 15:45:58 -0700891 vlib_process_signal_event (vm, dhcp_client_process_node.index,
892 EVENT_DHCP_CLIENT_WAKEUP, c - dcm->clients);
893 }
894 else
895 {
896 c = pool_elt_at_index (dcm->clients, p[0]);
897
Neale Ranns0bfe5d82016-08-25 15:29:12 +0100898 fib_table_entry_special_remove(fib_table_get_index_for_sw_if_index(
899 FIB_PROTOCOL_IP4,
900 c->sw_if_index),
901 &all_1s,
902 FIB_SOURCE_DHCP);
903
Ed Warnickecb9cada2015-12-08 15:45:58 -0700904 if (c->router_address.as_u32)
Neale Ranns0bfe5d82016-08-25 15:29:12 +0100905 {
906 ip46_address_t nh = {
907 .ip4 = c->router_address,
908 };
909
910 fib_table_entry_path_remove(fib_table_get_index_for_sw_if_index(
911 FIB_PROTOCOL_IP4,
912 c->sw_if_index),
913 &all_0s,
914 FIB_SOURCE_DHCP,
Neale Rannsda78f952017-05-24 09:15:43 -0700915 DPO_PROTO_IP4,
Neale Ranns0bfe5d82016-08-25 15:29:12 +0100916 &nh,
917 c->sw_if_index,
918 ~0,
919 1,
920 FIB_ROUTE_PATH_FLAG_NONE);
921 }
Neale Ranns808c5b22017-08-02 05:15:07 -0700922 dhcp_client_remove_rx_address (dcm, c);
Neale Rannsa2fbf6b2017-07-18 08:23:32 -0700923 dhcp_client_release_address (dcm, c);
Neale Ranns0bfe5d82016-08-25 15:29:12 +0100924 ip4_sw_interface_enable_disable (c->sw_if_index, 0);
925
Ed Warnickecb9cada2015-12-08 15:45:58 -0700926 vec_free (c->option_55_data);
927 vec_free (c->hostname);
928 vec_free (c->client_identifier);
929 vec_free (c->l2_rewrite);
930 hash_unset (dcm->client_by_sw_if_index, c->sw_if_index);
931 pool_put (dcm->clients, c);
932 }
933 return 0;
934}
935
936int
937dhcp_client_config (vlib_main_t * vm,
938 u32 sw_if_index,
939 u8 * hostname,
Neale Ranns51822bf2017-07-18 09:26:53 -0700940 u8 * client_id,
Ed Warnickecb9cada2015-12-08 15:45:58 -0700941 u32 is_add,
942 u32 client_index,
943 void * event_callback,
944 u32 pid)
945{
946 dhcp_client_add_del_args_t _a, *a = &_a;
947 int rv;
948
949 memset (a, 0, sizeof (*a));
950 a->is_add = is_add;
951 a->sw_if_index = sw_if_index;
952 a->client_index = client_index;
953 a->pid = pid;
954 a->event_callback = event_callback;
955 vec_validate(a->hostname, strlen((char *)hostname) - 1);
956 strncpy((char *)a->hostname, (char *)hostname, vec_len(a->hostname));
Neale Ranns51822bf2017-07-18 09:26:53 -0700957 vec_validate(a->client_identifier, strlen((char *)client_id) - 1);
958 strncpy((char *)a->client_identifier, (char *)client_id, vec_len(a->client_identifier));
959
Ed Warnickecb9cada2015-12-08 15:45:58 -0700960 /*
961 * Option 55 request list. These data precisely match
962 * the Ubuntu dhcp client. YMMV.
963 */
964
965 /* Subnet Mask */
966 vec_add1 (a->option_55_data, 1);
967 /* Broadcast address */
968 vec_add1 (a->option_55_data, 28);
969 /* time offset */
970 vec_add1 (a->option_55_data, 2);
971 /* Router */
972 vec_add1 (a->option_55_data, 3);
973 /* Domain Name */
974 vec_add1 (a->option_55_data, 15);
975 /* DNS */
976 vec_add1 (a->option_55_data, 6);
977 /* Domain search */
978 vec_add1 (a->option_55_data, 119);
979 /* Host name */
980 vec_add1 (a->option_55_data, 12);
981 /* NetBIOS name server */
982 vec_add1 (a->option_55_data, 44);
983 /* NetBIOS Scope */
984 vec_add1 (a->option_55_data, 47);
985 /* MTU */
986 vec_add1 (a->option_55_data, 26);
987 /* Classless static route */
988 vec_add1 (a->option_55_data, 121);
989 /* NTP servers */
990 vec_add1 (a->option_55_data, 42);
991
992 rv = dhcp_client_add_del (a);
993
994 switch (rv)
995 {
996 case 0:
997 break;
998
999 case VNET_API_ERROR_INVALID_VALUE:
1000
1001 vec_free (a->hostname);
1002 vec_free (a->client_identifier);
1003 vec_free (a->option_55_data);
1004
1005 if (is_add)
1006 clib_warning ("dhcp client already enabled on intf_idx %d",
1007 sw_if_index);
1008 else
1009 clib_warning ("dhcp client not enabled on on intf_idx %d",
1010 sw_if_index);
1011 break;
1012
1013 default:
1014 clib_warning ("dhcp_client_add_del returned %d", rv);
1015 }
1016
1017 return rv;
1018}
1019
1020static clib_error_t *
1021dhcp_client_set_command_fn (vlib_main_t * vm,
1022 unformat_input_t * input,
1023 vlib_cli_command_t * cmd)
1024{
1025
1026 dhcp_client_main_t * dcm = &dhcp_client_main;
1027 u32 sw_if_index;
1028 u8 * hostname = 0;
1029 u8 sw_if_index_set = 0;
1030 int is_add = 1;
1031 dhcp_client_add_del_args_t _a, *a = &_a;
1032 int rv;
1033
1034 while (unformat_check_input(input) != UNFORMAT_END_OF_INPUT)
1035 {
1036 if (unformat (input, "intfc %U",
1037 unformat_vnet_sw_interface, dcm->vnet_main,
1038 &sw_if_index))
1039 sw_if_index_set = 1;
1040 else if (unformat (input, "hostname %v", &hostname))
1041 ;
1042 else if (unformat (input, "del"))
1043 is_add = 0;
1044 else
1045 break;
1046 }
1047
1048 if (sw_if_index_set == 0)
1049 return clib_error_return (0, "interface not specified");
1050
1051 memset (a, 0, sizeof (*a));
1052 a->is_add = is_add;
1053 a->sw_if_index = sw_if_index;
1054 a->hostname = hostname;
1055 a->client_identifier = format (0, "vpe 1.0%c", 0);
1056
1057 /*
1058 * Option 55 request list. These data precisely match
1059 * the Ubuntu dhcp client. YMMV.
1060 */
1061
1062 /* Subnet Mask */
1063 vec_add1 (a->option_55_data, 1);
1064 /* Broadcast address */
1065 vec_add1 (a->option_55_data, 28);
1066 /* time offset */
1067 vec_add1 (a->option_55_data, 2);
1068 /* Router */
1069 vec_add1 (a->option_55_data, 3);
1070 /* Domain Name */
1071 vec_add1 (a->option_55_data, 15);
1072 /* DNS */
1073 vec_add1 (a->option_55_data, 6);
1074 /* Domain search */
1075 vec_add1 (a->option_55_data, 119);
1076 /* Host name */
1077 vec_add1 (a->option_55_data, 12);
1078 /* NetBIOS name server */
1079 vec_add1 (a->option_55_data, 44);
1080 /* NetBIOS Scope */
1081 vec_add1 (a->option_55_data, 47);
1082 /* MTU */
1083 vec_add1 (a->option_55_data, 26);
1084 /* Classless static route */
1085 vec_add1 (a->option_55_data, 121);
1086 /* NTP servers */
1087 vec_add1 (a->option_55_data, 42);
1088
1089 rv = dhcp_client_add_del (a);
1090
1091 switch (rv)
1092 {
1093 case 0:
1094 break;
1095
1096 case VNET_API_ERROR_INVALID_VALUE:
1097
1098 vec_free (a->hostname);
1099 vec_free (a->client_identifier);
1100 vec_free (a->option_55_data);
1101 if (is_add)
1102 return clib_error_return (0, "dhcp client already enabled on %U",
1103 format_vnet_sw_if_index_name,
1104 dcm->vnet_main, sw_if_index);
1105 else
1106 return clib_error_return (0, "dhcp client not enabled on %U",
1107 format_vnet_sw_if_index_name,
1108 dcm->vnet_main, sw_if_index);
1109 break;
1110
1111 default:
1112 vlib_cli_output (vm, "dhcp_client_add_del returned %d", rv);
1113 }
1114
1115 return 0;
1116}
1117
1118VLIB_CLI_COMMAND (dhcp_client_set_command, static) = {
1119 .path = "set dhcp client",
1120 .short_help = "set dhcp client [del] intfc <interface> [hostname <name>]",
1121 .function = dhcp_client_set_command_fn,
1122};
1123
1124static clib_error_t *
1125dhcp_client_init (vlib_main_t * vm)
1126{
1127 dhcp_client_main_t * dcm = &dhcp_client_main;
1128
1129 dcm->vlib_main = vm;
1130 dcm->vnet_main = vnet_get_main();
1131 dcm->seed = 0xdeaddabe;
1132 return 0;
1133}
1134
1135VLIB_INIT_FUNCTION (dhcp_client_init);