FreeBSD/sys/netinet6/nd6.c
/*
 * Checks if we need to switch from STALE state.
 *
 * RFC 4861 requires switching from STALE to DELAY state
 * on first packet matching entry, waiting V_nd6_delay and
 * transition to PROBE state (if upper layer confirmation was
 * not received).
 *
 * This code performs a bit differently:
 * On packet hit we don't change state (but desired state
 * can be guessed by control plane). However, after V_nd6_delay
 * seconds code will transition to PROBE state (so DELAY state
 * is kinda skipped in most situations).
 *
 * Typically, V_nd6_gctimer is bigger than V_nd6_delay, so
 * we perform the following upon entering STALE state:
 *
 * 1) Arm timer to run each V_nd6_delay seconds to make sure that
 * if packet was transmitted at the start of given interval, we
 * would be able to switch to PROBE state in V_nd6_delay seconds
 * as user expects.
 *
 * 2) Reschedule timer until original V_nd6_gctimer expires keeping
 * lle in STALE state (remaining timer value stored in lle_remtime).
 *
 * 3) Reschedule timer if packet was transmitted less that V_nd6_delay
 * seconds ago.
 *
 * Returns non-zero value if the entry is still STALE (storing
 * the next timer interval in @pdelay).
 *
 * Returns zero value if original timer expired or we need to switch to
 * PROBE (store that in @do_switch variable).
 */
Checks if we need to switch from STALE state.
RFC 4861 requires switching from STALE to DELAY state
on first packet matching entry, waiting V_nd6_delay and
transition to PROBE state (if upper layer confirmation was
not received).

This code performs a bit differently:
On packet hit we don't change state (but desired state
can be guessed by control plane). However, after V_nd6_delay
seconds code will transition to PROBE state (so DELAY state
is kinda skipped in most situations).

Typically, V_nd6_gctimer is bigger than V_nd6_delay, so
we perform the following upon entering STALE state.

1) Arm timer to run each V_nd6_delay seconds to make sure that
if packet was transmitted at the start of given interval, we
would be able to switch to PROBE state in V_nd6_delay seconds
as user expects.

2) Reschedule timer until original V_nd6_gctimer expires keeping
lle in STALE state (remaining timer value stored in lle_remtime).

3) Reschedule timer if packet was transmitted less that V_nd6_delay
seconds ago.

Returns non-zero value if the entry is still STALE (storing
the next timer interval in @pdelay).

Returns zero value if original timer expired or we need to switch to
PROBE (store that in @do_switch variable).
Checks if we need to switch from STALE state . RFC 4861 requires switching from STALE to DELAY state on first packet matching entry waiting V_nd6_delay and transition to PROBE state ( if upper layer confirmation was not received ) . This code performs a bit differently : On packet hit we don ' t change state ( but desired state can be guessed by control plane ) . However after V_nd6_delay seconds code will transition to PROBE state ( so DELAY state is kinda skipped in most situations ) . Typically V_nd6_gctimer is bigger than V_nd6_delay so we perform the following upon entering STALE state . 1 ) Arm timer to run each V_nd6_delay seconds to make sure that if packet was transmitted at the start of given interval we would be able to switch to PROBE state in V_nd6_delay seconds as user expects . 2 ) Reschedule timer until original V_nd6_gctimer expires keeping lle in STALE state ( remaining timer value stored in lle_remtime ) . 3 ) Reschedule timer if packet was transmitted less that V_nd6_delay seconds ago . Returns non - zero value if the entry is still STALE ( storing the next timer interval in @pdelay ) . Returns zero value if original timer expired or we need to switch to PROBE ( store that in @do_switch variable ) .
static int
nd6_is_stale(struct llentry *lle, long *pdelay, int *do_switch)
{
	int nd_delay, nd_gctimer, r_skip_req;
	time_t lle_hittime;
	long delay;
	*do_switch = 0;
	nd_gctimer = V_nd6_gctimer;
	nd_delay = V_nd6_delay;
	LLE_REQ_LOCK(lle);
	r_skip_req = lle->r_skip_req;
	lle_hittime = lle->lle_hittime;
	LLE_REQ_UNLOCK(lle);
	if (r_skip_req > 0) {
		delay = (long)(MIN(nd_gctimer, nd_delay));
		delay *= hz;
		if (lle->lle_remtime > delay)
			lle->lle_remtime -= delay;
		else {
			delay = lle->lle_remtime;
			lle->lle_remtime = 0;
		}
		if (delay == 0) {
			return (0);
		}
		*pdelay = delay;
		return (1);
	}
	delay = (long)(time_uptime - lle_hittime);
	if (delay < nd_delay) {
		*pdelay = (long)(nd_delay - delay) * hz;
		return (1);
	}
	*do_switch = 1;
	return (0);
}
static int nd6_is_stale ( struct llentry * lle , long * pdelay , int * do_switch ) { int nd_delay , nd_gctimer , r_skip_req ; time_t lle_hittime ; long delay ; * do_switch = 0 ; nd_gctimer = V_nd6_gctimer ; nd_delay = V_nd6_delay ; LLE_REQ_LOCK ( lle ) ; r_skip_req = lle -> r_skip_req ; lle_hittime = lle -> lle_hittime ; LLE_REQ_UNLOCK ( lle ) ; if ( r_skip_req > 0 ) { delay = ( long ) ( MIN ( nd_gctimer , nd_delay ) ) ; delay *= hz ; if ( lle -> lle_remtime > delay ) lle -> lle_remtime -= delay ; else { delay = lle -> lle_remtime ; lle -> lle_remtime = 0 ; } if ( delay == 0 ) { return ( 0 ) ; } * pdelay = delay ; return ( 1 ) ; } delay = ( long ) ( time_uptime - lle_hittime ) ; if ( delay < nd_delay ) { * pdelay = ( long ) ( nd_delay - delay ) * hz ; return ( 1 ) ; } * do_switch = 1 ; return ( 0 ) ; }
Checks if we need to switch from STALE state.
Checks if we need to switch from STALE state .
/*
		 * Nonzero r_skip_req value was set upon entering
		 * STALE state. Since value was not changed, no
		 * packets were passed using this lle. Ask for
		 * timer reschedule and keep STALE state.
		 */ /*
			 * The original ng6_gctime timeout ended,
			 * no more rescheduling.
			 */ /*
	 * Packet received. Verify timestamp
	 */ /*
		 * V_nd6_delay still not passed since the first
		 * hit in STALE state.
		 * Reshedule timer and return.
		 */ /* Request switching to probe */