blob: 48d2617e029053467f63569e0cc7f82a3d371d4f [file] [log] [blame]
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//! The definition of a TCP segment.
use core::{
convert::TryFrom as _,
num::{NonZeroU16, TryFromIntError},
ops::Range,
};
use packet_formats::tcp::options::TcpOption;
use crate::transport::tcp::{
buffer::SendPayload,
seqnum::{SeqNum, UnscaledWindowSize, WindowScale, WindowSize},
Control, Mss,
};
/// A TCP segment.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub(crate) struct Segment<P: Payload> {
/// The sequence number of the segment.
pub(crate) seq: SeqNum,
/// The acknowledge number of the segment. [`None`] if not present.
pub(crate) ack: Option<SeqNum>,
/// The advertised window size.
pub(crate) wnd: UnscaledWindowSize,
/// The carried data and its control flag.
pub(crate) contents: Contents<P>,
/// Options carried by this segment.
pub(crate) options: Options,
}
#[derive(Debug, Default, PartialEq, Eq, Clone, Copy)]
pub(crate) struct Options {
pub(crate) mss: Option<Mss>,
pub(crate) window_scale: Option<WindowScale>,
}
impl Options {
pub(crate) fn iter(
&self,
) -> impl Iterator<Item = TcpOption<'static>> + core::fmt::Debug + Clone {
self.mss
.map(|mss| TcpOption::Mss(mss.get().get()))
.into_iter()
.chain(self.window_scale.map(|ws| TcpOption::WindowScale(ws.get())))
}
pub(crate) fn from_iter<'a>(iter: impl IntoIterator<Item = TcpOption<'a>>) -> Self {
let mut options = Options::default();
for option in iter {
match option {
TcpOption::Mss(mss) => {
options.mss = NonZeroU16::new(mss).map(Mss);
}
TcpOption::WindowScale(ws) => {
// Per RFC 7323 Section 2.3:
// If a Window Scale option is received with a shift.cnt
// value larger than 14, the TCP SHOULD log the error but
// MUST use 14 instead of the specified value.
if ws > WindowScale::MAX.get() {
tracing::info!(
"received an out-of-range window scale: {}, want < {}",
ws,
WindowScale::MAX.get()
);
}
options.window_scale = Some(WindowScale::new(ws).unwrap_or(WindowScale::MAX));
}
// TODO(https://fxbug.dev/42072902): We don't support these yet.
TcpOption::SackPermitted
| TcpOption::Sack(_)
| TcpOption::Timestamp { ts_val: _, ts_echo_reply: _ } => {}
}
}
options
}
}
/// The maximum length that the sequence number doesn't wrap around.
pub(super) const MAX_PAYLOAD_AND_CONTROL_LEN: usize = 1 << 31;
// The following `as` is sound because it is representable by `u32`.
const MAX_PAYLOAD_AND_CONTROL_LEN_U32: u32 = MAX_PAYLOAD_AND_CONTROL_LEN as u32;
/// The contents of a TCP segment that takes up some sequence number space.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub(crate) struct Contents<P: Payload> {
/// The control flag of the segment.
control: Option<Control>,
/// The data carried by the segment; it is guaranteed that
/// `data.len() + control_len <= MAX_PAYLOAD_AND_CONTROL_LEN`.
data: P,
}
impl<P: Payload> Contents<P> {
/// Returns the length of the segment in sequence number space.
///
/// Per RFC 793 (https://tools.ietf.org/html/rfc793#page-25):
/// SEG.LEN = the number of octets occupied by the data in the segment
/// (counting SYN and FIN)
pub(super) fn len(&self) -> u32 {
let Self { data, control } = self;
// The following unwrap and addition are fine because:
// - `u32::from(has_control_len)` is 0 or 1.
// - `self.data.len() <= 2^31`.
let has_control_len = control.map(Control::has_sequence_no).unwrap_or(false);
u32::try_from(data.len()).unwrap() + u32::from(has_control_len)
}
pub(super) fn control(&self) -> Option<Control> {
self.control
}
pub(super) fn data(&self) -> &P {
&self.data
}
}
impl<P: Payload> Segment<P> {
pub(super) fn with_data_options(
seq: SeqNum,
ack: Option<SeqNum>,
control: Option<Control>,
wnd: UnscaledWindowSize,
data: P,
options: Options,
) -> (Self, usize) {
let has_control_len = control.map(Control::has_sequence_no).unwrap_or(false);
let discarded_len =
data.len().saturating_sub(MAX_PAYLOAD_AND_CONTROL_LEN - usize::from(has_control_len));
let contents = if discarded_len > 0 {
// If we have to truncate the segment, the FIN flag must be removed
// because it is logically the last octet of the segment.
let (control, control_len) = if control == Some(Control::FIN) {
(None, 0)
} else {
(control, has_control_len.into())
};
// The following slice will not panic because `discarded_len > 0`,
// thus `data.len() > MAX_PAYLOAD_AND_CONTROL_LEN - control_len`.
Contents { control, data: data.slice(0..MAX_PAYLOAD_AND_CONTROL_LEN_U32 - control_len) }
} else {
Contents { control, data }
};
(Segment { seq, ack, wnd, contents, options }, discarded_len)
}
/// Creates a new segment with data.
///
/// Returns the segment along with how many bytes were removed to make sure
/// sequence numbers don't wrap around, i.e., `seq.before(seq + seg.len())`.
pub(super) fn with_data(
seq: SeqNum,
ack: Option<SeqNum>,
control: Option<Control>,
wnd: UnscaledWindowSize,
data: P,
) -> (Self, usize) {
Self::with_data_options(seq, ack, control, wnd, data, Options::default())
}
}
impl<P: Payload> Segment<P> {
/// Returns the part of the incoming segment within the receive window.
pub(super) fn overlap(self, rnxt: SeqNum, rwnd: WindowSize) -> Option<Segment<P>> {
let Segment { seq, ack, wnd, contents, options } = self;
let len = contents.len();
let Contents { control, data } = contents;
// RFC 793 (https://tools.ietf.org/html/rfc793#page-69):
// There are four cases for the acceptability test for an incoming
// segment:
// Segment Receive Test
// Length Window
// ------- ------- -------------------------------------------
// 0 0 SEG.SEQ = RCV.NXT
// 0 >0 RCV.NXT =< SEG.SEQ < RCV.NXT+RCV.WND
// >0 0 not acceptable
// >0 >0 RCV.NXT =< SEG.SEQ < RCV.NXT+RCV.WND
// or RCV.NXT =< SEG.SEQ+SEG.LEN-1 < RCV.NXT+RCV.WND
let overlap = match (len, rwnd) {
(0, WindowSize::ZERO) => seq == rnxt,
(0, rwnd) => !rnxt.after(seq) && seq.before(rnxt + rwnd),
(_len, WindowSize::ZERO) => false,
(len, rwnd) => {
(!rnxt.after(seq) && seq.before(rnxt + rwnd))
// Note: here we use RCV.NXT <= SEG.SEQ+SEG.LEN instead of
// the condition as quoted above because of the following
// text immediately after the above table:
// One could tailor actual segments to fit this assumption by
// trimming off any portions that lie outside the window
// (including SYN and FIN), and only processing further if
// the segment then begins at RCV.NXT.
// This is essential for TCP simultaneous open to work,
// otherwise, the state machine would reject the SYN-ACK
// sent by the peer.
|| (!(seq + len).before(rnxt) && !(seq + len).after(rnxt + rwnd))
}
};
overlap.then(move || {
// We deliberately don't define `PartialOrd` for `SeqNum`, so we use
// `cmp` below to utilize `cmp::{max,min}_by`.
let cmp = |lhs: &SeqNum, rhs: &SeqNum| (*lhs - *rhs).cmp(&0);
let new_seq = core::cmp::max_by(seq, rnxt, cmp);
let new_len = core::cmp::min_by(seq + len, rnxt + rwnd, cmp) - new_seq;
// The following unwrap won't panic because:
// 1. if `seq` is after `rnxt`, then `start` would be 0.
// 2. the interesting case is when `rnxt` is after `seq`, in that
// case, we have `rnxt - seq > 0`, thus `new_seq - seq > 0`.
let start = u32::try_from(new_seq - seq).unwrap();
// The following unwrap won't panic because:
// 1. The witness on `Segment` and `WindowSize` guarantees that
// `len <= 2^31` and `rwnd <= 2^30-1` thus
// `seq <= seq + len` and `rnxt <= rnxt + rwnd`.
// 2. We are in the closure because `overlap` is true which means
// `seq <= rnxt + rwnd` and `rnxt <= seq + len`.
// With these two conditions combined, `new_len` can't be negative
// so the unwrap can't panic.
let new_len = u32::try_from(new_len).unwrap();
let (new_control, new_data) = {
match control {
Some(Control::SYN) => {
if start == 0 {
(Some(Control::SYN), data.slice(start..start + new_len - 1))
} else {
(None, data.slice(start - 1..start + new_len - 1))
}
}
Some(Control::FIN) => {
if len == start + new_len {
if new_len > 0 {
(Some(Control::FIN), data.slice(start..start + new_len - 1))
} else {
(None, data.slice(start - 1..start - 1))
}
} else {
(None, data.slice(start..start + new_len))
}
}
Some(Control::RST) | None => (control, data.slice(start..start + new_len)),
}
};
Segment {
seq: new_seq,
ack,
wnd,
contents: Contents { control: new_control, data: new_data },
options,
}
})
}
}
impl Segment<()> {
/// Creates a segment with no data.
pub(super) fn new(
seq: SeqNum,
ack: Option<SeqNum>,
control: Option<Control>,
wnd: UnscaledWindowSize,
) -> Self {
Self::with_options(seq, ack, control, wnd, Options::default())
}
pub(super) fn with_options(
seq: SeqNum,
ack: Option<SeqNum>,
control: Option<Control>,
wnd: UnscaledWindowSize,
options: Options,
) -> Self {
// All of the checks on lengths are optimized out:
// https://godbolt.org/z/KPd537G6Y
let (seg, truncated) = Segment::with_data_options(seq, ack, control, wnd, (), options);
debug_assert_eq!(truncated, 0);
seg
}
/// Creates an ACK segment.
pub(super) fn ack(seq: SeqNum, ack: SeqNum, wnd: UnscaledWindowSize) -> Self {
Segment::new(seq, Some(ack), None, wnd)
}
/// Creates a SYN segment.
pub(super) fn syn(seq: SeqNum, wnd: UnscaledWindowSize, options: Options) -> Self {
Segment::with_options(seq, None, Some(Control::SYN), wnd, options)
}
/// Creates a SYN-ACK segment.
pub(super) fn syn_ack(
seq: SeqNum,
ack: SeqNum,
wnd: UnscaledWindowSize,
options: Options,
) -> Self {
Segment::with_options(seq, Some(ack), Some(Control::SYN), wnd, options)
}
/// Creates a RST segment.
pub(super) fn rst(seq: SeqNum) -> Self {
Segment::new(seq, None, Some(Control::RST), UnscaledWindowSize::from(0))
}
/// Creates a RST-ACK segment.
pub(super) fn rst_ack(seq: SeqNum, ack: SeqNum) -> Self {
Segment::new(seq, Some(ack), Some(Control::RST), UnscaledWindowSize::from(0))
}
}
/// A TCP payload that operates around `u32` instead of `usize`.
pub trait Payload: Sized {
/// Returns the length of the payload.
fn len(&self) -> usize;
/// Creates a slice of the payload, reducing it to only the bytes within
/// `range`.
///
/// # Panics
///
/// Panics if the provided `range` is not within the bounds of this
/// `Payload`, or if the range is nonsensical (the end precedes
/// the start).
fn slice(self, range: Range<u32>) -> Self;
/// Copies part of the payload beginning at `offset` into `dst`.
///
/// # Panics
///
/// Panics if offset is too large or we couldn't fill the `dst` slice.
fn partial_copy(&self, offset: usize, dst: &mut [u8]);
}
impl Payload for &[u8] {
fn len(&self) -> usize {
<[u8]>::len(self)
}
fn slice(self, Range { start, end }: Range<u32>) -> Self {
// The following `unwrap`s are ok because:
// `usize::try_from(x)` fails when `x > usize::MAX`; given that
// `self.len() <= usize::MAX`, panic would be expected because `range`
// exceeds the bound of `self`.
let start = usize::try_from(start).unwrap_or_else(|TryFromIntError { .. }| {
panic!("range start index {} out of range for slice of length {}", start, self.len())
});
let end = usize::try_from(end).unwrap_or_else(|TryFromIntError { .. }| {
panic!("range end index {} out of range for slice of length {}", end, self.len())
});
&self[start..end]
}
fn partial_copy(&self, offset: usize, dst: &mut [u8]) {
dst.copy_from_slice(&self[offset..offset + dst.len()])
}
}
impl Payload for () {
fn len(&self) -> usize {
0
}
fn slice(self, Range { start, end }: Range<u32>) -> Self {
if start != 0 {
panic!("range start index {} out of range for slice of length 0", start);
}
if end != 0 {
panic!("range end index {} out of range for slice of length 0", end);
}
()
}
fn partial_copy(&self, offset: usize, dst: &mut [u8]) {
if dst.len() != 0 || offset != 0 {
panic!(
"source slice length (0) does not match destination slice length ({})",
dst.len()
);
}
}
}
impl From<Segment<()>> for Segment<&'static [u8]> {
fn from(
Segment { seq, ack, wnd, contents: Contents { control, data: () }, options }: Segment<()>,
) -> Self {
Segment { seq, ack, wnd, contents: Contents { control, data: &[] }, options }
}
}
impl From<Segment<()>> for Segment<SendPayload<'static>> {
fn from(
Segment { seq, ack, wnd, contents: Contents { control, data: () }, options }: Segment<()>,
) -> Self {
Segment {
seq,
ack,
wnd,
contents: Contents { control, data: SendPayload::Contiguous(&[]) },
options,
}
}
}
#[cfg(test)]
mod test {
use test_case::test_case;
use super::*;
#[test_case(None, &[][..] => (0, &[][..]); "empty")]
#[test_case(None, &[1][..] => (1, &[1][..]); "no control")]
#[test_case(Some(Control::SYN), &[][..] => (1, &[][..]); "empty slice with syn")]
#[test_case(Some(Control::SYN), &[1][..] => (2, &[1][..]); "non-empty slice with syn")]
#[test_case(Some(Control::FIN), &[][..] => (1, &[][..]); "empty slice with fin")]
#[test_case(Some(Control::FIN), &[1][..] => (2, &[1][..]); "non-empty slice with fin")]
#[test_case(Some(Control::RST), &[][..] => (0, &[][..]); "empty slice with rst")]
#[test_case(Some(Control::RST), &[1][..] => (1, &[1][..]); "non-empty slice with rst")]
fn segment_len(control: Option<Control>, data: &[u8]) -> (u32, &[u8]) {
let (seg, truncated) = Segment::with_data(
SeqNum::new(1),
Some(SeqNum::new(1)),
control,
UnscaledWindowSize::from(0),
data,
);
assert_eq!(truncated, 0);
(seg.contents.len(), seg.contents.data)
}
#[test_case(&[1, 2, 3, 4, 5][..], 0..4 => [1, 2, 3, 4])]
#[test_case((), 0..0 => [0, 0, 0, 0])]
fn payload_slice_copy(data: impl Payload, range: Range<u32>) -> [u8; 4] {
let sliced = data.slice(range);
let mut buffer = [0; 4];
sliced.partial_copy(0, &mut buffer[..sliced.len()]);
buffer
}
#[derive(Debug, PartialEq, Eq)]
struct TestPayload(Range<u32>);
impl TestPayload {
fn new(len: usize) -> Self {
Self(0..u32::try_from(len).unwrap())
}
}
impl Payload for TestPayload {
fn len(&self) -> usize {
self.0.len()
}
fn slice(self, range: Range<u32>) -> Self {
let Self(this) = self;
assert!(range.start >= this.start && range.end <= this.end);
TestPayload(range)
}
fn partial_copy(&self, _offset: usize, _dst: &mut [u8]) {
unimplemented!("TestPayload doesn't carry any data");
}
}
#[test_case(100, Some(Control::SYN) => (100, Some(Control::SYN), 0))]
#[test_case(100, Some(Control::FIN) => (100, Some(Control::FIN), 0))]
#[test_case(100, Some(Control::RST) => (100, Some(Control::RST), 0))]
#[test_case(100, None => (100, None, 0))]
#[test_case(MAX_PAYLOAD_AND_CONTROL_LEN - 1, Some(Control::SYN)
=> (MAX_PAYLOAD_AND_CONTROL_LEN - 1, Some(Control::SYN), 0))]
#[test_case(MAX_PAYLOAD_AND_CONTROL_LEN - 1, Some(Control::FIN)
=> (MAX_PAYLOAD_AND_CONTROL_LEN - 1, Some(Control::FIN), 0))]
#[test_case(MAX_PAYLOAD_AND_CONTROL_LEN - 1, Some(Control::RST)
=> (MAX_PAYLOAD_AND_CONTROL_LEN - 1, Some(Control::RST), 0))]
#[test_case(MAX_PAYLOAD_AND_CONTROL_LEN - 1, None
=> (MAX_PAYLOAD_AND_CONTROL_LEN - 1, None, 0))]
#[test_case(MAX_PAYLOAD_AND_CONTROL_LEN, Some(Control::SYN)
=> (MAX_PAYLOAD_AND_CONTROL_LEN - 1, Some(Control::SYN), 1))]
#[test_case(MAX_PAYLOAD_AND_CONTROL_LEN, Some(Control::FIN)
=> (MAX_PAYLOAD_AND_CONTROL_LEN, None, 1))]
#[test_case(MAX_PAYLOAD_AND_CONTROL_LEN, Some(Control::RST)
=> (MAX_PAYLOAD_AND_CONTROL_LEN, Some(Control::RST), 0))]
#[test_case(MAX_PAYLOAD_AND_CONTROL_LEN, None
=> (MAX_PAYLOAD_AND_CONTROL_LEN, None, 0))]
#[test_case(MAX_PAYLOAD_AND_CONTROL_LEN + 1, Some(Control::SYN)
=> (MAX_PAYLOAD_AND_CONTROL_LEN - 1, Some(Control::SYN), 2))]
#[test_case(MAX_PAYLOAD_AND_CONTROL_LEN + 1, Some(Control::FIN)
=> (MAX_PAYLOAD_AND_CONTROL_LEN, None, 2))]
#[test_case(MAX_PAYLOAD_AND_CONTROL_LEN + 1, Some(Control::RST)
=> (MAX_PAYLOAD_AND_CONTROL_LEN, Some(Control::RST), 1))]
#[test_case(MAX_PAYLOAD_AND_CONTROL_LEN + 1, None
=> (MAX_PAYLOAD_AND_CONTROL_LEN, None, 1))]
#[test_case(u32::MAX as usize, Some(Control::SYN)
=> (MAX_PAYLOAD_AND_CONTROL_LEN - 1, Some(Control::SYN), 1 << 31))]
fn segment_truncate(len: usize, control: Option<Control>) -> (usize, Option<Control>, usize) {
let (seg, truncated) = Segment::with_data(
SeqNum::new(0),
None,
control,
UnscaledWindowSize::from(0),
TestPayload::new(len),
);
(seg.contents.data.len(), seg.contents.control, truncated)
}
struct OverlapTestArgs {
seg_seq: u32,
control: Option<Control>,
data_len: u32,
rcv_nxt: u32,
rcv_wnd: usize,
}
#[test_case(OverlapTestArgs{
seg_seq: 1,
control: None,
data_len: 0,
rcv_nxt: 0,
rcv_wnd: 0,
} => None)]
#[test_case(OverlapTestArgs{
seg_seq: 1,
control: None,
data_len: 0,
rcv_nxt: 1,
rcv_wnd: 0,
} => Some((SeqNum::new(1), None, 0..0)))]
#[test_case(OverlapTestArgs{
seg_seq: 1,
control: None,
data_len: 0,
rcv_nxt: 2,
rcv_wnd: 0,
} => None)]
#[test_case(OverlapTestArgs{
seg_seq: 1,
control: Some(Control::SYN),
data_len: 0,
rcv_nxt: 2,
rcv_wnd: 0,
} => None)]
#[test_case(OverlapTestArgs{
seg_seq: 1,
control: Some(Control::SYN),
data_len: 0,
rcv_nxt: 1,
rcv_wnd: 0,
} => None)]
#[test_case(OverlapTestArgs{
seg_seq: 1,
control: Some(Control::SYN),
data_len: 0,
rcv_nxt: 0,
rcv_wnd: 0,
} => None)]
#[test_case(OverlapTestArgs{
seg_seq: 1,
control: Some(Control::FIN),
data_len: 0,
rcv_nxt: 2,
rcv_wnd: 0,
} => None)]
#[test_case(OverlapTestArgs{
seg_seq: 1,
control: Some(Control::FIN),
data_len: 0,
rcv_nxt: 1,
rcv_wnd: 0,
} => None)]
#[test_case(OverlapTestArgs{
seg_seq: 1,
control: Some(Control::FIN),
data_len: 0,
rcv_nxt: 0,
rcv_wnd: 0,
} => None)]
#[test_case(OverlapTestArgs{
seg_seq: 0,
control: None,
data_len: 0,
rcv_nxt: 1,
rcv_wnd: 1,
} => None)]
#[test_case(OverlapTestArgs{
seg_seq: 1,
control: None,
data_len: 0,
rcv_nxt: 1,
rcv_wnd: 1,
} => Some((SeqNum::new(1), None, 0..0)))]
#[test_case(OverlapTestArgs{
seg_seq: 2,
control: None,
data_len: 0,
rcv_nxt: 1,
rcv_wnd: 1,
} => None)]
#[test_case(OverlapTestArgs{
seg_seq: 0,
control: None,
data_len: 1,
rcv_nxt: 1,
rcv_wnd: 1,
} => Some((SeqNum::new(1), None, 1..1)))]
#[test_case(OverlapTestArgs{
seg_seq: 0,
control: Some(Control::SYN),
data_len: 0,
rcv_nxt: 1,
rcv_wnd: 1,
} => Some((SeqNum::new(1), None, 0..0)))]
#[test_case(OverlapTestArgs{
seg_seq: 2,
control: None,
data_len: 1,
rcv_nxt: 1,
rcv_wnd: 1,
} => None)]
#[test_case(OverlapTestArgs{
seg_seq: 0,
control: None,
data_len: 2,
rcv_nxt: 1,
rcv_wnd: 1,
} => Some((SeqNum::new(1), None, 1..2)))]
#[test_case(OverlapTestArgs{
seg_seq: 1,
control: None,
data_len: 2,
rcv_nxt: 1,
rcv_wnd: 1,
} => Some((SeqNum::new(1), None, 0..1)))]
#[test_case(OverlapTestArgs{
seg_seq: 0,
control: Some(Control::SYN),
data_len: 1,
rcv_nxt: 1,
rcv_wnd: 1,
} => Some((SeqNum::new(1), None, 0..1)))]
#[test_case(OverlapTestArgs{
seg_seq: 1,
control: Some(Control::SYN),
data_len: 1,
rcv_nxt: 1,
rcv_wnd: 1,
} => Some((SeqNum::new(1), Some(Control::SYN), 0..0)))]
#[test_case(OverlapTestArgs{
seg_seq: 0,
control: Some(Control::FIN),
data_len: 1,
rcv_nxt: 1,
rcv_wnd: 1,
} => Some((SeqNum::new(1), Some(Control::FIN), 1..1)))]
#[test_case(OverlapTestArgs{
seg_seq: 1,
control: Some(Control::FIN),
data_len: 1,
rcv_nxt: 1,
rcv_wnd: 1,
} => Some((SeqNum::new(1), None, 0..1)))]
#[test_case(OverlapTestArgs{
seg_seq: 1,
control: None,
data_len: MAX_PAYLOAD_AND_CONTROL_LEN_U32,
rcv_nxt: 1,
rcv_wnd: 10,
} => Some((SeqNum::new(1), None, 0..10)))]
#[test_case(OverlapTestArgs{
seg_seq: 10,
control: None,
data_len: MAX_PAYLOAD_AND_CONTROL_LEN_U32,
rcv_nxt: 1,
rcv_wnd: 10,
} => Some((SeqNum::new(10), None, 0..1)))]
#[test_case(OverlapTestArgs{
seg_seq: 1,
control: None,
data_len: 10,
rcv_nxt: 1,
rcv_wnd: 1 << 30 - 1,
} => Some((SeqNum::new(1), None, 0..10)))]
#[test_case(OverlapTestArgs{
seg_seq: 10,
control: None,
data_len: 10,
rcv_nxt: 1,
rcv_wnd: 1 << 30 - 1,
} => Some((SeqNum::new(10), None, 0..10)))]
#[test_case(OverlapTestArgs{
seg_seq: 1,
control: Some(Control::FIN),
data_len: 1,
rcv_nxt: 3,
rcv_wnd: 10,
} => Some((SeqNum::new(3), None, 1..1)); "regression test for https://fxbug.dev/42061750")]
fn segment_overlap(
OverlapTestArgs { seg_seq, control, data_len, rcv_nxt, rcv_wnd }: OverlapTestArgs,
) -> Option<(SeqNum, Option<Control>, Range<u32>)> {
let (seg, discarded) = Segment::with_data(
SeqNum::new(seg_seq),
None,
control,
UnscaledWindowSize::from(0),
TestPayload(0..data_len),
);
assert_eq!(discarded, 0);
seg.overlap(SeqNum::new(rcv_nxt), WindowSize::new(rcv_wnd).unwrap()).map(
|Segment {
seq,
ack: _,
wnd: _,
contents: Contents { control, data: TestPayload(range) },
options: _,
}| { (seq, control, range) },
)
}
}