Browse Source

dos: Track new and closed OR client connections

Implement a basic connection tracking that counts the number of concurrent
connections when they open and close.

This commit also adds the circuit creation mitigation data structure that will
be needed at later commit to keep track of the circuit rate.

Signed-off-by: David Goulet <dgoulet@torproject.org>
David Goulet 6 years ago
parent
commit
c05272783d
6 changed files with 100 additions and 0 deletions
  1. 5 0
      src/or/channel.c
  2. 8 0
      src/or/connection.c
  3. 75 0
      src/or/dos.c
  4. 3 0
      src/or/dos.h
  5. 5 0
      src/or/geoip.h
  6. 4 0
      src/or/or.h

+ 5 - 0
src/or/channel.c

@@ -2583,6 +2583,7 @@ channel_do_open_actions(channel_t *chan)
     if (!router_get_by_id_digest(chan->identity_digest)) {
       if (channel_get_addr_if_possible(chan, &remote_addr)) {
         char *transport_name = NULL;
+        channel_tls_t *tlschan = BASE_CHAN_TO_TLS(chan);
         if (chan->get_transport_name(chan, &transport_name) < 0)
           transport_name = NULL;
 
@@ -2590,6 +2591,10 @@ channel_do_open_actions(channel_t *chan)
                                &remote_addr, transport_name,
                                now);
         tor_free(transport_name);
+        /* Notify the DoS subsystem of a new client. */
+        if (tlschan && tlschan->conn) {
+          dos_new_client_conn(tlschan->conn);
+        }
       }
       /* Otherwise the underlying transport can't tell us this, so skip it */
     }

+ 8 - 0
src/or/connection.c

@@ -78,6 +78,7 @@
 #include "dirserv.h"
 #include "dns.h"
 #include "dnsserv.h"
+#include "dos.h"
 #include "entrynodes.h"
 #include "ext_orport.h"
 #include "geoip.h"
@@ -687,6 +688,13 @@ connection_free,(connection_t *conn))
                                                   "connection_free");
   }
 #endif
+
+  /* Notify the circuit creation DoS mitigation subsystem that an OR client
+   * connection has been closed. And only do that if we track it. */
+  if (conn->type == CONN_TYPE_OR) {
+    dos_close_client_conn(TO_OR_CONN(conn));
+  }
+
   connection_unregister_events(conn);
   connection_free_(conn);
 }

+ 75 - 0
src/or/dos.c

@@ -246,6 +246,81 @@ dos_is_enabled(void)
 
 /* General API */
 
+/* Called when a new client connection has been established on the given
+ * address. */
+void
+dos_new_client_conn(or_connection_t *or_conn)
+{
+  clientmap_entry_t *entry;
+
+  tor_assert(or_conn);
+
+  /* Past that point, we know we have at least one DoS detection subsystem
+   * enabled so we'll start allocating stuff. */
+  if (!dos_is_enabled()) {
+    goto end;
+  }
+
+  /* We are only interested in client connection from the geoip cache. */
+  entry = geoip_lookup_client(&or_conn->real_addr, NULL,
+                              GEOIP_CLIENT_CONNECT);
+  if (BUG(entry == NULL)) {
+    /* Should never happen because we note down the address in the geoip
+     * cache before this is called. */
+    goto end;
+  }
+
+  entry->dos_stats.concurrent_count++;
+  or_conn->tracked_for_dos_mitigation = 1;
+  log_debug(LD_DOS, "Client address %s has now %u concurrent connections.",
+            fmt_addr(&or_conn->real_addr),
+            entry->dos_stats.concurrent_count);
+
+ end:
+  return;
+}
+
+/* Called when a client connection for the given IP address has been closed. */
+void
+dos_close_client_conn(const or_connection_t *or_conn)
+{
+  clientmap_entry_t *entry;
+
+  tor_assert(or_conn);
+
+  /* We have to decrement the count on tracked connection only even if the
+   * subsystem has been disabled at runtime because it might be re-enabled
+   * after and we need to keep a synchronized counter at all time. */
+  if (!or_conn->tracked_for_dos_mitigation) {
+    goto end;
+  }
+
+  /* We are only interested in client connection from the geoip cache. */
+  entry = geoip_lookup_client(&or_conn->real_addr, NULL,
+                              GEOIP_CLIENT_CONNECT);
+  if (entry == NULL) {
+    /* This can happen because we can close a connection before the channel
+     * got to be noted down in the geoip cache. */
+    goto end;
+  }
+
+  /* Extra super duper safety. Going below 0 means an underflow which could
+   * lead to most likely a false positive. In theory, this should never happen
+   * but lets be extra safe. */
+  if (BUG(entry->dos_stats.concurrent_count == 0)) {
+    goto end;
+  }
+
+  entry->dos_stats.concurrent_count--;
+  log_debug(LD_DOS, "Client address %s has lost a connection. Concurrent "
+                    "connections are now at %u",
+            fmt_addr(&or_conn->real_addr),
+            entry->dos_stats.concurrent_count);
+
+ end:
+  return;
+}
+
 /* Called when the consensus has changed. We might have new consensus
  * parameters to look at. */
 void

+ 3 - 0
src/or/dos.h

@@ -48,6 +48,9 @@ void dos_free_all(void);
 void dos_consensus_has_changed(const networkstatus_t *ns);
 int dos_enabled(void);
 
+void dos_new_client_conn(or_connection_t *or_conn);
+void dos_close_client_conn(const or_connection_t *or_conn);
+
 /*
  * Circuit creation DoS mitigation subsystemn interface.
  */

+ 5 - 0
src/or/geoip.h

@@ -13,6 +13,7 @@
 #define TOR_GEOIP_H
 
 #include "testsupport.h"
+#include "dos.h"
 
 #ifdef GEOIP_PRIVATE
 STATIC int geoip_parse_entry(const char *line, sa_family_t family);
@@ -37,6 +38,10 @@ typedef struct clientmap_entry_t {
    * 4000 CE, please remember to add more bits to last_seen_in_minutes.) */
   unsigned int last_seen_in_minutes:30;
   unsigned int action:2;
+
+  /* This object is used to keep some statistics per client address for the
+   * DoS mitigation subsystem. */
+  dos_client_stats_t dos_stats;
 } clientmap_entry_t;
 
 int should_record_bridge_info(const or_options_t *options);

+ 4 - 0
src/or/or.h

@@ -1500,6 +1500,10 @@ typedef struct or_connection_t {
   /** True iff this connection has had its bootstrap failure logged with
    * control_event_bootstrap_problem. */
   unsigned int have_noted_bootstrap_problem:1;
+  /** True iff this is a client connection and its address has been put in the
+   * geoip cache and handled by the DoS mitigation subsystem. We use this to
+   * insure we have a coherent count of concurrent connection. */
+  unsigned int tracked_for_dos_mitigation : 1;
 
   uint16_t link_proto; /**< What protocol version are we using? 0 for
                         * "none negotiated yet." */