Skip to content

Commit

Permalink
Merge pull request #1643 from private-octopus/unique-multipath-nat
Browse files Browse the repository at this point in the history
NAT handling with unique path_id
  • Loading branch information
huitema authored Feb 28, 2024
2 parents ff867e8 + 2ed67d2 commit ccbf04a
Show file tree
Hide file tree
Showing 8 changed files with 228 additions and 80 deletions.
6 changes: 6 additions & 0 deletions UnitTest1/unittest1.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2548,6 +2548,12 @@ namespace UnitTest1
Assert::AreEqual(ret, 0);
}

TEST_METHOD(m_unip_nat) {
int ret = m_unip_nat_test();

Assert::AreEqual(ret, 0);
}

TEST_METHOD(simple_multipath_basic) {
int ret = simple_multipath_basic_test();

Expand Down
26 changes: 26 additions & 0 deletions picoquic/frames.c
Original file line number Diff line number Diff line change
Expand Up @@ -4617,13 +4617,37 @@ const uint8_t* picoquic_decode_path_response_frame(picoquic_cnx_t* cnx, const ui
/* Per QUIC V1, path responses must come on the same path. Ignore them if this cannot be verified. */
if (path_x != NULL) {
int found_challenge = 0;
int found_nat_challenge = 0;

for (int ichal = 0; ichal < PICOQUIC_CHALLENGE_REPEAT_MAX; ichal++) {
if (response == path_x->challenge[ichal]) {
found_challenge = 1;
break;
}
}
if (!found_challenge && path_x->nat_peer_addr.ss_family != AF_UNSPEC) {
for (int ichal = 0; ichal < PICOQUIC_CHALLENGE_REPEAT_MAX; ichal++) {
if (response == path_x->nat_challenge[ichal]) {
found_nat_challenge = 1;
break;
}
}
}
if (found_nat_challenge && !path_x->challenge_verified) {
/* while probing NAT, the NAT response arrived before the normal path response */
/* Update the addresses */
picoquic_store_addr(&path_x->local_addr, (struct sockaddr*)&path_x->nat_local_addr);
picoquic_store_addr(&path_x->peer_addr, (struct sockaddr*)&path_x->nat_peer_addr);
path_x->if_index_dest = path_x->if_index_nat_dest;
/* if useful, update the CID */
if (path_x->p_remote_nat_cnxid != NULL) {
picoquic_dereference_stashed_cnxid(cnx, path_x, 0);
path_x->p_remote_cnxid = path_x->p_remote_nat_cnxid;
path_x->p_remote_nat_cnxid = NULL;
}
/* Consider this a successful challenge */
found_challenge = 1;
}

if (found_challenge && !path_x->challenge_verified){
/* TODO: update the RTT if using initial value */
Expand All @@ -4641,6 +4665,8 @@ const uint8_t* picoquic_decode_path_response_frame(picoquic_cnx_t* cnx, const ui
picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_INTERNAL_ERROR, picoquic_frame_type_path_response);
bytes = NULL;
}
/* Erase the NAT address, to avoid continuing the NAT challenge */
path_x->nat_peer_addr.ss_family = AF_UNSPEC;
}
}
}
Expand Down
52 changes: 31 additions & 21 deletions picoquic/packet.c
Original file line number Diff line number Diff line change
Expand Up @@ -1792,6 +1792,7 @@ default path.
int picoquic_find_incoming_unique_path(picoquic_cnx_t* cnx, picoquic_packet_header* ph,
struct sockaddr* addr_from,
struct sockaddr* addr_to,
int if_index_to,
uint64_t current_time,
int* p_path_id,
int* path_is_not_allocated)
Expand All @@ -1817,6 +1818,7 @@ int picoquic_find_incoming_unique_path(picoquic_cnx_t* cnx, picoquic_packet_head
*/
path_id = cnx->nb_paths - 1;
path_x = cnx->path[path_id];
path_x->if_index_dest = if_index_to;

/* when creating the path, we need to copy the dest CID and chose
* destination CID with the matching path ID.
Expand All @@ -1827,32 +1829,39 @@ int picoquic_find_incoming_unique_path(picoquic_cnx_t* cnx, picoquic_packet_head
}
else {
path_x = cnx->path[path_id];
/* If the local CID is not set, set it */
if (path_x->p_local_cnxid == NULL) {
path_x->p_local_cnxid = picoquic_find_local_cnxid(cnx, path_x->unique_path_id, &ph->dest_cnx_id);
}
/* Else handle CID renewal if needed */
else if (picoquic_compare_connection_id(&path_x->p_local_cnxid->cnx_id, &ph->dest_cnx_id) != 0) {
path_x->p_local_cnxid = picoquic_find_local_cnxid(cnx, path_x->unique_path_id, &ph->dest_cnx_id);
if (cnx->client_mode == 0) {
(void)picoquic_renew_connection_id(cnx, path_id);
}
}
/* If the addresses match, we are good. */
if (picoquic_compare_addr(addr_from, (struct sockaddr*)&path_x->peer_addr) == 0) {
/* Consider whether to document the local address */
if (path_x->local_addr.ss_family == AF_UNSPEC) {
picoquic_store_addr(&cnx->path[path_id]->local_addr, addr_to);
}
/* If the local CID is not set, set it */
if (path_x->p_local_cnxid == NULL) {
path_x->p_local_cnxid = picoquic_find_local_cnxid(cnx, path_x->unique_path_id, &ph->dest_cnx_id);
}
/* Handle CID renewal if needed */
else if (picoquic_compare_connection_id(&path_x->p_local_cnxid->cnx_id, &ph->dest_cnx_id) != 0) {
path_x->p_local_cnxid = picoquic_find_local_cnxid(cnx, path_x->unique_path_id, &ph->dest_cnx_id);
if (cnx->client_mode == 0) {
(void)picoquic_renew_connection_id(cnx, path_id);
}
}
}
/* Else, this might be a NAT rebinding */
else {
/* TODO: handle NAT Traversal.
* - should be on same path_id as original.
* - assumption is that the peer will respond to one challenge or the other.
* - if response arrives for same-path-address challenge, drop NAT path.
* - if response arrives for same-path-nat-address challenge, drop original path.
*/
/* Else, this might be a NAT rebinding. But we only handle one NAT rebinding at a time.
*/
else if (path_x->nat_peer_addr.ss_family == AF_UNSPEC || !(path_x->challenge_verified == 0 && path_x->challenge_failed == 0)){
/* There is no NAT ongoing NAT rebinding, so try this one. */
picoquic_store_addr(&cnx->path[path_id]->nat_local_addr, addr_to);
picoquic_store_addr(&cnx->path[path_id]->nat_peer_addr, addr_from);
path_x->if_index_nat_dest = if_index_to;
/* Set the challenges on both the NAT path and the ongoing path */
picoquic_set_path_challenge(cnx, path_id, current_time);
for (int ichal = 0; ichal < PICOQUIC_CHALLENGE_REPEAT_MAX; ichal++) {
path_x->nat_challenge[ichal] = picoquic_public_random_64();
}
path_x->nat_challenge_time = 0;
path_x->nat_challenge_repeat_count;
path_x->p_remote_nat_cnxid = picoquic_obtain_stashed_cnxid(cnx, path_x->unique_path_id);
}
}
if (path_id < 0) {
Expand All @@ -1865,12 +1874,13 @@ int picoquic_find_incoming_unique_path(picoquic_cnx_t* cnx, picoquic_packet_head
int picoquic_find_incoming_path(picoquic_cnx_t* cnx, picoquic_packet_header* ph,
struct sockaddr* addr_from,
struct sockaddr* addr_to,
int if_index_to,
uint64_t current_time,
int* p_path_id,
int* path_is_not_allocated)
{
if (ph->ptype == picoquic_packet_1rtt_protected && cnx->is_unique_path_id_enabled) {
return picoquic_find_incoming_unique_path(cnx, ph, addr_from, addr_to, current_time, p_path_id, path_is_not_allocated);
return picoquic_find_incoming_unique_path(cnx, ph, addr_from, addr_to, if_index_to, current_time, p_path_id, path_is_not_allocated);
}
int ret = 0;
int partial_match_path = -1;
Expand Down Expand Up @@ -2285,7 +2295,7 @@ int picoquic_incoming_segment(
}
else {
/* Find the arrival path and update its state */
ret = picoquic_find_incoming_path(cnx, &ph, addr_from, addr_to, current_time, &path_id, &path_is_not_allocated);
ret = picoquic_find_incoming_path(cnx, &ph, addr_from, addr_to, if_index_to, current_time, &path_id, &path_is_not_allocated);
}
}

Expand Down
26 changes: 11 additions & 15 deletions picoquic/picoquic_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -1008,18 +1008,6 @@ typedef struct st_picoquic_remote_cnxid_stash_t {
* Congestion control and spin bit management are path specific.
* Packet numbering is global, see packet context.
*/
typedef struct st_picoquic_path_nat_t {
picoquic_local_cnxid_t* p_local_cnxid;
picoquic_remote_cnxid_t* p_remote_cnxid;
struct sockaddr_storage peer_addr;
struct sockaddr_storage local_addr;
unsigned long if_index_dest;
uint64_t challenge_response;
uint64_t challenge[PICOQUIC_CHALLENGE_REPEAT_MAX];
uint64_t challenge_time_first;
uint8_t challenge_repeat_count;
} picoquic_path_nat_t;

typedef struct st_picoquic_path_t {
picoquic_local_cnxid_t* p_local_cnxid;
picoquic_remote_cnxid_t* p_remote_cnxid;
Expand All @@ -1036,16 +1024,22 @@ typedef struct st_picoquic_path_t {
struct sockaddr_storage peer_addr;
struct sockaddr_storage local_addr;
unsigned long if_index_dest;
uint64_t last_non_path_probing_pn;
/* Challenge used for this path */
uint64_t challenge_response;
uint64_t challenge[PICOQUIC_CHALLENGE_REPEAT_MAX];
uint64_t challenge_time;
uint64_t demotion_time;
uint64_t challenge_time_first;
uint8_t challenge_repeat_count;
uint64_t last_non_path_probing_pn;
/* NAT Challenge for this path */
picoquic_path_nat_t* nat;
/* NAT Challenge for this path, if using unique path id */
uint64_t nat_challenge[PICOQUIC_CHALLENGE_REPEAT_MAX];
uint64_t nat_challenge_time;
uint64_t nat_challenge_repeat_count;
picoquic_remote_cnxid_t* p_remote_nat_cnxid;
unsigned long if_index_nat_dest;
struct sockaddr_storage nat_peer_addr;
struct sockaddr_storage nat_local_addr;
/* Last time a packet was sent on this path. */
uint64_t last_sent_time;
/* Number of packets sent on this path*/
Expand All @@ -1063,6 +1057,7 @@ typedef struct st_picoquic_path_t {
unsigned int challenge_verified : 1;
unsigned int challenge_failed : 1;
unsigned int response_required : 1;
unsigned int nat_challenge_required : 1;
unsigned int path_is_standby : 1;
unsigned int path_is_demoted : 1;
unsigned int current_spin : 1;
Expand All @@ -1082,6 +1077,7 @@ typedef struct st_picoquic_path_t {
unsigned int is_ack_expected : 1;
unsigned int is_datagram_ready : 1;
unsigned int is_pto_required : 1; /* Should send PTO probe */
unsigned int is_probing_nat : 1; /* When path transmission is scheduled only for NAT probing */

/* Management of retransmissions in a path.
* The "path_packet" variables are used for the RACK algorithm, per path, to avoid
Expand Down
Loading

0 comments on commit ccbf04a

Please sign in to comment.