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); } }
void send_v6_request(dhcp_smach_t *dsmp) { dhcp_pkt_t *dpkt; dhcpv6_ia_na_t d6in; dpkt = init_pkt(dsmp, DHCPV6_MSG_REQUEST); (void) add_pkt_opt(dpkt, DHCPV6_OPT_SERVERID, dsmp->dsm_serverid, dsmp->dsm_serveridlen); /* Add an IA_NA option for our controlling LIF */ d6in.d6in_iaid = htonl(dsmp->dsm_lif->lif_iaid); d6in.d6in_t1 = htonl(0); d6in.d6in_t2 = htonl(0); (void) add_pkt_opt(dpkt, DHCPV6_OPT_IA_NA, (dhcpv6_option_t *)&d6in + 1, sizeof (d6in) - sizeof (dhcpv6_option_t)); /* Add required Option Request option */ (void) add_pkt_prl(dpkt, dsmp); (void) send_pkt_v6(dsmp, dpkt, dsmp->dsm_server, stop_requesting, DHCPV6_REQ_TIMEOUT, DHCPV6_REQ_MAX_RT); /* For DHCPv6, state switch cannot fail */ (void) set_smach_state(dsmp, REQUESTING); }
/* 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); }
/* 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); }
static void accept_v6_message(dhcp_smach_t *dsmp, PKT_LIST *plp, const char *pname, uchar_t recv_type) { const dhcpv6_option_t *d6o; uint_t olen; const char *estr, *msg; uint_t msglen; int status; /* Account for received and processed messages */ dsmp->dsm_received++; /* We don't yet support Reconfigure at all. */ if (recv_type == DHCPV6_MSG_RECONFIGURE) { dhcpmsg(MSG_VERBOSE, "accept_v6_message: ignored Reconfigure " "on %s", dsmp->dsm_name); free_pkt_entry(plp); return; } /* * All valid DHCPv6 messages must have our Client ID specified. */ d6o = dhcpv6_pkt_option(plp, NULL, DHCPV6_OPT_CLIENTID, &olen); olen -= sizeof (*d6o); if (d6o == NULL || olen != dsmp->dsm_cidlen || memcmp(d6o + 1, dsmp->dsm_cid, olen) != 0) { dhcpmsg(MSG_VERBOSE, "accept_v6_message: discarded %s on %s: %s Client ID", pname, dsmp->dsm_name, d6o == NULL ? "no" : "wrong"); free_pkt_entry(plp); return; } /* * All valid DHCPv6 messages must have a Server ID specified. * * If this is a Reply and it's not in response to Solicit, Confirm, * Rebind, or Information-Request, then it must also match the Server * ID we're expecting. * * For Reply in the Solicit, Confirm, Rebind, and Information-Request * cases, the Server ID needs to be saved. This is done inside of * dhcp_bound(). */ d6o = dhcpv6_pkt_option(plp, NULL, DHCPV6_OPT_SERVERID, &olen); if (d6o == NULL) { dhcpmsg(MSG_DEBUG, "accept_v6_message: discarded %s on %s: no Server ID", pname, dsmp->dsm_name); free_pkt_entry(plp); return; } if (recv_type == DHCPV6_MSG_REPLY && dsmp->dsm_state != SELECTING && dsmp->dsm_state != INIT_REBOOT && dsmp->dsm_state != REBINDING && dsmp->dsm_state != INFORM_SENT) { olen -= sizeof (*d6o); if (olen != dsmp->dsm_serveridlen || memcmp(d6o + 1, dsmp->dsm_serverid, olen) != 0) { dhcpmsg(MSG_DEBUG, "accept_v6_message: discarded %s on " "%s: wrong Server ID", pname, dsmp->dsm_name); free_pkt_entry(plp); return; } } /* * Break out of the switch if the input message needs to be discarded. * Return from the function if the message has been enqueued or * consumed. */ switch (dsmp->dsm_state) { case SELECTING: /* A Reply message signifies a Rapid-Commit. */ if (recv_type == DHCPV6_MSG_REPLY) { if (dhcpv6_pkt_option(plp, NULL, DHCPV6_OPT_RAPID_COMMIT, &olen) == NULL) { dhcpmsg(MSG_DEBUG, "accept_v6_message: Reply " "on %s lacks Rapid-Commit; ignoring", dsmp->dsm_name); break; } dhcpmsg(MSG_VERBOSE, "accept_v6_message: rapid-commit Reply on %s", dsmp->dsm_name); cancel_offer_timer(dsmp); goto rapid_commit; } /* Otherwise, we're looking for Advertisements. */ if (recv_type != DHCPV6_MSG_ADVERTISE) break; /* * Special case: if this advertisement has preference 255, then * we must stop right now and select this server. */ d6o = dhcpv6_pkt_option(plp, NULL, DHCPV6_OPT_PREFERENCE, &olen); if (d6o != NULL && olen == sizeof (*d6o) + 1 && *(const uchar_t *)(d6o + 1) == 255) { pkt_smach_enqueue(dsmp, plp); dhcpmsg(MSG_DEBUG, "accept_v6_message: preference 255;" " immediate Request on %s", dsmp->dsm_name); dhcp_requesting(NULL, dsmp); } else { pkt_smach_enqueue(dsmp, plp); } return; case PRE_BOUND: case BOUND: /* * Not looking for anything in these states. (If we * implemented reconfigure, that might go here.) */ break; case REQUESTING: case INIT_REBOOT: case RENEWING: case REBINDING: case INFORM_SENT: /* * We're looking for Reply messages. */ if (recv_type != DHCPV6_MSG_REPLY) break; dhcpmsg(MSG_VERBOSE, "accept_v6_message: received Reply message on %s", dsmp->dsm_name); rapid_commit: /* * Extract the status code option. If one is present and the * request failed, then try to go to another advertisement in * the list or restart the selection machinery. */ d6o = dhcpv6_pkt_option(plp, NULL, DHCPV6_OPT_STATUS_CODE, &olen); status = dhcpv6_status_code(d6o, olen, &estr, &msg, &msglen); /* * Check for the UseMulticast status code. If this is present, * and if we were actually using unicast, then drop back and * try again. If we weren't using unicast, then just pretend * we never saw this message -- the peer is confused. (TAHI * does this.) */ if (status == DHCPV6_STAT_USEMCAST) { if (IN6_IS_ADDR_MULTICAST( &dsmp->dsm_send_dest.v6.sin6_addr)) { break; } else { free_pkt_entry(plp); dsmp->dsm_send_dest.v6.sin6_addr = ipv6_all_dhcp_relay_and_servers; retransmit_now(dsmp); return; } } print_server_msg(dsmp, msg, msglen); /* * We treat NoBinding at the top level as "success." Granted, * this doesn't make much sense, but the TAHI test suite does * this. NoBinding really only makes sense in the context of a * specific IA, as it refers to the GUID:IAID binding, so * ignoring it at the top level is safe. */ if (status == DHCPV6_STAT_SUCCESS || status == DHCPV6_STAT_NOBINDING) { if (dhcp_bound(dsmp, plp)) { /* * dhcp_bound will stop retransmission on * success, if that's called for. */ server_unicast_option(dsmp, plp); } else { stop_pkt_retransmission(dsmp); dhcpmsg(MSG_WARNING, "accept_v6_message: " "dhcp_bound failed for %s", dsmp->dsm_name); (void) remove_hostconf(dsmp->dsm_name, dsmp->dsm_isv6); dhcp_restart(dsmp); } } else { dhcpmsg(MSG_WARNING, "accept_v6_message: Reply: %s", estr); stop_pkt_retransmission(dsmp); free_pkt_entry(plp); if (dsmp->dsm_state == INFORM_SENT) { (void) set_smach_state(dsmp, INIT); ipc_action_finish(dsmp, DHCP_IPC_E_SRVFAILED); } else { (void) remove_hostconf(dsmp->dsm_name, dsmp->dsm_isv6); request_failed(dsmp); } } return; case DECLINING: /* * We're looking for Reply messages. */ if (recv_type != DHCPV6_MSG_REPLY) break; stop_pkt_retransmission(dsmp); /* * Extract the status code option. Note that it's not a * failure if the server reports an error. */ d6o = dhcpv6_pkt_option(plp, NULL, DHCPV6_OPT_STATUS_CODE, &olen); if (dhcpv6_status_code(d6o, olen, &estr, &msg, &msglen) == DHCPV6_STAT_SUCCESS) { print_server_msg(dsmp, msg, msglen); } else { dhcpmsg(MSG_WARNING, "accept_v6_message: Reply: %s", estr); } free_pkt_entry(plp); if (dsmp->dsm_leases == NULL) { dhcpmsg(MSG_VERBOSE, "accept_v6_message: %s has no " "leases left", dsmp->dsm_name); dhcp_restart(dsmp); } else if (dsmp->dsm_lif_wait == 0) { (void) set_smach_state(dsmp, BOUND); } else { (void) set_smach_state(dsmp, PRE_BOUND); } return; case RELEASING: /* * We're looking for Reply messages. */ if (recv_type != DHCPV6_MSG_REPLY) break; stop_pkt_retransmission(dsmp); /* * Extract the status code option. */ d6o = dhcpv6_pkt_option(plp, NULL, DHCPV6_OPT_STATUS_CODE, &olen); if (dhcpv6_status_code(d6o, olen, &estr, &msg, &msglen) == DHCPV6_STAT_SUCCESS) { print_server_msg(dsmp, msg, msglen); } else { dhcpmsg(MSG_WARNING, "accept_v6_message: Reply: %s", estr); } free_pkt_entry(plp); finished_smach(dsmp, DHCP_IPC_SUCCESS); return; } /* * Break from above switch means that the message must be discarded. */ dhcpmsg(MSG_VERBOSE, "accept_v6_message: discarded v6 %s on %s; state %s", pname, dsmp->dsm_name, dhcp_state_to_string(dsmp->dsm_state)); free_pkt_entry(plp); }
void dhcp_requesting(iu_tq_t *tqp, void *arg) { dhcp_smach_t *dsmp = arg; dhcp_pkt_t *dpkt; PKT_LIST *offer; lease_t lease; boolean_t isv6 = dsmp->dsm_isv6; /* * We assume here that if tqp is set, then this means we're being * called back by the offer wait timer. If so, then drop our hold * on the state machine. Otherwise, cancel the timer if it's running. */ if (tqp != NULL) { dhcpmsg(MSG_VERBOSE, "dhcp_requesting: offer wait timer on v%d %s", isv6 ? 6 : 4, dsmp->dsm_name); dsmp->dsm_offer_timer = -1; if (!verify_smach(dsmp)) return; } else { cancel_offer_timer(dsmp); } /* * select the best OFFER; all others pitched. */ offer = select_best(dsmp); if (offer == NULL) { dhcpmsg(MSG_VERBOSE, "no OFFERs/Advertisements on %s, waiting...", dsmp->dsm_name); /* * no acceptable OFFERs have come in. reschedule * ourself for callback. */ if ((dsmp->dsm_offer_timer = iu_schedule_timer(tq, dsmp->dsm_offer_wait, dhcp_requesting, dsmp)) == -1) { /* * ugh. the best we can do at this point is * revert back to INIT and wait for a user to * restart us. */ dhcpmsg(MSG_WARNING, "dhcp_requesting: cannot " "reschedule callback, reverting to INIT state on " "%s", dsmp->dsm_name); stop_pkt_retransmission(dsmp); (void) set_smach_state(dsmp, INIT); dsmp->dsm_dflags |= DHCP_IF_FAILED; ipc_action_finish(dsmp, DHCP_IPC_E_MEMORY); } else { hold_smach(dsmp); } return; } /* * With IPv4, the DHCPREQUEST packet we're about to transmit implicitly * declines all other offers we've received. We can no longer use any * cached offers, so we must discard them now. With DHCPv6, though, * we're permitted to hang onto the advertisements (offers) and try * them if the preferred one doesn't pan out. */ if (!isv6) free_pkt_list(&dsmp->dsm_recv_pkt_list); /* stop collecting packets. */ stop_pkt_retransmission(dsmp); /* * For IPv4, check to see whether we got an OFFER or a BOOTP packet. * If we got a BOOTP packet, go to the BOUND state now. */ if (!isv6 && offer->opts[CD_DHCP_TYPE] == NULL) { free_pkt_list(&dsmp->dsm_recv_pkt_list); if (!set_smach_state(dsmp, REQUESTING)) { dhcp_restart(dsmp); return; } if (!dhcp_bound(dsmp, offer)) { dhcpmsg(MSG_WARNING, "dhcp_requesting: dhcp_bound " "failed for %s", dsmp->dsm_name); dhcp_restart(dsmp); return; } return; } if (isv6) { const char *estr, *msg; const dhcpv6_option_t *d6o; uint_t olen, msglen; /* If there's a Status Code option, print the message */ d6o = dhcpv6_pkt_option(offer, NULL, DHCPV6_OPT_STATUS_CODE, &olen); (void) dhcpv6_status_code(d6o, olen, &estr, &msg, &msglen); print_server_msg(dsmp, msg, msglen); /* Copy in the Server ID (guaranteed to be present now) */ if (!save_server_id(dsmp, offer)) goto failure; /* * Determine how to send this message. If the Advertisement * (offer) has the unicast option, then use the address * specified in the option. Otherwise, send via multicast. */ server_unicast_option(dsmp, offer); send_v6_request(dsmp); } else { /* if we got a message from the server, display it. */ if (offer->opts[CD_MESSAGE] != NULL) { print_server_msg(dsmp, (char *)offer->opts[CD_MESSAGE]->value, offer->opts[CD_MESSAGE]->len); } /* * assemble a DHCPREQUEST, with the ciaddr field set to 0, * since we got here from the INIT state. */ dpkt = init_pkt(dsmp, REQUEST); /* * Grab the lease out of the OFFER; we know it's valid because * select_best() already checked. The max dhcp message size * option is set to the interface max, minus the size of the * udp and ip headers. */ (void) memcpy(&lease, offer->opts[CD_LEASE_TIME]->value, sizeof (lease_t)); (void) add_pkt_opt32(dpkt, CD_LEASE_TIME, lease); (void) add_pkt_opt16(dpkt, CD_MAX_DHCP_SIZE, htons(dsmp->dsm_lif->lif_max - sizeof (struct udpiphdr))); (void) add_pkt_opt32(dpkt, CD_REQUESTED_IP_ADDR, offer->pkt->yiaddr.s_addr); (void) add_pkt_opt(dpkt, CD_SERVER_ID, offer->opts[CD_SERVER_ID]->value, offer->opts[CD_SERVER_ID]->len); if (class_id_len != 0) { (void) add_pkt_opt(dpkt, CD_CLASS_ID, class_id, class_id_len); } (void) add_pkt_prl(dpkt, dsmp); /* * dsm_reqhost was set for this state machine in * dhcp_selecting() if the DF_REQUEST_HOSTNAME option set and a * host name was found */ if (dsmp->dsm_reqhost != NULL) { (void) add_pkt_opt(dpkt, CD_HOSTNAME, dsmp->dsm_reqhost, strlen(dsmp->dsm_reqhost)); } (void) add_pkt_opt(dpkt, CD_END, NULL, 0); /* * send out the REQUEST, trying retransmissions. either a NAK * or too many REQUEST attempts will revert us to SELECTING. */ if (!set_smach_state(dsmp, REQUESTING)) { dhcpmsg(MSG_ERROR, "dhcp_requesting: cannot switch to " "REQUESTING state; reverting to INIT on %s", dsmp->dsm_name); goto failure; } (void) send_pkt(dsmp, dpkt, htonl(INADDR_BROADCAST), stop_requesting); } /* all done with the offer */ free_pkt_entry(offer); return; failure: dsmp->dsm_dflags |= DHCP_IF_FAILED; (void) set_smach_state(dsmp, INIT); ipc_action_finish(dsmp, DHCP_IPC_E_MEMORY); free_pkt_list(&dsmp->dsm_recv_pkt_list); }
void dhcp_selecting(dhcp_smach_t *dsmp) { dhcp_pkt_t *dpkt; /* * We first set up to collect OFFER/Advertise packets as they arrive. * We then send out DISCOVER/Solicit probes. Then we wait a * user-tunable number of seconds before seeing if OFFERs/ * Advertisements have come in response to our DISCOVER/Solicit. If * none have come in, we continue to wait, sending out our DISCOVER/ * Solicit probes with exponential backoff. If no OFFER/Advertisement * is ever received, we will wait forever (note that since we're * event-driven though, we're still able to service other state * machines). * * Note that we do an reset_smach() here because we may be landing in * dhcp_selecting() as a result of restarting DHCP, so the state * machine may not be fresh. */ reset_smach(dsmp); if (!set_smach_state(dsmp, SELECTING)) { dhcpmsg(MSG_ERROR, "dhcp_selecting: cannot switch to SELECTING state; " "reverting to INIT on %s", dsmp->dsm_name); goto failed; } /* Remove the stale hostconf file, if there is any */ (void) remove_hostconf(dsmp->dsm_name, dsmp->dsm_isv6); dsmp->dsm_offer_timer = iu_schedule_timer(tq, dsmp->dsm_offer_wait, dhcp_requesting, dsmp); if (dsmp->dsm_offer_timer == -1) { dhcpmsg(MSG_ERROR, "dhcp_selecting: cannot schedule to read " "%s packets", dsmp->dsm_isv6 ? "Advertise" : "OFFER"); goto failed; } hold_smach(dsmp); /* * Assemble and send the DHCPDISCOVER or Solicit message. * * If this fails, we'll wait for the select timer to go off * before trying again. */ if (dsmp->dsm_isv6) { dhcpv6_ia_na_t d6in; if ((dpkt = init_pkt(dsmp, DHCPV6_MSG_SOLICIT)) == NULL) { dhcpmsg(MSG_ERROR, "dhcp_selecting: unable to set up " "Solicit packet"); return; } /* Add an IA_NA option for our controlling LIF */ d6in.d6in_iaid = htonl(dsmp->dsm_lif->lif_iaid); d6in.d6in_t1 = htonl(0); d6in.d6in_t2 = htonl(0); (void) add_pkt_opt(dpkt, DHCPV6_OPT_IA_NA, (dhcpv6_option_t *)&d6in + 1, sizeof (d6in) - sizeof (dhcpv6_option_t)); /* Option Request option for desired information */ (void) add_pkt_prl(dpkt, dsmp); /* Enable Rapid-Commit */ (void) add_pkt_opt(dpkt, DHCPV6_OPT_RAPID_COMMIT, NULL, 0); /* xxx add Reconfigure Accept */ (void) send_pkt_v6(dsmp, dpkt, ipv6_all_dhcp_relay_and_servers, stop_selecting, DHCPV6_SOL_TIMEOUT, DHCPV6_SOL_MAX_RT); } else { if ((dpkt = init_pkt(dsmp, DISCOVER)) == NULL) { dhcpmsg(MSG_ERROR, "dhcp_selecting: unable to set up " "DISCOVER packet"); return; } /* * The max DHCP message size option is set to the interface * MTU, minus the size of the UDP and IP headers. */ (void) add_pkt_opt16(dpkt, CD_MAX_DHCP_SIZE, htons(dsmp->dsm_lif->lif_max - sizeof (struct udpiphdr))); (void) add_pkt_opt32(dpkt, CD_LEASE_TIME, htonl(DHCP_PERM)); if (class_id_len != 0) { (void) add_pkt_opt(dpkt, CD_CLASS_ID, class_id, class_id_len); } (void) add_pkt_prl(dpkt, dsmp); if (!dhcp_add_fqdn_opt(dpkt, dsmp)) (void) dhcp_add_hostname_opt(dpkt, dsmp); (void) add_pkt_opt(dpkt, CD_END, NULL, 0); (void) send_pkt(dsmp, dpkt, htonl(INADDR_BROADCAST), stop_selecting); } return; failed: (void) set_smach_state(dsmp, INIT); dsmp->dsm_dflags |= DHCP_IF_FAILED; ipc_action_finish(dsmp, DHCP_IPC_E_MEMORY); }
boolean_t dhcp_extending(dhcp_smach_t *dsmp) { dhcp_pkt_t *dpkt; stop_pkt_retransmission(dsmp); /* * We change state here because this function is also called when * adopting a lease and on demand by the user. */ if (dsmp->dsm_state == BOUND) { dsmp->dsm_neg_hrtime = gethrtime(); dsmp->dsm_bad_offers = 0; dsmp->dsm_sent = 0; dsmp->dsm_received = 0; /* Bound->renew can't fail */ (void) set_smach_state(dsmp, RENEWING); } dhcpmsg(MSG_DEBUG, "dhcp_extending: sending request on %s", dsmp->dsm_name); if (dsmp->dsm_isv6) { dhcp_lease_t *dlp; dhcp_lif_t *lif; uint_t nlifs; uint_t irt, mrt; /* * Start constructing the Renew/Rebind message. Only Renew has * a server ID, as we still think our server might be * reachable. */ if (dsmp->dsm_state == RENEWING) { dpkt = init_pkt(dsmp, DHCPV6_MSG_RENEW); (void) add_pkt_opt(dpkt, DHCPV6_OPT_SERVERID, dsmp->dsm_serverid, dsmp->dsm_serveridlen); irt = DHCPV6_REN_TIMEOUT; mrt = DHCPV6_REN_MAX_RT; } else { dpkt = init_pkt(dsmp, DHCPV6_MSG_REBIND); irt = DHCPV6_REB_TIMEOUT; mrt = DHCPV6_REB_MAX_RT; } /* * Loop over the leases, and add an IA_NA for each and an * IAADDR for each address. */ for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next) { lif = dlp->dl_lifs; for (nlifs = dlp->dl_nlifs; nlifs > 0; nlifs--, lif = lif->lif_next) { (void) add_pkt_lif(dpkt, lif, DHCPV6_STAT_SUCCESS, NULL); } } /* Add required Option Request option */ (void) add_pkt_prl(dpkt, dsmp); return (send_pkt_v6(dsmp, dpkt, dsmp->dsm_server, stop_extending, irt, mrt)); } else { dhcp_lif_t *lif = dsmp->dsm_lif; ipaddr_t server; /* assemble the DHCPREQUEST message. */ dpkt = init_pkt(dsmp, REQUEST); dpkt->pkt->ciaddr.s_addr = lif->lif_addr; /* * The max dhcp message size option is set to the interface * max, minus the size of the udp and ip headers. */ (void) add_pkt_opt16(dpkt, CD_MAX_DHCP_SIZE, htons(lif->lif_max - sizeof (struct udpiphdr))); (void) add_pkt_opt32(dpkt, CD_LEASE_TIME, htonl(DHCP_PERM)); if (class_id_len != 0) { (void) add_pkt_opt(dpkt, CD_CLASS_ID, class_id, class_id_len); } (void) add_pkt_prl(dpkt, dsmp); /* * dsm_reqhost was set for this state machine in * dhcp_selecting() if the REQUEST_HOSTNAME option was set and * a host name was found. */ if (!dhcp_add_fqdn_opt(dpkt, dsmp) && dsmp->dsm_reqhost != NULL) { (void) add_pkt_opt(dpkt, CD_HOSTNAME, dsmp->dsm_reqhost, strlen(dsmp->dsm_reqhost)); } (void) add_pkt_opt(dpkt, CD_END, NULL, 0); IN6_V4MAPPED_TO_IPADDR(&dsmp->dsm_server, server); return (send_pkt(dsmp, dpkt, server, stop_extending)); } }
/* ARGSUSED */ void dhcp_rebind(iu_tq_t *tqp, void *arg) { dhcp_lease_t *dlp = arg; dhcp_smach_t *dsmp = dlp->dl_smach; int nlifs; dhcp_lif_t *lif; boolean_t some_valid; uint32_t expiremax; DHCPSTATE oldstate; dhcpmsg(MSG_VERBOSE, "dhcp_rebind: T2 timer expired on %s", dsmp->dsm_name); dlp->dl_t2.dt_id = -1; if ((oldstate = dsmp->dsm_state) == REBINDING) { dhcpmsg(MSG_DEBUG, "dhcp_renew: already rebinding"); release_lease(dlp); return; } /* * Sanity check: don't send packets if we've already expired on all of * the addresses. We compute the maximum expiration time here, because * it won't matter for v4 (there's only one lease) and for v6 we need * to know when the last lease ages away. */ some_valid = B_FALSE; expiremax = monosec(); lif = dlp->dl_lifs; for (nlifs = dlp->dl_nlifs; nlifs > 0; nlifs--, lif = lif->lif_next) { uint32_t expire; expire = dsmp->dsm_curstart_monosec + lif->lif_expire.dt_start; if (expire > expiremax) { expiremax = expire; some_valid = B_TRUE; } } if (!some_valid) { dhcpmsg(MSG_DEBUG, "dhcp_rebind: all leases expired on %s", dsmp->dsm_name); release_lease(dlp); return; } /* * This is our first venture into the REBINDING state, so reset the * server address. We know the renew timer has already been cancelled * (or we wouldn't be here). */ if (dsmp->dsm_isv6) { dsmp->dsm_server = ipv6_all_dhcp_relay_and_servers; } else { IN6_IPADDR_TO_V4MAPPED(htonl(INADDR_BROADCAST), &dsmp->dsm_server); } /* {Bound,Renew}->rebind transitions cannot fail */ (void) set_smach_state(dsmp, REBINDING); /* * If there isn't an async event pending, or if we can cancel the one * that's there, then try to rebind by sending an extension request. * If that fails, we'll clean up when the lease expires. */ if (!async_cancel(dsmp) || !async_start(dsmp, DHCP_EXTEND, B_FALSE) || !dhcp_extending(dsmp)) { if (monosec() + RETRY_DELAY < expiremax) { /* * Try again in RETRY_DELAY seconds; user command * should be gone. */ init_timer(&dlp->dl_t2, RETRY_DELAY); (void) set_smach_state(dsmp, oldstate); if (!schedule_lease_timer(dlp, &dlp->dl_t2, dhcp_rebind)) { dhcpmsg(MSG_INFO, "dhcp_rebind: unable to " "reschedule rebind around user command on " "%s; lease may expire", dsmp->dsm_name); } } else { dhcpmsg(MSG_WARNING, "dhcp_rebind: user busy on %s; " "will expire", dsmp->dsm_name); } } release_lease(dlp); }