kx->f |= KXF_TIMER;
}
+/* --- @f2tv@ --- *
+ *
+ * Arguments: @struct timeval *tv@ = where to write the timeval
+ * @double t@ = a time as a floating point number
+ *
+ * Returns: ---
+ *
+ * Use: Converts a floating-point time into a timeval.
+ */
+
+static void f2tv(struct timeval *tv, double t)
+{
+ tv->tv_sec = t;
+ tv->tv_usec = (t - tv->tv_sec)*MILLION;
+}
+
+/* --- @wobble@ --- *
+ *
+ * Arguments: @double t@ = a time interval
+ *
+ * Returns: The same time interval, with a random error applied.
+ */
+
+static double wobble(double t)
+{
+ uint32 r = rand_global.ops->word(&rand_global);
+ double w = (r/F_2P32) - 0.5;
+ return (t + t*w*T_WOBBLE);
+}
+
+/* --- @rs_time@ --- *
+ *
+ * Arguments: @retry *rs@ = current retry state
+ * @struct timeval *tv@ = where to write the result
+ * @const struct timeval *now@ = current time, or null
+ *
+ * Returns: ---
+ *
+ * Use: Computes a time at which to retry sending a key-exchange
+ * packet. This algorithm is subject to change, but it's
+ * currently a capped exponential backoff, slightly randomized
+ * to try to keep clients from hammering a server that's only
+ * just woken up.
+ *
+ * If @now@ is null then the function works out the time for
+ * itself.
+ */
+
+static void rs_time(retry *rs, struct timeval *tv, const struct timeval *now)
+{
+ double t;
+ struct timeval rtv;
+
+ if (!rs->t)
+ t = SEC(2);
+ else {
+ t = (rs->t * 5)/4;
+ if (t > MIN(5)) t = MIN(5);
+ }
+ rs->t = t;
+
+ if (!now) {
+ now = tv;
+ gettimeofday(tv, 0);
+ }
+ f2tv(&rtv, wobble(t));
+ TV_ADD(tv, now, &rtv);
+}
+
+/* --- @retry_reset@ --- *
+ *
+ * Arguments: @retry *rs@ = retry state
+ *
+ * Returns: --
+ *
+ * Use: Resets a retry state to indicate that progress has been
+ * made. Also useful for initializing the state in the first
+ * place.
+ */
+
+static void rs_reset(retry *rs) { rs->t = 0; }
+
/*----- Challenge management ----------------------------------------------*/
/* --- Notes on challenge management --- *
kxc->kx = kx;
kxc->f = 0;
kx->r[i] = kxc;
+ rs_reset(&kxc->rs);
return (kxc);
}
if (kxc->f & KXF_TIMER)
sel_rmtimer(&kxc->t);
gettimeofday(&tv, 0);
- tv.tv_sec += T_RETRY;
+ rs_time(&kxc->rs, &tv, &tv);
sel_addtimer(&sel, &kxc->t, &tv, kxc_timer, kxc);
kxc->f |= KXF_TIMER;
}
}
if (kx->s < KXS_SWITCH) {
- gettimeofday(&tv, 0);
- tv.tv_sec += T_RETRY;
+ rs_time(&kx->rs, &tv, 0);
settimer(kx, &tv);
}
}
static void kxfinish(keyexch *kx)
{
kxchal *kxc = kx->r[0];
- struct timeval tv;
+ struct timeval now, tv;
ks_activate(kxc->ks);
- gettimeofday(&tv, 0);
- tv.tv_sec += T_REGEN;
+ gettimeofday(&now, 0);
+ f2tv(&tv, wobble(T_REGEN));
+ TV_ADD(&tv, &now, &tv);
settimer(kx, &tv);
kx->s = KXS_SWITCH;
a_notify("KXDONE", "?PEER", kx->p, A_END);
int rc;
gettimeofday(&now, 0);
+ rs_reset(&kx->rs);
if (kx->f & KXF_CORK) {
start(kx, now.tv_sec);
- TV_ADDL(&tv, &now, T_RETRY, 0);
+ rs_time(&kx->rs, &tv, &now);
settimer(kx, &tv);
a_notify("KXSTART", A_END);
}
return (-1);
}
kx->f = KXF_DEAD | KXF_PUBKEY | f;
+ rs_reset(&kx->rs);
if (!(kx->f & KXF_CORK)) {
start(kx, time(0));
resend(kx);