Line data Source code
1 : #include "fd_quic_ack_tx.h"
2 : #include "fd_quic_private.h"
3 :
4 : #include "fd_quic_proto.h"
5 : #include "fd_quic_proto.c"
6 :
7 : static inline int
8 : fd_quic_range_can_extend( fd_quic_range_t const * range,
9 0 : ulong idx ) {
10 0 : return idx+1UL >= range->offset_lo && idx <= range->offset_hi;
11 0 : }
12 :
13 : static inline int
14 : fd_quic_range_extend( fd_quic_range_t * range,
15 0 : ulong idx ) {
16 0 : int lo_decreased = idx < range->offset_lo;
17 0 : int hi_increased = idx >= range->offset_hi;
18 0 : range->offset_lo = fd_ulong_min( range->offset_lo, idx );
19 0 : range->offset_hi = fd_ulong_max( range->offset_hi, idx+1UL );
20 0 : return lo_decreased || hi_increased;
21 0 : }
22 :
23 : int
24 : fd_quic_ack_pkt( fd_quic_ack_gen_t * gen,
25 : ulong pkt_number,
26 : uint enc_level,
27 0 : long now ) {
28 :
29 0 : if( pkt_number == FD_QUIC_PKT_NUM_UNUSED ) return FD_QUIC_ACK_TX_NOOP;
30 :
31 : /* Can we merge pkt_number into the most recent ACK? */
32 0 : uint cached_seq = gen->head - 1U;
33 0 : fd_quic_ack_t * cached_ack = fd_quic_ack_queue_ele( gen, cached_seq );
34 0 : if( ( enc_level == cached_ack->enc_level ) &
35 0 : fd_quic_range_can_extend( &cached_ack->pkt_number, pkt_number ) ) {
36 :
37 : /* update timestamp */
38 0 : if( pkt_number >= cached_ack->pkt_number.offset_hi ) {
39 0 : cached_ack->ts = now;
40 0 : }
41 :
42 : /* add packet number to existing range */
43 0 : int changed = fd_quic_range_extend( &cached_ack->pkt_number, pkt_number );
44 :
45 : /* re-enqueue most recent ACK for sending */
46 0 : if( changed && gen->head==gen->tail ) {
47 0 : gen->tail = cached_seq;
48 0 : }
49 :
50 0 : FD_ACK_DEBUG( FD_LOG_DEBUG(( "gen=%p queue ACK for enc=%u pkt_num=%lu range=[%lu,%lu) seq=%u (merged)",
51 0 : (void *)gen, enc_level, pkt_number, cached_ack->pkt_number.offset_lo, cached_ack->pkt_number.offset_hi, cached_seq )); )
52 0 : return FD_QUIC_ACK_TX_MERGED;
53 :
54 0 : }
55 :
56 : /* Attempt to allocate another ACK queue entry */
57 0 : if( gen->head - gen->tail >= FD_QUIC_ACK_QUEUE_CNT ) {
58 0 : FD_DEBUG( FD_LOG_DEBUG(( "ACK queue overflow! (excessive reordering)" )); )
59 0 : return FD_QUIC_ACK_TX_ENOSPC;
60 0 : }
61 :
62 : /* Start new pending ACK */
63 0 : FD_ACK_DEBUG( FD_LOG_DEBUG(( "gen=%p queue ACK for enc=%u pkt_num=%lu seq=%u",
64 0 : (void *)gen, enc_level, pkt_number, gen->head )); )
65 0 : fd_quic_ack_t * next_ack = fd_quic_ack_queue_ele( gen, gen->head );
66 0 : *next_ack = (fd_quic_ack_t) {
67 0 : .pkt_number = { .offset_lo = pkt_number, .offset_hi = pkt_number+1UL },
68 0 : .enc_level = (uchar)enc_level,
69 0 : .ts = now
70 0 : };
71 0 : gen->head += 1U;
72 0 : return FD_QUIC_ACK_TX_NEW;
73 0 : }
74 :
75 : void
76 : fd_quic_ack_gen_abandon_enc_level( fd_quic_ack_gen_t * gen,
77 0 : uint enc_level ) {
78 : /* Entries in the ring are not ordered by enc_level, so compact the
79 : whole ring instead of only popping from the tail. */
80 0 : uint w = gen->tail;
81 0 : for( uint r = gen->tail; r != gen->head; r++ ) {
82 0 : fd_quic_ack_t const * ack = fd_quic_ack_queue_ele( gen, r );
83 0 : if( ack->enc_level <= enc_level ) {
84 0 : FD_DEBUG( FD_LOG_DEBUG(( "gen=%p discard ACK for enc=%u range=[%lu,%lu) seq=%u",
85 0 : (void *)gen, ack->enc_level, ack->pkt_number.offset_lo, ack->pkt_number.offset_hi, r )); )
86 0 : continue;
87 0 : }
88 0 : if( w != r ) *fd_quic_ack_queue_ele( gen, w ) = *ack;
89 0 : w++;
90 0 : }
91 0 : gen->head = w;
92 0 : }
93 :
94 : extern ulong
95 : fd_quic_encode_ack_frame( uchar * buf,
96 : ulong sz,
97 : fd_quic_ack_frame_t * frame );
98 :
99 : uchar *
100 : fd_quic_gen_ack_frames( fd_quic_ack_gen_t * gen,
101 : uchar * payload_ptr,
102 : uchar * payload_end,
103 : uint enc_level,
104 : long now,
105 0 : int piggyback ) {
106 :
107 0 : FD_ACK_DEBUG( FD_LOG_DEBUG(( "[ACK gen] elicited=%d", gen->is_elicited )); )
108 : /* Never send an ACK-only packet unless an ACK-eliciting packet is
109 : pending: two such peers would ACK each other's ACKs forever. ACKs
110 : riding in an already ack-eliciting packet are always fine. */
111 0 : if( !gen->is_elicited && !piggyback ) return payload_ptr;
112 :
113 : /* Attempt to send all ACK ranges */
114 0 : ulong ranges_sent = 0UL;
115 0 : for( ; gen->tail != gen->head; gen->tail++ ) {
116 0 : fd_quic_ack_t * ack = fd_quic_ack_queue_ele( gen, gen->tail );
117 0 : if( ack->enc_level != enc_level ) {
118 0 : FD_ACK_DEBUG( FD_LOG_DEBUG(( "need encryption level %u for ACKs but have %u", ack->enc_level, enc_level )); )
119 0 : break;
120 0 : }
121 :
122 0 : long ack_delay_ns = fd_long_sat_sub( now, ack->ts );
123 0 : long ack_delay_us = ack_delay_ns / (long)1e3;
124 :
125 0 : if( FD_UNLIKELY( ack->pkt_number.offset_lo == ack->pkt_number.offset_hi ) ) continue;
126 0 : fd_quic_ack_frame_t ack_frame = {
127 0 : .type = 0x02, /* type 0x02 is the base ack, 0x03 indicates ECN */
128 0 : .largest_ack = ack->pkt_number.offset_hi - 1U,
129 0 : .ack_delay = (ulong)ack_delay_us,
130 0 : .ack_range_count = 0, /* no fragments */
131 0 : .first_ack_range = ack->pkt_number.offset_hi - ack->pkt_number.offset_lo - 1U,
132 0 : };
133 0 : ulong frame_sz = fd_quic_encode_ack_frame( payload_ptr, (ulong)( payload_end - payload_ptr ), &ack_frame );
134 0 : if( FD_UNLIKELY( frame_sz==FD_QUIC_ENCODE_FAIL ) ) {
135 0 : FD_DEBUG( FD_LOG_DEBUG(( "insufficient buffer space to send ACK" )); )
136 0 : break;
137 0 : }
138 0 : payload_ptr += frame_sz;
139 0 : ranges_sent += 1UL;
140 0 : FD_ACK_DEBUG( FD_LOG_DEBUG(( "gen=%p sending ACK enc=%u range=[%lu,%lu) seq=%u sz=%lu",
141 0 : (void *)gen, enc_level, ack->pkt_number.offset_lo, ack->pkt_number.offset_hi, gen->tail, frame_sz )); )
142 0 : }
143 :
144 : /* If all frames were flushed, reset status bits. */
145 0 : if( gen->head == gen->tail ) {
146 0 : gen->is_elicited = 0;
147 0 : } else {
148 0 : FD_ACK_DEBUG( FD_LOG_DEBUG(( "Not all ACK frames were flushed" )); )
149 0 : }
150 :
151 0 : int const flushed = !gen->is_elicited;
152 0 : FD_DTRACE_PROBE_3( fd_quic_gen_ack_frames, enc_level, ranges_sent, flushed );
153 :
154 0 : return payload_ptr;
155 0 : }
|