Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: smoltcp-rs/smoltcp
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: ebee5b7b5843
Choose a base ref
...
head repository: smoltcp-rs/smoltcp
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: c9f915033f5e
Choose a head ref
  • 3 commits
  • 3 files changed
  • 1 contributor

Commits on Jan 26, 2017

  1. Remove TcpControl::len().

    whitequark committed Jan 26, 2017
    Copy the full SHA
    cd880af View commit details
  2. Fix an inaccurate comment.

    whitequark committed Jan 26, 2017
    Copy the full SHA
    be7fb84 View commit details

Commits on Jan 27, 2017

  1. Copy the full SHA
    c9f9150 View commit details
Showing with 169 additions and 22 deletions.
  1. +3 −2 src/socket/tcp.rs
  2. +1 −0 src/wire/mod.rs
  3. +165 −20 src/wire/tcp.rs
5 changes: 3 additions & 2 deletions src/socket/tcp.rs
Original file line number Diff line number Diff line change
@@ -509,8 +509,9 @@ impl<'a> TcpSocket<'a> {
/// there are not enough octets queued in the receive buffer, down to
/// an empty slice.
pub fn recv(&mut self, size: usize) -> Result<&[u8], ()> {
// We may have received some data inside the initial SYN ("TCP Fast Open"),
// but until the connection is fully open we refuse to dequeue any data.
// We may have received some data inside the initial SYN, but until the connection
// is fully open we must not dequeue any data, as it may be overwritten by e.g.
// another (stale) SYN.
if !self.may_recv() { return Err(()) }

#[cfg(any(test, feature = "verbose"))]
1 change: 1 addition & 0 deletions src/wire/mod.rs
Original file line number Diff line number Diff line change
@@ -148,5 +148,6 @@ pub use self::udp::Repr as UdpRepr;

pub use self::tcp::SeqNumber as TcpSeqNumber;
pub use self::tcp::Packet as TcpPacket;
pub use self::tcp::TcpOption;
pub use self::tcp::Repr as TcpRepr;
pub use self::tcp::Control as TcpControl;
185 changes: 165 additions & 20 deletions src/wire/tcp.rs
Original file line number Diff line number Diff line change
@@ -60,7 +60,7 @@ impl cmp::PartialOrd for SeqNumber {
}
}

/// A read/write wrapper around an Transmission Control Protocol packet buffer.
/// A read/write wrapper around a Transmission Control Protocol packet buffer.
#[derive(Debug)]
pub struct Packet<T: AsRef<[u8]>> {
buffer: T
@@ -89,6 +89,11 @@ mod field {
pub const FLG_ECE: u16 = 0x040;
pub const FLG_CWR: u16 = 0x080;
pub const FLG_NS: u16 = 0x100;

pub const OPT_END: u8 = 0x00;
pub const OPT_NOP: u8 = 0x01;
pub const OPT_MSS: u8 = 0x02;
pub const OPT_WS: u8 = 0x03;
}

impl<T: AsRef<[u8]>> Packet<T> {
@@ -263,6 +268,14 @@ impl<T: AsRef<[u8]>> Packet<T> {
}

impl<'a, T: AsRef<[u8]> + ?Sized> Packet<&'a T> {
/// Return a pointer to the options.
#[inline]
pub fn options(&self) -> &'a [u8] {
let header_len = self.header_len() as usize;
let data = self.buffer.as_ref();
&data[field::URGENT.end..header_len]
}

/// Return a pointer to the payload.
#[inline]
pub fn payload(&self) -> &'a [u8] {
@@ -441,6 +454,14 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
}

impl<'a, T: AsRef<[u8]> + AsMut<[u8]> + ?Sized> Packet<&'a mut T> {
/// Return a pointer to the options.
#[inline]
pub fn options_mut(&mut self) -> &mut [u8] {
let header_len = self.header_len() as usize;
let data = self.buffer.as_mut();
&mut data[field::URGENT.end..header_len]
}

/// Return a mutable pointer to the payload data.
#[inline]
pub fn payload_mut(&mut self) -> &mut [u8] {
@@ -450,6 +471,99 @@ impl<'a, T: AsRef<[u8]> + AsMut<[u8]> + ?Sized> Packet<&'a mut T> {
}
}

/// A representation of a single TCP option.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum TcpOption<'a> {
EndOfList,
NoOperation,
MaxSegmentSize(u16),
WindowScale(u8),
Unknown { kind: u8, data: &'a [u8] }
}

impl<'a> TcpOption<'a> {
pub fn parse(buffer: &'a [u8]) -> Result<(&'a [u8], TcpOption<'a>), Error> {
let (length, option);
match *buffer.get(0).ok_or(Error::Truncated)? {
field::OPT_END => {
length = 1;
option = TcpOption::EndOfList;
}
field::OPT_NOP => {
length = 1;
option = TcpOption::NoOperation;
}
kind => {
length = *buffer.get(1).ok_or(Error::Truncated)? as usize;
if buffer.len() < length { return Err(Error::Truncated) }
let data = &buffer[2..length];
match (kind, length) {
(field::OPT_END, _) |
(field::OPT_NOP, _) =>
unreachable!(),
(field::OPT_MSS, 4) =>
option = TcpOption::MaxSegmentSize(NetworkEndian::read_u16(data)),
(field::OPT_MSS, _) =>
return Err(Error::Malformed),
(field::OPT_WS, 3) =>
option = TcpOption::WindowScale(data[0]),
(field::OPT_WS, _) =>
return Err(Error::Malformed),
(_, _) =>
option = TcpOption::Unknown { kind: kind, data: data }
}
}
}
Ok((&buffer[length..], option))
}

pub fn buffer_len(&self) -> usize {
match self {
&TcpOption::EndOfList => 1,
&TcpOption::NoOperation => 1,
&TcpOption::MaxSegmentSize(_) => 4,
&TcpOption::WindowScale(_) => 3,
&TcpOption::Unknown { data, .. } => 2 + data.len()
}
}

pub fn emit<'b>(&self, buffer: &'b mut [u8]) -> &'b mut [u8] {
let length;
match self {
&TcpOption::EndOfList => {
length = 1;
buffer[0] = field::OPT_END;
}
&TcpOption::NoOperation => {
length = 1;
buffer[0] = field::OPT_NOP;
}
_ => {
length = self.buffer_len();
buffer[1] = length as u8;
match self {
&TcpOption::EndOfList |
&TcpOption::NoOperation =>
unreachable!(),
&TcpOption::MaxSegmentSize(value) => {
buffer[0] = field::OPT_MSS;
NetworkEndian::write_u16(&mut buffer[2..], value)
}
&TcpOption::WindowScale(value) => {
buffer[0] = field::OPT_WS;
buffer[2] = value;
}
&TcpOption::Unknown { kind, data: provided } => {
buffer[0] = kind;
buffer[2..].copy_from_slice(provided)
}
}
}
}
&mut buffer[length..]
}
}

/// The control flags of a Transmission Control Protocol packet.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum Control {
@@ -459,18 +573,6 @@ pub enum Control {
Rst
}

impl Control {
/// Return the length of the control flag, in terms of sequence space.
pub fn len(self) -> i32 {
match self {
Control::None => 0,
Control::Syn => 1,
Control::Fin => 1,
Control::Rst => 0
}
}
}

/// A high-level representation of a Transmission Control Protocol packet.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct Repr<'a> {
@@ -618,14 +720,18 @@ mod test {
const SRC_ADDR: Ipv4Address = Ipv4Address([192, 168, 1, 1]);
const DST_ADDR: Ipv4Address = Ipv4Address([192, 168, 1, 2]);

static PACKET_BYTES: [u8; 24] =
static PACKET_BYTES: [u8; 28] =
[0xbf, 0x00, 0x00, 0x50,
0x01, 0x23, 0x45, 0x67,
0x89, 0xab, 0xcd, 0xef,
0x50, 0x35, 0x01, 0x23,
0x20, 0xbe, 0x02, 0x01,
0x60, 0x35, 0x01, 0x23,
0x01, 0xb6, 0x02, 0x01,
0x03, 0x03, 0x0c, 0x01,
0xaa, 0x00, 0x00, 0xff];

static OPTION_BYTES: [u8; 4] =
[0x03, 0x03, 0x0c, 0x01];

static PAYLOAD_BYTES: [u8; 4] =
[0xaa, 0x00, 0x00, 0xff];

@@ -636,7 +742,7 @@ mod test {
assert_eq!(packet.dst_port(), 80);
assert_eq!(packet.seq_number(), SeqNumber(0x01234567));
assert_eq!(packet.ack_number(), SeqNumber(0x89abcdefu32 as i32));
assert_eq!(packet.header_len(), 20);
assert_eq!(packet.header_len(), 24);
assert_eq!(packet.fin(), true);
assert_eq!(packet.syn(), false);
assert_eq!(packet.rst(), true);
@@ -645,20 +751,21 @@ mod test {
assert_eq!(packet.urg(), true);
assert_eq!(packet.window_len(), 0x0123);
assert_eq!(packet.urgent_at(), 0x0201);
assert_eq!(packet.checksum(), 0x20be);
assert_eq!(packet.checksum(), 0x01b6);
assert_eq!(packet.options(), &OPTION_BYTES[..]);
assert_eq!(packet.payload(), &PAYLOAD_BYTES[..]);
assert_eq!(packet.verify_checksum(&SRC_ADDR.into(), &DST_ADDR.into()), true);
}

#[test]
fn test_construct() {
let mut bytes = vec![0; 24];
let mut bytes = vec![0; PACKET_BYTES.len()];
let mut packet = Packet::new(&mut bytes).unwrap();
packet.set_src_port(48896);
packet.set_dst_port(80);
packet.set_seq_number(SeqNumber(0x01234567));
packet.set_ack_number(SeqNumber(0x89abcdefu32 as i32));
packet.set_header_len(20);
packet.set_header_len(24);
packet.set_fin(true);
packet.set_syn(false);
packet.set_rst(true);
@@ -668,6 +775,7 @@ mod test {
packet.set_window_len(0x0123);
packet.set_urgent_at(0x0201);
packet.set_checksum(0xEEEE);
packet.options_mut().copy_from_slice(&OPTION_BYTES[..]);
packet.payload_mut().copy_from_slice(&PAYLOAD_BYTES[..]);
packet.fill_checksum(&SRC_ADDR.into(), &DST_ADDR.into());
assert_eq!(&packet.into_inner()[..], &PACKET_BYTES[..]);
@@ -708,4 +816,41 @@ mod test {
repr.emit(&mut packet, &SRC_ADDR.into(), &DST_ADDR.into());
assert_eq!(&packet.into_inner()[..], &SYN_PACKET_BYTES[..]);
}

macro_rules! assert_option_parses {
($opt:expr, $data:expr) => ({
assert_eq!(TcpOption::parse($data), Ok((&[][..], $opt)));
let buffer = &mut [0; 20][..$opt.buffer_len()];
assert_eq!($opt.emit(buffer), &mut []);
assert_eq!(&*buffer, $data);
})
}

#[test]
fn test_tcp_options() {
assert_option_parses!(TcpOption::EndOfList,
&[0x00]);
assert_option_parses!(TcpOption::NoOperation,
&[0x01]);
assert_option_parses!(TcpOption::MaxSegmentSize(1500),
&[0x02, 0x04, 0x05, 0xdc]);
assert_option_parses!(TcpOption::WindowScale(12),
&[0x03, 0x03, 0x0c]);
assert_option_parses!(TcpOption::Unknown { kind: 12, data: &[1, 2, 3][..] },
&[0x0c, 0x05, 0x01, 0x02, 0x03])
}

#[test]
fn test_malformed_tcp_options() {
assert_eq!(TcpOption::parse(&[]),
Err(Error::Truncated));
assert_eq!(TcpOption::parse(&[0xc]),
Err(Error::Truncated));
assert_eq!(TcpOption::parse(&[0xc, 0x05, 0x01, 0x02]),
Err(Error::Truncated));
assert_eq!(TcpOption::parse(&[0x2, 0x02]),
Err(Error::Malformed));
assert_eq!(TcpOption::parse(&[0x3, 0x02]),
Err(Error::Malformed));
}
}