+/* --- @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; }
+