@@ -1148,28 +1148,6 @@ rops_handle_POLLIN_quic(struct lws_context_per_thread *pt, struct lws *wsi,
11481148 break ;
11491149 }
11501150
1151- /* Check reserved bits AFTER unmasking! */
1152- if (p [0 ] & 0x80 ) {
1153- /* Long header: Bits 0x0c MUST be zero */
1154- if (p [0 ] & 0x0c ) {
1155- lwsl_wsi_notice (wsi , "QUIC RX: Reserved bits non-zero in long header" );
1156- if (nwsi && nwsi != wsi ) {
1157- lws_quic_enter_closing_state (nwsi , LWS_QUIC_ERR_PROTOCOL_VIOLATION , 0 , 0 );
1158- goto next_packet ;
1159- }
1160- return LWS_HPI_RET_PLEASE_CLOSE_ME ;
1161- }
1162- } else {
1163- /* Short header: Bits 0x18 MUST be zero */
1164- if (p [0 ] & 0x18 ) {
1165- lwsl_wsi_notice (wsi , "QUIC RX: Reserved bits non-zero in short header" );
1166- if (nwsi && nwsi != wsi ) {
1167- lws_quic_enter_closing_state (nwsi , LWS_QUIC_ERR_PROTOCOL_VIOLATION , 0 , 0 );
1168- goto next_packet ;
1169- }
1170- return LWS_HPI_RET_PLEASE_CLOSE_ME ;
1171- }
1172- }
11731151
11741152 /*
11751153 * Reconstruct full 62-bit PN.
@@ -1221,6 +1199,29 @@ rops_handle_POLLIN_quic(struct lws_context_per_thread *pt, struct lws *wsi,
12211199 goto next_packet ;
12221200 }
12231201
1202+ /* Check reserved bits AFTER successful AEAD decryption (RFC 9000 5.4.1 & 12.2) */
1203+ if (p [0 ] & 0x80 ) {
1204+ /* Long header: Bits 0x0c MUST be zero */
1205+ if (p [0 ] & 0x0c ) {
1206+ lwsl_wsi_notice (wsi , "QUIC RX: Reserved bits non-zero in long header" );
1207+ if (nwsi && nwsi != wsi ) {
1208+ lws_quic_enter_closing_state (nwsi , LWS_QUIC_ERR_PROTOCOL_VIOLATION , 0 , 0 );
1209+ goto next_packet ;
1210+ }
1211+ return LWS_HPI_RET_PLEASE_CLOSE_ME ;
1212+ }
1213+ } else {
1214+ /* Short header: Bits 0x18 MUST be zero */
1215+ if (p [0 ] & 0x18 ) {
1216+ lwsl_wsi_notice (wsi , "QUIC RX: Reserved bits non-zero in short header" );
1217+ if (nwsi && nwsi != wsi ) {
1218+ lws_quic_enter_closing_state (nwsi , LWS_QUIC_ERR_PROTOCOL_VIOLATION , 0 , 0 );
1219+ goto next_packet ;
1220+ }
1221+ return LWS_HPI_RET_PLEASE_CLOSE_ME ;
1222+ }
1223+ }
1224+
12241225 /* Decryption succeeded! Commit key update if pending */
12251226 if (is_key_update ) {
12261227 lws_quic_keys_release_aead_rx (k );
@@ -1272,6 +1273,7 @@ rops_handle_POLLIN_quic(struct lws_context_per_thread *pt, struct lws *wsi,
12721273 }
12731274
12741275 /* Check for duplicate/replayed packet numbers (Security Fix) */
1276+ int is_out_of_order = 0 ;
12751277 if (nwsi -> quic .qn ) {
12761278 uint64_t highest = nwsi -> quic .qn -> highest_rx_pn [pn_space ];
12771279 if ((nwsi -> quic .qn -> rx_pn_bitmask [pn_space ] != 0 || highest != 0 ) && full_pn <= highest ) {
@@ -1281,6 +1283,7 @@ rops_handle_POLLIN_quic(struct lws_context_per_thread *pt, struct lws *wsi,
12811283 goto next_packet ;
12821284 }
12831285 nwsi -> quic .qn -> rx_pn_bitmask [pn_space ] |= (1ULL << diff );
1286+ is_out_of_order = 1 ;
12841287 } else {
12851288 if (nwsi -> quic .qn -> rx_pn_bitmask [pn_space ] != 0 || highest != 0 ) {
12861289 uint64_t diff = full_pn - highest ;
@@ -1295,6 +1298,11 @@ rops_handle_POLLIN_quic(struct lws_context_per_thread *pt, struct lws *wsi,
12951298
12961299 /* Connection Migration: Execute pending migration now that the packet is cryptographically verified */
12971300 if (pending_migration ) {
1301+ if (is_out_of_order ) {
1302+ lwsl_notice ("QUIC: Ignoring connection migration from out-of-order packet (PN %llu <= highest %llu)\n" ,
1303+ (unsigned long long )full_pn , (unsigned long long )highest );
1304+ pending_migration = 0 ;
1305+ } else {
12981306 pending_migration = 0 ;
12991307#if (_LWS_ENABLED_LOGS & LLL_NOTICE )
13001308 char buf_old [64 ], buf_new [64 ];
@@ -1318,7 +1326,7 @@ rops_handle_POLLIN_quic(struct lws_context_per_thread *pt, struct lws *wsi,
13181326 buf_old , (unsigned int )ntohs (port_old ),
13191327 buf_new , (unsigned int )ntohs (port_new ));
13201328#endif
1321- /* F-60: Do NOT commit nwsi->udp->sa46 yet! Wait for PATH_RESPONSE! */
1329+ nwsi -> quic . qn -> rx_has_non_probing = 0 ;
13221330 nwsi -> quic .qn -> probing_sa46 = migration_sa46 ;
13231331 nwsi -> quic .qn -> probing_sa46_valid = 1 ;
13241332
@@ -1381,6 +1389,7 @@ rops_handle_POLLIN_quic(struct lws_context_per_thread *pt, struct lws *wsi,
13811389 lws_callback_on_writable (nwsi );
13821390 }
13831391 }
1392+ }
13841393 }
13851394
13861395 /* 5. Parse and handle all frames in the payload */
@@ -1400,6 +1409,46 @@ rops_handle_POLLIN_quic(struct lws_context_per_thread *pt, struct lws *wsi,
14001409 nwsi = lws_get_quic_network_wsi (nwsi );
14011410 }
14021411
1412+ /* RFC 9000 Section 9.3: Receiving non-probing frames (STREAM, ACK, etc.) from a new address
1413+ * indicates that the peer has migrated (e.g. due to NAT rebinding or active client migration).
1414+ * The server MUST commit its active path to the new address. */
1415+ if (nwsi && nwsi -> quic .qn && nwsi -> quic .qn -> is_server &&
1416+ nwsi -> quic .qn -> probing_sa46_valid && nwsi -> quic .qn -> rx_has_non_probing ) {
1417+ #if (_LWS_ENABLED_LOGS & LLL_NOTICE )
1418+ char buf_old [64 ], buf_new [64 ];
1419+ uint16_t port_old , port_new ;
1420+ lws_sa46_write_numeric_address (& nwsi -> udp -> sa46 , buf_old , sizeof (buf_old ));
1421+ lws_sa46_write_numeric_address (& nwsi -> quic .qn -> probing_sa46 , buf_new , sizeof (buf_new ));
1422+ #if defined(LWS_WITH_IPV6 )
1423+ port_old = nwsi -> udp -> sa46 .sa4 .sin_family == AF_INET ? nwsi -> udp -> sa46 .sa4 .sin_port : nwsi -> udp -> sa46 .sa6 .sin6_port ;
1424+ 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 ;
1425+ #else
1426+ port_old = nwsi -> udp -> sa46 .sa4 .sin_port ;
1427+ port_new = nwsi -> quic .qn -> probing_sa46 .sa4 .sin_port ;
1428+ #endif
1429+ lwsl_notice ("QUIC Server: Connection Migration committed via non-probing packet! Peer address updated from %s:%u to %s:%u\n" ,
1430+ buf_old , (unsigned int )ntohs (port_old ),
1431+ buf_new , (unsigned int )ntohs (port_new ));
1432+ #endif
1433+
1434+ nwsi -> udp -> sa46 = nwsi -> quic .qn -> probing_sa46 ;
1435+ nwsi -> quic .qn -> probing_sa46_valid = 0 ;
1436+
1437+ /* Reset Congestion Control State (RFC 9000 9.3.3) */
1438+ if (nwsi -> quic .qn -> cc_ops && nwsi -> quic .qn -> cc_ops -> init )
1439+ nwsi -> quic .qn -> cc_ops -> init (nwsi );
1440+
1441+ /* Reset RTT estimator */
1442+ nwsi -> quic .qn -> smoothed_rtt = 0 ;
1443+ nwsi -> quic .qn -> rttvar = 0 ;
1444+ nwsi -> quic .qn -> latest_rtt = 0 ;
1445+
1446+ /* Reset PMTUD */
1447+ nwsi -> quic .qn -> current_mtu = 1280 ;
1448+ nwsi -> quic .qn -> probed_mtu = 1380 ;
1449+ nwsi -> quic .qn -> pmtud_state = 1 ;
1450+ }
1451+
14031452 if (nwsi ) {
14041453 struct lws * w = nwsi -> mux .child_list ;
14051454 while (w ) {
@@ -2648,6 +2697,9 @@ rops_client_bind_quic(struct lws *wsi, const struct lws_client_connect_info *i)
26482697
26492698 lws_quic_queue_path_challenge (wsi );
26502699
2700+ lws_role_transition (wsi , LWSIFR_CLIENT , LRS_UNCONNECTED , & role_ops_quic );
2701+ lws_callback_on_writable (wsi );
2702+
26512703 /* Skip all initialization and key derivation, just return */
26522704 return 1 ;
26532705 }
0 commit comments