Implement set_ttl for Tcp and Udp sockets

- Add the ttl member to the IpRepr
 - Add the ttl member along with setters and getters to the tcp and udp
   socket types
 - Add unit tests for the new set_ttl parameter
 - Update usage of IpRepr to include the ttl value
v0.7.x
Dan Robertson 2017-10-15 00:05:55 +00:00 committed by whitequark
parent 4f689be81b
commit eef65d2676
10 changed files with 181 additions and 30 deletions

View File

@ -31,7 +31,6 @@ The only supported medium is Ethernet.
The only supported internetworking protocol is IPv4.
* IPv4 header checksum is generated and validated.
* IPv4 time-to-live value is fixed at 64.
* IPv4 fragmentation is **not** supported.
* IPv4 options are **not** supported and are silently ignored.
* IPv4 routes or default gateways are **not** supported.

View File

@ -94,6 +94,7 @@ fn main() {
dst_addr: remote_addr,
protocol: IpProtocol::Icmp,
payload_len: icmp_repr.buffer_len(),
ttl: 64
};
let raw_payload = socket

View File

@ -378,7 +378,8 @@ impl<'a, 'b, 'c, DeviceT: Device + 'a> Interface<'a, 'b, 'c, DeviceT> {
src_addr: ipv4_repr.dst_addr,
dst_addr: ipv4_repr.src_addr,
protocol: IpProtocol::Icmp,
payload_len: icmp_reply_repr.buffer_len()
payload_len: icmp_reply_repr.buffer_len(),
ttl: 64,
};
Ok(Packet::Icmpv4(ipv4_reply_repr, icmp_reply_repr))
}
@ -403,7 +404,8 @@ impl<'a, 'b, 'c, DeviceT: Device + 'a> Interface<'a, 'b, 'c, DeviceT> {
src_addr: ipv4_repr.dst_addr,
dst_addr: ipv4_repr.src_addr,
protocol: IpProtocol::Icmp,
payload_len: icmp_reply_repr.buffer_len()
payload_len: icmp_reply_repr.buffer_len(),
ttl: 64
};
Ok(Packet::Icmpv4(ipv4_reply_repr, icmp_reply_repr))
}
@ -448,7 +450,8 @@ impl<'a, 'b, 'c, DeviceT: Device + 'a> Interface<'a, 'b, 'c, DeviceT> {
src_addr: ipv4_repr.dst_addr,
dst_addr: ipv4_repr.src_addr,
protocol: IpProtocol::Icmp,
payload_len: icmpv4_reply_repr.buffer_len()
payload_len: icmpv4_reply_repr.buffer_len(),
ttl: 64,
};
Ok(Packet::Icmpv4(ipv4_reply_repr, icmpv4_reply_repr))
},

View File

@ -270,7 +270,8 @@ mod test {
src_addr: Ipv4Address([10, 0, 0, 1]),
dst_addr: Ipv4Address([10, 0, 0, 2]),
protocol: IpProtocol::Unknown(IP_PROTO),
payload_len: 4
payload_len: 4,
ttl: 64
});
const PACKET_BYTES: [u8; 24] = [
0x45, 0x00, 0x00, 0x18,

View File

@ -179,6 +179,8 @@ pub struct TcpSocket<'a> {
timeout: Option<u64>,
/// Interval at which keep-alive packets will be sent.
keep_alive: Option<u64>,
/// The time-to-live (IPv4) or hop limit (IPv6) value used in outgoing packets.
ttl: Option<u8>,
/// Address passed to listen(). Listen address is set when listen() is called and
/// used every time the socket is reset back to the LISTEN state.
listen_address: IpAddress,
@ -236,6 +238,7 @@ impl<'a> TcpSocket<'a> {
rx_buffer: rx_buffer,
timeout: None,
keep_alive: None,
ttl: None,
listen_address: IpAddress::default(),
local_endpoint: IpEndpoint::default(),
remote_endpoint: IpEndpoint::default(),
@ -311,6 +314,33 @@ impl<'a> TcpSocket<'a> {
}
}
/// Return the time-to-live (IPv4) or hop limit (IPv6) value used in outgoing packets.
///
/// See also the [set_ttl](#method.set_ttl) method
pub fn ttl(&self) -> Option<u8> {
self.ttl
}
/// Set the time-to-live (IPv4) or hop limit (IPv6) value used in outgoing packets.
///
/// A socket without an explicitly set TTL value uses the default [IANA recommended]
/// value (`64`).
///
/// # Panics
///
/// This function panics if a TTL value of `0` is given. See [RFC 1122 § 3.2.1.7].
///
/// [IANA recommended]: https://www.iana.org/assignments/ip-parameters/ip-parameters.xhtml
/// [RFC 1122 § 3.2.1.7]: https://tools.ietf.org/html/rfc1122#section-3.2.1.7
pub fn set_ttl(&mut self, ttl: Option<u8>) {
// A host MUST NOT send a datagram with a Time-to-Live (TTL)
// value of 0
match ttl {
Some(0) => { panic!("A TTL value of 0 is invalid for a sent packet"); },
catchall => self.ttl = catchall,
}
}
/// Return the local endpoint.
#[inline]
pub fn local_endpoint(&self) -> IpEndpoint {
@ -337,6 +367,7 @@ impl<'a> TcpSocket<'a> {
self.rx_buffer.clear();
self.keep_alive = None;
self.timeout = None;
self.ttl = None;
self.listen_address = IpAddress::default();
self.local_endpoint = IpEndpoint::default();
self.remote_endpoint = IpEndpoint::default();
@ -733,7 +764,8 @@ impl<'a> TcpSocket<'a> {
src_addr: ip_repr.dst_addr(),
dst_addr: ip_repr.src_addr(),
protocol: IpProtocol::Tcp,
payload_len: reply_repr.buffer_len()
payload_len: reply_repr.buffer_len(),
ttl: 64
};
(ip_reply_repr, reply_repr)
}
@ -1239,6 +1271,7 @@ impl<'a> TcpSocket<'a> {
src_addr: self.local_endpoint.addr,
dst_addr: self.remote_endpoint.addr,
protocol: IpProtocol::Tcp,
ttl: self.ttl.unwrap_or(64),
payload_len: 0
}.lower(&[])?;
@ -1447,7 +1480,8 @@ impl<'a> fmt::Write for TcpSocket<'a> {
#[cfg(test)]
mod test {
use wire::{IpAddress, Ipv4Address, IpCidr};
use wire::{IpAddress, IpRepr};
use wire::{Ipv4Address, IpCidr, Ipv4Repr};
use super::*;
#[test]
@ -1479,7 +1513,8 @@ mod test {
const SEND_IP_TEMPL: IpRepr = IpRepr::Unspecified {
src_addr: LOCAL_IP, dst_addr: REMOTE_IP,
protocol: IpProtocol::Tcp, payload_len: 20
protocol: IpProtocol::Tcp, payload_len: 20,
ttl: 64
};
const SEND_TEMPL: TcpRepr<'static> = TcpRepr {
src_port: REMOTE_PORT, dst_port: LOCAL_PORT,
@ -1490,7 +1525,8 @@ mod test {
};
const _RECV_IP_TEMPL: IpRepr = IpRepr::Unspecified {
src_addr: REMOTE_IP, dst_addr: LOCAL_IP,
protocol: IpProtocol::Tcp, payload_len: 20
protocol: IpProtocol::Tcp, payload_len: 20,
ttl: 64
};
const RECV_TEMPL: TcpRepr<'static> = TcpRepr {
src_port: LOCAL_PORT, dst_port: REMOTE_PORT,
@ -1506,7 +1542,8 @@ mod test {
src_addr: REMOTE_IP,
dst_addr: LOCAL_IP,
protocol: IpProtocol::Tcp,
payload_len: repr.buffer_len()
payload_len: repr.buffer_len(),
ttl: 64
};
trace!("send: {}", repr);
@ -3470,7 +3507,8 @@ mod test {
src_addr: REMOTE_IP,
dst_addr: LOCAL_IP,
protocol: IpProtocol::Tcp,
payload_len: tcp_repr.buffer_len()
payload_len: tcp_repr.buffer_len(),
ttl: 64
};
assert!(s.accepts(&ip_repr, &tcp_repr));
@ -3478,7 +3516,8 @@ mod test {
src_addr: OTHER_IP,
dst_addr: LOCAL_IP,
protocol: IpProtocol::Tcp,
payload_len: tcp_repr.buffer_len()
payload_len: tcp_repr.buffer_len(),
ttl: 64
};
assert!(!s.accepts(&ip_repr_wrong_src, &tcp_repr));
@ -3486,8 +3525,28 @@ mod test {
src_addr: REMOTE_IP,
dst_addr: OTHER_IP,
protocol: IpProtocol::Tcp,
payload_len: tcp_repr.buffer_len()
payload_len: tcp_repr.buffer_len(),
ttl: 64
};
assert!(!s.accepts(&ip_repr_wrong_dst, &tcp_repr));
}
#[test]
fn test_set_ttl() {
let mut s = socket_syn_received();
let mut caps = DeviceCapabilities::default();
caps.max_transmission_unit = 1520;
s.set_ttl(Some(0x2a));
assert_eq!(s.dispatch(0, &caps, |(ip_repr, _)| {
assert_eq!(ip_repr, IpRepr::Ipv4(Ipv4Repr {
src_addr: Ipv4Address([10, 0, 0, 1]),
dst_addr: Ipv4Address([10, 0, 0, 2]),
protocol: IpProtocol::Tcp,
payload_len: 24,
ttl: 0x2a,
}));
Ok(())
}), Ok(()));
}
}

View File

@ -63,6 +63,8 @@ pub struct UdpSocket<'a, 'b: 'a> {
endpoint: IpEndpoint,
rx_buffer: SocketBuffer<'a, 'b>,
tx_buffer: SocketBuffer<'a, 'b>,
/// The time-to-live (IPv4) or hop limit (IPv6) value used in outgoing packets.
ttl: Option<u8>
}
impl<'a, 'b> UdpSocket<'a, 'b> {
@ -74,6 +76,7 @@ impl<'a, 'b> UdpSocket<'a, 'b> {
endpoint: IpEndpoint::default(),
rx_buffer: rx_buffer,
tx_buffer: tx_buffer,
ttl: None
})
}
@ -94,6 +97,33 @@ impl<'a, 'b> UdpSocket<'a, 'b> {
self.endpoint
}
/// Return the time-to-live (IPv4) or hop limit (IPv6) value used in outgoing packets.
///
/// See also the [set_ttl](#method.set_ttl) method
pub fn ttl(&self) -> Option<u8> {
self.ttl
}
/// Set the time-to-live (IPv4) or hop limit (IPv6) value used in outgoing packets.
///
/// A socket without an explicitly set TTL value uses the default [IANA recommended]
/// value (`64`).
///
/// # Panics
///
/// This function panics if a TTL value of `0` is given. See [RFC 1122 § 3.2.1.7].
///
/// [IANA recommended]: https://www.iana.org/assignments/ip-parameters/ip-parameters.xhtml
/// [RFC 1122 § 3.2.1.7]: https://tools.ietf.org/html/rfc1122#section-3.2.1.7
pub fn set_ttl(&mut self, ttl: Option<u8>) {
// A host MUST NOT send a datagram with a Time-to-Live (TTL)
// value of 0
match ttl {
Some(0) => { panic!("A TTL value of 0 is invalid for a sent packet"); },
catchall => self.ttl = catchall,
}
}
/// Bind the socket to the given endpoint.
///
/// This function returns `Err(Error::Illegal)` if the socket was open
@ -200,6 +230,7 @@ impl<'a, 'b> UdpSocket<'a, 'b> {
where F: FnOnce((IpRepr, UdpRepr)) -> Result<()> {
let handle = self.handle;
let endpoint = self.endpoint;
let ttl = self.ttl.unwrap_or(64);
self.tx_buffer.dequeue_one_with(|packet_buf| {
net_trace!("{}:{}:{}: sending {} octets",
handle, endpoint,
@ -214,7 +245,8 @@ impl<'a, 'b> UdpSocket<'a, 'b> {
src_addr: endpoint.addr,
dst_addr: packet_buf.endpoint.addr,
protocol: IpProtocol::Udp,
payload_len: repr.buffer_len()
payload_len: repr.buffer_len(),
ttl: ttl,
};
emit((ip_repr, repr))
})
@ -275,7 +307,8 @@ mod test {
src_addr: LOCAL_IP,
dst_addr: REMOTE_IP,
protocol: IpProtocol::Udp,
payload_len: 8 + 6
payload_len: 8 + 6,
ttl: 64,
};
const LOCAL_UDP_REPR: UdpRepr = UdpRepr {
src_port: LOCAL_PORT,
@ -337,7 +370,8 @@ mod test {
src_addr: Ipv4Address([10, 0, 0, 2]),
dst_addr: Ipv4Address([10, 0, 0, 1]),
protocol: IpProtocol::Udp,
payload_len: 8 + 6
payload_len: 8 + 6,
ttl: 64
});
const REMOTE_UDP_REPR: UdpRepr = UdpRepr {
src_port: REMOTE_PORT,
@ -407,7 +441,8 @@ mod test {
src_addr: Ipv4Address([10, 0, 0, 2]),
dst_addr: Ipv4Address([10, 0, 0, 10]),
protocol: IpProtocol::Udp,
payload_len: 8 + 6
payload_len: 8 + 6,
ttl: 64
});
let mut port_bound_socket = socket(buffer(1), buffer(0));
@ -418,4 +453,23 @@ mod test {
assert_eq!(ip_bound_socket.bind(LOCAL_END), Ok(()));
assert!(!ip_bound_socket.accepts(&ip_repr, &REMOTE_UDP_REPR));
}
#[test]
fn test_set_ttl() {
let mut s = socket(buffer(0), buffer(1));
assert_eq!(s.bind(LOCAL_END), Ok(()));
s.set_ttl(Some(0x2a));
assert_eq!(s.send_slice(b"abcdef", REMOTE_END), Ok(()));
assert_eq!(s.dispatch(|(ip_repr, _)| {
assert_eq!(ip_repr, IpRepr::Unspecified{
src_addr: LOCAL_IP,
dst_addr: REMOTE_IP,
protocol: IpProtocol::Udp,
payload_len: 8 + 6,
ttl: 0x2a,
});
Ok(())
}), Ok(()));
}
}

View File

@ -423,6 +423,7 @@ impl<'a> Repr<'a> {
dst_addr: ip_packet.dst_addr(),
protocol: ip_packet.protocol(),
payload_len: payload.len(),
ttl: ip_packet.ttl()
},
data: payload
})

View File

@ -266,7 +266,8 @@ pub enum IpRepr {
src_addr: Address,
dst_addr: Address,
protocol: Protocol,
payload_len: usize
payload_len: usize,
ttl: u8
},
Ipv4(Ipv4Repr),
#[doc(hidden)]
@ -336,6 +337,15 @@ impl IpRepr {
}
}
/// Return the TTL value.
pub fn ttl(&self) -> u8 {
match self {
&IpRepr::Unspecified { ttl, .. } => ttl,
&IpRepr::Ipv4(Ipv4Repr { ttl, .. }) => ttl,
&IpRepr::__Nonexhaustive => unreachable!()
}
}
/// Convert an unspecified representation into a concrete one, or return
/// `Err(Error::Unaddressable)` if not possible.
///
@ -347,20 +357,20 @@ impl IpRepr {
&IpRepr::Unspecified {
src_addr: Address::Ipv4(src_addr),
dst_addr: Address::Ipv4(dst_addr),
protocol, payload_len
protocol, payload_len, ttl
} => {
Ok(IpRepr::Ipv4(Ipv4Repr {
src_addr: src_addr,
dst_addr: dst_addr,
protocol: protocol,
payload_len: payload_len
payload_len: payload_len, ttl
}))
}
&IpRepr::Unspecified {
src_addr: Address::Unspecified,
dst_addr: Address::Ipv4(dst_addr),
protocol, payload_len
protocol, payload_len, ttl
} => {
let mut src_addr = None;
for cidr in fallback_src_addrs {
@ -374,9 +384,7 @@ impl IpRepr {
}
Ok(IpRepr::Ipv4(Ipv4Repr {
src_addr: src_addr.ok_or(Error::Unaddressable)?,
dst_addr: dst_addr,
protocol: protocol,
payload_len: payload_len
dst_addr, protocol, payload_len, ttl
}))
}
@ -534,12 +542,14 @@ mod test {
src_addr: IpAddress::Ipv4(ip_addr_a),
dst_addr: IpAddress::Ipv4(ip_addr_b),
protocol: proto,
payload_len
ttl: 0x2a,
payload_len,
}.lower(&[]),
Ok(IpRepr::Ipv4(Ipv4Repr{
src_addr: ip_addr_a,
dst_addr: ip_addr_b,
protocol: proto,
ttl: 0x2a,
payload_len
}))
);
@ -549,6 +559,7 @@ mod test {
src_addr: IpAddress::Unspecified,
dst_addr: IpAddress::Ipv4(ip_addr_b),
protocol: proto,
ttl: 64,
payload_len
}.lower(&[]),
Err(Error::Unaddressable)
@ -559,12 +570,14 @@ mod test {
src_addr: IpAddress::Unspecified,
dst_addr: IpAddress::Ipv4(ip_addr_b),
protocol: proto,
ttl: 64,
payload_len
}.lower(&[IpCidr::new(IpAddress::Ipv4(ip_addr_a), 24)]),
Ok(IpRepr::Ipv4(Ipv4Repr{
src_addr: ip_addr_a,
dst_addr: ip_addr_b,
protocol: proto,
ttl: 64,
payload_len
}))
);
@ -574,12 +587,14 @@ mod test {
src_addr: ip_addr_a,
dst_addr: ip_addr_b,
protocol: proto,
ttl: 255,
payload_len
}).lower(&[]),
Ok(IpRepr::Ipv4(Ipv4Repr{
src_addr: ip_addr_a,
dst_addr: ip_addr_b,
protocol: proto,
ttl: 255,
payload_len
}))
);
@ -589,6 +604,7 @@ mod test {
src_addr: Ipv4Address::UNSPECIFIED,
dst_addr: ip_addr_b,
protocol: proto,
ttl: 255,
payload_len
}).lower(&[]),
Err(Error::Unaddressable)
@ -599,12 +615,14 @@ mod test {
src_addr: Ipv4Address::UNSPECIFIED,
dst_addr: ip_addr_b,
protocol: proto,
ttl: 64,
payload_len
}).lower(&[IpCidr::new(IpAddress::Ipv4(ip_addr_a), 24)]),
Ok(IpRepr::Ipv4(Ipv4Repr{
src_addr: ip_addr_a,
dst_addr: ip_addr_b,
protocol: proto,
ttl: 64,
payload_len
}))
);

View File

@ -448,7 +448,8 @@ pub struct Repr {
pub src_addr: Address,
pub dst_addr: Address,
pub protocol: Protocol,
pub payload_len: usize
pub payload_len: usize,
pub ttl: u8
}
impl Repr {
@ -474,7 +475,8 @@ impl Repr {
src_addr: packet.src_addr(),
dst_addr: packet.dst_addr(),
protocol: packet.protocol(),
payload_len: payload_len
payload_len: payload_len,
ttl: packet.ttl()
})
}
@ -497,7 +499,7 @@ impl Repr {
packet.set_more_frags(false);
packet.set_dont_frag(true);
packet.set_frag_offset(0);
packet.set_ttl(64);
packet.set_ttl(self.ttl);
packet.set_protocol(self.protocol);
packet.set_src_addr(self.src_addr);
packet.set_dst_addr(self.dst_addr);
@ -722,7 +724,8 @@ mod test {
src_addr: Address([0x11, 0x12, 0x13, 0x14]),
dst_addr: Address([0x21, 0x22, 0x23, 0x24]),
protocol: Protocol::Icmp,
payload_len: 4
payload_len: 4,
ttl: 64
}
}
@ -733,6 +736,17 @@ mod test {
assert_eq!(repr, packet_repr());
}
#[test]
fn test_parse_bad_version() {
let mut bytes = vec![0; 24];
bytes.copy_from_slice(&REPR_PACKET_BYTES[..]);
let mut packet = Packet::new(&mut bytes);
packet.set_version(6);
packet.fill_checksum();
let packet = Packet::new(&*packet.into_inner());
assert_eq!(Repr::parse(&packet, &ChecksumCapabilities::default()), Err(Error::Malformed));
}
#[test]
fn test_parse_total_len_underflow() {
let mut bytes = vec![0; 24];

View File

@ -52,7 +52,8 @@ let repr = Ipv4Repr {
src_addr: Ipv4Address::new(10, 0, 0, 1),
dst_addr: Ipv4Address::new(10, 0, 0, 2),
protocol: IpProtocol::Tcp,
payload_len: 10
payload_len: 10,
ttl: 64
};
let mut buffer = vec![0; repr.buffer_len() + repr.payload_len];
{ // emission