blob: bea778a97943b368d7beffefc2047b569d2b5781 [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;
khemendra kumar34719e32017-12-08 18:06:52 +053021static u8 *format_dhcp_client_state (u8 * s, va_list * va);
Ed Warnickecb9cada2015-12-08 15:45:58 -070022static vlib_node_registration_t dhcp_client_process_node;
23
Dave Barach4585eb82018-03-23 09:52:52 -040024#define foreach_dhcp_sent_packet_stat \
Dave Barach4941fcc2018-02-14 17:58:33 -050025_(DISCOVER, "DHCP discover packets sent") \
26_(OFFER, "DHCP offer packets sent") \
27_(REQUEST, "DHCP request packets sent") \
28_(ACK, "DHCP ack packets sent")
29
Dave Barach4585eb82018-03-23 09:52:52 -040030#define foreach_dhcp_error_counter \
31_(NOT_FOR_US, "DHCP packets for other hosts, dropped") \
32_(NAK, "DHCP nak packets received") \
33_(NON_OFFER_DISCOVER, "DHCP non-offer packets in discover state") \
34_(ODDBALL, "DHCP non-ack, non-offer packets received") \
35_(BOUND, "DHCP bind success")
36
Dave Barach4941fcc2018-02-14 17:58:33 -050037typedef enum
38{
39#define _(sym,str) DHCP_STAT_##sym,
Dave Barach4585eb82018-03-23 09:52:52 -040040 foreach_dhcp_sent_packet_stat foreach_dhcp_error_counter
Dave Barach4941fcc2018-02-14 17:58:33 -050041#undef _
42 DHCP_STAT_UNKNOWN,
43 DHCP_STAT_N_STAT,
44} sample_error_t;
45
46static char *dhcp_client_process_stat_strings[] = {
47#define _(sym,string) string,
Dave Barach4585eb82018-03-23 09:52:52 -040048 foreach_dhcp_sent_packet_stat foreach_dhcp_error_counter
Dave Barach4941fcc2018-02-14 17:58:33 -050049#undef _
50 "DHCP unknown packets sent",
51};
52
53
khemendra kumar34719e32017-12-08 18:06:52 +053054static void
Ed Warnickecb9cada2015-12-08 15:45:58 -070055dhcp_client_acquire_address (dhcp_client_main_t * dcm, dhcp_client_t * c)
56{
khemendra kumar34719e32017-12-08 18:06:52 +053057 /*
Ed Warnickecb9cada2015-12-08 15:45:58 -070058 * Install any/all info gleaned from dhcp, right here
59 */
60 ip4_add_del_interface_address (dcm->vlib_main, c->sw_if_index,
khemendra kumar34719e32017-12-08 18:06:52 +053061 (void *) &c->leased_address,
62 c->subnet_mask_width, 0 /*is_del */ );
Ed Warnickecb9cada2015-12-08 15:45:58 -070063}
64
khemendra kumar34719e32017-12-08 18:06:52 +053065static void
Ed Warnickecb9cada2015-12-08 15:45:58 -070066dhcp_client_release_address (dhcp_client_main_t * dcm, dhcp_client_t * c)
67{
khemendra kumar34719e32017-12-08 18:06:52 +053068 /*
Ed Warnickecb9cada2015-12-08 15:45:58 -070069 * Remove any/all info gleaned from dhcp, right here. Caller(s)
70 * have not wiped out the info yet.
71 */
72
73 ip4_add_del_interface_address (dcm->vlib_main, c->sw_if_index,
khemendra kumar34719e32017-12-08 18:06:52 +053074 (void *) &c->leased_address,
75 c->subnet_mask_width, 1 /*is_del */ );
Ed Warnickecb9cada2015-12-08 15:45:58 -070076}
77
Neale Rannsb80c5362016-10-08 13:03:40 +010078static void
79set_l2_rewrite (dhcp_client_main_t * dcm, dhcp_client_t * c)
Ed Warnickecb9cada2015-12-08 15:45:58 -070080{
Ed Warnickecb9cada2015-12-08 15:45:58 -070081 /* Acquire the L2 rewrite string for the indicated sw_if_index */
khemendra kumar34719e32017-12-08 18:06:52 +053082 c->l2_rewrite = vnet_build_rewrite_for_sw_interface (dcm->vnet_main,
83 c->sw_if_index,
84 VNET_LINK_IP4,
85 0 /* broadcast */ );
Ed Warnickecb9cada2015-12-08 15:45:58 -070086}
87
John Lo609707e2017-09-19 21:45:10 -040088void vl_api_rpc_call_main_thread (void *fp, u8 * data, u32 data_length);
89
90static void
91dhcp_client_proc_callback (uword * client_index)
92{
93 vlib_main_t *vm = vlib_get_main ();
94 ASSERT (vlib_get_thread_index () == 0);
khemendra kumar34719e32017-12-08 18:06:52 +053095 vlib_process_signal_event (vm, dhcp_client_process_node.index,
John Lo609707e2017-09-19 21:45:10 -040096 EVENT_DHCP_CLIENT_WAKEUP, *client_index);
97}
98
Matthew Smith7a192832018-02-10 11:11:45 -060099static void
100dhcp_client_addr_callback (dhcp_client_t * c)
101{
102 dhcp_client_main_t *dcm = &dhcp_client_main;
103 void (*fp) (u32, u32, u8 *, u8, u8, u8 *, u8 *, u8 *) = c->event_callback;
104
105 /* add the advertised subnet and disable the feature */
106 dhcp_client_acquire_address (dcm, c);
107 vnet_feature_enable_disable ("ip4-unicast",
108 "ip4-dhcp-client-detect",
Dave Barach4585eb82018-03-23 09:52:52 -0400109 c->sw_if_index, 0 /* disable */ , 0, 0);
Matthew Smith7a192832018-02-10 11:11:45 -0600110
111 /*
112 * Configure default IP route:
113 */
114 if (c->router_address.as_u32)
115 {
116 fib_prefix_t all_0s = {
117 .fp_len = 0,
118 .fp_addr.ip4.as_u32 = 0x0,
119 .fp_proto = FIB_PROTOCOL_IP4,
120 };
121 ip46_address_t nh = {
122 .ip4 = c->router_address,
123 };
124
125 /* *INDENT-OFF* */
126 fib_table_entry_path_add (
127 fib_table_get_index_for_sw_if_index (
128 FIB_PROTOCOL_IP4,
129 c->sw_if_index),
130 &all_0s,
131 FIB_SOURCE_DHCP,
132 FIB_ENTRY_FLAG_NONE,
133 DPO_PROTO_IP4,
134 &nh, c->sw_if_index,
135 ~0, 1, NULL, // no label stack
136 FIB_ROUTE_PATH_FLAG_NONE);
137 /* *INDENT-ON* */
138 }
139
140 /*
141 * Call the user's event callback to report DHCP information
142 */
143 if (fp)
144 (*fp) (c->client_index, /* clinet index */
145 c->pid, c->hostname, c->subnet_mask_width, 0, /* is_ipv6 */
146 (u8 *) & c->leased_address, /* host IP address */
147 (u8 *) & c->router_address, /* router IP address */
148 (u8 *) (c->l2_rewrite + 6)); /* host MAC address */
149}
150
khemendra kumar34719e32017-12-08 18:06:52 +0530151/*
Ed Warnickecb9cada2015-12-08 15:45:58 -0700152 * dhcp_client_for_us - server-to-client callback.
153 * Called from proxy_node.c:dhcp_proxy_to_client_input().
154 * This function first decides that the packet in question is
155 * actually for the dhcp client code in case we're also acting as
156 * a dhcp proxy. Ay caramba, what a folly!
157 */
khemendra kumar34719e32017-12-08 18:06:52 +0530158int
159dhcp_client_for_us (u32 bi, vlib_buffer_t * b,
160 ip4_header_t * ip,
161 udp_header_t * udp, dhcp_header_t * dhcp)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700162{
khemendra kumar34719e32017-12-08 18:06:52 +0530163 dhcp_client_main_t *dcm = &dhcp_client_main;
164 vlib_main_t *vm = dcm->vlib_main;
165 dhcp_client_t *c;
166 uword *p;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700167 f64 now = vlib_time_now (dcm->vlib_main);
168 u8 dhcp_message_type = 0;
khemendra kumar34719e32017-12-08 18:06:52 +0530169 dhcp_option_t *o;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700170
khemendra kumar34719e32017-12-08 18:06:52 +0530171 /*
172 * Doing dhcp client on this interface?
Ed Warnickecb9cada2015-12-08 15:45:58 -0700173 * Presumably we will always receive dhcp clnt for-us pkts on
174 * the interface that's asking for an address.
175 */
khemendra kumar34719e32017-12-08 18:06:52 +0530176 p = hash_get (dcm->client_by_sw_if_index,
177 vnet_buffer (b)->sw_if_index[VLIB_RX]);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700178 if (p == 0)
khemendra kumar34719e32017-12-08 18:06:52 +0530179 return 0; /* no */
180
Ed Warnickecb9cada2015-12-08 15:45:58 -0700181 c = pool_elt_at_index (dcm->clients, p[0]);
182
183 /* Mixing dhcp relay and dhcp proxy? DGMS... */
184 if (c->state == DHCP_BOUND && c->retry_count == 0)
185 return 0;
186
Dave Barach4585eb82018-03-23 09:52:52 -0400187 /* Packet not for us? Turf it... */
188 if (memcmp (dhcp->client_hardware_address, c->client_hardware_address,
189 sizeof (c->client_hardware_address)))
190 {
191 vlib_node_increment_counter (vm, dhcp_client_process_node.index,
192 DHCP_STAT_NOT_FOR_US, 1);
193 return 0;
194 }
195
Ed Warnickecb9cada2015-12-08 15:45:58 -0700196 /* parse through the packet, learn what we can */
197 if (dhcp->your_ip_address.as_u32)
198 c->leased_address.as_u32 = dhcp->your_ip_address.as_u32;
Neale Ranns808c5b22017-08-02 05:15:07 -0700199
200 c->dhcp_server.as_u32 = dhcp->server_ip_address.as_u32;
201
Ed Warnickecb9cada2015-12-08 15:45:58 -0700202 o = (dhcp_option_t *) dhcp->options;
khemendra kumar34719e32017-12-08 18:06:52 +0530203
204 while (o->option != 0xFF /* end of options */ &&
205 (u8 *) o < (b->data + b->current_data + b->current_length))
Ed Warnickecb9cada2015-12-08 15:45:58 -0700206 {
207 switch (o->option)
khemendra kumar34719e32017-12-08 18:06:52 +0530208 {
209 case 53: /* dhcp message type */
210 dhcp_message_type = o->data[0];
211 break;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700212
khemendra kumar34719e32017-12-08 18:06:52 +0530213 case 51: /* lease time */
214 {
215 u32 lease_time_in_seconds =
216 clib_host_to_net_u32 (o->data_as_u32[0]);
217 c->lease_expires = now + (f64) lease_time_in_seconds;
218 c->lease_lifetime = lease_time_in_seconds;
219 /* Set a sensible default, in case we don't get opt 58 */
220 c->lease_renewal_interval = lease_time_in_seconds / 2;
221 }
222 break;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700223
khemendra kumar34719e32017-12-08 18:06:52 +0530224 case 58: /* lease renew time in seconds */
225 {
226 u32 lease_renew_time_in_seconds =
227 clib_host_to_net_u32 (o->data_as_u32[0]);
228 c->lease_renewal_interval = lease_renew_time_in_seconds;
229 }
230 break;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700231
khemendra kumar34719e32017-12-08 18:06:52 +0530232 case 54: /* dhcp server address */
233 c->dhcp_server.as_u32 = o->data_as_u32[0];
234 break;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700235
khemendra kumar34719e32017-12-08 18:06:52 +0530236 case 1: /* subnet mask */
237 {
238 u32 subnet_mask = clib_host_to_net_u32 (o->data_as_u32[0]);
239 c->subnet_mask_width = count_set_bits (subnet_mask);
240 }
241 break;
242 case 3: /* router address */
243 {
244 u32 router_address = o->data_as_u32[0];
245 c->router_address.as_u32 = router_address;
246 }
247 break;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700248
khemendra kumar34719e32017-12-08 18:06:52 +0530249 case 12: /* hostname */
250 {
251 /* Replace the existing hostname if necessary */
252 vec_free (c->hostname);
253 vec_validate (c->hostname, o->length - 1);
254 clib_memcpy (c->hostname, o->data, o->length);
255 }
256 break;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700257
khemendra kumar34719e32017-12-08 18:06:52 +0530258 /* $$$$ Your message in this space, parse more options */
259 default:
260 break;
261 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700262
263 o = (dhcp_option_t *) (((uword) o) + (o->length + 2));
264 }
265
266 switch (c->state)
267 {
268 case DHCP_DISCOVER:
269 if (dhcp_message_type != DHCP_PACKET_OFFER)
khemendra kumar34719e32017-12-08 18:06:52 +0530270 {
Dave Barach4585eb82018-03-23 09:52:52 -0400271 vlib_node_increment_counter (vm, dhcp_client_process_node.index,
272 DHCP_STAT_NON_OFFER_DISCOVER, 1);
khemendra kumar34719e32017-12-08 18:06:52 +0530273 c->next_transmit = now + 5.0;
274 break;
275 }
Neale Ranns808c5b22017-08-02 05:15:07 -0700276
Ed Warnickecb9cada2015-12-08 15:45:58 -0700277 /* Received an offer, go send a request */
278 c->state = DHCP_REQUEST;
279 c->retry_count = 0;
khemendra kumar34719e32017-12-08 18:06:52 +0530280 c->next_transmit = 0; /* send right now... */
Ed Warnickecb9cada2015-12-08 15:45:58 -0700281 /* Poke the client process, which will send the request */
khemendra kumar34719e32017-12-08 18:06:52 +0530282 uword client_id = c - dcm->clients;
John Lo609707e2017-09-19 21:45:10 -0400283 vl_api_rpc_call_main_thread (dhcp_client_proc_callback,
khemendra kumar34719e32017-12-08 18:06:52 +0530284 (u8 *) & client_id, sizeof (uword));
Ed Warnickecb9cada2015-12-08 15:45:58 -0700285 break;
286
287 case DHCP_BOUND:
288 case DHCP_REQUEST:
Dave Barach43ebe292018-03-21 13:45:12 -0400289 if (dhcp_message_type == DHCP_PACKET_NAK)
290 {
Dave Barach4585eb82018-03-23 09:52:52 -0400291 vlib_node_increment_counter (vm, dhcp_client_process_node.index,
292 DHCP_STAT_NAK, 1);
Dave Barach43ebe292018-03-21 13:45:12 -0400293 /* Probably never happens in bound state, but anyhow... */
294 if (c->state == DHCP_BOUND)
295 {
296 ip4_add_del_interface_address (dcm->vlib_main, c->sw_if_index,
297 (void *) &c->leased_address,
298 c->subnet_mask_width,
299 1 /*is_del */ );
300 vnet_feature_enable_disable ("ip4-unicast",
301 "ip4-dhcp-client-detect",
Dave Barach4585eb82018-03-23 09:52:52 -0400302 c->sw_if_index, 1 /* enable */ ,
303 0, 0);
Dave Barach43ebe292018-03-21 13:45:12 -0400304 }
305 /* Wipe out any memory of the address we had... */
306 c->state = DHCP_DISCOVER;
307 c->next_transmit = now;
308 c->retry_count = 0;
309 c->leased_address.as_u32 = 0;
310 c->subnet_mask_width = 0;
311 c->router_address.as_u32 = 0;
312 c->lease_renewal_interval = 0;
313 c->dhcp_server.as_u32 = 0;
314 break;
315 }
316
317 if (dhcp_message_type != DHCP_PACKET_ACK &&
318 dhcp_message_type != DHCP_PACKET_OFFER)
khemendra kumar34719e32017-12-08 18:06:52 +0530319 {
Dave Barach4585eb82018-03-23 09:52:52 -0400320 vlib_node_increment_counter (vm, dhcp_client_process_node.index,
321 DHCP_STAT_NON_OFFER_DISCOVER, 1);
khemendra kumar34719e32017-12-08 18:06:52 +0530322 clib_warning ("sw_if_index %d state %U message type %d",
323 c->sw_if_index, format_dhcp_client_state,
324 c->state, dhcp_message_type);
325 c->next_transmit = now + 5.0;
326 break;
327 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700328 /* OK, we own the address (etc), add to the routing table(s) */
329 if (c->state == DHCP_REQUEST)
Matthew Smith7a192832018-02-10 11:11:45 -0600330 vl_api_rpc_call_main_thread (dhcp_client_addr_callback,
331 (u8 *) c, sizeof (*c));
Ed Warnickecb9cada2015-12-08 15:45:58 -0700332
333 c->state = DHCP_BOUND;
334 c->retry_count = 0;
335 c->next_transmit = now + (f64) c->lease_renewal_interval;
336 c->lease_expires = now + (f64) c->lease_lifetime;
Dave Barach4585eb82018-03-23 09:52:52 -0400337 vlib_node_increment_counter (vm, dhcp_client_process_node.index,
338 DHCP_STAT_BOUND, 1);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700339 break;
340
341 default:
khemendra kumar34719e32017-12-08 18:06:52 +0530342 clib_warning ("client %d bogus state %d", c - dcm->clients, c->state);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700343 break;
344 }
345
346 /* drop the pkt, return 1 */
347 vlib_buffer_free (vm, &bi, 1);
348 return 1;
349}
350
khemendra kumar34719e32017-12-08 18:06:52 +0530351static void
352send_dhcp_pkt (dhcp_client_main_t * dcm, dhcp_client_t * c,
353 dhcp_packet_type_t type, int is_broadcast)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700354{
khemendra kumar34719e32017-12-08 18:06:52 +0530355 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
Ed Warnickecb9cada2015-12-08 15:45:58 -0700359 = vnet_get_sup_sw_interface (vnm, c->sw_if_index);
khemendra kumar34719e32017-12-08 18:06:52 +0530360 vnet_sw_interface_t *sw = vnet_get_sw_interface (vnm, c->sw_if_index);
361 vlib_buffer_t *b;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700362 u32 bi;
khemendra kumar34719e32017-12-08 18:06:52 +0530363 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;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700369 u16 udp_length, ip_length;
Dave Barach4941fcc2018-02-14 17:58:33 -0500370 u32 counter_index;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700371
372 /* Interface(s) down? */
373 if ((hw->flags & VNET_HW_INTERFACE_FLAG_LINK_UP) == 0)
374 return;
375 if ((sup_sw->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) == 0)
376 return;
377 if ((sw->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) == 0)
378 return;
379
khemendra kumar34719e32017-12-08 18:06:52 +0530380 if (vlib_buffer_alloc (vm, &bi, 1) != 1)
381 {
382 clib_warning ("buffer allocation failure");
383 c->next_transmit = 0;
384 return;
385 }
386
Ed Warnickecb9cada2015-12-08 15:45:58 -0700387 /* Build a dhcpv4 pkt from whole cloth */
388 b = vlib_get_buffer (vm, bi);
389
390 ASSERT (b->current_data == 0);
391
khemendra kumar34719e32017-12-08 18:06:52 +0530392 vnet_buffer (b)->sw_if_index[VLIB_RX] = c->sw_if_index;
393 if (is_broadcast)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700394 {
395 f = vlib_get_frame_to_node (vm, hw->output_node_index);
khemendra kumar34719e32017-12-08 18:06:52 +0530396 vnet_buffer (b)->sw_if_index[VLIB_TX] = c->sw_if_index;
397 clib_memcpy (b->data, c->l2_rewrite, vec_len (c->l2_rewrite));
Ed Warnickecb9cada2015-12-08 15:45:58 -0700398 ip = (void *)
khemendra kumar34719e32017-12-08 18:06:52 +0530399 (((u8 *) vlib_buffer_get_current (b)) + vec_len (c->l2_rewrite));
Ed Warnickecb9cada2015-12-08 15:45:58 -0700400 }
401 else
402 {
403 f = vlib_get_frame_to_node (vm, ip4_lookup_node.index);
khemendra kumar34719e32017-12-08 18:06:52 +0530404 vnet_buffer (b)->sw_if_index[VLIB_TX] = ~0; /* use interface VRF */
Ed Warnickecb9cada2015-12-08 15:45:58 -0700405 ip = vlib_buffer_get_current (b);
406 }
407
408 /* Enqueue the packet right now */
409 to_next = vlib_frame_vector_args (f);
410 to_next[0] = bi;
411 f->n_vectors = 1;
412
413 if (is_broadcast)
414 vlib_put_frame_to_node (vm, hw->output_node_index, f);
415 else
416 vlib_put_frame_to_node (vm, ip4_lookup_node.index, f);
khemendra kumar34719e32017-12-08 18:06:52 +0530417
418 udp = (udp_header_t *) (ip + 1);
419 dhcp = (dhcp_header_t *) (udp + 1);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700420
421 /* $$$ optimize, maybe */
422 memset (ip, 0, sizeof (*ip) + sizeof (*udp) + sizeof (*dhcp));
423
424 ip->ip_version_and_header_length = 0x45;
425 ip->ttl = 128;
426 ip->protocol = IP_PROTOCOL_UDP;
khemendra kumar34719e32017-12-08 18:06:52 +0530427
Ed Warnickecb9cada2015-12-08 15:45:58 -0700428 if (is_broadcast)
429 {
430 /* src = 0.0.0.0, dst = 255.255.255.255 */
431 ip->dst_address.as_u32 = ~0;
432 }
433 else
434 {
435 /* Renewing an active lease, plain old ip4 src/dst */
436 ip->src_address.as_u32 = c->leased_address.as_u32;
437 ip->dst_address.as_u32 = c->dhcp_server.as_u32;
438 }
439
440 udp->src_port = clib_host_to_net_u16 (UDP_DST_PORT_dhcp_to_client);
441 udp->dst_port = clib_host_to_net_u16 (UDP_DST_PORT_dhcp_to_server);
442
443 /* Send the interface MAC address */
Damjan Marionf1213b82016-03-13 02:22:06 +0100444 clib_memcpy (dhcp->client_hardware_address, c->l2_rewrite + 6, 6);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700445
Dave Barach4585eb82018-03-23 09:52:52 -0400446 /* And remember it for rx-packet-for-us checking */
447 clib_memcpy (c->client_hardware_address, dhcp->client_hardware_address,
448 sizeof (c->client_hardware_address));
449
Ed Warnickecb9cada2015-12-08 15:45:58 -0700450 /* Lease renewal, set up client_ip_address */
451 if (is_broadcast == 0)
452 dhcp->client_ip_address.as_u32 = c->leased_address.as_u32;
453
khemendra kumar34719e32017-12-08 18:06:52 +0530454 dhcp->opcode = 1; /* request, all we send */
455 dhcp->hardware_type = 1; /* ethernet */
456 dhcp->hardware_address_length = 6;
457 dhcp->transaction_identifier = c->transaction_id;
Neale Ranns54c6dc42018-01-17 10:29:10 -0800458 dhcp->flags =
459 clib_host_to_net_u16 (is_broadcast && c->set_broadcast_flag ?
460 DHCP_FLAG_BROADCAST : 0);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700461 dhcp->magic_cookie.as_u32 = DHCP_MAGIC;
khemendra kumar34719e32017-12-08 18:06:52 +0530462
463 o = (dhcp_option_t *) dhcp->options;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700464
465 /* Send option 53, the DHCP message type */
Neale Ranns3466c302017-02-16 07:45:03 -0800466 o->option = DHCP_PACKET_OPTION_MSG_TYPE;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700467 o->length = 1;
468 o->data[0] = type;
469 o = (dhcp_option_t *) (((uword) o) + (o->length + 2));
470
471 /* Send option 57, max msg length */
khemendra kumar34719e32017-12-08 18:06:52 +0530472 if (0 /* not needed, apparently */ )
Ed Warnickecb9cada2015-12-08 15:45:58 -0700473 {
474 o->option = 57;
475 o->length = 2;
476 {
khemendra kumar34719e32017-12-08 18:06:52 +0530477 u16 *o2 = (u16 *) o->data;
478 *o2 = clib_host_to_net_u16 (1152);
479 o = (dhcp_option_t *) (((uword) o) + (o->length + 2));
Ed Warnickecb9cada2015-12-08 15:45:58 -0700480 }
481 }
482
483 /*
484 * If server ip address is available with non-zero value,
485 * option 54 (DHCP Server Identifier) is sent.
486 */
487 if (c->dhcp_server.as_u32)
488 {
489 o->option = 54;
490 o->length = 4;
Damjan Marionf1213b82016-03-13 02:22:06 +0100491 clib_memcpy (o->data, &c->dhcp_server.as_u32, 4);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700492 o = (dhcp_option_t *) (((uword) o) + (o->length + 2));
493 }
494
495 /* send option 50, requested IP address */
496 if (c->leased_address.as_u32)
497 {
498 o->option = 50;
499 o->length = 4;
Damjan Marionf1213b82016-03-13 02:22:06 +0100500 clib_memcpy (o->data, &c->leased_address.as_u32, 4);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700501 o = (dhcp_option_t *) (((uword) o) + (o->length + 2));
502 }
503
504 /* send option 12, host name */
505 if (vec_len (c->hostname))
506 {
507 o->option = 12;
508 o->length = vec_len (c->hostname);
Damjan Marionf1213b82016-03-13 02:22:06 +0100509 clib_memcpy (o->data, c->hostname, vec_len (c->hostname));
Ed Warnickecb9cada2015-12-08 15:45:58 -0700510 o = (dhcp_option_t *) (((uword) o) + (o->length + 2));
511 }
512
Neale Ranns51822bf2017-07-18 09:26:53 -0700513 /* send option 61, client_id */
514 if (vec_len (c->client_identifier))
515 {
516 o->option = 61;
517 o->length = vec_len (c->client_identifier);
518 clib_memcpy (o->data, c->client_identifier,
khemendra kumar34719e32017-12-08 18:06:52 +0530519 vec_len (c->client_identifier));
Neale Ranns51822bf2017-07-18 09:26:53 -0700520 o = (dhcp_option_t *) (((uword) o) + (o->length + 2));
521 }
522
Ed Warnickecb9cada2015-12-08 15:45:58 -0700523 /* $$ maybe send the client s/w version if anyone cares */
524
khemendra kumar34719e32017-12-08 18:06:52 +0530525 /*
Ed Warnickecb9cada2015-12-08 15:45:58 -0700526 * send option 55, parameter request list
527 * The current list - see below, matches the Linux dhcp client's list
528 * Any specific dhcp server config and/or dhcp server may or may
529 * not yield specific options.
530 */
531 o->option = 55;
532 o->length = vec_len (c->option_55_data);
khemendra kumar34719e32017-12-08 18:06:52 +0530533 clib_memcpy (o->data, c->option_55_data, vec_len (c->option_55_data));
Ed Warnickecb9cada2015-12-08 15:45:58 -0700534 o = (dhcp_option_t *) (((uword) o) + (o->length + 2));
535
536 /* End of list */
537 o->option = 0xff;
538 o->length = 0;
539 o++;
khemendra kumar34719e32017-12-08 18:06:52 +0530540
541 b->current_length = ((u8 *) o) - b->data;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700542
543 /* fix ip length, checksum and udp length */
544 ip_length = vlib_buffer_length_in_chain (vm, b);
545 if (is_broadcast)
khemendra kumar34719e32017-12-08 18:06:52 +0530546 ip_length -= vec_len (c->l2_rewrite);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700547
khemendra kumar34719e32017-12-08 18:06:52 +0530548 ip->length = clib_host_to_net_u16 (ip_length);
549 ip->checksum = ip4_header_checksum (ip);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700550
551 udp_length = ip_length - (sizeof (*ip));
552 udp->length = clib_host_to_net_u16 (udp_length);
Dave Barach4941fcc2018-02-14 17:58:33 -0500553
554 switch (type)
555 {
556#define _(a,b) case DHCP_PACKET_##a: {counter_index = DHCP_STAT_##a; break;}
Dave Barach4585eb82018-03-23 09:52:52 -0400557 foreach_dhcp_sent_packet_stat
Dave Barach4941fcc2018-02-14 17:58:33 -0500558#undef _
559 default:
560 counter_index = DHCP_STAT_UNKNOWN;
561 break;
562 }
563
564 vlib_node_increment_counter (vm, dhcp_client_process_node.index,
565 counter_index, 1);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700566}
567
khemendra kumar34719e32017-12-08 18:06:52 +0530568static int
Ed Warnickecb9cada2015-12-08 15:45:58 -0700569dhcp_discover_state (dhcp_client_main_t * dcm, dhcp_client_t * c, f64 now)
570{
571 /*
572 * State machine "DISCOVER" state. Send a dhcp discover packet,
573 * eventually back off the retry rate.
574 */
khemendra kumar34719e32017-12-08 18:06:52 +0530575 send_dhcp_pkt (dcm, c, DHCP_PACKET_DISCOVER, 1 /* is_broadcast */ );
Ed Warnickecb9cada2015-12-08 15:45:58 -0700576
577 c->retry_count++;
578 if (c->retry_count > 10)
579 c->next_transmit = now + 5.0;
580 else
581 c->next_transmit = now + 1.0;
582 return 0;
583}
khemendra kumar34719e32017-12-08 18:06:52 +0530584
585static int
Ed Warnickecb9cada2015-12-08 15:45:58 -0700586dhcp_request_state (dhcp_client_main_t * dcm, dhcp_client_t * c, f64 now)
khemendra kumar34719e32017-12-08 18:06:52 +0530587{
Ed Warnickecb9cada2015-12-08 15:45:58 -0700588 /*
589 * State machine "REQUEST" state. Send a dhcp request packet,
590 * eventually drop back to the discover state.
591 */
khemendra kumar34719e32017-12-08 18:06:52 +0530592 send_dhcp_pkt (dcm, c, DHCP_PACKET_REQUEST, 1 /* is_broadcast */ );
Ed Warnickecb9cada2015-12-08 15:45:58 -0700593
594 c->retry_count++;
khemendra kumar34719e32017-12-08 18:06:52 +0530595 if (c->retry_count > 7 /* lucky you */ )
Ed Warnickecb9cada2015-12-08 15:45:58 -0700596 {
597 c->state = DHCP_DISCOVER;
598 c->next_transmit = now;
599 c->retry_count = 0;
600 return 1;
601 }
602 c->next_transmit = now + 1.0;
603 return 0;
604}
605
khemendra kumar34719e32017-12-08 18:06:52 +0530606static int
Ed Warnickecb9cada2015-12-08 15:45:58 -0700607dhcp_bound_state (dhcp_client_main_t * dcm, dhcp_client_t * c, f64 now)
608{
609 /*
610 * State machine "BOUND" state. Send a dhcp request packet,
611 * eventually, when the lease expires, forget the dhcp data
612 * and go back to the stone age.
613 */
khemendra kumar34719e32017-12-08 18:06:52 +0530614 send_dhcp_pkt (dcm, c, DHCP_PACKET_REQUEST, 0 /* is_broadcast */ );
615
Ed Warnickecb9cada2015-12-08 15:45:58 -0700616 c->retry_count++;
617 if (c->retry_count > 10)
618 c->next_transmit = now + 5.0;
619 else
620 c->next_transmit = now + 1.0;
khemendra kumar34719e32017-12-08 18:06:52 +0530621
Ed Warnickecb9cada2015-12-08 15:45:58 -0700622 if (now > c->lease_expires)
623 {
Dave Barach4941fcc2018-02-14 17:58:33 -0500624 /* Remove the default route */
Ed Warnickecb9cada2015-12-08 15:45:58 -0700625 if (c->router_address.as_u32)
khemendra kumar34719e32017-12-08 18:06:52 +0530626 {
627 fib_prefix_t all_0s = {
628 .fp_len = 0,
629 .fp_addr.ip4.as_u32 = 0x0,
630 .fp_proto = FIB_PROTOCOL_IP4,
Neale Ranns0bfe5d82016-08-25 15:29:12 +0100631 };
632 ip46_address_t nh = {
khemendra kumar34719e32017-12-08 18:06:52 +0530633 .ip4 = c->router_address,
Neale Ranns0bfe5d82016-08-25 15:29:12 +0100634 };
635
khemendra kumar34719e32017-12-08 18:06:52 +0530636 fib_table_entry_path_remove (fib_table_get_index_for_sw_if_index
637 (FIB_PROTOCOL_IP4, c->sw_if_index),
638 &all_0s, FIB_SOURCE_DHCP,
639 DPO_PROTO_IP4, &nh, c->sw_if_index, ~0,
640 1, FIB_ROUTE_PATH_FLAG_NONE);
Neale Ranns0bfe5d82016-08-25 15:29:12 +0100641 }
Dave Barach4941fcc2018-02-14 17:58:33 -0500642 /* Remove the interface address */
Ed Warnickecb9cada2015-12-08 15:45:58 -0700643 dhcp_client_release_address (dcm, c);
644 c->state = DHCP_DISCOVER;
645 c->next_transmit = now;
646 c->retry_count = 0;
647 /* Wipe out any memory of the address we had... */
648 c->leased_address.as_u32 = 0;
649 c->subnet_mask_width = 0;
650 c->router_address.as_u32 = 0;
651 c->lease_renewal_interval = 0;
652 c->dhcp_server.as_u32 = 0;
Dave Barach4941fcc2018-02-14 17:58:33 -0500653 /*
654 * We disable the client detect feature when we bind a
655 * DHCP address. Turn it back on again here.
656 * Otherwise, if the DHCP server replies after an outage,
657 * we'll never see it.
658 */
659 vnet_feature_enable_disable ("ip4-unicast",
660 "ip4-dhcp-client-detect",
Dave Barach4585eb82018-03-23 09:52:52 -0400661 c->sw_if_index, 1 /* enable */ , 0, 0);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700662 return 1;
663 }
664 return 0;
665}
666
khemendra kumar34719e32017-12-08 18:06:52 +0530667static f64
668dhcp_client_sm (f64 now, f64 timeout, uword pool_index)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700669{
khemendra kumar34719e32017-12-08 18:06:52 +0530670 dhcp_client_main_t *dcm = &dhcp_client_main;
671 dhcp_client_t *c;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700672
673 /* deleted, pooched, yadda yadda yadda */
674 if (pool_is_free_index (dcm->clients, pool_index))
675 return timeout;
676
677 c = pool_elt_at_index (dcm->clients, pool_index);
678
679 /* Time for us to do something with this client? */
680 if (now < c->next_transmit)
681 return timeout;
682
khemendra kumar34719e32017-12-08 18:06:52 +0530683again:
Ed Warnickecb9cada2015-12-08 15:45:58 -0700684 switch (c->state)
685 {
khemendra kumar34719e32017-12-08 18:06:52 +0530686 case DHCP_DISCOVER: /* send a discover */
Ed Warnickecb9cada2015-12-08 15:45:58 -0700687 if (dhcp_discover_state (dcm, c, now))
khemendra kumar34719e32017-12-08 18:06:52 +0530688 goto again;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700689 break;
690
khemendra kumar34719e32017-12-08 18:06:52 +0530691 case DHCP_REQUEST: /* send a request */
Ed Warnickecb9cada2015-12-08 15:45:58 -0700692 if (dhcp_request_state (dcm, c, now))
khemendra kumar34719e32017-12-08 18:06:52 +0530693 goto again;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700694 break;
khemendra kumar34719e32017-12-08 18:06:52 +0530695
696 case DHCP_BOUND: /* bound, renew needed? */
Ed Warnickecb9cada2015-12-08 15:45:58 -0700697 if (dhcp_bound_state (dcm, c, now))
khemendra kumar34719e32017-12-08 18:06:52 +0530698 goto again;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700699 break;
khemendra kumar34719e32017-12-08 18:06:52 +0530700
Ed Warnickecb9cada2015-12-08 15:45:58 -0700701 default:
khemendra kumar34719e32017-12-08 18:06:52 +0530702 clib_warning ("dhcp client %d bogus state %d",
703 c - dcm->clients, c->state);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700704 break;
705 }
khemendra kumar34719e32017-12-08 18:06:52 +0530706
Ed Warnickecb9cada2015-12-08 15:45:58 -0700707 if (c->next_transmit < now + timeout)
708 return c->next_transmit - now;
709
710 return timeout;
711}
712
713static uword
714dhcp_client_process (vlib_main_t * vm,
khemendra kumar34719e32017-12-08 18:06:52 +0530715 vlib_node_runtime_t * rt, vlib_frame_t * f)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700716{
717 f64 timeout = 100.0;
718 f64 now;
719 uword event_type;
khemendra kumar34719e32017-12-08 18:06:52 +0530720 uword *event_data = 0;
721 dhcp_client_main_t *dcm = &dhcp_client_main;
722 dhcp_client_t *c;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700723 int i;
724
725 while (1)
726 {
727 vlib_process_wait_for_event_or_clock (vm, timeout);
728
729 event_type = vlib_process_get_events (vm, &event_data);
730
731 now = vlib_time_now (vm);
732
733 switch (event_type)
khemendra kumar34719e32017-12-08 18:06:52 +0530734 {
735 case EVENT_DHCP_CLIENT_WAKEUP:
736 for (i = 0; i < vec_len (event_data); i++)
737 timeout = dhcp_client_sm (now, timeout, event_data[i]);
738 break;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700739
khemendra kumar34719e32017-12-08 18:06:52 +0530740 case ~0:
Neale Ranns54c6dc42018-01-17 10:29:10 -0800741 /* *INDENT-OFF* */
742 pool_foreach (c, dcm->clients,
743 ({
744 timeout = dhcp_client_sm (now, timeout,
745 (uword) (c - dcm->clients));
746 }));
747 /* *INDENT-ON* */
khemendra kumar34719e32017-12-08 18:06:52 +0530748 if (pool_elts (dcm->clients) == 0)
749 timeout = 100.0;
750 break;
751 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700752
753 vec_reset_length (event_data);
754 }
755
756 /* NOTREACHED */
757 return 0;
758}
759
khemendra kumar34719e32017-12-08 18:06:52 +0530760/* *INDENT-OFF* */
Ed Warnickecb9cada2015-12-08 15:45:58 -0700761VLIB_REGISTER_NODE (dhcp_client_process_node,static) = {
762 .function = dhcp_client_process,
763 .type = VLIB_NODE_TYPE_PROCESS,
764 .name = "dhcp-client-process",
765 .process_log2_n_stack_bytes = 16,
Dave Barach4941fcc2018-02-14 17:58:33 -0500766 .n_errors = ARRAY_LEN(dhcp_client_process_stat_strings),
767 .error_strings = dhcp_client_process_stat_strings,
Ed Warnickecb9cada2015-12-08 15:45:58 -0700768};
khemendra kumar34719e32017-12-08 18:06:52 +0530769/* *INDENT-ON* */
Ed Warnickecb9cada2015-12-08 15:45:58 -0700770
khemendra kumar34719e32017-12-08 18:06:52 +0530771static u8 *
772format_dhcp_client_state (u8 * s, va_list * va)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700773{
774 dhcp_client_state_t state = va_arg (*va, dhcp_client_state_t);
khemendra kumar34719e32017-12-08 18:06:52 +0530775 char *str = "BOGUS!";
Ed Warnickecb9cada2015-12-08 15:45:58 -0700776
777 switch (state)
778 {
779#define _(a) \
780 case a: \
781 str = #a; \
782 break;
783 foreach_dhcp_client_state;
784#undef _
785 default:
786 break;
787 }
788
789 s = format (s, "%s", str);
790 return s;
791}
792
khemendra kumar34719e32017-12-08 18:06:52 +0530793static u8 *
794format_dhcp_client (u8 * s, va_list * va)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700795{
khemendra kumar34719e32017-12-08 18:06:52 +0530796 dhcp_client_main_t *dcm = va_arg (*va, dhcp_client_main_t *);
797 dhcp_client_t *c = va_arg (*va, dhcp_client_t *);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700798 int verbose = va_arg (*va, int);
799
khemendra kumar34719e32017-12-08 18:06:52 +0530800 s = format (s, "[%d] %U state %U ", c - dcm->clients,
801 format_vnet_sw_if_index_name, dcm->vnet_main, c->sw_if_index,
802 format_dhcp_client_state, c->state);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700803
804 if (c->leased_address.as_u32)
805 s = format (s, "addr %U/%d gw %U\n",
khemendra kumar34719e32017-12-08 18:06:52 +0530806 format_ip4_address, &c->leased_address,
807 c->subnet_mask_width, format_ip4_address, &c->router_address);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700808 else
809 s = format (s, "no address\n");
810
811 if (verbose)
812 {
813 s = format (s, "retry count %d, next xmt %.2f",
khemendra kumar34719e32017-12-08 18:06:52 +0530814 c->retry_count, c->next_transmit);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700815 }
816 return s;
817}
818
819static clib_error_t *
820show_dhcp_client_command_fn (vlib_main_t * vm,
khemendra kumar34719e32017-12-08 18:06:52 +0530821 unformat_input_t * input,
822 vlib_cli_command_t * cmd)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700823{
khemendra kumar34719e32017-12-08 18:06:52 +0530824 dhcp_client_main_t *dcm = &dhcp_client_main;
825 dhcp_client_t *c;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700826 int verbose = 0;
827 u32 sw_if_index = ~0;
khemendra kumar34719e32017-12-08 18:06:52 +0530828 uword *p;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700829
khemendra kumar34719e32017-12-08 18:06:52 +0530830 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700831 {
khemendra kumar34719e32017-12-08 18:06:52 +0530832 if (unformat (input, "intfc %U",
833 unformat_vnet_sw_interface, dcm->vnet_main, &sw_if_index))
834 ;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700835 else if (unformat (input, "verbose"))
khemendra kumar34719e32017-12-08 18:06:52 +0530836 verbose = 1;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700837 else
khemendra kumar34719e32017-12-08 18:06:52 +0530838 break;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700839 }
840
841 if (sw_if_index != ~0)
842 {
843 p = hash_get (dcm->client_by_sw_if_index, sw_if_index);
844 if (p == 0)
khemendra kumar34719e32017-12-08 18:06:52 +0530845 return clib_error_return (0, "dhcp client not configured");
Neale Ranns0bfe5d82016-08-25 15:29:12 +0100846 c = pool_elt_at_index (dcm->clients, p[0]);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700847 vlib_cli_output (vm, "%U", format_dhcp_client, dcm, c, verbose);
848 return 0;
849 }
850
Neale Ranns54c6dc42018-01-17 10:29:10 -0800851 /* *INDENT-OFF* */
852 pool_foreach (c, dcm->clients,
853 ({
854 vlib_cli_output (vm, "%U",
855 format_dhcp_client, dcm,
856 c, verbose);
857 }));
858 /* *INDENT-ON* */
khemendra kumar34719e32017-12-08 18:06:52 +0530859
Ed Warnickecb9cada2015-12-08 15:45:58 -0700860 return 0;
861}
862
khemendra kumar34719e32017-12-08 18:06:52 +0530863/* *INDENT-OFF* */
Ed Warnickecb9cada2015-12-08 15:45:58 -0700864VLIB_CLI_COMMAND (show_dhcp_client_command, static) = {
865 .path = "show dhcp client",
866 .short_help = "show dhcp client [intfc <intfc>][verbose]",
867 .function = show_dhcp_client_command_fn,
868};
khemendra kumar34719e32017-12-08 18:06:52 +0530869/* *INDENT-ON* */
Ed Warnickecb9cada2015-12-08 15:45:58 -0700870
871
khemendra kumar34719e32017-12-08 18:06:52 +0530872int
873dhcp_client_add_del (dhcp_client_add_del_args_t * a)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700874{
khemendra kumar34719e32017-12-08 18:06:52 +0530875 dhcp_client_main_t *dcm = &dhcp_client_main;
876 vlib_main_t *vm = dcm->vlib_main;
877 dhcp_client_t *c;
878 uword *p;
khemendra kumar34719e32017-12-08 18:06:52 +0530879 fib_prefix_t all_0s = {
880 .fp_len = 0,
881 .fp_addr.ip4.as_u32 = 0x0,
882 .fp_proto = FIB_PROTOCOL_IP4,
Neale Ranns0bfe5d82016-08-25 15:29:12 +0100883 };
Ed Warnickecb9cada2015-12-08 15:45:58 -0700884
885 p = hash_get (dcm->client_by_sw_if_index, a->sw_if_index);
886
887 if ((p && a->is_add) || (!p && a->is_add == 0))
888 return VNET_API_ERROR_INVALID_VALUE;
889
890 if (a->is_add)
891 {
892 pool_get (dcm->clients, c);
893 memset (c, 0, sizeof (*c));
894 c->state = DHCP_DISCOVER;
895 c->sw_if_index = a->sw_if_index;
896 c->client_index = a->client_index;
897 c->pid = a->pid;
898 c->event_callback = a->event_callback;
899 c->option_55_data = a->option_55_data;
900 c->hostname = a->hostname;
901 c->client_identifier = a->client_identifier;
Neale Ranns54c6dc42018-01-17 10:29:10 -0800902 c->set_broadcast_flag = a->set_broadcast_flag;
khemendra kumar34719e32017-12-08 18:06:52 +0530903 do
904 {
905 c->transaction_id = random_u32 (&dcm->seed);
906 }
907 while (c->transaction_id == 0);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700908 set_l2_rewrite (dcm, c);
909 hash_set (dcm->client_by_sw_if_index, a->sw_if_index, c - dcm->clients);
Neale Ranns0bfe5d82016-08-25 15:29:12 +0100910
khemendra kumar34719e32017-12-08 18:06:52 +0530911 /*
Neale Ranns54c6dc42018-01-17 10:29:10 -0800912 * In order to accept any OFFER, whether broadcasted or unicasted, we
913 * need to configure the dhcp-client-detect feature as an input feature
914 * so the DHCP OFFER is sent to the ip4-local node. Without this a
915 * broadcasted OFFER hits the 255.255.255.255/32 address and a unicast
916 * hits 0.0.0.0/0 both of which default to drop and the latter may forward
917 * of box - not what we want. Nor to we want to change these route for
918 * all interfaces in this table
Neale Ranns0bfe5d82016-08-25 15:29:12 +0100919 */
Neale Ranns54c6dc42018-01-17 10:29:10 -0800920 vnet_feature_enable_disable ("ip4-unicast",
921 "ip4-dhcp-client-detect",
Dave Barach4585eb82018-03-23 09:52:52 -0400922 c->sw_if_index, 1 /* enable */ , 0, 0);
Neale Ranns0bfe5d82016-08-25 15:29:12 +0100923
khemendra kumar34719e32017-12-08 18:06:52 +0530924 vlib_process_signal_event (vm, dhcp_client_process_node.index,
925 EVENT_DHCP_CLIENT_WAKEUP, c - dcm->clients);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700926 }
927 else
928 {
929 c = pool_elt_at_index (dcm->clients, p[0]);
930
931 if (c->router_address.as_u32)
khemendra kumar34719e32017-12-08 18:06:52 +0530932 {
Neale Ranns0bfe5d82016-08-25 15:29:12 +0100933 ip46_address_t nh = {
khemendra kumar34719e32017-12-08 18:06:52 +0530934 .ip4 = c->router_address,
Neale Ranns0bfe5d82016-08-25 15:29:12 +0100935 };
936
khemendra kumar34719e32017-12-08 18:06:52 +0530937 fib_table_entry_path_remove (fib_table_get_index_for_sw_if_index
938 (FIB_PROTOCOL_IP4, c->sw_if_index),
939 &all_0s, FIB_SOURCE_DHCP,
940 DPO_PROTO_IP4, &nh, c->sw_if_index, ~0,
941 1, FIB_ROUTE_PATH_FLAG_NONE);
942 }
Neale Rannsa2fbf6b2017-07-18 08:23:32 -0700943 dhcp_client_release_address (dcm, c);
Neale Ranns0bfe5d82016-08-25 15:29:12 +0100944
Ed Warnickecb9cada2015-12-08 15:45:58 -0700945 vec_free (c->option_55_data);
946 vec_free (c->hostname);
947 vec_free (c->client_identifier);
948 vec_free (c->l2_rewrite);
949 hash_unset (dcm->client_by_sw_if_index, c->sw_if_index);
950 pool_put (dcm->clients, c);
951 }
952 return 0;
953}
954
955int
956dhcp_client_config (vlib_main_t * vm,
khemendra kumar34719e32017-12-08 18:06:52 +0530957 u32 sw_if_index,
958 u8 * hostname,
959 u8 * client_id,
960 u32 is_add,
Neale Ranns54c6dc42018-01-17 10:29:10 -0800961 u32 client_index,
962 void *event_callback, u8 set_broadcast_flag, u32 pid)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700963{
964 dhcp_client_add_del_args_t _a, *a = &_a;
965 int rv;
966
967 memset (a, 0, sizeof (*a));
968 a->is_add = is_add;
969 a->sw_if_index = sw_if_index;
970 a->client_index = client_index;
971 a->pid = pid;
972 a->event_callback = event_callback;
Neale Ranns54c6dc42018-01-17 10:29:10 -0800973 a->set_broadcast_flag = set_broadcast_flag;
khemendra kumar34719e32017-12-08 18:06:52 +0530974 vec_validate (a->hostname, strlen ((char *) hostname) - 1);
975 strncpy ((char *) a->hostname, (char *) hostname, vec_len (a->hostname));
976 vec_validate (a->client_identifier, strlen ((char *) client_id) - 1);
977 strncpy ((char *) a->client_identifier, (char *) client_id,
978 vec_len (a->client_identifier));
Neale Ranns51822bf2017-07-18 09:26:53 -0700979
khemendra kumar34719e32017-12-08 18:06:52 +0530980 /*
Ed Warnickecb9cada2015-12-08 15:45:58 -0700981 * Option 55 request list. These data precisely match
982 * the Ubuntu dhcp client. YMMV.
983 */
984
985 /* Subnet Mask */
986 vec_add1 (a->option_55_data, 1);
987 /* Broadcast address */
988 vec_add1 (a->option_55_data, 28);
989 /* time offset */
990 vec_add1 (a->option_55_data, 2);
991 /* Router */
992 vec_add1 (a->option_55_data, 3);
993 /* Domain Name */
994 vec_add1 (a->option_55_data, 15);
995 /* DNS */
996 vec_add1 (a->option_55_data, 6);
997 /* Domain search */
998 vec_add1 (a->option_55_data, 119);
999 /* Host name */
1000 vec_add1 (a->option_55_data, 12);
1001 /* NetBIOS name server */
1002 vec_add1 (a->option_55_data, 44);
1003 /* NetBIOS Scope */
1004 vec_add1 (a->option_55_data, 47);
1005 /* MTU */
1006 vec_add1 (a->option_55_data, 26);
1007 /* Classless static route */
1008 vec_add1 (a->option_55_data, 121);
1009 /* NTP servers */
1010 vec_add1 (a->option_55_data, 42);
1011
1012 rv = dhcp_client_add_del (a);
1013
1014 switch (rv)
1015 {
1016 case 0:
1017 break;
1018
1019 case VNET_API_ERROR_INVALID_VALUE:
1020
1021 vec_free (a->hostname);
1022 vec_free (a->client_identifier);
1023 vec_free (a->option_55_data);
1024
1025 if (is_add)
khemendra kumar34719e32017-12-08 18:06:52 +05301026 clib_warning ("dhcp client already enabled on intf_idx %d",
1027 sw_if_index);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001028 else
khemendra kumar34719e32017-12-08 18:06:52 +05301029 clib_warning ("dhcp client not enabled on on intf_idx %d",
1030 sw_if_index);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001031 break;
1032
1033 default:
1034 clib_warning ("dhcp_client_add_del returned %d", rv);
1035 }
1036
1037 return rv;
1038}
1039
1040static clib_error_t *
1041dhcp_client_set_command_fn (vlib_main_t * vm,
khemendra kumar34719e32017-12-08 18:06:52 +05301042 unformat_input_t * input,
1043 vlib_cli_command_t * cmd)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001044{
1045
khemendra kumar34719e32017-12-08 18:06:52 +05301046 dhcp_client_main_t *dcm = &dhcp_client_main;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001047 u32 sw_if_index;
khemendra kumar34719e32017-12-08 18:06:52 +05301048 u8 *hostname = 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001049 u8 sw_if_index_set = 0;
Neale Ranns54c6dc42018-01-17 10:29:10 -08001050 u8 set_broadcast_flag = 1;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001051 int is_add = 1;
1052 dhcp_client_add_del_args_t _a, *a = &_a;
1053 int rv;
1054
khemendra kumar34719e32017-12-08 18:06:52 +05301055 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001056 {
khemendra kumar34719e32017-12-08 18:06:52 +05301057 if (unformat (input, "intfc %U",
1058 unformat_vnet_sw_interface, dcm->vnet_main, &sw_if_index))
1059 sw_if_index_set = 1;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001060 else if (unformat (input, "hostname %v", &hostname))
khemendra kumar34719e32017-12-08 18:06:52 +05301061 ;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001062 else if (unformat (input, "del"))
khemendra kumar34719e32017-12-08 18:06:52 +05301063 is_add = 0;
Neale Ranns54c6dc42018-01-17 10:29:10 -08001064 else if (unformat (input, "broadcast", &set_broadcast_flag))
1065 is_add = 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001066 else
khemendra kumar34719e32017-12-08 18:06:52 +05301067 break;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001068 }
1069
1070 if (sw_if_index_set == 0)
1071 return clib_error_return (0, "interface not specified");
1072
1073 memset (a, 0, sizeof (*a));
1074 a->is_add = is_add;
1075 a->sw_if_index = sw_if_index;
1076 a->hostname = hostname;
1077 a->client_identifier = format (0, "vpe 1.0%c", 0);
Neale Ranns54c6dc42018-01-17 10:29:10 -08001078 a->set_broadcast_flag = set_broadcast_flag;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001079
khemendra kumar34719e32017-12-08 18:06:52 +05301080 /*
Ed Warnickecb9cada2015-12-08 15:45:58 -07001081 * Option 55 request list. These data precisely match
1082 * the Ubuntu dhcp client. YMMV.
1083 */
1084
1085 /* Subnet Mask */
1086 vec_add1 (a->option_55_data, 1);
1087 /* Broadcast address */
1088 vec_add1 (a->option_55_data, 28);
1089 /* time offset */
1090 vec_add1 (a->option_55_data, 2);
1091 /* Router */
1092 vec_add1 (a->option_55_data, 3);
1093 /* Domain Name */
1094 vec_add1 (a->option_55_data, 15);
1095 /* DNS */
1096 vec_add1 (a->option_55_data, 6);
1097 /* Domain search */
1098 vec_add1 (a->option_55_data, 119);
1099 /* Host name */
1100 vec_add1 (a->option_55_data, 12);
1101 /* NetBIOS name server */
1102 vec_add1 (a->option_55_data, 44);
1103 /* NetBIOS Scope */
1104 vec_add1 (a->option_55_data, 47);
1105 /* MTU */
1106 vec_add1 (a->option_55_data, 26);
1107 /* Classless static route */
1108 vec_add1 (a->option_55_data, 121);
1109 /* NTP servers */
1110 vec_add1 (a->option_55_data, 42);
1111
1112 rv = dhcp_client_add_del (a);
1113
1114 switch (rv)
1115 {
1116 case 0:
1117 break;
1118
1119 case VNET_API_ERROR_INVALID_VALUE:
1120
1121 vec_free (a->hostname);
1122 vec_free (a->client_identifier);
1123 vec_free (a->option_55_data);
1124 if (is_add)
khemendra kumar34719e32017-12-08 18:06:52 +05301125 return clib_error_return (0, "dhcp client already enabled on %U",
1126 format_vnet_sw_if_index_name,
1127 dcm->vnet_main, sw_if_index);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001128 else
khemendra kumar34719e32017-12-08 18:06:52 +05301129 return clib_error_return (0, "dhcp client not enabled on %U",
1130 format_vnet_sw_if_index_name,
1131 dcm->vnet_main, sw_if_index);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001132 break;
1133
1134 default:
1135 vlib_cli_output (vm, "dhcp_client_add_del returned %d", rv);
1136 }
1137
1138 return 0;
1139}
1140
khemendra kumar34719e32017-12-08 18:06:52 +05301141/* *INDENT-OFF* */
Ed Warnickecb9cada2015-12-08 15:45:58 -07001142VLIB_CLI_COMMAND (dhcp_client_set_command, static) = {
1143 .path = "set dhcp client",
1144 .short_help = "set dhcp client [del] intfc <interface> [hostname <name>]",
1145 .function = dhcp_client_set_command_fn,
1146};
khemendra kumar34719e32017-12-08 18:06:52 +05301147/* *INDENT-ON* */
Ed Warnickecb9cada2015-12-08 15:45:58 -07001148
1149static clib_error_t *
1150dhcp_client_init (vlib_main_t * vm)
1151{
khemendra kumar34719e32017-12-08 18:06:52 +05301152 dhcp_client_main_t *dcm = &dhcp_client_main;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001153
1154 dcm->vlib_main = vm;
khemendra kumar34719e32017-12-08 18:06:52 +05301155 dcm->vnet_main = vnet_get_main ();
Ed Warnickecb9cada2015-12-08 15:45:58 -07001156 dcm->seed = 0xdeaddabe;
1157 return 0;
1158}
1159
1160VLIB_INIT_FUNCTION (dhcp_client_init);
khemendra kumar34719e32017-12-08 18:06:52 +05301161
1162/*
1163 * fd.io coding-style-patch-verification: ON
1164 *
1165 * Local Variables:
1166 * eval: (c-set-style "gnu")
1167 * End:
1168 */