[tor-commits] [tor/master] Make the NET_PARTICIPANT role dependent on user activity

nickm at torproject.org nickm at torproject.org
Mon Nov 26 21:36:44 UTC 2018


commit 2c15b6538123047c258987b00475fa658ca14878
Author: Nick Mathewson <nickm at torproject.org>
Date:   Tue Nov 13 15:33:46 2018 -0500

    Make the NET_PARTICIPANT role dependent on user activity
    
    This patch implements all of 28337, except for the part where we
    turn off the role if we've been idle for a long time.
---
 src/app/main/main.c             | 15 +++++++++
 src/core/mainloop/connection.c  |  3 ++
 src/core/mainloop/mainloop.c    | 34 +++++++++++++++++--
 src/core/mainloop/mainloop.h    |  1 +
 src/core/mainloop/netstatus.c   | 73 +++++++++++++++++++++++++++++++++++++++++
 src/core/mainloop/netstatus.h   |  7 ++++
 src/core/or/or.h                |  2 ++
 src/feature/client/dnsserv.c    |  4 +++
 src/feature/control/control.c   |  2 ++
 src/test/test_compat_libevent.c |  1 -
 src/test/test_periodic_event.c  | 11 ++++++-
 11 files changed, 149 insertions(+), 4 deletions(-)

diff --git a/src/app/main/main.c b/src/app/main/main.c
index b8dcb852d..03b3a95d0 100644
--- a/src/app/main/main.c
+++ b/src/app/main/main.c
@@ -303,6 +303,19 @@ process_signal(int sig)
       log_heartbeat(time(NULL));
       control_event_signal(sig);
       break;
+    case SIGACTIVE:
+      /* "SIGACTIVE" counts as ersatz user activity. */
+      note_user_activity(approx_time());
+      control_event_signal(sig);
+      break;
+    case SIGDORMANT:
+      /* "SIGDORMANT" means to ignore past user activity */
+      log_notice(LD_GENERAL, "Going dormant because of controller request.");
+      reset_user_activity(0);
+      set_network_participation(false);
+      schedule_rescan_periodic_events();
+      control_event_signal(sig);
+      break;
   }
 }
 
@@ -472,6 +485,8 @@ static struct {
   { SIGNEWNYM, 0, NULL },
   { SIGCLEARDNSCACHE, 0, NULL },
   { SIGHEARTBEAT, 0, NULL },
+  { SIGACTIVE, 0, NULL },
+  { SIGDORMANT, 0, NULL },
   { -1, -1, NULL }
 };
 
