Example #1
0
/* 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);
}
Example #2
0
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);
}
Example #3
0
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;
}
Example #4
0
/* 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);
}
Example #5
0
/* 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);
}
Example #6
0
/* 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);
}
Example #7
0
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;
}