Skip to content

Commit 0a2b28a

Browse files
committed
cm3
1 parent 6e423e9 commit 0a2b28a

3 files changed

Lines changed: 59 additions & 57 deletions

File tree

lib/roles/quic/crypto-quic.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -946,6 +946,19 @@ lws_tls_quic_rx_crypto(struct lws *wsi, int level, const uint8_t *buf, size_t le
946946

947947
wsi->quic.qn->handshake_done = 1;
948948

949+
/*
950+
* If the client deferred its preferred_address migration until
951+
* the handshake was complete, execute it now: both sides have
952+
* APP keys so the PATH_CHALLENGE/RESPONSE will succeed.
953+
*/
954+
if (!wsi->quic.qn->is_server && wsi->quic.qn->prefaddr_pending) {
955+
wsi->quic.qn->prefaddr_pending = 0;
956+
lws_quic_client_probe_preferred_address(wsi,
957+
&wsi->quic.qn->probing_sa46,
958+
&wsi->quic.qn->prefaddr_rem_cid,
959+
wsi->quic.qn->prefaddr_rem_token);
960+
}
961+
949962
if (wsi->quic.qn->is_server) {
950963
struct lws_quic_tx_frame *f_hd = lws_zalloc(sizeof(*f_hd), "HANDSHAKE_DONE");
951964
if (f_hd) {

lib/roles/quic/ops-quic.c

Lines changed: 45 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ lws_quic_queue_path_challenge(struct lws *nwsi)
6060
* validates the new path; a timeout reverts to a fresh socket on the original
6161
* server address with the original DCID.
6262
*/
63-
#define LWS_QUIC_PREFADDR_DEADLINE_US (3 * LWS_USEC_PER_SEC)
63+
#define LWS_QUIC_PREFADDR_DEADLINE_US (10 * LWS_USEC_PER_SEC)
6464

6565
static int
6666
lws_quic_prefaddr_swap_socket(struct lws *nwsi, const lws_sockaddr46 *to_sa46)
@@ -149,16 +149,29 @@ lws_quic_client_probe_preferred_address(struct lws *nwsi,
149149
if (qn->is_server)
150150
return 1;
151151

152+
/* Save the migration parameters */
152153
qn->prefaddr_original_sa46 = nwsi->udp->sa46;
153154
qn->prefaddr_original_rem_cid = qn->rem_cid;
155+
qn->probing_sa46 = *pref_sa46;
156+
qn->probing_sa46_valid = 1;
154157
if (pref_cid) {
155158
qn->prefaddr_rem_cid = *pref_cid;
156159
if (pref_token)
157160
memcpy(qn->prefaddr_rem_token, pref_token, 16);
158161
}
159162

160-
qn->probing_sa46 = *pref_sa46;
161-
qn->probing_sa46_valid = 1;
163+
/*
164+
* Defer the actual socket swap + DCID change until the handshake is
165+
* complete: the preferred_address TP arrives during the handshake, and
166+
* swapping before both sides have APP keys strands the migration (the
167+
* server can't decrypt APP-level packets from the new source port).
168+
*/
169+
if (!qn->handshake_done) {
170+
qn->prefaddr_pending = 1;
171+
return 0;
172+
}
173+
174+
qn->prefaddr_pending = 0;
162175
qn->prefaddr_active = 1;
163176
qn->prefaddr_committed = 0;
164177

@@ -1434,9 +1447,19 @@ rops_handle_POLLIN_quic(struct lws_context_per_thread *pt, struct lws *wsi,
14341447
nwsi->quic.qn->highest_rx_pn[pn_space] = full_pn;
14351448
}
14361449

1437-
/* Connection Migration: Execute pending migration now that the packet is cryptographically verified */
1450+
/*
1451+
* Connection Migration: Execute pending migration now that
1452+
* the packet is cryptographically verified. Skip if a probe
1453+
* is already in flight for this address to avoid overwriting
1454+
* the pending path_challenge (which would make the client's
1455+
* PATH_RESPONSE not match).
1456+
*/
14381457
if (pending_migration) {
1439-
if (is_out_of_order) {
1458+
if (nwsi->quic.qn->probing_sa46_valid &&
1459+
!lws_sa46_compare_ads(&migration_sa46,
1460+
&nwsi->quic.qn->probing_sa46)) {
1461+
pending_migration = 0;
1462+
} else if (is_out_of_order) {
14401463
lwsl_notice("QUIC: Ignoring connection migration from out-of-order packet (PN %llu <= highest %llu)\n",
14411464
(unsigned long long)full_pn, (unsigned long long)highest);
14421465
pending_migration = 0;
@@ -1464,20 +1487,21 @@ rops_handle_POLLIN_quic(struct lws_context_per_thread *pt, struct lws *wsi,
14641487
buf_old, (unsigned int)ntohs(port_old),
14651488
buf_new, (unsigned int)ntohs(port_new));
14661489
#endif
1490+
/*
1491+
* Commit the peer address immediately: the
1492+
* client has moved to a new source port, the
1493+
* old path is dead. Since we defer client
1494+
* migration until handshake_done, there should
1495+
* be no pending Handshake-level TX to race.
1496+
* Queue PATH_CHALLENGE at the HEAD of APP
1497+
* pending_tx so it is the first APP frame
1498+
* emitted to the new path.
1499+
*/
1500+
nwsi->udp->sa46 = migration_sa46;
14671501
nwsi->quic.qn->rx_has_non_probing = 0;
14681502
nwsi->quic.qn->probing_sa46 = migration_sa46;
14691503
nwsi->quic.qn->probing_sa46_valid = 1;
14701504

1471-
/*
1472-
* Queue PATH_CHALLENGE immediately, tagged to
1473-
* the new 4-tuple via has_dest. Do NOT commit
1474-
* udp->sa46 yet — keep ordinary traffic (incl.
1475-
* Handshake ACKs) flowing to the OLD path until
1476-
* PATH_RESPONSE validates the new one. This
1477-
* ensures the first server datagram to the new
1478-
* client port carries PATH_CHALLENGE (RFC 9000
1479-
* §8.2 / QIR connectionmigration requirement).
1480-
*/
14811505
if (!nwsi->quic.qn->path_challenge_pending) {
14821506
struct lws_quic_tx_frame *f_pc =
14831507
lws_zalloc(sizeof(*f_pc) + 8,
@@ -1494,8 +1518,6 @@ rops_handle_POLLIN_quic(struct lws_context_per_thread *pt, struct lws *wsi,
14941518
memcpy(nwsi->quic.qn->path_challenge,
14951519
f_pc->data, 8);
14961520
nwsi->quic.qn->path_challenge_pending = 1;
1497-
f_pc->has_dest = 1;
1498-
f_pc->dest_sa46 = migration_sa46;
14991521
lws_dll2_add_head(&f_pc->list,
15001522
&nwsi->quic.qn->pending_tx[LWS_QUIC_LEVEL_APP]);
15011523
}
@@ -1582,47 +1604,13 @@ rops_handle_POLLIN_quic(struct lws_context_per_thread *pt, struct lws *wsi,
15821604
}
15831605

15841606
/*
1585-
* RFC 9000 Section 9.3: commit only when the non-probing packet
1586-
* actually came FROM the address being probed, otherwise ordinary
1587-
* traffic on the existing path makes us flap between paths.
1607+
* The server commits the new path ONLY on PATH_RESPONSE
1608+
* validation (handled in parse-quic.c). Do NOT commit
1609+
* here on non-probing packets: doing so would make the
1610+
* first server datagram to the new client port be a
1611+
* regular ACK/STREAM frame instead of the PATH_CHALLENGE
1612+
* that QIR connectionmigration requires.
15881613
*/
1589-
if (nwsi && nwsi->quic.qn && nwsi->quic.qn->is_server &&
1590-
nwsi->quic.qn->probing_sa46_valid && nwsi->quic.qn->rx_has_non_probing &&
1591-
!lws_sa46_compare_ads(&sa46, &nwsi->quic.qn->probing_sa46)) {
1592-
#if (_LWS_ENABLED_LOGS & LLL_NOTICE)
1593-
char buf_old[64], buf_new[64];
1594-
uint16_t port_old, port_new;
1595-
lws_sa46_write_numeric_address(&nwsi->udp->sa46, buf_old, sizeof(buf_old));
1596-
lws_sa46_write_numeric_address(&nwsi->quic.qn->probing_sa46, buf_new, sizeof(buf_new));
1597-
#if defined(LWS_WITH_IPV6)
1598-
port_old = nwsi->udp->sa46.sa4.sin_family == AF_INET ? nwsi->udp->sa46.sa4.sin_port : nwsi->udp->sa46.sa6.sin6_port;
1599-
port_new = nwsi->quic.qn->probing_sa46.sa4.sin_family == AF_INET ? nwsi->quic.qn->probing_sa46.sa4.sin_port : nwsi->quic.qn->probing_sa46.sa6.sin6_port;
1600-
#else
1601-
port_old = nwsi->udp->sa46.sa4.sin_port;
1602-
port_new = nwsi->quic.qn->probing_sa46.sa4.sin_port;
1603-
#endif
1604-
lwsl_notice("QUIC Server: Connection Migration committed via non-probing packet! Peer address updated from %s:%u to %s:%u\n",
1605-
buf_old, (unsigned int)ntohs(port_old),
1606-
buf_new, (unsigned int)ntohs(port_new));
1607-
#endif
1608-
1609-
nwsi->udp->sa46 = nwsi->quic.qn->probing_sa46;
1610-
nwsi->quic.qn->probing_sa46_valid = 0;
1611-
1612-
/* Reset Congestion Control State (RFC 9000 9.3.3) */
1613-
if (nwsi->quic.qn->cc_ops && nwsi->quic.qn->cc_ops->init)
1614-
nwsi->quic.qn->cc_ops->init(nwsi);
1615-
1616-
/* Reset RTT estimator */
1617-
nwsi->quic.qn->smoothed_rtt = 0;
1618-
nwsi->quic.qn->rttvar = 0;
1619-
nwsi->quic.qn->latest_rtt = 0;
1620-
1621-
/* Reset PMTUD */
1622-
nwsi->quic.qn->current_mtu = 1280;
1623-
nwsi->quic.qn->probed_mtu = 1380;
1624-
nwsi->quic.qn->pmtud_state = 1;
1625-
}
16261614

16271615
if (nwsi) {
16281616
struct lws *w = nwsi->mux.child_list;

lib/roles/quic/private-lib-roles-quic.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,7 @@ struct lws_quic_netconn {
333333
lws_sockfd_type prefaddr_sockfd; /* new probe socket */
334334
uint8_t prefaddr_active:1;
335335
uint8_t prefaddr_committed:1; /* PATH_RESPONSE done */
336+
uint8_t prefaddr_pending:1; /* deferred until handshake done */
336337
lws_sorted_usec_list_t prefaddr_sul;
337338

338339
/* ECN (Explicit Congestion Notification) */

0 commit comments

Comments
 (0)