aboutsummaryrefslogtreecommitdiff
path: root/src/events/kickoff_time_sync.c
blob: 33c15ed5211267fb341978fcf704a74286b49a1c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
/*
 * kickoff_time_sync.c - network time synchronization
 * Copyright (c) 2013 The Chromium Authors. All rights reserved.
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "config.h"

#ifdef USE_POLARSSL
#include <polarssl/entropy.h>
#include <polarssl/ctr_drbg.h>
#else
#include <openssl/rand.h>
#endif
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <event2/event.h>

#include "src/conf.h"
#include "src/util.h"
#include "src/tlsdate.h"

#ifdef USE_POLARSSL
static int random_init = 0;
static entropy_context entropy;
static ctr_drbg_context ctr_drbg;
static char *pers = "tlsdated";
#endif

int
add_jitter (int base, int jitter)
{
  int n = 0;
  if (!jitter)
    return base;
#ifdef USE_POLARSSL
  if (0 == random_init)
  {
    entropy_init(&entropy);
    if (0 > ctr_drbg_init(&ctr_drbg, entropy_func, &entropy,
                          (unsigned char *) pers, strlen(pers)))
    {
      pfatal ("Failed to initialize random source");
    }
    random_init = 1;
  }
  if (0 != ctr_drbg_random(&ctr_drbg, (unsigned char *)&n, sizeof(n)))
    fatal ("ctr_drbg_random() failed");
#else
  if (RAND_bytes ( (unsigned char *) &n, sizeof (n)) != 1)
    fatal ("RAND_bytes() failed");
#endif
  return base + (abs (n) % (2 * jitter)) - jitter;
}

void
invalidate_time (struct state *state)
{
  state->last_sync_type = SYNC_TYPE_RTC;
  state->last_time = time (NULL);
  /* Note(!) this does not invalidate the clock_delta implicitly.
   * This allows forced invalidation to not lose synchronization
   * data.
   */
}

void
action_invalidate_time (evutil_socket_t fd, short what, void *arg)
{
  struct state *state = arg;
  debug ("[event:%s] fired", __func__);
  /* If time is already invalid and being acquired, do nothing. */
  if (state->last_sync_type == SYNC_TYPE_RTC &&
      event_pending (state->events[E_TLSDATE], EV_TIMEOUT, NULL))
    return;
  /* Time out our trust in network synchronization but don't persist
   * the change to disk or notify the system.  Let a network sync
   * failure or success do that.
   */
  invalidate_time (state);
  /* Then trigger a network sync if possible. */
  action_kickoff_time_sync (-1, EV_TIMEOUT, arg);
}

int
setup_event_timer_sync (struct state *state)
{
  int wait_time = add_jitter (state->opts.steady_state_interval,
                              state->opts.jitter);
  struct timeval interval = { wait_time, 0 };
  state->events[E_STEADYSTATE] = event_new (state->base, -1,
                                 EV_TIMEOUT|EV_PERSIST,
                                 action_invalidate_time, state);
  if (!state->events[E_STEADYSTATE])
    {
      error ("Failed to create interval event");
      return 1;
    }
  event_priority_set (state->events[E_STEADYSTATE], PRI_ANY);
  return event_add (state->events[E_STEADYSTATE], &interval);
}

/* Begins a network synchronization attempt.  If the local clocks
 * are synchronized, then make sure that the _current_ synchronization
 * source is set to the real-time clock and note that the clock_delta
 * is unreliable.  If the clock was in sync and the last synchronization
 * source was the network, then this action does nothing.
 *
 * In the case of desynchronization, the clock_delta value is used as a
 * guard to indicate that even if the synchronization source isn't the
 * network, the source is still tracking the clock delta that was
 * established from a network source.
 * TODO(wad) Change the name of clock_delta to indicate that it is the local
 *           clock delta after the last network sync.
 */
void action_kickoff_time_sync (evutil_socket_t fd, short what, void *arg)
{
  struct state *state = arg;
  debug ("[event:%s] fired", __func__);
  time_t delta = state->clock_delta;
  int jitter = 0;
  if (check_continuity (&delta) > 0)
    {
      info ("[event:%s] clock delta desync detected (%d != %d)", __func__,
            state->clock_delta, delta);
      /* Add jitter iff we had network synchronization once before. */
      if (state->clock_delta)
        jitter = add_jitter (30, 30); /* TODO(wad) make configurable */
      /* Forget the old delta until we have time again. */
      state->clock_delta = 0;
      invalidate_time (state);
    }
  if (state->last_sync_type == SYNC_TYPE_NET)
    {
      debug ("[event:%s] time in sync. skipping", __func__);
      return;
    }
  /* Keep parity with run_tlsdate: for every wake, allow it to retry again. */
  if (state->tries > 0)
    {
      state->tries -= 1;
      /* Don't bother re-triggering tlsdate */
      debug ("[event:%s] called while tries are in progress", __func__);
      return;
    }
  /* Don't over-schedule if the first attempt hasn't fired. If a wake event
   * impacts the result of a proxy resolution, then the updated value can be
   * acquired on the next run. If the wake comes in after E_TLSDATE is
   * serviced, then the tries count will be decremented.
   */
  if (event_pending (state->events[E_TLSDATE], EV_TIMEOUT, NULL))
    {
      debug ("[event:%s] called while tlsdate is pending", __func__);
      return;
    }
  if (!state->events[E_RESOLVER])
    {
      trigger_event (state, E_TLSDATE, jitter);
      return;
    }
  /* If the resolver relies on an external response, then make sure that a
   * tlsdate event is waiting in the wings if the resolver is too slow.  Even
   * if this fires, it won't stop eventual handling of the resolver since it
   * doesn't event_del() E_RESOLVER.
   */
  trigger_event (state, E_TLSDATE, jitter + RESOLVER_TIMEOUT);
  trigger_event (state, E_RESOLVER, jitter);
}