static PKT_LIST * select_best(dhcp_smach_t *dsmp) { PKT_LIST *current = dsmp->dsm_recv_pkt_list; PKT_LIST *next, *best = NULL; int points, best_points = -1; /* * pick out the best offer. point system. * what's important for IPv4? * * 0) DHCP (30 points) * 1) no option overload * 2) encapsulated vendor option (80 points) * 3) non-null sname and siaddr fields * 4) non-null file field * 5) hostname (5 points) * 6) subnetmask (1 point) * 7) router (1 point) */ for (; current != NULL; current = next) { next = current->next; points = current->isv6 ? compute_points_v6(current, dsmp) : compute_points_v4(current); /* * Just discard any unacceptable entries we encounter. */ if (points == -1) { remque(current); free_pkt_entry(current); continue; } dhcpmsg(MSG_DEBUG, "select_best: OFFER had %d points", points); /* Special case: stop now and select */ if (points == -2) { best = current; break; } if (points >= best_points) { best_points = points; best = current; } } if (best != NULL) { dhcpmsg(MSG_DEBUG, "select_best: most points: %d", best_points); remque(best); } else { dhcpmsg(MSG_DEBUG, "select_best: no valid OFFER/BOOTP reply"); } return (best); }
void dhcp_restart(dhcp_smach_t *dsmp) { if (dsmp->dsm_state == INFORM_SENT || dsmp->dsm_state == INFORMATION) return; /* * As we're returning to INIT state, we need to discard any leases we * may have, and (for v4) canonize the LIF. There's a bit of tension * between keeping around a possibly still working address, and obeying * the RFCs. A more elaborate design would be to mark the addresses as * DEPRECATED, and then start a removal timer. Such a design would * probably compromise testing. */ deprecate_leases(dsmp); if (!set_start_timer(dsmp)) { dhcpmsg(MSG_ERROR, "dhcp_restart: cannot schedule dhcp_start, " "reverting to INIT state on %s", dsmp->dsm_name); (void) set_smach_state(dsmp, INIT); dsmp->dsm_dflags |= DHCP_IF_FAILED; ipc_action_finish(dsmp, DHCP_IPC_E_MEMORY); } else { dhcpmsg(MSG_DEBUG, "dhcp_restart: restarting DHCP on %s", dsmp->dsm_name); } }
/* ARGSUSED */ void dhcp_packet_lif(iu_eh_t *ehp, int fd, short events, iu_event_id_t id, void *arg) { dhcp_lif_t *lif = arg; PKT_LIST *plp; uchar_t recv_type; const char *pname; uint_t xid; dhcp_smach_t *dsmp; if ((plp = recv_pkt(fd, lif->lif_max, B_FALSE)) == NULL) return; recv_type = pkt_recv_type(plp); pname = pkt_type_to_string(recv_type, B_FALSE); if (!pkt_v4_match(recv_type, DHCP_PACK | DHCP_PNAK | DHCP_PUNTYPED | DHCP_POFFER)) { dhcpmsg(MSG_VERBOSE, "dhcp_packet_lif: ignored v4 %s packet " "received via LIF %s", pname, lif->lif_name); free_pkt_entry(plp); return; } /* * Find the corresponding state machine. */ xid = pkt_get_xid(plp->pkt, B_FALSE); for (dsmp = lookup_smach_by_xid(xid, NULL, B_FALSE); dsmp != NULL; dsmp = lookup_smach_by_xid(xid, dsmp, B_FALSE)) { if (dsmp->dsm_lif == lif) break; } if (dsmp == NULL) goto drop; if (pkt_v4_match(recv_type, DHCP_PACK|DHCP_PNAK)) { /* * We've got an ACK/NAK; make sure it's acceptable and cancel * the REQUEST retransmissions. */ accept_v4_acknak(dsmp, plp); } else { if (is_bound_state(dsmp->dsm_state)) goto drop; /* * Must be an OFFER or a BOOTP message: enqueue it for later * processing by select_best(). */ pkt_smach_enqueue(dsmp, plp); } return; drop: dhcpmsg(MSG_VERBOSE, "dhcp_packet_lif: ignored %s packet xid " "%x received via LIF %s; %s", pname, xid, lif->lif_name, dsmp == NULL ? "unknown state machine" : "bound"); free_pkt_entry(plp); }
void send_data_reply(ipc_action_t *ia, int error, dhcp_data_type_t type, const void *buffer, size_t size) { dhcp_ipc_reply_t *reply; int retval; if (ia->ia_fd == -1 || ia->ia_request == NULL) return; reply = dhcp_ipc_alloc_reply(ia->ia_request, error, buffer, size, type); if (reply == NULL) { dhcpmsg(MSG_ERR, "send_data_reply: cannot allocate reply"); } else if ((retval = dhcp_ipc_send_reply(ia->ia_fd, reply)) != 0) { dhcpmsg(MSG_ERROR, "send_data_reply: dhcp_ipc_send_reply: %s", dhcp_ipc_strerror(retval)); } /* * free the request since we've now used it to send our reply. * we can also close the socket since the reply has been sent. */ free(reply); free(ia->ia_request); if (ia->ia_eid != -1) (void) iu_unregister_event(eh, ia->ia_eid, NULL); (void) dhcp_ipc_close(ia->ia_fd); ia->ia_request = NULL; ia->ia_fd = -1; ia->ia_eid = -1; }
void server_unicast_option(dhcp_smach_t *dsmp, PKT_LIST *plp) { const dhcpv6_option_t *d6o; uint_t olen; d6o = dhcpv6_pkt_option(plp, NULL, DHCPV6_OPT_UNICAST, &olen); olen -= sizeof (*d6o); /* LINTED: no consequent */ if (d6o == NULL) { /* No Server Unicast option specified */ } else if (olen != sizeof (dsmp->dsm_server)) { dhcpmsg(MSG_WARNING, "server_unicast_option: %s has Server " "Unicast option with bad length", pkt_type_to_string(pkt_recv_type(plp), B_TRUE)); } else { in6_addr_t addr; (void) memcpy(&addr, d6o + 1, olen); if (IN6_IS_ADDR_UNSPECIFIED(&addr)) { dhcpmsg(MSG_WARNING, "server_unicast_option: unicast " "to unspecified address ignored"); } else if (IN6_IS_ADDR_MULTICAST(&addr)) { dhcpmsg(MSG_WARNING, "server_unicast_option: unicast " "to multicast address ignored"); } else if (IN6_IS_ADDR_V4COMPAT(&addr) || IN6_IS_ADDR_V4MAPPED(&addr)) { dhcpmsg(MSG_WARNING, "server_unicast_option: unicast " "to invalid address ignored"); } else { dsmp->dsm_server = addr; } } }
/* ARGSUSED */ void dhcp_renew(iu_tq_t *tqp, void *arg) { dhcp_lease_t *dlp = arg; dhcp_smach_t *dsmp = dlp->dl_smach; uint32_t t2; dhcpmsg(MSG_VERBOSE, "dhcp_renew: T1 timer expired on %s", dsmp->dsm_name); dlp->dl_t1.dt_id = -1; if (dsmp->dsm_state == RENEWING || dsmp->dsm_state == REBINDING) { dhcpmsg(MSG_DEBUG, "dhcp_renew: already renewing"); release_lease(dlp); return; } /* * Sanity check: don't send packets if we're past T2, or if we're * extremely close. */ t2 = dsmp->dsm_curstart_monosec + dlp->dl_t2.dt_start; if (monosec() + TOO_CLOSE >= t2) { dhcpmsg(MSG_DEBUG, "dhcp_renew: %spast T2 on %s", monosec() > t2 ? "" : "almost ", dsmp->dsm_name); release_lease(dlp); return; } /* * If there isn't an async event pending, or if we can cancel the one * that's there, then try to renew by sending an extension request. If * that fails, we'll try again when the next timer fires. */ if (!async_cancel(dsmp) || !async_start(dsmp, DHCP_EXTEND, B_FALSE) || !dhcp_extending(dsmp)) { if (monosec() + RETRY_DELAY < t2) { /* * Try again in RETRY_DELAY seconds; user command * should be gone. */ init_timer(&dlp->dl_t1, RETRY_DELAY); (void) set_smach_state(dsmp, BOUND); if (!schedule_lease_timer(dlp, &dlp->dl_t1, dhcp_renew)) { dhcpmsg(MSG_INFO, "dhcp_renew: unable to " "reschedule renewal around user command " "on %s; will wait for rebind", dsmp->dsm_name); } } else { dhcpmsg(MSG_DEBUG, "dhcp_renew: user busy on %s; will " "wait for rebind", dsmp->dsm_name); } } release_lease(dlp); }
static boolean_t stop_init_reboot(dhcp_smach_t *dsmp, unsigned int n_requests) { if (dsmp->dsm_isv6) { uint_t nowabs, maxabs; nowabs = gethrtime() / (NANOSEC / MILLISEC); maxabs = dsmp->dsm_neg_hrtime / (NANOSEC / MILLISEC) + DHCPV6_CNF_MAX_RD; if (nowabs < maxabs) { /* Cap the timer based on the maximum */ if (nowabs + dsmp->dsm_send_timeout > maxabs) dsmp->dsm_send_timeout = maxabs - nowabs; return (B_FALSE); } } else { if (n_requests < DHCP_MAX_REQUESTS) return (B_FALSE); } if (df_get_bool(dsmp->dsm_name, dsmp->dsm_isv6, DF_VERIFIED_LEASE_ONLY)) { dhcpmsg(MSG_INFO, "unable to verify existing lease on %s; restarting", dsmp->dsm_name); dhcp_selecting(dsmp); return (B_TRUE); } if (dsmp->dsm_isv6) { dhcpmsg(MSG_INFO, "no Reply to Confirm, using remainder of " "existing lease on %s", dsmp->dsm_name); } else { dhcpmsg(MSG_INFO, "no ACK/NAK to INIT_REBOOT REQUEST, " "using remainder of existing lease on %s", dsmp->dsm_name); } /* * We already stuck our old ack in dsmp->dsm_ack and relativized the * packet times, so we can just pretend that the server sent it to us * and move to bound. If that fails, fall back to selecting. */ if (dhcp_bound(dsmp, NULL)) { if (dsmp->dsm_isv6) { if (!save_server_id(dsmp, dsmp->dsm_ack)) goto failure; server_unicast_option(dsmp, dsmp->dsm_ack); } } else { failure: dhcpmsg(MSG_INFO, "unable to use saved lease on %s; restarting", dsmp->dsm_name); dhcp_selecting(dsmp); } return (B_TRUE); }
boolean_t ipc_action_start(dhcp_smach_t *dsmp, ipc_action_t *iareq) { struct ipc_action *ia = &dsmp->dsm_ia; if (ia->ia_fd != -1 || ia->ia_tid != -1 || iareq->ia_fd == -1) { dhcpmsg(MSG_CRIT, "ipc_action_start: attempted restart on %s", dsmp->dsm_name); return (B_FALSE); } if (!async_cancel(dsmp)) { dhcpmsg(MSG_WARNING, "ipc_action_start: unable to cancel " "action on %s", dsmp->dsm_name); return (B_FALSE); } if (iareq->ia_request->timeout == DHCP_IPC_WAIT_DEFAULT) iareq->ia_request->timeout = DHCP_IPC_DEFAULT_WAIT; if (iareq->ia_request->timeout == DHCP_IPC_WAIT_FOREVER) { iareq->ia_tid = -1; } else { iareq->ia_tid = iu_schedule_timer(tq, iareq->ia_request->timeout, ipc_action_timeout, dsmp); if (iareq->ia_tid == -1) { dhcpmsg(MSG_ERROR, "ipc_action_start: failed to set " "timer for %s on %s", dhcp_ipc_type_to_string(iareq->ia_cmd), dsmp->dsm_name); return (B_FALSE); } hold_smach(dsmp); } *ia = *iareq; /* We've taken ownership, so the input request is now invalid */ ipc_action_init(iareq); dhcpmsg(MSG_DEBUG, "ipc_action_start: started %s (command %d) on %s," " buffer length %u", dhcp_ipc_type_to_string(ia->ia_cmd), ia->ia_cmd, dsmp->dsm_name, ia->ia_request == NULL ? 0 : ia->ia_request->data_length); dsmp->dsm_dflags |= DHCP_IF_BUSY; /* This cannot fail due to the async_cancel above */ (void) async_start(dsmp, ia->ia_cmd, B_TRUE); return (B_TRUE); }
static void dhcp_init_reboot_v4(dhcp_smach_t *dsmp) { dhcp_pkt_t *dpkt; const char *reqhost; char hostfile[PATH_MAX + 1]; /* * assemble DHCPREQUEST message. The max dhcp message size * option is set to the interface max, minus the size of the udp and * ip headers. */ dpkt = init_pkt(dsmp, REQUEST); (void) add_pkt_opt32(dpkt, CD_REQUESTED_IP_ADDR, dsmp->dsm_ack->pkt->yiaddr.s_addr); (void) add_pkt_opt32(dpkt, CD_LEASE_TIME, htonl(DHCP_PERM)); (void) add_pkt_opt16(dpkt, CD_MAX_DHCP_SIZE, htons(dsmp->dsm_lif->lif_pif->pif_max - sizeof (struct udpiphdr))); if (class_id_len != 0) (void) add_pkt_opt(dpkt, CD_CLASS_ID, class_id, class_id_len); (void) add_pkt_prl(dpkt, dsmp); /* * Set CD_HOSTNAME option if REQUEST_HOSTNAME is set and a hostname * is found in /etc/hostname.<ifname> */ if (df_get_bool(dsmp->dsm_name, dsmp->dsm_isv6, DF_REQUEST_HOSTNAME)) { (void) snprintf(hostfile, sizeof (hostfile), "/etc/hostname.%s", dsmp->dsm_name); if ((reqhost = iffile_to_hostname(hostfile)) != NULL) { dhcpmsg(MSG_DEBUG, "dhcp_selecting: host %s", reqhost); if ((dsmp->dsm_reqhost = strdup(reqhost)) != NULL) (void) add_pkt_opt(dpkt, CD_HOSTNAME, dsmp->dsm_reqhost, strlen(dsmp->dsm_reqhost)); else dhcpmsg(MSG_WARNING, "dhcp_selecting: cannot" " allocate memory for host name option"); } else { dhcpmsg(MSG_DEBUG, "dhcp_selecting: no hostname for %s", dsmp->dsm_name); } } (void) add_pkt_opt(dpkt, CD_END, NULL, 0); (void) send_pkt(dsmp, dpkt, htonl(INADDR_BROADCAST), stop_init_reboot); }
/* ARGSUSED */ void dhcp_expire(iu_tq_t *tqp, void *arg) { struct ifslist *ifsp = (struct ifslist *)arg; ifsp->if_timer[DHCP_LEASE_TIMER] = -1; if (check_ifs(ifsp) == 0) { (void) release_ifs(ifsp); return; } if (async_pending(ifsp)) if (async_cancel(ifsp) == 0) { dhcpmsg(MSG_WARNING, "dhcp_expire: cannot cancel " "current asynchronous command against %s", ifsp->if_name); /* * try to schedule ourselves for callback. * we're really situation critical here * there's not much hope for us if this fails. */ if (iu_schedule_timer(tq, DHCP_EXPIRE_WAIT, dhcp_expire, ifsp) != -1) { hold_ifs(ifsp); return; } dhcpmsg(MSG_CRIT, "dhcp_expire: cannot reschedule " "dhcp_expire to get called back, proceeding..."); } /* * just march on if this fails; at worst someone will be able * to async_start() while we're actually busy with our own * asynchronous transaction. better than not having a lease. */ if (async_start(ifsp, DHCP_START, B_FALSE) == 0) dhcpmsg(MSG_WARNING, "dhcp_expire: cannot start asynchronous " "transaction on %s, continuing...", ifsp->if_name); (void) script_start(ifsp, EVENT_EXPIRE, dhcp_restart_lease, NULL, NULL); }
/* ARGSUSED */ static void accept_event(iu_eh_t *ehp, int fd, short events, iu_event_id_t id, void *arg) { int client_fd; int is_priv; if (dhcp_ipc_accept(fd, &client_fd, &is_priv) != 0) { dhcpmsg(MSG_ERR, "accept_event: accept on ipc socket"); return; } if (iu_register_event(eh, client_fd, POLLIN, ipc_event, (void *)is_priv) == -1) { dhcpmsg(MSG_ERROR, "accept_event: cannot register ipc socket " "for callback"); } }
static int dhcp_finish_expire(dhcp_smach_t *dsmp, void *arg) { dhcp_lif_t *lif = arg; dhcp_lease_t *dlp; dhcpmsg(MSG_DEBUG, "lease expired on %s; removing", lif->lif_name); dlp = lif->lif_lease; unplumb_lif(lif); if (dlp->dl_nlifs == 0) remove_lease(dlp); release_lif(lif); /* If some valid leases remain, then drive on */ if (dsmp->dsm_leases != NULL) { dhcpmsg(MSG_DEBUG, "dhcp_finish_expire: some leases remain on %s", dsmp->dsm_name); return (1); } (void) remove_hostconf(dsmp->dsm_name, dsmp->dsm_isv6); dhcpmsg(MSG_INFO, "last lease expired on %s -- restarting DHCP", dsmp->dsm_name); /* * in the case where the lease is less than DHCP_REBIND_MIN * seconds, we will never enter dhcp_renew() and thus the packet * counters will not be reset. in that case, reset them here. */ if (dsmp->dsm_state == BOUND) { dsmp->dsm_bad_offers = 0; dsmp->dsm_sent = 0; dsmp->dsm_received = 0; } deprecate_leases(dsmp); /* reset_smach() in dhcp_selecting() will clean up any leftover state */ dhcp_selecting(dsmp); return (1); }
void print_server_msg(dhcp_smach_t *dsmp, const char *msg, uint_t msglen) { if (msglen > 0) { dhcpmsg(MSG_INFO, "%s: message from server: %.*s", dsmp->dsm_name, msglen, msg); } }
/* * Sanity check that dsvcd_request_t `req' (which is `reqsize' bytes long) * is a correctly formed request; if not, return an error which will be * returned to the door caller. */ static int check_door_req(dsvcd_request_t *req, size_t reqsize, size_t minsize) { door_cred_t cred; if (req == NULL) { dhcpmsg(MSG_WARNING, "empty request, ignoring"); return (DSVC_SYNCH_ERR); } /* * Check credentials; we don't allow any non-super-user requests * since this would open a denial-of-service hole (since a lock * could be checked out indefinitely). */ if (door_cred(&cred) != 0) { dhcpmsg(MSG_WARNING, "request with unknown credentials"); return (DSVC_ACCESS); } if (cred.dc_euid != 0) { dhcpmsg(MSG_WARNING, "request with non-super-user credentials"); return (DSVC_ACCESS); } /* * Check the version and size; we check this before checking the * size of the request structure since an "incompatible version" * message is more helpful than a "short request" message. */ if (reqsize > offsetof(dsvcd_request_t, rq_version) && req->rq_version != DSVCD_DOOR_VERSION) { dhcpmsg(MSG_WARNING, "request with unsupported version `%d'", req->rq_version); return (DSVC_SYNCH_ERR); } if (reqsize < minsize) { dhcpmsg(MSG_VERBOSE, "short request (%d bytes, minimum %d " "bytes)", reqsize, minsize); return (DSVC_SYNCH_ERR); } return (DSVC_SUCCESS); }
/* * srealloc() -- safe realloc() * * Always returns a valid pointer(if it returns at all). * If realloc() returns an error, a message is printed using the syslog() * function and the program aborts with a status of 1. * Unlike smalloc(), does not initialize the buffer to all zeros. * * Must be MT SAFE - called by threads other than the main thread. */ void * srealloc(void *arg, uint_t nbytes) { if ((arg = realloc(arg, nbytes)) == NULL) { dhcpmsg(LOG_ERR, "Cannot allocate memory (%s), exiting\n", strerror(errno)); exit(1); } return (arg); }
/* ARGSUSED */ void inactivity_shutdown(iu_tq_t *tqp, void *arg) { if (smach_count() > 0) /* shouldn't happen, but... */ return; dhcpmsg(MSG_VERBOSE, "inactivity_shutdown: timed out"); iu_stop_handling_events(eh, DHCP_REASON_INACTIVITY, NULL, NULL); }
/* ARGSUSED */ static boolean_t stop_extending(dhcp_smach_t *dsmp, unsigned int n_requests) { dhcp_lease_t *dlp; /* * If we're renewing and rebind time is soon approaching, then don't * schedule */ if (dsmp->dsm_state == RENEWING) { monosec_t t2; t2 = 0; for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next) { if (dlp->dl_t2.dt_start > t2) t2 = dlp->dl_t2.dt_start; } t2 += dsmp->dsm_curstart_monosec; if (monosec() + TOO_CLOSE >= t2) { dhcpmsg(MSG_DEBUG, "stop_extending: %spast T2 on %s", monosec() > t2 ? "" : "almost ", dsmp->dsm_name); return (B_TRUE); } } /* * Note that returning B_TRUE cancels both this transmission and the * one that would occur at dsm_send_timeout, and that for v4 we cut the * time in half for each retransmission. Thus we check here against * half of the minimum. */ if (!dsmp->dsm_isv6 && dsmp->dsm_send_timeout < DHCP_REBIND_MIN * MILLISEC / 2) { dhcpmsg(MSG_DEBUG, "stop_extending: next retry would be in " "%d.%03d; stopping", dsmp->dsm_send_timeout / MILLISEC, dsmp->dsm_send_timeout % MILLISEC); return (B_TRUE); } /* Otherwise, w stop only when the next timer (rebind, expire) fires */ return (B_FALSE); }
static boolean_t check_main_lif(dhcp_smach_t *dsmp, const struct ifa_msghdr *ifam, int msglen) { dhcp_lif_t *lif = dsmp->dsm_lif; struct lifreq lifr; /* * Get the real (64 bit) logical interface flags. Note that the * routing socket message has flags, but these are just the lower 32 * bits. */ (void) memset(&lifr, 0, sizeof (lifr)); (void) strlcpy(lifr.lifr_name, lif->lif_name, sizeof (lifr.lifr_name)); if (ioctl(v6_sock_fd, SIOCGLIFFLAGS, &lifr) == -1) { /* * Failing to retrieve flags means that the interface is gone. * Our state machine is now trash. */ if (errno == ENXIO) { dhcpmsg(MSG_INFO, "%s has been removed; abandoning", lif->lif_name); } else { dhcpmsg(MSG_ERR, "unable to retrieve interface flags on %s", lif->lif_name); } return (B_FALSE); } else if (!check_rtm_addr(ifam, msglen, B_TRUE, &lif->lif_v6addr)) { /* * If the message is not about this logical interface, * then just ignore it. */ return (B_TRUE); } else if (lifr.lifr_flags & IFF_DUPLICATE) { dhcpmsg(MSG_ERROR, "interface %s has duplicate address", lif->lif_name); return (B_FALSE); } else { return (B_TRUE); } }
void write_lease_to_hostconf(dhcp_smach_t *dsmp) { PKT_LIST *plp[2]; const char *hcfile; hcfile = ifname_to_hostconf(dsmp->dsm_name, dsmp->dsm_isv6); plp[0] = dsmp->dsm_ack; plp[1] = dsmp->dsm_orig_ack; if (write_hostconf(dsmp->dsm_name, plp, 2, monosec_to_time(dsmp->dsm_curstart_monosec), dsmp->dsm_isv6) != -1) { dhcpmsg(MSG_DEBUG, "wrote lease to %s", hcfile); } else if (errno == EROFS) { dhcpmsg(MSG_DEBUG, "%s is on a read-only file " "system; not saving lease", hcfile); } else { dhcpmsg(MSG_ERR, "cannot write %s (reboot will " "not use cached configuration)", hcfile); } }
/* * smalloc() -- safe malloc() * * Always returns a valid pointer(if it returns at all). The allocated * memory is initialized to all zeros. If malloc() returns an error, a * message is printed using the syslog() function and the program aborts * with a status of 1. * * Must be MT SAFE - called by threads other than the main thread. */ void * smalloc(uint_t nbytes) { char *retvalue; if ((retvalue = calloc(nbytes, sizeof (char))) == NULL) { dhcpmsg(LOG_ERR, "Cannot allocate memory (%s), exiting\n", strerror(errno)); exit(1); } return (retvalue); }
/* ARGSUSED */ static void dhcp_start(iu_tq_t *tqp, void *arg) { dhcp_smach_t *dsmp = arg; dsmp->dsm_start_timer = -1; (void) set_smach_state(dsmp, INIT); if (verify_smach(dsmp)) { dhcpmsg(MSG_VERBOSE, "starting DHCP on %s", dsmp->dsm_name); dhcp_selecting(dsmp); } }
void dhcp_init_reboot(dhcp_smach_t *dsmp) { dhcpmsg(MSG_VERBOSE, "%s has cached configuration - entering " "INIT_REBOOT", dsmp->dsm_name); if (!set_smach_state(dsmp, INIT_REBOOT)) { dhcpmsg(MSG_ERROR, "dhcp_init_reboot: cannot register to " "collect ACK/NAK packets, reverting to INIT on %s", dsmp->dsm_name); dsmp->dsm_dflags |= DHCP_IF_FAILED; (void) set_smach_state(dsmp, INIT); ipc_action_finish(dsmp, DHCP_IPC_E_MEMORY); return; } if (dsmp->dsm_isv6) dhcp_init_reboot_v6(dsmp); else dhcp_init_reboot_v4(dsmp); }
void ipc_action_finish(dhcp_smach_t *dsmp, int reason) { struct ipc_action *ia = &dsmp->dsm_ia; dsmp->dsm_dflags &= ~DHCP_IF_BUSY; if (dsmp->dsm_ia.ia_fd == -1) { dhcpmsg(MSG_ERROR, "ipc_action_finish: attempted to finish unknown action " "on %s", dsmp->dsm_name); return; } dhcpmsg(MSG_DEBUG, "ipc_action_finish: finished %s (command %d) on %s: %d", dhcp_ipc_type_to_string(ia->ia_cmd), (int)ia->ia_cmd, dsmp->dsm_name, reason); /* * if we can't cancel this timer, we're really in the * twilight zone. however, as long as we don't drop the * reference to the state machine, it shouldn't hurt us */ if (dsmp->dsm_ia.ia_tid != -1 && iu_cancel_timer(tq, dsmp->dsm_ia.ia_tid, NULL) == 1) { dsmp->dsm_ia.ia_tid = -1; release_smach(dsmp); } if (reason == 0) send_ok_reply(ia); else send_error_reply(ia, reason); async_finish(dsmp); }
/* * Create an unlock descriptor for container `cn' -- returns an unlock * descriptor on success, or NULL on failure; the reason for failure is in * `retvalp'. Since creating door descriptors is expensive, we keep a few * cache a small list of old descriptors around on a reclaim list and only * allocate a new one if the list is empty. */ static dsvcd_unlock_desc_t * ud_create(dsvcd_container_t *cn, int *retvalp) { dsvcd_unlock_desc_t *ud; *retvalp = DSVC_SUCCESS; (void) mutex_lock(&ud_reclaim_lock); if (ud_reclaim_list != NULL) { ud = ud_reclaim_list; ud_reclaim_list = ud->ud_next; ud_reclaim_count--; (void) mutex_unlock(&ud_reclaim_lock); } else { (void) mutex_unlock(&ud_reclaim_lock); ud = malloc(sizeof (dsvcd_unlock_desc_t)); if (ud == NULL) { dhcpmsg(MSG_WARNING, "cannot allocate unlock door " "descriptor; denying %s lock request", cn->cn_id); *retvalp = DSVC_NO_MEMORY; return (NULL); } (void) mutex_init(&ud->ud_lock, USYNC_THREAD, NULL); ud->ud_fd = door_create((void (*)())svc_unlock, ud, DOOR_UNREF_MULTI | DOOR_REFUSE_DESC | DOOR_NO_CANCEL); if (ud->ud_fd == -1) { dhcpmsg(MSG_WARNING, "cannot create unlock door; " "denying %s lock request", cn->cn_id); free(ud); *retvalp = DSVC_NO_RESOURCES; return (NULL); } } ud->ud_next = NULL; ud->ud_cn = cn; return (ud); }
static void request_failed(dhcp_smach_t *dsmp) { PKT_LIST *offer; dsmp->dsm_server = ipv6_all_dhcp_relay_and_servers; if ((offer = select_best(dsmp)) != NULL) { insque(offer, &dsmp->dsm_recv_pkt_list); dhcp_requesting(NULL, dsmp); } else { dhcpmsg(MSG_INFO, "no offers left on %s; restarting", dsmp->dsm_name); dhcp_selecting(dsmp); } }
static boolean_t stop_requesting(dhcp_smach_t *dsmp, unsigned int n_requests) { uint_t maxreq; maxreq = dsmp->dsm_isv6 ? DHCPV6_REQ_MAX_RC : DHCP_MAX_REQUESTS; if (n_requests >= maxreq) { dhcpmsg(MSG_INFO, "no ACK/NAK/Reply to REQUEST on %s", dsmp->dsm_name); request_failed(dsmp); return (B_TRUE); } else { return (B_FALSE); } }
/* ARGSUSED */ static void doorserv_create(door_info_t *infop) { void *stackbase; unsigned int stacksize = 32 * 1024; stackbase = stack_create(&stacksize); if (stackbase != NULL) { errno = thr_create(stackbase, stacksize, doorserv_thread, NULL, THR_BOUND | THR_DETACHED, NULL); if (errno != 0) { dhcpmsg(MSG_ERR, "cannot create door server thread; " "server thread pool will not be grown"); stack_destroy(stackbase, stacksize); } } }
/* ARGSUSED */ static void ipc_action_timeout(iu_tq_t *tq, void *arg) { dhcp_smach_t *dsmp = arg; struct ipc_action *ia = &dsmp->dsm_ia; dsmp->dsm_dflags &= ~DHCP_IF_BUSY; ia->ia_tid = -1; dhcpmsg(MSG_VERBOSE, "ipc timeout waiting for agent to complete " "%s (command %d) for %s", dhcp_ipc_type_to_string(ia->ia_cmd), ia->ia_cmd, dsmp->dsm_name); send_error_reply(ia, DHCP_IPC_E_TIMEOUT); async_finish(dsmp); release_smach(dsmp); }
/* * Reap containers that have not been recently used. */ static void * reaper(void *ds_table_raw) { dsvcd_datastore_t **ds_table; unsigned int i, nreaped; ds_table = (dsvcd_datastore_t **)ds_table_raw; for (;;) { (void) sleep(DSVCD_REAP_INTERVAL); for (i = 0; ds_table[i] != NULL; i++) { nreaped = ds_reap_containers(ds_table[i], DSVCD_REAP_THRESH); if (nreaped > 0) { dhcpmsg(MSG_VERBOSE, "reaped %u container " "synchpoints from %s", nreaped, ds_table[i]->ds_name); } } } /* NOTREACHED */ return (NULL); }
/* ARGSUSED */ static int dhcp_restart_lease(struct ifslist *ifsp, const char *msg) { dhcpmsg(MSG_INFO, "lease expired on %s -- restarting DHCP", ifsp->if_name); /* * in the case where the lease is less than DHCP_REBIND_MIN * seconds, we will never enter dhcp_renew() and thus the packet * counters will not be reset. in that case, reset them here. */ if (ifsp->if_state == BOUND) { ifsp->if_bad_offers = 0; ifsp->if_sent = 0; ifsp->if_received = 0; } (void) canonize_ifs(ifsp); /* reset_ifs() in dhcp_selecting() will clean up any leftover state */ dhcp_selecting(ifsp); return (1); }