@@ -778,15 +778,43 @@ xqc_send_ctl_on_packet_sent(xqc_send_ctl_t *send_ctl, xqc_pn_ctl_t *pn_ctl, xqc_
778778 }
779779}
780780
781+
782+ xqc_int_t
783+ xqc_send_ctl_detect_optimistic_ack_attack (xqc_send_ctl_t * send_ctl ,
784+ xqc_pn_ctl_t * pn_ctl , xqc_ack_info_t * const ack_info , xqc_usec_t ack_recv_time )
785+ {
786+ xqc_connection_t * conn = send_ctl -> ctl_conn ;
787+ xqc_pktno_range_t * range = NULL ;
788+ xqc_pkt_num_space_t pns = ack_info -> pns ;
789+ int i ;
790+
791+ if (!conn -> conn_settings .disable_pn_skipping
792+ /* only check application data */
793+ && pns == XQC_PNS_APP_DATA
794+ /* if ack is received after the next skip chance,
795+ it is unlikely to be optimistic ack */
796+ && ack_recv_time < pn_ctl -> ctl_next_skip_chance )
797+ {
798+ for (i = ack_info -> n_ranges - 1 ; i >= 0 ; i -- ) {
799+ range = & ack_info -> ranges [i ];
800+ /* check if skipped packet numbers are acked */
801+ if (xqc_max (range -> low , pn_ctl -> ctl_skipped_pn_low ) <= xqc_min (range -> high , pn_ctl -> ctl_skipped_pn_high )) {
802+ xqc_log (conn -> log , XQC_LOG_ERROR , "|optimistic ack attack detected|ack_range:%ui-%ui|skipped_range:%ui-%ui|" , range -> low , range -> high , pn_ctl -> ctl_skipped_pn_low , pn_ctl -> ctl_skipped_pn_high );
803+ return - XQC_EPROTO ;
804+ }
805+ }
806+ }
807+
808+ return XQC_OK ;
809+ }
810+
811+
781812/**
782813 * OnAckReceived
783814 */
784815int
785816xqc_send_ctl_on_ack_received (xqc_send_ctl_t * send_ctl , xqc_pn_ctl_t * pn_ctl , xqc_send_queue_t * send_queue , xqc_ack_info_t * const ack_info , xqc_usec_t ack_recv_time , xqc_bool_t ack_on_same_path )
786817{
787- /* ack info里包含的packet不一定会是send_ctl这条路径发出的 */
788- /* info里的largest ack 不一定是send_ctl这条路径的largest ack */
789-
790818 xqc_connection_t * conn = send_ctl -> ctl_conn ;
791819
792820 xqc_packet_out_t * packet_out ;
@@ -803,6 +831,11 @@ xqc_send_ctl_on_ack_received(xqc_send_ctl_t *send_ctl, xqc_pn_ctl_t *pn_ctl, xqc
803831 unsigned char need_del_record = 0 ;
804832 int stream_frame_acked = 0 ;
805833
834+ if (xqc_send_ctl_detect_optimistic_ack_attack (send_ctl , pn_ctl , ack_info , ack_recv_time ) < 0 ) {
835+ XQC_CONN_ERR (conn , TRA_PROTOCOL_VIOLATION );
836+ return - XQC_EPROTO ;
837+ }
838+
806839 xqc_packet_number_t largest_acked_ack = xqc_ack_sent_record_on_ack (& pn_ctl -> ack_sent_record [pns ], ack_info );
807840 if (largest_acked_ack > pn_ctl -> ctl_largest_acked_ack [pns ]) {
808841 pn_ctl -> ctl_largest_acked_ack [pns ] = largest_acked_ack ;
@@ -1864,3 +1897,37 @@ uint64_t
18641897xqc_send_ctl_get_pacing_rate (xqc_send_ctl_t * send_ctl ) {
18651898 return xqc_pacing_rate_calc (& send_ctl -> ctl_pacing );
18661899}
1900+
1901+
1902+ void
1903+ xqc_send_ctl_set_next_pn_for_packet (xqc_connection_t * conn , xqc_pn_ctl_t * pn_ctl ,
1904+ xqc_packet_out_t * packet_out , xqc_usec_t current_time )
1905+ {
1906+ if (conn -> conn_settings .disable_pn_skipping ) {
1907+ packet_out -> po_pkt .pkt_num = pn_ctl -> ctl_packet_number [packet_out -> po_pkt .pkt_pns ]++ ;
1908+
1909+ } else {
1910+ packet_out -> po_pkt .pkt_num = pn_ctl -> ctl_packet_number [packet_out -> po_pkt .pkt_pns ];
1911+ if (packet_out -> po_pkt .pkt_num > 0
1912+ && packet_out -> po_pkt .pkt_pns == XQC_PNS_APP_DATA )
1913+ {
1914+ /* randomly skip some packet numbers to avoid optimistic ack attacks */
1915+ if (current_time > pn_ctl -> ctl_next_skip_chance ) {
1916+ /* we have 20% chance to skip some packet numbers */
1917+ /* we must skip some packet numbers if pn has been increased up to 2^10 since the last skip */
1918+ if (current_time % 10 < 2
1919+ || packet_out -> po_pkt .pkt_num > (pn_ctl -> ctl_skipped_pn_high + 1024 ))
1920+ {
1921+ /* the number of skipped packet numbers is also random */
1922+ pn_ctl -> ctl_skipped_pn_low = packet_out -> po_pkt .pkt_num ;
1923+ pn_ctl -> ctl_skipped_pn_high = pn_ctl -> ctl_skipped_pn_low + current_time % 7 ;
1924+ packet_out -> po_pkt .pkt_num = pn_ctl -> ctl_skipped_pn_high + 1 ;
1925+ /* we should keep this range for 3xPTO to detect malicious ACKs */
1926+ pn_ctl -> ctl_next_skip_chance = current_time + 3 * xqc_conn_get_max_pto (conn );
1927+ xqc_log (conn -> log , XQC_LOG_DEBUG , "|optimistic ack detection|skipped_range:%ui-%ui|next_skip_chance:%ui|current_time:%ui|" , pn_ctl -> ctl_skipped_pn_low , pn_ctl -> ctl_skipped_pn_high , pn_ctl -> ctl_next_skip_chance , current_time );
1928+ }
1929+ }
1930+ }
1931+ pn_ctl -> ctl_packet_number [packet_out -> po_pkt .pkt_pns ] = packet_out -> po_pkt .pkt_num + 1 ;
1932+ }
1933+ }
0 commit comments