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/states.c
+++ new/usr/src/cmd/cmd-inet/sbin/dhcpagent/states.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 * This module contains core functions for managing DHCP state machine
25 26 * instances.
26 27 */
27 28
28 29 #include <assert.h>
29 30 #include <stdlib.h>
30 31 #include <search.h>
31 32 #include <string.h>
32 33 #include <ctype.h>
33 34 #include <sys/types.h>
34 35 #include <sys/socket.h>
35 36 #include <netinet/in.h>
36 37 #include <netinet/arp.h>
37 38 #include <arpa/inet.h>
38 39 #include <dhcpmsg.h>
39 40 #include <dhcpagent_util.h>
40 41 #include <dhcp_stable.h>
41 42 #include <dhcp_inittab.h>
42 43
43 44 #include "agent.h"
44 45 #include "states.h"
45 46 #include "interface.h"
46 47 #include "defaults.h"
47 48 #include "script_handler.h"
48 49
49 50 static uint_t global_smach_count;
50 51
51 52 static uchar_t *global_duid;
52 53 static size_t global_duidlen;
53 54
54 55 /*
55 56 * iaid_retry(): attempt to write LIF IAID again
56 57 *
57 58 * input: iu_tq_t *: ignored
58 59 * void *: pointer to LIF
59 60 * output: none
60 61 */
61 62
62 63 /* ARGSUSED */
63 64 static void
64 65 iaid_retry(iu_tq_t *tqp, void *arg)
65 66 {
66 67 dhcp_lif_t *lif = arg;
67 68
68 69 if (write_stable_iaid(lif->lif_name, lif->lif_iaid) == -1) {
69 70 if (errno != EROFS) {
70 71 dhcpmsg(MSG_ERR,
71 72 "iaid_retry: unable to write out IAID for %s",
72 73 lif->lif_name);
73 74 release_lif(lif);
74 75 } else {
75 76 lif->lif_iaid_id = iu_schedule_timer(tq, 60,
76 77 iaid_retry, lif);
77 78 }
78 79 } else {
79 80 release_lif(lif);
80 81 }
81 82 }
82 83
83 84 /*
84 85 * parse_param_list(): parse a parameter list.
85 86 *
86 87 * input: const char *: parameter list string with comma-separated entries
87 88 * uint_t *: return parameter; number of entries decoded
88 89 * const char *: name of parameter list for logging purposes
89 90 * dhcp_smach_t *: smach pointer for logging
90 91 * output: uint16_t *: allocated array of parameters, or NULL if none.
91 92 */
92 93
93 94 static uint16_t *
94 95 parse_param_list(const char *param_list, uint_t *param_cnt,
95 96 const char *param_name, dhcp_smach_t *dsmp)
96 97 {
97 98 int i, maxparam;
98 99 char tsym[DSYM_MAX_SYM_LEN + 1];
99 100 uint16_t *params;
100 101 const char *cp;
101 102 dhcp_symbol_t *entry;
102 103
103 104 *param_cnt = 0;
104 105
105 106 if (param_list == NULL)
106 107 return (NULL);
107 108
108 109 for (maxparam = 1, i = 0; param_list[i] != '\0'; i++) {
109 110 if (param_list[i] == ',')
110 111 maxparam++;
111 112 }
112 113
113 114 params = malloc(maxparam * sizeof (*params));
114 115 if (params == NULL) {
115 116 dhcpmsg(MSG_WARNING,
116 117 "cannot allocate parameter %s list for %s (continuing)",
117 118 param_name, dsmp->dsm_name);
118 119 return (NULL);
119 120 }
120 121
121 122 for (i = 0; i < maxparam; ) {
122 123
123 124 if (isspace(*param_list))
124 125 param_list++;
125 126
126 127 /* extract the next element on the list */
127 128 cp = strchr(param_list, ',');
128 129 if (cp == NULL || cp - param_list >= sizeof (tsym))
129 130 (void) strlcpy(tsym, param_list, sizeof (tsym));
130 131 else
131 132 (void) strlcpy(tsym, param_list, cp - param_list + 1);
132 133
133 134 /* LINTED -- do nothing with blanks on purpose */
134 135 if (tsym[0] == '\0') {
135 136 ;
136 137 } else if (isalpha(tsym[0])) {
137 138 entry = inittab_getbyname(ITAB_CAT_SITE |
138 139 ITAB_CAT_STANDARD |
139 140 (dsmp->dsm_isv6 ? ITAB_CAT_V6 : 0),
140 141 ITAB_CONS_INFO, tsym);
141 142 if (entry == NULL) {
142 143 dhcpmsg(MSG_INFO, "ignored unknown %s list "
143 144 "entry '%s' for %s", param_name, tsym,
144 145 dsmp->dsm_name);
145 146 } else {
146 147 params[i++] = entry->ds_code;
147 148 free(entry);
148 149 }
149 150 } else {
150 151 params[i++] = strtoul(tsym, NULL, 0);
151 152 }
152 153 if (cp == NULL)
153 154 break;
154 155 param_list = cp + 1;
155 156 }
156 157
157 158 *param_cnt = i;
158 159 return (params);
159 160 }
160 161
161 162 /*
162 163 * insert_smach(): Create a state machine instance on a given logical
163 164 * interface. The state machine holds the caller's LIF
164 165 * reference on success, and frees it on failure.
165 166 *
166 167 * input: dhcp_lif_t *: logical interface name
167 168 * int *: set to DHCP_IPC_E_* if creation fails
168 169 * output: dhcp_smach_t *: state machine instance
169 170 */
170 171
171 172 dhcp_smach_t *
172 173 insert_smach(dhcp_lif_t *lif, int *error)
173 174 {
174 175 dhcp_smach_t *dsmp, *alt_primary;
175 176 boolean_t isv6;
176 177 const char *plist;
177 178
178 179 if ((dsmp = calloc(1, sizeof (*dsmp))) == NULL) {
179 180 dhcpmsg(MSG_ERR, "cannot allocate state machine entry for %s",
180 181 lif->lif_name);
181 182 remove_lif(lif);
182 183 release_lif(lif);
183 184 *error = DHCP_IPC_E_MEMORY;
184 185 return (NULL);
185 186 }
186 187 dsmp->dsm_name = lif->lif_name;
187 188 dsmp->dsm_lif = lif;
188 189 dsmp->dsm_hold_count = 1;
189 190 dsmp->dsm_state = INIT;
190 191 dsmp->dsm_dflags = DHCP_IF_REMOVED; /* until added to list */
191 192 isv6 = lif->lif_pif->pif_isv6;
192 193
193 194 /*
194 195 * Now that we have a controlling LIF, we need to assign an IAID to
195 196 * that LIF.
196 197 */
197 198 if (lif->lif_iaid == 0 &&
198 199 (lif->lif_iaid = read_stable_iaid(lif->lif_name)) == 0) {
199 200 static uint32_t iaidctr = 0x80000000u;
200 201
201 202 /*
202 203 * If this is a logical interface, then use an arbitrary seed
203 204 * value. Otherwise, use the ifIndex.
204 205 */
205 206 lif->lif_iaid = make_stable_iaid(lif->lif_name,
206 207 strchr(lif->lif_name, ':') != NULL ? iaidctr++ :
207 208 lif->lif_pif->pif_index);
208 209 dhcpmsg(MSG_INFO,
209 210 "insert_smach: manufactured IAID %u for v%d %s",
210 211 lif->lif_iaid, isv6 ? 6 : 4, lif->lif_name);
211 212 hold_lif(lif);
212 213 iaid_retry(NULL, lif);
213 214 }
214 215
215 216 if (isv6) {
216 217 dsmp->dsm_dflags |= DHCP_IF_V6;
217 218 dsmp->dsm_server = ipv6_all_dhcp_relay_and_servers;
218 219
219 220 /*
220 221 * With DHCPv6, we do all of our I/O using the common
221 222 * v6_sock_fd. There's no need for per-interface file
222 223 * descriptors because we have IPV6_PKTINFO.
223 224 */
224 225 } else {
225 226 IN6_IPADDR_TO_V4MAPPED(htonl(INADDR_BROADCAST),
226 227 &dsmp->dsm_server);
227 228
228 229 /*
229 230 * With IPv4 DHCP, we use a socket per lif.
230 231 */
231 232 if (!open_ip_lif(lif, INADDR_ANY, B_TRUE)) {
232 233 dhcpmsg(MSG_ERR, "unable to open socket for %s",
233 234 lif->lif_name);
234 235 /* This will also dispose of the LIF */
235 236 release_smach(dsmp);
236 237 *error = DHCP_IPC_E_SOCKET;
237 238 return (NULL);
238 239 }
239 240 }
240 241
241 242 script_init(dsmp);
242 243 ipc_action_init(&dsmp->dsm_ia);
243 244
244 245 dsmp->dsm_neg_hrtime = gethrtime();
245 246 dsmp->dsm_offer_timer = -1;
246 247 dsmp->dsm_start_timer = -1;
247 248 dsmp->dsm_retrans_timer = -1;
248 249
249 250 /*
250 251 * Initialize the parameter request and ignore lists, if any.
251 252 */
252 253 plist = df_get_string(dsmp->dsm_name, isv6, DF_PARAM_REQUEST_LIST);
253 254 dsmp->dsm_prl = parse_param_list(plist, &dsmp->dsm_prllen, "request",
254 255 dsmp);
255 256 plist = df_get_string(dsmp->dsm_name, isv6, DF_PARAM_IGNORE_LIST);
256 257 dsmp->dsm_pil = parse_param_list(plist, &dsmp->dsm_pillen, "ignore",
257 258 dsmp);
258 259
259 260 dsmp->dsm_offer_wait = df_get_int(dsmp->dsm_name, isv6,
260 261 DF_OFFER_WAIT);
261 262
262 263 /*
263 264 * If there is no primary of this type, and there is one of the other,
264 265 * then make this one primary if it's on the same named PIF.
265 266 */
266 267 if (primary_smach(isv6) == NULL &&
267 268 (alt_primary = primary_smach(!isv6)) != NULL) {
268 269 if (strcmp(lif->lif_pif->pif_name,
269 270 alt_primary->dsm_lif->lif_pif->pif_name) == 0) {
270 271 dhcpmsg(MSG_DEBUG,
271 272 "insert_smach: making %s primary for v%d",
272 273 dsmp->dsm_name, isv6 ? 6 : 4);
273 274 dsmp->dsm_dflags |= DHCP_IF_PRIMARY;
274 275 }
275 276 }
276 277
277 278 /*
278 279 * We now have at least one state machine running, so cancel any
279 280 * running inactivity timer.
280 281 */
281 282 if (inactivity_id != -1 &&
282 283 iu_cancel_timer(tq, inactivity_id, NULL) == 1)
283 284 inactivity_id = -1;
284 285
285 286 dsmp->dsm_dflags &= ~DHCP_IF_REMOVED;
286 287 insque(dsmp, &lif->lif_smachs);
287 288 global_smach_count++;
288 289 dhcpmsg(MSG_DEBUG2, "insert_smach: inserted %s", dsmp->dsm_name);
289 290
290 291 return (dsmp);
291 292 }
292 293
293 294 /*
294 295 * hold_smach(): acquires a hold on a state machine
295 296 *
296 297 * input: dhcp_smach_t *: the state machine to acquire a hold on
297 298 * output: void
298 299 */
299 300
300 301 void
301 302 hold_smach(dhcp_smach_t *dsmp)
302 303 {
303 304 dsmp->dsm_hold_count++;
304 305
305 306 dhcpmsg(MSG_DEBUG2, "hold_smach: hold count on %s: %d",
306 307 dsmp->dsm_name, dsmp->dsm_hold_count);
307 308 }
308 309
309 310 /*
310 311 * free_smach(): frees the memory occupied by a state machine
311 312 *
312 313 * input: dhcp_smach_t *: the DHCP state machine to free
313 314 * output: void
314 315 */
315 316
316 317 static void
317 318 free_smach(dhcp_smach_t *dsmp)
318 319 {
319 320 dhcpmsg(MSG_DEBUG, "free_smach: freeing state machine %s",
320 321 dsmp->dsm_name);
321 322
322 323 deprecate_leases(dsmp);
323 324 remove_lif(dsmp->dsm_lif);
324 325 release_lif(dsmp->dsm_lif);
|
↓ open down ↓ |
292 lines elided |
↑ open up ↑ |
325 326 free_pkt_list(&dsmp->dsm_recv_pkt_list);
326 327 if (dsmp->dsm_ack != dsmp->dsm_orig_ack)
327 328 free_pkt_entry(dsmp->dsm_orig_ack);
328 329 free_pkt_entry(dsmp->dsm_ack);
329 330 free(dsmp->dsm_send_pkt.pkt);
330 331 free(dsmp->dsm_cid);
331 332 free(dsmp->dsm_prl);
332 333 free(dsmp->dsm_pil);
333 334 free(dsmp->dsm_routers);
334 335 free(dsmp->dsm_reqhost);
336 + free(dsmp->dsm_msg_reqhost);
337 + free(dsmp->dsm_reqfqdn);
335 338 free(dsmp);
336 339
337 340 /* no big deal if this fails */
338 341 if (global_smach_count == 0 && inactivity_id == -1) {
339 342 inactivity_id = iu_schedule_timer(tq, DHCP_INACTIVITY_WAIT,
340 343 inactivity_shutdown, NULL);
341 344 }
342 345 }
343 346
344 347 /*
345 348 * release_smach(): releases a hold previously acquired on a state machine.
346 349 * If the hold count reaches 0, the state machine is freed.
347 350 *
348 351 * input: dhcp_smach_t *: the state machine entry to release the hold on
349 352 * output: void
350 353 */
351 354
352 355 void
353 356 release_smach(dhcp_smach_t *dsmp)
354 357 {
355 358 if (dsmp->dsm_hold_count == 0) {
356 359 dhcpmsg(MSG_CRIT, "release_smach: extraneous release");
357 360 return;
358 361 }
359 362
360 363 if (dsmp->dsm_hold_count == 1 &&
361 364 !(dsmp->dsm_dflags & DHCP_IF_REMOVED)) {
362 365 dhcpmsg(MSG_CRIT, "release_smach: missing removal");
363 366 return;
364 367 }
365 368
366 369 if (--dsmp->dsm_hold_count == 0) {
367 370 free_smach(dsmp);
368 371 } else {
369 372 dhcpmsg(MSG_DEBUG2, "release_smach: hold count on %s: %d",
370 373 dsmp->dsm_name, dsmp->dsm_hold_count);
371 374 }
372 375 }
373 376
374 377 /*
375 378 * next_smach(): state machine iterator function
376 379 *
377 380 * input: dhcp_smach_t *: current state machine (or NULL for list start)
378 381 * boolean_t: B_TRUE if DHCPv6, B_FALSE otherwise
379 382 * output: dhcp_smach_t *: next state machine in list
380 383 */
381 384
382 385 dhcp_smach_t *
383 386 next_smach(dhcp_smach_t *dsmp, boolean_t isv6)
384 387 {
385 388 dhcp_lif_t *lif;
386 389 dhcp_pif_t *pif;
387 390
388 391 if (dsmp != NULL) {
389 392 if (dsmp->dsm_next != NULL)
390 393 return (dsmp->dsm_next);
391 394
392 395 if ((lif = dsmp->dsm_lif) != NULL)
393 396 lif = lif->lif_next;
394 397 for (; lif != NULL; lif = lif->lif_next) {
395 398 if (lif->lif_smachs != NULL)
396 399 return (lif->lif_smachs);
397 400 }
398 401
399 402 if ((pif = dsmp->dsm_lif->lif_pif) != NULL)
400 403 pif = pif->pif_next;
401 404 } else {
402 405 pif = isv6 ? v6root : v4root;
403 406 }
404 407 for (; pif != NULL; pif = pif->pif_next) {
405 408 for (lif = pif->pif_lifs; lif != NULL; lif = lif->lif_next) {
406 409 if (lif->lif_smachs != NULL)
407 410 return (lif->lif_smachs);
408 411 }
409 412 }
410 413 return (NULL);
411 414 }
412 415
413 416 /*
414 417 * primary_smach(): loop through all state machines of the given type (v4 or
415 418 * v6) in the system, and locate the one that's primary.
416 419 *
417 420 * input: boolean_t: B_TRUE for IPv6
418 421 * output: dhcp_smach_t *: the primary state machine
419 422 */
420 423
421 424 dhcp_smach_t *
422 425 primary_smach(boolean_t isv6)
423 426 {
424 427 dhcp_smach_t *dsmp;
425 428
426 429 for (dsmp = next_smach(NULL, isv6); dsmp != NULL;
427 430 dsmp = next_smach(dsmp, isv6)) {
428 431 if (dsmp->dsm_dflags & DHCP_IF_PRIMARY)
429 432 break;
430 433 }
431 434 return (dsmp);
432 435 }
433 436
434 437 /*
435 438 * info_primary_smach(): loop through all state machines of the given type (v4
436 439 * or v6) in the system, and locate the one that should
437 440 * be considered "primary" for dhcpinfo.
438 441 *
439 442 * input: boolean_t: B_TRUE for IPv6
440 443 * output: dhcp_smach_t *: the dhcpinfo primary state machine
441 444 */
442 445
443 446 dhcp_smach_t *
444 447 info_primary_smach(boolean_t isv6)
445 448 {
446 449 dhcp_smach_t *bestdsm = NULL;
447 450 dhcp_smach_t *dsmp;
448 451
449 452 for (dsmp = next_smach(NULL, isv6); dsmp != NULL;
450 453 dsmp = next_smach(dsmp, isv6)) {
451 454 /*
452 455 * If there is a primary, then something previously went wrong
453 456 * with verification, because the caller uses primary_smach()
454 457 * before calling this routine. There's nothing else we can do
455 458 * but return failure, as the designated primary must be bad.
456 459 */
457 460 if (dsmp->dsm_dflags & DHCP_IF_PRIMARY)
458 461 return (NULL);
459 462
460 463 /* If we have no information, then we're not primary. */
461 464 if (dsmp->dsm_ack == NULL)
462 465 continue;
463 466
464 467 /*
465 468 * Among those interfaces that have DHCP information, the
466 469 * "primary" is the one that sorts lexically first.
467 470 */
468 471 if (bestdsm == NULL ||
469 472 strcmp(dsmp->dsm_name, bestdsm->dsm_name) < 0)
470 473 bestdsm = dsmp;
471 474 }
472 475 return (bestdsm);
473 476 }
474 477
475 478 /*
476 479 * make_primary(): designate a given state machine as being the primary
477 480 * instance on the primary interface. Note that the user often
478 481 * thinks in terms of a primary "interface" (rather than just
479 482 * an instance), so we go to lengths here to keep v4 and v6 in
480 483 * sync.
481 484 *
482 485 * input: dhcp_smach_t *: the primary state machine
483 486 * output: none
484 487 */
485 488
486 489 void
487 490 make_primary(dhcp_smach_t *dsmp)
488 491 {
489 492 dhcp_smach_t *old_primary, *alt_primary;
490 493 dhcp_pif_t *pif;
491 494
492 495 if ((old_primary = primary_smach(dsmp->dsm_isv6)) != NULL)
493 496 old_primary->dsm_dflags &= ~DHCP_IF_PRIMARY;
494 497 dsmp->dsm_dflags |= DHCP_IF_PRIMARY;
495 498
496 499 /*
497 500 * Find the primary for the other protocol.
498 501 */
499 502 alt_primary = primary_smach(!dsmp->dsm_isv6);
500 503
501 504 /*
502 505 * If it's on a different interface, then cancel that. If it's on the
503 506 * same interface, then we're done.
504 507 */
505 508 if (alt_primary != NULL) {
506 509 if (strcmp(alt_primary->dsm_lif->lif_pif->pif_name,
507 510 dsmp->dsm_lif->lif_pif->pif_name) == 0)
508 511 return;
509 512 alt_primary->dsm_dflags &= ~DHCP_IF_PRIMARY;
510 513 }
511 514
512 515 /*
513 516 * We need a new primary for the other protocol. If the PIF exists,
514 517 * there must be at least one state machine. Just choose the first for
515 518 * consistency with insert_smach().
516 519 */
517 520 if ((pif = lookup_pif_by_name(dsmp->dsm_lif->lif_pif->pif_name,
518 521 !dsmp->dsm_isv6)) != NULL) {
519 522 pif->pif_lifs->lif_smachs->dsm_dflags |= DHCP_IF_PRIMARY;
520 523 }
521 524 }
522 525
523 526 /*
524 527 * lookup_smach(): finds a state machine by name and type; used for dispatching
525 528 * user commands.
526 529 *
527 530 * input: const char *: the name of the state machine
528 531 * boolean_t: B_TRUE if DHCPv6, B_FALSE otherwise
529 532 * output: dhcp_smach_t *: the state machine found
530 533 */
531 534
532 535 dhcp_smach_t *
533 536 lookup_smach(const char *smname, boolean_t isv6)
534 537 {
535 538 dhcp_smach_t *dsmp;
536 539
537 540 for (dsmp = next_smach(NULL, isv6); dsmp != NULL;
538 541 dsmp = next_smach(dsmp, isv6)) {
539 542 if (strcmp(dsmp->dsm_name, smname) == 0)
540 543 break;
541 544 }
542 545 return (dsmp);
543 546 }
544 547
545 548 /*
546 549 * lookup_smach_by_uindex(): iterate through running state machines by
547 550 * truncated interface index.
548 551 *
549 552 * input: uint16_t: the interface index (truncated)
550 553 * dhcp_smach_t *: the previous state machine, or NULL for start
551 554 * boolean_t: B_TRUE for DHCPv6, B_FALSE for IPv4 DHCP
552 555 * output: dhcp_smach_t *: next state machine, or NULL at end of list
553 556 */
554 557
555 558 dhcp_smach_t *
556 559 lookup_smach_by_uindex(uint16_t ifindex, dhcp_smach_t *dsmp, boolean_t isv6)
557 560 {
558 561 dhcp_pif_t *pif;
559 562 dhcp_lif_t *lif;
560 563
561 564 /*
562 565 * If the user gives us a state machine, then check that the next one
563 566 * available is on the same physical interface. If so, then go ahead
564 567 * and return that.
565 568 */
566 569 if (dsmp != NULL) {
567 570 pif = dsmp->dsm_lif->lif_pif;
568 571 if ((dsmp = next_smach(dsmp, isv6)) == NULL)
569 572 return (NULL);
570 573 if (pif == dsmp->dsm_lif->lif_pif)
571 574 return (dsmp);
572 575 } else {
573 576 /* Otherwise, start at the beginning of the list */
574 577 pif = NULL;
575 578 }
576 579
577 580 /*
578 581 * Find the next physical interface with the same truncated interface
579 582 * index, and return the first state machine on that. If there are no
580 583 * more physical interfaces that match, then we're done.
581 584 */
582 585 do {
583 586 pif = lookup_pif_by_uindex(ifindex, pif, isv6);
584 587 if (pif == NULL)
585 588 return (NULL);
586 589 for (lif = pif->pif_lifs; lif != NULL; lif = lif->lif_next) {
587 590 if ((dsmp = lif->lif_smachs) != NULL)
588 591 break;
589 592 }
590 593 } while (dsmp == NULL);
591 594 return (dsmp);
592 595 }
593 596
594 597 /*
595 598 * lookup_smach_by_xid(): iterate through running state machines by transaction
596 599 * id. Transaction ID zero means "all state machines."
597 600 *
598 601 * input: uint32_t: the transaction id to look up
599 602 * dhcp_smach_t *: the previous state machine, or NULL for start
600 603 * boolean_t: B_TRUE if DHCPv6, B_FALSE otherwise
601 604 * output: dhcp_smach_t *: next state machine, or NULL at end of list
602 605 */
603 606
604 607 dhcp_smach_t *
605 608 lookup_smach_by_xid(uint32_t xid, dhcp_smach_t *dsmp, boolean_t isv6)
606 609 {
607 610 for (dsmp = next_smach(dsmp, isv6); dsmp != NULL;
608 611 dsmp = next_smach(dsmp, isv6)) {
609 612 if (xid == 0 ||
610 613 pkt_get_xid(dsmp->dsm_send_pkt.pkt, isv6) == xid)
611 614 break;
612 615 }
613 616
614 617 return (dsmp);
615 618 }
616 619
617 620 /*
618 621 * lookup_smach_by_event(): find a state machine busy with a particular event
619 622 * ID. This is used only for error handling.
620 623 *
621 624 * input: iu_event_id_t: the event id to look up
622 625 * output: dhcp_smach_t *: matching state machine, or NULL if none
623 626 */
624 627
625 628 dhcp_smach_t *
626 629 lookup_smach_by_event(iu_event_id_t eid)
627 630 {
628 631 dhcp_smach_t *dsmp;
629 632 boolean_t isv6 = B_FALSE;
630 633
631 634 for (;;) {
632 635 for (dsmp = next_smach(NULL, isv6); dsmp != NULL;
633 636 dsmp = next_smach(dsmp, isv6)) {
634 637 if ((dsmp->dsm_dflags & DHCP_IF_BUSY) &&
635 638 eid == dsmp->dsm_ia.ia_eid)
636 639 return (dsmp);
637 640 }
638 641 if (isv6)
639 642 break;
640 643 isv6 = B_TRUE;
641 644 }
642 645
643 646 return (dsmp);
644 647 }
645 648
646 649 /*
647 650 * cancel_offer_timer(): stop the offer polling timer on a given state machine
648 651 *
649 652 * input: dhcp_smach_t *: state machine on which to stop polling for offers
650 653 * output: none
651 654 */
652 655
653 656 void
654 657 cancel_offer_timer(dhcp_smach_t *dsmp)
655 658 {
656 659 int retval;
657 660
658 661 if (dsmp->dsm_offer_timer != -1) {
659 662 retval = iu_cancel_timer(tq, dsmp->dsm_offer_timer, NULL);
660 663 dsmp->dsm_offer_timer = -1;
661 664 if (retval == 1)
662 665 release_smach(dsmp);
663 666 }
664 667 }
665 668
666 669 /*
667 670 * cancel_smach_timers(): stop all of the timers related to a given state
668 671 * machine, including lease and LIF expiry.
669 672 *
670 673 * input: dhcp_smach_t *: state machine to cancel
671 674 * output: none
672 675 * note: this function assumes that the iu timer functions are synchronous
673 676 * and thus don't require any protection or ordering on cancellation.
674 677 */
675 678
676 679 void
677 680 cancel_smach_timers(dhcp_smach_t *dsmp)
678 681 {
679 682 dhcp_lease_t *dlp;
680 683 dhcp_lif_t *lif;
681 684 uint_t nlifs;
682 685
683 686 for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next) {
684 687 cancel_lease_timers(dlp);
685 688 lif = dlp->dl_lifs;
686 689 nlifs = dlp->dl_nlifs;
687 690 for (; nlifs > 0; nlifs--, lif = lif->lif_next)
688 691 cancel_lif_timers(lif);
689 692 }
690 693
691 694 cancel_offer_timer(dsmp);
692 695 stop_pkt_retransmission(dsmp);
693 696 if (dsmp->dsm_start_timer != -1) {
694 697 (void) iu_cancel_timer(tq, dsmp->dsm_start_timer, NULL);
695 698 dsmp->dsm_start_timer = -1;
696 699 release_smach(dsmp);
697 700 }
698 701 }
699 702
700 703 /*
701 704 * remove_smach(): removes a given state machine from the system. marks it
702 705 * for being freed (but may not actually free it).
703 706 *
704 707 * input: dhcp_smach_t *: the state machine to remove
705 708 * output: void
706 709 */
707 710
708 711 void
709 712 remove_smach(dhcp_smach_t *dsmp)
710 713 {
711 714 if (dsmp->dsm_dflags & DHCP_IF_REMOVED)
712 715 return;
713 716
714 717 dhcpmsg(MSG_DEBUG2, "remove_smach: removing %s", dsmp->dsm_name);
715 718 dsmp->dsm_dflags |= DHCP_IF_REMOVED;
716 719 remque(dsmp);
717 720 global_smach_count--;
718 721
719 722 /*
720 723 * if we have long term timers, cancel them so that state machine
721 724 * resources can be reclaimed in a reasonable amount of time.
722 725 */
723 726 cancel_smach_timers(dsmp);
724 727
725 728 /* Drop the hold that the LIF's state machine list had on us */
726 729 release_smach(dsmp);
727 730 }
728 731
729 732 /*
730 733 * finished_smach(): we're finished with a given state machine; remove it from
731 734 * the system and tell the user (who may have initiated the
732 735 * removal process). Note that we remove it from the system
733 736 * first to allow back-to-back drop and create invocations.
734 737 *
735 738 * input: dhcp_smach_t *: the state machine to remove
736 739 * int: error for IPC
737 740 * output: void
738 741 */
739 742
740 743 void
741 744 finished_smach(dhcp_smach_t *dsmp, int error)
742 745 {
743 746 hold_smach(dsmp);
744 747 remove_smach(dsmp);
745 748 if (dsmp->dsm_ia.ia_fd != -1)
746 749 ipc_action_finish(dsmp, error);
747 750 else
748 751 (void) async_cancel(dsmp);
749 752 release_smach(dsmp);
750 753 }
751 754
752 755 /*
753 756 * is_bound_state(): checks if a state indicates the client is bound
754 757 *
755 758 * input: DHCPSTATE: the state to check
756 759 * output: boolean_t: B_TRUE if the state is bound, B_FALSE if not
757 760 */
758 761
759 762 boolean_t
760 763 is_bound_state(DHCPSTATE state)
761 764 {
762 765 return (state == BOUND || state == REBINDING || state == INFORMATION ||
763 766 state == RELEASING || state == INFORM_SENT || state == RENEWING);
764 767 }
765 768
766 769 /*
767 770 * set_smach_state(): changes state and updates I/O
768 771 *
769 772 * input: dhcp_smach_t *: the state machine to change
770 773 * DHCPSTATE: the new state
771 774 * output: boolean_t: B_TRUE on success, B_FALSE on failure
772 775 */
773 776
774 777 boolean_t
775 778 set_smach_state(dhcp_smach_t *dsmp, DHCPSTATE state)
776 779 {
777 780 dhcp_lif_t *lif = dsmp->dsm_lif;
778 781
779 782 if (dsmp->dsm_state != state) {
780 783 dhcpmsg(MSG_DEBUG,
781 784 "set_smach_state: changing from %s to %s on %s",
782 785 dhcp_state_to_string(dsmp->dsm_state),
783 786 dhcp_state_to_string(state), dsmp->dsm_name);
784 787
785 788 /*
786 789 * For IPv4, when we're in a bound state our socket must be
787 790 * bound to our address. Otherwise, our socket must be bound
788 791 * to INADDR_ANY. For IPv6, no such change is necessary.
789 792 */
790 793 if (!dsmp->dsm_isv6) {
791 794 if (is_bound_state(dsmp->dsm_state)) {
792 795 if (!is_bound_state(state)) {
793 796 close_ip_lif(lif);
794 797 if (!open_ip_lif(lif, INADDR_ANY,
795 798 B_FALSE))
796 799 return (B_FALSE);
797 800 }
798 801 } else {
799 802 if (is_bound_state(state)) {
800 803 close_ip_lif(lif);
801 804 if (!open_ip_lif(lif,
802 805 ntohl(lif->lif_addr), B_FALSE))
803 806 return (B_FALSE);
804 807 }
805 808 }
806 809 }
807 810
808 811 dsmp->dsm_state = state;
809 812 }
810 813 return (B_TRUE);
811 814 }
812 815
813 816 /*
814 817 * duid_retry(): attempt to write DUID again
815 818 *
816 819 * input: iu_tq_t *: ignored
817 820 * void *: ignored
818 821 * output: none
819 822 */
820 823
821 824 /* ARGSUSED */
822 825 static void
823 826 duid_retry(iu_tq_t *tqp, void *arg)
824 827 {
825 828 if (write_stable_duid(global_duid, global_duidlen) == -1) {
826 829 if (errno != EROFS) {
827 830 dhcpmsg(MSG_ERR,
828 831 "duid_retry: unable to write out DUID");
829 832 } else {
830 833 (void) iu_schedule_timer(tq, 60, duid_retry, NULL);
831 834 }
832 835 }
833 836 }
834 837
835 838 /*
836 839 * get_smach_cid(): gets the client ID for a given state machine.
837 840 *
838 841 * input: dhcp_smach_t *: the state machine to set up
839 842 * output: int: DHCP_IPC_SUCCESS or one of DHCP_IPC_E_* on failure.
840 843 */
841 844
842 845 int
843 846 get_smach_cid(dhcp_smach_t *dsmp)
844 847 {
845 848 uchar_t *client_id;
846 849 uint_t client_id_len;
847 850 dhcp_lif_t *lif = dsmp->dsm_lif;
848 851 dhcp_pif_t *pif = lif->lif_pif;
849 852 const char *value;
850 853 size_t slen;
851 854
852 855 /*
853 856 * Look in defaults file for the client-id. If present, this takes
854 857 * precedence over all other forms of ID.
855 858 */
856 859
857 860 dhcpmsg(MSG_DEBUG, "get_smach_cid: getting default client-id "
858 861 "property on %s", dsmp->dsm_name);
859 862 value = df_get_string(dsmp->dsm_name, pif->pif_isv6, DF_CLIENT_ID);
860 863 if (value != NULL) {
861 864 /*
862 865 * The Client ID string can have one of three basic forms:
863 866 * <decimal>,<data...>
864 867 * 0x<hex...>
865 868 * <string...>
866 869 *
867 870 * The first form is an RFC 3315 DUID. This is legal for both
868 871 * IPv4 DHCP and DHCPv6. For IPv4, an RFC 4361 Client ID is
869 872 * constructed from this value.
870 873 *
871 874 * The second and third forms are legal for IPv4 only. This is
872 875 * a raw Client ID, in hex or ASCII string format.
873 876 */
874 877
875 878 if (isdigit(*value) &&
876 879 value[strspn(value, "0123456789")] == ',') {
877 880 char *cp;
878 881 ulong_t duidtype;
879 882 ulong_t subtype;
880 883
881 884 errno = 0;
882 885 duidtype = strtoul(value, &cp, 0);
883 886 if (value == cp || errno != 0 || *cp != ',' ||
884 887 duidtype > 65535) {
885 888 dhcpmsg(MSG_ERR, "get_smach_cid: cannot parse "
886 889 "DUID type in %s", value);
887 890 goto no_specified_id;
888 891 }
889 892 value = cp + 1;
890 893 switch (duidtype) {
891 894 case DHCPV6_DUID_LL:
892 895 case DHCPV6_DUID_LLT: {
893 896 int num;
894 897 char chr;
895 898
896 899 errno = 0;
897 900 subtype = strtoul(value, &cp, 0);
898 901 if (value == cp || errno != 0 || *cp != ',' ||
899 902 subtype > 65535) {
900 903 dhcpmsg(MSG_ERR, "get_smach_cid: "
901 904 "cannot parse MAC type in %s",
902 905 value);
903 906 goto no_specified_id;
904 907 }
905 908 value = cp + 1;
906 909 client_id_len = pif->pif_isv6 ? 1 : 5;
907 910 for (; *cp != '\0'; cp++) {
908 911 if (*cp == ':')
909 912 client_id_len++;
910 913 else if (!isxdigit(*cp))
911 914 break;
912 915 }
913 916 if (duidtype == DHCPV6_DUID_LL) {
914 917 duid_llt_t *dllt;
915 918 time_t now;
916 919
917 920 client_id_len += sizeof (*dllt);
918 921 dllt = malloc(client_id_len);
919 922 if (dllt == NULL)
920 923 goto alloc_failure;
921 924 dsmp->dsm_cid = (uchar_t *)dllt;
922 925 dllt->dllt_dutype = htons(duidtype);
923 926 dllt->dllt_hwtype = htons(subtype);
924 927 now = time(NULL) - DUID_TIME_BASE;
925 928 dllt->dllt_time = htonl(now);
926 929 cp = (char *)(dllt + 1);
927 930 } else {
928 931 duid_ll_t *dll;
929 932
930 933 client_id_len += sizeof (*dll);
931 934 dll = malloc(client_id_len);
932 935 if (dll == NULL)
933 936 goto alloc_failure;
934 937 dsmp->dsm_cid = (uchar_t *)dll;
935 938 dll->dll_dutype = htons(duidtype);
936 939 dll->dll_hwtype = htons(subtype);
937 940 cp = (char *)(dll + 1);
938 941 }
939 942 num = 0;
940 943 while ((chr = *value) != '\0') {
941 944 if (isdigit(chr)) {
942 945 num = (num << 4) + chr - '0';
943 946 } else if (isxdigit(chr)) {
944 947 num = (num << 4) + 10 + chr -
945 948 (isupper(chr) ? 'A' : 'a');
946 949 } else if (chr == ':') {
947 950 *cp++ = num;
948 951 num = 0;
949 952 } else {
950 953 break;
951 954 }
952 955 }
953 956 break;
954 957 }
955 958 case DHCPV6_DUID_EN: {
956 959 duid_en_t *den;
957 960
958 961 errno = 0;
959 962 subtype = strtoul(value, &cp, 0);
960 963 if (value == cp || errno != 0 || *cp != ',') {
961 964 dhcpmsg(MSG_ERR, "get_smach_cid: "
962 965 "cannot parse enterprise in %s",
963 966 value);
964 967 goto no_specified_id;
965 968 }
966 969 value = cp + 1;
967 970 slen = strlen(value);
968 971 client_id_len = (slen + 1) / 2;
969 972 den = malloc(sizeof (*den) + client_id_len);
970 973 if (den == NULL)
971 974 goto alloc_failure;
972 975 den->den_dutype = htons(duidtype);
973 976 DHCPV6_SET_ENTNUM(den, subtype);
974 977 if (hexascii_to_octet(value, slen, den + 1,
975 978 &client_id_len) != 0) {
976 979 dhcpmsg(MSG_ERROR, "get_smach_cid: "
977 980 "cannot parse hex string in %s",
978 981 value);
979 982 free(den);
980 983 goto no_specified_id;
981 984 }
982 985 dsmp->dsm_cid = (uchar_t *)den;
983 986 break;
984 987 }
985 988 default:
986 989 slen = strlen(value);
987 990 client_id_len = (slen + 1) / 2;
988 991 cp = malloc(client_id_len);
989 992 if (cp == NULL)
990 993 goto alloc_failure;
991 994 if (hexascii_to_octet(value, slen, cp,
992 995 &client_id_len) != 0) {
993 996 dhcpmsg(MSG_ERROR, "get_smach_cid: "
994 997 "cannot parse hex string in %s",
995 998 value);
996 999 free(cp);
997 1000 goto no_specified_id;
998 1001 }
999 1002 dsmp->dsm_cid = (uchar_t *)cp;
1000 1003 break;
1001 1004 }
1002 1005 dsmp->dsm_cidlen = client_id_len;
1003 1006 if (!pif->pif_isv6) {
1004 1007 (void) memmove(dsmp->dsm_cid + 5,
1005 1008 dsmp->dsm_cid, client_id_len - 5);
1006 1009 dsmp->dsm_cid[0] = 255;
1007 1010 dsmp->dsm_cid[1] = lif->lif_iaid >> 24;
1008 1011 dsmp->dsm_cid[2] = lif->lif_iaid >> 16;
1009 1012 dsmp->dsm_cid[3] = lif->lif_iaid >> 8;
1010 1013 dsmp->dsm_cid[4] = lif->lif_iaid;
1011 1014 }
1012 1015 return (DHCP_IPC_SUCCESS);
1013 1016 }
1014 1017
1015 1018 if (pif->pif_isv6) {
1016 1019 dhcpmsg(MSG_ERROR,
1017 1020 "get_smach_cid: client ID for %s invalid: %s",
1018 1021 dsmp->dsm_name, value);
1019 1022 } else if (strncasecmp("0x", value, 2) == 0 &&
1020 1023 value[2] != '\0') {
1021 1024 /* skip past the 0x and convert the value to binary */
1022 1025 value += 2;
1023 1026 slen = strlen(value);
1024 1027 client_id_len = (slen + 1) / 2;
1025 1028 dsmp->dsm_cid = malloc(client_id_len);
1026 1029 if (dsmp->dsm_cid == NULL)
1027 1030 goto alloc_failure;
1028 1031 if (hexascii_to_octet(value, slen, dsmp->dsm_cid,
1029 1032 &client_id_len) == 0) {
1030 1033 dsmp->dsm_cidlen = client_id_len;
1031 1034 return (DHCP_IPC_SUCCESS);
1032 1035 }
1033 1036 dhcpmsg(MSG_WARNING, "get_smach_cid: cannot convert "
1034 1037 "hex value for Client ID on %s", dsmp->dsm_name);
1035 1038 } else {
1036 1039 client_id_len = strlen(value);
1037 1040 dsmp->dsm_cid = malloc(client_id_len);
1038 1041 if (dsmp->dsm_cid == NULL)
1039 1042 goto alloc_failure;
1040 1043 dsmp->dsm_cidlen = client_id_len;
1041 1044 (void) memcpy(dsmp->dsm_cid, value, client_id_len);
|
↓ open down ↓ |
697 lines elided |
↑ open up ↑ |
1042 1045 return (DHCP_IPC_SUCCESS);
1043 1046 }
1044 1047 }
1045 1048 no_specified_id:
1046 1049
1047 1050 /*
1048 1051 * There was either no user-specified Client ID value, or we were
1049 1052 * unable to parse it. We need to determine if a Client ID is required
1050 1053 * and, if so, generate one.
1051 1054 *
1052 - * If it's IPv4, not in an IPMP group, and not a logical interface,
1055 + * If it's IPv4, not in an IPMP group, not a logical interface,
1056 + * and a DHCP default for DF_V4_DEFAULT_IAID_DUID is not affirmative,
1053 1057 * then we need to preserve backward-compatibility by avoiding
1054 1058 * new-fangled DUID/IAID construction. (Note: even for IPMP test
1055 1059 * addresses, we construct a DUID/IAID since we may renew a lease for
1056 1060 * an IPMP test address on any functioning IP interface in the group.)
1057 1061 */
1058 1062 if (!pif->pif_isv6 && pif->pif_grifname[0] == '\0' &&
1059 - strchr(dsmp->dsm_name, ':') == NULL) {
1063 + strchr(dsmp->dsm_name, ':') == NULL &&
1064 + !df_get_bool(dsmp->dsm_name, pif->pif_isv6,
1065 + DF_V4_DEFAULT_IAID_DUID)) {
1060 1066 if (pif->pif_hwtype == ARPHRD_IB) {
1061 1067 /*
1062 1068 * This comes from the DHCP over IPoIB specification.
1063 1069 * In the absence of an user specified client id, IPoIB
1064 1070 * automatically uses the required format, with the
1065 1071 * unique 4 octet value set to 0 (since IPoIB driver
1066 1072 * allows only a single interface on a port with a
1067 1073 * specific GID to belong to an IP subnet (PSARC
1068 1074 * 2001/289, FWARC 2002/702).
1069 1075 *
1070 1076 * Type Client-Identifier
1071 1077 * +-----+-----+-----+-----+-----+----....----+
1072 1078 * | 0 | 0 (4 octets) | GID (16 octets)|
1073 1079 * +-----+-----+-----+-----+-----+----....----+
1074 1080 */
1075 1081 dsmp->dsm_cidlen = 1 + 4 + 16;
1076 1082 dsmp->dsm_cid = client_id = malloc(dsmp->dsm_cidlen);
1077 1083 if (dsmp->dsm_cid == NULL)
1078 1084 goto alloc_failure;
1079 1085
1080 1086 /*
1081 1087 * Pick the GID from the mac address. The format
1082 1088 * of the hardware address is:
1083 1089 * +-----+-----+-----+-----+----....----+
1084 1090 * | QPN (4 octets) | GID (16 octets)|
1085 1091 * +-----+-----+-----+-----+----....----+
1086 1092 */
1087 1093 (void) memcpy(client_id + 5, pif->pif_hwaddr + 4,
1088 1094 pif->pif_hwlen - 4);
1089 1095 (void) memset(client_id, 0, 5);
1090 1096 }
1091 1097 return (DHCP_IPC_SUCCESS);
1092 1098 }
1093 1099
1094 1100 /*
1095 1101 * Now check for a saved DUID. If there is one, then use it. If there
1096 1102 * isn't, then generate a new one. For IPv4, we need to construct the
1097 1103 * RFC 4361 Client ID with this value and the LIF's IAID.
1098 1104 */
1099 1105 if (global_duid == NULL &&
1100 1106 (global_duid = read_stable_duid(&global_duidlen)) == NULL) {
1101 1107 global_duid = make_stable_duid(pif->pif_name, &global_duidlen);
1102 1108 if (global_duid == NULL)
1103 1109 goto alloc_failure;
1104 1110 duid_retry(NULL, NULL);
1105 1111 }
1106 1112
1107 1113 if (pif->pif_isv6) {
1108 1114 dsmp->dsm_cid = malloc(global_duidlen);
1109 1115 if (dsmp->dsm_cid == NULL)
1110 1116 goto alloc_failure;
1111 1117 (void) memcpy(dsmp->dsm_cid, global_duid, global_duidlen);
1112 1118 dsmp->dsm_cidlen = global_duidlen;
1113 1119 } else {
1114 1120 dsmp->dsm_cid = malloc(5 + global_duidlen);
1115 1121 if (dsmp->dsm_cid == NULL)
1116 1122 goto alloc_failure;
1117 1123 dsmp->dsm_cid[0] = 255;
1118 1124 dsmp->dsm_cid[1] = lif->lif_iaid >> 24;
1119 1125 dsmp->dsm_cid[2] = lif->lif_iaid >> 16;
1120 1126 dsmp->dsm_cid[3] = lif->lif_iaid >> 8;
1121 1127 dsmp->dsm_cid[4] = lif->lif_iaid;
1122 1128 (void) memcpy(dsmp->dsm_cid + 5, global_duid, global_duidlen);
1123 1129 dsmp->dsm_cidlen = 5 + global_duidlen;
1124 1130 }
1125 1131
1126 1132 return (DHCP_IPC_SUCCESS);
1127 1133
1128 1134 alloc_failure:
1129 1135 dhcpmsg(MSG_ERR, "get_smach_cid: cannot allocate Client Id for %s",
1130 1136 dsmp->dsm_name);
1131 1137 return (DHCP_IPC_E_MEMORY);
1132 1138 }
1133 1139
1134 1140 /*
1135 1141 * smach_count(): returns the number of state machines running
1136 1142 *
1137 1143 * input: void
1138 1144 * output: uint_t: the number of state machines
1139 1145 */
1140 1146
1141 1147 uint_t
1142 1148 smach_count(void)
1143 1149 {
1144 1150 return (global_smach_count);
1145 1151 }
1146 1152
1147 1153 /*
1148 1154 * discard_default_routes(): removes a state machine's default routes alone.
1149 1155 *
1150 1156 * input: dhcp_smach_t *: the state machine whose default routes need to be
1151 1157 * discarded
1152 1158 * output: void
1153 1159 */
1154 1160
1155 1161 void
1156 1162 discard_default_routes(dhcp_smach_t *dsmp)
1157 1163 {
1158 1164 free(dsmp->dsm_routers);
1159 1165 dsmp->dsm_routers = NULL;
1160 1166 dsmp->dsm_nrouters = 0;
1161 1167 }
1162 1168
1163 1169 /*
1164 1170 * remove_default_routes(): removes a state machine's default routes from the
1165 1171 * kernel and from the state machine.
1166 1172 *
1167 1173 * input: dhcp_smach_t *: the state machine whose default routes need to be
1168 1174 * removed
1169 1175 * output: void
1170 1176 */
1171 1177
1172 1178 void
1173 1179 remove_default_routes(dhcp_smach_t *dsmp)
1174 1180 {
1175 1181 int idx;
1176 1182 uint32_t ifindex;
1177 1183
1178 1184 if (dsmp->dsm_routers != NULL) {
1179 1185 ifindex = dsmp->dsm_lif->lif_pif->pif_index;
1180 1186 for (idx = dsmp->dsm_nrouters - 1; idx >= 0; idx--) {
1181 1187 if (del_default_route(ifindex,
1182 1188 &dsmp->dsm_routers[idx])) {
1183 1189 dhcpmsg(MSG_DEBUG, "remove_default_routes: "
1184 1190 "removed %s from %s",
1185 1191 inet_ntoa(dsmp->dsm_routers[idx]),
1186 1192 dsmp->dsm_name);
1187 1193 } else {
1188 1194 dhcpmsg(MSG_INFO, "remove_default_routes: "
1189 1195 "unable to remove %s from %s",
1190 1196 inet_ntoa(dsmp->dsm_routers[idx]),
1191 1197 dsmp->dsm_name);
1192 1198 }
1193 1199 }
1194 1200 discard_default_routes(dsmp);
1195 1201 }
1196 1202 }
1197 1203
1198 1204 /*
1199 1205 * reset_smach(): resets a state machine to its initial state
1200 1206 *
1201 1207 * input: dhcp_smach_t *: the state machine to reset
1202 1208 * output: void
1203 1209 */
1204 1210
1205 1211 void
1206 1212 reset_smach(dhcp_smach_t *dsmp)
1207 1213 {
1208 1214 dsmp->dsm_dflags &= ~DHCP_IF_FAILED;
1209 1215
1210 1216 remove_default_routes(dsmp);
|
↓ open down ↓ |
141 lines elided |
↑ open up ↑ |
1211 1217
1212 1218 free_pkt_list(&dsmp->dsm_recv_pkt_list);
1213 1219 free_pkt_entry(dsmp->dsm_ack);
1214 1220 if (dsmp->dsm_orig_ack != dsmp->dsm_ack)
1215 1221 free_pkt_entry(dsmp->dsm_orig_ack);
1216 1222 dsmp->dsm_ack = dsmp->dsm_orig_ack = NULL;
1217 1223
1218 1224 free(dsmp->dsm_reqhost);
1219 1225 dsmp->dsm_reqhost = NULL;
1220 1226
1227 + /*
1228 + * Do not reset dsm_msg_reqhost here. Unlike dsm_reqhost coming from
1229 + * /etc/host.*, dsm_msg_reqhost comes externally, and it survives until
1230 + * it is reset from another external message.
1231 + */
1232 +
1233 + free(dsmp->dsm_reqfqdn);
1234 + dsmp->dsm_reqfqdn = NULL;
1235 +
1221 1236 cancel_smach_timers(dsmp);
1222 1237
1223 1238 (void) set_smach_state(dsmp, INIT);
1224 1239 if (dsmp->dsm_isv6) {
1225 1240 dsmp->dsm_server = ipv6_all_dhcp_relay_and_servers;
1226 1241 } else {
1227 1242 IN6_IPADDR_TO_V4MAPPED(htonl(INADDR_BROADCAST),
1228 1243 &dsmp->dsm_server);
1229 1244 }
1230 1245 dsmp->dsm_neg_hrtime = gethrtime();
1231 1246 /*
1232 1247 * We must never get here with a script running, since it means we're
1233 1248 * resetting an smach that is still in the middle of another state
1234 1249 * transition with a pending dsm_script_callback.
1235 1250 */
1236 1251 assert(dsmp->dsm_script_pid == -1);
1237 1252 }
1238 1253
1239 1254 /*
1240 1255 * refresh_smach(): refreshes a given state machine, as though awakened from
1241 1256 * hibernation or by lower layer "link up."
1242 1257 *
1243 1258 * input: dhcp_smach_t *: state machine to refresh
1244 1259 * output: void
1245 1260 */
1246 1261
1247 1262 void
1248 1263 refresh_smach(dhcp_smach_t *dsmp)
1249 1264 {
1250 1265 if (dsmp->dsm_state == BOUND || dsmp->dsm_state == RENEWING ||
1251 1266 dsmp->dsm_state == REBINDING || dsmp->dsm_state == INFORMATION) {
1252 1267 dhcpmsg(MSG_WARNING, "refreshing state on %s", dsmp->dsm_name);
1253 1268 cancel_smach_timers(dsmp);
1254 1269 if (dsmp->dsm_state == INFORMATION)
1255 1270 dhcp_inform(dsmp);
1256 1271 else
1257 1272 dhcp_init_reboot(dsmp);
1258 1273 }
1259 1274 }
1260 1275
1261 1276 /*
1262 1277 * refresh_smachs(): refreshes all finite leases under DHCP control
1263 1278 *
1264 1279 * input: iu_eh_t *: unused
1265 1280 * int: unused
1266 1281 * void *: unused
1267 1282 * output: void
1268 1283 */
1269 1284
1270 1285 /* ARGSUSED */
1271 1286 void
1272 1287 refresh_smachs(iu_eh_t *eh, int sig, void *arg)
1273 1288 {
1274 1289 boolean_t isv6 = B_FALSE;
1275 1290 dhcp_smach_t *dsmp;
1276 1291
1277 1292 for (;;) {
1278 1293 for (dsmp = next_smach(NULL, isv6); dsmp != NULL;
1279 1294 dsmp = next_smach(dsmp, isv6)) {
1280 1295 refresh_smach(dsmp);
1281 1296 }
1282 1297 if (isv6)
1283 1298 break;
1284 1299 isv6 = B_TRUE;
1285 1300 }
1286 1301 }
1287 1302
1288 1303 /*
1289 1304 * nuke_smach_list(): delete the state machine list. For use when the
1290 1305 * dhcpagent is exiting.
1291 1306 *
1292 1307 * input: none
1293 1308 * output: none
1294 1309 */
1295 1310
1296 1311 void
1297 1312 nuke_smach_list(void)
1298 1313 {
1299 1314 boolean_t isv6 = B_FALSE;
1300 1315 dhcp_smach_t *dsmp, *dsmp_next;
1301 1316
1302 1317 for (;;) {
1303 1318 for (dsmp = next_smach(NULL, isv6); dsmp != NULL;
1304 1319 dsmp = dsmp_next) {
1305 1320 int status;
1306 1321
1307 1322 dsmp_next = next_smach(dsmp, isv6);
1308 1323
1309 1324 /* If we're already dropping or releasing, skip */
1310 1325 if (dsmp->dsm_droprelease)
1311 1326 continue;
1312 1327 dsmp->dsm_droprelease = B_TRUE;
1313 1328
1314 1329 cancel_smach_timers(dsmp);
1315 1330
1316 1331 /*
1317 1332 * If the script is started by script_start, dhcp_drop
1318 1333 * and dhcp_release should and will only be called
1319 1334 * after the script exits.
1320 1335 */
1321 1336 if (df_get_bool(dsmp->dsm_name, isv6,
1322 1337 DF_RELEASE_ON_SIGTERM) ||
1323 1338 df_get_bool(dsmp->dsm_name, isv6,
1324 1339 DF_VERIFIED_LEASE_ONLY)) {
1325 1340 if (script_start(dsmp, isv6 ? EVENT_RELEASE6 :
1326 1341 EVENT_RELEASE, dhcp_release,
1327 1342 "DHCP agent is exiting", &status)) {
1328 1343 continue;
1329 1344 }
1330 1345 if (status == 1)
1331 1346 continue;
1332 1347 }
1333 1348 (void) script_start(dsmp, isv6 ? EVENT_DROP6 :
1334 1349 EVENT_DROP, dhcp_drop, NULL, NULL);
1335 1350 }
1336 1351 if (isv6)
1337 1352 break;
1338 1353 isv6 = B_TRUE;
1339 1354 }
1340 1355 }
1341 1356
1342 1357 /*
1343 1358 * insert_lease(): Create a lease structure on a given state machine. The
1344 1359 * lease holds a reference to the state machine.
1345 1360 *
1346 1361 * input: dhcp_smach_t *: state machine
1347 1362 * output: dhcp_lease_t *: newly-created lease
1348 1363 */
1349 1364
1350 1365 dhcp_lease_t *
1351 1366 insert_lease(dhcp_smach_t *dsmp)
1352 1367 {
1353 1368 dhcp_lease_t *dlp;
1354 1369
1355 1370 if ((dlp = calloc(1, sizeof (*dlp))) == NULL)
1356 1371 return (NULL);
1357 1372 dlp->dl_smach = dsmp;
1358 1373 dlp->dl_hold_count = 1;
1359 1374 init_timer(&dlp->dl_t1, 0);
1360 1375 init_timer(&dlp->dl_t2, 0);
1361 1376 insque(dlp, &dsmp->dsm_leases);
1362 1377 dhcpmsg(MSG_DEBUG2, "insert_lease: new lease for %s", dsmp->dsm_name);
1363 1378 return (dlp);
1364 1379 }
1365 1380
1366 1381 /*
1367 1382 * hold_lease(): acquires a hold on a lease
1368 1383 *
1369 1384 * input: dhcp_lease_t *: the lease to acquire a hold on
1370 1385 * output: void
1371 1386 */
1372 1387
1373 1388 void
1374 1389 hold_lease(dhcp_lease_t *dlp)
1375 1390 {
1376 1391 dlp->dl_hold_count++;
1377 1392
1378 1393 dhcpmsg(MSG_DEBUG2, "hold_lease: hold count on lease for %s: %d",
1379 1394 dlp->dl_smach->dsm_name, dlp->dl_hold_count);
1380 1395 }
1381 1396
1382 1397 /*
1383 1398 * release_lease(): releases a hold previously acquired on a lease.
1384 1399 * If the hold count reaches 0, the lease is freed.
1385 1400 *
1386 1401 * input: dhcp_lease_t *: the lease to release the hold on
1387 1402 * output: void
1388 1403 */
1389 1404
1390 1405 void
1391 1406 release_lease(dhcp_lease_t *dlp)
1392 1407 {
1393 1408 if (dlp->dl_hold_count == 0) {
1394 1409 dhcpmsg(MSG_CRIT, "release_lease: extraneous release");
1395 1410 return;
1396 1411 }
1397 1412
1398 1413 if (dlp->dl_hold_count == 1 && !dlp->dl_removed) {
1399 1414 dhcpmsg(MSG_CRIT, "release_lease: missing removal");
1400 1415 return;
1401 1416 }
1402 1417
1403 1418 if (--dlp->dl_hold_count == 0) {
1404 1419 dhcpmsg(MSG_DEBUG,
1405 1420 "release_lease: freeing lease on state machine %s",
1406 1421 dlp->dl_smach->dsm_name);
1407 1422 free(dlp);
1408 1423 } else {
1409 1424 dhcpmsg(MSG_DEBUG2,
1410 1425 "release_lease: hold count on lease for %s: %d",
1411 1426 dlp->dl_smach->dsm_name, dlp->dl_hold_count);
1412 1427 }
1413 1428 }
1414 1429
1415 1430 /*
1416 1431 * remove_lease(): removes a given lease from the state machine and drops the
1417 1432 * state machine's hold on the lease.
1418 1433 *
1419 1434 * input: dhcp_lease_t *: the lease to remove
1420 1435 * output: void
1421 1436 */
1422 1437
1423 1438 void
1424 1439 remove_lease(dhcp_lease_t *dlp)
1425 1440 {
1426 1441 if (dlp->dl_removed) {
1427 1442 dhcpmsg(MSG_CRIT, "remove_lease: extraneous removal");
1428 1443 } else {
1429 1444 dhcp_lif_t *lif, *lifnext;
1430 1445 uint_t nlifs;
1431 1446
1432 1447 dhcpmsg(MSG_DEBUG,
1433 1448 "remove_lease: removed lease from state machine %s",
1434 1449 dlp->dl_smach->dsm_name);
1435 1450 dlp->dl_removed = B_TRUE;
1436 1451 remque(dlp);
1437 1452
1438 1453 cancel_lease_timers(dlp);
1439 1454
1440 1455 lif = dlp->dl_lifs;
1441 1456 nlifs = dlp->dl_nlifs;
1442 1457 for (; nlifs > 0; nlifs--, lif = lifnext) {
1443 1458 lifnext = lif->lif_next;
1444 1459 unplumb_lif(lif);
1445 1460 }
1446 1461
1447 1462 release_lease(dlp);
1448 1463 }
1449 1464 }
1450 1465
1451 1466 /*
1452 1467 * cancel_lease_timer(): cancels a lease-related timer
1453 1468 *
1454 1469 * input: dhcp_lease_t *: the lease to operate on
1455 1470 * dhcp_timer_t *: the timer to cancel
1456 1471 * output: void
1457 1472 */
1458 1473
1459 1474 static void
1460 1475 cancel_lease_timer(dhcp_lease_t *dlp, dhcp_timer_t *dt)
1461 1476 {
1462 1477 if (dt->dt_id == -1)
1463 1478 return;
1464 1479 if (cancel_timer(dt)) {
1465 1480 release_lease(dlp);
1466 1481 } else {
1467 1482 dhcpmsg(MSG_WARNING,
1468 1483 "cancel_lease_timer: cannot cancel timer");
1469 1484 }
1470 1485 }
1471 1486
1472 1487 /*
1473 1488 * cancel_lease_timers(): cancels an lease's pending timers
1474 1489 *
1475 1490 * input: dhcp_lease_t *: the lease to operate on
1476 1491 * output: void
1477 1492 */
1478 1493
1479 1494 void
1480 1495 cancel_lease_timers(dhcp_lease_t *dlp)
1481 1496 {
1482 1497 cancel_lease_timer(dlp, &dlp->dl_t1);
1483 1498 cancel_lease_timer(dlp, &dlp->dl_t2);
1484 1499 }
1485 1500
1486 1501 /*
1487 1502 * schedule_lease_timer(): schedules a lease-related timer
1488 1503 *
1489 1504 * input: dhcp_lease_t *: the lease to operate on
1490 1505 * dhcp_timer_t *: the timer to schedule
1491 1506 * iu_tq_callback_t *: the callback to call upon firing
1492 1507 * output: boolean_t: B_TRUE if the timer was scheduled successfully
1493 1508 */
1494 1509
1495 1510 boolean_t
1496 1511 schedule_lease_timer(dhcp_lease_t *dlp, dhcp_timer_t *dt,
1497 1512 iu_tq_callback_t *expire)
1498 1513 {
1499 1514 /*
1500 1515 * If there's a timer running, cancel it and release its lease
1501 1516 * reference.
1502 1517 */
1503 1518 if (dt->dt_id != -1) {
1504 1519 if (!cancel_timer(dt))
1505 1520 return (B_FALSE);
1506 1521 release_lease(dlp);
1507 1522 }
1508 1523
1509 1524 if (schedule_timer(dt, expire, dlp)) {
1510 1525 hold_lease(dlp);
1511 1526 return (B_TRUE);
1512 1527 } else {
1513 1528 dhcpmsg(MSG_WARNING,
1514 1529 "schedule_lease_timer: cannot schedule timer");
1515 1530 return (B_FALSE);
1516 1531 }
1517 1532 }
1518 1533
1519 1534 /*
1520 1535 * deprecate_leases(): remove all of the leases from a given state machine
1521 1536 *
1522 1537 * input: dhcp_smach_t *: the state machine
1523 1538 * output: none
1524 1539 */
1525 1540
1526 1541 void
1527 1542 deprecate_leases(dhcp_smach_t *dsmp)
1528 1543 {
1529 1544 dhcp_lease_t *dlp;
1530 1545
1531 1546 /*
1532 1547 * note that due to infelicities in the routing code, any default
1533 1548 * routes must be removed prior to canonizing or deprecating the LIF.
1534 1549 */
1535 1550
1536 1551 remove_default_routes(dsmp);
1537 1552
1538 1553 while ((dlp = dsmp->dsm_leases) != NULL)
1539 1554 remove_lease(dlp);
1540 1555 }
1541 1556
1542 1557 /*
1543 1558 * verify_smach(): if the state machine is in a bound state, then verify the
1544 1559 * standing of the configured interfaces. Abandon those that
1545 1560 * the user has modified. If we end up with no valid leases,
1546 1561 * then just terminate the state machine.
1547 1562 *
1548 1563 * input: dhcp_smach_t *: the state machine
1549 1564 * output: boolean_t: B_TRUE if the state machine is still valid.
1550 1565 * note: assumes caller holds a state machine reference; as with most
1551 1566 * callback functions.
1552 1567 */
1553 1568
1554 1569 boolean_t
1555 1570 verify_smach(dhcp_smach_t *dsmp)
1556 1571 {
1557 1572 dhcp_lease_t *dlp, *dlpn;
1558 1573
1559 1574 if (dsmp->dsm_dflags & DHCP_IF_REMOVED) {
1560 1575 release_smach(dsmp);
1561 1576 return (B_FALSE);
1562 1577 }
1563 1578
1564 1579 if (!dsmp->dsm_isv6) {
1565 1580 /*
1566 1581 * If this is DHCPv4, then verify the main LIF.
1567 1582 */
1568 1583 if (!verify_lif(dsmp->dsm_lif))
1569 1584 goto smach_terminate;
1570 1585 }
1571 1586
1572 1587 /*
1573 1588 * If we're not in one of the bound states, then there are no LIFs to
1574 1589 * verify here.
1575 1590 */
1576 1591 if (dsmp->dsm_state != BOUND &&
1577 1592 dsmp->dsm_state != RENEWING &&
1578 1593 dsmp->dsm_state != REBINDING) {
1579 1594 release_smach(dsmp);
1580 1595 return (B_TRUE);
1581 1596 }
1582 1597
1583 1598 for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlpn) {
1584 1599 dhcp_lif_t *lif, *lifnext;
1585 1600 uint_t nlifs;
1586 1601
1587 1602 dlpn = dlp->dl_next;
1588 1603 lif = dlp->dl_lifs;
1589 1604 nlifs = dlp->dl_nlifs;
1590 1605 for (; nlifs > 0; lif = lifnext, nlifs--) {
1591 1606 lifnext = lif->lif_next;
1592 1607 if (!verify_lif(lif)) {
1593 1608 /*
1594 1609 * User has manipulated the interface. Even
1595 1610 * if we plumbed it, we must now disown it.
1596 1611 */
1597 1612 lif->lif_plumbed = B_FALSE;
1598 1613 remove_lif(lif);
1599 1614 }
1600 1615 }
1601 1616 if (dlp->dl_nlifs == 0)
1602 1617 remove_lease(dlp);
1603 1618 }
1604 1619
1605 1620 /*
1606 1621 * If there are leases left, then everything's ok.
1607 1622 */
1608 1623 if (dsmp->dsm_leases != NULL) {
1609 1624 release_smach(dsmp);
1610 1625 return (B_TRUE);
1611 1626 }
1612 1627
1613 1628 smach_terminate:
1614 1629 finished_smach(dsmp, DHCP_IPC_E_INVIF);
1615 1630 release_smach(dsmp);
1616 1631
1617 1632 return (B_FALSE);
1618 1633 }
|
↓ open down ↓ |
388 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX