Implement the TCP TIME-WAIT state.
This commit is contained in:
parent
94963faf12
commit
287affb447
|
@ -52,11 +52,12 @@ The TCP protocol is supported over IPv4.
|
||||||
|
|
||||||
* TCP header checksum is supported.
|
* TCP header checksum is supported.
|
||||||
* Multiple packets will be transmitted without waiting for an acknowledgement.
|
* Multiple packets will be transmitted without waiting for an acknowledgement.
|
||||||
* TCP urgent pointer is **not** supported; any urgent octets will be received alongside data.
|
* TCP urgent pointer is **not** supported; any urgent octets will be received alongside
|
||||||
|
data octets.
|
||||||
* Reassembly of out-of-order segments is **not** supported.
|
* Reassembly of out-of-order segments is **not** supported.
|
||||||
* TCP options are **not** supported, in particular:
|
* TCP options are **not** supported, in particular:
|
||||||
* Maximum segment size is hardcoded at the default value, 536.
|
* Maximum segment size is hardcoded at the default value, 536.
|
||||||
* Window scaling is **not** supported.
|
* Window scaling is **not** supported, and the maximum buffer size is 65536.
|
||||||
* Keepalive is **not** supported.
|
* Keepalive is **not** supported.
|
||||||
|
|
||||||
Installation
|
Installation
|
||||||
|
@ -143,7 +144,9 @@ It responds to:
|
||||||
* pings (`ping 192.168.69.1`);
|
* 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"`),
|
||||||
where it will respond "yo dawg" to any incoming packet;
|
where it will respond "yo dawg" to any incoming packet;
|
||||||
* TCP packets on port 6969 (`socat stdio tcp4-connect:192.168.69.1:6969 <<<"abcdefg"`),
|
* TCP packets on port 6969 (`socat stdio tcp4-connect:192.168.69.1:6969`),
|
||||||
|
where it will respond "yo dawg" to any incoming connection and immediately close it;
|
||||||
|
* TCP packets on port 6970 (`socat stdio tcp4-connect:192.168.69.1:6970 <<<"abcdefg"`),
|
||||||
where it will respond with reversed chunks of the input indefinitely.
|
where it will respond with reversed chunks of the input indefinitely.
|
||||||
|
|
||||||
The buffers are only 64 bytes long, for convenience of testing resource exhaustion conditions.
|
The buffers are only 64 bytes long, for convenience of testing resource exhaustion conditions.
|
||||||
|
|
|
@ -53,23 +53,28 @@ fn main() {
|
||||||
let udp_tx_buffer = UdpSocketBuffer::new(vec![UdpPacketBuffer::new(vec![0; 128])]);
|
let udp_tx_buffer = UdpSocketBuffer::new(vec![UdpPacketBuffer::new(vec![0; 128])]);
|
||||||
let udp_socket = UdpSocket::new(endpoint, udp_rx_buffer, udp_tx_buffer);
|
let udp_socket = UdpSocket::new(endpoint, udp_rx_buffer, udp_tx_buffer);
|
||||||
|
|
||||||
let tcp_rx_buffer = TcpSocketBuffer::new(vec![0; 64]);
|
let tcp1_rx_buffer = TcpSocketBuffer::new(vec![0; 64]);
|
||||||
let tcp_tx_buffer = TcpSocketBuffer::new(vec![0; 128]);
|
let tcp1_tx_buffer = TcpSocketBuffer::new(vec![0; 128]);
|
||||||
let tcp_socket = TcpSocket::new(tcp_rx_buffer, tcp_tx_buffer);
|
let tcp1_socket = TcpSocket::new(tcp1_rx_buffer, tcp1_tx_buffer);
|
||||||
|
|
||||||
|
let tcp2_rx_buffer = TcpSocketBuffer::new(vec![0; 64]);
|
||||||
|
let tcp2_tx_buffer = TcpSocketBuffer::new(vec![0; 128]);
|
||||||
|
let tcp2_socket = TcpSocket::new(tcp2_rx_buffer, tcp2_tx_buffer);
|
||||||
|
|
||||||
let hardware_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]);
|
let hardware_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]);
|
||||||
let protocol_addrs = [IpAddress::v4(192, 168, 69, 1)];
|
let protocol_addrs = [IpAddress::v4(192, 168, 69, 1)];
|
||||||
let sockets = vec![udp_socket, tcp_socket];
|
let sockets = vec![udp_socket, tcp1_socket, tcp2_socket];
|
||||||
let mut iface = EthernetInterface::new(device, arp_cache,
|
let mut iface = EthernetInterface::new(device, arp_cache,
|
||||||
hardware_addr, protocol_addrs, sockets);
|
hardware_addr, protocol_addrs, sockets);
|
||||||
|
|
||||||
let mut tcp_6969_connected = false;
|
let mut tcp_6969_connected = false;
|
||||||
loop {
|
loop {
|
||||||
|
// udp:6969: respond "yo dawg"
|
||||||
{
|
{
|
||||||
let socket: &mut UdpSocket = iface.sockets()[0].as_socket();
|
let socket: &mut UdpSocket = iface.sockets()[0].as_socket();
|
||||||
let client = match socket.recv() {
|
let client = match socket.recv() {
|
||||||
Ok((endpoint, data)) => {
|
Ok((endpoint, data)) => {
|
||||||
debug!("udp recv data: {:?} from {}",
|
debug!("udp:6969 recv data: {:?} from {}",
|
||||||
str::from_utf8(data.as_ref()).unwrap(), endpoint);
|
str::from_utf8(data.as_ref()).unwrap(), endpoint);
|
||||||
Some(endpoint)
|
Some(endpoint)
|
||||||
}
|
}
|
||||||
|
@ -77,28 +82,46 @@ fn main() {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
debug!("udp recv error: {}", e);
|
debug!("udp:6969 recv error: {}", e);
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if let Some(endpoint) = client {
|
if let Some(endpoint) = client {
|
||||||
let data = b"yo dawg";
|
let data = b"yo dawg\n";
|
||||||
debug!("udp send data: {:?}",
|
debug!("udp:6969 send data: {:?}",
|
||||||
str::from_utf8(data.as_ref()).unwrap());
|
str::from_utf8(data.as_ref()).unwrap());
|
||||||
socket.send_slice(endpoint, data).unwrap()
|
socket.send_slice(endpoint, data).unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// tcp:6969: respond "yo dawg"
|
||||||
{
|
{
|
||||||
let socket: &mut TcpSocket = iface.sockets()[1].as_socket();
|
let socket: &mut TcpSocket = iface.sockets()[1].as_socket();
|
||||||
if !socket.is_open() {
|
if !socket.is_open() {
|
||||||
socket.listen(endpoint).unwrap()
|
socket.listen(6969).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
if socket.can_send() {
|
||||||
|
let data = b"yo dawg\n";
|
||||||
|
debug!("tcp:6969 send data: {:?}",
|
||||||
|
str::from_utf8(data.as_ref()).unwrap());
|
||||||
|
socket.send_slice(data).unwrap();
|
||||||
|
debug!("tcp:6969 close");
|
||||||
|
socket.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// tcp:6970: echo with reverse
|
||||||
|
{
|
||||||
|
let socket: &mut TcpSocket = iface.sockets()[2].as_socket();
|
||||||
|
if !socket.is_open() {
|
||||||
|
socket.listen(6970).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
if socket.is_connected() && !tcp_6969_connected {
|
if socket.is_connected() && !tcp_6969_connected {
|
||||||
debug!("tcp connected");
|
debug!("tcp:6970 connected");
|
||||||
} else if !socket.is_connected() && tcp_6969_connected {
|
} else if !socket.is_connected() && tcp_6969_connected {
|
||||||
debug!("tcp disconnected");
|
debug!("tcp:6970 disconnected");
|
||||||
}
|
}
|
||||||
tcp_6969_connected = socket.is_connected();
|
tcp_6969_connected = socket.is_connected();
|
||||||
|
|
||||||
|
@ -106,7 +129,7 @@ fn main() {
|
||||||
let data = {
|
let data = {
|
||||||
let mut data = socket.recv(128).unwrap().to_owned();
|
let mut data = socket.recv(128).unwrap().to_owned();
|
||||||
if data.len() > 0 {
|
if data.len() > 0 {
|
||||||
debug!("tcp recv data: {:?}",
|
debug!("tcp:6970 recv data: {:?}",
|
||||||
str::from_utf8(data.as_ref()).unwrap_or("(invalid utf8)"));
|
str::from_utf8(data.as_ref()).unwrap_or("(invalid utf8)"));
|
||||||
data = data.split(|&b| b == b'\n').collect::<Vec<_>>().concat();
|
data = data.split(|&b| b == b'\n').collect::<Vec<_>>().concat();
|
||||||
data.reverse();
|
data.reverse();
|
||||||
|
@ -115,13 +138,13 @@ fn main() {
|
||||||
data
|
data
|
||||||
};
|
};
|
||||||
if socket.can_send() && data.len() > 0 {
|
if socket.can_send() && data.len() > 0 {
|
||||||
debug!("tcp send data: {:?}",
|
debug!("tcp:6970 send data: {:?}",
|
||||||
str::from_utf8(data.as_ref()).unwrap_or("(invalid utf8)"));
|
str::from_utf8(data.as_ref()).unwrap_or("(invalid utf8)"));
|
||||||
socket.send_slice(&data[..]).unwrap();
|
socket.send_slice(&data[..]).unwrap();
|
||||||
}
|
}
|
||||||
} else if socket.can_send() {
|
} else if socket.can_send() {
|
||||||
|
debug!("tcp:6970 close");
|
||||||
socket.close();
|
socket.close();
|
||||||
debug!("tcp closed")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -249,9 +249,10 @@ impl<'a> TcpSocket<'a> {
|
||||||
/// Start listening on the given endpoint.
|
/// Start listening on the given endpoint.
|
||||||
///
|
///
|
||||||
/// This function returns an error if the socket was open; see [is_open](#method.is_open).
|
/// This function returns an error if the socket was open; see [is_open](#method.is_open).
|
||||||
pub fn listen(&mut self, endpoint: IpEndpoint) -> Result<(), ()> {
|
pub fn listen<T: Into<IpEndpoint>>(&mut self, endpoint: T) -> Result<(), ()> {
|
||||||
if self.is_open() { return Err(()) }
|
if self.is_open() { return Err(()) }
|
||||||
|
|
||||||
|
let endpoint = endpoint.into();
|
||||||
self.listen_address = endpoint.addr;
|
self.listen_address = endpoint.addr;
|
||||||
self.local_endpoint = endpoint;
|
self.local_endpoint = endpoint;
|
||||||
self.remote_endpoint = IpEndpoint::default();
|
self.remote_endpoint = IpEndpoint::default();
|
||||||
|
@ -507,8 +508,8 @@ impl<'a> TcpSocket<'a> {
|
||||||
let control_len = match state {
|
let control_len = match state {
|
||||||
// In SYN-SENT or SYN-RECEIVED, we've just sent a SYN.
|
// In SYN-SENT or SYN-RECEIVED, we've just sent a SYN.
|
||||||
State::SynSent | State::SynReceived => 1,
|
State::SynSent | State::SynReceived => 1,
|
||||||
// In FIN-WAIT-1 or LAST-ACK, we've just sent a FIN.
|
// In FIN-WAIT-1, LAST-ACK, or CLOSING, we've just sent a FIN.
|
||||||
State::FinWait1 | State::LastAck => 1,
|
State::FinWait1 | State::LastAck | State::Closing => 1,
|
||||||
// In all other states we've already got acknowledgemetns for
|
// In all other states we've already got acknowledgemetns for
|
||||||
// all of the control flags we sent.
|
// all of the control flags we sent.
|
||||||
_ => 0
|
_ => 0
|
||||||
|
@ -625,9 +626,9 @@ impl<'a> TcpSocket<'a> {
|
||||||
|
|
||||||
// ACK packets in CLOSING state change it to TIME-WAIT.
|
// ACK packets in CLOSING state change it to TIME-WAIT.
|
||||||
(State::Closing, TcpRepr { control: TcpControl::None, .. }) => {
|
(State::Closing, TcpRepr { control: TcpControl::None, .. }) => {
|
||||||
// Clear the remote endpoint, or we'll send an ACK there.
|
self.local_seq_no += 1;
|
||||||
self.remote_endpoint = IpEndpoint::default();
|
|
||||||
self.set_state(State::TimeWait);
|
self.set_state(State::TimeWait);
|
||||||
|
self.retransmit.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ACK packets in CLOSE-WAIT state do nothing.
|
// ACK packets in CLOSE-WAIT state do nothing.
|
||||||
|
@ -696,8 +697,8 @@ impl<'a> TcpSocket<'a> {
|
||||||
|
|
||||||
let mut should_send = false;
|
let mut should_send = false;
|
||||||
match self.state {
|
match self.state {
|
||||||
// We never transmit anything in the CLOSED, LISTEN, TIME-WAIT or FIN-WAIT-2 states.
|
// We never transmit anything in the CLOSED, LISTEN, or FIN-WAIT-2 states.
|
||||||
State::Closed | State::Listen | State::TimeWait | State::FinWait2 => {
|
State::Closed | State::Listen | State::FinWait2 => {
|
||||||
return Err(Error::Exhausted)
|
return Err(Error::Exhausted)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -723,11 +724,11 @@ impl<'a> TcpSocket<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// We transmit data in the ESTABLISHED state,
|
// We transmit data in the ESTABLISHED state,
|
||||||
// ACK in CLOSE-WAIT and CLOSING states,
|
// ACK in CLOSE-WAIT, CLOSING, and TIME-WAIT states,
|
||||||
// FIN in FIN-WAIT-1 and LAST-ACK states.
|
// FIN in FIN-WAIT-1 and LAST-ACK states.
|
||||||
State::Established |
|
State::Established |
|
||||||
State::CloseWait | State::LastAck |
|
State::CloseWait | State::Closing | State::TimeWait |
|
||||||
State::FinWait1 | State::Closing => {
|
State::FinWait1 | State::LastAck => {
|
||||||
// See if we should send data to the remote end because:
|
// See if we should send data to the remote end because:
|
||||||
let mut may_send = false;
|
let mut may_send = false;
|
||||||
// 1. the retransmit timer has expired or was reset, or...
|
// 1. the retransmit timer has expired or was reset, or...
|
||||||
|
@ -1375,7 +1376,6 @@ mod test {
|
||||||
..SEND_TEMPL
|
..SEND_TEMPL
|
||||||
}]);
|
}]);
|
||||||
assert_eq!(s.state, State::TimeWait);
|
assert_eq!(s.state, State::TimeWait);
|
||||||
assert!(!s.remote_endpoint.is_unspecified());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1391,7 +1391,7 @@ mod test {
|
||||||
fn socket_closing() -> TcpSocket<'static> {
|
fn socket_closing() -> TcpSocket<'static> {
|
||||||
let mut s = socket_fin_wait_1();
|
let mut s = socket_fin_wait_1();
|
||||||
s.state = State::Closing;
|
s.state = State::Closing;
|
||||||
s.local_seq_no = LOCAL_SEQ + 1 + 1;
|
s.local_seq_no = LOCAL_SEQ + 1;
|
||||||
s.remote_seq_no = REMOTE_SEQ + 1 + 1;
|
s.remote_seq_no = REMOTE_SEQ + 1 + 1;
|
||||||
s
|
s
|
||||||
}
|
}
|
||||||
|
@ -1400,7 +1400,7 @@ mod test {
|
||||||
fn test_closing_ack_fin() {
|
fn test_closing_ack_fin() {
|
||||||
let mut s = socket_closing();
|
let mut s = socket_closing();
|
||||||
recv!(s, [TcpRepr {
|
recv!(s, [TcpRepr {
|
||||||
seq_number: LOCAL_SEQ + 1 + 1,
|
seq_number: LOCAL_SEQ + 1,
|
||||||
ack_number: Some(REMOTE_SEQ + 1 + 1),
|
ack_number: Some(REMOTE_SEQ + 1 + 1),
|
||||||
..RECV_TEMPL
|
..RECV_TEMPL
|
||||||
}]);
|
}]);
|
||||||
|
@ -1410,7 +1410,6 @@ mod test {
|
||||||
..SEND_TEMPL
|
..SEND_TEMPL
|
||||||
}]);
|
}]);
|
||||||
assert_eq!(s.state, State::TimeWait);
|
assert_eq!(s.state, State::TimeWait);
|
||||||
assert!(s.remote_endpoint.is_unspecified());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1423,17 +1422,35 @@ mod test {
|
||||||
// =========================================================================================//
|
// =========================================================================================//
|
||||||
// Tests for the TIME-WAIT state.
|
// Tests for the TIME-WAIT state.
|
||||||
// =========================================================================================//
|
// =========================================================================================//
|
||||||
fn socket_time_wait() -> TcpSocket<'static> {
|
fn socket_time_wait(from_closing: bool) -> TcpSocket<'static> {
|
||||||
let mut s = socket_fin_wait_2();
|
let mut s = socket_fin_wait_2();
|
||||||
s.state = State::TimeWait;
|
s.state = State::TimeWait;
|
||||||
s.remote_seq_no = REMOTE_SEQ + 1 + 1;
|
s.remote_seq_no = REMOTE_SEQ + 1 + 1;
|
||||||
s.remote_last_ack = REMOTE_SEQ + 1 + 1;
|
if from_closing {
|
||||||
|
s.remote_last_ack = REMOTE_SEQ + 1 + 1;
|
||||||
|
}
|
||||||
s
|
s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_time_wait_from_fin_wait_2_ack() {
|
||||||
|
let mut s = socket_time_wait(false);
|
||||||
|
recv!(s, [TcpRepr {
|
||||||
|
seq_number: LOCAL_SEQ + 1 + 1,
|
||||||
|
ack_number: Some(REMOTE_SEQ + 1 + 1),
|
||||||
|
..RECV_TEMPL
|
||||||
|
}]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_time_wait_from_closing_no_ack() {
|
||||||
|
let mut s = socket_time_wait(true);
|
||||||
|
recv!(s, []);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_time_wait_close() {
|
fn test_time_wait_close() {
|
||||||
let mut s = socket_time_wait();
|
let mut s = socket_time_wait(false);
|
||||||
s.close();
|
s.close();
|
||||||
assert_eq!(s.state, State::TimeWait);
|
assert_eq!(s.state, State::TimeWait);
|
||||||
}
|
}
|
||||||
|
@ -1573,5 +1590,70 @@ mod test {
|
||||||
ack_number: Some(LOCAL_SEQ + 1 + 1),
|
ack_number: Some(LOCAL_SEQ + 1 + 1),
|
||||||
..SEND_TEMPL
|
..SEND_TEMPL
|
||||||
}]);
|
}]);
|
||||||
|
assert_eq!(s.state, State::Closed);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_local_close() {
|
||||||
|
let mut s = socket_established();
|
||||||
|
s.close();
|
||||||
|
assert_eq!(s.state, State::FinWait1);
|
||||||
|
recv!(s, [TcpRepr {
|
||||||
|
control: TcpControl::Fin,
|
||||||
|
seq_number: LOCAL_SEQ + 1,
|
||||||
|
ack_number: Some(REMOTE_SEQ + 1),
|
||||||
|
..RECV_TEMPL
|
||||||
|
}]);
|
||||||
|
send!(s, [TcpRepr {
|
||||||
|
seq_number: REMOTE_SEQ + 1,
|
||||||
|
ack_number: Some(LOCAL_SEQ + 1 + 1),
|
||||||
|
..SEND_TEMPL
|
||||||
|
}]);
|
||||||
|
assert_eq!(s.state, State::FinWait2);
|
||||||
|
send!(s, [TcpRepr {
|
||||||
|
control: TcpControl::Fin,
|
||||||
|
seq_number: REMOTE_SEQ + 1,
|
||||||
|
ack_number: Some(LOCAL_SEQ + 1 + 1),
|
||||||
|
..SEND_TEMPL
|
||||||
|
}]);
|
||||||
|
assert_eq!(s.state, State::TimeWait);
|
||||||
|
recv!(s, [TcpRepr {
|
||||||
|
seq_number: LOCAL_SEQ + 1 + 1,
|
||||||
|
ack_number: Some(REMOTE_SEQ + 1 + 1),
|
||||||
|
..RECV_TEMPL
|
||||||
|
}]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_simultaneous_close() {
|
||||||
|
let mut s = socket_established();
|
||||||
|
s.close();
|
||||||
|
assert_eq!(s.state, State::FinWait1);
|
||||||
|
recv!(s, [TcpRepr { // this is logically located...
|
||||||
|
control: TcpControl::Fin,
|
||||||
|
seq_number: LOCAL_SEQ + 1,
|
||||||
|
ack_number: Some(REMOTE_SEQ + 1),
|
||||||
|
..RECV_TEMPL
|
||||||
|
}]);
|
||||||
|
send!(s, [TcpRepr {
|
||||||
|
control: TcpControl::Fin,
|
||||||
|
seq_number: REMOTE_SEQ + 1,
|
||||||
|
ack_number: Some(LOCAL_SEQ + 1),
|
||||||
|
..SEND_TEMPL
|
||||||
|
}]);
|
||||||
|
assert_eq!(s.state, State::Closing);
|
||||||
|
recv!(s, [TcpRepr {
|
||||||
|
seq_number: LOCAL_SEQ + 1,
|
||||||
|
ack_number: Some(REMOTE_SEQ + 1 + 1),
|
||||||
|
..RECV_TEMPL
|
||||||
|
}]);
|
||||||
|
// ... at this point
|
||||||
|
send!(s, [TcpRepr {
|
||||||
|
seq_number: REMOTE_SEQ + 1 + 1,
|
||||||
|
ack_number: Some(LOCAL_SEQ + 1 + 1),
|
||||||
|
..SEND_TEMPL
|
||||||
|
}]);
|
||||||
|
assert_eq!(s.state, State::TimeWait);
|
||||||
|
recv!(s, []);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,6 +78,8 @@ impl fmt::Display for Address {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An internet endpoint address.
|
/// An internet endpoint address.
|
||||||
|
///
|
||||||
|
/// An endpoint can be constructed from a port, in which case the address is unspecified.
|
||||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default)]
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default)]
|
||||||
pub struct Endpoint {
|
pub struct Endpoint {
|
||||||
pub addr: Address,
|
pub addr: Address,
|
||||||
|
@ -104,6 +106,12 @@ impl fmt::Display for Endpoint {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<u16> for Endpoint {
|
||||||
|
fn from(port: u16) -> Endpoint {
|
||||||
|
Endpoint { addr: Address::Unspecified, port: port }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// An IP packet representation.
|
/// An IP packet representation.
|
||||||
///
|
///
|
||||||
/// This enum abstracts the various versions of IP packets. It either contains a concrete
|
/// This enum abstracts the various versions of IP packets. It either contains a concrete
|
||||||
|
|
Loading…
Reference in New Issue