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: cef2d58c73bf
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: 388e94b32a4c
Choose a head ref
  • 4 commits
  • 5 files changed
  • 2 contributors

Commits on Sep 8, 2017

  1. Factor out RawSocket::accepts.

    batonius authored and whitequark committed Sep 8, 2017
    Copy the full SHA
    5303501 View commit details
  2. Factor out UdpSocket::accepts.

    batonius authored and whitequark committed Sep 8, 2017
    Copy the full SHA
    44cf21b View commit details
  3. Factor out TcpSocket::accepts.

    batonius authored and whitequark committed Sep 8, 2017
    Copy the full SHA
    a2c66fd View commit details
  4. Remove Error::Rejected.

    This wasn't an actual error, just a poorly designed communication
    mechanism between sockets and whatever lies on the layer below them.
    whitequark committed Sep 8, 2017
    Copy the full SHA
    388e94b View commit details
Showing with 180 additions and 39 deletions.
  1. +13 −11 src/iface/ethernet.rs
  2. +0 −5 src/lib.rs
  3. +26 −4 src/socket/raw.rs
  4. +99 −15 src/socket/tcp.rs
  5. +42 −4 src/socket/udp.rs
24 changes: 13 additions & 11 deletions src/iface/ethernet.rs
Original file line number Diff line number Diff line change
@@ -289,13 +289,15 @@ impl<'a, 'b, 'c, DeviceT: Device + 'a> Interface<'a, 'b, 'c, DeviceT> {
let mut handled_by_raw_socket = false;
for raw_socket in sockets.iter_mut().filter_map(
<Socket as AsSocket<RawSocket>>::try_as_socket) {
if !raw_socket.accepts(&ip_repr) { continue }

match raw_socket.process(&ip_repr, ip_payload) {
// The packet is valid and handled by socket.
Ok(()) => handled_by_raw_socket = true,
// The packet isn't addressed to the socket, or cannot be accepted by it.
Err(Error::Rejected) | Err(Error::Exhausted) => (),
// Raw sockets either accept or reject packets, not parse them.
_ => unreachable!(),
// The socket buffer is full.
Err(Error::Exhausted) => (),
// Raw sockets don't validate the packets in any way.
Err(_) => unreachable!(),
}
}

@@ -369,12 +371,12 @@ impl<'a, 'b, 'c, DeviceT: Device + 'a> Interface<'a, 'b, 'c, DeviceT> {

for udp_socket in sockets.iter_mut().filter_map(
<Socket as AsSocket<UdpSocket>>::try_as_socket) {
if !udp_socket.accepts(&ip_repr, &udp_repr) { continue }

match udp_socket.process(&ip_repr, &udp_repr) {
// The packet is valid and handled by socket.
Ok(()) => return Ok(Packet::None),
// The packet isn't addressed to the socket.
Err(Error::Rejected) => continue,
// The packet is malformed, or addressed to the socket but cannot be accepted.
// The packet is malformed, or the socket buffer is full.
Err(e) => return Err(e)
}
}
@@ -410,13 +412,13 @@ impl<'a, 'b, 'c, DeviceT: Device + 'a> Interface<'a, 'b, 'c, DeviceT> {

for tcp_socket in sockets.iter_mut().filter_map(
<Socket as AsSocket<TcpSocket>>::try_as_socket) {
if !tcp_socket.accepts(&ip_repr, &tcp_repr) { continue }

match tcp_socket.process(timestamp, &ip_repr, &tcp_repr) {
// The packet is valid and handled by socket.
Ok(reply) => return Ok(reply.map_or(Packet::None, Packet::Tcp)),
// The packet isn't addressed to the socket.
// Send RST only if no other socket accepts the packet.
Err(Error::Rejected) => continue,
// The packet is malformed, or addressed to the socket but cannot be accepted.
// The packet is malformed, or doesn't match the socket state,
// or the socket buffer is full.
Err(e) => return Err(e)
}
}
5 changes: 0 additions & 5 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -126,10 +126,6 @@ pub enum Error {
/// E.g. a TCP packet addressed to a socket that doesn't exist.
Dropped,

// Implementation detail.
#[doc(hidden)]
Rejected,

#[doc(hidden)]
__Nonexhaustive
}
@@ -149,7 +145,6 @@ impl fmt::Display for Error {
&Error::Fragmented => write!(f, "fragmented packet"),
&Error::Malformed => write!(f, "malformed packet"),
&Error::Dropped => write!(f, "dropped by socket"),
&Error::Rejected => write!(f, "rejected by socket"),
&Error::__Nonexhaustive => unreachable!()
}
}
30 changes: 26 additions & 4 deletions src/socket/raw.rs
Original file line number Diff line number Diff line change
@@ -166,9 +166,15 @@ impl<'a, 'b> RawSocket<'a, 'b> {
Ok(length)
}

