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,
};
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub(crate) struct Segment<P: Payload> {
pub(crate) seq: SeqNum,
pub(crate) ack: Option<SeqNum>,
pub(crate) wnd: UnscaledWindowSize,
pub(crate) contents: Contents<P>,
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) => {
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));
}
TcpOption::SackPermitted
| TcpOption::Sack(_)
| TcpOption::Timestamp { ts_val: _, ts_echo_reply: _ } => {}
}
}
options
}
}
pub(super) const MAX_PAYLOAD_AND_CONTROL_LEN: usize = 1 << 31;
const MAX_PAYLOAD_AND_CONTROL_LEN_U32: u32 = MAX_PAYLOAD_AND_CONTROL_LEN as u32;
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub(crate) struct Contents<P: Payload> {
control: Option<Control>,
data: P,
}
impl<P: Payload> Contents<P> {
pub(super) fn len(&self) -> u32 {
let Self { data, control } = self;
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 {
let (control, control_len) = if control == Some(Control::FIN) {
(None, 0)
} else {
(control, has_control_len.into())
};
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)
}
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> {
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;
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))
|| (!(seq + len).before(rnxt) && !(seq + len).after(rnxt + rwnd))
}
};
overlap.then(move || {
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;
let start = u32::try_from(new_seq - seq).unwrap();
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<()> {
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 {
let (seg, truncated) = Segment::with_data_options(seq, ack, control, wnd, (), options);
debug_assert_eq!(truncated, 0);
seg
}
pub(super) fn ack(seq: SeqNum, ack: SeqNum, wnd: UnscaledWindowSize) -> Self {
Segment::new(seq, Some(ack), None, wnd)
}
pub(super) fn syn(seq: SeqNum, wnd: UnscaledWindowSize, options: Options) -> Self {
Segment::with_options(seq, None, Some(Control::SYN), wnd, options)
}
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)
}
pub(super) fn rst(seq: SeqNum) -> Self {
Segment::new(seq, None, Some(Control::RST), UnscaledWindowSize::from(0))
}
pub(super) fn rst_ack(seq: SeqNum, ack: SeqNum) -> Self {
Segment::new(seq, Some(ack), Some(Control::RST), UnscaledWindowSize::from(0))
}
}
pub trait Payload: Sized {
fn len(&self) -> usize;
fn slice(self, range: Range<u32>) -> Self;
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 {
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) },
)
}
}