[tor-commits] [tor/master] Better policy support for IPv6

nickm at torproject.org nickm at torproject.org
Thu Nov 15 19:47:52 UTC 2012


commit a96c0affcb4cda1a2e0d83d123993d10efc6e396
Author: Nick Mathewson <nickm at torproject.org>
Date:   Wed Oct 24 15:03:29 2012 -0400

    Better policy support for IPv6
    
    Now, "accept *:80" means "accept all addresses on port 80", and not
    just IPv4.  For just v4, say "accept *4:80"; for just v6 say "accept
    *6:80".
    
    We can parse these policies from torrc just fine, and we should be
    successfully keeping them out of descriptors for now.
    
    We also now include appropriate IPv6 addresses in "reject private:*"
---
 src/or/dirserv.c     |    2 +-
 src/or/dirvote.c     |    2 +-
 src/or/or.h          |   10 +++-
 src/or/policies.c    |  135 ++++++++++++++++++++++++++++++++++++++++---------
 src/or/policies.h    |    3 +-
 src/or/router.c      |    6 +-
 src/or/routerparse.c |   15 ++++--
 src/or/routerset.c   |    1 +
 src/test/test.c      |    4 +-
 9 files changed, 139 insertions(+), 39 deletions(-)

diff --git a/src/or/dirserv.c b/src/or/dirserv.c
index e7aa582..1d63be0 100644
--- a/src/or/dirserv.c
+++ b/src/or/dirserv.c
@@ -2237,7 +2237,7 @@ routerstatus_format_entry(char *buf, size_t buf_len,
     }
 
     if (desc) {
-      summary = policy_summarize(desc->exit_policy);
+      summary = policy_summarize(desc->exit_policy, AF_INET);
       r = tor_snprintf(cp, buf_len - (cp-buf), "p %s\n", summary);
       if (r<0) {
         log_warn(LD_BUG, "Not enough space in buffer.");
diff --git a/src/or/dirvote.c b/src/or/dirvote.c
index 7020943..f8b8d37 100644
--- a/src/or/dirvote.c
+++ b/src/or/dirvote.c
@@ -3552,7 +3552,7 @@ dirvote_create_microdescriptor(const routerinfo_t *ri, int consensus_method)
 
   if (crypto_pk_write_public_key_to_string(ri->onion_pkey, &key, &keylen)<0)
     goto done;
-  summary = policy_summarize(ri->exit_policy);
+  summary = policy_summarize(ri->exit_policy, AF_INET);
   if (ri->declared_family)
     family = smartlist_join_strings(ri->declared_family, " ", 0, NULL);
 
diff --git a/src/or/or.h b/src/or/or.h
index 6510725..42bf0a8 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -1730,7 +1730,15 @@ typedef struct addr_policy_t {
   maskbits_t maskbits; /**< Accept/reject all addresses <b>a</b> such that the
                  * first <b>maskbits</b> bits of <b>a</b> match
                  * <b>addr</b>. */
-  tor_addr_t addr; /**< Base address to accept or reject. */
+  /** Base address to accept or reject.
+   *
+   * Note that wildcards are treated
+   * differntly depending on address family. An AF_UNSPEC address means
+   * "All addresses, IPv4 or IPv6." An AF_INET address with maskbits==0 means
+   * "All IPv4 addresses" and an AF_INET6 address with maskbits == 0 means
+   * "All IPv6 addresses".
+  **/
+  tor_addr_t addr;
   uint16_t prt_min; /**< Lowest port number to accept/reject. */
   uint16_t prt_max; /**< Highest port number to accept/reject. */
 } addr_policy_t;
diff --git a/src/or/policies.c b/src/or/policies.c
index 442377b..8367446 100644
--- a/src/or/policies.c
+++ b/src/or/policies.c
@@ -59,8 +59,10 @@ typedef struct policy_summary_item_t {
 static const char *private_nets[] = {
   "0.0.0.0/8", "169.254.0.0/16",
   "127.0.0.0/8", "192.168.0.0/16", "10.0.0.0/8", "172.16.0.0/12",
-  // "fc00::/7", "fe80::/10", "fec0::/10", "::/127",
-  NULL };
+  "[::]/8",
+  "[fc00::]/7", "[fe80::]/10", "[fec0::]/10", "[ff00::]/8", "[::]/127",
+  NULL
+};
 
 /** Replace all "private" entries in *<b>policy</b> with their expanded
  * equivalents. */
@@ -101,6 +103,49 @@ policy_expand_private(smartlist_t **policy)
   *policy = tmp;
 }
 
+/** Expand each of the AF_UNSPEC elements in *<b>policy</b> (which indicate
+ * protocol-neutral wildcards) into a pair of wildcard elements: one IPv4-
+ * specific and one IPv6-specific. */
+void
+policy_expand_unspec(smartlist_t **policy)
+{
+  smartlist_t *tmp;
+  if (!*policy)
+    return;
+
+  tmp = smartlist_new();
+  SMARTLIST_FOREACH_BEGIN(*policy, addr_policy_t *, p) {
+    sa_family_t family = tor_addr_family(&p->addr);
+    if (family == AF_INET6 || family == AF_INET || p->is_private) {
+      smartlist_add(tmp, p);
+    } else if (family == AF_UNSPEC) {
+      addr_policy_t newpolicy_ipv4;
+      addr_policy_t newpolicy_ipv6;
+      memcpy(&newpolicy_ipv4, p, sizeof(addr_policy_t));
+      memcpy(&newpolicy_ipv6, p, sizeof(addr_policy_t));
+      newpolicy_ipv4.is_canonical = 0;
+      newpolicy_ipv6.is_canonical = 0;
+      if (p->maskbits != 0) {
+        log_warn(LD_BUG, "AF_UNSPEC policy with maskbits==%d", p->maskbits);
+        newpolicy_ipv4.maskbits = 0;
+        newpolicy_ipv6.maskbits = 0;
+      }
+      tor_addr_from_ipv4h(&newpolicy_ipv4.addr, 0);
+      tor_addr_from_ipv6_bytes(&newpolicy_ipv6.addr,
+                               "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0");
+      smartlist_add(tmp, addr_policy_get_canonical_entry(&newpolicy_ipv4));
+      smartlist_add(tmp, addr_policy_get_canonical_entry(&newpolicy_ipv6));
+      addr_policy_free(p);
+    } else {
+      log_warn(LD_BUG, "Funny-looking address policy with family %d", family);
+      smartlist_add(tmp, p);
+    }
+  } SMARTLIST_FOREACH_END(p);
+
+  smartlist_free(*policy);
+  *policy = tmp;
+}
+
 /**
  * Given a linked list of config lines containing "allow" and "deny"
  * tokens, parse them and append the result to <b>dest</b>. Return -1
@@ -145,6 +190,7 @@ parse_addr_policy(config_line_t *cfg, smartlist_t **dest,
     addr_policy_list_free(result);
   } else {
     policy_expand_private(&result);
+    policy_expand_unspec(&result);
 
     if (*dest) {
       smartlist_add_all(*dest, result);
@@ -735,6 +781,10 @@ compare_tor_addr_to_addr_policy(const tor_addr_t *addr, uint16_t port,
 static int
 addr_policy_covers(addr_policy_t *a, addr_policy_t *b)
 {
+  if (tor_addr_family(&a->addr) != tor_addr_family(&b->addr)) {
+    /* You can't cover a different family. */
+    return 0;
+  }
   /* We can ignore accept/reject, since "accept *:80, reject *:80" reduces
    * to "accept *:80". */
   if (a->maskbits > b->maskbits) {
@@ -790,20 +840,32 @@ append_exit_policy_string(smartlist_t **policy, const char *more)
 static void
 exit_policy_remove_redundancies(smartlist_t *dest)
 {
-  addr_policy_t *ap, *tmp, *victim;
+  addr_policy_t *ap, *tmp;
   int i, j;
 
-  /* Step one: find a *:* entry and cut off everything after it. */
-  for (i = 0; i < smartlist_len(dest); ++i) {
-    ap = smartlist_get(dest, i);
-    if (ap->maskbits == 0 && ap->prt_min <= 1 && ap->prt_max >= 65535) {
-      /* This is a catch-all line -- later lines are unreachable. */
-      while (i+1 < smartlist_len(dest)) {
-        victim = smartlist_get(dest, i+1);
-        smartlist_del(dest, i+1);
-        addr_policy_free(victim);
+  /* Step one: kill every ipv4 thing after *4:*, every IPv6 thing after *6:*
+   */
+  {
+    int kill_v4=0, kill_v6=0;
+    for (i = 0; i < smartlist_len(dest); ++i) {
+      sa_family_t family;
+      ap = smartlist_get(dest, i);
+      family = tor_addr_family(&ap->addr);
+      if ((family == AF_INET && kill_v4) ||
+          (family == AF_INET6 && kill_v6)) {
+        smartlist_del_keeporder(dest, i--);
+        addr_policy_free(ap);
+        continue;
+      }
+
+      if (ap->maskbits == 0 && ap->prt_min <= 1 && ap->prt_max >= 65535) {
+        /* This is a catch-all line -- later lines are unreachable. */
+        if (family == AF_INET) {
+          kill_v4 = 1;
+        } else if (family == AF_INET6) {
+          kill_v6 = 1;
+        }
       }
-      break;
     }
   }
 
@@ -869,6 +931,10 @@ exit_policy_remove_redundancies(smartlist_t *dest)
  * policy afterwards. If <b>rejectprivate</b> is true, prepend
  * "reject private:*" to the policy. Return -1 if we can't parse cfg,
  * else return 0.
+ *
+ * This function is used to parse the exit policy from our torrc. For
+ * the functions used to parse the exit policy from a router descriptor,
+ * see router_add_exit_policy.
  */
 int
 policies_parse_exit_policy(config_line_t *cfg, smartlist_t **dest,
@@ -885,10 +951,12 @@ policies_parse_exit_policy(config_line_t *cfg, smartlist_t **dest,
   }
   if (parse_addr_policy(cfg, dest, -1))
     return -1;
-  if (add_default_policy)
+  if (add_default_policy) {
     append_exit_policy_string(dest, DEFAULT_EXIT_POLICY);
-  else
-    append_exit_policy_string(dest, "reject *:*");
+  } else {
+    append_exit_policy_string(dest, "reject *4:*");
+    append_exit_policy_string(dest, "reject *6:*");
+  }
   exit_policy_remove_redundancies(*dest);
 
   return 0;
@@ -899,7 +967,8 @@ policies_parse_exit_policy(config_line_t *cfg, smartlist_t **dest,
 void
 policies_exit_policy_append_reject_star(smartlist_t **dest)
 {
-  append_exit_policy_string(dest, "reject *:*");
+  append_exit_policy_string(dest, "reject *4:*");
+  append_exit_policy_string(dest, "reject *6:*");
 }
 
 /** Replace the exit policy of <b>node</b> with reject *:* */
@@ -1001,17 +1070,26 @@ policy_write_item(char *buf, size_t buflen, addr_policy_t *policy,
   const char *addrpart;
   int result;
   const int is_accept = policy->policy_type == ADDR_POLICY_ACCEPT;
-  const int is_ip6 = tor_addr_family(&policy->addr) == AF_INET6;
+  const sa_family_t family = tor_addr_family(&policy->addr);
+  const int is_ip6 = (family == AF_INET6);
 
   tor_addr_to_str(addrbuf, &policy->addr, sizeof(addrbuf), 1);
 
   /* write accept/reject 1.2.3.4 */
-  if (policy->is_private)
+  if (policy->is_private) {
     addrpart = "private";
-  else if (policy->maskbits == 0)
-    addrpart = "*";
-  else
+  } else if (policy->maskbits == 0) {
+    if (format_for_desc)
+      addrpart = "*";
+    else if (family == AF_INET6)
+      addrpart = "*6";
+    else if (family == AF_INET)
+      addrpart = "*4";
+    else
+      addrpart = "*";
+  } else {
     addrpart = addrbuf;
+  }
 
   result = tor_snprintf(buf, buflen, "%s%s %s",
                         is_accept ? "accept" : "reject",
@@ -1220,7 +1298,7 @@ policy_summary_add_item(smartlist_t *summary, addr_policy_t *p)
  * is an exception to the shorter-representation-wins rule).
  */
 char *
-policy_summarize(smartlist_t *policy)
+policy_summarize(smartlist_t *policy, sa_family_t family)
 {
   smartlist_t *summary = policy_summary_create();
   smartlist_t *accepts, *rejects;
@@ -1232,9 +1310,16 @@ policy_summarize(smartlist_t *policy)
   tor_assert(policy);
 
   /* Create the summary list */
-  SMARTLIST_FOREACH(policy, addr_policy_t *, p, {
+  SMARTLIST_FOREACH_BEGIN(policy, addr_policy_t *, p) {
+    sa_family_t f = tor_addr_family(&p->addr);
+    if (f != AF_INET && f != AF_INET6) {
+      log_warn(LD_BUG, "Weird family when summarizing address policy");
+    }
+    if (f != family)
+      continue;
+    /* XXXX-ipv6 More family work is needed */
     policy_summary_add_item(summary, p);
-  });
+  } SMARTLIST_FOREACH_END(p);
 
   /* Now create two lists of strings, one for accepted and one
    * for rejected ports.  We take care to merge ranges so that
diff --git a/src/or/policies.h b/src/or/policies.h
index 431e69e..e9d214d 100644
--- a/src/or/policies.h
+++ b/src/or/policies.h
@@ -31,6 +31,7 @@ int authdir_policy_badexit_address(uint32_t addr, uint16_t port);
 
 int validate_addr_policies(const or_options_t *options, char **msg);
 void policy_expand_private(smartlist_t **policy);
+void policy_expand_unspec(smartlist_t **policy);
 int policies_parse_from_options(const or_options_t *options);
 
 addr_policy_t *addr_policy_get_canonical_entry(addr_policy_t *ent);
@@ -58,7 +59,7 @@ void addr_policy_list_free(smartlist_t *p);
 void addr_policy_free(addr_policy_t *p);
 void policies_free_all(void);
 
-char *policy_summarize(smartlist_t *policy);
+char *policy_summarize(smartlist_t *policy, sa_family_t family);
 
 short_policy_t *parse_short_policy(const char *summary);
 char *write_short_policy(const short_policy_t *policy);
diff --git a/src/or/router.c b/src/or/router.c
index 1cac63a..efe24d7 100644
--- a/src/or/router.c
+++ b/src/or/router.c
@@ -2001,7 +2001,6 @@ router_dump_router_to_string(char *s, size_t maxlen, routerinfo_t *router,
   size_t onion_pkeylen, identity_pkeylen;
   size_t written;
   int result=0;
-  addr_policy_t *tmpe;
   char *family_line;
   char *extra_or_address = NULL;
   const or_options_t *options = get_options();
@@ -2130,11 +2129,12 @@ router_dump_router_to_string(char *s, size_t maxlen, routerinfo_t *router,
   if (!router->exit_policy || !smartlist_len(router->exit_policy)) {
     strlcat(s+written, "reject *:*\n", maxlen-written);
     written += strlen("reject *:*\n");
-    tmpe = NULL;
   } else if (router->exit_policy) {
     int i;
     for (i = 0; i < smartlist_len(router->exit_policy); ++i) {
-      tmpe = smartlist_get(router->exit_policy, i);
+      addr_policy_t *tmpe = smartlist_get(router->exit_policy, i);
+      if (tor_addr_family(&tmpe->addr) == AF_INET6)
+        continue; /* Don't include IPv6 parts of address policy */
       result = policy_write_item(s+written, maxlen-written, tmpe, 1);
       if (result < 0) {
         log_warn(LD_BUG,"descriptor policy_write_item ran out of room!");
diff --git a/src/or/routerparse.c b/src/or/routerparse.c
index 6069c8d..7c1dd88 100644
--- a/src/or/routerparse.c
+++ b/src/or/routerparse.c
@@ -535,7 +535,8 @@ static token_rule_t microdesc_token_table[] = {
 
 /* static function prototypes */
 static int router_add_exit_policy(routerinfo_t *router,directory_token_t *tok);
-static addr_policy_t *router_parse_addr_policy(directory_token_t *tok);
+static addr_policy_t *router_parse_addr_policy(directory_token_t *tok,
+                                               unsigned fmt_flags);
 static addr_policy_t *router_parse_addr_policy_private(directory_token_t *tok);
 
 static int router_get_hash_impl(const char *s, size_t s_len, char *digest,
@@ -3633,6 +3634,10 @@ networkstatus_parse_detached_signatures(const char *s, const char *eos)
 /** Parse the addr policy in the string <b>s</b> and return it.  If
  * assume_action is nonnegative, then insert its action (ADDR_POLICY_ACCEPT or
  * ADDR_POLICY_REJECT) for items that specify no action.
+ *
+ * The addr_policy_t returned by this function can have its address set to
+ * AF_UNSPEC for '*'.  Use policy_expand_unspec() to turn this into a pair
+ * of AF_INET and AF_INET6 items.
  */
 addr_policy_t *
 router_parse_addr_policy_item_from_string(const char *s, int assume_action)
@@ -3672,7 +3677,7 @@ router_parse_addr_policy_item_from_string(const char *s, int assume_action)
     goto err;
   }
 
-  r = router_parse_addr_policy(tok);
+  r = router_parse_addr_policy(tok, TAPMP_EXTENDED_STAR);
   goto done;
  err:
   r = NULL;
@@ -3691,7 +3696,7 @@ static int
 router_add_exit_policy(routerinfo_t *router, directory_token_t *tok)
 {
   addr_policy_t *newe;
-  newe = router_parse_addr_policy(tok);
+  newe = router_parse_addr_policy(tok, 0);
   if (!newe)
     return -1;
   if (! router->exit_policy)
@@ -3716,7 +3721,7 @@ router_add_exit_policy(routerinfo_t *router, directory_token_t *tok)
 /** Given a K_ACCEPT or K_REJECT token and a router, create and return
  * a new exit_policy_t corresponding to the token. */
 static addr_policy_t *
-router_parse_addr_policy(directory_token_t *tok)
+router_parse_addr_policy(directory_token_t *tok, unsigned fmt_flags)
 {
   addr_policy_t newe;
   char *arg;
@@ -3738,7 +3743,7 @@ router_parse_addr_policy(directory_token_t *tok)
   else
     newe.policy_type = ADDR_POLICY_ACCEPT;
 
-  if (tor_addr_parse_mask_ports(arg, 0, &newe.addr, &newe.maskbits,
+  if (tor_addr_parse_mask_ports(arg, fmt_flags, &newe.addr, &newe.maskbits,
                                 &newe.prt_min, &newe.prt_max) < 0) {
     log_warn(LD_DIR,"Couldn't parse line %s. Dropping", escaped(arg));
     return NULL;
diff --git a/src/or/routerset.c b/src/or/routerset.c
index 8a5ff21..a495863 100644
--- a/src/or/routerset.c
+++ b/src/or/routerset.c
@@ -148,6 +148,7 @@ routerset_parse(routerset_t *target, const char *s, const char *description)
         SMARTLIST_DEL_CURRENT(list, nick);
       }
   } SMARTLIST_FOREACH_END(nick);
+  policy_expand_unspec(&target->policies);
   smartlist_add_all(target->list, list);
   smartlist_free(list);
   if (added_countries)
diff --git a/src/test/test.c b/src/test/test.c
index fc3c36e..6652e77 100644
--- a/src/test/test.c
+++ b/src/test/test.c
@@ -1046,7 +1046,7 @@ test_policy_summary_helper(const char *policy_str,
 
   r = policies_parse_exit_policy(&line, &policy, 0, NULL, 1);
   test_eq(r, 0);
-  summary = policy_summarize(policy);
+  summary = policy_summarize(policy, AF_INET);
 
   test_assert(summary != NULL);
   test_streq(summary, expected_summary);
@@ -1192,7 +1192,7 @@ test_policies(void)
   test_assert(policy);
   //test_streq(policy->string, "accept *:80");
   //test_streq(policy->next->string, "reject *:*");
-  test_eq(smartlist_len(policy), 2);
+  test_eq(smartlist_len(policy), 4);
 
   /* test policy summaries */
   /* check if we properly ignore private IP addresses */





More information about the tor-commits mailing list