Skip to content

Commit

Permalink
Implement TCP Window Scaling, Phase 1 (Issue #106)
Browse files Browse the repository at this point in the history
Closes: #232
Approved by: whitequark
  • Loading branch information
jhwgh1968 authored and Homu committed Jun 24, 2018
1 parent d4b3318 commit 42d25bf
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 5 deletions.
58 changes: 54 additions & 4 deletions src/socket/tcp.rs
Expand Up @@ -229,6 +229,8 @@ pub struct TcpSocket<'a> {
/// The speculative remote window size.
/// I.e. the actual remote window size minus the count of in-flight octets.
remote_win_len: usize,
/// The receive window scaling factor for remotes which support RFC 1323, None if unsupported.
remote_win_scale: Option<u8>,
/// The maximum number of data octets that the remote side may receive.
remote_mss: usize,
/// The timestamp of the last packet received.
Expand Down Expand Up @@ -271,6 +273,7 @@ impl<'a> TcpSocket<'a> {
remote_last_ack: None,
remote_last_win: 0,
remote_win_len: 0,
remote_win_scale: None,
remote_mss: DEFAULT_MSS,
remote_last_ts: None,
local_rx_last_ack: None,
Expand Down Expand Up @@ -397,6 +400,7 @@ impl<'a> TcpSocket<'a> {
self.remote_last_ack = None;
self.remote_last_win = 0;
self.remote_win_len = 0;
self.remote_win_scale = None;
self.remote_mss = DEFAULT_MSS;
self.remote_last_ts = None;
}
Expand Down Expand Up @@ -768,6 +772,7 @@ impl<'a> TcpSocket<'a> {
seq_number: TcpSeqNumber(0),
ack_number: None,
window_len: 0,
window_scale: None,
max_seg_size: None,
payload: &[]
};
Expand Down Expand Up @@ -1024,6 +1029,7 @@ impl<'a> TcpSocket<'a> {
if let Some(max_seg_size) = repr.max_seg_size {
self.remote_mss = max_seg_size as usize
}
self.remote_win_scale = repr.window_scale;
self.set_state(State::SynReceived);
self.timer.set_for_idle(timestamp, self.keep_alive);
}
Expand Down Expand Up @@ -1134,7 +1140,10 @@ impl<'a> TcpSocket<'a> {

// Update remote state.
self.remote_last_ts = Some(timestamp);
self.remote_win_len = repr.window_len as usize;

// RFC 1323: The window field (SEG.WND) in the header of every incoming segment, with the
// exception of SYN segments, is left-shifted by Snd.Wind.Scale bits before updating SND.WND.
self.remote_win_len = (repr.window_len as usize) << (self.remote_win_scale.unwrap_or(0) as usize);

if ack_len > 0 {
// Dequeue acknowledged octets.
Expand Down Expand Up @@ -1376,6 +1385,7 @@ impl<'a> TcpSocket<'a> {
seq_number: self.remote_last_seq,
ack_number: Some(self.remote_seq_no + self.rx_buffer.len()),
window_len: self.rx_buffer.window() as u16,
window_scale: None,
max_seg_size: None,
payload: &[]
};
Expand All @@ -1396,6 +1406,9 @@ impl<'a> TcpSocket<'a> {
repr.control = TcpControl::Syn;
if self.state == State::SynSent {
repr.ack_number = None;
repr.window_scale = Some(0);
} else {
repr.window_scale = self.remote_win_scale.map(|_| 0);
}
}

Expand Down Expand Up @@ -1471,7 +1484,7 @@ impl<'a> TcpSocket<'a> {
// Fill the MSS option. See RFC 6691 for an explanation of this calculation.
let mut max_segment_size = caps.max_transmission_unit;
max_segment_size -= ip_repr.buffer_len();
max_segment_size -= repr.header_len();
max_segment_size -= repr.mss_header_len();
repr.max_seg_size = Some(max_segment_size as u16);
}

Expand Down Expand Up @@ -1589,7 +1602,8 @@ mod test {
src_port: REMOTE_PORT, dst_port: LOCAL_PORT,
control: TcpControl::None,
seq_number: TcpSeqNumber(0), ack_number: Some(TcpSeqNumber(0)),
window_len: 256, max_seg_size: None,
window_len: 256, window_scale: None,
max_seg_size: None,
payload: &[]
};
const _RECV_IP_TEMPL: IpRepr = IpRepr::Unspecified {
Expand All @@ -1601,7 +1615,8 @@ mod test {
src_port: LOCAL_PORT, dst_port: REMOTE_PORT,
control: TcpControl::None,
seq_number: TcpSeqNumber(0), ack_number: Some(TcpSeqNumber(0)),
window_len: 64, max_seg_size: None,
window_len: 64, window_scale: None,
max_seg_size: None,
payload: &[]
};

Expand Down Expand Up @@ -2020,6 +2035,35 @@ mod test {
assert_eq!(s.remote_endpoint, IpEndpoint::default());
}

#[test]
fn test_syn_received_no_window_scaling() {
let mut s = socket_listen();
send!(s, TcpRepr {
control: TcpControl::Syn,
seq_number: REMOTE_SEQ,
ack_number: None,
..SEND_TEMPL
});
assert_eq!(s.state(), State::SynReceived);
assert_eq!(s.local_endpoint(), LOCAL_END);
assert_eq!(s.remote_endpoint(), REMOTE_END);
recv!(s, [TcpRepr {
control: TcpControl::Syn,
seq_number: LOCAL_SEQ,
ack_number: Some(REMOTE_SEQ + 1),
max_seg_size: Some(BASE_MSS),
window_scale: None,
..RECV_TEMPL
}]);
send!(s, TcpRepr {
seq_number: REMOTE_SEQ + 1,
ack_number: Some(LOCAL_SEQ + 1),
window_scale: None,
..SEND_TEMPL
});
assert_eq!(s.remote_win_scale, None);
}

#[test]
fn test_syn_received_close() {
let mut s = socket_syn_received();
Expand Down Expand Up @@ -2055,13 +2099,15 @@ mod test {
seq_number: LOCAL_SEQ,
ack_number: None,
max_seg_size: Some(BASE_MSS),
window_scale: Some(0),
..RECV_TEMPL
}]);
send!(s, TcpRepr {
control: TcpControl::Syn,
seq_number: REMOTE_SEQ,
ack_number: Some(LOCAL_SEQ + 1),
max_seg_size: Some(BASE_MSS - 80),
window_scale: Some(0),
..SEND_TEMPL
});
assert_eq!(s.local_endpoint, LOCAL_END);
Expand Down Expand Up @@ -2110,13 +2156,15 @@ mod test {
seq_number: LOCAL_SEQ,
ack_number: None,
max_seg_size: Some(BASE_MSS),
window_scale: Some(0),
..RECV_TEMPL
}]);
send!(s, TcpRepr {
control: TcpControl::Syn,
seq_number: REMOTE_SEQ,
ack_number: Some(LOCAL_SEQ + 1),
max_seg_size: Some(BASE_MSS - 80),
window_scale: Some(0),
..SEND_TEMPL
});
recv!(s, [TcpRepr {
Expand Down Expand Up @@ -3762,6 +3810,7 @@ mod test {
seq_number: LOCAL_SEQ,
ack_number: None,
max_seg_size: Some(BASE_MSS),
window_scale: Some(0),
..RECV_TEMPL
}));
assert_eq!(s.state, State::SynSent);
Expand All @@ -3770,6 +3819,7 @@ mod test {
control: TcpControl::Rst,
seq_number: LOCAL_SEQ + 1,
ack_number: Some(TcpSeqNumber(0)),
window_scale: None,
..RECV_TEMPL
}));
assert_eq!(s.state, State::Closed);
Expand Down
34 changes: 33 additions & 1 deletion src/wire/tcp.rs
Expand Up @@ -645,6 +645,7 @@ pub struct Repr<'a> {
pub seq_number: SeqNumber,
pub ack_number: Option<SeqNumber>,
pub window_len: u16,
pub window_scale: Option<u8>,
pub max_seg_size: Option<u16>,
pub payload: &'a [u8]
}
Expand Down Expand Up @@ -682,6 +683,7 @@ impl<'a> Repr<'a> {
// cut the byte at the urgent pointer from the stream.

let mut max_seg_size = None;
let mut window_scale = None;
let mut options = packet.options();
while options.len() > 0 {
let (next_options, option) = TcpOption::parse(options)?;
Expand All @@ -690,7 +692,19 @@ impl<'a> Repr<'a> {
TcpOption::NoOperation => (),
TcpOption::MaxSegmentSize(value) =>
max_seg_size = Some(value),
_ => ()
TcpOption::WindowScale(value) => {
// RFC 1323: Thus, the shift count must be limited to 14 (which allows windows
// of 2**30 = 1 Gbyte). If a Window Scale option is received with a shift.cnt
// value exceeding 14, the TCP should log the error but use 14 instead of the
// specified value.
window_scale = if value > 14 {
net_debug!("{}:{}:{}:{}: parsed window scaling factor >14, setting to 14", src_addr, packet.src_port(), dst_addr, packet.dst_port());
Some(14)
} else {
Some(value)
};
}
_ => (),
}
options = next_options;
}
Expand All @@ -702,20 +716,34 @@ impl<'a> Repr<'a> {
seq_number: packet.seq_number(),
ack_number: ack_number,
window_len: packet.window_len(),
window_scale: window_scale,
max_seg_size: max_seg_size,
payload: packet.payload()
})
}