diff --git a/src/core/mainloop/connection.c b/src/core/mainloop/connection.c
index 1198a01ad..e0f1680c9 100644
--- a/src/core/mainloop/connection.c
+++ b/src/core/mainloop/connection.c
@@ -1874,6 +1874,9 @@ connection_init_accepted_conn(connection_t *conn,
       TO_ENTRY_CONN(conn)->nym_epoch = get_signewnym_epoch();
       TO_ENTRY_CONN(conn)->socks_request->listener_type = listener->base_.type;
 
+      /* Any incoming connection on an entry port counts as user activity. */
+      note_user_activity(approx_time());
+
       switch (TO_CONN(listener)->type) {
         case CONN_TYPE_AP_LISTENER:
           conn->state = AP_CONN_STATE_SOCKS_WAIT;
diff --git a/src/core/mainloop/mainloop.c b/src/core/mainloop/mainloop.c
index e67ebdb4a..f18db2898 100644
--- a/src/core/mainloop/mainloop.c
+++ b/src/core/mainloop/mainloop.c
@@ -1513,8 +1513,7 @@ get_my_roles(const or_options_t *options)
                   options->ControlPort_set ||
                   options->OwningControllerFD != UINT64_MAX;
 
-  /* We actually want a better definition here for our work on dormancy. */
-  int is_net_participant = ! net_is_disabled();
+  int is_net_participant = is_participating_on_network();
 
   if (is_bridge) roles |= PERIODIC_EVENT_ROLE_BRIDGE;
   if (is_client) roles |= PERIODIC_EVENT_ROLE_CLIENT;
@@ -1592,6 +1591,30 @@ teardown_periodic_events(void)
   periodic_events_initialized = 0;
 }
 
+static mainloop_event_t *rescan_periodic_events_ev = NULL;
+
+/** Callback: rescan the periodic event list. */
+static void
+rescan_periodic_events_cb(mainloop_event_t *event, void *arg)
+{
+  (void)event;
+  (void)arg;
+  rescan_periodic_events(get_options());
+}
+
+/**
+ * Schedule an event that will rescan which periodic events should run.
+ **/
+void
+schedule_rescan_periodic_events(void)
+{
+  if (!rescan_periodic_events_ev) {
+    rescan_periodic_events_ev =
+      mainloop_event_new(rescan_periodic_events_cb, NULL);
+  }
+  mainloop_event_activate(rescan_periodic_events_ev);
+}
+
 /** Do a pass at all our periodic events, disable those we don't need anymore
  * and enable those we need now using the given options. */
 void
@@ -2714,6 +2737,12 @@ initialize_mainloop_events(void)
 int
 do_main_loop(void)
 {
+  /* For now, starting Tor always counts as user activity. Later, we might
+   * have an option to control this.
+   */
+  reset_user_activity(approx_time());
+  set_network_participation(true);
+
   /* initialize the periodic events first, so that code that depends on the
    * events being present does not assert.
    */
@@ -2912,6 +2941,7 @@ tor_mainloop_free_all(void)
   mainloop_event_free(postloop_cleanup_ev);
   mainloop_event_free(handle_deferred_signewnym_ev);
   mainloop_event_free(scheduled_shutdown_ev);
+  mainloop_event_free(rescan_periodic_events_ev);
 
 #ifdef HAVE_SYSTEMD_209
   periodic_timer_free(systemd_watchdog_timer);
diff --git a/src/core/mainloop/mainloop.h b/src/core/mainloop/mainloop.h
index be642d81f..7f27ef9a5 100644
--- a/src/core/mainloop/mainloop.h
+++ b/src/core/mainloop/mainloop.h
@@ -65,6 +65,7 @@ void reschedule_or_state_save(void);
 void reschedule_dirvote(const or_options_t *options);
 void mainloop_schedule_postloop_cleanup(void);
 void rescan_periodic_events(const or_options_t *options);
+void schedule_rescan_periodic_events(void);
 
 void update_current_time(time_t now);
 
diff --git a/src/core/mainloop/netstatus.c b/src/core/mainloop/netstatus.c
index f02647449..ed7c952dc 100644
--- a/src/core/mainloop/netstatus.c
+++ b/src/core/mainloop/netstatus.c
@@ -6,6 +6,7 @@
 
 #include "core/or/or.h"
 #include "core/mainloop/netstatus.h"
+#include "core/mainloop/mainloop.h"
 #include "app/config/config.h"
 #include "feature/hibernate/hibernate.h"
 
@@ -26,3 +27,75 @@ net_is_completely_disabled(void)
 {
   return get_options()->DisableNetwork || we_are_fully_hibernating();
 }
+
+/**
+ * The time at which we've last seen "user activity" -- that is, any activity
+ * that should keep us as a participant on the network.
+ */
+static time_t last_user_activity_seen = 0;
+
+/**
+ * True iff we are currently a "network participant" -- that is, we
+ * are building circuits, fetching directory information, and so on.
+ **/
+static bool participating_on_network = false;
+
+/**
+ * Record the fact that we have seen "user activity" at the time now.  Move
+ * "last activity seen" time forwards, but never backwards.
+ *
+ * If we were previously not participating on the network, set our
+ * participation status to true, and launch periodic events as appropriate.
+ **/
+void
+note_user_activity(time_t now)
+{
+  last_user_activity_seen = MAX(now, last_user_activity_seen);
+
+  if (! participating_on_network) {
+    log_notice(LD_GENERAL, "Tor is no longer dormant.");
+    set_network_participation(true);
+    schedule_rescan_periodic_events();
+  }
+}
+
+/**
+ * Change the time at which "user activitiy" was last seen to <b>now</b>.
+ *
+ * Unlike note_user_actity, this function sets the time without checking
+ * whether it is in the past, and without causing any rescan of periodic events
+ * or change in participation status.
+ */
+void
+reset_user_activity(time_t now)
+{
+  last_user_activity_seen = now;
+}
+
+/**
+ * Return the most recent time at which we recorded "user activity".
+ **/
+time_t
+get_last_user_activity_time(void)
+{
+  return last_user_activity_seen;
+}
+
+/**
+ * Set the field that remembers whether we are currently participating on the
+ * network.  Does not schedule or un-schedule periodic events.
+ **/
+void
+set_network_participation(bool participation)
+{
+  participating_on_network = participation;
+}
+
+/**
+ * Return true iff we are currently participating on the network.
+ **/
+bool
+is_participating_on_network(void)
+{
+  return participating_on_network;
+}
diff --git a/src/core/mainloop/netstatus.h b/src/core/mainloop/netstatus.h
index e9310c292..58c994fd1 100644
--- a/src/core/mainloop/netstatus.h
+++ b/src/core/mainloop/netstatus.h
@@ -10,4 +10,11 @@
 int net_is_disabled(void);
 int net_is_completely_disabled(void);
 
+void note_user_activity(time_t now);
+void reset_user_activity(time_t now);
+time_t get_last_user_activity_time(void);
+
+void set_network_participation(bool participation);
+bool is_participating_on_network(void);
+
 #endif
diff --git a/src/core/or/or.h b/src/core/or/or.h
index acf092c8d..e4b374b12 100644
--- a/src/core/or/or.h
+++ b/src/core/or/or.h
@@ -97,6 +97,8 @@ struct curve25519_public_key_t;
 #define SIGNEWNYM 129
 #define SIGCLEARDNSCACHE 130
 #define SIGHEARTBEAT 131
+#define SIGACTIVE 132
+#define SIGDORMANT 133
 
 #if (SIZEOF_CELL_T != 0)
 /* On Irix, stdlib.h defines a cell_t type, so we need to make sure
diff --git a/src/feature/client/dnsserv.c b/src/feature/client/dnsserv.c
index ea4951f91..e5abe5c6a 100644
--- a/src/feature/client/dnsserv.c
+++ b/src/feature/client/dnsserv.c
@@ -28,6 +28,7 @@
 #include "core/or/connection_edge.h"
 #include "feature/control/control.h"
 #include "core/mainloop/mainloop.h"
+#include "core/mainloop/netstatus.h"
 #include "core/or/policies.h"
 
 #include "feature/control/control_connection_st.h"
@@ -213,6 +214,9 @@ dnsserv_launch_request(const char *name, int reverse,
   edge_connection_t *conn;
   char *q_name;
 
+  /* Launching a request for a user counts as user activity. */
+  note_user_activity(approx_time());
+
   /* Make a new dummy AP connection, and attach the request to it. */
   entry_conn = entry_connection_new(CONN_TYPE_AP, AF_INET);
   entry_conn->entry_cfg.dns_request = 1;
diff --git a/src/feature/control/control.c b/src/feature/control/control.c
index a5b6ab3bf..1344d66a3 100644
--- a/src/feature/control/control.c
+++ b/src/feature/control/control.c
@@ -1681,6 +1681,8 @@ static const struct signal_t signal_table[] = {
   { SIGNEWNYM, "NEWNYM" },
   { SIGCLEARDNSCACHE, "CLEARDNSCACHE"},
   { SIGHEARTBEAT, "HEARTBEAT"},
+  { SIGACTIVE, "ACTIVE" },
+  { SIGDORMANT, "DORMANT" },
   { 0, NULL },
 };
 
diff --git a/src/test/test_compat_libevent.c b/src/test/test_compat_libevent.c
index 3f505d013..ade76bdb0 100644
--- a/src/test/test_compat_libevent.c
+++ b/src/test/test_compat_libevent.c
@@ -187,4 +187,3 @@ struct testcase_t compat_libevent_tests[] = {
     TT_FORK, NULL, NULL },
   END_OF_TESTCASES
 };
-
diff --git a/src/test/test_periodic_event.c b/src/test/test_periodic_event.c
index 6a3e320b2..f3d518eb7 100644
--- a/src/test/test_periodic_event.c
+++ b/src/test/test_periodic_event.c
@@ -51,6 +51,8 @@ test_pe_initialize(void *arg)
    * need to run the main loop and then wait for a second delaying the unit
    * tests. Instead, we'll test the callback work indepedently elsewhere. */
   initialize_periodic_events();
+  set_network_participation(false);
+  rescan_periodic_events(get_options());
 
   /* Validate that all events have been set up. */
   for (int i = 0; periodic_events[i].name; ++i) {
@@ -82,6 +84,8 @@ test_pe_launch(void *arg)
    * network gets enabled. */
   consider_hibernation(time(NULL));
 
+  set_network_participation(true);
+
   /* Hack: We'll set a dumb fn() of each events so they don't get called when
    * dispatching them. We just want to test the state of the callbacks, not
    * the whole code path. */
@@ -93,6 +97,7 @@ test_pe_launch(void *arg)
   options = get_options_mutable();
   options->SocksPort_set = 1;
   periodic_events_on_new_options(options);
+
 #if 0
   /* Lets make sure that before intialization, we can't scan the periodic
    * events list and launch them. Lets try by being a Client. */
@@ -148,6 +153,7 @@ test_pe_launch(void *arg)
   options->SocksPort_set = 0;
   options->ORPort_set = 0;
   options->DisableNetwork = 1;
+  set_network_participation(false);
   periodic_events_on_new_options(options);
 
   for (int i = 0; periodic_events[i].name; ++i) {
@@ -162,6 +168,7 @@ test_pe_launch(void *arg)
   options->BridgeRelay = 1; options->AuthoritativeDir = 1;
   options->V3AuthoritativeDir = 1; options->BridgeAuthoritativeDir = 1;
   options->DisableNetwork = 0;
+  set_network_participation(true);
   register_dummy_hidden_service(&service);
   periodic_events_on_new_options(options);
   /* Note down the reference because we need to remove this service from the
@@ -195,8 +202,10 @@ test_pe_get_roles(void *arg)
 
   or_options_t *options = get_options_mutable();
   tt_assert(options);
+  set_network_participation(true);
 
-  const int ALL = PERIODIC_EVENT_ROLE_ALL;
+  const int ALL = PERIODIC_EVENT_ROLE_ALL |
+    PERIODIC_EVENT_ROLE_NET_PARTICIPANT;
 
   /* Nothing configured, should be no roles. */
   tt_assert(net_is_disabled());





More information about the tor-commits mailing list