Commit b0e92634 authored by Mike Perry's avatar Mike Perry Committed by Nick Mathewson
Browse files

Netflow record collapsing defense.

This defense will cause Cisco, Juniper, Fortinet, and other routers operating
in the default configuration to collapse netflow records that would normally
be split due to the 15 second flow idle timeout.

Collapsing these records should greatly reduce the utility of default netflow
data for correlation attacks, since all client-side records should become 30
minute chunks of total bytes sent/received, rather than creating multiple
separate records for every webpage load/ssh command interaction/XMPP chat/whatever
else happens to be inactive for more than 15 seconds.

The defense adds consensus parameters to govern the range of timeout values
for sending padding packets, as well as for keeping connections open.

The defense only sends padding when connections are otherwise inactive, and it
does not pad connections used solely for directory traffic at all. By default
it also doesn't pad inter-relay connections.

Statistics on the total padding in the last 24 hours are export...
parent 515e1f66
o Major features (traffic analysis)
- Relays and clients will now send a padding cell on idle OR
connections every 1.5 to 9.5 seconds (tunable via consensus
parameters). Directory connections and inter-relay connections
are not padded. Padding is negotiated using Tor's link protocol,
so both relays and clients must upgrade for this to take effect.
Clients may still send padding despite the relay's version by
setting ConnectionPadding 1 in torrc, and may disable padding
by setting ConnectionPadding 0 in torrc. Padding may be minimized
for mobile users with the torrc option ReducedConnectionPadding.
Implements Proposal 251 and Section 2 of Proposal 254; closes ticket
#16861.
- Relays will publish 24 hour totals of padding and non-padding cell
counts to their extra-info descriptors, unless PaddingStatistics 0
is set in torrc. These 24 hour totals are also rounded to multiples
of 10000.
......@@ -832,6 +832,22 @@ The following options are useful only for clients (that is, if
and fast enough. The current behavior is simply that Tor is a client
unless ORPort, ExtORPort, or DirPort are configured.) (Default: 0)
[[ConnectionPadding]] **ConnectionPadding** **0**|**1**|**auto**::
This option governs Tor's use of padding to defend against some forms of
traffic analysis. If it is set to 'auto', Tor will send padding only
if both the client and the relay support it. If it is set to 0, Tor will
not send any padding cells. If it is set to 1, Tor will still send padding
for client connections regardless of relay support. Only clients may set
this option. This option should be offered via the UI to mobile users
for use where bandwidth may be expensive.
(Default: auto)
[[ReducedConnectionPadding]] **ReducedConnectionPadding** **0**|**1**::
If set to 1, Tor will not not hold OR connections open for very long,
and will send less padding on these connections. Only clients may set
this option. This option should be offered via the UI to mobile users
for use where bandwidth may be expensive. (Default: 0)
[[ExcludeNodes]] **ExcludeNodes** __node__,__node__,__...__::
A list of identity fingerprints, country codes, and address
patterns of nodes to avoid when building a circuit. Country codes are
......@@ -2031,6 +2047,14 @@ is non-zero):
If ExtraInfoStatistics is enabled, it will published as part of
extra-info document. (Default: 0)
[[PaddingStatistics]] **PaddingStatistics** **0**|**1**::
Relays only.
When this option is enabled, Tor collects statistics for padding cells
sent and received by this relay, in addition to total cell counts.
These statistics are rounded, and omitted if traffic is low. This
information is important for load balancing decisions related to padding.
(Default: 1)
[[DirReqStatistics]] **DirReqStatistics** **0**|**1**::
Relays and bridges only.
When this option is enabled, a Tor directory writes statistics on the
......
......@@ -14,6 +14,7 @@ LIBTOR_OBJECTS = \
addressmap.obj \
buffers.obj \
channel.obj \
channelpadding.obj \
channeltls.obj \
circpathbias.obj \
circuitbuild.obj \
......
......@@ -49,6 +49,7 @@
#include "or.h"
#include "channel.h"
#include "channeltls.h"
#include "channelpadding.h"
#include "circuitbuild.h"
#include "circuitlist.h"
#include "circuitstats.h"
......@@ -63,6 +64,7 @@
#include "router.h"
#include "routerlist.h"
#include "scheduler.h"
#include "compat_time.h"
/* Global lists of channels */
......@@ -105,6 +107,8 @@ HT_GENERATE2(channel_gid_map, channel_s, gidmap_node,
channel_id_hash, channel_id_eq,
0.6, tor_reallocarray_, tor_free_);
HANDLE_IMPL(channel, channel_s,);
/* Counter for ID numbers */
static uint64_t n_channels_allocated = 0;
/*
......@@ -922,6 +926,11 @@ channel_free(channel_t *chan)
circuitmux_set_policy(chan->cmux, NULL);
}
/* Remove all timers and associated handle entries now */
timer_free(chan->padding_timer);
channel_handle_free(chan->timer_handle);
channel_handles_clear(chan);
/* Call a free method if there is one */
if (chan->free_fn) chan->free_fn(chan);
......@@ -1000,6 +1009,11 @@ channel_force_free(channel_t *chan)
circuitmux_set_policy(chan->cmux, NULL);
}
/* Remove all timers and associated handle entries now */
timer_free(chan->padding_timer);
channel_handle_free(chan->timer_handle);
channel_handles_clear(chan);
/* Call a free method if there is one */
if (chan->free_fn) chan->free_fn(chan);
......@@ -2619,6 +2633,19 @@ channel_do_open_actions(channel_t *chan)
}
}
/* Disable or reduce padding according to user prefs. */
if (chan->padding_enabled || get_options()->ConnectionPadding == 1) {
if (!get_options()->ConnectionPadding) {
channelpadding_disable_padding_on_channel(chan);
}
/* Padding can be forced and/or reduced by clients, regardless of if
* the channel supports it */
if (get_options()->ReducedConnectionPadding) {
channelpadding_reduce_padding_on_channel(chan);
}
}
circuit_n_chan_done(chan, 1, close_origin_circuits);
}
......@@ -4215,8 +4242,12 @@ channel_timestamp_active(channel_t *chan)
time_t now = time(NULL);
tor_assert(chan);
chan->timestamp_xfer_ms = monotime_coarse_absolute_msec();
chan->timestamp_active = now;
/* Clear any potential netflow padding timer. We're active */
chan->next_padding_time_ms = 0;
}
/**
......@@ -4299,11 +4330,14 @@ void
channel_timestamp_recv(channel_t *chan)
{
time_t now = time(NULL);
tor_assert(chan);
chan->timestamp_xfer_ms = monotime_coarse_absolute_msec();
chan->timestamp_active = now;
chan->timestamp_recv = now;
/* Clear any potential netflow padding timer. We're active */
chan->next_padding_time_ms = 0;
}
/**
......@@ -4316,11 +4350,15 @@ void
channel_timestamp_xmit(channel_t *chan)
{
time_t now = time(NULL);
tor_assert(chan);
chan->timestamp_xfer_ms = monotime_coarse_absolute_msec();
chan->timestamp_active = now;
chan->timestamp_xmit = now;
/* Clear any potential netflow padding timer. We're active */
chan->next_padding_time_ms = 0;
}
/***************************************************************
......
......@@ -11,6 +11,8 @@
#include "or.h"
#include "circuitmux.h"
#include "timers.h"
#include "handles.h"
/* Channel handler function pointer typedefs */
typedef void (*channel_listener_fn_ptr)(channel_listener_t *, channel_t *);
......@@ -21,6 +23,17 @@ struct cell_queue_entry_s;
TOR_SIMPLEQ_HEAD(chan_cell_queue, cell_queue_entry_s);
typedef struct chan_cell_queue chan_cell_queue_t;
/**
* This enum is used by channelpadding to decide when to pad channels.
* Don't add values to it without updating the checks in
* channelpadding_decide_to_pad_channel().
*/
typedef enum {
CHANNEL_USED_NOT_USED_FOR_FULL_CIRCS = 0,
CHANNEL_USED_FOR_FULL_CIRCS,
CHANNEL_USED_FOR_USER_TRAFFIC,
} channel_usage_info_t;
/**
* Channel struct; see the channel_t typedef in or.h. A channel is an
* abstract interface for the OR-to-OR connection, similar to connection_or_t,
......@@ -37,6 +50,9 @@ struct channel_s {
/** List entry for hashtable for global-identifier lookup. */
HT_ENTRY(channel_s) gidmap_node;
/** Handle entry for handle-based lookup */
HANDLE_ENTRY(channel, channel_s);
/** Current channel state */
channel_state_t state;
......@@ -51,6 +67,58 @@ struct channel_s {
/** has this channel ever been open? */
unsigned int has_been_open:1;
/**
* This field indicates if the other side has enabled or disabled
* padding via either the link protocol version or
* channelpadding_negotiate cells.
*
* Clients can override this with ConnectionPadding in torrc to
* disable or force padding to relays, but relays cannot override the
* client's request.
*/
unsigned int padding_enabled:1;
/** Cached value of our decision to pad (to avoid expensive
* checks during critical path statistics counting). */
unsigned int currently_padding:1;
/** Is there a pending netflow padding callback? */
unsigned int pending_padding_callback:1;
/** Has this channel ever been used for non-directory traffic?
* Used to decide what channels to pad, and when. */
channel_usage_info_t channel_usage;
/** When should we send a cell for netflow padding, in absolute
* milliseconds since monotime system start. 0 means no padding
* is scheduled. */
uint64_t next_padding_time_ms;
/** The callback pointer for the padding callbacks */
tor_timer_t *padding_timer;
/** The handle to this channel (to free on canceled timers) */
struct channel_handle_t *timer_handle;
/**
* These two fields specify the minimum and maximum negotiated timeout
* values for inactivity (send or receive) before we decide to pad a
* channel. These fields can be set either via a PADDING_NEGOTIATE cell,
* or the torrc option ReducedConnectionPadding. The consensus parameters
* nf_ito_low and nf_ito_high are used to ensure that padding can only be
* negotiated to be less frequent than what is specified in the consensus.
* (This is done to prevent wingnut clients from requesting excessive
* padding).
*
* The actual timeout value is randomly chosen between these two values
* as per the table in channelpadding_get_netflow_inactive_timeout_ms(),
* after ensuring that these values do not specify lower timeouts than
* the consensus parameters.
*
* If these are 0, we have not negotiated or specified custom padding
* times, and instead use consensus defaults. */
uint16_t padding_timeout_low_ms;
uint16_t padding_timeout_high_ms;
/** Why did we close?
*/
enum {
......@@ -90,6 +158,18 @@ struct channel_s {
time_t timestamp_created; /* Channel created */
time_t timestamp_active; /* Any activity */
/**
* This is a high-resolution monotonic timestamp that marks when we
* believe the channel has actually sent or received data to/from
* the wire. Right now, it is used to determine when we should send
* a padding cell for channelpadding.
*
* XXX: Are we setting timestamp_xfer_ms in the right places to
* accurately reflect actual network data transfer? Or might this be
* very wrong wrt when bytes actually go on the wire?
*/
uint64_t timestamp_xfer_ms;
/* Methods implemented by the lower layer */
/** Free a channel */
......@@ -633,5 +713,8 @@ int packed_cell_is_destroy(channel_t *chan,
const packed_cell_t *packed_cell,
circid_t *circid_out);
/* Declare the handle helpers */
HANDLE_DECL(channel, channel_s,);
#endif
This diff is collapsed.
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
* Copyright (c) 2007-2015, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
* \file circuitbuild.h
* \brief Header file for circuitbuild.c.
**/
#ifndef TOR_CHANNELPADDING_H
#define TOR_CHANNELPADDING_H
#include "channelpadding_negotiation.h"
typedef enum {
CHANNELPADDING_WONTPAD,
CHANNELPADDING_PADLATER,
CHANNELPADDING_PADDING_SCHEDULED,
CHANNELPADDING_PADDING_ALREADY_SCHEDULED,
CHANNELPADDING_PADDING_SENT,
} channelpadding_decision_t;
channelpadding_decision_t channelpadding_decide_to_pad_channel(channel_t
*chan);
int channelpadding_update_padding_for_channel(channel_t *,
const channelpadding_negotiate_t *);
void channelpadding_disable_padding_on_channel(channel_t *chan);
void channelpadding_reduce_padding_on_channel(channel_t *chan);
int channelpadding_send_enable_command(channel_t *chan, uint16_t low_timeout,
uint16_t high_timeout);
int channelpadding_get_circuits_available_timeout(void);
unsigned int channelpadding_get_channel_idle_timeout(const channel_t *, int);
#endif
......@@ -57,6 +57,9 @@
#include "routerlist.h"
#include "scheduler.h"
#include "torcert.h"
#include "networkstatus.h"
#include "channelpadding_negotiation.h"
#include "channelpadding.h"
/** How many CELL_PADDING cells have we received, ever? */
uint64_t stats_n_padding_cells_processed = 0;
......@@ -122,6 +125,8 @@ static void channel_tls_process_netinfo_cell(cell_t *cell,
static int command_allowed_before_handshake(uint8_t command);
static int enter_v3_handshake_with_cell(var_cell_t *cell,
channel_tls_t *tlschan);
static void channel_tls_process_padding_negotiate_cell(cell_t *cell,
channel_tls_t *chan);
/**
* Do parts of channel_tls_t initialization common to channel_tls_connect()
......@@ -1098,9 +1103,16 @@ channel_tls_handle_cell(cell_t *cell, or_connection_t *conn)
/* We note that we're on the internet whenever we read a cell. This is
* a fast operation. */
entry_guards_note_internet_connectivity(get_guard_selection_info());
rep_hist_padding_count_read(PADDING_TYPE_TOTAL);
if (chan->base_.currently_padding)
rep_hist_padding_count_read(PADDING_TYPE_ENABLED_TOTAL);
switch (cell->command) {
case CELL_PADDING:
rep_hist_padding_count_read(PADDING_TYPE_CELL);
if (chan->base_.currently_padding)
rep_hist_padding_count_read(PADDING_TYPE_ENABLED_CELL);
++stats_n_padding_cells_processed;
/* do nothing */
break;
......@@ -1111,6 +1123,10 @@ channel_tls_handle_cell(cell_t *cell, or_connection_t *conn)
++stats_n_netinfo_cells_processed;
PROCESS_CELL(netinfo, cell, chan);
break;
case CELL_PADDING_NEGOTIATE:
++stats_n_netinfo_cells_processed;
PROCESS_CELL(padding_negotiate, cell, chan);
break;
case CELL_CREATE:
case CELL_CREATE_FAST:
case CELL_CREATED:
......@@ -1570,6 +1586,9 @@ channel_tls_process_versions_cell(var_cell_t *cell, channel_tls_t *chan)
chan->conn->link_proto >= MIN_LINK_PROTO_FOR_WIDE_CIRC_IDS;
chan->conn->wide_circ_ids = chan->base_.wide_circ_ids;
chan->base_.padding_enabled =
chan->conn->link_proto >= MIN_LINK_PROTO_FOR_CHANNEL_PADDING;
if (send_certs) {
if (connection_or_send_certs_cell(chan->conn) < 0) {
log_warn(LD_OR, "Couldn't send certs cell");
......@@ -1594,6 +1613,43 @@ channel_tls_process_versions_cell(var_cell_t *cell, channel_tls_t *chan)
}
}
/**
* Process a 'padding_negotiate' cell
*
* This function is called to handle an incoming PADDING_NEGOTIATE cell;
* enable or disable padding accordingly, and read and act on its timeout
* value contents.
*/
static void
channel_tls_process_padding_negotiate_cell(cell_t *cell, channel_tls_t *chan)
{
channelpadding_negotiate_t *negotiation;
tor_assert(cell);
tor_assert(chan);
tor_assert(chan->conn);
if (chan->conn->link_proto < MIN_LINK_PROTO_FOR_CHANNEL_PADDING) {
log_fn(LOG_PROTOCOL_WARN, LD_OR,
"Received a PADDING_NEGOTIATE cell on v%d connection; dropping.",
chan->conn->link_proto);
return;
}
if (channelpadding_negotiate_parse(&negotiation, cell->payload,
CELL_PAYLOAD_SIZE) < 0) {
log_fn(LOG_PROTOCOL_WARN, LD_OR,
"Received malformed PADDING_NEGOTIATE cell on v%d connection; "
"dropping.", chan->conn->link_proto);
return;
}
channelpadding_update_padding_for_channel(TLS_CHAN_TO_BASE(chan),
negotiation);
channelpadding_negotiate_free(negotiation);
}
/**
* Process a 'netinfo' cell
*
......
......@@ -919,9 +919,18 @@ circuit_send_next_onion_skin(origin_circuit_t *circ)
memset(&cc, 0, sizeof(cc));
if (circ->build_state->onehop_tunnel)
control_event_bootstrap(BOOTSTRAP_STATUS_ONEHOP_CREATE, 0);
else
else {
control_event_bootstrap(BOOTSTRAP_STATUS_CIRCUIT_CREATE, 0);
/* If this is not a one-hop tunnel, the channel is being used
* for traffic that wants anonymity and protection from traffic
* analysis (such as netflow record retention). That means we want
* to pad it.
*/
if (circ->base_.n_chan->channel_usage < CHANNEL_USED_FOR_FULL_CIRCS)
circ->base_.n_chan->channel_usage = CHANNEL_USED_FOR_FULL_CIRCS;
}
node = node_get_by_id(circ->base_.n_chan->identity_digest);
fast = should_use_create_fast_for_circuit(circ);
if (!fast) {
......
......@@ -328,8 +328,16 @@ command_process_create_cell(cell_t *cell, channel_t *chan)
if (create_cell->handshake_type != ONION_HANDSHAKE_TYPE_FAST) {
/* hand it off to the cpuworkers, and then return. */
if (connection_or_digest_is_known_relay(chan->identity_digest))
if (connection_or_digest_is_known_relay(chan->identity_digest)) {
rep_hist_note_circuit_handshake_requested(create_cell->handshake_type);
// Needed for chutney: Sometimes relays aren't in the consensus yet, and
// get marked as clients. This resets their channels once they appear.
// Probably useful for normal operation wrt relay flapping, too.
chan->is_client = 0;
} else {
channel_mark_client(chan);
}
if (assign_onionskin_to_cpuworker(circ, create_cell) < 0) {
log_debug(LD_GENERAL,"Failed to hand off onionskin. Closing.");
circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_RESOURCELIMIT);
......@@ -344,9 +352,15 @@ command_process_create_cell(cell_t *cell, channel_t *chan)
int len;
created_cell_t created_cell;
/* Make sure we never try to use the OR connection on which we
* received this cell to satisfy an EXTEND request, */
channel_mark_client(chan);
/* If this is a create_fast, this might be a client. Let's check. */
if (connection_or_digest_is_known_relay(chan->identity_digest)) {
// Needed for chutney: Sometimes relays aren't in the consensus yet, and
// get marked as clients. This resets their channels once they appear.
// Probably useful for normal operation wrt relay flapping, too.
chan->is_client = 0;
} else {
channel_mark_client(chan);
}
memset(&created_cell, 0, sizeof(created_cell));
len = onion_skin_server_handshake(ONION_HANDSHAKE_TYPE_FAST,
......
......@@ -242,6 +242,7 @@ static config_var_t option_vars_[] = {
V(BridgeRecordUsageByCountry, BOOL, "1"),
V(BridgeRelay, BOOL, "0"),
V(CellStatistics, BOOL, "0"),
V(PaddingStatistics, BOOL, "1"),
V(LearnCircuitBuildTimeout, BOOL, "1"),
V(CircuitBuildTimeout, INTERVAL, "0"),
V(CircuitIdleTimeout, INTERVAL, "1 hour"),
......@@ -458,6 +459,8 @@ static config_var_t option_vars_[] = {
V(RecommendedClientVersions, LINELIST, NULL),
V(RecommendedServerVersions, LINELIST, NULL),
V(RecommendedPackages, LINELIST, NULL),
V(ReducedConnectionPadding, BOOL, "0"),
V(ConnectionPadding, AUTOBOOL, "auto"),
V(RefuseUnknownExits, AUTOBOOL, "auto"),
V(RejectPlaintextPorts, CSV, ""),
V(RelayBandwidthBurst, MEMUNIT, "0"),
......@@ -3429,6 +3432,14 @@ options_validate(or_options_t *old_options, or_options_t *options,
options->DirPort_set = 0;
}
if (server_mode(options) && options->ConnectionPadding != -1) {
REJECT("Relays must use 'auto' for the ConnectionPadding setting.");
}
if (server_mode(options) && options->ReducedConnectionPadding != 0) {
REJECT("Relays cannot set ReducedConnectionPadding. ");
}
if (options->MinUptimeHidServDirectoryV2 < 0) {
log_warn(LD_CONFIG, "MinUptimeHidServDirectoryV2 option must be at "
"least 0 seconds. Changing to 0.");
......
......@@ -55,6 +55,7 @@
#include "ext_orport.h"
#include "scheduler.h"
#include "torcert.h"
#include "channelpadding.h"
static int connection_tls_finish_handshake(or_connection_t *conn);
static int connection_or_launch_v3_or_handshake(or_connection_t *conn);
......@@ -1983,12 +1984,23 @@ connection_or_write_cell_to_buf(const cell_t *cell, or_connection_t *conn)
cell_pack(&networkcell, cell, conn->wide_circ_ids);
rep_hist_padding_count_write(PADDING_TYPE_TOTAL);
if (cell->command == CELL_PADDING)
rep_hist_padding_count_write(PADDING_TYPE_CELL);
connection_write_to_buf(networkcell.body, cell_network_size, TO_CONN(conn));
/* Touch the channel's active timestamp if there is one */
if (conn->chan)
if (conn->chan) {
channel_timestamp_active(TLS_CHAN_TO_BASE(conn->chan));
if (conn->chan->base_.currently_padding) {
rep_hist_padding_count_write(PADDING_TYPE_ENABLED_TOTAL);
if (cell->command == CELL_PADDING)
rep_hist_padding_count_write(PADDING_TYPE_ENABLED_CELL);
}
}
if (conn->base_.state == OR_CONN_STATE_OR_HANDSHAKING_V3)
or_handshake_state_record_cell(conn, conn->handshake_state, cell, 0);
}
......@@ -2094,7 +2106,7 @@ connection_or_process_cells_from_inbuf(or_connection_t *conn)
}
/** Array of recognized link protocol versions. */
static const uint16_t or_protocol_versions[] = { 1, 2, 3, 4 };
static const uint16_t or_protocol_versions[] = { 1, 2, 3, 4, 5 };
/** Number of versions in <b>or_protocol_versions</b>. */
static const int n_or_protocol_versions =
(int)( sizeof(or_protocol_versions)/sizeof(uint16_t) );
......
......@@ -109,6 +109,8 @@ void var_cell_free(var_cell_t *cell);
/* DOCDOC */
#define MIN_LINK_PROTO_FOR_WIDE_CIRC_IDS 4
#define MIN_LINK_PROTO_FOR_CHANNEL_PADDING 5
#define MAX_LINK_PROTO MIN_LINK_PROTO_FOR_CHANNEL_PADDING
void connection_or_group_set_badness_(smartlist_t *group, int force);
......
......@@ -22,6 +22,7 @@ LIBTOR_A_SOURCES = \
src/or/bridges.c \
src/or/buffers.c \
src/or/channel.c \
src/or/channelpadding.c \
src/or/channeltls.c \
src/or/circpathbias.c \
src/or/circuitbuild.c \
......@@ -137,6 +138,7 @@ ORHEADERS = \
src/or/bridges.h \
src/or/buffers.h \
src/or/channel.h \
src/or/channelpadding.h \
src/or/channeltls.h \
src/or/circpathbias.h \
src/or/circuitbuild.h \
......
......@@ -54,6 +54,7 @@
#include "buffers.h"
#include "channel.h"
#include "channeltls.h"
#include "channelpadding.h"
#include "circuitbuild.h"
#include "circuitlist.h"
#include "circuituse.h"
......@@ -176,7 +177,7 @@ static int signewnym_is_pending = 0;
static unsigned newnym_epoch = 0;
/** Smartlist of all open connections. */
static smartlist_t *connection_array = NULL;
STATIC smartlist_t *connection_array = NULL;
/** List of connections that have been marked for close and need to be freed
* and removed from connection_array. */
static smartlist_t *closeable_connection_lst = NULL;
......@@ -1119,6 +1120,8 @@ run_connection_housekeeping(int i, time_t now)
memset(&cell,0,sizeof(cell_t));
cell.command = CELL_PADDING;
connection_or_write_cell_to_buf(&cell, or_conn);
} else {
channelpadding_decide_to_pad_channel(chan);
}
}
......@@ -1184,6 +1187,7 @@ CALLBACK(check_dns_honesty);
CALLBACK(write_bridge_ns);
CALLBACK(check_fw_helper_app);
CALLBACK(heartbeat);
CALLBACK(reset_padding_counts);
#undef CALLBACK
......@@ -1215,6 +1219,7 @@ static periodic_event_item_t periodic_events[] = {
CALLBACK(write_bridge_ns),
CALLBACK(check_fw_helper_app),
CALLBACK(heartbeat),
CALLBACK(reset_padding_counts),
END_OF_PERIODIC_EVENTS
};
#undef CALLBACK
......@@ -1723,6 +1728,17 @@ write_stats_file_callback(time_t now, const or_options_t *options)
/**