/* 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 uint32_t next_extend_time(monosec_t limit_monosec) { monosec_t current_monosec = monosec(); if (limit_monosec - current_monosec < DHCP_REBIND_MIN) return (0); return ((limit_monosec - current_monosec) / 2); }
/* 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); }
/* ARGSUSED */ void dhcp_renew(iu_tq_t *tqp, void *arg) { struct ifslist *ifsp = (struct ifslist *)arg; uint32_t next; ifsp->if_timer[DHCP_T1_TIMER] = -1; if (check_ifs(ifsp) == 0) { (void) release_ifs(ifsp); return; } /* * sanity check: don't send packets if we're past t2. */ if (monosec() > (ifsp->if_curstart_monosec + ifsp->if_t2)) return; next = next_extend_time(ifsp->if_curstart_monosec + ifsp->if_t2); /* * if there isn't an async event pending, then try to renew. */ if (!async_pending(ifsp)) if (async_start(ifsp, DHCP_EXTEND, B_FALSE) != 0) /* * try to send extend. if we don't succeed, * async_timeout() will clean us up. */ (void) dhcp_extending(ifsp); /* * if we're within DHCP_REBIND_MIN seconds of REBINDING, don't * reschedule ourselves. */ if (next == 0) return; /* * no big deal if we can't reschedule; we still have the REBIND * state to save us. */ (void) schedule_ifs_timer(ifsp, DHCP_T1_TIMER, next, dhcp_renew); }
/* 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); }
time_t monosec_to_time(monosec_t abs_monosec) { return (abs_monosec - monosec()) + time(NULL); }
int dhcp_extending(struct ifslist *ifsp) { dhcp_pkt_t *dpkt; if (ifsp->if_state == BOUND) { ifsp->if_neg_monosec = monosec(); ifsp->if_state = RENEWING; ifsp->if_bad_offers = 0; ifsp->if_sent = 0; ifsp->if_received = 0; } dhcpmsg(MSG_DEBUG, "dhcp_extending: registering dhcp_acknak on %s", ifsp->if_name); if (register_acknak(ifsp) == 0) { ipc_action_finish(ifsp, DHCP_IPC_E_MEMORY); async_finish(ifsp); dhcpmsg(MSG_WARNING, "dhcp_extending: cannot register " "dhcp_acknak for %s, not sending renew request", ifsp->if_name); return (0); } /* * 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(ifsp, REQUEST); dpkt->pkt->ciaddr.s_addr = ifsp->if_addr.s_addr; add_pkt_opt16(dpkt, CD_MAX_DHCP_SIZE, htons(ifsp->if_max - sizeof (struct udpiphdr))); add_pkt_opt32(dpkt, CD_LEASE_TIME, htonl(DHCP_PERM)); add_pkt_opt(dpkt, CD_CLASS_ID, class_id, class_id_len); add_pkt_opt(dpkt, CD_REQUEST_LIST, ifsp->if_prl, ifsp->if_prllen); /* * if_reqhost was set for this interface in dhcp_selecting() * if the REQUEST_HOSTNAME option was set and a host name was * found. */ if (ifsp->if_reqhost != NULL) { add_pkt_opt(dpkt, CD_HOSTNAME, ifsp->if_reqhost, strlen(ifsp->if_reqhost)); } add_pkt_opt(dpkt, CD_END, NULL, 0); /* * if we can't send the packet, leave the event handler registered * anyway, since we're not expecting to get any other types of * packets in other than ACKs/NAKs anyway. */ return (send_pkt(ifsp, dpkt, ifsp->if_server.s_addr, NULL)); }
/* ARGSUSED */ void dhcp_rebind(iu_tq_t *tqp, void *arg) { struct ifslist *ifsp = (struct ifslist *)arg; uint32_t next; ifsp->if_timer[DHCP_T2_TIMER] = -1; if (check_ifs(ifsp) == 0) { (void) release_ifs(ifsp); return; } /* * sanity check: don't send packets if we've already expired. */ if (monosec() > (ifsp->if_curstart_monosec + ifsp->if_lease)) return; next = next_extend_time(ifsp->if_curstart_monosec + ifsp->if_lease); /* * if this is our first venture into the REBINDING state, then * reset the server address. we know the renew timer has * already been cancelled (or we wouldn't be here). */ if (ifsp->if_state == RENEWING) { ifsp->if_state = REBINDING; ifsp->if_server.s_addr = htonl(INADDR_BROADCAST); } /* * if there isn't an async event pending, then try to rebind. */ if (!async_pending(ifsp)) if (async_start(ifsp, DHCP_EXTEND, B_FALSE) != 0) /* * try to send extend. if we don't succeed, * async_timeout() will clean us up. */ (void) dhcp_extending(ifsp); /* * if we're within DHCP_REBIND_MIN seconds of EXPIRE, don't * reschedule ourselves. */ if (next == 0) { dhcpmsg(MSG_WARNING, "dhcp_rebind: lease on %s expires in less " "than %i seconds!", ifsp->if_name, DHCP_REBIND_MIN); return; } if (schedule_ifs_timer(ifsp, DHCP_T2_TIMER, next, dhcp_rebind) == 0) /* * we'll just end up in dhcp_expire(), but it sure sucks. */ dhcpmsg(MSG_CRIT, "dhcp_rebind: cannot reschedule another " "rebind attempt; lease may expire for %s", ifsp->if_name); }