diff --git a/src/core/or/or.h b/src/core/or/or.h index 28211b48c6..7e02da6648 100644 --- a/src/core/or/or.h +++ b/src/core/or/or.h @@ -815,6 +815,18 @@ typedef struct protover_summary_flags_t { * accept EXTEND2 cells. This requires Relay=2. */ unsigned int supports_extend2_cells:1; + /** True iff this router has a version or protocol list that allows it to + * accept IPv6 connections. This requires Relay=2 or Relay=3. */ + unsigned int supports_accepting_ipv6_extends:1; + + /** True iff this router has a version or protocol list that allows it to + * initiate IPv6 connections. This requires Relay=3. */ + unsigned int supports_initiating_ipv6_extends:1; + + /** True iff this router has a version or protocol list that allows it to + * consider IPv6 connections canonical. This requires Relay=3. */ + unsigned int supports_canonical_ipv6_conns:1; + /** True iff this router has a protocol list that allows it to negotiate * ed25519 identity keys on a link handshake with us. This * requires LinkAuth=3. */ diff --git a/src/core/or/versions.c b/src/core/or/versions.c index 2a33bf68fe..a8dfe7e61c 100644 --- a/src/core/or/versions.c +++ b/src/core/or/versions.c @@ -437,6 +437,13 @@ memoize_protover_summary(protover_summary_flags_t *out, out->supports_extend2_cells = protocol_list_supports_protocol(protocols, PRT_RELAY, 2); + out->supports_accepting_ipv6_extends = ( + protocol_list_supports_protocol(protocols, PRT_RELAY, 2) || + protocol_list_supports_protocol(protocols, PRT_RELAY, 3)); + out->supports_initiating_ipv6_extends = + protocol_list_supports_protocol(protocols, PRT_RELAY, 3); + out->supports_canonical_ipv6_conns = + protocol_list_supports_protocol(protocols, PRT_RELAY, 3); out->supports_ed25519_link_handshake_compat = protocol_list_supports_protocol(protocols, PRT_LINKAUTH, 3); diff --git a/src/feature/nodelist/nodelist.c b/src/feature/nodelist/nodelist.c index 6a8e1723de..a553390628 100644 --- a/src/feature/nodelist/nodelist.c +++ b/src/feature/nodelist/nodelist.c @@ -1133,7 +1133,7 @@ node_ed25519_id_matches(const node_t *node, const ed25519_public_key_t *id) /** Dummy object that should be unreturnable. Used to ensure that * node_get_protover_summary_flags() always returns non-NULL. */ static const protover_summary_flags_t zero_protover_flags = { - 0,0,0,0,0,0,0,0,0 + 0,0,0,0,0,0,0,0,0,0,0,0 }; /** Return the protover_summary_flags for a given node. */ @@ -1219,6 +1219,56 @@ node_supports_establish_intro_dos_extension(const node_t *node) supports_establish_intro_dos_extension; } +/** Return true iff node can initiate IPv6 extends (Relay=3). + * + * This check should only be performed by client path selection code. + * + * Extending relays should check their own IPv6 support using + * router_can_extend_over_ipv6(). Like other extends, they should not verify + * the link specifiers in the extend cell against the consensus, because it + * may be out of date. */ +bool +node_supports_initiating_ipv6_extends(const node_t *node) +{ + tor_assert(node); + + /* Relays can't initiate an IPv6 extend, unless they have an IPv6 ORPort. */ + if (!node_has_ipv6_orport(node)) { + return 0; + } + + /* Initiating relays also need to support the relevant protocol version. */ + return + node_get_protover_summary_flags(node)->supports_initiating_ipv6_extends; +} + +/** Return true iff node can accept IPv6 extends (Relay=2 or Relay=3) + * from other relays. If need_canonical_ipv6_conn is true, also check + * if the relay supports canonical IPv6 connections (Relay=3 only). + * + * This check should only be performed by client path selection code. + */ +bool +node_supports_accepting_ipv6_extends(const node_t *node, + bool need_canonical_ipv6_conn) +{ + tor_assert(node); + + /* Relays can't accept an IPv6 extend, unless they have an IPv6 ORPort. */ + if (!node_has_ipv6_orport(node)) { + return 0; + } + + /* Accepting relays also need to support the relevant protocol version. */ + if (need_canonical_ipv6_conn) { + return + node_get_protover_summary_flags(node)->supports_canonical_ipv6_conns; + } else { + return + node_get_protover_summary_flags(node)->supports_accepting_ipv6_extends; + } +} + /** Return the RSA ID key's SHA1 digest for the provided node. */ const uint8_t * node_get_rsa_id_digest(const node_t *node) diff --git a/src/feature/nodelist/nodelist.h b/src/feature/nodelist/nodelist.h index 57ab2d5913..991cec319b 100644 --- a/src/feature/nodelist/nodelist.h +++ b/src/feature/nodelist/nodelist.h @@ -81,6 +81,11 @@ int node_supports_v3_hsdir(const node_t *node); int node_supports_ed25519_hs_intro(const node_t *node); int node_supports_v3_rendezvous_point(const node_t *node); int node_supports_establish_intro_dos_extension(const node_t *node); +bool node_supports_initiating_ipv6_extends(const node_t *node); +bool node_supports_accepting_ipv6_extends( + const node_t *node, + bool need_canonical_ipv6_conn); + const uint8_t *node_get_rsa_id_digest(const node_t *node); MOCK_DECL(smartlist_t *,node_get_link_specifier_smartlist,(const node_t *node, bool direct_conn)); diff --git a/src/feature/relay/router.c b/src/feature/relay/router.c index 1a35e27e1c..85788c07ea 100644 --- a/src/feature/relay/router.c +++ b/src/feature/relay/router.c @@ -1499,7 +1499,22 @@ router_has_advertised_ipv6_orport(const or_options_t *options) return tor_addr_port_is_valid_ap(&ipv6_ap, 0); } -/** Returns true if this router has an advertised IPv6 ORPort. */ +/** Returns true if this router can extend over IPv6. + * + * This check should only be performed by relay extend code. + * + * Clients should check if relays can initiate and accept IPv6 extends using + * node_supports_initiating_ipv6_extends() and + * node_supports_accepting_ipv6_extends(). + * + * As with other extends, relays should assume the client has already + * performed the relevant checks for the next hop. (Otherwise, relays that + * have just added IPv6 ORPorts won't be able to self-test those ORPorts.) + * + * Accepting relays don't need to perform any IPv6-specific checks before + * accepting a connection, because having an IPv6 ORPort implies support for + * the relevant protocol version. + */ MOCK_IMPL(bool, router_can_extend_over_ipv6,(const or_options_t *options)) {