pub(crate) fn accepts(&self, ip_repr: &IpRepr) -> bool {
if ip_repr.version() != self.ip_version { return false }
if ip_repr.protocol() != self.ip_protocol { return false }

true
}

pub(crate) fn process(&mut self, ip_repr: &IpRepr, payload: &[u8]) -> Result<()> {
if ip_repr.version() != self.ip_version { return Err(Error::Rejected) }
if ip_repr.protocol() != self.ip_protocol { return Err(Error::Rejected) }
debug_assert!(self.accepts(ip_repr));

let header_len = ip_repr.buffer_len();
let total_len = header_len + payload.len();
@@ -246,17 +252,18 @@ mod test {
fn socket(rx_buffer: SocketBuffer<'static, 'static>,
tx_buffer: SocketBuffer<'static, 'static>)
-> RawSocket<'static, 'static> {
match RawSocket::new(IpVersion::Ipv4, IpProtocol::Unknown(63),
match RawSocket::new(IpVersion::Ipv4, IpProtocol::Unknown(IP_PROTO),
rx_buffer, tx_buffer) {
Socket::Raw(socket) => socket,
_ => unreachable!()
}
}

const IP_PROTO: u8 = 63;
const HEADER_REPR: IpRepr = IpRepr::Ipv4(Ipv4Repr {
src_addr: Ipv4Address([10, 0, 0, 1]),
dst_addr: Ipv4Address([10, 0, 0, 2]),
protocol: IpProtocol::Unknown(63),
protocol: IpProtocol::Unknown(IP_PROTO),
payload_len: 4
});
const PACKET_BYTES: [u8; 24] = [
@@ -332,10 +339,12 @@ mod test {
Ipv4Packet::new(&mut cksumd_packet).fill_checksum();

assert_eq!(socket.recv(), Err(Error::Exhausted));
assert!(socket.accepts(&HEADER_REPR));
assert_eq!(socket.process(&HEADER_REPR, &PACKET_PAYLOAD),
Ok(()));
assert!(socket.can_recv());

assert!(socket.accepts(&HEADER_REPR));
assert_eq!(socket.process(&HEADER_REPR, &PACKET_PAYLOAD),
Err(Error::Exhausted));
assert_eq!(socket.recv(), Ok(&cksumd_packet[..]));
@@ -346,6 +355,7 @@ mod test {
fn test_recv_truncated_slice() {
let mut socket = socket(buffer(1), buffer(0));

assert!(socket.accepts(&HEADER_REPR));
assert_eq!(socket.process(&HEADER_REPR, &PACKET_PAYLOAD),
Ok(()));

@@ -361,7 +371,19 @@ mod test {
let mut buffer = vec![0; 128];
buffer[..PACKET_BYTES.len()].copy_from_slice(&PACKET_BYTES[..]);

assert!(socket.accepts(&HEADER_REPR));
assert_eq!(socket.process(&HEADER_REPR, &buffer),
Err(Error::Truncated));
}

#[test]
fn test_doesnt_accept_wrong_proto() {
let socket = match RawSocket::new(IpVersion::Ipv4,
IpProtocol::Unknown(IP_PROTO+1),
buffer(1), buffer(1)) {
Socket::Raw(socket) => socket,
_ => unreachable!()
};
assert!(!socket.accepts(&HEADER_REPR));
}
}
114 changes: 99 additions & 15 deletions src/socket/tcp.rs
Original file line number Diff line number Diff line change
@@ -666,25 +666,31 @@ impl<'a> TcpSocket<'a> {
(ip_reply_repr, reply_repr)
}

pub(crate) fn process(&mut self, timestamp: u64, ip_repr: &IpRepr, repr: &TcpRepr) ->
Result<Option<(IpRepr, TcpRepr<'static>)>> {
if self.state == State::Closed { return Err(Error::Rejected) }
pub(crate) fn accepts(&self, ip_repr: &IpRepr, repr: &TcpRepr) -> bool {
if self.state == State::Closed { return false }

// If we're still listening for SYNs and the packet has an ACK, it cannot
// be destined to this socket, but another one may well listen on the same
// local endpoint.
if self.state == State::Listen && repr.ack_number.is_some() { return Err(Error::Rejected) }
if self.state == State::Listen && repr.ack_number.is_some() { return false }

// Reject packets with a wrong destination.
if self.local_endpoint.port != repr.dst_port { return Err(Error::Rejected) }
if self.local_endpoint.port != repr.dst_port { return false }
if !self.local_endpoint.addr.is_unspecified() &&
self.local_endpoint.addr != ip_repr.dst_addr() { return Err(Error::Rejected) }
self.local_endpoint.addr != ip_repr.dst_addr() { return false }

// Reject packets from a source to which we aren't connected.
if self.remote_endpoint.port != 0 &&
self.remote_endpoint.port != repr.src_port { return Err(Error::Rejected) }
self.remote_endpoint.port != repr.src_port { return false }
if !self.remote_endpoint.addr.is_unspecified() &&
self.remote_endpoint.addr != ip_repr.src_addr() { return Err(Error::Rejected) }
self.remote_endpoint.addr != ip_repr.src_addr() { return false }

true
}

pub(crate) fn process(&mut self, timestamp: u64, ip_repr: &IpRepr, repr: &TcpRepr) ->
Result<Option<(IpRepr, TcpRepr<'static>)>> {
debug_assert!(self.accepts(ip_repr, repr));

// Consider how much the sequence number space differs from the transmit buffer space.
let (sent_syn, sent_fin) = match self.state {
@@ -1241,20 +1247,29 @@ mod test {

const LOCAL_IP: IpAddress = IpAddress::Ipv4(Ipv4Address([10, 0, 0, 1]));
const REMOTE_IP: IpAddress = IpAddress::Ipv4(Ipv4Address([10, 0, 0, 2]));
const OTHER_IP: IpAddress = IpAddress::Ipv4(Ipv4Address([10, 0, 0, 3]));
const LOCAL_PORT: u16 = 80;
const REMOTE_PORT: u16 = 49500;
const LOCAL_END: IpEndpoint = IpEndpoint { addr: LOCAL_IP, port: LOCAL_PORT };
const REMOTE_END: IpEndpoint = IpEndpoint { addr: REMOTE_IP, port: REMOTE_PORT };
const LOCAL_SEQ: TcpSeqNumber = TcpSeqNumber(10000);
const REMOTE_SEQ: TcpSeqNumber = TcpSeqNumber(-10000);

const SEND_IP_TEMPL: IpRepr = IpRepr::Unspecified {
src_addr: LOCAL_IP, dst_addr: REMOTE_IP,
protocol: IpProtocol::Tcp, payload_len: 20
};
const SEND_TEMPL: TcpRepr<'static> = TcpRepr {
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,
payload: &[]
};
const RECV_IP_TEMPL: IpRepr = IpRepr::Unspecified {
src_addr: REMOTE_IP, dst_addr: LOCAL_IP,
protocol: IpProtocol::Tcp, payload_len: 20
};
const RECV_TEMPL: TcpRepr<'static> = TcpRepr {
src_port: LOCAL_PORT, dst_port: REMOTE_PORT,
control: TcpControl::None,
@@ -1265,13 +1280,15 @@ mod test {

fn send(socket: &mut TcpSocket, timestamp: u64, repr: &TcpRepr) ->
Result<Option<TcpRepr<'static>>> {
trace!("send: {}", repr);
let ip_repr = IpRepr::Unspecified {
src_addr: REMOTE_IP,
dst_addr: LOCAL_IP,
protocol: IpProtocol::Tcp,
payload_len: repr.buffer_len()
};
trace!("send: {}", repr);

assert!(socket.accepts(&ip_repr, repr));
match socket.process(timestamp, &ip_repr, repr) {
Ok(Some((_ip_repr, repr))) => {
trace!("recv: {}", repr);
@@ -1395,10 +1412,11 @@ mod test {
let mut s = socket();
assert_eq!(s.state, State::Closed);

send!(s, TcpRepr {
let tcp_repr = TcpRepr {
control: TcpControl::Syn,
..SEND_TEMPL
}, Err(Error::Rejected));
};
assert!(!s.accepts(&SEND_IP_TEMPL, &tcp_repr));
}

#[test]
@@ -1407,10 +1425,11 @@ mod test {
s.listen(LOCAL_END).unwrap();
s.close();

send!(s, TcpRepr {
let tcp_repr = TcpRepr {
control: TcpControl::Syn,
..SEND_TEMPL
}, Err(Error::Rejected));
};
assert!(!s.accepts(&SEND_IP_TEMPL, &tcp_repr));
}

#[test]
@@ -1465,12 +1484,15 @@ mod test {
#[test]
fn test_listen_syn_reject_ack() {
let mut s = socket_listen();
send!(s, TcpRepr {

let tcp_repr = TcpRepr {
control: TcpControl::Syn,
seq_number: REMOTE_SEQ,
ack_number: Some(LOCAL_SEQ),
..SEND_TEMPL
}, Err(Error::Rejected));
};
assert!(!s.accepts(&SEND_IP_TEMPL, &tcp_repr));

assert_eq!(s.state, State::Listen);
}

@@ -2930,4 +2952,66 @@ mod test {
..RECV_TEMPL
}]);
}

// =========================================================================================//
// Tests for packet filtering
// =========================================================================================//

#[test]
fn test_doesnt_accept_wrong_port() {
let mut s = socket_established();
s.rx_buffer = SocketBuffer::new(vec![0; 6]);

let tcp_repr = TcpRepr {
seq_number: REMOTE_SEQ + 1,
ack_number: Some(LOCAL_SEQ + 1),
dst_port: LOCAL_PORT + 1,
..SEND_TEMPL
};
assert!(!s.accepts(&SEND_IP_TEMPL, &tcp_repr));

let tcp_repr = TcpRepr {
seq_number: REMOTE_SEQ + 1,
ack_number: Some(LOCAL_SEQ + 1),
src_port: REMOTE_PORT + 1,
..SEND_TEMPL
};
assert!(!s.accepts(&SEND_IP_TEMPL, &tcp_repr));
}

#[test]
fn test_doesnt_accept_wrong_ip() {
let s = socket_established();

let tcp_repr = TcpRepr {
seq_number: REMOTE_SEQ + 1,
ack_number: Some(LOCAL_SEQ + 1),
payload: &b"abcdef"[..],
..SEND_TEMPL
};

let ip_repr = IpRepr::Unspecified {
src_addr: REMOTE_IP,
dst_addr: LOCAL_IP,
protocol: IpProtocol::Tcp,
payload_len: tcp_repr.buffer_len()
};
assert!(s.accepts(&ip_repr, &tcp_repr));

let ip_repr_wrong_src = IpRepr::Unspecified {
src_addr: OTHER_IP,
dst_addr: LOCAL_IP,
protocol: IpProtocol::Tcp,
payload_len: tcp_repr.buffer_len()
};
assert!(!s.accepts(&ip_repr_wrong_src, &tcp_repr));

let ip_repr_wrong_dst = IpRepr::Unspecified {
src_addr: REMOTE_IP,
dst_addr: OTHER_IP,
protocol: IpProtocol::Tcp,
payload_len: tcp_repr.buffer_len()
};
assert!(!s.accepts(&ip_repr_wrong_dst, &tcp_repr));
}
}
46 changes: 42 additions & 4 deletions src/socket/udp.rs
Original file line number Diff line number Diff line change
@@ -179,11 +179,16 @@ impl<'a, 'b> UdpSocket<'a, 'b> {
Ok((length, endpoint))
}

pub(crate) fn process(&mut self, ip_repr: &IpRepr, repr: &UdpRepr) -> Result<()> {
// Reject packets with a wrong destination.
if self.endpoint.port != repr.dst_port { return Err(Error::Rejected) }
pub(crate) fn accepts(&self, ip_repr: &IpRepr, repr: &UdpRepr) -> bool {
if self.endpoint.port != repr.dst_port { return false }
if !self.endpoint.addr.is_unspecified() &&
self.endpoint.addr != ip_repr.dst_addr() { return Err(Error::Rejected) }
self.endpoint.addr != ip_repr.dst_addr() { return false }

true
}

pub(crate) fn process(&mut self, ip_repr: &IpRepr, repr: &UdpRepr) -> Result<()> {
debug_assert!(self.accepts(ip_repr, repr));

let packet_buf = self.rx_buffer.enqueue_one_with(|buf| buf.resize(repr.payload.len()))?;
packet_buf.as_mut().copy_from_slice(repr.payload);
@@ -351,10 +356,12 @@ mod test {
assert!(!socket.can_recv());
assert_eq!(socket.recv(), Err(Error::Exhausted));

assert!(socket.accepts(&REMOTE_IP_REPR, &REMOTE_UDP_REPR));
assert_eq!(socket.process(&REMOTE_IP_REPR, &REMOTE_UDP_REPR),
Ok(()));
assert!(socket.can_recv());

assert!(socket.accepts(&REMOTE_IP_REPR, &REMOTE_UDP_REPR));
assert_eq!(socket.process(&REMOTE_IP_REPR, &REMOTE_UDP_REPR),
Err(Error::Exhausted));
assert_eq!(socket.recv(), Ok((&b"abcdef"[..], REMOTE_END)));
@@ -366,6 +373,7 @@ mod test {
let mut socket = socket(buffer(1), buffer(0));
assert_eq!(socket.bind(LOCAL_PORT), Ok(()));

assert!(socket.accepts(&REMOTE_IP_REPR, &REMOTE_UDP_REPR));
assert_eq!(socket.process(&REMOTE_IP_REPR, &REMOTE_UDP_REPR),
Ok(()));

@@ -380,7 +388,37 @@ mod test {
assert_eq!(socket.bind(LOCAL_PORT), Ok(()));

let udp_repr = UdpRepr { payload: &[0; 100][..], ..REMOTE_UDP_REPR };
assert!(socket.accepts(&REMOTE_IP_REPR, &udp_repr));
assert_eq!(socket.process(&REMOTE_IP_REPR, &udp_repr),
Err(Error::Truncated));
}

#[test]
fn test_doesnt_accept_wrong_port() {
let mut socket = socket(buffer(1), buffer(0));
assert_eq!(socket.bind(LOCAL_PORT), Ok(()));

let mut udp_repr = REMOTE_UDP_REPR;
assert!(socket.accepts(&REMOTE_IP_REPR, &udp_repr));
udp_repr.dst_port += 1;
assert!(!socket.accepts(&REMOTE_IP_REPR, &udp_repr));
}

#[test]
fn test_doesnt_accept_wrong_ip() {
let ip_repr = IpRepr::Ipv4(Ipv4Repr {
src_addr: Ipv4Address([10, 0, 0, 2]),
dst_addr: Ipv4Address([10, 0, 0, 10]),
protocol: IpProtocol::Udp,
payload_len: 8 + 6
});

let mut port_bound_socket = socket(buffer(1), buffer(0));
assert_eq!(port_bound_socket.bind(LOCAL_PORT), Ok(()));
assert!(port_bound_socket.accepts(&ip_repr, &REMOTE_UDP_REPR));

let mut ip_bound_socket = socket(buffer(1), buffer(0));
assert_eq!(ip_bound_socket.bind(LOCAL_END), Ok(()));
assert!(!ip_bound_socket.accepts(&ip_repr, &REMOTE_UDP_REPR));
}
}