/* 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); }
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 kill_connection(struct argos_net_conn *conn) { if (conn->reconnect_evt_reg != NULL) { if (async_cancel(conn->reconnect_evt_reg) != 0) orion_log_crit_errno("async_cancel"); } if (conn->sock != 0) close_socket(conn->sock); conn->state = ARGOS_NET_CONN_DEAD; }
/* 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 */ void dhcp_expire(iu_tq_t *tqp, void *arg) { dhcp_lif_t *lif = arg; dhcp_smach_t *dsmp; const char *event; dhcpmsg(MSG_VERBOSE, "dhcp_expire: lease timer expired on %s", lif->lif_name); lif->lif_expire.dt_id = -1; if (lif->lif_lease == NULL) { release_lif(lif); return; } set_lif_deprecated(lif); dsmp = lif->lif_lease->dl_smach; if (!async_cancel(dsmp)) { dhcpmsg(MSG_WARNING, "dhcp_expire: cannot cancel current asynchronous command " "on %s", dsmp->dsm_name); /* * Try to schedule ourselves for callback. We're really * situation-critical here; there's not much hope for us if * this fails. */ init_timer(&lif->lif_expire, DHCP_EXPIRE_WAIT); if (schedule_lif_timer(lif, &lif->lif_expire, dhcp_expire)) return; dhcpmsg(MSG_CRIT, "dhcp_expire: cannot reschedule dhcp_expire " "to get called back, proceeding..."); } if (!async_start(dsmp, DHCP_START, B_FALSE)) dhcpmsg(MSG_WARNING, "dhcp_expire: cannot start asynchronous " "transaction on %s, continuing...", dsmp->dsm_name); /* * Determine if this state machine has any non-expired LIFs left in it. * If it doesn't, then this is an "expire" event. Otherwise, if some * valid leases remain, it's a "loss" event. The SOMEEXP case can * occur only with DHCPv6. */ if (expired_lif_state(dsmp) == DHCP_EXP_SOMEEXP) event = EVENT_LOSS6; else if (dsmp->dsm_isv6) event = EVENT_EXPIRE6; else event = EVENT_EXPIRE; /* * 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. */ (void) script_start(dsmp, event, dhcp_finish_expire, lif, NULL); }
/* 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); }
static int compress_and_xfer(struct argos_net_conn *conn, u_char force) { /* * if the packet-buf is empty, quit right off; this check is not necessary * for correctness, but its nice to check it early for efficiency and also * to avoid some spam in the logs (e.g. lots of 'compression timeout' * messages) since this case is quite common. */ if (buffer_len(conn->pktbuf) == 0) return 0; #if ARGOS_NET_USE_COMPRESSION == ARGOS_NET_COMPRESS_NONE size_t to_xfer = min(buffer_len(conn->pktbuf), buffer_remaining(conn->outbuf)); if (to_xfer == 0) return 0; #if ARGOS_NET_TRACE_IO struct timeval start; if (gettimeofday(&start, NULL) != 0) { orion_log_crit_errno("gettimeofday"); return 0; } #endif /* #if ARGOS_NET_TRACE_IO */ memcpy(buffer_tail(conn->outbuf), buffer_head(conn->pktbuf), to_xfer); #if ARGOS_NET_TRACE_IO struct timeval end; if (gettimeofday(&end, NULL) != 0) { orion_log_crit_errno("gettimeofday"); return 0; } struct timeval elapsed; orion_time_subtract(&end, &start, &elapsed); float elapsed_msec = elapsed.tv_sec*1000 + (float)elapsed.tv_usec/1000; orion_log_debug("memcpy'ed %u bytes in %.2f ms (%.2f MB/s)", to_xfer, elapsed_msec, ((to_xfer/elapsed_msec)*1000)/(1024*1024)); #endif /* #if ARGOS_NET_TRACE_IO */ if (buffer_expand(conn->outbuf, to_xfer) < 0) KABOOM("buffer_expand"); if (buffer_discard(conn->pktbuf, to_xfer) < 0) KABOOM("buffer_discard"); #else /* #if ARGOS_NET_USE_COMPRESSION == ARGOS_NET_COMPRESS_NONE */ /* * In general, we want large blocks of packets to compress (because that * makes the compression more space-efficient). So if there isn't much data * in pktbuf, just return without doing anything. Note that we also need to * check to make sure there is enough room in the outbuf for us to put the * packets once they are compressed. */ size_t minlen; if (force) { minlen = 1; } else { /* * If this conn has a small pktbuf (such that even with totally full, * the COMPRESS_HARD_MIN won't be met), then we need to adjust to a * limit that is actually attainable; we use 75% of the buffer size. */ minlen = (3*buffer_size(conn->pktbuf))/4; /* usually, this is the value that we end up with for minlen: */ minlen = min(minlen, COMPRESS_HARD_MIN); /* * one more special case: if argos_net_shutdown() was called on this * connection then there is no minimum compression size - we just want * to drain the buffers no matter how much is in there */ if (conn->shutdown) minlen = 1; } /* quit if we don't have at least 'minlen' bytes of packet data to compress */ if (buffer_len(conn->pktbuf) < minlen) return 0; /* check the total space available in the connection outbuf */ size_t total_space = buffer_remaining(conn->outbuf); if (total_space < sizeof(struct argos_net_compress_msg)) return 0; /* not enough space available */ /* this is the total space available for the compressed data */ size_t space = total_space - sizeof(struct argos_net_compress_msg); /* don't exceed the maximum compression-block size */ space = min(space, ARGOS_NET_MAX_COMPRESS_LEN); /* * given the space available, calculate how much packet data we can safely * consume (considering worst-cast input:output size ratios for whatever * compression algorithm we are using). */ ssize_t ok_to_consume; #if ARGOS_NET_USE_COMPRESSION == ARGOS_NET_COMPRESS_LZO /* this is the inversion of the function given in the LZO faq file */ ok_to_consume = (16*(space - 64 - 3))/17; #elif ARGOS_NET_USE_COMPRESSION == ARGOS_NET_COMPRESS_QUICKLZ /* this is the inversion of the function given in the QuickLZ manual */ ok_to_consume = space - 400; #else #error "unknown value for ARGOS_NET_USE_COMPRESSION" #endif if (ok_to_consume <= 0) return 0; /* not enough space available */ /* number of bytes that will actually be compressed and transferred */ size_t readlen = min(ok_to_consume, buffer_len(conn->pktbuf)); assert(readlen > 0); /* don't exceed the maximum compression-block size */ readlen = min(readlen, COMPRESS_HARD_MAX); /* where the compressed data should be written */ u_char *write_ptr = buffer_tail(conn->outbuf) + sizeof(struct argos_net_compress_msg); struct argos_net_compress_msg *msg = (struct argos_net_compress_msg*)buffer_tail(conn->outbuf); msg->msgtype = htons(ARGOS_NET_COMPRESS_MSGTYPE); /* have to defer filling in msglen field */ msg->algorithm = ARGOS_NET_USE_COMPRESSION; msg->orig_len = htonl(readlen); #if ARGOS_NET_TRACE_IO /* measure the elapsed process time to try to detect cpu starvation */ struct itimerval itimer_start; bzero(&itimer_start, sizeof(itimer_start)); itimer_start.it_value.tv_sec = 100; /* arbitrary large value */ struct timeval start; if (gettimeofday(&start, NULL) != 0) { orion_log_crit_errno("gettimeofday"); return 0; } if (setitimer(ITIMER_PROF, &itimer_start, NULL) != 0) { orion_log_crit_errno("setitimer"); return 0; } #endif /* #if ARGOS_NET_TRACE_IO */ #if ARGOS_NET_USE_COMPRESSION == ARGOS_NET_COMPRESS_LZO lzo_uint lzo_outlen; int rv = lzo1x_1_compress(buffer_head(conn->pktbuf), readlen, write_ptr, &lzo_outlen, conn->lzo_wrk_space); if (rv != LZO_E_OK) { /* according to LZO documentation "this should NEVER happen" */ orion_log_crit("LZO compression library internal error: %d", rv); return 0; } uint32_t outlen = lzo_outlen; #elif ARGOS_NET_USE_COMPRESSION == ARGOS_NET_COMPRESS_QUICKLZ uint32_t outlen = qlz_compress(buffer_head(conn->pktbuf), (char*)write_ptr, readlen, conn->qlz_scratch); #else #error "unknown value for ARGOS_NET_USE_COMPRESSION" #endif #if ARGOS_NET_TRACE_IO /* call this before gettimeofday */ struct itimerval itimer_end; if (getitimer(ITIMER_PROF, &itimer_end) != 0) { orion_log_crit_errno("getitimer"); return 0; } #endif /* #if ARGOS_NET_TRACE_IO */ struct timeval end; if (gettimeofday(&end, NULL) != 0) { orion_log_crit_errno("gettimeofday"); return 0; } #if ARGOS_NET_TRACE_IO struct timeval real_elapsed; orion_time_subtract(&end, &start, &real_elapsed); float real_msec = real_elapsed.tv_sec*1000 + (float)real_elapsed.tv_usec/1000; struct timeval process_elapsed; orion_time_subtract(&itimer_start.it_value, &itimer_end.it_value, &process_elapsed); float process_msec = process_elapsed.tv_sec*1000 + (float)process_elapsed.tv_usec/1000; orion_log_debug("compressed %u bytes to %u (%.2f%%) in %.2f ms" " (%.2f MB/s); %.2f ms process time", readlen, outlen, (float)outlen*100/readlen, real_msec, ((readlen/real_msec)*1000)/(1024*1024), process_msec); #endif /* #if ARGOS_NET_TRACE_IO */ size_t total_len = sizeof(struct argos_net_compress_msg) + outlen; if (buffer_expand(conn->outbuf, total_len) < 0) KABOOM("buffer_expand"); if (buffer_discard(conn->pktbuf, readlen) < 0) KABOOM("buffer_discard"); /* write back into the msglen field now that we know the total length */ msg->msglen = htonl(sizeof(struct argos_net_compress_msg) + outlen); /* check for incompressible block (this is normal for small blocks) */ if (outlen > readlen) { if (readlen < 4096) orion_log_debug("incompressible block: inlen=%d, outlen=%d", readlen, outlen); else orion_log_warn("incompressible block: inlen=%d, outlen=%d", readlen, outlen); } #endif /* #if ARGOS_NET_USE_COMPRESSION == ARGOS_NET_COMPRESS_NONE */ /* cancel any currently schedule compression timeout event */ if (conn->compress_evt_reg != NULL) { if (async_cancel(conn->compress_evt_reg) != 0) orion_log_crit_errno("async_cancel"); conn->compress_evt_reg = NULL; } return 1; }