diff --git a/changes/bug21357 b/changes/bug21357 new file mode 100644 index 000000000..a1cb43a78 --- /dev/null +++ b/changes/bug21357 @@ -0,0 +1,7 @@ + o Major bugfixes (IPv6 Exits): + - Stop rejecting all IPv6 traffic on Exits whose exit policy rejects IPv6 + addresses. Instead, only reject a port over IPv6 if the exit policy + rejects that port on more than an IPv6 /16 of addresses. This bug was + made worse by 17027 in 0.2.8.1-alpha, which rejects a relay's own IPv6 + address by default. + Fixes bug 21357; bugfix on commit 004f3f4e53 in 0.2.4.7-alpha. diff --git a/src/or/policies.c b/src/or/policies.c index 227e168d9..6037ee311 100644 --- a/src/or/policies.c +++ b/src/or/policies.c @@ -2299,7 +2299,26 @@ policy_summary_item_split(policy_summary_item_t* old, uint16_t new_starts) * my immortal soul, he can clean it up himself. */ #define AT(x) ((policy_summary_item_t*)smartlist_get(summary, x)) -#define REJECT_CUTOFF_COUNT (1<<25) +#define IPV4_BITS (32) +/* Every IPv4 address is counted as one rejection */ +#define REJECT_CUTOFF_SCALE_IPV4 (0) +/* Ports are rejected in an IPv4 summary if they are rejected in more than two + * IPv4 /8 address blocks */ +#define REJECT_CUTOFF_COUNT_IPV4 (U64_LITERAL(1) << \ + (IPV4_BITS - REJECT_CUTOFF_SCALE_IPV4 - 7)) + +#define IPV6_BITS (128) +/* IPv6 /64s are counted as one rejection, anything smaller is ignored */ +#define REJECT_CUTOFF_SCALE_IPV6 (64) +/* Ports are rejected in an IPv6 summary if they are rejected in more than one + * IPv6 /16 address block. + * This is rougly equivalent to the IPv4 cutoff, as only five IPv6 /12s (and + * some scattered smaller blocks) have been allocated to the RIRs. + * Network providers are typically allocated one or more IPv6 /32s. + */ +#define REJECT_CUTOFF_COUNT_IPV6 (U64_LITERAL(1) << \ + (IPV6_BITS - REJECT_CUTOFF_SCALE_IPV6 - 16)) + /** Split an exit policy summary so that prt_min and prt_max * fall at exactly the start and end of an item respectively. */ @@ -2332,35 +2351,82 @@ policy_summary_split(smartlist_t *summary, return start_at_index; } -/** Mark port ranges as accepted if they are below the reject_count */ +/** Mark port ranges as accepted if they are below the reject_count for family + */ static void policy_summary_accept(smartlist_t *summary, - uint16_t prt_min, uint16_t prt_max) + uint16_t prt_min, uint16_t prt_max, + sa_family_t family) { + tor_assert_nonfatal_once(family == AF_INET || family == AF_INET6); + uint64_t family_reject_count = ((family == AF_INET) ? + REJECT_CUTOFF_COUNT_IPV4 : + REJECT_CUTOFF_COUNT_IPV6); + int i = policy_summary_split(summary, prt_min, prt_max); while (i < smartlist_len(summary) && AT(i)->prt_max <= prt_max) { if (!AT(i)->accepted && - AT(i)->reject_count <= REJECT_CUTOFF_COUNT) + AT(i)->reject_count <= family_reject_count) AT(i)->accepted = 1; i++; } tor_assert(i < smartlist_len(summary) || prt_max==65535); } -/** Count the number of addresses in a network with prefixlen maskbits - * against the given portrange. */ +/** Count the number of addresses in a network in family with prefixlen + * maskbits against the given portrange. */ static void policy_summary_reject(smartlist_t *summary, maskbits_t maskbits, - uint16_t prt_min, uint16_t prt_max) + uint16_t prt_min, uint16_t prt_max, + sa_family_t family) { + tor_assert_nonfatal_once(family == AF_INET || family == AF_INET6); + int i = policy_summary_split(summary, prt_min, prt_max); - /* XXX: ipv4 specific */ - uint64_t count = (U64_LITERAL(1) << (32-maskbits)); + + /* The length of a single address mask */ + int addrbits = (family == AF_INET) ? IPV4_BITS : IPV6_BITS; + tor_assert_nonfatal_once(addrbits >= maskbits); + + /* We divide IPv6 address counts by (1 << scale) to keep them in a uint64_t + */ + int scale = ((family == AF_INET) ? + REJECT_CUTOFF_SCALE_IPV4 : + REJECT_CUTOFF_SCALE_IPV6); + + tor_assert_nonfatal_once(addrbits >= scale); + if (maskbits > (addrbits - scale)) { + tor_assert_nonfatal_once(family == AF_INET6); + /* The address range is so small, we'd need billions of them to reach the + * rejection limit. So we ignore this range in the reject count. */ + return; + } + + uint64_t count = 0; + if (addrbits - scale - maskbits >= 64) { + tor_assert_nonfatal_once(family == AF_INET6); + /* The address range is so large, it's an automatic rejection for all ports + * in the range. */ + count = UINT64_MAX; + } else { + count = (U64_LITERAL(1) << (addrbits - scale - maskbits)); + } + tor_assert_nonfatal_once(count > 0); while (i < smartlist_len(summary) && AT(i)->prt_max <= prt_max) { - AT(i)->reject_count += count; + if (AT(i)->reject_count <= UINT64_MAX - count) { + AT(i)->reject_count += count; + } else { + /* IPv4 would require a 4-billion address redundant policy to get here, + * but IPv6 just needs to have ::/0 */ + if (family == AF_INET) { + tor_assert_nonfatal_unreached_once(); + } + /* If we do get here, use saturating arithmetic */ + AT(i)->reject_count = UINT64_MAX; + } i++; } tor_assert(i < smartlist_len(summary) || prt_max==65535); @@ -2380,7 +2446,7 @@ policy_summary_add_item(smartlist_t *summary, addr_policy_t *p) { if (p->policy_type == ADDR_POLICY_ACCEPT) { if (p->maskbits == 0) { - policy_summary_accept(summary, p->prt_min, p->prt_max); + policy_summary_accept(summary, p->prt_min, p->prt_max, p->addr.family); } } else if (p->policy_type == ADDR_POLICY_REJECT) { @@ -2401,7 +2467,8 @@ policy_summary_add_item(smartlist_t *summary, addr_policy_t *p) } if (!is_private) { - policy_summary_reject(summary, p->maskbits, p->prt_min, p->prt_max); + policy_summary_reject(summary, p->maskbits, p->prt_min, p->prt_max, + p->addr.family); } } else tor_assert(0); @@ -2435,7 +2502,6 @@ policy_summarize(smartlist_t *policy, sa_family_t family) } if (f != family) continue; - /* XXXX-ipv6 More family work is needed */ policy_summary_add_item(summary, p); } SMARTLIST_FOREACH_END(p); @@ -2624,8 +2690,7 @@ parse_short_policy(const char *summary) return result; } -/** Write policy back out into a string. Used only for unit tests - * currently. */ +/** Write policy back out into a string. */ char * write_short_policy(const short_policy_t *policy) { diff --git a/src/or/policies.h b/src/or/policies.h index 20f58f2be..850fa863d 100644 --- a/src/or/policies.h +++ b/src/or/policies.h @@ -22,6 +22,9 @@ #define EXIT_POLICY_REJECT_PRIVATE (1 << 1) #define EXIT_POLICY_ADD_DEFAULT (1 << 2) #define EXIT_POLICY_REJECT_LOCAL_INTERFACES (1 << 3) +#define EXIT_POLICY_OPTION_MAX EXIT_POLICY_REJECT_LOCAL_INTERFACES +/* All options set: used for unit testing */ +#define EXIT_POLICY_OPTION_ALL ((EXIT_POLICY_OPTION_MAX << 1) - 1) typedef enum firewall_connection_t { FIREWALL_OR_CONNECTION = 0, diff --git a/src/test/test_policy.c b/src/test/test_policy.c index 22f473f27..f2d42b956 100644 --- a/src/test/test_policy.c +++ b/src/test/test_policy.c @@ -32,12 +32,14 @@ test_short_policy_parse(const char *input, short_policy_free(short_policy); } -/** Helper: Parse the exit policy string in policy_str, and make sure - * that policies_summarize() produces the string expected_summary from - * it. */ +/** Helper: Parse the exit policy string in policy_str with + * options, and make sure that policies_summarize() produces the string + * expected_summary from it when called with family. */ static void -test_policy_summary_helper(const char *policy_str, - const char *expected_summary) +test_policy_summary_helper_family_flags(const char *policy_str, + const char *expected_summary, + sa_family_t family, + exit_policy_parser_cfg_t options) { config_line_t line; smartlist_t *policy = smartlist_new(); @@ -45,17 +47,17 @@ test_policy_summary_helper(const char *policy_str, char *summary_after = NULL; int r; short_policy_t *short_policy = NULL; + int success = 0; line.key = (char*)"foo"; line.value = (char *)policy_str; line.next = NULL; r = policies_parse_exit_policy(&line, &policy, - EXIT_POLICY_IPV6_ENABLED | - EXIT_POLICY_ADD_DEFAULT, NULL); + options, NULL); tt_int_op(r,OP_EQ, 0); - summary = policy_summarize(policy, AF_INET); + summary = policy_summarize(policy, family); tt_assert(summary != NULL); tt_str_op(summary,OP_EQ, expected_summary); @@ -65,7 +67,12 @@ test_policy_summary_helper(const char *policy_str, summary_after = write_short_policy(short_policy); tt_str_op(summary,OP_EQ, summary_after); + success = 1; done: + /* If we don't print the flags on failure, it's very hard to diagnose bugs */ + if (!success) + TT_DECLARE("CTXT", ("\n IPv%d\n Options: %x\n Policy: %s", + family == AF_INET ? 4 : 6, options, policy_str)); tor_free(summary_after); tor_free(summary); if (policy) @@ -73,6 +80,50 @@ test_policy_summary_helper(const char *policy_str, short_policy_free(short_policy); } +/** Like test_policy_summary_helper_family_flags, but tries all the different + * flag combinations */ +static void +test_policy_summary_helper_family(const char *policy_str, + const char *expected_summary, + sa_family_t family) +{ + for (exit_policy_parser_cfg_t opt = 0; + opt <= EXIT_POLICY_OPTION_ALL; + opt++) { + if (family == AF_INET6 && !(opt & EXIT_POLICY_IPV6_ENABLED)) + /* Skip the test: IPv6 addresses need IPv6 enabled */ + continue; + + if (opt & EXIT_POLICY_REJECT_LOCAL_INTERFACES) + /* Skip the test: local interfaces are machine-specific */ + continue; + + test_policy_summary_helper_family_flags(policy_str, expected_summary, + family, opt); + } +} + +/** Like test_policy_summary_helper_family, but uses expected_summary for + * both IPv4 and IPv6. */ +static void +test_policy_summary_helper(const char *policy_str, + const char *expected_summary) +{ + test_policy_summary_helper_family(policy_str, expected_summary, AF_INET); + test_policy_summary_helper_family(policy_str, expected_summary, AF_INET6); +} + +/** Like test_policy_summary_helper_family, but uses expected_summary4 for + * IPv4 and expected_summary6 for IPv6. */ +static void +test_policy_summary_helper6(const char *policy_str, + const char *expected_summary4, + const char *expected_summary6) +{ + test_policy_summary_helper_family(policy_str, expected_summary4, AF_INET); + test_policy_summary_helper_family(policy_str, expected_summary6, AF_INET6); +} + /** Run unit tests for generating summary lines of exit policies */ static void test_policies_general(void *arg) @@ -394,13 +445,14 @@ test_policies_general(void *arg) "reject 14.0.0.0/9:80," "reject 15.0.0.0:81," "accept *:*", "accept 1-65535"); - test_policy_summary_helper("reject 11.0.0.0/9:80," - "reject 12.0.0.0/9:80," - "reject 13.0.0.0/9:80," - "reject 14.0.0.0/9:80," - "reject 15.0.0.0:80," - "accept *:*", - "reject 80"); + test_policy_summary_helper6("reject 11.0.0.0/9:80," + "reject 12.0.0.0/9:80," + "reject 13.0.0.0/9:80," + "reject 14.0.0.0/9:80," + "reject 15.0.0.0:80," + "accept *:*", + "reject 80", + "accept 1-65535"); /* no exits */ test_policy_summary_helper("accept 11.0.0.0/9:80," "reject *:*", @@ -431,6 +483,458 @@ test_policies_general(void *arg) "reject *:7," "accept *:*", "reject 1,3,5,7"); + /* long policies */ + /* standard long policy on many exits */ + test_policy_summary_helper("accept *:20-23," + "accept *:43," + "accept *:53," + "accept *:79-81," + "accept *:88," + "accept *:110," + "accept *:143," + "accept *:194," + "accept *:220," + "accept *:389," + "accept *:443," + "accept *:464," + "accept *:531," + "accept *:543-544," + "accept *:554," + "accept *:563," + "accept *:636," + "accept *:706," + "accept *:749," + "accept *:873," + "accept *:902-904," + "accept *:981," + "accept *:989-995," + "accept *:1194," + "accept *:1220," + "accept *:1293," + "accept *:1500," + "accept *:1533," + "accept *:1677," + "accept *:1723," + "accept *:1755," + "accept *:1863," + "accept *:2082," + "accept *:2083," + "accept *:2086-2087," + "accept *:2095-2096," + "accept *:2102-2104," + "accept *:3128," + "accept *:3389," + "accept *:3690," + "accept *:4321," + "accept *:4643," + "accept *:5050," + "accept *:5190," + "accept *:5222-5223," + "accept *:5228," + "accept *:5900," + "accept *:6660-6669," + "accept *:6679," + "accept *:6697," + "accept *:8000," + "accept *:8008," + "accept *:8074," + "accept *:8080," + "accept *:8087-8088," + "accept *:8332-8333," + "accept *:8443," + "accept *:8888," + "accept *:9418," + "accept *:9999," + "accept *:10000," + "accept *:11371," + "accept *:12350," + "accept *:19294," + "accept *:19638," + "accept *:23456," + "accept *:33033," + "accept *:64738," + "reject *:*", + "accept 20-23,43,53,79-81,88,110,143,194,220,389," + "443,464,531,543-544,554,563,636,706,749,873," + "902-904,981,989-995,1194,1220,1293,1500,1533," + "1677,1723,1755,1863,2082-2083,2086-2087," + "2095-2096,2102-2104,3128,3389,3690,4321,4643," + "5050,5190,5222-5223,5228,5900,6660-6669,6679," + "6697,8000,8008,8074,8080,8087-8088,8332-8333," + "8443,8888,9418,9999-10000,11371,12350,19294," + "19638,23456,33033,64738"); + /* short policy with configured addresses */ + test_policy_summary_helper("reject 149.56.1.1:*," + "reject [2607:5300:1:1::1:0]:*," + "accept *:80," + "accept *:443," + "reject *:*", + "accept 80,443"); + /* short policy with configured and local interface addresses */ + test_policy_summary_helper("reject 149.56.1.0:*," + "reject 149.56.1.1:*," + "reject 149.56.1.2:*," + "reject 149.56.1.3:*," + "reject 149.56.1.4:*," + "reject 149.56.1.5:*," + "reject 149.56.1.6:*," + "reject 149.56.1.7:*," + "reject [2607:5300:1:1::1:0]:*," + "reject [2607:5300:1:1::1:1]:*," + "reject [2607:5300:1:1::1:2]:*," + "reject [2607:5300:1:1::1:3]:*," + "reject [2607:5300:1:1::2:0]:*," + "reject [2607:5300:1:1::2:1]:*," + "reject [2607:5300:1:1::2:2]:*," + "reject [2607:5300:1:1::2:3]:*," + "accept *:80," + "accept *:443," + "reject *:*", + "accept 80,443"); + /* short policy with configured netblocks */ + test_policy_summary_helper("reject 149.56.0.0/16," + "reject6 2607:5300::/32," + "reject6 2608:5300::/64," + "reject6 2609:5300::/96," + "accept *:80," + "accept *:443," + "reject *:*", + "accept 80,443"); + /* short policy with large netblocks that do not count as a rejection */ + test_policy_summary_helper("reject 148.0.0.0/7," + "reject6 2600::/16," + "accept *:80," + "accept *:443," + "reject *:*", + "accept 80,443"); + /* short policy with large netblocks that count as a rejection */ + test_policy_summary_helper("reject 148.0.0.0/6," + "reject6 2600::/15," + "accept *:80," + "accept *:443," + "reject *:*", + "reject 1-65535"); + /* short policy with huge netblocks that count as a rejection */ + test_policy_summary_helper("reject 128.0.0.0/1," + "reject6 8000::/1," + "accept *:80," + "accept *:443," + "reject *:*", + "reject 1-65535"); + /* short policy which blocks everything using netblocks */ + test_policy_summary_helper("reject 0.0.0.0/0," + "reject6 ::/0," + "accept *:80," + "accept *:443," + "reject *:*", + "reject 1-65535"); + /* short policy which has repeated redundant netblocks */ + test_policy_summary_helper("reject 0.0.0.0/0," + "reject 0.0.0.0/0," + "reject 0.0.0.0/0," + "reject 0.0.0.0/0," + "reject 0.0.0.0/0," + "reject6 ::/0," + "reject6 ::/0," + "reject6 ::/0," + "reject6 ::/0," + "reject6 ::/0," + "accept *:80," + "accept *:443," + "reject *:*", + "reject 1-65535"); + + /* longest possible policy + * (1-2,4-5,... is longer, but gets reduced to 3,6,... ) + * Going all the way to 65535 is incredibly slow, so we just go slightly + * more than the expected length */ + test_policy_summary_helper("accept *:1," + "accept *:3," + "accept *:5," + "accept *:7," + "accept *:9," + "accept *:11," + "accept *:13," + "accept *:15," + "accept *:17," + "accept *:19," + "accept *:21," + "accept *:23," + "accept *:25," + "accept *:27," + "accept *:29," + "accept *:31," + "accept *:33," + "accept *:35," + "accept *:37," + "accept *:39," + "accept *:41," + "accept *:43," + "accept *:45," + "accept *:47," + "accept *:49," + "accept *:51," + "accept *:53," + "accept *:55," + "accept *:57," + "accept *:59," + "accept *:61," + "accept *:63," + "accept *:65," + "accept *:67," + "accept *:69," + "accept *:71," + "accept *:73," + "accept *:75," + "accept *:77," + "accept *:79," + "accept *:81," + "accept *:83," + "accept *:85," + "accept *:87," + "accept *:89," + "accept *:91," + "accept *:93," + "accept *:95," + "accept *:97," + "accept *:99," + "accept *:101," + "accept *:103," + "accept *:105," + "accept *:107," + "accept *:109," + "accept *:111," + "accept *:113," + "accept *:115," + "accept *:117," + "accept *:119," + "accept *:121," + "accept *:123," + "accept *:125," + "accept *:127," + "accept *:129," + "accept *:131," + "accept *:133," + "accept *:135," + "accept *:137," + "accept *:139," + "accept *:141," + "accept *:143," + "accept *:145," + "accept *:147," + "accept *:149," + "accept *:151," + "accept *:153," + "accept *:155," + "accept *:157," + "accept *:159," + "accept *:161," + "accept *:163," + "accept *:165," + "accept *:167," + "accept *:169," + "accept *:171," + "accept *:173," + "accept *:175," + "accept *:177," + "accept *:179," + "accept *:181," + "accept *:183," + "accept *:185," + "accept *:187," + "accept *:189," + "accept *:191," + "accept *:193," + "accept *:195," + "accept *:197," + "accept *:199," + "accept *:201," + "accept *:203," + "accept *:205," + "accept *:207," + "accept *:209," + "accept *:211," + "accept *:213," + "accept *:215," + "accept *:217," + "accept *:219," + "accept *:221," + "accept *:223," + "accept *:225," + "accept *:227," + "accept *:229," + "accept *:231," + "accept *:233," + "accept *:235," + "accept *:237," + "accept *:239," + "accept *:241," + "accept *:243," + "accept *:245," + "accept *:247," + "accept *:249," + "accept *:251," + "accept *:253," + "accept *:255," + "accept *:257," + "accept *:259," + "accept *:261," + "accept *:263," + "accept *:265," + "accept *:267," + "accept *:269," + "accept *:271," + "accept *:273," + "accept *:275," + "accept *:277," + "accept *:279," + "accept *:281," + "accept *:283," + "accept *:285," + "accept *:287," + "accept *:289," + "accept *:291," + "accept *:293," + "accept *:295," + "accept *:297," + "accept *:299," + "accept *:301," + "accept *:303," + "accept *:305," + "accept *:307," + "accept *:309," + "accept *:311," + "accept *:313," + "accept *:315," + "accept *:317," + "accept *:319," + "accept *:321," + "accept *:323," + "accept *:325," + "accept *:327," + "accept *:329," + "accept *:331," + "accept *:333," + "accept *:335," + "accept *:337," + "accept *:339," + "accept *:341," + "accept *:343," + "accept *:345," + "accept *:347," + "accept *:349," + "accept *:351," + "accept *:353," + "accept *:355," + "accept *:357," + "accept *:359," + "accept *:361," + "accept *:363," + "accept *:365," + "accept *:367," + "accept *:369," + "accept *:371," + "accept *:373," + "accept *:375," + "accept *:377," + "accept *:379," + "accept *:381," + "accept *:383," + "accept *:385," + "accept *:387," + "accept *:389," + "accept *:391," + "accept *:393," + "accept *:395," + "accept *:397," + "accept *:399," + "accept *:401," + "accept *:403," + "accept *:405," + "accept *:407," + "accept *:409," + "accept *:411," + "accept *:413," + "accept *:415," + "accept *:417," + "accept *:419," + "accept *:421," + "accept *:423," + "accept *:425," + "accept *:427," + "accept *:429," + "accept *:431," + "accept *:433," + "accept *:435," + "accept *:437," + "accept *:439," + "accept *:441," + "accept *:443," + "accept *:445," + "accept *:447," + "accept *:449," + "accept *:451," + "accept *:453," + "accept *:455," + "accept *:457," + "accept *:459," + "accept *:461," + "accept *:463," + "accept *:465," + "accept *:467," + "accept *:469," + "accept *:471," + "accept *:473," + "accept *:475," + "accept *:477," + "accept *:479," + "accept *:481," + "accept *:483," + "accept *:485," + "accept *:487," + "accept *:489," + "accept *:491," + "accept *:493," + "accept *:495," + "accept *:497," + "accept *:499," + "accept *:501," + "accept *:503," + "accept *:505," + "accept *:507," + "accept *:509," + "accept *:511," + "accept *:513," + "accept *:515," + "accept *:517," + "accept *:519," + "accept *:521," + "accept *:523," + "accept *:525," + "accept *:527," + "accept *:529," + "reject *:*", + "accept 1,3,5,7,9,11,13,15,17,19,21,23,25,27,29," + "31,33,35,37,39,41,43,45,47,49,51,53,55,57,59,61," + "63,65,67,69,71,73,75,77,79,81,83,85,87,89,91,93," + "95,97,99,101,103,105,107,109,111,113,115,117," + "119,121,123,125,127,129,131,133,135,137,139,141," + "143,145,147,149,151,153,155,157,159,161,163,165," + "167,169,171,173,175,177,179,181,183,185,187,189," + "191,193,195,197,199,201,203,205,207,209,211,213," + "215,217,219,221,223,225,227,229,231,233,235,237," + "239,241,243,245,247,249,251,253,255,257,259,261," + "263,265,267,269,271,273,275,277,279,281,283,285," + "287,289,291,293,295,297,299,301,303,305,307,309," + "311,313,315,317,319,321,323,325,327,329,331,333," + "335,337,339,341,343,345,347,349,351,353,355,357," + "359,361,363,365,367,369,371,373,375,377,379,381," + "383,385,387,389,391,393,395,397,399,401,403,405," + "407,409,411,413,415,417,419,421,423,425,427,429," + "431,433,435,437,439,441,443,445,447,449,451,453," + "455,457,459,461,463,465,467,469,471,473,475,477," + "479,481,483,485,487,489,491,493,495,497,499,501," + "503,505,507,509,511,513,515,517,519,521,523"); /* Short policies with unrecognized formats should get accepted. */ test_short_policy_parse("accept fred,2,3-5", "accept 2,3-5");