commit 9f2f359494f826f9fe57573b46daefe30b493dc1
parent 56e8e473648b9fc04d5fb61f52e6443c57d3d2be
Author: Nick Mathewson <nickm@torproject.org>
Date: Mon, 2 Jun 2025 15:59:01 -0400
client-side support for negotiating CGO via subprotocol request
This has been a bit tricky, since the old code assumed that
we never wanted to use extensions besides CC.
Diffstat:
6 files changed, 141 insertions(+), 43 deletions(-)
diff --git a/src/core/crypto/onion_crypto.c b/src/core/crypto/onion_crypto.c
@@ -182,7 +182,8 @@ onion_skin_create(int type,
return -1;
size_t msg_len = 0;
uint8_t *msg = NULL;
- if (client_circ_negotiation_message(node, &msg, &msg_len) < 0)
+ if (client_circ_negotiation_message(node, &msg, &msg_len,
+ &state_out->chosen_params) < 0)
return -1;
uint8_t *onion_skin = NULL;
size_t onion_skin_len = 0;
diff --git a/src/core/or/circuitbuild.c b/src/core/or/circuitbuild.c
@@ -52,6 +52,7 @@
#include "core/or/relay.h"
#include "core/or/trace_probes_circuit.h"
#include "core/or/crypt_path.h"
+#include "core/or/protover.h"
#include "feature/client/bridges.h"
#include "feature/client/circpathbias.h"
#include "feature/client/entrynodes.h"
@@ -84,6 +85,7 @@
#include "trunnel/extension.h"
#include "trunnel/congestion_control.h"
+#include "trunnel/subproto_request.h"
static int circuit_send_first_onion_skin(origin_circuit_t *circ);
static int circuit_build_no_more_hops(origin_circuit_t *circ);
@@ -883,6 +885,9 @@ circuit_pick_create_handshake(uint8_t *cell_type_out,
if (ei->exit_supports_congestion_control &&
congestion_control_enabled())
*handshake_type_out = ONION_HANDSHAKE_TYPE_NTOR_V3;
+ else if (ei->enable_cgo)
+ // XXXX CGO: provide an enable option?
+ *handshake_type_out = ONION_HANDSHAKE_TYPE_NTOR_V3;
else
*handshake_type_out = ONION_HANDSHAKE_TYPE_NTOR;
}
@@ -2636,6 +2641,80 @@ circuit_upgrade_circuits_from_guard_wait(void)
smartlist_free(to_upgrade);
}
+// TODO: Find a better place to declare this; it's duplicated in
+// onion_crypto.c
+#define EXT_TYPE_SUBPROTO 3
+
+/** Add a request for the CGO subprotocol capability to ext.
+ *
+ * NOTE: If we need to support other subprotocol extensions,
+ * do not add separate functions! Instead rename this function
+ * and adapt it as appropriate.
+ */
+static int
+build_cgo_subproto_request(trn_extension_t *ext)
+{
+ trn_extension_field_t *fld = NULL;
+ trn_subproto_request_t *req = NULL;
+ trn_subproto_request_ext_t *req_ext = NULL;
+ int r = 0;
+
+ fld = trn_extension_field_new();
+ req_ext = trn_subproto_request_ext_new();
+
+ req = trn_subproto_request_new();
+ req->protocol_id = PRT_RELAY;
+ req->proto_cap_number = PROTOVER_RELAY_CRYPT_CGO;
+ trn_subproto_request_ext_add_reqs(req_ext, req);
+ req = NULL; // prevent double-free
+
+ // TODO: If we add other capabilities here, we need to make
+ // sure they are correctly sorted.
+
+ ssize_t len = trn_subproto_request_ext_encoded_len(req_ext);
+ if (BUG(len<0))
+ goto err;
+ if (BUG(len > UINT8_MAX))
+ goto err;
+
+ trn_extension_field_setlen_field(fld, len);
+ trn_extension_field_set_field_type(fld, EXT_TYPE_SUBPROTO);
+ trn_extension_field_set_field_len(fld, len);
+ uint8_t *out = trn_extension_field_getarray_field(fld);
+ ssize_t len2 = trn_subproto_request_ext_encode(out, len, req_ext);
+ if (BUG(len != len2))
+ goto err;
+
+ trn_extension_add_fields(ext, fld);
+ fld = NULL; // prevent double-free
+
+ // We succeeded!
+ r = 0;
+
+ err:
+ trn_subproto_request_ext_free(req_ext);
+ trn_subproto_request_free(req);
+ trn_extension_field_free(fld);
+
+ return r;
+}
+
+/** Helper: Comparison function to sort extensions. */
+static int
+ext_cmp(const void *a, const void *b)
+{
+ const trn_extension_field_t *fa = *(trn_extension_field_t **)a;
+ const trn_extension_field_t *fb = *(trn_extension_field_t **)b;
+ uint8_t ta = trn_extension_field_get_field_type(fa);
+ uint8_t tb = trn_extension_field_get_field_type(fb);
+ if (ta < tb)
+ return -1;
+ else if (ta == tb)
+ return 0;
+ else
+ return 1;
+}
+
/**
* Try to generate a circuit-negotiation message for communication with a
* given relay. Assumes we are using ntor v3, or some later version that
@@ -2647,13 +2726,53 @@ circuit_upgrade_circuits_from_guard_wait(void)
int
client_circ_negotiation_message(const extend_info_t *ei,
uint8_t **msg_out,
- size_t *msg_len_out)
+ size_t *msg_len_out,
+ circuit_params_t *params_out)
{
- tor_assert(ei && msg_out && msg_len_out);
+ tor_assert(ei && msg_out && msg_len_out && params_out);
+ bool cc_enabled = false;
- if (!ei->exit_supports_congestion_control) {
- return -1;
+ *msg_out = NULL;
+
+ trn_extension_t *ext = trn_extension_new();
+
+ if (ei->exit_supports_congestion_control &&
+ congestion_control_enabled()) {
+ if (congestion_control_build_ext_request(ext) < 0) {
+ goto err;
+ }
+ cc_enabled = true;
}
- return congestion_control_build_ext_request(msg_out, msg_len_out);
+ if (cc_enabled && ei->enable_cgo) {
+ if (build_cgo_subproto_request(ext) < 0) {
+ goto err;
+ }
+ params_out->cell_fmt = RELAY_CELL_FORMAT_V1;
+ params_out->crypto_alg = RELAY_CRYPTO_ALG_CGO_CLIENT;
+ }
+
+ size_t n_fields = trn_extension_getlen_fields(ext);
+ qsort(trn_extension_getarray_fields(ext),
+ n_fields, sizeof(trn_extension_field_t *),
+ ext_cmp);
+
+ trn_extension_set_num(ext, n_fields);
+
+ ssize_t total_len = trn_extension_encoded_len(ext);
+ if (BUG(total_len < 0))
+ goto err;
+
+ *msg_out = tor_malloc_zero(total_len);
+ *msg_len_out = total_len;
+ if (BUG(trn_extension_encode(*msg_out, total_len, ext) < 0)) {
+ goto err;
+ }
+ trn_extension_free(ext);
+
+ return 0;
+ err:
+ trn_extension_free(ext);
+ tor_free(*msg_out);
+ return -1;
}
diff --git a/src/core/or/circuitbuild.h b/src/core/or/circuitbuild.h
@@ -70,9 +70,11 @@ circuit_deliver_create_cell,(circuit_t *circ,
const struct create_cell_t *create_cell,
int relayed));
+struct circuit_params_t;
int client_circ_negotiation_message(const extend_info_t *ei,
uint8_t **msg_out,
- size_t *msg_len_out);
+ size_t *msg_len_out,
+ struct circuit_params_t *params_out);
#ifdef CIRCUITBUILD_PRIVATE
STATIC circid_t get_unique_circ_id_by_chan(channel_t *chan);
diff --git a/src/core/or/congestion_control_common.c b/src/core/or/congestion_control_common.c
@@ -998,26 +998,18 @@ congestion_control_dispatch_cc_alg(congestion_control_t *cc,
* Build an extension field request to negotiate congestion control.
*
* If congestion control is enabled, field TRUNNEL_EXT_TYPE_CC_FIELD_REQUEST
- * is created in msg_out. It is a single 0-length field that signifies that we
- * want to use congestion control. The length of msg_out is provided via
- * msg_len_out.
+ * added to ext. It is a single 0-length field that signifies that we
+ * want to use congestion control.
*
- * If congestion control is not enabled, a payload with 0 extensions is created
- * and returned.
+ * If congestion control is not enabled, no extension is added.
*
* If there is a failure building the request, -1 is returned, else 0.
- *
- * *msg_out must be freed if the return value is 0.
*/
int
-congestion_control_build_ext_request(uint8_t **msg_out, size_t *msg_len_out)
+congestion_control_build_ext_request(trn_extension_t *ext)
{
- uint8_t *request = NULL;
- trn_extension_t *ext = NULL;
trn_extension_field_t *field = NULL;
- ext = trn_extension_new();
-
/* With congestion control enabled, add the request, else it is an empty
* request in the payload. */
@@ -1032,30 +1024,9 @@ congestion_control_build_ext_request(uint8_t **msg_out, size_t *msg_len_out)
/* Build final extension. */
trn_extension_add_fields(ext, field);
- trn_extension_set_num(ext, 1);
- }
-
- /* Encode extension. */
- ssize_t ret = trn_extension_encoded_len(ext);
- if (BUG(ret < 0)) {
- goto err;
- }
- size_t request_len = ret;
- request = tor_malloc_zero(request_len);
- ret = trn_extension_encode(request, request_len, ext);
- if (BUG(ret < 0)) {
- tor_free(request);
- goto err;
}
- *msg_out = request;
- *msg_len_out = request_len;
-
- /* Free everything, we've encoded the request now. */
- ret = 0;
- err:
- trn_extension_free(ext);
- return (int)ret;
+ return 0;
}
/**
diff --git a/src/core/or/congestion_control_common.h b/src/core/or/congestion_control_common.h
@@ -66,9 +66,9 @@ void congestion_control_new_consensus_params(const networkstatus_t *ns);
bool congestion_control_enabled(void);
-int congestion_control_build_ext_request(uint8_t **msg_out,
- size_t *msg_len_out);
struct trn_extension_st;
+int congestion_control_build_ext_request(struct trn_extension_st *ext);
+
int congestion_control_parse_ext_request(const struct trn_extension_st *ext);
int congestion_control_build_ext_response(const circuit_params_t *our_params,
const circuit_params_t *circ_params,
diff --git a/src/core/or/extend_info_st.h b/src/core/or/extend_info_st.h
@@ -40,6 +40,11 @@ struct extend_info_t {
* and it also supports supports NtorV3 _and_ negotiation
* of congestion control parameters */
bool exit_supports_congestion_control;
+ /**
+ * True if this hop supports CGO relay message enryption,
+ * and we intend to use it.
+ */
+ bool enable_cgo;
};
#endif /* !defined(EXTEND_INFO_ST_H) */