relay/circuitbuild: Re-use IPv6 connections for circuits

Search for existing connections using the remote IPv4 and IPv6
addresses.

Part of 33817.
This commit is contained in:
teor 2020-04-15 13:04:33 +10:00
parent ec5f4f3c5a
commit 16f3f6a1af
7 changed files with 96 additions and 40 deletions

View File

@ -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;
}
/**

View File

@ -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));

View File

@ -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. */

View File

@ -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);

View File

@ -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 <b>a</b>, or NULL if <b>a</b> is not

View File

@ -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.");

View File

@ -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);