From bd6ab90ad4b40a64c604c1a4b6b37da6991fad9e Mon Sep 17 00:00:00 2001 From: teor Date: Tue, 14 Apr 2020 15:08:42 +1000 Subject: [PATCH 01/28] core/or: Support IPv6 EXTEND2 cells Allow clients and relays to send dual-stack and IPv6-only EXTEND2 cells. Parse dual-stack and IPv6-only EXTEND2 cells on relays. Relays do not make connections or extend circuits via IPv6: that's the next step. Closes ticket 33901. --- changes/ticket33901 | 4 ++++ src/core/or/onion.c | 41 ++++++++++++++++++++++++++++++------ src/lib/net/address.c | 14 ++++++++++++ src/lib/net/address.h | 1 + src/test/test_cell_formats.c | 39 ++++++++++++++++++++++------------ 5 files changed, 79 insertions(+), 20 deletions(-) create mode 100644 changes/ticket33901 diff --git a/changes/ticket33901 b/changes/ticket33901 new file mode 100644 index 0000000000..b824cc5b07 --- /dev/null +++ b/changes/ticket33901 @@ -0,0 +1,4 @@ + o Minor features (IPv6, relay): + - Allow clients and relays to send dual-stack and IPv6-only EXTEND2 cells. + Parse dual-stack and IPv6-only EXTEND2 cells on relays. + Closes ticket 33901. diff --git a/src/core/or/onion.c b/src/core/or/onion.c index 45144b5e6c..543d9f3e47 100644 --- a/src/core/or/onion.c +++ b/src/core/or/onion.c @@ -240,11 +240,21 @@ created_cell_parse(created_cell_t *cell_out, const cell_t *cell_in) static int check_extend_cell(const extend_cell_t *cell) { + const bool is_extend2 = (cell->cell_type == RELAY_COMMAND_EXTEND2); + if (tor_digest_is_zero((const char*)cell->node_id)) return -1; - /* We don't currently allow EXTEND2 cells without an IPv4 address */ - if (tor_addr_family(&cell->orport_ipv4.addr) == AF_UNSPEC) - return -1; + if (tor_addr_family(&cell->orport_ipv4.addr) == AF_UNSPEC) { + /* EXTEND cells must have an IPv4 address. */ + if (!is_extend2) { + return -1; + } + /* EXTEND2 cells must have at least one IP address. + * It can be IPv4 or IPv6. */ + if (tor_addr_family(&cell->orport_ipv6.addr) == AF_UNSPEC) { + return -1; + } + } if (cell->create_cell.cell_type == CELL_CREATE) { if (cell->cell_type != RELAY_COMMAND_EXTEND) return -1; @@ -364,7 +374,12 @@ extend_cell_from_extend2_cell_body(extend_cell_t *cell_out, } } - if (!found_rsa_id || !found_ipv4) /* These are mandatory */ + /* EXTEND2 cells must have an RSA ID */ + if (!found_rsa_id) + return -1; + + /* EXTEND2 cells must have at least one IP address */ + if (!found_ipv4 && !found_ipv6) return -1; return create_cell_from_create2_cell_body(&cell_out->create_cell, @@ -620,12 +635,13 @@ extend_cell_format(uint8_t *command_out, uint16_t *len_out, break; case RELAY_COMMAND_EXTEND2: { - uint8_t n_specifiers = 2; + uint8_t n_specifiers = 1; *command_out = RELAY_COMMAND_EXTEND2; extend2_cell_body_t *cell = extend2_cell_body_new(); link_specifier_t *ls; - { - /* IPv4 specifier first. */ + if (tor_addr_port_is_valid_ap(&cell_in->orport_ipv4, 0)) { + /* Maybe IPv4 specifier first. */ + ++n_specifiers; ls = link_specifier_new(); extend2_cell_body_add_ls(cell, ls); ls->ls_type = LS_IPV4; @@ -651,6 +667,17 @@ extend_cell_format(uint8_t *command_out, uint16_t *len_out, ls->ls_len = 32; memcpy(ls->un_ed25519_id, cell_in->ed_pubkey.pubkey, 32); } + if (tor_addr_port_is_valid_ap(&cell_in->orport_ipv6, 0)) { + /* Then maybe IPv6 specifier. */ + ++n_specifiers; + ls = link_specifier_new(); + extend2_cell_body_add_ls(cell, ls); + ls->ls_type = LS_IPV6; + ls->ls_len = 18; + tor_addr_get_ipv6_bytes((char *)ls->un_ipv6_addr, + &cell_in->orport_ipv6.addr); + ls->un_ipv6_port = cell_in->orport_ipv6.port; + } cell->n_spec = n_specifiers; /* Now, the handshake */ diff --git a/src/lib/net/address.c b/src/lib/net/address.c index 5dbef6a79d..5fe9abca1b 100644 --- a/src/lib/net/address.c +++ b/src/lib/net/address.c @@ -903,6 +903,20 @@ tor_addr_from_in6(tor_addr_t *dest, const struct in6_addr *in6) tor_addr_from_ipv6_bytes(dest, (const char*)in6->s6_addr); } +/** Set the 16 bytes at dest to equal the IPv6 address src. + * src must be an IPv6 address, if it is not, log a warning, and clear + * dest. */ +void +tor_addr_get_ipv6_bytes(char *dest, const tor_addr_t *src) +{ + tor_assert(dest); + tor_assert(src); + memset(dest, 0, 16); + IF_BUG_ONCE(src->family != AF_INET6) + return; + memcpy(dest, src->addr.in6_addr.s6_addr, 16); +} + /** Copy a tor_addr_t from src to dest. */ void diff --git a/src/lib/net/address.h b/src/lib/net/address.h index 4984494939..f1c2233103 100644 --- a/src/lib/net/address.h +++ b/src/lib/net/address.h @@ -303,6 +303,7 @@ void tor_addr_from_ipv6_bytes(tor_addr_t *dest, const char *bytes); #define tor_addr_from_in(dest, in) \ tor_addr_from_ipv4n((dest), (in)->s_addr); void tor_addr_from_in6(tor_addr_t *dest, const struct in6_addr *in6); +void tor_addr_get_ipv6_bytes(char *dest, const tor_addr_t *src); int tor_addr_is_null(const tor_addr_t *addr); int tor_addr_is_loopback(const tor_addr_t *addr); diff --git a/src/test/test_cell_formats.c b/src/test/test_cell_formats.c index 8d6d1940fd..3d3f151fcb 100644 --- a/src/test/test_cell_formats.c +++ b/src/test/test_cell_formats.c @@ -713,16 +713,20 @@ test_cfmt_extend_cells(void *arg) tt_mem_op(cc->onionskin,OP_EQ, b, 99+20); tt_int_op(0, OP_EQ, extend_cell_format(&p2_cmd, &p2_len, p2, &ec)); tt_int_op(p2_cmd, OP_EQ, RELAY_COMMAND_EXTEND2); - /* We'll generate it minus the IPv6 address and minus the konami code */ - tt_int_op(p2_len, OP_EQ, 89+99-34-20); + /* We'll generate it minus the konami code */ + tt_int_op(p2_len, OP_EQ, 89+99-34); test_memeq_hex(p2, - /* Two items: one that same darn IP address. */ - "02000612F40001F0F1" - /* The next is a digest : anthropomorphization */ - "0214616e7468726f706f6d6f727068697a6174696f6e" + /* Three items */ + "03" + /* IPv4 address */ + "0006" "12F40001" "F0F1" + /* The next is an RSA digest: anthropomorphization */ + "0214" "616e7468726f706f6d6f727068697a6174696f6e" + /*IPv6 address */ + "0112" "20020000000000000000000000f0c51e" "1112" /* Now the handshake prologue */ "01050063"); - tt_mem_op(p2+1+8+22+4,OP_EQ, b, 99+20); + tt_mem_op(p2+1+8+22+20+4, OP_EQ, b, 99+20); tt_int_op(0, OP_EQ, create_cell_format_relayed(&cell, cc)); /* Now let's add an ed25519 key to that extend2 cell. */ @@ -732,22 +736,31 @@ test_cfmt_extend_cells(void *arg) /* As before, since we aren't extending by ed25519. */ get_options_mutable()->ExtendByEd25519ID = 0; tt_int_op(0, OP_EQ, extend_cell_format(&p2_cmd, &p2_len, p2, &ec)); - tt_int_op(p2_len, OP_EQ, 89+99-34-20); + tt_int_op(p2_len, OP_EQ, 89+99-34); test_memeq_hex(p2, - "02000612F40001F0F1" + "03" + "000612F40001F0F1" "0214616e7468726f706f6d6f727068697a6174696f6e" + "011220020000000000000000000000f0c51e1112" "01050063"); /* Now try with the ed25519 ID. */ get_options_mutable()->ExtendByEd25519ID = 1; tt_int_op(0, OP_EQ, extend_cell_format(&p2_cmd, &p2_len, p2, &ec)); - tt_int_op(p2_len, OP_EQ, 89+99-34-20 + 34); + tt_int_op(p2_len, OP_EQ, 89+99); test_memeq_hex(p2, - "03000612F40001F0F1" + /* Four items */ + "04" + /* IPv4 address */ + "0006" "12F40001" "F0F1" + /* The next is an RSA digest: anthropomorphization */ "0214616e7468726f706f6d6f727068697a6174696f6e" - // ed digest follows: + /* Then an ed public key: brownshoesdontmakeit/brownshoesd */ "0320" "62726f776e73686f6573646f6e746d616b656" "9742f62726f776e73686f657364" + /*IPv6 address */ + "0112" "20020000000000000000000000f0c51e" "1112" + /* Now the handshake prologue */ "01050063"); /* Can we parse that? Did the key come through right? */ memset(&ec, 0, sizeof(ec)); @@ -816,7 +829,7 @@ test_cfmt_extend_cells(void *arg) memcpy(p+1, "\x02\x14" "anarchoindividualist", 22); memcpy(p+23, "\x01\x12" "xxxxxxxxxxxxxxxxYY", 18); memcpy(p+41, "\xff\xff\x00\x20", 4); - tt_int_op(-1, OP_EQ, extend_cell_parse(&ec, RELAY_COMMAND_EXTEND2, + tt_int_op(0, OP_EQ, extend_cell_parse(&ec, RELAY_COMMAND_EXTEND2, p, sizeof(p))); /* Running out of space in specifiers */ From f6c8a8c5387eaf35470f08e9cd9b35f05bdc1057 Mon Sep 17 00:00:00 2001 From: teor Date: Tue, 14 Apr 2020 18:44:06 +1000 Subject: [PATCH 02/28] test/cell_formats: Expand the IPv6-only EXTEND2 test Part of 33901. --- src/test/test_cell_formats.c | 41 ++++++++++++++++++++++++++++++------ 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/src/test/test_cell_formats.c b/src/test/test_cell_formats.c index 3d3f151fcb..f9ff101c98 100644 --- a/src/test/test_cell_formats.c +++ b/src/test/test_cell_formats.c @@ -769,6 +769,40 @@ test_cfmt_extend_cells(void *arg) tt_mem_op("brownshoesdontmakeit/brownshoesd", OP_EQ, ec.ed_pubkey.pubkey, 32); + /* Now try IPv6 without IPv4 */ + memset(p, 0, sizeof(p)); + memcpy(p, "\x02", 1); + memcpy(p+1, "\x02\x14" "anthropomorphization", 22); + memcpy(p+23, "\x01\x12" "xxxxxxxxxxxxxxxxYY", 20); + memcpy(p+43, "\xff\xff\x00\x20", 4); + tt_int_op(0, OP_EQ, extend_cell_parse(&ec, RELAY_COMMAND_EXTEND2, + p, sizeof(p))); + tt_int_op(RELAY_COMMAND_EXTEND2, OP_EQ, ec.cell_type); + tt_assert(fast_mem_is_zero((const char *)&ec.orport_ipv4.addr, + sizeof(tor_addr_t))); + tt_int_op(0, OP_EQ, ec.orport_ipv4.port); + tt_str_op("7878:7878:7878:7878:7878:7878:7878:7878", + OP_EQ, fmt_addr(&ec.orport_ipv6.addr)); + tt_int_op(22873, OP_EQ, ec.orport_ipv6.port); + tt_assert(ed25519_public_key_is_zero(&ec.ed_pubkey)); + tt_mem_op(ec.node_id,OP_EQ, "anthropomorphization", 20); + tt_int_op(cc->cell_type, OP_EQ, CELL_CREATE2); + tt_int_op(cc->handshake_type, OP_EQ, 0xffff); + tt_int_op(cc->handshake_len, OP_EQ, 32); + tt_int_op(0, OP_EQ, extend_cell_format(&p2_cmd, &p2_len, p2, &ec)); + tt_int_op(p2_cmd, OP_EQ, RELAY_COMMAND_EXTEND2); + tt_int_op(p2_len, OP_EQ, 47+32); + test_memeq_hex(p2, + /* Two items */ + "02" + /* The next is an RSA digest: anthropomorphization */ + "0214" "616e7468726f706f6d6f727068697a6174696f6e" + /*IPv6 address */ + "0112" "78787878787878787878787878787878" "5959" + /* Now the handshake prologue */ + "ffff0020"); + tt_int_op(0, OP_EQ, create_cell_format_relayed(&cell, cc)); + /* == Now try parsing some junk */ /* Try a too-long handshake */ @@ -824,13 +858,6 @@ test_cfmt_extend_cells(void *arg) memcpy(p+48, "\xff\xff\x00\x20", 4); tt_int_op(-1, OP_EQ, extend_cell_parse(&ec, RELAY_COMMAND_EXTEND2, p, sizeof(p))); - memset(p, 0, sizeof(p)); - memcpy(p, "\x02", 1); - memcpy(p+1, "\x02\x14" "anarchoindividualist", 22); - memcpy(p+23, "\x01\x12" "xxxxxxxxxxxxxxxxYY", 18); - memcpy(p+41, "\xff\xff\x00\x20", 4); - tt_int_op(0, OP_EQ, extend_cell_parse(&ec, RELAY_COMMAND_EXTEND2, - p, sizeof(p))); /* Running out of space in specifiers */ memset(p,0,sizeof(p)); From 587a7fbcf62447c7ade05c57220f1bfe2f3acdc1 Mon Sep 17 00:00:00 2001 From: teor Date: Tue, 14 Apr 2020 15:53:17 +1000 Subject: [PATCH 03/28] core/or: Check extends for zero addresses and ports Check for invalid zero IPv4 addresses and ports, when sending and receiving extend cells. Fixes bug 33900; bugfix on 0.2.4.8-alpha. --- changes/bug33900 | 3 +++ src/core/or/onion.c | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) create mode 100644 changes/bug33900 diff --git a/changes/bug33900 b/changes/bug33900 new file mode 100644 index 0000000000..c1649d2284 --- /dev/null +++ b/changes/bug33900 @@ -0,0 +1,3 @@ + o Minor bugfixes (IPv4, relay): + - Check for invalid zero IPv4 addresses and ports, when sending and + receiving extend cells. Fixes bug 33900; bugfix on 0.2.4.8-alpha. diff --git a/src/core/or/onion.c b/src/core/or/onion.c index 543d9f3e47..d73f981d2c 100644 --- a/src/core/or/onion.c +++ b/src/core/or/onion.c @@ -244,14 +244,14 @@ check_extend_cell(const extend_cell_t *cell) if (tor_digest_is_zero((const char*)cell->node_id)) return -1; - if (tor_addr_family(&cell->orport_ipv4.addr) == AF_UNSPEC) { + if (!tor_addr_port_is_valid_ap(&cell->orport_ipv4, 0)) { /* EXTEND cells must have an IPv4 address. */ if (!is_extend2) { return -1; } /* EXTEND2 cells must have at least one IP address. * It can be IPv4 or IPv6. */ - if (tor_addr_family(&cell->orport_ipv6.addr) == AF_UNSPEC) { + if (!tor_addr_port_is_valid_ap(&cell->orport_ipv6, 0)) { return -1; } } From 07c008c6723644553827140f2f8a863a1e2d071a Mon Sep 17 00:00:00 2001 From: teor Date: Wed, 15 Apr 2020 08:58:32 +1000 Subject: [PATCH 04/28] relay: Refactor address and port checks tor_addr_port_is_valid_ap(ap, 0) checks if the address or port are zero, exactly like the previous code. Preparation for 33817. --- src/feature/relay/circuitbuild_relay.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/feature/relay/circuitbuild_relay.c b/src/feature/relay/circuitbuild_relay.c index 2fa92eeac1..96b46bb65a 100644 --- a/src/feature/relay/circuitbuild_relay.c +++ b/src/feature/relay/circuitbuild_relay.c @@ -139,7 +139,7 @@ circuit_extend_lspec_valid_helper(const struct extend_cell_t *ec, return -1; } - if (!ec->orport_ipv4.port || tor_addr_is_null(&ec->orport_ipv4.addr)) { + if (!tor_addr_port_is_valid_ap(&ec->orport_ipv4, 0)) { log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, "Client asked me to extend to zero destination port or addr."); return -1; From ffc2fd001a306894bb8082d81de9b136937ef124 Mon Sep 17 00:00:00 2001 From: teor Date: Wed, 15 Apr 2020 09:13:16 +1000 Subject: [PATCH 05/28] relay: Refactor address checks into a function No behaviour change. Part of 33817. --- src/feature/relay/circuitbuild_relay.c | 39 +++++++++++++++++++------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/src/feature/relay/circuitbuild_relay.c b/src/feature/relay/circuitbuild_relay.c index 96b46bb65a..dd38a28258 100644 --- a/src/feature/relay/circuitbuild_relay.c +++ b/src/feature/relay/circuitbuild_relay.c @@ -119,6 +119,33 @@ circuit_extend_add_ed25519_helper(struct extend_cell_t *ec) return 0; } +/* Check if the address and port in the tor_addr_port_t ap are valid, + * and are allowed by the current ExtendAllowPrivateAddresses config. + * + * If they are valid, return 0. + * Otherwise, if they are invalid, log a warning at log_level, + * and return -1. + */ +static int +circuit_extend_addr_port_helper(const struct tor_addr_port_t *ap, + int log_level) +{ + if (!tor_addr_port_is_valid_ap(ap, 0)) { + log_fn(log_level, LD_PROTOCOL, + "Client asked me to extend to zero destination port or addr."); + return -1; + } + + if (tor_addr_is_internal(&ap->addr, 0) && + !get_options()->ExtendAllowPrivateAddresses) { + log_fn(log_level, LD_PROTOCOL, + "Client asked me to extend to a private address."); + return -1; + } + + return 0; +} + /* Before replying to an extend cell, check the link specifiers in the extend * cell ec, which was received on the circuit circ. * @@ -139,16 +166,8 @@ circuit_extend_lspec_valid_helper(const struct extend_cell_t *ec, return -1; } - if (!tor_addr_port_is_valid_ap(&ec->orport_ipv4, 0)) { - log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, - "Client asked me to extend to zero destination port or addr."); - return -1; - } - - if (tor_addr_is_internal(&ec->orport_ipv4.addr, 0) && - !get_options()->ExtendAllowPrivateAddresses) { - log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, - "Client asked me to extend to a private address."); + if (circuit_extend_addr_port_helper(&ec->orport_ipv4, + LOG_PROTOCOL_WARN) < 0) { return -1; } From e9d04b05c6ba3930d55dc6c7e512e082ff67ebfb Mon Sep 17 00:00:00 2001 From: teor Date: Wed, 15 Apr 2020 09:52:25 +1000 Subject: [PATCH 06/28] net: Remove an extra space in address.h --- src/lib/net/address.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/net/address.h b/src/lib/net/address.h index f1c2233103..186f1fe86a 100644 --- a/src/lib/net/address.h +++ b/src/lib/net/address.h @@ -221,7 +221,7 @@ char *tor_addr_to_str_dup(const tor_addr_t *addr) ATTR_MALLOC; const char *fmt_addr_impl(const tor_addr_t *addr, int decorate); const char *fmt_addrport(const tor_addr_t *addr, uint16_t port); -const char * fmt_addr32(uint32_t addr); +const char *fmt_addr32(uint32_t addr); MOCK_DECL(int,get_interface_address6,(int severity, sa_family_t family, tor_addr_t *addr)); From a72e017e7f69581ceb005d05ce8033a6fd05626e Mon Sep 17 00:00:00 2001 From: teor Date: Wed, 15 Apr 2020 09:52:57 +1000 Subject: [PATCH 07/28] net: Add fmt_addrport_ap() and fmt_addr_family() Add fmt_addrport_ap(), a macro that takes a tor_addr_port_t, and uses it to call fmt_addrport(). Add fmt_addr_family(), a function that returns a string constant describing the address family. Utility functions for 33817. --- src/lib/net/address.c | 33 +++++++++++++++++++++++++++++++++ src/lib/net/address.h | 2 ++ 2 files changed, 35 insertions(+) diff --git a/src/lib/net/address.c b/src/lib/net/address.c index 5fe9abca1b..9a90093aef 100644 --- a/src/lib/net/address.c +++ b/src/lib/net/address.c @@ -1206,6 +1206,39 @@ fmt_addr32(uint32_t addr) return buf; } +/** Return a string representing the family of addr. + * + * This string is a string constant, and must not be freed. + * This function is thread-safe. + */ +const char * +fmt_addr_family(const tor_addr_t *addr) +{ + static int default_bug_once = 0; + + IF_BUG_ONCE(!addr) + return "NULL pointer"; + + switch (tor_addr_family(addr)) { + case AF_INET6: + return "IPv6"; + case AF_INET: + return "IPv4"; + case AF_UNIX: + return "UNIX socket"; + case AF_UNSPEC: + return "unspecified"; + default: + if (!default_bug_once) { + log_warn(LD_BUG, "Called with unknown address family %d", + (int)tor_addr_family(addr)); + default_bug_once = 1; + } + return "unknown"; + } + //return "(unreachable code)"; +} + /** Convert the string in src to a tor_addr_t addr. The string * may be an IPv4 address, or an IPv6 address surrounded by square brackets. * diff --git a/src/lib/net/address.h b/src/lib/net/address.h index 186f1fe86a..611d1ca1ee 100644 --- a/src/lib/net/address.h +++ b/src/lib/net/address.h @@ -221,7 +221,9 @@ char *tor_addr_to_str_dup(const tor_addr_t *addr) ATTR_MALLOC; const char *fmt_addr_impl(const tor_addr_t *addr, int decorate); const char *fmt_addrport(const tor_addr_t *addr, uint16_t port); +#define fmt_addrport_ap(ap) fmt_addrport(&(ap)->addr, (ap)->port) const char *fmt_addr32(uint32_t addr); +const char *fmt_addr_family(const tor_addr_t *addr); MOCK_DECL(int,get_interface_address6,(int severity, sa_family_t family, tor_addr_t *addr)); From 44f71e08c414f6c7aad6304e24be90d5d320c95b Mon Sep 17 00:00:00 2001 From: teor Date: Wed, 15 Apr 2020 09:55:09 +1000 Subject: [PATCH 08/28] relay: Log the address in circuit protocol warnings Always log the address family in extend protocol warnings. If SafeLogging is 0, also log the address and port. Diagnostics for 33817. --- src/feature/relay/circuitbuild_relay.c | 12 ++++++++++-- src/test/test_circuitbuild.c | 19 ++++++++++--------- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/feature/relay/circuitbuild_relay.c b/src/feature/relay/circuitbuild_relay.c index dd38a28258..05146f1b67 100644 --- a/src/feature/relay/circuitbuild_relay.c +++ b/src/feature/relay/circuitbuild_relay.c @@ -130,16 +130,24 @@ static int circuit_extend_addr_port_helper(const struct tor_addr_port_t *ap, int log_level) { + /* It's safe to print the family. But we don't want to print the address, + * unless specifically configured to do so. (Zero addresses aren't sensitive, + * But some internal addresses might be.)*/ + if (!tor_addr_port_is_valid_ap(ap, 0)) { log_fn(log_level, LD_PROTOCOL, - "Client asked me to extend to zero destination port or addr."); + "Client asked me to extend to a zero destination port or " + "%s address '%s'.", + fmt_addr_family(&ap->addr), safe_str(fmt_addrport_ap(ap))); return -1; } if (tor_addr_is_internal(&ap->addr, 0) && !get_options()->ExtendAllowPrivateAddresses) { log_fn(log_level, LD_PROTOCOL, - "Client asked me to extend to a private address."); + "Client asked me to extend to a private %s address '%s'.", + fmt_addr_family(&ap->addr), + safe_str(fmt_and_decorate_addr(&ap->addr))); return -1; } diff --git a/src/test/test_circuitbuild.c b/src/test/test_circuitbuild.c index 061f39937a..ab5c9c9938 100644 --- a/src/test/test_circuitbuild.c +++ b/src/test/test_circuitbuild.c @@ -521,21 +521,21 @@ test_circuit_extend_lspec_valid(void *arg) /* IPv4 addr or port are 0, these should fail */ tt_int_op(circuit_extend_lspec_valid_helper(ec, circ), OP_EQ, -1); - expect_log_msg("Client asked me to extend to " - "zero destination port or addr.\n"); + expect_log_msg("Client asked me to extend to a zero destination port " + "or unspecified address '[scrubbed]'.\n"); mock_clean_saved_logs(); tor_addr_parse(&ec->orport_ipv4.addr, PUBLIC_IPV4); tt_int_op(circuit_extend_lspec_valid_helper(ec, circ), OP_EQ, -1); - expect_log_msg("Client asked me to extend to " - "zero destination port or addr.\n"); + expect_log_msg("Client asked me to extend to a zero destination port " + "or IPv4 address '[scrubbed]'.\n"); mock_clean_saved_logs(); tor_addr_make_null(&ec->orport_ipv4.addr, AF_INET); ec->orport_ipv4.port = VALID_PORT; tt_int_op(circuit_extend_lspec_valid_helper(ec, circ), OP_EQ, -1); - expect_log_msg("Client asked me to extend to " - "zero destination port or addr.\n"); + expect_log_msg("Client asked me to extend to a zero destination port " + "or IPv4 address '[scrubbed]'.\n"); mock_clean_saved_logs(); ec->orport_ipv4.port = 0; @@ -546,7 +546,8 @@ test_circuit_extend_lspec_valid(void *arg) fake_options->ExtendAllowPrivateAddresses = 0; tt_int_op(circuit_extend_lspec_valid_helper(ec, circ), OP_EQ, -1); - expect_log_msg("Client asked me to extend to a private address.\n"); + expect_log_msg("Client asked me to extend " + "to a private IPv4 address '[scrubbed]'.\n"); mock_clean_saved_logs(); fake_options->ExtendAllowPrivateAddresses = 0; @@ -1002,8 +1003,8 @@ test_circuit_extend(void *arg) tt_int_op(circuit_extend(cell, circ), OP_EQ, -1); tt_int_op(mock_extend_cell_parse_calls, OP_EQ, 1); - expect_log_msg("Client asked me to extend to " - "zero destination port or addr.\n"); + expect_log_msg("Client asked me to extend to a zero destination port " + "or unspecified address '[scrubbed]'.\n"); mock_clean_saved_logs(); mock_extend_cell_parse_calls = 0; From 7cef02ec1fa76f593dfb9bd53f76922225696934 Mon Sep 17 00:00:00 2001 From: teor Date: Wed, 15 Apr 2020 10:07:26 +1000 Subject: [PATCH 09/28] test/circuitbuild: Show bad addresses in some logs Disable SafeLogging for some extend tests, so we can check the actual addresses. Part of 33817. --- src/test/test_circuitbuild.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/test/test_circuitbuild.c b/src/test/test_circuitbuild.c index ab5c9c9938..9322480844 100644 --- a/src/test/test_circuitbuild.c +++ b/src/test/test_circuitbuild.c @@ -525,17 +525,19 @@ test_circuit_extend_lspec_valid(void *arg) "or unspecified address '[scrubbed]'.\n"); mock_clean_saved_logs(); + /* Now ask for the actual address in the logs */ + fake_options->SafeLogging_ = SAFELOG_SCRUB_NONE; tor_addr_parse(&ec->orport_ipv4.addr, PUBLIC_IPV4); tt_int_op(circuit_extend_lspec_valid_helper(ec, circ), OP_EQ, -1); expect_log_msg("Client asked me to extend to a zero destination port " - "or IPv4 address '[scrubbed]'.\n"); + "or IPv4 address '1.2.3.4:0'.\n"); mock_clean_saved_logs(); tor_addr_make_null(&ec->orport_ipv4.addr, AF_INET); ec->orport_ipv4.port = VALID_PORT; tt_int_op(circuit_extend_lspec_valid_helper(ec, circ), OP_EQ, -1); expect_log_msg("Client asked me to extend to a zero destination port " - "or IPv4 address '[scrubbed]'.\n"); + "or IPv4 address '0.0.0.0:4660'.\n"); mock_clean_saved_logs(); ec->orport_ipv4.port = 0; @@ -547,7 +549,7 @@ test_circuit_extend_lspec_valid(void *arg) fake_options->ExtendAllowPrivateAddresses = 0; tt_int_op(circuit_extend_lspec_valid_helper(ec, circ), OP_EQ, -1); expect_log_msg("Client asked me to extend " - "to a private IPv4 address '[scrubbed]'.\n"); + "to a private IPv4 address '0.0.0.1'.\n"); mock_clean_saved_logs(); fake_options->ExtendAllowPrivateAddresses = 0; From bad1181b5d1bef55e060d64a9d4ef9278619495b Mon Sep 17 00:00:00 2001 From: teor Date: Wed, 15 Apr 2020 11:34:12 +1000 Subject: [PATCH 10/28] relay/circuitbuild: Consider IPv6-only extends valid Allow extend cells with IPv6-only link specifiers. Warn and fail if both IPv4 and IPv6 are invalid. Also warn if the IPv4 or IPv6 addresses are unexpectedly internal, but continue with the valid address. Part of 33817. --- src/feature/relay/circuitbuild_relay.c | 50 +++++++--- src/test/test_circuitbuild.c | 125 ++++++++++++++++++++++++- 2 files changed, 161 insertions(+), 14 deletions(-) diff --git a/src/feature/relay/circuitbuild_relay.c b/src/feature/relay/circuitbuild_relay.c index 05146f1b67..080781f719 100644 --- a/src/feature/relay/circuitbuild_relay.c +++ b/src/feature/relay/circuitbuild_relay.c @@ -123,11 +123,14 @@ circuit_extend_add_ed25519_helper(struct extend_cell_t *ec) * and are allowed by the current ExtendAllowPrivateAddresses config. * * If they are valid, return 0. - * Otherwise, if they are invalid, log a warning at log_level, - * and return -1. + * Otherwise, if they are invalid, return -1. + * If log_zero_addrs is true, log warnings about zero addresses at + * log_level. If log_internal_addrs is true, log warnings about + * internal addresses at log_level. */ static int circuit_extend_addr_port_helper(const struct tor_addr_port_t *ap, + bool log_zero_addrs, bool log_internal_addrs, int log_level) { /* It's safe to print the family. But we don't want to print the address, @@ -135,19 +138,23 @@ circuit_extend_addr_port_helper(const struct tor_addr_port_t *ap, * But some internal addresses might be.)*/ if (!tor_addr_port_is_valid_ap(ap, 0)) { - log_fn(log_level, LD_PROTOCOL, - "Client asked me to extend to a zero destination port or " - "%s address '%s'.", - fmt_addr_family(&ap->addr), safe_str(fmt_addrport_ap(ap))); + if (log_zero_addrs) { + log_fn(log_level, LD_PROTOCOL, + "Client asked me to extend to a zero destination port or " + "%s address '%s'.", + fmt_addr_family(&ap->addr), safe_str(fmt_addrport_ap(ap))); + } return -1; } if (tor_addr_is_internal(&ap->addr, 0) && !get_options()->ExtendAllowPrivateAddresses) { - log_fn(log_level, LD_PROTOCOL, - "Client asked me to extend to a private %s address '%s'.", - fmt_addr_family(&ap->addr), - safe_str(fmt_and_decorate_addr(&ap->addr))); + if (log_internal_addrs) { + log_fn(log_level, LD_PROTOCOL, + "Client asked me to extend to a private %s address '%s'.", + fmt_addr_family(&ap->addr), + safe_str(fmt_and_decorate_addr(&ap->addr))); + } return -1; } @@ -174,9 +181,28 @@ circuit_extend_lspec_valid_helper(const struct extend_cell_t *ec, return -1; } - if (circuit_extend_addr_port_helper(&ec->orport_ipv4, - LOG_PROTOCOL_WARN) < 0) { + /* Check the addresses, without logging */ + const int ipv4_valid = + (circuit_extend_addr_port_helper(&ec->orport_ipv4, false, false, 0) == 0); + const int ipv6_valid = + (circuit_extend_addr_port_helper(&ec->orport_ipv6, false, false, 0) == 0); + /* We need at least one valid address */ + if (!ipv4_valid && !ipv6_valid) { + /* Now, log the invalid addresses at protocol warning level */ + circuit_extend_addr_port_helper(&ec->orport_ipv4, true, true, + LOG_PROTOCOL_WARN); + circuit_extend_addr_port_helper(&ec->orport_ipv6, true, true, + LOG_PROTOCOL_WARN); + /* And fail */ return -1; + } else if (!ipv4_valid) { + /* Always log unexpected internal addresses, but go on to use the other + * valid address */ + circuit_extend_addr_port_helper(&ec->orport_ipv4, false, true, + LOG_PROTOCOL_WARN); + } else if (!ipv6_valid) { + circuit_extend_addr_port_helper(&ec->orport_ipv6, false, true, + LOG_PROTOCOL_WARN); } IF_BUG_ONCE(circ->magic != OR_CIRCUIT_MAGIC) { diff --git a/src/test/test_circuitbuild.c b/src/test/test_circuitbuild.c index 9322480844..a8d6323e40 100644 --- a/src/test/test_circuitbuild.c +++ b/src/test/test_circuitbuild.c @@ -474,6 +474,9 @@ mock_get_options(void) #define PUBLIC_IPV4 "1.2.3.4" #define INTERNAL_IPV4 "0.0.0.1" +#define PUBLIC_IPV6 "1234::cdef" +#define INTERNAL_IPV6 "::1" + #define VALID_PORT 0x1234 /* Test the different cases in circuit_extend_lspec_valid_helper(). */ @@ -519,7 +522,7 @@ test_circuit_extend_lspec_valid(void *arg) tor_end_capture_bugs_(); mock_clean_saved_logs(); - /* IPv4 addr or port are 0, these should fail */ + /* IPv4 and IPv6 addr and port are all zero, this should fail */ tt_int_op(circuit_extend_lspec_valid_helper(ec, circ), OP_EQ, -1); expect_log_msg("Client asked me to extend to a zero destination port " "or unspecified address '[scrubbed]'.\n"); @@ -527,21 +530,28 @@ test_circuit_extend_lspec_valid(void *arg) /* Now ask for the actual address in the logs */ fake_options->SafeLogging_ = SAFELOG_SCRUB_NONE; + + /* IPv4 port is 0, IPv6 addr and port are both zero, this should fail */ tor_addr_parse(&ec->orport_ipv4.addr, PUBLIC_IPV4); tt_int_op(circuit_extend_lspec_valid_helper(ec, circ), OP_EQ, -1); expect_log_msg("Client asked me to extend to a zero destination port " "or IPv4 address '1.2.3.4:0'.\n"); mock_clean_saved_logs(); - tor_addr_make_null(&ec->orport_ipv4.addr, AF_INET); + tor_addr_port_make_null_ap(&ec->orport_ipv4, AF_INET); + tor_addr_port_make_null_ap(&ec->orport_ipv6, AF_INET6); + /* IPv4 addr is 0, IPv6 addr and port are both zero, this should fail */ ec->orport_ipv4.port = VALID_PORT; tt_int_op(circuit_extend_lspec_valid_helper(ec, circ), OP_EQ, -1); expect_log_msg("Client asked me to extend to a zero destination port " "or IPv4 address '0.0.0.0:4660'.\n"); mock_clean_saved_logs(); ec->orport_ipv4.port = 0; + tor_addr_port_make_null_ap(&ec->orport_ipv4, AF_INET); + tor_addr_port_make_null_ap(&ec->orport_ipv6, AF_INET6); /* IPv4 addr is internal, and port is valid. + * (IPv6 addr and port are both zero.) * Result depends on ExtendAllowPrivateAddresses. */ tor_addr_parse(&ec->orport_ipv4.addr, INTERNAL_IPV4); ec->orport_ipv4.port = VALID_PORT; @@ -552,10 +562,71 @@ test_circuit_extend_lspec_valid(void *arg) "to a private IPv4 address '0.0.0.1'.\n"); mock_clean_saved_logs(); fake_options->ExtendAllowPrivateAddresses = 0; + tor_addr_port_make_null_ap(&ec->orport_ipv4, AF_INET); + tor_addr_port_make_null_ap(&ec->orport_ipv6, AF_INET6); + + /* Now do the same tests, but for IPv6 */ + + /* IPv6 port is 0, IPv4 addr and port are both zero, this should fail */ + tor_addr_parse(&ec->orport_ipv6.addr, PUBLIC_IPV6); + tt_int_op(circuit_extend_lspec_valid_helper(ec, circ), OP_EQ, -1); + expect_log_msg("Client asked me to extend to a zero destination port " + "or IPv6 address '[1234::cdef]:0'.\n"); + mock_clean_saved_logs(); + tor_addr_port_make_null_ap(&ec->orport_ipv4, AF_INET); + tor_addr_port_make_null_ap(&ec->orport_ipv6, AF_INET6); + + /* IPv6 addr is 0, IPv4 addr and port are both zero, this should fail */ + ec->orport_ipv6.port = VALID_PORT; + tt_int_op(circuit_extend_lspec_valid_helper(ec, circ), OP_EQ, -1); + expect_log_msg("Client asked me to extend to a zero destination port " + "or IPv6 address '[::]:4660'.\n"); + mock_clean_saved_logs(); + ec->orport_ipv4.port = 0; + tor_addr_port_make_null_ap(&ec->orport_ipv4, AF_INET); + tor_addr_port_make_null_ap(&ec->orport_ipv6, AF_INET6); + + /* IPv6 addr is internal, and port is valid. + * (IPv4 addr and port are both zero.) + * Result depends on ExtendAllowPrivateAddresses. */ + tor_addr_parse(&ec->orport_ipv6.addr, INTERNAL_IPV6); + ec->orport_ipv6.port = VALID_PORT; + + fake_options->ExtendAllowPrivateAddresses = 0; + tt_int_op(circuit_extend_lspec_valid_helper(ec, circ), OP_EQ, -1); + expect_log_msg("Client asked me to extend " + "to a private IPv6 address '[::1]'.\n"); + mock_clean_saved_logs(); + fake_options->ExtendAllowPrivateAddresses = 0; + tor_addr_port_make_null_ap(&ec->orport_ipv4, AF_INET); + tor_addr_port_make_null_ap(&ec->orport_ipv6, AF_INET6); + + /* Both addresses are internal. + * Result depends on ExtendAllowPrivateAddresses. */ + tor_addr_parse(&ec->orport_ipv4.addr, INTERNAL_IPV4); + ec->orport_ipv4.port = VALID_PORT; + tor_addr_parse(&ec->orport_ipv6.addr, INTERNAL_IPV6); + ec->orport_ipv6.port = VALID_PORT; + + fake_options->ExtendAllowPrivateAddresses = 0; + tt_int_op(circuit_extend_lspec_valid_helper(ec, circ), OP_EQ, -1); + expect_log_msg("Client asked me to extend " + "to a private IPv4 address '0.0.0.1'.\n"); + expect_log_msg("Client asked me to extend " + "to a private IPv6 address '[::1]'.\n"); + mock_clean_saved_logs(); + fake_options->ExtendAllowPrivateAddresses = 0; + tor_addr_port_make_null_ap(&ec->orport_ipv4, AF_INET); + tor_addr_port_make_null_ap(&ec->orport_ipv6, AF_INET6); /* If we pass the private address check, but don't have the right * OR circuit magic number, we trigger another bug */ + tor_addr_parse(&ec->orport_ipv4.addr, INTERNAL_IPV4); + ec->orport_ipv4.port = VALID_PORT; + tor_addr_parse(&ec->orport_ipv6.addr, INTERNAL_IPV6); + ec->orport_ipv6.port = VALID_PORT; fake_options->ExtendAllowPrivateAddresses = 1; + tor_capture_bugs_(1); tt_int_op(circuit_extend_lspec_valid_helper(ec, circ), OP_EQ, -1); tt_int_op(smartlist_len(tor_get_captured_bug_log_()), OP_EQ, 1); @@ -564,6 +635,21 @@ test_circuit_extend_lspec_valid(void *arg) tor_end_capture_bugs_(); mock_clean_saved_logs(); fake_options->ExtendAllowPrivateAddresses = 0; + tor_addr_port_make_null_ap(&ec->orport_ipv4, AF_INET); + tor_addr_port_make_null_ap(&ec->orport_ipv6, AF_INET6); + + /* Fail again, but this time only set an IPv4 address. */ + tor_addr_parse(&ec->orport_ipv4.addr, INTERNAL_IPV4); + ec->orport_ipv4.port = VALID_PORT; + fake_options->ExtendAllowPrivateAddresses = 1; + tor_capture_bugs_(1); + tt_int_op(circuit_extend_lspec_valid_helper(ec, circ), OP_EQ, -1); + /* Since we're using IF_BUG_ONCE(), expect 0-1 bug logs */ + tt_int_op(smartlist_len(tor_get_captured_bug_log_()), OP_GE, 0); + tt_int_op(smartlist_len(tor_get_captured_bug_log_()), OP_LE, 1); + tor_end_capture_bugs_(); + mock_clean_saved_logs(); + fake_options->ExtendAllowPrivateAddresses = 0; /* Now set the right magic */ or_circ->base_.magic = OR_CIRCUIT_MAGIC; @@ -625,6 +711,41 @@ test_circuit_extend_lspec_valid(void *arg) tt_int_op(circuit_extend_lspec_valid_helper(ec, circ), OP_EQ, 0); mock_clean_saved_logs(); + /* Now let's check that we warn, but succeed, when only one address is + * private */ + tor_addr_parse(&ec->orport_ipv4.addr, INTERNAL_IPV4); + ec->orport_ipv4.port = VALID_PORT; + tor_addr_parse(&ec->orport_ipv6.addr, PUBLIC_IPV6); + ec->orport_ipv6.port = VALID_PORT; + fake_options->ExtendAllowPrivateAddresses = 0; + + tt_int_op(circuit_extend_lspec_valid_helper(ec, circ), OP_EQ, 0); + expect_log_msg("Client asked me to extend " + "to a private IPv4 address '0.0.0.1'.\n"); + mock_clean_saved_logs(); + tor_addr_port_make_null_ap(&ec->orport_ipv4, AF_INET); + tor_addr_port_make_null_ap(&ec->orport_ipv6, AF_INET6); + + /* Now with private IPv6 */ + tor_addr_parse(&ec->orport_ipv4.addr, PUBLIC_IPV4); + ec->orport_ipv4.port = VALID_PORT; + tor_addr_parse(&ec->orport_ipv6.addr, INTERNAL_IPV6); + ec->orport_ipv6.port = VALID_PORT; + fake_options->ExtendAllowPrivateAddresses = 0; + + tt_int_op(circuit_extend_lspec_valid_helper(ec, circ), OP_EQ, 0); + expect_log_msg("Client asked me to extend " + "to a private IPv6 address '[::1]'.\n"); + mock_clean_saved_logs(); + tor_addr_port_make_null_ap(&ec->orport_ipv4, AF_INET); + tor_addr_port_make_null_ap(&ec->orport_ipv6, AF_INET6); + + /* Now reset to public IPv4 and IPv6 */ + tor_addr_parse(&ec->orport_ipv4.addr, PUBLIC_IPV4); + ec->orport_ipv4.port = VALID_PORT; + tor_addr_parse(&ec->orport_ipv6.addr, PUBLIC_IPV6); + ec->orport_ipv6.port = VALID_PORT; + /* Fail on matching non-zero identities */ memset(&ec->ed_pubkey, 0xEE, sizeof(ec->ed_pubkey)); memset(&p_chan->ed25519_identity, 0xEE, sizeof(p_chan->ed25519_identity)); From f8f688b3097d4aeaabc3f91d644c28540c8c9548 Mon Sep 17 00:00:00 2001 From: teor Date: Wed, 15 Apr 2020 12:08:46 +1000 Subject: [PATCH 11/28] channel: Make channel_matches_target_addr_for_extend() static It isn't used outside channel.c. Part of 33817. --- src/core/or/channel.c | 5 +++++ src/core/or/channel.h | 2 -- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/core/or/channel.c b/src/core/or/channel.c index 75054aa0c4..89804826a3 100644 --- a/src/core/or/channel.c +++ b/src/core/or/channel.c @@ -83,6 +83,11 @@ #include "core/or/cell_queue_st.h" +/* Static function prototypes */ + +static int channel_matches_target_addr_for_extend(channel_t *chan, + const tor_addr_t *target); + /* Global lists of channels */ /* All channel_t instances */ diff --git a/src/core/or/channel.h b/src/core/or/channel.h index 49331d5d58..f86e77992d 100644 --- a/src/core/or/channel.h +++ b/src/core/or/channel.h @@ -737,8 +737,6 @@ int channel_is_outgoing(channel_t *chan); void channel_mark_client(channel_t *chan); void channel_clear_client(channel_t *chan); int channel_matches_extend_info(channel_t *chan, extend_info_t *extend_info); -int channel_matches_target_addr_for_extend(channel_t *chan, - const tor_addr_t *target); unsigned int channel_num_circuits(channel_t *chan); MOCK_DECL(void,channel_set_circid_type,(channel_t *chan, crypto_pk_t *identity_rcvd, From ec5f4f3c5a5aa4d69b2867ba41bc83ba1e6c888a Mon Sep 17 00:00:00 2001 From: teor Date: Wed, 15 Apr 2020 13:07:10 +1000 Subject: [PATCH 12/28] relay/circuitbuild: Report IPv6 addresses in a debug log Part of 33817. --- src/feature/relay/circuitbuild_relay.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/feature/relay/circuitbuild_relay.c b/src/feature/relay/circuitbuild_relay.c index 080781f719..7d3d589777 100644 --- a/src/feature/relay/circuitbuild_relay.c +++ b/src/feature/relay/circuitbuild_relay.c @@ -337,9 +337,13 @@ circuit_extend(struct cell_t *cell, struct circuit_t *circ) &should_launch); if (!n_chan) { - log_debug(LD_CIRC|LD_OR,"Next router (%s): %s.", - fmt_addrport(&ec.orport_ipv4.addr,ec.orport_ipv4.port), - msg?msg:"????"); + /* We can't use fmt_addr*() twice in the same function call, + * because it uses a static buffer. */ + log_debug(LD_CIRC|LD_OR, "Next router IPv4 (%s): %s.", + fmt_addrport_ap(&ec.orport_ipv4), + msg ? msg : "????"); + log_debug(LD_CIRC|LD_OR, "Next router IPv6 (%s).", + fmt_addrport_ap(&ec.orport_ipv6)); circuit_open_connection_for_extend(&ec, circ, should_launch); From 16f3f6a1afe5dcd75536039029f51392d05ce153 Mon Sep 17 00:00:00 2001 From: teor Date: Wed, 15 Apr 2020 13:04:33 +1000 Subject: [PATCH 13/28] relay/circuitbuild: Re-use IPv6 connections for circuits Search for existing connections using the remote IPv4 and IPv6 addresses. Part of 33817. --- src/core/or/channel.c | 57 +++++++++++++++++--------- src/core/or/channel.h | 3 +- src/core/or/circuitbuild.c | 16 +++++--- src/feature/relay/circuitbuild_relay.c | 13 +++++- src/lib/net/address.h | 4 ++ src/test/test_channel.c | 37 +++++++++++------ src/test/test_circuitbuild.c | 6 ++- 7 files changed, 96 insertions(+), 40 deletions(-) diff --git a/src/core/or/channel.c b/src/core/or/channel.c index 89804826a3..93245ce81e 100644 --- a/src/core/or/channel.c +++ b/src/core/or/channel.c @@ -85,8 +85,10 @@ /* Static function prototypes */ -static int channel_matches_target_addr_for_extend(channel_t *chan, - const tor_addr_t *target); +static int channel_matches_target_addr_for_extend( + channel_t *chan, + const tor_addr_t *target_ipv4_addr, + const tor_addr_t *target_ipv6_addr); /* Global lists of channels */ @@ -2365,9 +2367,9 @@ channel_is_better(channel_t *a, channel_t *b) * Get a channel to extend a circuit. * * Given the desired relay identity, pick a suitable channel to extend a - * circuit to the target address requsted by the client. Search for an - * existing channel for the requested endpoint. Make sure the channel is - * usable for new circuits, and matches the target address. + * circuit to the target IPv4 or IPv6 address requsted by the client. Search + * for an existing channel for the requested endpoint. Make sure the channel + * is usable for new circuits, and matches one of the target addresses. * * Try to return the best channel. But if there is no good channel, set * *msg_out to a message describing the channel's state and our next action, @@ -2377,7 +2379,8 @@ channel_is_better(channel_t *a, channel_t *b) MOCK_IMPL(channel_t *, channel_get_for_extend,(const char *rsa_id_digest, const ed25519_public_key_t *ed_id, - const tor_addr_t *target_addr, + const tor_addr_t *target_ipv4_addr, + const tor_addr_t *target_ipv6_addr, const char **msg_out, int *launch_out)) { @@ -2409,11 +2412,15 @@ channel_get_for_extend,(const char *rsa_id_digest, continue; } + const int matches_target = + channel_matches_target_addr_for_extend(chan, + target_ipv4_addr, + target_ipv6_addr); /* Never return a non-open connection. */ if (!CHANNEL_IS_OPEN(chan)) { /* If the address matches, don't launch a new connection for this * circuit. */ - if (channel_matches_target_addr_for_extend(chan, target_addr)) + if (matches_target) ++n_inprogress_goodaddr; continue; } @@ -2424,22 +2431,21 @@ channel_get_for_extend,(const char *rsa_id_digest, continue; } - /* Never return a non-canonical connection using a recent link protocol - * if the address is not what we wanted. + /* If the connection is using a recent link protocol, only return canonical + * connections, when the address is one of the addresses we wanted. * * The channel_is_canonical_is_reliable() function asks the lower layer - * if we should trust channel_is_canonical(). The below is from the - * comments of the old circuit_or_get_for_extend() and applies when + * if we should trust channel_is_canonical(). It only applies when * the lower-layer transport is channel_tls_t. * - * (For old link protocols, we can't rely on is_canonical getting + * For old link protocols, we can't rely on is_canonical getting * set properly if we're talking to the right address, since we might * have an out-of-date descriptor, and we will get no NETINFO cell to - * tell us about the right address.) + * tell us about the right address. */ if (!channel_is_canonical(chan) && channel_is_canonical_is_reliable(chan) && - !channel_matches_target_addr_for_extend(chan, target_addr)) { + !matches_target) { ++n_noncanonical; continue; } @@ -3302,20 +3308,33 @@ channel_matches_extend_info(channel_t *chan, extend_info_t *extend_info) } /** - * Check if a channel matches a given target address; return true iff we do. + * Check if a channel matches the given target IPv4 or IPv6 addresses. + * If either address matches, return true. If neither address matches, + * return false. + * + * Both addresses can't be NULL. * * This function calls into the lower layer and asks if this channel thinks - * it matches a given target address for circuit extension purposes. + * it matches the target addresses for circuit extension purposes. */ int channel_matches_target_addr_for_extend(channel_t *chan, - const tor_addr_t *target) + const tor_addr_t *target_ipv4_addr, + const tor_addr_t *target_ipv6_addr) { tor_assert(chan); tor_assert(chan->matches_target); - tor_assert(target); - return chan->matches_target(chan, target); + IF_BUG_ONCE(!target_ipv4_addr && !target_ipv6_addr) + return 0; + + if (target_ipv4_addr && chan->matches_target(chan, target_ipv4_addr)) + return 1; + + if (target_ipv6_addr && chan->matches_target(chan, target_ipv6_addr)) + return 1; + + return 0; } /** diff --git a/src/core/or/channel.h b/src/core/or/channel.h index f86e77992d..4968c8714a 100644 --- a/src/core/or/channel.h +++ b/src/core/or/channel.h @@ -661,7 +661,8 @@ channel_t * channel_connect(const tor_addr_t *addr, uint16_t port, MOCK_DECL(channel_t *, channel_get_for_extend,( const char *rsa_id_digest, const struct ed25519_public_key_t *ed_id, - const tor_addr_t *target_addr, + const tor_addr_t *target_ipv4_addr, + const tor_addr_t *target_ipv6_addr, const char **msg_out, int *launch_out)); diff --git a/src/core/or/circuitbuild.c b/src/core/or/circuitbuild.c index ce0f9618fe..0381a4dc35 100644 --- a/src/core/or/circuitbuild.c +++ b/src/core/or/circuitbuild.c @@ -559,11 +559,17 @@ circuit_handle_first_hop(origin_circuit_t *circ) fmt_addrport(&firsthop->extend_info->addr, firsthop->extend_info->port)); - n_chan = channel_get_for_extend(firsthop->extend_info->identity_digest, - &firsthop->extend_info->ed_identity, - &firsthop->extend_info->addr, - &msg, - &should_launch); + /* We'll cleanup this code in #33220, when we add an IPv6 address to + * extend_info_t. */ + const bool addr_is_ipv4 = + (tor_addr_family(&firsthop->extend_info->addr) == AF_INET); + n_chan = channel_get_for_extend( + firsthop->extend_info->identity_digest, + &firsthop->extend_info->ed_identity, + addr_is_ipv4 ? &firsthop->extend_info->addr : NULL, + addr_is_ipv4 ? NULL : &firsthop->extend_info->addr, + &msg, + &should_launch); if (!n_chan) { /* not currently connected in a useful way. */ diff --git a/src/feature/relay/circuitbuild_relay.c b/src/feature/relay/circuitbuild_relay.c index 7d3d589777..a926a1d81d 100644 --- a/src/feature/relay/circuitbuild_relay.c +++ b/src/feature/relay/circuitbuild_relay.c @@ -330,9 +330,20 @@ circuit_extend(struct cell_t *cell, struct circuit_t *circ) if (circuit_extend_lspec_valid_helper(&ec, circ) < 0) return -1; + /* Check the addresses, without logging */ + const int ipv4_valid = + (circuit_extend_addr_port_helper(&ec.orport_ipv4, false, false, 0) == 0); + const int ipv6_valid = + (circuit_extend_addr_port_helper(&ec.orport_ipv6, false, false, 0) == 0); + IF_BUG_ONCE(!ipv4_valid && !ipv6_valid) { + /* circuit_extend_lspec_valid_helper() should have caught this */ + return -1; + } + n_chan = channel_get_for_extend((const char*)ec.node_id, &ec.ed_pubkey, - &ec.orport_ipv4.addr, + ipv4_valid ? &ec.orport_ipv4.addr : NULL, + ipv6_valid ? &ec.orport_ipv6.addr : NULL, &msg, &should_launch); diff --git a/src/lib/net/address.h b/src/lib/net/address.h index 611d1ca1ee..651d6bc714 100644 --- a/src/lib/net/address.h +++ b/src/lib/net/address.h @@ -104,6 +104,10 @@ int tor_addr_from_sockaddr(tor_addr_t *a, const struct sockaddr *sa, uint16_t *port_out); void tor_addr_make_unspec(tor_addr_t *a); void tor_addr_make_null(tor_addr_t *a, sa_family_t family); +#define tor_addr_port_make_null(addr, port, family) \ + (void)(tor_addr_make_null(addr, family), (port) = 0) +#define tor_addr_port_make_null_ap(ap, family) \ + tor_addr_port_make_null(&(ap)->addr, (ap)->port, family) char *tor_sockaddr_to_str(const struct sockaddr *sa); /** Return an in6_addr* equivalent to a, or NULL if a is not diff --git a/src/test/test_channel.c b/src/test/test_channel.c index f7efbd7aba..849cc497fc 100644 --- a/src/test/test_channel.c +++ b/src/test/test_channel.c @@ -1326,7 +1326,7 @@ test_channel_for_extend(void *arg) channel_t *ret_chan = NULL; char digest[DIGEST_LEN]; ed25519_public_key_t ed_id; - tor_addr_t addr; + tor_addr_t ipv4_addr, ipv6_addr; const char *msg; int launch; time_t now = time(NULL); @@ -1336,6 +1336,9 @@ test_channel_for_extend(void *arg) memset(digest, 'A', sizeof(digest)); memset(&ed_id, 'B', sizeof(ed_id)); + tor_addr_make_null(&ipv4_addr, AF_INET); + tor_addr_make_null(&ipv6_addr, AF_INET6); + chan1 = new_fake_channel(); tt_assert(chan1); /* Need to be registered to get added to the id map. */ @@ -1366,7 +1369,8 @@ test_channel_for_extend(void *arg) tt_ptr_op(channel_find_by_remote_identity(digest, &ed_id), OP_EQ, chan1); /* The expected result is chan2 because it is older than chan1. */ - ret_chan = channel_get_for_extend(digest, &ed_id, &addr, &msg, &launch); + ret_chan = channel_get_for_extend(digest, &ed_id, &ipv4_addr, &ipv6_addr, + &msg, &launch); tt_assert(ret_chan); tt_ptr_op(ret_chan, OP_EQ, chan2); tt_int_op(launch, OP_EQ, 0); @@ -1374,7 +1378,8 @@ test_channel_for_extend(void *arg) /* Switch that around from previous test. */ chan2->timestamp_created = chan1->timestamp_created + 1; - ret_chan = channel_get_for_extend(digest, &ed_id, &addr, &msg, &launch); + ret_chan = channel_get_for_extend(digest, &ed_id, &ipv4_addr, &ipv6_addr, + &msg, &launch); tt_assert(ret_chan); tt_ptr_op(ret_chan, OP_EQ, chan1); tt_int_op(launch, OP_EQ, 0); @@ -1383,7 +1388,8 @@ test_channel_for_extend(void *arg) /* Same creation time, num circuits will be used and they both have 0 so the * channel 2 should be picked due to how channel_is_better() works. */ chan2->timestamp_created = chan1->timestamp_created; - ret_chan = channel_get_for_extend(digest, &ed_id, &addr, &msg, &launch); + ret_chan = channel_get_for_extend(digest, &ed_id, &ipv4_addr, &ipv6_addr, + &msg, &launch); tt_assert(ret_chan); tt_ptr_op(ret_chan, OP_EQ, chan1); tt_int_op(launch, OP_EQ, 0); @@ -1394,7 +1400,8 @@ test_channel_for_extend(void *arg) /* Condemned the older channel. */ chan1->state = CHANNEL_STATE_CLOSING; - ret_chan = channel_get_for_extend(digest, &ed_id, &addr, &msg, &launch); + ret_chan = channel_get_for_extend(digest, &ed_id, &ipv4_addr, &ipv6_addr, + &msg, &launch); tt_assert(ret_chan); tt_ptr_op(ret_chan, OP_EQ, chan2); tt_int_op(launch, OP_EQ, 0); @@ -1403,7 +1410,8 @@ test_channel_for_extend(void *arg) /* Make the older channel a client one. */ channel_mark_client(chan1); - ret_chan = channel_get_for_extend(digest, &ed_id, &addr, &msg, &launch); + ret_chan = channel_get_for_extend(digest, &ed_id, &ipv4_addr, &ipv6_addr, + &msg, &launch); tt_assert(ret_chan); tt_ptr_op(ret_chan, OP_EQ, chan2); tt_int_op(launch, OP_EQ, 0); @@ -1413,8 +1421,9 @@ test_channel_for_extend(void *arg) /* Non matching ed identity with valid digest. */ ed25519_public_key_t dumb_ed_id; memset(&dumb_ed_id, 0, sizeof(dumb_ed_id)); - ret_chan = channel_get_for_extend(digest, &dumb_ed_id, &addr, &msg, - &launch); + ret_chan = channel_get_for_extend(digest, &dumb_ed_id, + &ipv4_addr, &ipv6_addr, + &msg, &launch); tt_assert(!ret_chan); tt_str_op(msg, OP_EQ, "Not connected. Connecting."); tt_int_op(launch, OP_EQ, 1); @@ -1423,7 +1432,8 @@ test_channel_for_extend(void *arg) test_chan_should_match_target = 1; chan1->state = CHANNEL_STATE_OPENING; chan2->state = CHANNEL_STATE_OPENING; - ret_chan = channel_get_for_extend(digest, &ed_id, &addr, &msg, &launch); + ret_chan = channel_get_for_extend(digest, &ed_id, &ipv4_addr, &ipv6_addr, + &msg, &launch); tt_assert(!ret_chan); tt_str_op(msg, OP_EQ, "Connection in progress; waiting."); tt_int_op(launch, OP_EQ, 0); @@ -1432,7 +1442,8 @@ test_channel_for_extend(void *arg) /* Mark channel 1 as bad for circuits. */ channel_mark_bad_for_new_circs(chan1); - ret_chan = channel_get_for_extend(digest, &ed_id, &addr, &msg, &launch); + ret_chan = channel_get_for_extend(digest, &ed_id, &ipv4_addr, &ipv6_addr, + &msg, &launch); tt_assert(ret_chan); tt_ptr_op(ret_chan, OP_EQ, chan2); tt_int_op(launch, OP_EQ, 0); @@ -1442,7 +1453,8 @@ test_channel_for_extend(void *arg) /* Mark both channels as unusable. */ channel_mark_bad_for_new_circs(chan1); channel_mark_bad_for_new_circs(chan2); - ret_chan = channel_get_for_extend(digest, &ed_id, &addr, &msg, &launch); + ret_chan = channel_get_for_extend(digest, &ed_id, &ipv4_addr, &ipv6_addr, + &msg, &launch); tt_assert(!ret_chan); tt_str_op(msg, OP_EQ, "Connections all too old, or too non-canonical. " " Launching a new one."); @@ -1453,7 +1465,8 @@ test_channel_for_extend(void *arg) /* Non canonical channels. */ test_chan_should_match_target = 0; test_chan_canonical_should_be_reliable = 1; - ret_chan = channel_get_for_extend(digest, &ed_id, &addr, &msg, &launch); + ret_chan = channel_get_for_extend(digest, &ed_id, &ipv4_addr, &ipv6_addr, + &msg, &launch); tt_assert(!ret_chan); tt_str_op(msg, OP_EQ, "Connections all too old, or too non-canonical. " " Launching a new one."); diff --git a/src/test/test_circuitbuild.c b/src/test/test_circuitbuild.c index a8d6323e40..668d9869df 100644 --- a/src/test/test_circuitbuild.c +++ b/src/test/test_circuitbuild.c @@ -993,13 +993,15 @@ static channel_t *mock_channel_get_for_extend_nchan = NULL; static channel_t * mock_channel_get_for_extend(const char *rsa_id_digest, const ed25519_public_key_t *ed_id, - const tor_addr_t *target_addr, + const tor_addr_t *target_ipv4_addr, + const tor_addr_t *target_ipv6_addr, const char **msg_out, int *launch_out) { (void)rsa_id_digest; (void)ed_id; - (void)target_addr; + (void)target_ipv4_addr; + (void)target_ipv6_addr; /* channel_get_for_extend() requires non-NULL arguments */ tt_ptr_op(msg_out, OP_NE, NULL); From 6c458d2d6eaed20e48b1e66bebd1ec0466838e33 Mon Sep 17 00:00:00 2001 From: teor Date: Wed, 15 Apr 2020 17:45:29 +1000 Subject: [PATCH 14/28] log/util_bug: Make IF_BUG_ONCE() support ALL_BUGS_ARE_FATAL ... and DISABLE_ASSERTS_IN_UNIT_TESTS. Make all of tor's assertion macros support the ALL_BUGS_ARE_FATAL and DISABLE_ASSERTS_IN_UNIT_TESTS debugging modes. Implements these modes for IF_BUG_ONCE(). (It used to log a non-fatal warning, regardless of the debugging mode.) Fixes bug 33917; bugfix on 0.2.9.1-alpha. --- changes/bug33917 | 5 +++++ src/lib/log/util_bug.h | 23 ++++++++++++++++++----- 2 files changed, 23 insertions(+), 5 deletions(-) create mode 100644 changes/bug33917 diff --git a/changes/bug33917 b/changes/bug33917 new file mode 100644 index 0000000000..6a8daa9e26 --- /dev/null +++ b/changes/bug33917 @@ -0,0 +1,5 @@ + o Minor bugfixes (logging, testing): + - Make all of tor's assertion macros support the ALL_BUGS_ARE_FATAL and + DISABLE_ASSERTS_IN_UNIT_TESTS debugging modes. Implements these modes + for IF_BUG_ONCE(). (It used to log a non-fatal warning, regardless of + the debugging mode.) Fixes bug 33917; bugfix on 0.2.9.1-alpha. diff --git a/src/lib/log/util_bug.h b/src/lib/log/util_bug.h index ae3d125a08..6b27b36f03 100644 --- a/src/lib/log/util_bug.h +++ b/src/lib/log/util_bug.h @@ -142,6 +142,8 @@ #define ALL_BUGS_ARE_FATAL #endif +/** Define ALL_BUGS_ARE_FATAL if you want Tor to crash when any problem comes + * up, so you can get a coredump and track things down. */ #ifdef ALL_BUGS_ARE_FATAL #define tor_assert_nonfatal_unreached() tor_assert(0) #define tor_assert_nonfatal(cond) tor_assert((cond)) @@ -154,6 +156,9 @@ (tor_assertion_failed_(SHORT_FILE__,__LINE__,__func__,"!("#cond")",NULL), \ tor_abort_(), 1) \ : 0) +#ifndef COCCI +#define IF_BUG_ONCE(cond) if (BUG(cond)) +#endif #elif defined(TOR_UNIT_TESTS) && defined(DISABLE_ASSERTS_IN_UNIT_TESTS) #define tor_assert_nonfatal_unreached() STMT_NIL #define tor_assert_nonfatal(cond) ((void)(cond)) @@ -164,6 +169,9 @@ #define tor_assert_nonfatal_unreached_once() STMT_NIL #define tor_assert_nonfatal_once(cond) ((void)(cond)) #define BUG(cond) (ASSERT_PREDICT_UNLIKELY_(cond) ? 1 : 0) +#ifndef COCCI +#define IF_BUG_ONCE(cond) if (BUG(cond)) +#endif #else /* Normal case, !ALL_BUGS_ARE_FATAL, !DISABLE_ASSERTS_IN_UNIT_TESTS */ #define tor_assert_nonfatal_unreached() STMT_BEGIN \ tor_bug_occurred_(SHORT_FILE__, __LINE__, __func__, NULL, 0, NULL); \ @@ -200,7 +208,6 @@ (ASSERT_PREDICT_UNLIKELY_(cond) ? \ (tor_bug_occurred_(SHORT_FILE__,__LINE__,__func__,"!("#cond")",0,NULL),1) \ : 0) -#endif /* defined(ALL_BUGS_ARE_FATAL) || ... */ #ifndef COCCI #ifdef __GNUC__ @@ -232,7 +239,7 @@ #define IF_BUG_ONCE_VARNAME__(a) \ IF_BUG_ONCE_VARNAME_(a) -/** This macro behaves as 'if (bug(x))', except that it only logs its +/** This macro behaves as 'if (BUG(x))', except that it only logs its * warning once, no matter how many times it triggers. */ @@ -240,9 +247,15 @@ IF_BUG_ONCE__(ASSERT_PREDICT_UNLIKELY_(cond), \ IF_BUG_ONCE_VARNAME__(__LINE__)) -/** Define this if you want Tor to crash when any problem comes up, - * so you can get a coredump and track things down. */ -// #define tor_fragile_assert() tor_assert_unreached(0) +#endif /* defined(ALL_BUGS_ARE_FATAL) || ... */ + +/** In older code, we used tor_fragile_assert() to mark optional failure + * points. At these points, we could make some debug builds fail. + * (But release builds would continue.) + * + * To get the same behaviour in recent tor versions, define + * ALL_BUGS_ARE_FATAL, and use any non-fatal assertion or *BUG() macro. + */ #define tor_fragile_assert() tor_assert_nonfatal_unreached_once() void tor_assertion_failed_(const char *fname, unsigned int line, From 063505446f5b85dd11ee8c9b2089f58fd3bf7427 Mon Sep 17 00:00:00 2001 From: teor Date: Wed, 15 Apr 2020 19:07:58 +1000 Subject: [PATCH 15/28] test/circuitbuild: Disable some tests when ALL_BUGS_ARE_FATAL Some tests use IF_BUG_ONCE(), which is fatal when ALL_BUGS_ARE_FATAL, after the fixes in 33917. Also run "make autostyle" on these changes. Part of 33817. --- src/test/test_circuitbuild.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/test/test_circuitbuild.c b/src/test/test_circuitbuild.c index 668d9869df..5d09ba557d 100644 --- a/src/test/test_circuitbuild.c +++ b/src/test/test_circuitbuild.c @@ -219,6 +219,7 @@ test_circuit_extend_state_valid(void *arg) expect_log_msg("Got an extend cell, but running as a client. Closing.\n"); mock_clean_saved_logs(); +#ifndef ALL_BUGS_ARE_FATAL /* Circuit must be non-NULL */ tor_capture_bugs_(1); server = 1; @@ -228,6 +229,7 @@ test_circuit_extend_state_valid(void *arg) "!(ASSERT_PREDICT_UNLIKELY_(!circ))"); tor_end_capture_bugs_(); mock_clean_saved_logs(); +#endif /* !defined(ALL_BUGS_ARE_FATAL) */ /* n_chan and n_hop are NULL, this should succeed */ server = 1; @@ -314,6 +316,7 @@ test_circuit_extend_add_ed25519(void *arg) setup_full_capture_of_logs(LOG_INFO); +#ifndef ALL_BUGS_ARE_FATAL /* The extend cell must be non-NULL */ tor_capture_bugs_(1); tt_int_op(circuit_extend_add_ed25519_helper(NULL), OP_EQ, -1); @@ -322,6 +325,7 @@ test_circuit_extend_add_ed25519(void *arg) "!(ASSERT_PREDICT_UNLIKELY_(!ec))"); tor_end_capture_bugs_(); mock_clean_saved_logs(); +#endif /* !defined(ALL_BUGS_ARE_FATAL) */ /* The node id must be non-zero */ memcpy(old_ec, ec, sizeof(extend_cell_t)); @@ -495,6 +499,7 @@ test_circuit_extend_lspec_valid(void *arg) setup_full_capture_of_logs(LOG_INFO); +#ifndef ALL_BUGS_ARE_FATAL /* Extend cell must be non-NULL */ tor_capture_bugs_(1); tt_int_op(circuit_extend_lspec_valid_helper(NULL, circ), OP_EQ, -1); @@ -521,6 +526,7 @@ test_circuit_extend_lspec_valid(void *arg) tt_int_op(smartlist_len(tor_get_captured_bug_log_()), OP_LE, 2); tor_end_capture_bugs_(); mock_clean_saved_logs(); +#endif /* !defined(ALL_BUGS_ARE_FATAL) */ /* IPv4 and IPv6 addr and port are all zero, this should fail */ tt_int_op(circuit_extend_lspec_valid_helper(ec, circ), OP_EQ, -1); @@ -619,6 +625,7 @@ test_circuit_extend_lspec_valid(void *arg) tor_addr_port_make_null_ap(&ec->orport_ipv4, AF_INET); tor_addr_port_make_null_ap(&ec->orport_ipv6, AF_INET6); +#ifndef ALL_BUGS_ARE_FATAL /* If we pass the private address check, but don't have the right * OR circuit magic number, we trigger another bug */ tor_addr_parse(&ec->orport_ipv4.addr, INTERNAL_IPV4); @@ -650,10 +657,12 @@ test_circuit_extend_lspec_valid(void *arg) tor_end_capture_bugs_(); mock_clean_saved_logs(); fake_options->ExtendAllowPrivateAddresses = 0; +#endif /* !defined(ALL_BUGS_ARE_FATAL) */ /* Now set the right magic */ or_circ->base_.magic = OR_CIRCUIT_MAGIC; +#ifndef ALL_BUGS_ARE_FATAL /* If we pass the OR circuit magic check, but don't have p_chan, * we trigger another bug */ fake_options->ExtendAllowPrivateAddresses = 1; @@ -680,6 +689,7 @@ test_circuit_extend_lspec_valid(void *arg) tor_addr_make_null(&ec->orport_ipv4.addr, AF_INET); ec->orport_ipv4.port = 0x0000; +#endif /* !defined(ALL_BUGS_ARE_FATAL) */ /* Now let's fake a p_chan and the addresses */ tor_addr_parse(&ec->orport_ipv4.addr, PUBLIC_IPV4); @@ -855,6 +865,7 @@ test_circuit_open_connection_for_extend(void *arg) setup_full_capture_of_logs(LOG_INFO); +#ifndef ALL_BUGS_ARE_FATAL /* Circuit must be non-NULL */ mock_circuit_close_calls = 0; mock_channel_connect_calls = 0; @@ -895,6 +906,7 @@ test_circuit_open_connection_for_extend(void *arg) tt_int_op(smartlist_len(tor_get_captured_bug_log_()), OP_LE, 2); tor_end_capture_bugs_(); mock_clean_saved_logs(); +#endif /* !defined(ALL_BUGS_ARE_FATAL) */ /* Succeed, but don't try to open a connection */ mock_circuit_close_calls = 0; @@ -1067,6 +1079,7 @@ test_circuit_extend(void *arg) setup_full_capture_of_logs(LOG_INFO); +#ifndef ALL_BUGS_ARE_FATAL /* Circuit must be non-NULL */ tor_capture_bugs_(1); tt_int_op(circuit_extend(cell, NULL), OP_EQ, -1); @@ -1093,6 +1106,7 @@ test_circuit_extend(void *arg) tt_int_op(smartlist_len(tor_get_captured_bug_log_()), OP_LE, 2); tor_end_capture_bugs_(); mock_clean_saved_logs(); +#endif /* !defined(ALL_BUGS_ARE_FATAL) */ /* Clients can't extend */ server = 0; @@ -1138,6 +1152,7 @@ test_circuit_extend(void *arg) PUBLIC_IPV4); mock_extend_cell_parse_cell_out.orport_ipv4.port = VALID_PORT; +#ifndef ALL_BUGS_ARE_FATAL tor_capture_bugs_(1); tt_int_op(circuit_extend(cell, circ), OP_EQ, -1); tt_int_op(mock_extend_cell_parse_calls, OP_EQ, 1); @@ -1147,6 +1162,7 @@ test_circuit_extend(void *arg) tor_end_capture_bugs_(); mock_clean_saved_logs(); mock_extend_cell_parse_calls = 0; +#endif /* !defined(ALL_BUGS_ARE_FATAL) */ /* Now add the right magic and a p_chan. */ or_circ->base_.magic = OR_CIRCUIT_MAGIC; @@ -1292,6 +1308,7 @@ test_onionskin_answer(void *arg) setup_full_capture_of_logs(LOG_INFO); +#ifndef ALL_BUGS_ARE_FATAL /* Circuit must be non-NULL */ tor_capture_bugs_(1); tt_int_op(onionskin_answer(NULL, created_cell, @@ -1335,6 +1352,7 @@ test_onionskin_answer(void *arg) "!(ASSERT_PREDICT_UNLIKELY_(!rend_circ_nonce))"); tor_end_capture_bugs_(); mock_clean_saved_logs(); +#endif /* !defined(ALL_BUGS_ARE_FATAL) */ /* Also, the keys length must be CPATH_KEY_MATERIAL_LEN, but we can't catch * asserts in unit tests. */ From a0b12f3cd40fc00c9bdbb1ff01b0d074673a7524 Mon Sep 17 00:00:00 2001 From: teor Date: Thu, 16 Apr 2020 08:13:02 +1000 Subject: [PATCH 16/28] relay/circuitbuild: Refactor open connection for extend Re-use the newly created extend_info to launch the connection in circuit_open_connection_for_extend(). No behaviour change. Part of 33817. --- src/feature/relay/circuitbuild_relay.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/feature/relay/circuitbuild_relay.c b/src/feature/relay/circuitbuild_relay.c index a926a1d81d..d6ea22ca70 100644 --- a/src/feature/relay/circuitbuild_relay.c +++ b/src/feature/relay/circuitbuild_relay.c @@ -273,10 +273,11 @@ circuit_open_connection_for_extend(const struct extend_cell_t *ec, if (should_launch) { /* we should try to open a connection */ - channel_t *n_chan = channel_connect_for_circuit(&ec->orport_ipv4.addr, - ec->orport_ipv4.port, - (const char*)ec->node_id, - &ec->ed_pubkey); + channel_t *n_chan = channel_connect_for_circuit( + &circ->n_hop->addr, + circ->n_hop->port, + circ->n_hop->identity_digest, + &circ->n_hop->ed_identity); if (!n_chan) { log_info(LD_CIRC,"Launching n_chan failed. Closing circuit."); circuit_mark_for_close(circ, END_CIRC_REASON_CONNECTFAILED); From c3e058dfac1cbc7cb0dee5cdb1bdc61c1dc0f4fa Mon Sep 17 00:00:00 2001 From: teor Date: Wed, 29 Apr 2020 15:56:40 +1000 Subject: [PATCH 17/28] relay: Choose between IPv4 and IPv6 extends at random When an EXTEND2 cell has an IPv4 and an IPv6 address, choose one of them uniformly at random. Part of 33817. --- src/feature/relay/circuitbuild_relay.c | 66 ++++++++++++++++++++++++-- src/feature/relay/router.c | 11 ++++- src/feature/relay/router.h | 1 + src/test/test_circuitbuild.c | 32 ++++++++++++- 4 files changed, 103 insertions(+), 7 deletions(-) diff --git a/src/feature/relay/circuitbuild_relay.c b/src/feature/relay/circuitbuild_relay.c index d6ea22ca70..261fbc7e45 100644 --- a/src/feature/relay/circuitbuild_relay.c +++ b/src/feature/relay/circuitbuild_relay.c @@ -18,6 +18,8 @@ #include "orconfig.h" #include "feature/relay/circuitbuild_relay.h" +#include "lib/crypt_ops/crypto_rand.h" + #include "core/or/or.h" #include "app/config/config.h" @@ -36,6 +38,7 @@ #include "feature/nodelist/nodelist.h" +#include "feature/relay/router.h" #include "feature/relay/routermode.h" #include "feature/relay/selftest.h" @@ -237,9 +240,14 @@ circuit_extend_lspec_valid_helper(const struct extend_cell_t *ec, } /* When there is no open channel for an extend cell ec, set up the - * circuit circ to wait for a new connection. If should_launch - * is true, open a new connection. (Otherwise, we are already waiting for a - * new connection to the same relay.) + * circuit circ to wait for a new connection. + * + * If should_launch is true, open a new connection. (Otherwise, we are + * already waiting for a new connection to the same relay.) + * + * Check if IPv6 extends are supported by our current configuration. If they + * are, new connections may be made over IPv4 or IPv6. (IPv4 connections are + * always supported.) */ STATIC void circuit_open_connection_for_extend(const struct extend_cell_t *ec, @@ -258,13 +266,61 @@ circuit_open_connection_for_extend(const struct extend_cell_t *ec, return; } + /* Check the addresses, without logging */ + const int ipv4_valid = + (circuit_extend_addr_port_helper(&ec->orport_ipv4, false, false, 0) == 0); + const int ipv6_valid = + (circuit_extend_addr_port_helper(&ec->orport_ipv6, false, false, 0) == 0); + + IF_BUG_ONCE(!ipv4_valid && !ipv6_valid) { + /* circuit_extend_lspec_valid_helper() should have caught this */ + circuit_mark_for_close(circ, END_CIRC_REASON_CONNECTFAILED); + return; + } + + /* If we could make an IPv4 or an IPv6 connection, make an IPv6 connection + * at random, with probability 1 in N. + * 1 means "always IPv6 (and no IPv4)" + * 2 means "equal probability of IPv4 or IPv6" + * ... (and so on) ... + * (UINT_MAX - 1) means "almost always IPv4 (and almost never IPv6)" + * To disable IPv6, set ipv6_supported to 0. + */ +#define IPV6_CONNECTION_ONE_IN_N 2 + + const bool ipv6_supported = router_has_advertised_ipv6_orport(get_options()); + const tor_addr_port_t *chosen_ap = NULL; + + /* IPv4 is always supported */ + if (ipv4_valid && ipv6_valid && ipv6_supported) { + /* Choose between IPv4 and IPv6 at random */ + bool choose_ipv6 = crypto_fast_rng_one_in_n(get_thread_fast_rng(), + IPV6_CONNECTION_ONE_IN_N); + if (choose_ipv6) { + chosen_ap = &ec->orport_ipv6; + } else { + chosen_ap = &ec->orport_ipv4; + } + } else if (ipv6_valid && ipv6_supported) { + /* There's only one valid address: try to use it */ + chosen_ap = &ec->orport_ipv6; + } else if (ipv4_valid) { + chosen_ap = &ec->orport_ipv4; + } else { + /* An IPv6-only extend, but IPv6 is not supported */ + log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, + "Received IPv6-only extend, but we don't have an IPv6 ORPort."); + circuit_mark_for_close(circ, END_CIRC_REASON_CONNECTFAILED); + return; + } + circ->n_hop = extend_info_new(NULL /*nickname*/, (const char*)ec->node_id, &ec->ed_pubkey, NULL, /*onion_key*/ NULL, /*curve25519_key*/ - &ec->orport_ipv4.addr, - ec->orport_ipv4.port); + &chosen_ap->addr, + chosen_ap->port); circ->n_chan_create_cell = tor_memdup(&ec->create_cell, sizeof(ec->create_cell)); diff --git a/src/feature/relay/router.c b/src/feature/relay/router.c index a4a9c6a817..89e232ccc0 100644 --- a/src/feature/relay/router.c +++ b/src/feature/relay/router.c @@ -1469,7 +1469,7 @@ router_get_advertised_ipv6_or_ap(const or_options_t *options, AF_INET6); if (!addr || port == 0) { - log_info(LD_CONFIG, "There is no advertised IPv6 ORPort."); + log_debug(LD_CONFIG, "There is no advertised IPv6 ORPort."); return; } @@ -1490,6 +1490,15 @@ router_get_advertised_ipv6_or_ap(const or_options_t *options, ipv6_ap_out->port = port; } +/** Returns true if this router has an advertised IPv6 ORPort. */ +bool +router_has_advertised_ipv6_orport(const or_options_t *options) +{ + tor_addr_port_t ipv6_ap; + router_get_advertised_ipv6_or_ap(options, &ipv6_ap); + return tor_addr_port_is_valid_ap(&ipv6_ap, 0); +} + /** Return the port that we should advertise as our DirPort; * this is one of three possibilities: * The one that is passed as dirport if the DirPort option is 0, or diff --git a/src/feature/relay/router.h b/src/feature/relay/router.h index d1b4ce5f8f..c3a93cc0aa 100644 --- a/src/feature/relay/router.h +++ b/src/feature/relay/router.h @@ -68,6 +68,7 @@ uint16_t router_get_active_listener_port_by_type_af(int listener_type, uint16_t router_get_advertised_or_port(const or_options_t *options); void router_get_advertised_ipv6_or_ap(const or_options_t *options, tor_addr_port_t *ipv6_ap_out); +bool router_has_advertised_ipv6_orport(const or_options_t *options); uint16_t router_get_advertised_or_port_by_af(const or_options_t *options, sa_family_t family); uint16_t router_get_advertised_dir_port(const or_options_t *options, diff --git a/src/test/test_circuitbuild.c b/src/test/test_circuitbuild.c index 5d09ba557d..a26109ed88 100644 --- a/src/test/test_circuitbuild.c +++ b/src/test/test_circuitbuild.c @@ -857,6 +857,10 @@ test_circuit_open_connection_for_extend(void *arg) circuit_t *circ = tor_malloc_zero(sizeof(circuit_t)); channel_t *fake_n_chan = tor_malloc_zero(sizeof(channel_t)); + or_options_t *fake_options = options_new(); + MOCK(get_options, mock_get_options); + mocked_options = fake_options; + MOCK(circuit_mark_for_close_, mock_circuit_mark_for_close_); mock_circuit_close_calls = 0; MOCK(channel_connect_for_circuit, mock_channel_connect_for_circuit); @@ -906,8 +910,30 @@ test_circuit_open_connection_for_extend(void *arg) tt_int_op(smartlist_len(tor_get_captured_bug_log_()), OP_LE, 2); tor_end_capture_bugs_(); mock_clean_saved_logs(); + + /* Fail, because neither address is valid */ + mock_circuit_close_calls = 0; + mock_channel_connect_calls = 0; + tor_capture_bugs_(1); + circuit_open_connection_for_extend(ec, circ, 0); + /* Close the circuit, don't connect */ + tt_int_op(mock_circuit_close_calls, OP_EQ, 1); + tt_int_op(mock_channel_connect_calls, OP_EQ, 0); + /* Check state */ + tt_ptr_op(circ->n_hop, OP_EQ, NULL); + tt_ptr_op(circ->n_chan_create_cell, OP_EQ, NULL); + tt_int_op(circ->state, OP_EQ, 0); + /* Cleanup */ + tor_end_capture_bugs_(); + mock_clean_saved_logs(); #endif /* !defined(ALL_BUGS_ARE_FATAL) */ + /* Set up valid addresses */ + tor_addr_parse(&ec->orport_ipv4.addr, PUBLIC_IPV4); + ec->orport_ipv4.port = VALID_PORT; + tor_addr_parse(&ec->orport_ipv6.addr, PUBLIC_IPV6); + ec->orport_ipv6.port = VALID_PORT; + /* Succeed, but don't try to open a connection */ mock_circuit_close_calls = 0; mock_channel_connect_calls = 0; @@ -948,7 +974,7 @@ test_circuit_open_connection_for_extend(void *arg) mock_circuit_close_calls = 0; mock_channel_connect_calls = 0; circuit_open_connection_for_extend(ec, circ, 1); - /* Try to connect, and succeed, leaving the circuit open */ + /* Connection attempt succeeded, leaving the circuit open */ tt_int_op(mock_circuit_close_calls, OP_EQ, 0); tt_int_op(mock_channel_connect_calls, OP_EQ, 1); /* Check state */ @@ -971,6 +997,10 @@ test_circuit_open_connection_for_extend(void *arg) UNMOCK(channel_connect_for_circuit); mock_channel_connect_calls = 0; + UNMOCK(get_options); + or_options_free(fake_options); + mocked_options = NULL; + tor_free(ec); tor_free(circ->n_hop); tor_free(circ->n_chan_create_cell); From cab05a84cd1504bc4929207a1f33b266744e5dca Mon Sep 17 00:00:00 2001 From: teor Date: Wed, 29 Apr 2020 20:10:14 +1000 Subject: [PATCH 18/28] relay: Add IP version tests for circuit extends Add IPv4-only and IPv6-only tests for circuit_open_connection_for_extend(). Part of 33817. --- src/feature/relay/router.c | 4 +-- src/feature/relay/router.h | 3 ++- src/test/test_circuitbuild.c | 48 ++++++++++++++++++++++++++++++------ 3 files changed, 45 insertions(+), 10 deletions(-) diff --git a/src/feature/relay/router.c b/src/feature/relay/router.c index 89e232ccc0..b0a56012db 100644 --- a/src/feature/relay/router.c +++ b/src/feature/relay/router.c @@ -1491,8 +1491,8 @@ router_get_advertised_ipv6_or_ap(const or_options_t *options, } /** Returns true if this router has an advertised IPv6 ORPort. */ -bool -router_has_advertised_ipv6_orport(const or_options_t *options) +MOCK_IMPL(bool, +router_has_advertised_ipv6_orport,(const or_options_t *options)) { tor_addr_port_t ipv6_ap; router_get_advertised_ipv6_or_ap(options, &ipv6_ap); diff --git a/src/feature/relay/router.h b/src/feature/relay/router.h index c3a93cc0aa..1a262a6799 100644 --- a/src/feature/relay/router.h +++ b/src/feature/relay/router.h @@ -68,7 +68,8 @@ uint16_t router_get_active_listener_port_by_type_af(int listener_type, uint16_t router_get_advertised_or_port(const or_options_t *options); void router_get_advertised_ipv6_or_ap(const or_options_t *options, tor_addr_port_t *ipv6_ap_out); -bool router_has_advertised_ipv6_orport(const or_options_t *options); +MOCK_DECL(bool, router_has_advertised_ipv6_orport,( + const or_options_t *options)); uint16_t router_get_advertised_or_port_by_af(const or_options_t *options, sa_family_t family); uint16_t router_get_advertised_dir_port(const or_options_t *options, diff --git a/src/test/test_circuitbuild.c b/src/test/test_circuitbuild.c index a26109ed88..5ae7dbf97b 100644 --- a/src/test/test_circuitbuild.c +++ b/src/test/test_circuitbuild.c @@ -30,6 +30,7 @@ #include "feature/client/entrynodes.h" #include "feature/nodelist/nodelist.h" #include "feature/relay/circuitbuild_relay.h" +#include "feature/relay/router.h" #include "feature/relay/routermode.h" #include "feature/nodelist/node_st.h" @@ -848,11 +849,27 @@ mock_channel_connect_for_circuit(const tor_addr_t *addr, return mock_channel_connect_nchan; } -/* Test the different cases in circuit_open_connection_for_extend(). */ +static bool +mock_router_has_advertised_ipv6_orport(const or_options_t *options) +{ + (void)options; + return 1; +} + +/* Test the different cases in circuit_open_connection_for_extend(). + * Chooses different IP addresses depending on the first character in arg: + * - 4: IPv4 + * - 6: IPv6 + * - d: IPv4 and IPv6 (dual-stack) + */ static void test_circuit_open_connection_for_extend(void *arg) { - (void)arg; + const char ip_version = ((const char *)arg)[0]; + const bool use_ipv4 = (ip_version == '4' || ip_version == 'd'); + const bool use_ipv6 = (ip_version == '6' || ip_version == 'd'); + tor_assert(use_ipv4 || use_ipv6); + extend_cell_t *ec = tor_malloc_zero(sizeof(extend_cell_t)); circuit_t *circ = tor_malloc_zero(sizeof(circuit_t)); channel_t *fake_n_chan = tor_malloc_zero(sizeof(channel_t)); @@ -867,6 +884,9 @@ test_circuit_open_connection_for_extend(void *arg) mock_channel_connect_calls = 0; mock_channel_connect_nchan = NULL; + MOCK(router_has_advertised_ipv6_orport, + mock_router_has_advertised_ipv6_orport); + setup_full_capture_of_logs(LOG_INFO); #ifndef ALL_BUGS_ARE_FATAL @@ -929,10 +949,14 @@ test_circuit_open_connection_for_extend(void *arg) #endif /* !defined(ALL_BUGS_ARE_FATAL) */ /* Set up valid addresses */ - tor_addr_parse(&ec->orport_ipv4.addr, PUBLIC_IPV4); - ec->orport_ipv4.port = VALID_PORT; - tor_addr_parse(&ec->orport_ipv6.addr, PUBLIC_IPV6); - ec->orport_ipv6.port = VALID_PORT; + if (use_ipv4) { + tor_addr_parse(&ec->orport_ipv4.addr, PUBLIC_IPV4); + ec->orport_ipv4.port = VALID_PORT; + } + if (use_ipv6) { + tor_addr_parse(&ec->orport_ipv6.addr, PUBLIC_IPV6); + ec->orport_ipv6.port = VALID_PORT; + } /* Succeed, but don't try to open a connection */ mock_circuit_close_calls = 0; @@ -1001,6 +1025,8 @@ test_circuit_open_connection_for_extend(void *arg) or_options_free(fake_options); mocked_options = NULL; + UNMOCK(router_has_advertised_ipv6_orport); + tor_free(ec); tor_free(circ->n_hop); tor_free(circ->n_chan_create_cell); @@ -1414,6 +1440,12 @@ test_onionskin_answer(void *arg) #define TEST_CIRCUIT(name, flags) \ { #name, test_circuit_ ## name, flags, NULL, NULL } +#ifndef COCCI +#define TEST_CIRCUIT_PASSTHROUGH(name, flags, arg) \ + { #name "/" arg, test_circuit_ ## name, flags, \ + &passthrough_setup, (void *)(arg) } +#endif + struct testcase_t circuitbuild_tests[] = { TEST_NEW_ROUTE_LEN(noexit, 0), TEST_NEW_ROUTE_LEN(safe_exit, 0), @@ -1425,7 +1457,9 @@ struct testcase_t circuitbuild_tests[] = { TEST_CIRCUIT(extend_state_valid, TT_FORK), TEST_CIRCUIT(extend_add_ed25519, TT_FORK), TEST_CIRCUIT(extend_lspec_valid, TT_FORK), - TEST_CIRCUIT(open_connection_for_extend, TT_FORK), + TEST_CIRCUIT_PASSTHROUGH(open_connection_for_extend, TT_FORK, "4"), + TEST_CIRCUIT_PASSTHROUGH(open_connection_for_extend, TT_FORK, "6"), + TEST_CIRCUIT_PASSTHROUGH(open_connection_for_extend, TT_FORK, "dual-stack"), TEST_CIRCUIT(extend, TT_FORK), TEST(onionskin_answer, TT_FORK, NULL, NULL), From 9a6186c267c613ab1cbcb7544e988fe62aab8548 Mon Sep 17 00:00:00 2001 From: teor Date: Wed, 29 Apr 2020 20:29:15 +1000 Subject: [PATCH 19/28] relay: Refactor circuit_open_connection_for_extend() Refactor circuit_open_connection_for_extend(), splitting out the IP address choice code into a new function. Adds unit tests. No behaviour changes in tor. Part of 33817. --- src/feature/relay/circuitbuild_relay.c | 77 ++++++++++++++++---------- src/feature/relay/circuitbuild_relay.h | 3 + 2 files changed, 51 insertions(+), 29 deletions(-) diff --git a/src/feature/relay/circuitbuild_relay.c b/src/feature/relay/circuitbuild_relay.c index 261fbc7e45..2bf08547d8 100644 --- a/src/feature/relay/circuitbuild_relay.c +++ b/src/feature/relay/circuitbuild_relay.c @@ -239,6 +239,50 @@ circuit_extend_lspec_valid_helper(const struct extend_cell_t *ec, return 0; } +/* If possible, return a supported, non-NULL IP address. + * + * If both addresses are supported and non-NULL, choose one uniformly at + * random. + * + * If we have an IPv6-only extend, but IPv6 is not supported, returns NULL. + * If both addresses are NULL, also returns NULL. */ +STATIC const tor_addr_port_t * +circuit_choose_ip_ap_for_extend(const tor_addr_port_t *ipv4_ap, + const tor_addr_port_t *ipv6_ap) +{ + /* If we could make an IPv4 or an IPv6 connection, make an IPv6 connection + * at random, with probability 1 in N. + * 1 means "always IPv6 (and no IPv4)" + * 2 means "equal probability of IPv4 or IPv6" + * ... (and so on) ... + * (UINT_MAX - 1) means "almost always IPv4 (and almost never IPv6)" + * To disable IPv6, set ipv6_supported to 0. + */ +#define IPV6_CONNECTION_ONE_IN_N 2 + + /* IPv4 is always supported */ + const bool ipv6_supported = router_has_advertised_ipv6_orport(get_options()); + + if (ipv4_ap && ipv6_ap && ipv6_supported) { + /* Choose between IPv4 and IPv6 at random */ + bool choose_ipv6 = crypto_fast_rng_one_in_n(get_thread_fast_rng(), + IPV6_CONNECTION_ONE_IN_N); + if (choose_ipv6) { + return ipv6_ap; + } else { + return ipv4_ap; + } + } else if (ipv6_ap && ipv6_supported) { + /* There's only one valid address: try to use it */ + return ipv6_ap; + } else if (ipv4_ap) { + return ipv4_ap; + } else { + /* IPv6-only extend, but IPv6 is not supported. */ + return NULL; + } +} + /* When there is no open channel for an extend cell ec, set up the * circuit circ to wait for a new connection. * @@ -278,35 +322,10 @@ circuit_open_connection_for_extend(const struct extend_cell_t *ec, return; } - /* If we could make an IPv4 or an IPv6 connection, make an IPv6 connection - * at random, with probability 1 in N. - * 1 means "always IPv6 (and no IPv4)" - * 2 means "equal probability of IPv4 or IPv6" - * ... (and so on) ... - * (UINT_MAX - 1) means "almost always IPv4 (and almost never IPv6)" - * To disable IPv6, set ipv6_supported to 0. - */ -#define IPV6_CONNECTION_ONE_IN_N 2 - - const bool ipv6_supported = router_has_advertised_ipv6_orport(get_options()); - const tor_addr_port_t *chosen_ap = NULL; - - /* IPv4 is always supported */ - if (ipv4_valid && ipv6_valid && ipv6_supported) { - /* Choose between IPv4 and IPv6 at random */ - bool choose_ipv6 = crypto_fast_rng_one_in_n(get_thread_fast_rng(), - IPV6_CONNECTION_ONE_IN_N); - if (choose_ipv6) { - chosen_ap = &ec->orport_ipv6; - } else { - chosen_ap = &ec->orport_ipv4; - } - } else if (ipv6_valid && ipv6_supported) { - /* There's only one valid address: try to use it */ - chosen_ap = &ec->orport_ipv6; - } else if (ipv4_valid) { - chosen_ap = &ec->orport_ipv4; - } else { + const tor_addr_port_t *chosen_ap = circuit_choose_ip_ap_for_extend( + ipv4_valid ? &ec->orport_ipv4 : NULL, + ipv6_valid ? &ec->orport_ipv6 : NULL); + if (!chosen_ap) { /* An IPv6-only extend, but IPv6 is not supported */ log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, "Received IPv6-only extend, but we don't have an IPv6 ORPort."); diff --git a/src/feature/relay/circuitbuild_relay.h b/src/feature/relay/circuitbuild_relay.h index d14f304f1c..0783161538 100644 --- a/src/feature/relay/circuitbuild_relay.h +++ b/src/feature/relay/circuitbuild_relay.h @@ -75,6 +75,9 @@ STATIC int circuit_extend_state_valid_helper(const struct circuit_t *circ); STATIC int circuit_extend_add_ed25519_helper(struct extend_cell_t *ec); STATIC int circuit_extend_lspec_valid_helper(const struct extend_cell_t *ec, const struct circuit_t *circ); +STATIC const tor_addr_port_t * circuit_choose_ip_ap_for_extend( + const tor_addr_port_t *ipv4_ap, + const tor_addr_port_t *ipv6_ap); STATIC void circuit_open_connection_for_extend(const struct extend_cell_t *ec, struct circuit_t *circ, int should_launch); From df5ea297751d68dac7986f827494f2be87fe954f Mon Sep 17 00:00:00 2001 From: teor Date: Wed, 29 Apr 2020 21:25:10 +1000 Subject: [PATCH 20/28] relay: Add tests for choosing extend IPs Part of 33817. --- src/test/test_circuitbuild.c | 100 ++++++++++++++++++++++++++++++++--- 1 file changed, 93 insertions(+), 7 deletions(-) diff --git a/src/test/test_circuitbuild.c b/src/test/test_circuitbuild.c index 5ae7dbf97b..ed75d102b9 100644 --- a/src/test/test_circuitbuild.c +++ b/src/test/test_circuitbuild.c @@ -821,6 +821,97 @@ test_circuit_extend_lspec_valid(void *arg) tor_free(p_chan); } +static bool router_has_ipv6_orport_result = false; +static int mock_router_ipv6_orport_calls = 0; +static bool +mock_router_has_advertised_ipv6_orport(const or_options_t *options) +{ + (void)options; + mock_router_ipv6_orport_calls++; + return router_has_ipv6_orport_result; +} + +/* Test the different cases in circuit_choose_ip_ap_for_extend(). */ +static void +test_circuit_choose_ip_ap_for_extend(void *arg) +{ + (void)arg; + tor_addr_port_t ipv4_ap; + tor_addr_port_t ipv6_ap; + + /* Set up valid addresses */ + tor_addr_parse(&ipv4_ap.addr, PUBLIC_IPV4); + ipv4_ap.port = VALID_PORT; + tor_addr_parse(&ipv6_ap.addr, PUBLIC_IPV6); + ipv6_ap.port = VALID_PORT; + + or_options_t *fake_options = options_new(); + MOCK(get_options, mock_get_options); + mocked_options = fake_options; + + MOCK(router_has_advertised_ipv6_orport, + mock_router_has_advertised_ipv6_orport); + router_has_ipv6_orport_result = true; + mock_router_ipv6_orport_calls = 0; + + /* No valid addresses */ + router_has_ipv6_orport_result = true; + mock_router_ipv6_orport_calls = 0; + tt_ptr_op(circuit_choose_ip_ap_for_extend(NULL, NULL), OP_EQ, NULL); + tt_int_op(mock_router_ipv6_orport_calls, OP_EQ, 1); + + router_has_ipv6_orport_result = false; + mock_router_ipv6_orport_calls = 0; + tt_ptr_op(circuit_choose_ip_ap_for_extend(NULL, NULL), OP_EQ, NULL); + tt_int_op(mock_router_ipv6_orport_calls, OP_EQ, 1); + + /* One valid address: IPv4 */ + router_has_ipv6_orport_result = true; + mock_router_ipv6_orport_calls = 0; + tt_ptr_op(circuit_choose_ip_ap_for_extend(&ipv4_ap, NULL), OP_EQ, &ipv4_ap); + tt_int_op(mock_router_ipv6_orport_calls, OP_EQ, 1); + + router_has_ipv6_orport_result = false; + mock_router_ipv6_orport_calls = 0; + tt_ptr_op(circuit_choose_ip_ap_for_extend(&ipv4_ap, NULL), OP_EQ, &ipv4_ap); + tt_int_op(mock_router_ipv6_orport_calls, OP_EQ, 1); + + /* One valid address: IPv6 */ + router_has_ipv6_orport_result = true; + mock_router_ipv6_orport_calls = 0; + tt_ptr_op(circuit_choose_ip_ap_for_extend(NULL, &ipv6_ap), OP_EQ, &ipv6_ap); + tt_int_op(mock_router_ipv6_orport_calls, OP_EQ, 1); + + router_has_ipv6_orport_result = false; + mock_router_ipv6_orport_calls = 0; + tt_ptr_op(circuit_choose_ip_ap_for_extend(NULL, &ipv6_ap), OP_EQ, NULL); + tt_int_op(mock_router_ipv6_orport_calls, OP_EQ, 1); + + /* Two valid addresses */ + const tor_addr_port_t *chosen_addr = NULL; + + router_has_ipv6_orport_result = true; + mock_router_ipv6_orport_calls = 0; + chosen_addr = circuit_choose_ip_ap_for_extend(&ipv4_ap, &ipv6_ap); + tt_assert(chosen_addr == &ipv4_ap || chosen_addr == &ipv6_ap); + tt_int_op(mock_router_ipv6_orport_calls, OP_EQ, 1); + + router_has_ipv6_orport_result = false; + mock_router_ipv6_orport_calls = 0; + tt_ptr_op(circuit_choose_ip_ap_for_extend(&ipv4_ap, &ipv6_ap), + OP_EQ, &ipv4_ap); + tt_int_op(mock_router_ipv6_orport_calls, OP_EQ, 1); + + done: + UNMOCK(get_options); + or_options_free(fake_options); + mocked_options = NULL; + + UNMOCK(router_has_advertised_ipv6_orport); + + tor_free(fake_options); +} + static int mock_circuit_close_calls = 0; static void mock_circuit_mark_for_close_(circuit_t *circ, int reason, @@ -849,13 +940,6 @@ mock_channel_connect_for_circuit(const tor_addr_t *addr, return mock_channel_connect_nchan; } -static bool -mock_router_has_advertised_ipv6_orport(const or_options_t *options) -{ - (void)options; - return 1; -} - /* Test the different cases in circuit_open_connection_for_extend(). * Chooses different IP addresses depending on the first character in arg: * - 4: IPv4 @@ -886,6 +970,7 @@ test_circuit_open_connection_for_extend(void *arg) MOCK(router_has_advertised_ipv6_orport, mock_router_has_advertised_ipv6_orport); + router_has_ipv6_orport_result = true; setup_full_capture_of_logs(LOG_INFO); @@ -1457,6 +1542,7 @@ struct testcase_t circuitbuild_tests[] = { TEST_CIRCUIT(extend_state_valid, TT_FORK), TEST_CIRCUIT(extend_add_ed25519, TT_FORK), TEST_CIRCUIT(extend_lspec_valid, TT_FORK), + TEST_CIRCUIT(choose_ip_ap_for_extend, 0), TEST_CIRCUIT_PASSTHROUGH(open_connection_for_extend, TT_FORK, "4"), TEST_CIRCUIT_PASSTHROUGH(open_connection_for_extend, TT_FORK, "6"), TEST_CIRCUIT_PASSTHROUGH(open_connection_for_extend, TT_FORK, "dual-stack"), From bcec1ec071400464f7cefa4f0d493964984905ce Mon Sep 17 00:00:00 2001 From: teor Date: Wed, 29 Apr 2020 21:37:31 +1000 Subject: [PATCH 21/28] relay: Refactor choosing extend IPs Flatten the logic in circuit_choose_ip_ap_for_extend(). Part of 33817. --- src/feature/relay/circuitbuild_relay.c | 45 ++++++++++++++------------ 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/src/feature/relay/circuitbuild_relay.c b/src/feature/relay/circuitbuild_relay.c index 2bf08547d8..9420ea11a5 100644 --- a/src/feature/relay/circuitbuild_relay.c +++ b/src/feature/relay/circuitbuild_relay.c @@ -250,8 +250,27 @@ STATIC const tor_addr_port_t * circuit_choose_ip_ap_for_extend(const tor_addr_port_t *ipv4_ap, const tor_addr_port_t *ipv6_ap) { - /* If we could make an IPv4 or an IPv6 connection, make an IPv6 connection - * at random, with probability 1 in N. + const bool ipv6_supported = router_has_advertised_ipv6_orport(get_options()); + + /* If IPv6 is not supported, we can't use the IPv6 address. */ + if (!ipv6_supported) { + ipv6_ap = NULL; + } + + /* If there is no IPv6 address, IPv4 is always supported. + * Until clients include IPv6 ORPorts, and most relays support IPv6, + * this is the most common case. */ + if (!ipv6_ap) { + return ipv4_ap; + } + + /* If there is no IPv4 address, return the (possibly NULL) IPv6 address. */ + if (!ipv4_ap) { + return ipv6_ap; + } + + /* Now we have an IPv4 and an IPv6 address, and IPv6 is supported. + * So make an IPv6 connection at random, with probability 1 in N. * 1 means "always IPv6 (and no IPv4)" * 2 means "equal probability of IPv4 or IPv6" * ... (and so on) ... @@ -260,26 +279,12 @@ circuit_choose_ip_ap_for_extend(const tor_addr_port_t *ipv4_ap, */ #define IPV6_CONNECTION_ONE_IN_N 2 - /* IPv4 is always supported */ - const bool ipv6_supported = router_has_advertised_ipv6_orport(get_options()); - - if (ipv4_ap && ipv6_ap && ipv6_supported) { - /* Choose between IPv4 and IPv6 at random */ - bool choose_ipv6 = crypto_fast_rng_one_in_n(get_thread_fast_rng(), - IPV6_CONNECTION_ONE_IN_N); - if (choose_ipv6) { - return ipv6_ap; - } else { - return ipv4_ap; - } - } else if (ipv6_ap && ipv6_supported) { - /* There's only one valid address: try to use it */ + bool choose_ipv6 = crypto_fast_rng_one_in_n(get_thread_fast_rng(), + IPV6_CONNECTION_ONE_IN_N); + if (choose_ipv6) { return ipv6_ap; - } else if (ipv4_ap) { - return ipv4_ap; } else { - /* IPv6-only extend, but IPv6 is not supported. */ - return NULL; + return ipv4_ap; } } From 528a1fe985fbcbd348825c5ab98d0e9018453c5b Mon Sep 17 00:00:00 2001 From: teor Date: Wed, 15 Apr 2020 20:03:22 +1000 Subject: [PATCH 22/28] changes: file for 33817 --- changes/ticket33817 | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 changes/ticket33817 diff --git a/changes/ticket33817 b/changes/ticket33817 new file mode 100644 index 0000000000..9c22d084eb --- /dev/null +++ b/changes/ticket33817 @@ -0,0 +1,12 @@ + o Major features (IPv6, relay): + - Relays may extend circuits over IPv6, if the relay has an IPv6 ORPort, + and the client supplies the other relay's IPv6 ORPort in the EXTEND2 + cell. IPv6 extends will be used by the relay IPv6 ORPort self-tests in + 33222. Closes ticket 33817. + - Consider IPv6-only EXTEND2 cells valid on relays. Log a protocol warning + if the IPv4 or IPv6 address is an internal address, and internal + addresses are not allowed. But continue to use the other address, if it + is valid. Closes ticket 33817. + - If a relay can extend over IPv4 and IPv6, it chooses between them + uniformly at random. Closes ticket 33817. + - Re-use existing IPv6 connections for circuit extends. Closes ticket 33817. From f62b051e873aa4be564e9f83745902d4541f79f4 Mon Sep 17 00:00:00 2001 From: teor Date: Thu, 30 Apr 2020 05:54:39 +1000 Subject: [PATCH 23/28] Rename tor_addr_get_ipv6_bytes to tor_addr_copy_ipv6_bytes This is an automated commit, generated by this command: ./scripts/maint/rename_c_identifier.py \ tor_addr_get_ipv6_bytes tor_addr_copy_ipv6_bytes --- src/core/or/onion.c | 2 +- src/lib/net/address.c | 2 +- src/lib/net/address.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/or/onion.c b/src/core/or/onion.c index d73f981d2c..923f850f2c 100644 --- a/src/core/or/onion.c +++ b/src/core/or/onion.c @@ -674,7 +674,7 @@ extend_cell_format(uint8_t *command_out, uint16_t *len_out, extend2_cell_body_add_ls(cell, ls); ls->ls_type = LS_IPV6; ls->ls_len = 18; - tor_addr_get_ipv6_bytes((char *)ls->un_ipv6_addr, + tor_addr_copy_ipv6_bytes((char *)ls->un_ipv6_addr, &cell_in->orport_ipv6.addr); ls->un_ipv6_port = cell_in->orport_ipv6.port; } diff --git a/src/lib/net/address.c b/src/lib/net/address.c index 9a90093aef..af0e58fa4f 100644 --- a/src/lib/net/address.c +++ b/src/lib/net/address.c @@ -907,7 +907,7 @@ tor_addr_from_in6(tor_addr_t *dest, const struct in6_addr *in6) * src must be an IPv6 address, if it is not, log a warning, and clear * dest. */ void -tor_addr_get_ipv6_bytes(char *dest, const tor_addr_t *src) +tor_addr_copy_ipv6_bytes(char *dest, const tor_addr_t *src) { tor_assert(dest); tor_assert(src); diff --git a/src/lib/net/address.h b/src/lib/net/address.h index 651d6bc714..89b1cae322 100644 --- a/src/lib/net/address.h +++ b/src/lib/net/address.h @@ -309,7 +309,7 @@ void tor_addr_from_ipv6_bytes(tor_addr_t *dest, const char *bytes); #define tor_addr_from_in(dest, in) \ tor_addr_from_ipv4n((dest), (in)->s_addr); void tor_addr_from_in6(tor_addr_t *dest, const struct in6_addr *in6); -void tor_addr_get_ipv6_bytes(char *dest, const tor_addr_t *src); +void tor_addr_copy_ipv6_bytes(char *dest, const tor_addr_t *src); int tor_addr_is_null(const tor_addr_t *addr); int tor_addr_is_loopback(const tor_addr_t *addr); From cd7e2fc210349615c8e9cdb879f98cb0c9cac57b Mon Sep 17 00:00:00 2001 From: teor Date: Thu, 30 Apr 2020 06:17:18 +1000 Subject: [PATCH 24/28] net: Make all address bytes functions take uint8_t * Part of 33817. --- src/core/or/channeltls.c | 2 +- src/core/or/connection_edge.c | 2 +- src/core/or/onion.c | 6 +++--- src/core/or/policies.c | 4 ++-- src/core/or/relay.c | 8 ++++---- src/core/proto/proto_socks.c | 5 ++--- src/feature/client/addressmap.c | 2 +- src/lib/net/address.c | 17 +++++++++-------- src/lib/net/address.h | 8 ++++---- src/test/test_address.c | 4 ++-- src/tools/tor-resolve.c | 2 +- 11 files changed, 30 insertions(+), 30 deletions(-) diff --git a/src/core/or/channeltls.c b/src/core/or/channeltls.c index b424d02a59..be941c1762 100644 --- a/src/core/or/channeltls.c +++ b/src/core/or/channeltls.c @@ -1669,7 +1669,7 @@ tor_addr_from_netinfo_addr(tor_addr_t *tor_addr, } else if (type == NETINFO_ADDR_TYPE_IPV6 && len == 16) { const uint8_t *ipv6_bytes = netinfo_addr_getconstarray_addr_ipv6( netinfo_addr); - tor_addr_from_ipv6_bytes(tor_addr, (const char *)ipv6_bytes); + tor_addr_from_ipv6_bytes(tor_addr, ipv6_bytes); } else { log_fn(LOG_PROTOCOL_WARN, LD_OR, "Cannot read address from NETINFO " "- wrong type/length."); diff --git a/src/core/or/connection_edge.c b/src/core/or/connection_edge.c index 23c6e230cb..803bd82fc8 100644 --- a/src/core/or/connection_edge.c +++ b/src/core/or/connection_edge.c @@ -3531,7 +3531,7 @@ connection_ap_handshake_socks_resolved,(entry_connection_t *conn, } } else if (answer_type == RESOLVED_TYPE_IPV6 && answer_len == 16) { tor_addr_t a; - tor_addr_from_ipv6_bytes(&a, (char*)answer); + tor_addr_from_ipv6_bytes(&a, answer); if (! tor_addr_is_null(&a)) { client_dns_set_addressmap(conn, conn->socks_request->address, &a, diff --git a/src/core/or/onion.c b/src/core/or/onion.c index 923f850f2c..7b8f3c61fc 100644 --- a/src/core/or/onion.c +++ b/src/core/or/onion.c @@ -353,7 +353,7 @@ extend_cell_from_extend2_cell_body(extend_cell_t *cell_out, continue; found_ipv6 = 1; tor_addr_from_ipv6_bytes(&cell_out->orport_ipv6.addr, - (const char *)ls->un_ipv6_addr); + ls->un_ipv6_addr); cell_out->orport_ipv6.port = ls->un_ipv6_port; break; case LS_LEGACY_ID: @@ -674,8 +674,8 @@ extend_cell_format(uint8_t *command_out, uint16_t *len_out, extend2_cell_body_add_ls(cell, ls); ls->ls_type = LS_IPV6; ls->ls_len = 18; - tor_addr_copy_ipv6_bytes((char *)ls->un_ipv6_addr, - &cell_in->orport_ipv6.addr); + tor_addr_copy_ipv6_bytes(ls->un_ipv6_addr, + &cell_in->orport_ipv6.addr); ls->un_ipv6_port = cell_in->orport_ipv6.port; } cell->n_spec = n_specifiers; diff --git a/src/core/or/policies.c b/src/core/or/policies.c index dd4feaadfc..ea803f393d 100644 --- a/src/core/or/policies.c +++ b/src/core/or/policies.c @@ -167,7 +167,7 @@ policy_expand_unspec(smartlist_t **policy) } 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"); + (const uint8_t *)"\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); @@ -1005,7 +1005,7 @@ fascist_firewall_choose_address_ls(const smartlist_t *lspecs, * direct connection. */ if (have_v6) continue; tor_addr_from_ipv6_bytes(&addr_v6, - (const char *) link_specifier_getconstarray_un_ipv6_addr(ls)); + link_specifier_getconstarray_un_ipv6_addr(ls)); port_v6 = link_specifier_get_un_ipv6_port(ls); have_v6 = 1; break; diff --git a/src/core/or/relay.c b/src/core/or/relay.c index 00bbf77b99..b831af0ac3 100644 --- a/src/core/or/relay.c +++ b/src/core/or/relay.c @@ -867,7 +867,7 @@ connection_ap_process_end_not_open( ttl = (int)ntohl(get_uint32(cell->payload+RELAY_HEADER_SIZE+5)); } else if (rh->length == 17 || rh->length == 21) { tor_addr_from_ipv6_bytes(&addr, - (char*)(cell->payload+RELAY_HEADER_SIZE+1)); + (cell->payload+RELAY_HEADER_SIZE+1)); if (rh->length == 21) ttl = (int)ntohl(get_uint32(cell->payload+RELAY_HEADER_SIZE+17)); } @@ -1092,7 +1092,7 @@ connected_cell_parse(const relay_header_t *rh, const cell_t *cell, return -1; if (get_uint8(payload + 4) != 6) return -1; - tor_addr_from_ipv6_bytes(addr_out, (char*)(payload + 5)); + tor_addr_from_ipv6_bytes(addr_out, (payload + 5)); bytes = ntohl(get_uint32(payload + 21)); if (bytes <= INT32_MAX) *ttl_out = (int) bytes; @@ -1165,7 +1165,7 @@ resolved_cell_parse(const cell_t *cell, const relay_header_t *rh, if (answer_len != 16) goto err; addr = tor_malloc_zero(sizeof(*addr)); - tor_addr_from_ipv6_bytes(&addr->addr, (const char*) cp); + tor_addr_from_ipv6_bytes(&addr->addr, cp); cp += 16; addr->ttl = ntohl(get_uint32(cp)); cp += 4; @@ -3217,7 +3217,7 @@ decode_address_from_payload(tor_addr_t *addr_out, const uint8_t *payload, case RESOLVED_TYPE_IPV6: if (payload[1] != 16) return NULL; - tor_addr_from_ipv6_bytes(addr_out, (char*)(payload+2)); + tor_addr_from_ipv6_bytes(addr_out, (payload+2)); break; default: tor_addr_make_unspec(addr_out); diff --git a/src/core/proto/proto_socks.c b/src/core/proto/proto_socks.c index 6fd08b2273..c7d2af3d97 100644 --- a/src/core/proto/proto_socks.c +++ b/src/core/proto/proto_socks.c @@ -587,9 +587,8 @@ parse_socks5_client_request(const uint8_t *raw_data, socks_request_t *req, strlcpy(req->address, hostname, sizeof(req->address)); } break; case 4: { - const char *ipv6 = - (const char *)socks5_client_request_getarray_dest_addr_ipv6( - trunnel_req); + const uint8_t *ipv6 = + socks5_client_request_getarray_dest_addr_ipv6(trunnel_req); tor_addr_from_ipv6_bytes(&destaddr, ipv6); tor_addr_to_str(req->address, &destaddr, sizeof(req->address), 1); diff --git a/src/feature/client/addressmap.c b/src/feature/client/addressmap.c index cc97166f36..9ad2d7f934 100644 --- a/src/feature/client/addressmap.c +++ b/src/feature/client/addressmap.c @@ -902,7 +902,7 @@ get_random_virtual_addr(const virtual_addr_conf_t *conf, tor_addr_t *addr_out) } if (ipv6) - tor_addr_from_ipv6_bytes(addr_out, (char*) bytes); + tor_addr_from_ipv6_bytes(addr_out, bytes); else tor_addr_from_ipv4n(addr_out, get_uint32(bytes)); diff --git a/src/lib/net/address.c b/src/lib/net/address.c index af0e58fa4f..4193053ee1 100644 --- a/src/lib/net/address.c +++ b/src/lib/net/address.c @@ -608,7 +608,8 @@ tor_addr_parse_mask_ports(const char *s, family = AF_INET; tor_addr_from_ipv4h(addr_out, 0); } else if (flags & TAPMP_STAR_IPV6_ONLY) { - static char nil_bytes[16] = { [0]=0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 }; + static uint8_t nil_bytes[16] = + { [0]=0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 }; family = AF_INET6; tor_addr_from_ipv6_bytes(addr_out, nil_bytes); } else { @@ -629,7 +630,7 @@ tor_addr_parse_mask_ports(const char *s, tor_addr_from_ipv4h(addr_out, 0); any_flag = 1; } else if (!strcmp(address, "*6") && (flags & TAPMP_EXTENDED_STAR)) { - static char nil_bytes[16] = { [0]=0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 }; + static uint8_t nil_bytes[16] = { [0]=0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 }; family = AF_INET6; tor_addr_from_ipv6_bytes(addr_out, nil_bytes); any_flag = 1; @@ -887,7 +888,7 @@ tor_addr_from_ipv4n(tor_addr_t *dest, uint32_t v4addr) /** Set dest to equal the IPv6 address in the 16 bytes at * ipv6_bytes. */ void -tor_addr_from_ipv6_bytes(tor_addr_t *dest, const char *ipv6_bytes) +tor_addr_from_ipv6_bytes(tor_addr_t *dest, const uint8_t *ipv6_bytes) { tor_assert(dest); tor_assert(ipv6_bytes); @@ -900,14 +901,14 @@ tor_addr_from_ipv6_bytes(tor_addr_t *dest, const char *ipv6_bytes) void tor_addr_from_in6(tor_addr_t *dest, const struct in6_addr *in6) { - tor_addr_from_ipv6_bytes(dest, (const char*)in6->s6_addr); + tor_addr_from_ipv6_bytes(dest, in6->s6_addr); } /** Set the 16 bytes at dest to equal the IPv6 address src. * src must be an IPv6 address, if it is not, log a warning, and clear * dest. */ void -tor_addr_copy_ipv6_bytes(char *dest, const tor_addr_t *src) +tor_addr_copy_ipv6_bytes(uint8_t *dest, const tor_addr_t *src) { tor_assert(dest); tor_assert(src); @@ -1463,10 +1464,10 @@ ifconf_free_ifc_buf(struct ifconf *ifc) * into smartlist of tor_addr_t structures. */ STATIC smartlist_t * -ifreq_to_smartlist(char *buf, size_t buflen) +ifreq_to_smartlist(const uint8_t *buf, size_t buflen) { smartlist_t *result = smartlist_new(); - char *end = buf + buflen; + const uint8_t *end = buf + buflen; /* These acrobatics are due to alignment issues which trigger * undefined behaviour traps on OSX. */ @@ -1540,7 +1541,7 @@ get_interface_addresses_ioctl(int severity, sa_family_t family) /* Ensure we have least IFREQ_SIZE bytes unused at the end. Otherwise, we * don't know if we got everything during ioctl. */ } while (mult * IFREQ_SIZE - ifc.ifc_len <= IFREQ_SIZE); - result = ifreq_to_smartlist(ifc.ifc_buf, ifc.ifc_len); + result = ifreq_to_smartlist((const uint8_t *)ifc.ifc_buf, ifc.ifc_len); done: if (fd >= 0) diff --git a/src/lib/net/address.h b/src/lib/net/address.h index 89b1cae322..b5754087cc 100644 --- a/src/lib/net/address.h +++ b/src/lib/net/address.h @@ -304,12 +304,12 @@ void tor_addr_from_ipv4n(tor_addr_t *dest, uint32_t v4addr); * order. */ #define tor_addr_from_ipv4h(dest, v4addr) \ tor_addr_from_ipv4n((dest), htonl(v4addr)) -void tor_addr_from_ipv6_bytes(tor_addr_t *dest, const char *bytes); +void tor_addr_from_ipv6_bytes(tor_addr_t *dest, const uint8_t *bytes); /** Set dest to the IPv4 address incoded in in. */ #define tor_addr_from_in(dest, in) \ tor_addr_from_ipv4n((dest), (in)->s_addr); void tor_addr_from_in6(tor_addr_t *dest, const struct in6_addr *in6); -void tor_addr_copy_ipv6_bytes(char *dest, const tor_addr_t *src); +void tor_addr_copy_ipv6_bytes(uint8_t *dest, const tor_addr_t *src); int tor_addr_is_null(const tor_addr_t *addr); int tor_addr_is_loopback(const tor_addr_t *addr); @@ -400,8 +400,8 @@ STATIC struct smartlist_t *get_interface_addresses_win32(int severity, #endif /* defined(HAVE_IP_ADAPTER_TO_SMARTLIST) */ #ifdef HAVE_IFCONF_TO_SMARTLIST -STATIC struct smartlist_t *ifreq_to_smartlist(char *ifr, - size_t buflen); +STATIC struct smartlist_t *ifreq_to_smartlist(const uint8_t *ifr, + size_t buflen); STATIC struct smartlist_t *get_interface_addresses_ioctl(int severity, sa_family_t family); #endif /* defined(HAVE_IFCONF_TO_SMARTLIST) */ diff --git a/src/test/test_address.c b/src/test/test_address.c index e068c99d97..4cedbda347 100644 --- a/src/test/test_address.c +++ b/src/test/test_address.c @@ -460,7 +460,7 @@ test_address_ifreq_to_smartlist(void *arg) ifc->ifc_len = sizeof(struct ifreq); ifc->ifc_ifcu.ifcu_req = ifr; - results = ifreq_to_smartlist(ifc->ifc_buf,ifc->ifc_len); + results = ifreq_to_smartlist((const uint8_t *)ifc->ifc_buf,ifc->ifc_len); tt_int_op(smartlist_len(results),OP_EQ,1); tor_addr = smartlist_get(results, 0); @@ -483,7 +483,7 @@ test_address_ifreq_to_smartlist(void *arg) SMARTLIST_FOREACH(results, tor_addr_t *, t, tor_free(t)); smartlist_free(results); - results = ifreq_to_smartlist(ifc->ifc_buf,ifc->ifc_len); + results = ifreq_to_smartlist((const uint8_t *)ifc->ifc_buf,ifc->ifc_len); tt_int_op(smartlist_len(results),OP_EQ,2); tor_addr = smartlist_get(results, 0); diff --git a/src/tools/tor-resolve.c b/src/tools/tor-resolve.c index d369445dfc..e6d6bddcdb 100644 --- a/src/tools/tor-resolve.c +++ b/src/tools/tor-resolve.c @@ -509,7 +509,7 @@ do_resolve(const char *hostname, } else if (atype == SOCKS5_ATYPE_IPV6) { /* IPv6 address */ tor_addr_from_ipv6_bytes(result_addr, - (const char *)socks5_server_reply_getarray_bind_addr_ipv6(reply)); + socks5_server_reply_getarray_bind_addr_ipv6(reply)); } else if (atype == SOCKS5_ATYPE_HOSTNAME) { /* Domain name */ domainname_t *dn = From 496ddd2877051a43aaa7bbeb68bd5b255c71adc7 Mon Sep 17 00:00:00 2001 From: teor Date: Thu, 30 Apr 2020 06:26:40 +1000 Subject: [PATCH 25/28] relay: Refactor extend address validity function Rename the function, and give it a boolean return value. No behaviour change. Part of 33817. --- src/feature/relay/circuitbuild_relay.c | 59 +++++++++++++------------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/src/feature/relay/circuitbuild_relay.c b/src/feature/relay/circuitbuild_relay.c index 9420ea11a5..75b2767b82 100644 --- a/src/feature/relay/circuitbuild_relay.c +++ b/src/feature/relay/circuitbuild_relay.c @@ -125,16 +125,17 @@ circuit_extend_add_ed25519_helper(struct extend_cell_t *ec) /* Check if the address and port in the tor_addr_port_t ap are valid, * and are allowed by the current ExtendAllowPrivateAddresses config. * - * If they are valid, return 0. - * Otherwise, if they are invalid, return -1. + * If they are valid, return true. + * Otherwise, if they are invalid, return false. + * * If log_zero_addrs is true, log warnings about zero addresses at * log_level. If log_internal_addrs is true, log warnings about * internal addresses at log_level. */ -static int -circuit_extend_addr_port_helper(const struct tor_addr_port_t *ap, - bool log_zero_addrs, bool log_internal_addrs, - int log_level) +static bool +circuit_extend_addr_port_is_valid(const struct tor_addr_port_t *ap, + bool log_zero_addrs, bool log_internal_addrs, + int log_level) { /* It's safe to print the family. But we don't want to print the address, * unless specifically configured to do so. (Zero addresses aren't sensitive, @@ -147,7 +148,7 @@ circuit_extend_addr_port_helper(const struct tor_addr_port_t *ap, "%s address '%s'.", fmt_addr_family(&ap->addr), safe_str(fmt_addrport_ap(ap))); } - return -1; + return false; } if (tor_addr_is_internal(&ap->addr, 0) && @@ -158,10 +159,10 @@ circuit_extend_addr_port_helper(const struct tor_addr_port_t *ap, fmt_addr_family(&ap->addr), safe_str(fmt_and_decorate_addr(&ap->addr))); } - return -1; + return false; } - return 0; + return true; } /* Before replying to an extend cell, check the link specifiers in the extend @@ -185,27 +186,27 @@ circuit_extend_lspec_valid_helper(const struct extend_cell_t *ec, } /* Check the addresses, without logging */ - const int ipv4_valid = - (circuit_extend_addr_port_helper(&ec->orport_ipv4, false, false, 0) == 0); - const int ipv6_valid = - (circuit_extend_addr_port_helper(&ec->orport_ipv6, false, false, 0) == 0); + const int ipv4_valid = circuit_extend_addr_port_is_valid(&ec->orport_ipv4, + false, false, 0); + const int ipv6_valid = circuit_extend_addr_port_is_valid(&ec->orport_ipv6, + false, false, 0); /* We need at least one valid address */ if (!ipv4_valid && !ipv6_valid) { /* Now, log the invalid addresses at protocol warning level */ - circuit_extend_addr_port_helper(&ec->orport_ipv4, true, true, - LOG_PROTOCOL_WARN); - circuit_extend_addr_port_helper(&ec->orport_ipv6, true, true, - LOG_PROTOCOL_WARN); + circuit_extend_addr_port_is_valid(&ec->orport_ipv4, + true, true, LOG_PROTOCOL_WARN); + circuit_extend_addr_port_is_valid(&ec->orport_ipv6, + true, true, LOG_PROTOCOL_WARN); /* And fail */ return -1; } else if (!ipv4_valid) { /* Always log unexpected internal addresses, but go on to use the other * valid address */ - circuit_extend_addr_port_helper(&ec->orport_ipv4, false, true, - LOG_PROTOCOL_WARN); + circuit_extend_addr_port_is_valid(&ec->orport_ipv4, + false, true, LOG_PROTOCOL_WARN); } else if (!ipv6_valid) { - circuit_extend_addr_port_helper(&ec->orport_ipv6, false, true, - LOG_PROTOCOL_WARN); + circuit_extend_addr_port_is_valid(&ec->orport_ipv6, + false, true, LOG_PROTOCOL_WARN); } IF_BUG_ONCE(circ->magic != OR_CIRCUIT_MAGIC) { @@ -316,10 +317,10 @@ circuit_open_connection_for_extend(const struct extend_cell_t *ec, } /* Check the addresses, without logging */ - const int ipv4_valid = - (circuit_extend_addr_port_helper(&ec->orport_ipv4, false, false, 0) == 0); - const int ipv6_valid = - (circuit_extend_addr_port_helper(&ec->orport_ipv6, false, false, 0) == 0); + const int ipv4_valid = circuit_extend_addr_port_is_valid(&ec->orport_ipv4, + false, false, 0); + const int ipv6_valid = circuit_extend_addr_port_is_valid(&ec->orport_ipv6, + false, false, 0); IF_BUG_ONCE(!ipv4_valid && !ipv6_valid) { /* circuit_extend_lspec_valid_helper() should have caught this */ @@ -412,10 +413,10 @@ circuit_extend(struct cell_t *cell, struct circuit_t *circ) return -1; /* Check the addresses, without logging */ - const int ipv4_valid = - (circuit_extend_addr_port_helper(&ec.orport_ipv4, false, false, 0) == 0); - const int ipv6_valid = - (circuit_extend_addr_port_helper(&ec.orport_ipv6, false, false, 0) == 0); + const int ipv4_valid = circuit_extend_addr_port_is_valid(&ec.orport_ipv4, + false, false, 0); + const int ipv6_valid = circuit_extend_addr_port_is_valid(&ec.orport_ipv6, + false, false, 0); IF_BUG_ONCE(!ipv4_valid && !ipv6_valid) { /* circuit_extend_lspec_valid_helper() should have caught this */ return -1; From 066d2deb3d322e960600d80739739199d8c4cfa6 Mon Sep 17 00:00:00 2001 From: teor Date: Thu, 30 Apr 2020 06:31:05 +1000 Subject: [PATCH 26/28] channel: Refactor matches target address function Refactor channel_matches_target_addr_for_extend() to return a boolean result. Part of 33817. --- src/core/or/channel.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/core/or/channel.c b/src/core/or/channel.c index 93245ce81e..a05554472f 100644 --- a/src/core/or/channel.c +++ b/src/core/or/channel.c @@ -85,7 +85,7 @@ /* Static function prototypes */ -static int channel_matches_target_addr_for_extend( +static bool channel_matches_target_addr_for_extend( channel_t *chan, const tor_addr_t *target_ipv4_addr, const tor_addr_t *target_ipv6_addr); @@ -2412,7 +2412,7 @@ channel_get_for_extend,(const char *rsa_id_digest, continue; } - const int matches_target = + const bool matches_target = channel_matches_target_addr_for_extend(chan, target_ipv4_addr, target_ipv6_addr); @@ -3317,7 +3317,7 @@ channel_matches_extend_info(channel_t *chan, extend_info_t *extend_info) * This function calls into the lower layer and asks if this channel thinks * it matches the target addresses for circuit extension purposes. */ -int +static bool channel_matches_target_addr_for_extend(channel_t *chan, const tor_addr_t *target_ipv4_addr, const tor_addr_t *target_ipv6_addr) @@ -3326,15 +3326,15 @@ channel_matches_target_addr_for_extend(channel_t *chan, tor_assert(chan->matches_target); IF_BUG_ONCE(!target_ipv4_addr && !target_ipv6_addr) - return 0; + return false; if (target_ipv4_addr && chan->matches_target(chan, target_ipv4_addr)) - return 1; + return true; if (target_ipv6_addr && chan->matches_target(chan, target_ipv6_addr)) - return 1; + return true; - return 0; + return false; } /** From 15a4180a7e041531d6923a41684e9e6ffc833760 Mon Sep 17 00:00:00 2001 From: teor Date: Thu, 30 Apr 2020 06:47:46 +1000 Subject: [PATCH 27/28] relay: Refactor can extend over IPv6 checks Split "can extend over IPv6" and "has advertised IPv6 ORPort" into separate functions. They currently have the same result, but this may change in 33818 with ExtendAllowIPv6Addresses. Part of 33817. --- src/feature/relay/circuitbuild_relay.c | 2 +- src/feature/relay/router.c | 13 ++++- src/feature/relay/router.h | 4 +- src/test/test_circuitbuild.c | 76 +++++++++++++------------- 4 files changed, 52 insertions(+), 43 deletions(-) diff --git a/src/feature/relay/circuitbuild_relay.c b/src/feature/relay/circuitbuild_relay.c index 75b2767b82..b89866b477 100644 --- a/src/feature/relay/circuitbuild_relay.c +++ b/src/feature/relay/circuitbuild_relay.c @@ -251,7 +251,7 @@ STATIC const tor_addr_port_t * circuit_choose_ip_ap_for_extend(const tor_addr_port_t *ipv4_ap, const tor_addr_port_t *ipv6_ap) { - const bool ipv6_supported = router_has_advertised_ipv6_orport(get_options()); + const bool ipv6_supported = router_can_extend_over_ipv6(get_options()); /* If IPv6 is not supported, we can't use the IPv6 address. */ if (!ipv6_supported) { diff --git a/src/feature/relay/router.c b/src/feature/relay/router.c index b0a56012db..2858371af5 100644 --- a/src/feature/relay/router.c +++ b/src/feature/relay/router.c @@ -1491,14 +1491,23 @@ router_get_advertised_ipv6_or_ap(const or_options_t *options, } /** Returns true if this router has an advertised IPv6 ORPort. */ -MOCK_IMPL(bool, -router_has_advertised_ipv6_orport,(const or_options_t *options)) +bool +router_has_advertised_ipv6_orport(const or_options_t *options) { tor_addr_port_t ipv6_ap; router_get_advertised_ipv6_or_ap(options, &ipv6_ap); return tor_addr_port_is_valid_ap(&ipv6_ap, 0); } +/** Returns true if this router has an advertised IPv6 ORPort. */ +MOCK_IMPL(bool, +router_can_extend_over_ipv6,(const or_options_t *options)) +{ + /* We might add some extra checks here, such as ExtendAllowIPv6Addresses + * from ticket 33818. */ + return router_has_advertised_ipv6_orport(options); +} + /** Return the port that we should advertise as our DirPort; * this is one of three possibilities: * The one that is passed as dirport if the DirPort option is 0, or diff --git a/src/feature/relay/router.h b/src/feature/relay/router.h index 1a262a6799..39c550dd25 100644 --- a/src/feature/relay/router.h +++ b/src/feature/relay/router.h @@ -68,8 +68,8 @@ uint16_t router_get_active_listener_port_by_type_af(int listener_type, uint16_t router_get_advertised_or_port(const or_options_t *options); void router_get_advertised_ipv6_or_ap(const or_options_t *options, tor_addr_port_t *ipv6_ap_out); -MOCK_DECL(bool, router_has_advertised_ipv6_orport,( - const or_options_t *options)); +bool router_has_advertised_ipv6_orport(const or_options_t *options); +MOCK_DECL(bool, router_can_extend_over_ipv6,(const or_options_t *options)); uint16_t router_get_advertised_or_port_by_af(const or_options_t *options, sa_family_t family); uint16_t router_get_advertised_dir_port(const or_options_t *options, diff --git a/src/test/test_circuitbuild.c b/src/test/test_circuitbuild.c index ed75d102b9..03fd176ead 100644 --- a/src/test/test_circuitbuild.c +++ b/src/test/test_circuitbuild.c @@ -821,14 +821,14 @@ test_circuit_extend_lspec_valid(void *arg) tor_free(p_chan); } -static bool router_has_ipv6_orport_result = false; -static int mock_router_ipv6_orport_calls = 0; +static bool can_extend_over_ipv6_result = false; +static int mock_router_can_extend_over_ipv6_calls = 0; static bool -mock_router_has_advertised_ipv6_orport(const or_options_t *options) +mock_router_can_extend_over_ipv6(const or_options_t *options) { (void)options; - mock_router_ipv6_orport_calls++; - return router_has_ipv6_orport_result; + mock_router_can_extend_over_ipv6_calls++; + return can_extend_over_ipv6_result; } /* Test the different cases in circuit_choose_ip_ap_for_extend(). */ @@ -849,65 +849,65 @@ test_circuit_choose_ip_ap_for_extend(void *arg) MOCK(get_options, mock_get_options); mocked_options = fake_options; - MOCK(router_has_advertised_ipv6_orport, - mock_router_has_advertised_ipv6_orport); - router_has_ipv6_orport_result = true; - mock_router_ipv6_orport_calls = 0; + MOCK(router_can_extend_over_ipv6, + mock_router_can_extend_over_ipv6); + can_extend_over_ipv6_result = true; + mock_router_can_extend_over_ipv6_calls = 0; /* No valid addresses */ - router_has_ipv6_orport_result = true; - mock_router_ipv6_orport_calls = 0; + can_extend_over_ipv6_result = true; + mock_router_can_extend_over_ipv6_calls = 0; tt_ptr_op(circuit_choose_ip_ap_for_extend(NULL, NULL), OP_EQ, NULL); - tt_int_op(mock_router_ipv6_orport_calls, OP_EQ, 1); + tt_int_op(mock_router_can_extend_over_ipv6_calls, OP_EQ, 1); - router_has_ipv6_orport_result = false; - mock_router_ipv6_orport_calls = 0; + can_extend_over_ipv6_result = false; + mock_router_can_extend_over_ipv6_calls = 0; tt_ptr_op(circuit_choose_ip_ap_for_extend(NULL, NULL), OP_EQ, NULL); - tt_int_op(mock_router_ipv6_orport_calls, OP_EQ, 1); + tt_int_op(mock_router_can_extend_over_ipv6_calls, OP_EQ, 1); /* One valid address: IPv4 */ - router_has_ipv6_orport_result = true; - mock_router_ipv6_orport_calls = 0; + can_extend_over_ipv6_result = true; + mock_router_can_extend_over_ipv6_calls = 0; tt_ptr_op(circuit_choose_ip_ap_for_extend(&ipv4_ap, NULL), OP_EQ, &ipv4_ap); - tt_int_op(mock_router_ipv6_orport_calls, OP_EQ, 1); + tt_int_op(mock_router_can_extend_over_ipv6_calls, OP_EQ, 1); - router_has_ipv6_orport_result = false; - mock_router_ipv6_orport_calls = 0; + can_extend_over_ipv6_result = false; + mock_router_can_extend_over_ipv6_calls = 0; tt_ptr_op(circuit_choose_ip_ap_for_extend(&ipv4_ap, NULL), OP_EQ, &ipv4_ap); - tt_int_op(mock_router_ipv6_orport_calls, OP_EQ, 1); + tt_int_op(mock_router_can_extend_over_ipv6_calls, OP_EQ, 1); /* One valid address: IPv6 */ - router_has_ipv6_orport_result = true; - mock_router_ipv6_orport_calls = 0; + can_extend_over_ipv6_result = true; + mock_router_can_extend_over_ipv6_calls = 0; tt_ptr_op(circuit_choose_ip_ap_for_extend(NULL, &ipv6_ap), OP_EQ, &ipv6_ap); - tt_int_op(mock_router_ipv6_orport_calls, OP_EQ, 1); + tt_int_op(mock_router_can_extend_over_ipv6_calls, OP_EQ, 1); - router_has_ipv6_orport_result = false; - mock_router_ipv6_orport_calls = 0; + can_extend_over_ipv6_result = false; + mock_router_can_extend_over_ipv6_calls = 0; tt_ptr_op(circuit_choose_ip_ap_for_extend(NULL, &ipv6_ap), OP_EQ, NULL); - tt_int_op(mock_router_ipv6_orport_calls, OP_EQ, 1); + tt_int_op(mock_router_can_extend_over_ipv6_calls, OP_EQ, 1); /* Two valid addresses */ const tor_addr_port_t *chosen_addr = NULL; - router_has_ipv6_orport_result = true; - mock_router_ipv6_orport_calls = 0; + can_extend_over_ipv6_result = true; + mock_router_can_extend_over_ipv6_calls = 0; chosen_addr = circuit_choose_ip_ap_for_extend(&ipv4_ap, &ipv6_ap); tt_assert(chosen_addr == &ipv4_ap || chosen_addr == &ipv6_ap); - tt_int_op(mock_router_ipv6_orport_calls, OP_EQ, 1); + tt_int_op(mock_router_can_extend_over_ipv6_calls, OP_EQ, 1); - router_has_ipv6_orport_result = false; - mock_router_ipv6_orport_calls = 0; + can_extend_over_ipv6_result = false; + mock_router_can_extend_over_ipv6_calls = 0; tt_ptr_op(circuit_choose_ip_ap_for_extend(&ipv4_ap, &ipv6_ap), OP_EQ, &ipv4_ap); - tt_int_op(mock_router_ipv6_orport_calls, OP_EQ, 1); + tt_int_op(mock_router_can_extend_over_ipv6_calls, OP_EQ, 1); done: UNMOCK(get_options); or_options_free(fake_options); mocked_options = NULL; - UNMOCK(router_has_advertised_ipv6_orport); + UNMOCK(router_can_extend_over_ipv6); tor_free(fake_options); } @@ -968,9 +968,9 @@ test_circuit_open_connection_for_extend(void *arg) mock_channel_connect_calls = 0; mock_channel_connect_nchan = NULL; - MOCK(router_has_advertised_ipv6_orport, - mock_router_has_advertised_ipv6_orport); - router_has_ipv6_orport_result = true; + MOCK(router_can_extend_over_ipv6, + mock_router_can_extend_over_ipv6); + can_extend_over_ipv6_result = true; setup_full_capture_of_logs(LOG_INFO); @@ -1110,7 +1110,7 @@ test_circuit_open_connection_for_extend(void *arg) or_options_free(fake_options); mocked_options = NULL; - UNMOCK(router_has_advertised_ipv6_orport); + UNMOCK(router_can_extend_over_ipv6); tor_free(ec); tor_free(circ->n_hop); From ed4420b4b8b891bd29587a27cb51e9184ff3e093 Mon Sep 17 00:00:00 2001 From: teor Date: Thu, 30 Apr 2020 06:52:43 +1000 Subject: [PATCH 28/28] practracker: Allow an extra line in tor_addr_parse_mask_ports() We added a cast, and wrapped a line. --- scripts/maint/practracker/exceptions.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/maint/practracker/exceptions.txt b/scripts/maint/practracker/exceptions.txt index cf2ec701eb..bf37d476f2 100644 --- a/scripts/maint/practracker/exceptions.txt +++ b/scripts/maint/practracker/exceptions.txt @@ -305,7 +305,7 @@ problem function-size /src/lib/encoding/confline.c:parse_config_line_from_str_ve problem function-size /src/lib/encoding/cstring.c:unescape_string() 108 problem function-size /src/lib/fs/dir.c:check_private_dir() 230 problem function-size /src/lib/math/prob_distr.c:sample_uniform_interval() 145 -problem function-size /src/lib/net/address.c:tor_addr_parse_mask_ports() 194 +problem function-size /src/lib/net/address.c:tor_addr_parse_mask_ports() 195 problem function-size /src/lib/net/address.c:tor_addr_compare_masked() 110 problem function-size /src/lib/net/inaddr.c:tor_inet_pton() 107 problem function-size /src/lib/net/socketpair.c:tor_ersatz_socketpair() 102