/// Return the length of a header that will be emitted from this high-level representation.
///
/// This should be used for buffer space calculations.
pub fn header_len(&self) -> usize {
let mut length = field::URGENT.end;
if self.max_seg_size.is_some() {
length += 4
}
if self.window_scale.is_some() {
length += 3
}
length
}

/// Return the length of the header for the TCP protocol.
///
/// Per RFC 6691, this should be used for MSS calculations. It may be smaller than the buffer
/// space required to accomodate this packet's data.
pub fn mss_header_len(&self) -> usize {
field::URGENT.end
}

/// Return the length of a packet that will be emitted from this high-level representation.
pub fn buffer_len(&self) -> usize {
self.header_len() + self.payload.len()
Expand All @@ -742,6 +770,9 @@ impl<'a> Repr<'a> {
packet.set_ack(self.ack_number.is_some());
{
let mut options = packet.options_mut();
if let Some(value) = self.window_scale {
let tmp = options; options = TcpOption::WindowScale(value).emit(tmp);
}
if let Some(value) = self.max_seg_size {
let tmp = options; options = TcpOption::MaxSegmentSize(value).emit(tmp);
}
Expand Down Expand Up @@ -967,6 +998,7 @@ mod test {
seq_number: SeqNumber(0x01234567),
ack_number: None,
window_len: 0x0123,
window_scale: None,
control: Control::Syn,
max_seg_size: None,
payload: &PAYLOAD_BYTES
Expand Down

0 comments on commit 42d25bf

Please sign in to comment.