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: a81dd55cca5e
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: a8d19a1da465
Choose a head ref
  • 2 commits
  • 4 files changed
  • 1 contributor

Commits on Dec 26, 2016

  1. Copy the full SHA
    6ea7832 View commit details
  2. Copy the full SHA
    a8d19a1 View commit details
Showing with 149 additions and 39 deletions.
  1. +4 −2 README.md
  2. +21 −8 examples/smoltcpserver.rs
  3. +119 −24 src/socket/tcp.rs
  4. +5 −5 src/socket/udp.rs
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -46,7 +46,8 @@ The UDP protocol is supported over IPv4.
The TCP protocol is supported over IPv4.

* TCP header checksum is supported.
* TCP options are **not** supported.
* TCP options are **not** supported. In particular, the MSS of the remote endpoint
is hardcoded at the default value, 536.
* Reassembly of out-of-order segments is **not** supported.

Installation
@@ -105,7 +106,8 @@ cargo run --example smoltcpserver -- tap0
It responds to:

* pings (`ping 192.168.69.1`),
* UDP packets on port 6969 (`socat stdio udp4-connect:192.168.69.1:6969 <<<"abcdefg"`).
* UDP packets on port 6969 (`socat stdio udp4-connect:192.168.69.1:6969 <<<"abcdefg"`),
* TCP packets on port 6969 (`socat stdio tcp4-connect:192.168.69.1:6969 <<<"abcdefg"`),

