static int circuit_deliver_create_cell(circuit_t *circ,
const create_cell_t *create_cell,
int relayed);
-static int onion_pick_cpath_exit(origin_circuit_t *circ, extend_info_t *exit);
+static int onion_pick_cpath_exit(origin_circuit_t *circ, extend_info_t *exit,
+ int is_hs_v3_rp_circuit);
static crypt_path_t *onion_next_hop_in_cpath(crypt_path_t *cpath);
static int onion_extend_cpath(origin_circuit_t *circ);
static int onion_append_hop(crypt_path_t **head_ptr, extend_info_t *choice);
{
origin_circuit_t *circ;
int err_reason = 0;
+ int is_hs_v3_rp_circuit = 0;
+
+ if (flags & CIRCLAUNCH_IS_V3_RP) {
+ is_hs_v3_rp_circuit = 1;
+ }
circ = origin_circuit_init(purpose, flags);
- if (onion_pick_cpath_exit(circ, exit_ei) < 0 ||
+ if (onion_pick_cpath_exit(circ, exit_ei, is_hs_v3_rp_circuit) < 0 ||
onion_populate_cpath(circ) < 0) {
circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_NOPATH);
return NULL;
*/
static const node_t *
choose_good_exit_server(uint8_t purpose,
- int need_uptime, int need_capacity, int is_internal)
+ int need_uptime, int need_capacity, int is_internal,
+ int need_hs_v3)
{
const or_options_t *options = get_options();
router_crn_flags_t flags = CRN_NEED_DESC;
flags |= CRN_NEED_UPTIME;
if (need_capacity)
flags |= CRN_NEED_CAPACITY;
+ if (need_hs_v3)
+ flags |= CRN_RENDEZVOUS_V3;
switch (purpose) {
case CIRCUIT_PURPOSE_C_GENERAL:
/** Decide a suitable length for circ's cpath, and pick an exit
* router (or use <b>exit</b> if provided). Store these in the
- * cpath. Return 0 if ok, -1 if circuit should be closed. */
+ * cpath.
+ *
+ * If <b>is_hs_v3_rp_circuit</b> is set, then this exit should be suitable to
+ * be used as an HS v3 rendezvous point.
+ *
+ * Return 0 if ok, -1 if circuit should be closed. */
static int
-onion_pick_cpath_exit(origin_circuit_t *circ, extend_info_t *exit_ei)
+onion_pick_cpath_exit(origin_circuit_t *circ, extend_info_t *exit_ei,
+ int is_hs_v3_rp_circuit)
{
cpath_build_state_t *state = circ->build_state;
} else { /* we have to decide one */
const node_t *node =
choose_good_exit_server(circ->base_.purpose, state->need_uptime,
- state->need_capacity, state->is_internal);
+ state->need_capacity, state->is_internal,
+ is_hs_v3_rp_circuit);
if (!node) {
log_warn(LD_CIRC,"Failed to choose an exit server");
return -1;
return NULL;
}
+/** We might cannibalize this circuit: Return true if its last hop can be used
+ * as a v3 rendezvous point. */
+static int
+circuit_can_be_cannibalized_for_v3_rp(const origin_circuit_t *circ)
+{
+ if (!circ->build_state) {
+ return 0;
+ }
+
+ extend_info_t *chosen_exit = circ->build_state->chosen_exit;
+ if (BUG(!chosen_exit)) {
+ return 0;
+ }
+
+ const node_t *rp_node = node_get_by_id(chosen_exit->identity_digest);
+ if (rp_node) {
+ if (node_supports_v3_rendezvous_point(rp_node)) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
/** Return a circuit that is open, is CIRCUIT_PURPOSE_C_GENERAL,
* has a timestamp_dirty value of 0, has flags matching the CIRCLAUNCH_*
* flags in <b>flags</b>, and if info is defined, does not already use info
hop = hop->next;
} while (hop != circ->cpath);
}
+
+ if ((flags & CIRCLAUNCH_IS_V3_RP) &&
+ !circuit_can_be_cannibalized_for_v3_rp(circ)) {
+ log_debug(LD_GENERAL, "Skipping uncannibalizable circuit for v3 "
+ "rendezvous point.");
+ goto next;
+ }
+
if (!best || (best->build_state->need_uptime && !need_uptime))
best = circ;
next: ;
if (want_onehop) flags |= CIRCLAUNCH_ONEHOP_TUNNEL;
if (need_uptime) flags |= CIRCLAUNCH_NEED_UPTIME;
if (need_internal) flags |= CIRCLAUNCH_IS_INTERNAL;
+
+ /* If we are about to pick a v3 RP right now, make sure we pick a
+ * rendezvous point that supports the v3 protocol! */
+ if (desired_circuit_purpose == CIRCUIT_PURPOSE_C_REND_JOINED &&
+ new_circ_purpose == CIRCUIT_PURPOSE_C_ESTABLISH_REND &&
+ ENTRY_TO_EDGE_CONN(conn)->hs_ident) {
+ flags |= CIRCLAUNCH_IS_V3_RP;
+ log_info(LD_GENERAL, "Getting rendezvous circuit to v3 service!");
+ }
+
circ = circuit_launch_by_extend_info(new_circ_purpose, extend_info,
flags);
}
/** Flag to set when the last hop of a circuit doesn't need to be an
* exit node. */
#define CIRCLAUNCH_IS_INTERNAL (1<<3)
+/** Flag to set when we are trying to launch a v3 rendezvous circuit. We need
+ * to apply some additional filters on the node picked. */
+#define CIRCLAUNCH_IS_V3_RP (1<<4)
origin_circuit_t *circuit_launch_by_extend_info(uint8_t purpose,
extend_info_t *info,
int flags);
#include "circuitlist.h"
#include "circuituse.h"
#include "connection.h"
+#include "nodelist.h"
#include "circpathbias.h"
#include "connection.h"
#include "hs_ntor.h"
tor_assert(circ);
tor_assert(TO_CIRCUIT(circ)->purpose == CIRCUIT_PURPOSE_C_ESTABLISH_REND);
+ const extend_info_t *rp_ei = circ->build_state->chosen_exit;
+
+ /* Check that we didn't accidentally choose a node that does not understand
+ * the v3 rendezvous protocol */
+ if (rp_ei) {
+ const node_t *rp_node = node_get_by_id(rp_ei->identity_digest);
+ if (rp_node) {
+ if (BUG(!node_supports_v3_rendezvous_point(rp_node))) {
+ return;
+ }
+ }
+ }
+
log_info(LD_REND, "Rendezvous circuit has opened to %s.",
- safe_str_client(
- extend_info_describe(circ->build_state->chosen_exit)));
+ safe_str_client(extend_info_describe(rp_ei)));
/* Ignore returned value, nothing we can really do. On failure, the circuit
* will be marked for close. */
return 0;
}
+/** Return true iff <b>node</b> supports to be a rendezvous point for hidden
+ * service version 3 (HSRend=2). */
+int
+node_supports_v3_rendezvous_point(const node_t *node)
+{
+ tor_assert(node);
+
+ if (node->rs) {
+ return node->rs->supports_v3_rendezvous_point;
+ }
+ if (node->ri) {
+ if (node->ri->protocol_list == NULL) {
+ return 0;
+ }
+ return protocol_list_supports_protocol(node->ri->protocol_list,
+ PRT_HSREND,
+ PROTOVER_HS_RENDEZVOUS_POINT_V3);
+ }
+ tor_assert_nonfatal_unreached_once();
+ return 0;
+}
+
/** Return the RSA ID key's SHA1 digest for the provided node. */
const uint8_t *
node_get_rsa_id_digest(const node_t *node)
int node_supports_ed25519_link_authentication(const node_t *node);
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);
const uint8_t *node_get_rsa_id_digest(const node_t *node);
int node_has_ipv6_addr(const node_t *node);
* requires HSDir=2. */
unsigned int supports_v3_hsdir : 1;
+ /** True iff this router has a protocol list that allows it to be an hidden
+ * service rendezvous point supporting version 3 as seen in proposal 224.
+ * This requires HSRend=2. */
+ unsigned int supports_v3_rendezvous_point: 1;
+
unsigned int has_bandwidth:1; /**< The vote/consensus had bw info */
unsigned int has_exitsummary:1; /**< The vote/consensus had exit summaries */
unsigned int bw_is_unmeasured:1; /**< This is a consensus entry, with
CRN_PREF_ADDR = 1<<7,
/* On clients, only provide nodes that we can connect to directly, based on
* our firewall rules */
- CRN_DIRECT_CONN = 1<<8
+ CRN_DIRECT_CONN = 1<<8,
+ /* On clients, only provide nodes with HSRend >= 2 protocol version which
+ * is required for hidden service version >= 3. */
+ CRN_RENDEZVOUS_V3 = 1<<9,
} router_crn_flags_t;
/** Return value for router_add_to_routerlist() and dirserv_add_descriptor() */
#define PROTOVER_HSDIR_V3 2
/** The protover version number that signifies HSv3 intro point support */
#define PROTOVER_HS_INTRO_V3 4
+/** The protover version number that signifies HSv3 rendezvous point support */
+#define PROTOVER_HS_RENDEZVOUS_POINT_V3 2
/** List of recognized subprotocols. */
typedef enum protocol_type_t {
const int need_desc = (flags & CRN_NEED_DESC) != 0;
const int pref_addr = (flags & CRN_PREF_ADDR) != 0;
const int direct_conn = (flags & CRN_DIRECT_CONN) != 0;
+ const int rendezvous_v3 = (flags & CRN_RENDEZVOUS_V3) != 0;
smartlist_t *sl=smartlist_new(),
*excludednodes=smartlist_new();
rule = weight_for_exit ? WEIGHT_FOR_EXIT :
(need_guard ? WEIGHT_FOR_GUARD : WEIGHT_FOR_MID);
- /* Exclude relays that allow single hop exit circuits. This is an obsolete
- * option since 0.2.9.2-alpha and done by default in 0.3.1.0-alpha. */
- SMARTLIST_FOREACH(nodelist_get_list(), node_t *, node,
+ SMARTLIST_FOREACH_BEGIN(nodelist_get_list(), node_t *, node) {
if (node_allows_single_hop_exits(node)) {
+ /* Exclude relays that allow single hop exit circuits. This is an
+ * obsolete option since 0.2.9.2-alpha and done by default in
+ * 0.3.1.0-alpha. */
smartlist_add(excludednodes, node);
- });
+ } else if (rendezvous_v3 &&
+ !node_supports_v3_rendezvous_point(node)) {
+ /* Exclude relays that do not support to rendezvous for a hidden service
+ * version 3. */
+ smartlist_add(excludednodes, node);
+ }
+ } SMARTLIST_FOREACH_END(node);
if ((r = routerlist_find_my_routerinfo()))
routerlist_add_node_and_family(excludednodes, r);
rs->supports_v3_hsdir =
protocol_list_supports_protocol(tok->args[0], PRT_HSDIR,
PROTOVER_HSDIR_V3);
+ rs->supports_v3_rendezvous_point =
+ protocol_list_supports_protocol(tok->args[0], PRT_HSDIR,
+ PROTOVER_HS_RENDEZVOUS_POINT_V3);
}
if ((tok = find_opt_by_keyword(tokens, K_V))) {
tor_assert(tok->n_args == 1);