Print this page
7388 Support DHCP Client FQDN. Allow IAID/DUID for all v4.
| Split |
Close |
| Expand all |
| Collapse all |
--- old/usr/src/cmd/cmd-inet/sbin/dhcpagent/renew.c
+++ new/usr/src/cmd/cmd-inet/sbin/dhcpagent/renew.c
1 1 /*
2 2 * CDDL HEADER START
3 3 *
4 4 * The contents of this file are subject to the terms of the
5 5 * Common Development and Distribution License (the "License").
6 6 * You may not use this file except in compliance with the License.
7 7 *
8 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 9 * or http://www.opensolaris.org/os/licensing.
10 10 * See the License for the specific language governing permissions
11 11 * and limitations under the License.
12 12 *
|
↓ open down ↓ |
12 lines elided |
↑ open up ↑ |
13 13 * When distributing Covered Code, include this CDDL HEADER in each
14 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 15 * If applicable, add the following below this CDDL HEADER, with the
16 16 * fields enclosed by brackets "[]" replaced with your own identifying
17 17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 18 *
19 19 * CDDL HEADER END
20 20 */
21 21 /*
22 22 * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
23 + * Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
23 24 */
24 25
25 26 #include <sys/types.h>
26 27 #include <time.h>
27 28 #include <netinet/in.h>
28 29 #include <netinet/dhcp.h>
29 30 #include <netinet/udp.h>
30 31 #include <netinet/ip_var.h>
31 32 #include <netinet/udp_var.h>
32 33 #include <libinetutil.h>
33 34 #include <dhcpmsg.h>
34 35 #include <dhcp_hostconf.h>
35 36 #include <string.h>
36 37
37 38 #include "packet.h"
38 39 #include "agent.h"
39 40 #include "script_handler.h"
40 41 #include "interface.h"
41 42 #include "states.h"
42 43 #include "util.h"
43 44
44 45 /*
45 46 * Number of seconds to wait for a retry if the user is interacting with the
46 47 * daemon.
47 48 */
48 49 #define RETRY_DELAY 10
49 50
50 51 /*
51 52 * If the renew timer fires within this number of seconds of the rebind timer,
52 53 * then skip renew. This prevents us from sending back-to-back renew and
53 54 * rebind messages -- a pointless activity.
54 55 */
55 56 #define TOO_CLOSE 2
56 57
57 58 static boolean_t stop_extending(dhcp_smach_t *, unsigned int);
58 59
59 60 /*
60 61 * dhcp_renew(): attempts to renew a DHCP lease on expiration of the T1 timer.
61 62 *
62 63 * input: iu_tq_t *: unused
63 64 * void *: the lease to renew (dhcp_lease_t)
64 65 * output: void
65 66 *
66 67 * notes: The primary expense involved with DHCP (like most UDP protocols) is
67 68 * with the generation and handling of packets, not the contents of
68 69 * those packets. Thus, we try to reduce the number of packets that
69 70 * are sent. It would be nice to just renew all leases here (each one
70 71 * added has trivial added overhead), but the DHCPv6 RFC doesn't
71 72 * explicitly allow that behavior. Rather than having that argument,
72 73 * we settle for ones that are close in expiry to the one that fired.
73 74 * For v4, we repeatedly reschedule the T1 timer to do the
74 75 * retransmissions. For v6, we rely on the common timer computation
75 76 * in packet.c.
76 77 */
77 78
78 79 /* ARGSUSED */
79 80 void
80 81 dhcp_renew(iu_tq_t *tqp, void *arg)
81 82 {
82 83 dhcp_lease_t *dlp = arg;
83 84 dhcp_smach_t *dsmp = dlp->dl_smach;
84 85 uint32_t t2;
85 86
86 87 dhcpmsg(MSG_VERBOSE, "dhcp_renew: T1 timer expired on %s",
87 88 dsmp->dsm_name);
88 89
89 90 dlp->dl_t1.dt_id = -1;
90 91
91 92 if (dsmp->dsm_state == RENEWING || dsmp->dsm_state == REBINDING) {
92 93 dhcpmsg(MSG_DEBUG, "dhcp_renew: already renewing");
93 94 release_lease(dlp);
94 95 return;
95 96 }
96 97
97 98 /*
98 99 * Sanity check: don't send packets if we're past T2, or if we're
99 100 * extremely close.
100 101 */
101 102
102 103 t2 = dsmp->dsm_curstart_monosec + dlp->dl_t2.dt_start;
103 104 if (monosec() + TOO_CLOSE >= t2) {
104 105 dhcpmsg(MSG_DEBUG, "dhcp_renew: %spast T2 on %s",
105 106 monosec() > t2 ? "" : "almost ", dsmp->dsm_name);
106 107 release_lease(dlp);
107 108 return;
108 109 }
109 110
110 111 /*
111 112 * If there isn't an async event pending, or if we can cancel the one
112 113 * that's there, then try to renew by sending an extension request. If
113 114 * that fails, we'll try again when the next timer fires.
114 115 */
115 116 if (!async_cancel(dsmp) || !async_start(dsmp, DHCP_EXTEND, B_FALSE) ||
116 117 !dhcp_extending(dsmp)) {
117 118 if (monosec() + RETRY_DELAY < t2) {
118 119 /*
119 120 * Try again in RETRY_DELAY seconds; user command
120 121 * should be gone.
121 122 */
122 123 init_timer(&dlp->dl_t1, RETRY_DELAY);
123 124 (void) set_smach_state(dsmp, BOUND);
124 125 if (!schedule_lease_timer(dlp, &dlp->dl_t1,
125 126 dhcp_renew)) {
126 127 dhcpmsg(MSG_INFO, "dhcp_renew: unable to "
127 128 "reschedule renewal around user command "
128 129 "on %s; will wait for rebind",
129 130 dsmp->dsm_name);
130 131 }
131 132 } else {
132 133 dhcpmsg(MSG_DEBUG, "dhcp_renew: user busy on %s; will "
133 134 "wait for rebind", dsmp->dsm_name);
134 135 }
135 136 }
136 137 release_lease(dlp);
137 138 }
138 139
139 140 /*
140 141 * dhcp_rebind(): attempts to renew a DHCP lease from the REBINDING state (T2
141 142 * timer expiry).
142 143 *
143 144 * input: iu_tq_t *: unused
144 145 * void *: the lease to renew
145 146 * output: void
146 147 * notes: For v4, we repeatedly reschedule the T2 timer to do the
147 148 * retransmissions. For v6, we rely on the common timer computation
148 149 * in packet.c.
149 150 */
150 151
151 152 /* ARGSUSED */
152 153 void
153 154 dhcp_rebind(iu_tq_t *tqp, void *arg)
154 155 {
155 156 dhcp_lease_t *dlp = arg;
156 157 dhcp_smach_t *dsmp = dlp->dl_smach;
157 158 int nlifs;
158 159 dhcp_lif_t *lif;
159 160 boolean_t some_valid;
160 161 uint32_t expiremax;
161 162 DHCPSTATE oldstate;
162 163
163 164 dhcpmsg(MSG_VERBOSE, "dhcp_rebind: T2 timer expired on %s",
164 165 dsmp->dsm_name);
165 166
166 167 dlp->dl_t2.dt_id = -1;
167 168
168 169 if ((oldstate = dsmp->dsm_state) == REBINDING) {
169 170 dhcpmsg(MSG_DEBUG, "dhcp_renew: already rebinding");
170 171 release_lease(dlp);
171 172 return;
172 173 }
173 174
174 175 /*
175 176 * Sanity check: don't send packets if we've already expired on all of
176 177 * the addresses. We compute the maximum expiration time here, because
177 178 * it won't matter for v4 (there's only one lease) and for v6 we need
178 179 * to know when the last lease ages away.
179 180 */
180 181
181 182 some_valid = B_FALSE;
182 183 expiremax = monosec();
183 184 lif = dlp->dl_lifs;
184 185 for (nlifs = dlp->dl_nlifs; nlifs > 0; nlifs--, lif = lif->lif_next) {
185 186 uint32_t expire;
186 187
187 188 expire = dsmp->dsm_curstart_monosec + lif->lif_expire.dt_start;
188 189 if (expire > expiremax) {
189 190 expiremax = expire;
190 191 some_valid = B_TRUE;
191 192 }
192 193 }
193 194 if (!some_valid) {
194 195 dhcpmsg(MSG_DEBUG, "dhcp_rebind: all leases expired on %s",
195 196 dsmp->dsm_name);
196 197 release_lease(dlp);
197 198 return;
198 199 }
199 200
200 201 /*
201 202 * This is our first venture into the REBINDING state, so reset the
202 203 * server address. We know the renew timer has already been cancelled
203 204 * (or we wouldn't be here).
204 205 */
205 206 if (dsmp->dsm_isv6) {
206 207 dsmp->dsm_server = ipv6_all_dhcp_relay_and_servers;
207 208 } else {
208 209 IN6_IPADDR_TO_V4MAPPED(htonl(INADDR_BROADCAST),
209 210 &dsmp->dsm_server);
210 211 }
211 212
212 213 /* {Bound,Renew}->rebind transitions cannot fail */
213 214 (void) set_smach_state(dsmp, REBINDING);
214 215
215 216 /*
216 217 * If there isn't an async event pending, or if we can cancel the one
217 218 * that's there, then try to rebind by sending an extension request.
218 219 * If that fails, we'll clean up when the lease expires.
219 220 */
220 221 if (!async_cancel(dsmp) || !async_start(dsmp, DHCP_EXTEND, B_FALSE) ||
221 222 !dhcp_extending(dsmp)) {
222 223 if (monosec() + RETRY_DELAY < expiremax) {
223 224 /*
224 225 * Try again in RETRY_DELAY seconds; user command
225 226 * should be gone.
226 227 */
227 228 init_timer(&dlp->dl_t2, RETRY_DELAY);
228 229 (void) set_smach_state(dsmp, oldstate);
229 230 if (!schedule_lease_timer(dlp, &dlp->dl_t2,
230 231 dhcp_rebind)) {
231 232 dhcpmsg(MSG_INFO, "dhcp_rebind: unable to "
232 233 "reschedule rebind around user command on "
233 234 "%s; lease may expire", dsmp->dsm_name);
234 235 }
235 236 } else {
236 237 dhcpmsg(MSG_WARNING, "dhcp_rebind: user busy on %s; "
237 238 "will expire", dsmp->dsm_name);
238 239 }
239 240 }
240 241 release_lease(dlp);
241 242 }
242 243
243 244 /*
244 245 * dhcp_finish_expire(): finish expiration of a lease after the user script
245 246 * runs. If this is the last lease, then restart DHCP.
246 247 * The caller has a reference to the LIF, which will be
247 248 * dropped.
248 249 *
249 250 * input: dhcp_smach_t *: the state machine to be restarted
250 251 * void *: logical interface that has expired
251 252 * output: int: always 1
252 253 */
253 254
254 255 static int
255 256 dhcp_finish_expire(dhcp_smach_t *dsmp, void *arg)
256 257 {
257 258 dhcp_lif_t *lif = arg;
258 259 dhcp_lease_t *dlp;
259 260
260 261 dhcpmsg(MSG_DEBUG, "lease expired on %s; removing", lif->lif_name);
261 262
262 263 dlp = lif->lif_lease;
263 264 unplumb_lif(lif);
264 265 if (dlp->dl_nlifs == 0)
265 266 remove_lease(dlp);
266 267 release_lif(lif);
267 268
268 269 /* If some valid leases remain, then drive on */
269 270 if (dsmp->dsm_leases != NULL) {
270 271 dhcpmsg(MSG_DEBUG,
271 272 "dhcp_finish_expire: some leases remain on %s",
272 273 dsmp->dsm_name);
273 274 return (1);
274 275 }
275 276
276 277 (void) remove_hostconf(dsmp->dsm_name, dsmp->dsm_isv6);
277 278
278 279 dhcpmsg(MSG_INFO, "last lease expired on %s -- restarting DHCP",
279 280 dsmp->dsm_name);
280 281
281 282 /*
282 283 * in the case where the lease is less than DHCP_REBIND_MIN
283 284 * seconds, we will never enter dhcp_renew() and thus the packet
284 285 * counters will not be reset. in that case, reset them here.
285 286 */
286 287
287 288 if (dsmp->dsm_state == BOUND) {
288 289 dsmp->dsm_bad_offers = 0;
289 290 dsmp->dsm_sent = 0;
290 291 dsmp->dsm_received = 0;
291 292 }
292 293
293 294 deprecate_leases(dsmp);
294 295
295 296 /* reset_smach() in dhcp_selecting() will clean up any leftover state */
296 297 dhcp_selecting(dsmp);
297 298
298 299 return (1);
299 300 }
300 301
301 302 /*
302 303 * dhcp_deprecate(): deprecates an address on a given logical interface when
303 304 * the preferred lifetime expires.
304 305 *
305 306 * input: iu_tq_t *: unused
306 307 * void *: the logical interface whose lease is expiring
307 308 * output: void
308 309 */
309 310
310 311 /* ARGSUSED */
311 312 void
312 313 dhcp_deprecate(iu_tq_t *tqp, void *arg)
313 314 {
314 315 dhcp_lif_t *lif = arg;
315 316
316 317 set_lif_deprecated(lif);
317 318 release_lif(lif);
318 319 }
319 320
320 321 /*
321 322 * dhcp_expire(): expires a lease on a given logical interface and, if there
322 323 * are no more leases, restarts DHCP.
323 324 *
324 325 * input: iu_tq_t *: unused
325 326 * void *: the logical interface whose lease has expired
326 327 * output: void
327 328 */
328 329
329 330 /* ARGSUSED */
330 331 void
331 332 dhcp_expire(iu_tq_t *tqp, void *arg)
332 333 {
333 334 dhcp_lif_t *lif = arg;
334 335 dhcp_smach_t *dsmp;
335 336 const char *event;
336 337
337 338 dhcpmsg(MSG_VERBOSE, "dhcp_expire: lease timer expired on %s",
338 339 lif->lif_name);
339 340
340 341 lif->lif_expire.dt_id = -1;
341 342 if (lif->lif_lease == NULL) {
342 343 release_lif(lif);
343 344 return;
344 345 }
345 346
346 347 set_lif_deprecated(lif);
347 348
348 349 dsmp = lif->lif_lease->dl_smach;
349 350
350 351 if (!async_cancel(dsmp)) {
351 352
352 353 dhcpmsg(MSG_WARNING,
353 354 "dhcp_expire: cannot cancel current asynchronous command "
354 355 "on %s", dsmp->dsm_name);
355 356
356 357 /*
357 358 * Try to schedule ourselves for callback. We're really
358 359 * situation-critical here; there's not much hope for us if
359 360 * this fails.
360 361 */
361 362 init_timer(&lif->lif_expire, DHCP_EXPIRE_WAIT);
362 363 if (schedule_lif_timer(lif, &lif->lif_expire, dhcp_expire))
363 364 return;
364 365
365 366 dhcpmsg(MSG_CRIT, "dhcp_expire: cannot reschedule dhcp_expire "
366 367 "to get called back, proceeding...");
367 368 }
368 369
369 370 if (!async_start(dsmp, DHCP_START, B_FALSE))
370 371 dhcpmsg(MSG_WARNING, "dhcp_expire: cannot start asynchronous "
371 372 "transaction on %s, continuing...", dsmp->dsm_name);
372 373
373 374 /*
374 375 * Determine if this state machine has any non-expired LIFs left in it.
375 376 * If it doesn't, then this is an "expire" event. Otherwise, if some
376 377 * valid leases remain, it's a "loss" event. The SOMEEXP case can
377 378 * occur only with DHCPv6.
378 379 */
379 380 if (expired_lif_state(dsmp) == DHCP_EXP_SOMEEXP)
380 381 event = EVENT_LOSS6;
381 382 else if (dsmp->dsm_isv6)
382 383 event = EVENT_EXPIRE6;
383 384 else
384 385 event = EVENT_EXPIRE;
385 386
386 387 /*
387 388 * just march on if this fails; at worst someone will be able
388 389 * to async_start() while we're actually busy with our own
389 390 * asynchronous transaction. better than not having a lease.
390 391 */
391 392
392 393 (void) script_start(dsmp, event, dhcp_finish_expire, lif, NULL);
393 394 }
394 395
395 396 /*
396 397 * dhcp_extending(): sends a REQUEST (IPv4 DHCP) or Rebind/Renew (DHCPv6) to
397 398 * extend a lease on a given state machine
398 399 *
399 400 * input: dhcp_smach_t *: the state machine to send the message from
400 401 * output: boolean_t: B_TRUE if the extension request was sent
401 402 */
402 403
403 404 boolean_t
404 405 dhcp_extending(dhcp_smach_t *dsmp)
405 406 {
406 407 dhcp_pkt_t *dpkt;
407 408
408 409 stop_pkt_retransmission(dsmp);
409 410
410 411 /*
411 412 * We change state here because this function is also called when
412 413 * adopting a lease and on demand by the user.
413 414 */
414 415 if (dsmp->dsm_state == BOUND) {
415 416 dsmp->dsm_neg_hrtime = gethrtime();
416 417 dsmp->dsm_bad_offers = 0;
417 418 dsmp->dsm_sent = 0;
418 419 dsmp->dsm_received = 0;
419 420 /* Bound->renew can't fail */
420 421 (void) set_smach_state(dsmp, RENEWING);
421 422 }
422 423
423 424 dhcpmsg(MSG_DEBUG, "dhcp_extending: sending request on %s",
424 425 dsmp->dsm_name);
425 426
426 427 if (dsmp->dsm_isv6) {
427 428 dhcp_lease_t *dlp;
428 429 dhcp_lif_t *lif;
429 430 uint_t nlifs;
430 431 uint_t irt, mrt;
431 432
432 433 /*
433 434 * Start constructing the Renew/Rebind message. Only Renew has
434 435 * a server ID, as we still think our server might be
435 436 * reachable.
436 437 */
437 438 if (dsmp->dsm_state == RENEWING) {
438 439 dpkt = init_pkt(dsmp, DHCPV6_MSG_RENEW);
439 440 (void) add_pkt_opt(dpkt, DHCPV6_OPT_SERVERID,
440 441 dsmp->dsm_serverid, dsmp->dsm_serveridlen);
441 442 irt = DHCPV6_REN_TIMEOUT;
442 443 mrt = DHCPV6_REN_MAX_RT;
443 444 } else {
444 445 dpkt = init_pkt(dsmp, DHCPV6_MSG_REBIND);
445 446 irt = DHCPV6_REB_TIMEOUT;
446 447 mrt = DHCPV6_REB_MAX_RT;
447 448 }
448 449
449 450 /*
450 451 * Loop over the leases, and add an IA_NA for each and an
451 452 * IAADDR for each address.
452 453 */
453 454 for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next) {
454 455 lif = dlp->dl_lifs;
|
↓ open down ↓ |
422 lines elided |
↑ open up ↑ |
455 456 for (nlifs = dlp->dl_nlifs; nlifs > 0;
456 457 nlifs--, lif = lif->lif_next) {
457 458 (void) add_pkt_lif(dpkt, lif,
458 459 DHCPV6_STAT_SUCCESS, NULL);
459 460 }
460 461 }
461 462
462 463 /* Add required Option Request option */
463 464 (void) add_pkt_prl(dpkt, dsmp);
464 465
466 + /* Add FQDN if configured */
467 + (void) dhcp_add_fqdn_opt(dpkt, dsmp);
468 +
465 469 return (send_pkt_v6(dsmp, dpkt, dsmp->dsm_server,
466 470 stop_extending, irt, mrt));
467 471 } else {
468 472 dhcp_lif_t *lif = dsmp->dsm_lif;
469 473 ipaddr_t server;
470 474
471 475 /* assemble the DHCPREQUEST message. */
472 476 dpkt = init_pkt(dsmp, REQUEST);
473 477 dpkt->pkt->ciaddr.s_addr = lif->lif_addr;
474 478
475 479 /*
476 480 * The max dhcp message size option is set to the interface
477 481 * max, minus the size of the udp and ip headers.
478 482 */
479 483 (void) add_pkt_opt16(dpkt, CD_MAX_DHCP_SIZE,
480 484 htons(lif->lif_max - sizeof (struct udpiphdr)));
481 485 (void) add_pkt_opt32(dpkt, CD_LEASE_TIME, htonl(DHCP_PERM));
482 486
|
↓ open down ↓ |
8 lines elided |
↑ open up ↑ |
483 487 if (class_id_len != 0) {
484 488 (void) add_pkt_opt(dpkt, CD_CLASS_ID, class_id,
485 489 class_id_len);
486 490 }
487 491 (void) add_pkt_prl(dpkt, dsmp);
488 492 /*
489 493 * dsm_reqhost was set for this state machine in
490 494 * dhcp_selecting() if the REQUEST_HOSTNAME option was set and
491 495 * a host name was found.
492 496 */
493 - if (dsmp->dsm_reqhost != NULL) {
497 + if (dhcp_add_fqdn_opt(dpkt, dsmp) != 0 &&
498 + dsmp->dsm_reqhost != NULL) {
494 499 (void) add_pkt_opt(dpkt, CD_HOSTNAME, dsmp->dsm_reqhost,
495 500 strlen(dsmp->dsm_reqhost));
496 501 }
497 502 (void) add_pkt_opt(dpkt, CD_END, NULL, 0);
498 503
499 504 IN6_V4MAPPED_TO_IPADDR(&dsmp->dsm_server, server);
500 505 return (send_pkt(dsmp, dpkt, server, stop_extending));
501 506 }
502 507 }
503 508
504 509 /*
505 510 * stop_extending(): decides when to stop retransmitting v4 REQUEST or v6
506 511 * Renew/Rebind messages. If we're renewing, then stop if
507 512 * T2 is soon approaching.
508 513 *
509 514 * input: dhcp_smach_t *: the state machine REQUESTs are being sent from
510 515 * unsigned int: the number of REQUESTs sent so far
511 516 * output: boolean_t: B_TRUE if retransmissions should stop
512 517 */
513 518
514 519 /* ARGSUSED */
515 520 static boolean_t
516 521 stop_extending(dhcp_smach_t *dsmp, unsigned int n_requests)
517 522 {
518 523 dhcp_lease_t *dlp;
519 524
520 525 /*
521 526 * If we're renewing and rebind time is soon approaching, then don't
522 527 * schedule
523 528 */
524 529 if (dsmp->dsm_state == RENEWING) {
525 530 monosec_t t2;
526 531
527 532 t2 = 0;
528 533 for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next) {
529 534 if (dlp->dl_t2.dt_start > t2)
530 535 t2 = dlp->dl_t2.dt_start;
531 536 }
532 537 t2 += dsmp->dsm_curstart_monosec;
533 538 if (monosec() + TOO_CLOSE >= t2) {
534 539 dhcpmsg(MSG_DEBUG, "stop_extending: %spast T2 on %s",
535 540 monosec() > t2 ? "" : "almost ", dsmp->dsm_name);
536 541 return (B_TRUE);
537 542 }
538 543 }
539 544
540 545 /*
541 546 * Note that returning B_TRUE cancels both this transmission and the
542 547 * one that would occur at dsm_send_timeout, and that for v4 we cut the
543 548 * time in half for each retransmission. Thus we check here against
544 549 * half of the minimum.
545 550 */
546 551 if (!dsmp->dsm_isv6 &&
547 552 dsmp->dsm_send_timeout < DHCP_REBIND_MIN * MILLISEC / 2) {
548 553 dhcpmsg(MSG_DEBUG, "stop_extending: next retry would be in "
549 554 "%d.%03d; stopping", dsmp->dsm_send_timeout / MILLISEC,
550 555 dsmp->dsm_send_timeout % MILLISEC);
551 556 return (B_TRUE);
552 557 }
553 558
554 559 /* Otherwise, w stop only when the next timer (rebind, expire) fires */
555 560 return (B_FALSE);
556 561 }
|
↓ open down ↓ |
53 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX