1/// An object to encode the window scaling logic. It asserts that no operations have been taken
2/// out-of-order.
3#[derive(Copy, Clone, Debug)]
4pub(crate) struct WindowScaling {
5 sent_syn: bool,
6 received_syn: bool,
7 recv_window_scale_shift: Option<u8>,
8 send_window_scale_shift: Option<u8>,
9 disabled: bool,
10}
1112impl WindowScaling {
13/// Maximum value of the "Window Scale" TCP option as specified by RFC 7323.
14const MAX_SCALE_SHIFT: u8 = 14;
1516pub fn new() -> Self {
17Self {
18 sent_syn: false,
19 received_syn: false,
20 recv_window_scale_shift: None,
21 send_window_scale_shift: None,
22 disabled: false,
23 }
24 }
2526/// Given a maximum window size, this returns the window scale shift that should be used. As the
27 /// window size must be less than 2^30, the returned shift will be limited to 14. This does not
28 /// guarantee that `window >> shift` fits within a `u16`.
29pub fn scale_shift_for_max_window(window: u32) -> u8 {
30// the maximum possible window allowed by tcp
31let max_window_size = (u16::MAX as u32) << Self::MAX_SCALE_SHIFT;
3233// calculation based on linux 6.5 `tcp_select_initial_window`:
34 // https://elixir.bootlin.com/linux/v6.5/source/net/ipv4/tcp_output.c#L206
3536let window = std::cmp::min(window, max_window_size);
3738if window == 0 {
39return 0;
40 }
4142let shift = window
43 .ilog2()
44 .saturating_sub(Self::MAX_SCALE_SHIFT as u32 + 1);
45let shift = std::cmp::min(shift, Self::MAX_SCALE_SHIFT as u32);
4647 shift.try_into().unwrap()
48 }
4950/// Disable window scaling. This will ensure that a window scale is not sent in a SYN packet.
51pub fn disable(&mut self) {
52// tried to disable window scaling after sending the SYN
53assert!(!self.sent_syn);
54self.disabled = true;
55 }
5657/// A SYN packet was sent with the given window scale.
58pub fn sent_syn(&mut self, window_scale: Option<u8>) {
59// why did we send the SYN twice?
60assert!(!self.sent_syn);
6162// if it was disabled, we shouldn't have sent a window scale in the SYN
63if self.disabled {
64assert!(window_scale.is_none());
65 }
6667if self.received_syn && self.send_window_scale_shift.is_none() {
68// RFC 7323 1.3.:
69 // > Furthermore, the Window Scale option will be sent in a <SYN,ACK> segment only if
70 // > the corresponding option was received in the initial <SYN> segment.
71assert!(window_scale.is_none());
72 }
7374if let Some(window_scale) = window_scale {
75// RFC 7323 2.3.:
76 // > Since the max window is 2^S (where S is the scaling shift count) times at most 2^16
77 // > - 1 (the maximum unscaled window), the maximum window is guaranteed to be < 2^30 if
78 // > S <= 14. Thus, the shift count MUST be limited to 14 (which allows windows of 2^30
79 // > = 1 GiB).
80assert!(window_scale <= Self::MAX_SCALE_SHIFT);
81 }
8283self.sent_syn = true;
84self.recv_window_scale_shift = window_scale;
85 }
8687/// A SYN packet was received with the given window scale.
88pub fn received_syn(&mut self, mut window_scale: Option<u8>) {
89// why did we receive the SYN twice?
90assert!(!self.received_syn);
9192if let Some(ref mut window_scale) = window_scale {
93// RFC 7323 2.3.:
94 // > If a Window Scale option is received with a shift.cnt value larger than 14, the TCP
95 // > SHOULD log the error but MUST use 14 instead of the specified value.
96if *window_scale > Self::MAX_SCALE_SHIFT {
97*window_scale = Self::MAX_SCALE_SHIFT;
98 }
99 }
100101self.received_syn = true;
102self.send_window_scale_shift = window_scale;
103 }
104105/// Checks if it's valid for an outward SYN packet to contain a window scale option.
106pub fn can_send_window_scale(&self) -> bool {
107// can't send if window scaling has been disabled, or if we've already received a SYN packet
108 // that did not have window scaling enabled
109!(self.disabled || (self.received_syn && self.send_window_scale_shift.is_none()))
110 }
111112/// Has window scaling been configured? This does *not* mean that it's enabled. If this returns
113 /// true, then `recv_window_scale_shift()` and `send_window_scale_shift()` should not panic.
114pub fn is_configured(&self) -> bool {
115self.disabled || (self.sent_syn && self.received_syn)
116 }
117118fn recv_shift(&self) -> u8 {
119if self.disabled {
120return 0;
121 }
122123if self.send_window_scale_shift.is_some() && self.recv_window_scale_shift.is_some() {
124self.recv_window_scale_shift.unwrap()
125 } else {
1260
127}
128 }
129130fn send_shift(&self) -> u8 {
131if self.disabled {
132return 0;
133 }
134135if self.send_window_scale_shift.is_some() && self.recv_window_scale_shift.is_some() {
136self.send_window_scale_shift.unwrap()
137 } else {
1380
139}
140 }
141142/// The right bit-shift to apply to the receive buffer's window size when sending a packet.
143pub fn recv_window_scale_shift(&self) -> u8 {
144// we shouldn't be trying to get the receive window scale before we've fully configured
145 // window scaling
146assert!(self.is_configured());
147148self.recv_shift()
149 }
150151/// The left bit-shift to apply to the send buffer's window size when receiving a packet.
152pub fn send_window_scale_shift(&self) -> u8 {
153// we shouldn't be trying to get the send window scale before we've fully configured window
154 // scaling
155assert!(self.is_configured());
156157self.send_shift()
158 }
159160pub fn recv_window_max(&self) -> u32 {
161 (u16::MAX as u32) << self.recv_shift()
162 }
163}
164165#[cfg(test)]
166mod tests {
167use super::*;
168169#[test]
170fn test_scale_shift_for_max_window() {
171// the maximum possible window allowed by tcp
172const MAX_WINDOW_SIZE: u32 = (u16::MAX as u32) << 14;
173174fn test(window: u32, expected: u8) {
175let rv = WindowScaling::scale_shift_for_max_window(window);
176177assert_eq!(rv, expected);
178179// Check that applying the window scale bit-shift does result in a value that fits
180 // within a u16. We can't check this if the provided window size is larger than
181 // `MAX_WINDOW_SIZE`, since we use a max bit shift value of 14.
182if window <= MAX_WINDOW_SIZE {
183assert!(window >> rv <= u16::MAX as u32);
184 }
185 }
186187const U16_MAX: u32 = u16::MAX as u32;
188189 test(0, 0);
190 test(1, 0);
191192 test(U16_MAX, 0);
193 test(U16_MAX + 1, 1);
194195 test(2 * U16_MAX, 1);
196 test(2 * U16_MAX + 1, 1);
197 test(2 * (U16_MAX + 1), 2);
198199 test(4 * U16_MAX, 2);
200 test(4 * U16_MAX + 1, 2);
201 test(4 * U16_MAX + 2, 2);
202 test(4 * (U16_MAX + 1), 3);
203204 test(MAX_WINDOW_SIZE / 2, 13);
205 test(MAX_WINDOW_SIZE / 2 + 1, 13);
206 test(MAX_WINDOW_SIZE / 2 + 14, 13);
207 test(MAX_WINDOW_SIZE / 2 + 2_u32.pow(13) - 1, 13);
208 test(MAX_WINDOW_SIZE / 2 + 2_u32.pow(13), 14);
209210 test(MAX_WINDOW_SIZE - 1, 14);
211 test(MAX_WINDOW_SIZE, 14);
212 test(MAX_WINDOW_SIZE + 1, 14);
213 test(MAX_WINDOW_SIZE + 2_u32.pow(14), 14);
214 test(MAX_WINDOW_SIZE + 2_u32.pow(14) + 1, 14);
215216 test(u32::MAX, 14);
217 }
218}