License
-------
29 changes: 21 additions & 8 deletions examples/smoltcpserver.rs
Original file line number Diff line number Diff line change
@@ -56,29 +56,42 @@ fn main() {
loop {
match iface.poll() {
Ok(()) => (),
Err(e) => debug!("error {}", e)
Err(e) => debug!("poll error: {}", e)
}

{
let udp_socket: &mut UdpSocket = iface.sockets()[0].as_socket();
let udp_client = match udp_socket.recv() {
let socket: &mut UdpSocket = iface.sockets()[0].as_socket();
let client = match socket.recv() {
Ok((endpoint, data)) => {
debug!("data {:?} from {}", data, endpoint);
debug!("udp recv data: {:?} from {}", data, endpoint);
Some(endpoint)
}
Err(Error::Exhausted) => {
None
}
Err(e) => {
debug!("error {}", e);
debug!("udp recv error: {}", e);
None
}
};
if let Some(endpoint) = udp_client {
udp_socket.send_slice(endpoint, "hihihi".as_bytes()).unwrap()
if let Some(endpoint) = client {
socket.send_slice(endpoint, b"yo dawg").unwrap()
}
}

let _tcp_socket: &mut TcpSocket = iface.sockets()[1].as_socket();
{
let socket: &mut TcpSocket = iface.sockets()[1].as_socket();
let data = {
let mut data = socket.recv(128).to_owned();
if data.len() > 0 {
debug!("tcp recv data: {:?}", &data[..]);
data = data.split(|&b| b == b'\n').next().unwrap().to_owned();
data.reverse();
data.extend(b"\n");
}
data
};
socket.send_slice(data.as_ref());
}
}
}
143 changes: 119 additions & 24 deletions src/socket/tcp.rs
Original file line number Diff line number Diff line change
@@ -90,6 +90,7 @@ impl<'a> SocketBuffer<'a> {
self.length -= size;
}

#[allow(dead_code)] // only used in tests
fn dequeue(&mut self, size: usize) -> &[u8] {
let (read_at, size) = self.clamp_reader(size);
self.read_at = (self.read_at + size) % self.storage.len();
@@ -243,6 +244,59 @@ impl<'a> TcpSocket<'a> {
self.set_state(State::Listen);
}

/// Enqueue a sequence of octets to be sent, and return a pointer to it.
///
/// This function may return a slice smaller than the requested size in case
/// there is not enough contiguous free space in the transmit buffer, down to
/// an empty slice.
pub fn send(&mut self, size: usize) -> &mut [u8] {
let buffer = self.tx_buffer.enqueue(size);
if buffer.len() > 0 {
net_trace!("tcp:{}:{}: buffer to send {} octets",
self.local_endpoint, self.remote_endpoint, buffer.len());
}
buffer
}

/// Enqueue a sequence of octets to be sent, and fill it from a slice.
///
/// This function returns the amount of bytes actually enqueued, which is limited
/// by the amount of free space in the transmit buffer; down to zero.
///
/// See also [send](#method.send).
pub fn send_slice(&mut self, data: &[u8]) -> usize {
let buffer = self.send(data.len());
buffer.copy_from_slice(data);
buffer.len()
}

/// Dequeue a sequence of received octets, and return a pointer to it.
///
/// This function may return a slice smaller than the requested size in case
/// there are not enough octets queued in the receive buffer, down to
/// an empty slice.
pub fn recv(&mut self, size: usize) -> &[u8] {
let buffer = self.rx_buffer.dequeue(size);
self.remote_seq_no += buffer.len() as i32;
if buffer.len() > 0 {
net_trace!("tcp:{}:{}: receive {} buffered octets",
self.local_endpoint, self.remote_endpoint, buffer.len());
}
buffer
}

/// Dequeue a sequence of received octets, and fill a slice from it.
///
/// This function returns the amount of bytes actually dequeued, which is limited
/// by the amount of free space in the transmit buffer; down to zero.
///
/// See also [recv](#method.recv).
pub fn recv_slice(&mut self, data: &mut [u8]) -> usize {
let buffer = self.recv(data.len());
data[..buffer.len()].copy_from_slice(buffer);
buffer.len()
}

/// See [Socket::collect](enum.Socket.html#method.collect).
pub fn collect(&mut self, ip_repr: &IpRepr, payload: &[u8]) -> Result<(), Error> {
if ip_repr.protocol() != IpProtocol::Tcp { return Err(Error::Rejected) }
@@ -422,54 +476,75 @@ impl<'a> TcpSocket<'a> {
/// See [Socket::dispatch](enum.Socket.html#method.dispatch).
pub fn dispatch<F, R>(&mut self, emit: &mut F) -> Result<R, Error>
where F: FnMut(&IpRepr, &IpPayload) -> Result<R, Error> {
let ip_repr = IpRepr::Unspecified {
src_addr: self.local_endpoint.addr,
dst_addr: self.remote_endpoint.addr,
protocol: IpProtocol::Tcp,
};
let mut repr = TcpRepr {
src_port: self.local_endpoint.port,
dst_port: self.remote_endpoint.port,
control: TcpControl::None,
seq_number: 0,
seq_number: self.local_seq_no,
ack_number: None,
window_len: self.rx_buffer.window() as u16,
payload: &[]
};

// FIXME: process
let ack_number = self.remote_seq_no + self.rx_buffer.len() as i32;

match self.state {
State::Closed |
State::Listen => {
return Err(Error::Exhausted)
}
State::Closed | State::Listen => return Err(Error::Exhausted),

State::SynReceived => {
if !self.retransmit.check() { return Err(Error::Exhausted) }

repr.control = TcpControl::Syn;
repr.seq_number = self.local_seq_no;
repr.ack_number = Some(self.remote_seq_no);
net_trace!("tcp:{}:{}: SYN|ACK sent",
repr.control = TcpControl::Syn;
net_trace!("tcp:{}:{}: sending SYN|ACK",
self.local_endpoint, self.remote_endpoint);
self.remote_last_ack = self.remote_seq_no;
}

State::Established => {
let ack_number = self.remote_seq_no + self.rx_buffer.len() as i32;
if self.remote_last_ack == ack_number { return Err(Error::Exhausted) }

repr.seq_number = self.local_seq_no;
repr.ack_number = Some(ack_number);
net_trace!("tcp:{}:{}: ACK sent",
self.local_endpoint, self.remote_endpoint);
self.remote_last_ack = ack_number;
if self.tx_buffer.len() > 0 && self.remote_win_len > 0 {
if !self.retransmit.check() { return Err(Error::Exhausted) }

// We can send something, so let's do that.
let mut size = self.remote_win_len;
// Clamp to MSS. Currently we only support the default MSS value.
if size > 536 { size = 536 }
// Extract data from the buffer. This may return less than what we want,
// in case it's not possible to extract a contiguous slice.
let data = self.tx_buffer.peek(size);

net_trace!("tcp:{}:{}: sending {} octets",
self.local_endpoint, self.remote_endpoint, data.len());
repr.payload = data;
} else if self.remote_last_ack != ack_number {
// We don't have anything to send, or can't because the remote end does not
// have any space to accept it, but we haven't yet acknowledged everything
// we have received. So, do it.
net_trace!("tcp:{}:{}: sending ACK",
self.local_endpoint, self.remote_endpoint);
} else {
// We don't have anything to send and we've already acknowledged everything.
return Err(Error::Exhausted)
}
}

_ => unreachable!()
}

let ip_repr = IpRepr::Unspecified {
src_addr: self.local_endpoint.addr,
dst_addr: self.remote_endpoint.addr,
protocol: IpProtocol::Tcp,
};
match self.state {
// We don't have anything to acknowledge yet.
State::Closed | State::Listen | State::SynSent => (),
// Acknowledge all data we have received, since it is all in order.
_ => {
self.remote_last_ack = ack_number;
repr.ack_number = Some(ack_number);
}
}

emit(&ip_repr, &repr)
}
}
@@ -749,11 +824,12 @@ mod test {
s.remote_endpoint = REMOTE_END;
s.local_seq_no = LOCAL_SEQ + 1;
s.remote_seq_no = REMOTE_SEQ + 1;
s.remote_win_len = 128;
s
}

#[test]
fn test_established_data() {
fn test_established_receive() {
let mut s = socket_established();
send!(s, [TcpRepr {
seq_number: REMOTE_SEQ + 1,
@@ -770,6 +846,25 @@ mod test {
assert_eq!(s.rx_buffer.dequeue(6), &b"abcdef"[..]);
}

#[test]
fn test_established_send() {
let mut s = socket_established();
s.tx_buffer.enqueue_slice(b"abcdef");
recv!(s, [TcpRepr {
seq_number: LOCAL_SEQ + 1,
ack_number: Some(REMOTE_SEQ + 1),
payload: &b"abcdef"[..],
..RECV_TEMPL
}]);
assert_eq!(s.tx_buffer.len(), 6);
send!(s, [TcpRepr {
seq_number: REMOTE_SEQ + 1,
ack_number: Some(LOCAL_SEQ + 1 + 6),
..SEND_TEMPL
}]);
assert_eq!(s.tx_buffer.len(), 0);
}

#[test]
fn test_established_no_ack() {
let mut s = socket_established();
10 changes: 5 additions & 5 deletions src/socket/udp.rs
Original file line number Diff line number Diff line change
@@ -131,12 +131,12 @@ impl<'a, 'b> UdpSocket<'a, 'b> {
let packet_buf = try!(self.tx_buffer.enqueue());
packet_buf.endpoint = endpoint;
packet_buf.size = size;
net_trace!("udp:{}:{}: send {} octets",
net_trace!("udp:{}:{}: buffer to send {} octets",
self.endpoint, packet_buf.endpoint, packet_buf.size);
Ok(&mut packet_buf.as_mut()[..size])
}

/// Enqueue a packete to be sent to a given remote endpoint, and fill it from a slice.
/// Enqueue a packet to be sent to a given remote endpoint, and fill it from a slice.
///
/// See also [send](#method.send).
pub fn send_slice(&mut self, endpoint: IpEndpoint, data: &[u8]) -> Result<(), Error> {
@@ -150,7 +150,7 @@ impl<'a, 'b> UdpSocket<'a, 'b> {
/// This function returns `Err(Error::Exhausted)` if the receive buffer is empty.
pub fn recv(&mut self) -> Result<(IpEndpoint, &[u8]), Error> {
let packet_buf = try!(self.rx_buffer.dequeue());
net_trace!("udp:{}:{}: recv {} octets",
net_trace!("udp:{}:{}: receive {} buffered octets",
self.endpoint, packet_buf.endpoint, packet_buf.size);
Ok((packet_buf.endpoint, &packet_buf.as_ref()[..packet_buf.size]))
}
@@ -183,7 +183,7 @@ impl<'a, 'b> UdpSocket<'a, 'b> {
packet_buf.endpoint = IpEndpoint { addr: ip_repr.src_addr(), port: repr.src_port };
packet_buf.size = repr.payload.len();
packet_buf.as_mut()[..repr.payload.len()].copy_from_slice(repr.payload);
net_trace!("udp:{}:{}: collect {} octets",
net_trace!("udp:{}:{}: receiving {} octets",
self.endpoint, packet_buf.endpoint, packet_buf.size);
Ok(())
}
@@ -192,7 +192,7 @@ impl<'a, 'b> UdpSocket<'a, 'b> {
pub fn dispatch<F, R>(&mut self, emit: &mut F) -> Result<R, Error>
where F: FnMut(&IpRepr, &IpPayload) -> Result<R, Error> {
let packet_buf = try!(self.tx_buffer.dequeue());
net_trace!("udp:{}:{}: dispatch {} octets",
net_trace!("udp:{}:{}: sending {} octets",
self.endpoint, packet_buf.endpoint, packet_buf.size);
let ip_repr = IpRepr::Unspecified {
src_addr: self.endpoint.addr,