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