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