mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-24 04:13:28 +01:00
Outbindbindaddress variants for Exit and OR.
Allow separation of exit and relay traffic to different source IP addresses (Ticket #17975). Written by Michael Sonntag.
This commit is contained in:
parent
ad382049ed
commit
81c78ec755
2
changes/change_separate_exit_and_relay.txt
Normal file
2
changes/change_separate_exit_and_relay.txt
Normal file
@ -0,0 +1,2 @@
|
||||
- Minor features:
|
||||
- Allow separation of exit and relay traffic to different source IP addresses (Ticket #17975). Written by Michael Sonntag.
|
@ -640,6 +640,20 @@ GENERAL OPTIONS
|
||||
This setting will be ignored for connections to the loopback addresses
|
||||
(127.0.0.0/8 and ::1).
|
||||
|
||||
[[OutboundBindAddressOR]] **OutboundBindAddressOR** __IP__::
|
||||
Make all outbound non-exit (=relay and other) connections originate from the IP
|
||||
address specified. This option overrides **OutboundBindAddress** for the same
|
||||
IP version. This option may be used twice, once with an IPv4 address and once
|
||||
with an IPv6 address. This setting will be ignored for connections to the
|
||||
loopback addresses (127.0.0.0/8 and ::1).
|
||||
|
||||
[[OutboundBindAddressExit]] **OutboundBindAddressExit** __IP__::
|
||||
Make all outbound exit connections originate from the IP address specified. This
|
||||
option overrides **OutboundBindAddress** for the same IP version. This option
|
||||
may be used twice, once with an IPv4 address and once with an IPv6 address. This
|
||||
setting will be ignored for connections to the loopback addresses (127.0.0.0/8
|
||||
and ::1).
|
||||
|
||||
[[PidFile]] **PidFile** __FILE__::
|
||||
On startup, write our PID to FILE. On clean shutdown, remove
|
||||
FILE. Can not be changed while tor is running.
|
||||
|
@ -95,7 +95,12 @@
|
||||
|
||||
## If you have multiple network interfaces, you can specify one for
|
||||
## outgoing traffic to use.
|
||||
# OutboundBindAddress 10.0.0.5
|
||||
## OutboundBindAddressExit will be used for all exit traffic, while
|
||||
## OutboundBindAddressOR will be used for all other connections.
|
||||
## If you do not wish to differentiate, use OutboundBindAddress to
|
||||
## specify the same address for both in a single line.
|
||||
#OutboundBindAddressExit 10.0.0.4
|
||||
#OutboundBindAddressOR 10.0.0.5
|
||||
|
||||
## A handle for your relay, so people don't have to refer to it by key.
|
||||
## Nicknames must be between 1 and 19 characters inclusive, and must
|
||||
|
116
src/or/config.c
116
src/or/config.c
@ -411,6 +411,8 @@ static config_var_t option_vars_[] = {
|
||||
V(ORListenAddress, LINELIST, NULL),
|
||||
VPORT(ORPort),
|
||||
V(OutboundBindAddress, LINELIST, NULL),
|
||||
V(OutboundBindAddressOR, LINELIST, NULL),
|
||||
V(OutboundBindAddressExit, LINELIST, NULL),
|
||||
|
||||
OBSOLETE("PathBiasDisableRate"),
|
||||
V(PathBiasCircThreshold, INT, "-1"),
|
||||
@ -7917,57 +7919,81 @@ getinfo_helper_config(control_connection_t *conn,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check whether an address has already been set against the options
|
||||
* depending on address family and destination type. Any exsting
|
||||
* value will lead to a fail, even if it is the same value. If not
|
||||
* set and not only validating, copy it into this location too.
|
||||
* Returns 0 on success or -1 if this address is already set.
|
||||
*/
|
||||
static int
|
||||
verify_and_store_outbound_address(sa_family_t family, tor_addr_t *addr,
|
||||
outbound_addr_t type, or_options_t *options, int validate_only)
|
||||
{
|
||||
if (type<0 || type>=OUTBOUND_ADDR_MAX
|
||||
|| (family!=AF_INET && family!=AF_INET6)) {
|
||||
return -1;
|
||||
}
|
||||
int fam_index=0;
|
||||
if (family==AF_INET6) {
|
||||
fam_index=1;
|
||||
}
|
||||
tor_addr_t *dest=&options->OutboundBindAddresses[type][fam_index];
|
||||
if (!tor_addr_is_null(dest)) {
|
||||
return -1;
|
||||
}
|
||||
if (!validate_only) {
|
||||
tor_addr_copy(dest, addr);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Parse a list of address lines for a specific destination type.
|
||||
* Will store them into the options if not validate_only. If a
|
||||
* problem occurs, a suitable error message is store in msg.
|
||||
* Returns 0 on success or -1 if any address is already set.
|
||||
*/
|
||||
static int
|
||||
parse_outbound_address_lines(const config_line_t *lines, outbound_addr_t type,
|
||||
or_options_t *options, int validate_only, char **msg)
|
||||
{
|
||||
tor_addr_t addr;
|
||||
sa_family_t family;
|
||||
while (lines) {
|
||||
family = tor_addr_parse(&addr, lines->value);
|
||||
if (verify_and_store_outbound_address(family, &addr, type,
|
||||
options, validate_only)) {
|
||||
if (msg)
|
||||
tor_asprintf(msg, "Multiple%s%s outbound bind addresses "
|
||||
"configured: %s",
|
||||
family==AF_INET?" IPv4":(family==AF_INET6?" IPv6":""),
|
||||
type==OUTBOUND_ADDR_OR?" OR":
|
||||
(type==OUTBOUND_ADDR_EXIT?" exit":""), lines->value);
|
||||
return -1;
|
||||
}
|
||||
lines = lines->next;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Parse outbound bind address option lines. If <b>validate_only</b>
|
||||
* is not 0 update OutboundBindAddressIPv4_ and
|
||||
* OutboundBindAddressIPv6_ in <b>options</b>. On failure, set
|
||||
* <b>msg</b> (if provided) to a newly allocated string containing a
|
||||
* description of the problem and return -1. */
|
||||
* is not 0 update OutboundBindAddresses in <b>options</b>.
|
||||
* Only one address can be set for any of these values.
|
||||
* On failure, set <b>msg</b> (if provided) to a newly allocated string
|
||||
* containing a description of the problem and return -1.
|
||||
*/
|
||||
static int
|
||||
parse_outbound_addresses(or_options_t *options, int validate_only, char **msg)
|
||||
{
|
||||
const config_line_t *lines = options->OutboundBindAddress;
|
||||
int found_v4 = 0, found_v6 = 0;
|
||||
|
||||
if (!validate_only) {
|
||||
memset(&options->OutboundBindAddressIPv4_, 0,
|
||||
sizeof(options->OutboundBindAddressIPv4_));
|
||||
memset(&options->OutboundBindAddressIPv6_, 0,
|
||||
sizeof(options->OutboundBindAddressIPv6_));
|
||||
}
|
||||
while (lines) {
|
||||
tor_addr_t addr, *dst_addr = NULL;
|
||||
int af = tor_addr_parse(&addr, lines->value);
|
||||
switch (af) {
|
||||
case AF_INET:
|
||||
if (found_v4) {
|
||||
if (msg)
|
||||
tor_asprintf(msg, "Multiple IPv4 outbound bind addresses "
|
||||
"configured: %s", lines->value);
|
||||
return -1;
|
||||
}
|
||||
found_v4 = 1;
|
||||
dst_addr = &options->OutboundBindAddressIPv4_;
|
||||
break;
|
||||
case AF_INET6:
|
||||
if (found_v6) {
|
||||
if (msg)
|
||||
tor_asprintf(msg, "Multiple IPv6 outbound bind addresses "
|
||||
"configured: %s", lines->value);
|
||||
return -1;
|
||||
}
|
||||
found_v6 = 1;
|
||||
dst_addr = &options->OutboundBindAddressIPv6_;
|
||||
break;
|
||||
default:
|
||||
if (msg)
|
||||
tor_asprintf(msg, "Outbound bind address '%s' didn't parse.",
|
||||
lines->value);
|
||||
return -1;
|
||||
}
|
||||
if (!validate_only)
|
||||
tor_addr_copy(dst_addr, &addr);
|
||||
lines = lines->next;
|
||||
memset(&options->OutboundBindAddresses, 0,
|
||||
sizeof(options->OutboundBindAddresses));
|
||||
}
|
||||
parse_outbound_address_lines(options->OutboundBindAddress,
|
||||
OUTBOUND_ADDR_EXIT_AND_OR, options, validate_only, msg);
|
||||
parse_outbound_address_lines(options->OutboundBindAddressOR,
|
||||
OUTBOUND_ADDR_OR, options, validate_only, msg);
|
||||
parse_outbound_address_lines(options->OutboundBindAddressExit,
|
||||
OUTBOUND_ADDR_EXIT, options, validate_only, msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -134,6 +134,8 @@ static int connection_read_https_proxy_response(connection_t *conn);
|
||||
static void connection_send_socks5_connect(connection_t *conn);
|
||||
static const char *proxy_type_to_string(int proxy_type);
|
||||
static int get_proxy_type(void);
|
||||
const tor_addr_t *conn_get_outbound_address(sa_family_t family,
|
||||
const or_options_t *options, unsigned int conn_type);
|
||||
|
||||
/** The last addresses that our network interface seemed to have been
|
||||
* binding to. We use this as one way to detect when our IP changes.
|
||||
@ -1771,7 +1773,7 @@ connection_connect_sockaddr,(connection_t *conn,
|
||||
|
||||
/*
|
||||
* We've got the socket open; give the OOS handler a chance to check
|
||||
* against configuured maximum socket number, but tell it no exhaustion
|
||||
* against configured maximum socket number, but tell it no exhaustion
|
||||
* failure.
|
||||
*/
|
||||
connection_check_oos(get_n_open_sockets(), 0);
|
||||
@ -1890,6 +1892,47 @@ connection_connect_log_client_use_ip_version(const connection_t *conn)
|
||||
}
|
||||
}
|
||||
|
||||
/** Retrieve the outbound address depending on the protocol (IPv4 or IPv6)
|
||||
* and the connection type (relay, exit, ...)
|
||||
* Return a socket address or NULL in case nothing is configured.
|
||||
**/
|
||||
const tor_addr_t *
|
||||
conn_get_outbound_address(sa_family_t family,
|
||||
const or_options_t *options, unsigned int conn_type)
|
||||
{
|
||||
const tor_addr_t *ext_addr = NULL;
|
||||
|
||||
int fam_index=0;
|
||||
if (family==AF_INET6) {
|
||||
fam_index=1;
|
||||
}
|
||||
// If an exit connection, use the exit address (if present)
|
||||
if (conn_type == CONN_TYPE_EXIT) {
|
||||
if (!tor_addr_is_null(
|
||||
&options->OutboundBindAddresses[OUTBOUND_ADDR_EXIT][fam_index])) {
|
||||
ext_addr = &options->OutboundBindAddresses[OUTBOUND_ADDR_EXIT]
|
||||
[fam_index];
|
||||
} else if (!tor_addr_is_null(
|
||||
&options->OutboundBindAddresses[OUTBOUND_ADDR_EXIT_AND_OR]
|
||||
[fam_index])) {
|
||||
ext_addr = &options->OutboundBindAddresses[OUTBOUND_ADDR_EXIT_AND_OR]
|
||||
[fam_index];
|
||||
}
|
||||
} else { // All non-exit connections
|
||||
if (!tor_addr_is_null(
|
||||
&options->OutboundBindAddresses[OUTBOUND_ADDR_OR][fam_index])) {
|
||||
ext_addr = &options->OutboundBindAddresses[OUTBOUND_ADDR_OR]
|
||||
[fam_index];
|
||||
} else if (!tor_addr_is_null(
|
||||
&options->OutboundBindAddresses[OUTBOUND_ADDR_EXIT_AND_OR]
|
||||
[fam_index])) {
|
||||
ext_addr = &options->OutboundBindAddresses[OUTBOUND_ADDR_EXIT_AND_OR]
|
||||
[fam_index];
|
||||
}
|
||||
}
|
||||
return ext_addr;
|
||||
}
|
||||
|
||||
/** Take conn, make a nonblocking socket; try to connect to
|
||||
* addr:port (port arrives in *host order*). If fail, return -1 and if
|
||||
* applicable put your best guess about errno into *<b>socket_error</b>.
|
||||
@ -1911,26 +1954,15 @@ connection_connect(connection_t *conn, const char *address,
|
||||
struct sockaddr *bind_addr = NULL;
|
||||
struct sockaddr *dest_addr;
|
||||
int dest_addr_len, bind_addr_len = 0;
|
||||
const or_options_t *options = get_options();
|
||||
int protocol_family;
|
||||
|
||||
/* Log if we didn't stick to ClientUseIPv4/6 or ClientPreferIPv6OR/DirPort
|
||||
*/
|
||||
connection_connect_log_client_use_ip_version(conn);
|
||||
|
||||
if (tor_addr_family(addr) == AF_INET6)
|
||||
protocol_family = PF_INET6;
|
||||
else
|
||||
protocol_family = PF_INET;
|
||||
|
||||
if (!tor_addr_is_loopback(addr)) {
|
||||
const tor_addr_t *ext_addr = NULL;
|
||||
if (protocol_family == AF_INET &&
|
||||
!tor_addr_is_null(&options->OutboundBindAddressIPv4_))
|
||||
ext_addr = &options->OutboundBindAddressIPv4_;
|
||||
else if (protocol_family == AF_INET6 &&
|
||||
!tor_addr_is_null(&options->OutboundBindAddressIPv6_))
|
||||
ext_addr = &options->OutboundBindAddressIPv6_;
|
||||
ext_addr = conn_get_outbound_address(tor_addr_family(addr), get_options(),
|
||||
conn->type);
|
||||
if (ext_addr) {
|
||||
memset(&bind_addr_ss, 0, sizeof(bind_addr_ss));
|
||||
bind_addr_len = tor_addr_to_sockaddr(ext_addr, 0,
|
||||
|
18
src/or/or.h
18
src/or/or.h
@ -3545,6 +3545,12 @@ typedef struct routerset_t routerset_t;
|
||||
* to pick its own port. */
|
||||
#define CFG_AUTO_PORT 0xc4005e
|
||||
|
||||
/** Enumeration of outbound address configuration types:
|
||||
* Exit-only, OR-only, or both */
|
||||
typedef enum {OUTBOUND_ADDR_EXIT, OUTBOUND_ADDR_OR,
|
||||
OUTBOUND_ADDR_EXIT_AND_OR,
|
||||
OUTBOUND_ADDR_MAX} outbound_addr_t;
|
||||
|
||||
/** Configuration options for a Tor process. */
|
||||
typedef struct {
|
||||
uint32_t magic_;
|
||||
@ -3628,10 +3634,14 @@ typedef struct {
|
||||
config_line_t *ControlListenAddress;
|
||||
/** Local address to bind outbound sockets */
|
||||
config_line_t *OutboundBindAddress;
|
||||
/** IPv4 address derived from OutboundBindAddress. */
|
||||
tor_addr_t OutboundBindAddressIPv4_;
|
||||
/** IPv6 address derived from OutboundBindAddress. */
|
||||
tor_addr_t OutboundBindAddressIPv6_;
|
||||
/** Local address to bind outbound relay sockets */
|
||||
config_line_t *OutboundBindAddressOR;
|
||||
/** Local address to bind outbound exit sockets */
|
||||
config_line_t *OutboundBindAddressExit;
|
||||
/** Addresses derived from the various OutboundBindAddress lines.
|
||||
* [][0] is IPv4, [][1] is IPv6
|
||||
*/
|
||||
tor_addr_t OutboundBindAddresses[OUTBOUND_ADDR_MAX][2];
|
||||
/** Directory server only: which versions of
|
||||
* Tor should we tell users to run? */
|
||||
config_line_t *RecommendedVersions;
|
||||
|
@ -2019,10 +2019,10 @@ policies_copy_ipv4h_to_smartlist(smartlist_t *addr_list, uint32_t ipv4h_addr)
|
||||
}
|
||||
}
|
||||
|
||||
/** Helper function that adds copies of
|
||||
* or_options->OutboundBindAddressIPv[4|6]_ to a smartlist as tor_addr_t *, as
|
||||
* long as or_options is non-NULL, and the addresses are not
|
||||
* tor_addr_is_null(), by passing them to policies_add_addr_to_smartlist.
|
||||
/** Helper function that adds copies of or_options->OutboundBindAddresses
|
||||
* to a smartlist as tor_addr_t *, as long as or_options is non-NULL, and
|
||||
* the addresses are not tor_addr_is_null(), by passing them to
|
||||
* policies_add_addr_to_smartlist.
|
||||
*
|
||||
* The caller is responsible for freeing all the tor_addr_t* in the smartlist.
|
||||
*/
|
||||
@ -2031,10 +2031,14 @@ policies_copy_outbound_addresses_to_smartlist(smartlist_t *addr_list,
|
||||
const or_options_t *or_options)
|
||||
{
|
||||
if (or_options) {
|
||||
policies_copy_addr_to_smartlist(addr_list,
|
||||
&or_options->OutboundBindAddressIPv4_);
|
||||
policies_copy_addr_to_smartlist(addr_list,
|
||||
&or_options->OutboundBindAddressIPv6_);
|
||||
for (int i=0;i<OUTBOUND_ADDR_MAX;i++) {
|
||||
for (int j=0;j<2;j++) {
|
||||
if (!tor_addr_is_null(&or_options->OutboundBindAddresses[i][j])) {
|
||||
policies_copy_addr_to_smartlist(addr_list,
|
||||
&or_options->OutboundBindAddresses[i][j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2051,10 +2055,10 @@ policies_copy_outbound_addresses_to_smartlist(smartlist_t *addr_list,
|
||||
* - if ipv6_local_address is non-NULL, and not the null tor_addr_t, add it
|
||||
* to the list of configured addresses.
|
||||
* If <b>or_options->ExitPolicyRejectLocalInterfaces</b> is true:
|
||||
* - if or_options->OutboundBindAddressIPv4_ is not the null tor_addr_t, add
|
||||
* it to the list of configured addresses.
|
||||
* - if or_options->OutboundBindAddressIPv6_ is not the null tor_addr_t, add
|
||||
* it to the list of configured addresses.
|
||||
* - if or_options->OutboundBindAddresses[][0] (=IPv4) is not the null
|
||||
* tor_addr_t, add it to the list of configured addresses.
|
||||
* - if or_options->OutboundBindAddresses[][1] (=IPv6) is not the null
|
||||
* tor_addr_t, add it to the list of configured addresses.
|
||||
*
|
||||
* If <b>or_options->BridgeRelay</b> is false, append entries of default
|
||||
* Tor exit policy into <b>result</b> smartlist.
|
||||
|
@ -1083,8 +1083,12 @@ test_policies_getinfo_helper_policies(void *arg)
|
||||
append_exit_policy_string(&mock_my_routerinfo.exit_policy, "reject *6:*");
|
||||
|
||||
mock_options.IPv6Exit = 1;
|
||||
tor_addr_from_ipv4h(&mock_options.OutboundBindAddressIPv4_, TEST_IPV4_ADDR);
|
||||
tor_addr_parse(&mock_options.OutboundBindAddressIPv6_, TEST_IPV6_ADDR);
|
||||
tor_addr_from_ipv4h(
|
||||
&mock_options.OutboundBindAddresses[OUTBOUND_ADDR_EXIT][0],
|
||||
TEST_IPV4_ADDR);
|
||||
tor_addr_parse(
|
||||
&mock_options.OutboundBindAddresses[OUTBOUND_ADDR_EXIT][1],
|
||||
TEST_IPV6_ADDR);
|
||||
|
||||
mock_options.ExitPolicyRejectPrivate = 1;
|
||||
mock_options.ExitPolicyRejectLocalInterfaces = 1;
|
||||
|
Loading…
Reference in New Issue
Block a user