Calculate IP payload length from the total length field.

Before this commit, IP payload length was calculated by subtracting
the IP header length from the total underlying buffer length, which
fails if the underlying buffer has padding, e.g. like Ethernet
does.
v0.7.x
whitequark 2017-01-14 11:07:06 +00:00
parent 9b24627d69
commit 578d7bce5f
7 changed files with 114 additions and 96 deletions

View File

@ -170,7 +170,7 @@ impl<'a, 'b, 'c, DeviceT: Device + 'a> Interface<'a, 'b, 'c, DeviceT> {
Ipv4Repr { dst_addr, .. } if !self.has_protocol_addr(dst_addr) => (),
// Respond to ICMP packets.
Ipv4Repr { protocol: IpProtocol::Icmp, src_addr, dst_addr } => {
Ipv4Repr { protocol: IpProtocol::Icmp, src_addr, dst_addr, .. } => {
let icmp_packet = try!(Icmpv4Packet::new(ipv4_packet.payload()));
let icmp_repr = try!(Icmpv4Repr::parse(&icmp_packet));
match icmp_repr {
@ -178,16 +178,17 @@ impl<'a, 'b, 'c, DeviceT: Device + 'a> Interface<'a, 'b, 'c, DeviceT> {
Icmpv4Repr::EchoRequest {
ident, seq_no, data
} => {
let ipv4_reply_repr = Ipv4Repr {
src_addr: dst_addr,
dst_addr: src_addr,
protocol: IpProtocol::Icmp
};
let icmp_reply_repr = Icmpv4Repr::EchoReply {
ident: ident,
seq_no: seq_no,
data: data
};
let ipv4_reply_repr = Ipv4Repr {
src_addr: dst_addr,
dst_addr: src_addr,
protocol: IpProtocol::Icmp,
payload_len: icmp_reply_repr.buffer_len()
};
response = Response::Icmpv4(ipv4_reply_repr, icmp_reply_repr)
}
@ -200,7 +201,7 @@ impl<'a, 'b, 'c, DeviceT: Device + 'a> Interface<'a, 'b, 'c, DeviceT> {
},
// Try dispatching a packet to a socket.
Ipv4Repr { src_addr, dst_addr, protocol } => {
Ipv4Repr { src_addr, dst_addr, protocol, .. } => {
let mut handled = false;
for socket in sockets.iter_mut() {
let ip_repr = IpRepr::Ipv4(ipv4_repr);
@ -228,11 +229,6 @@ impl<'a, 'b, 'c, DeviceT: Device + 'a> Interface<'a, 'b, 'c, DeviceT> {
if !handled && protocol == IpProtocol::Tcp {
let tcp_packet = try!(TcpPacket::new(ipv4_packet.payload()));
let ipv4_reply_repr = Ipv4Repr {
src_addr: dst_addr,
dst_addr: src_addr,
protocol: IpProtocol::Tcp
};
let tcp_reply_repr = TcpRepr {
src_port: tcp_packet.dst_port(),
dst_port: tcp_packet.src_port(),
@ -243,6 +239,12 @@ impl<'a, 'b, 'c, DeviceT: Device + 'a> Interface<'a, 'b, 'c, DeviceT> {
window_len: 0,
payload: &[]
};
let ipv4_reply_repr = Ipv4Repr {
src_addr: dst_addr,
dst_addr: src_addr,
protocol: IpProtocol::Tcp,
payload_len: tcp_reply_repr.buffer_len()
};
response = Response::Tcpv4(ipv4_reply_repr, tcp_reply_repr);
} else if !handled {
let reason;
@ -255,16 +257,16 @@ impl<'a, 'b, 'c, DeviceT: Device + 'a> Interface<'a, 'b, 'c, DeviceT> {
let mut data = [0; 8];
data.copy_from_slice(&ipv4_packet.payload()[0..8]);
let ipv4_reply_repr = Ipv4Repr {
src_addr: dst_addr,
dst_addr: src_addr,
protocol: IpProtocol::Icmp
};
let icmp_reply_repr = Icmpv4Repr::DstUnreachable {
reason: reason,
header: ipv4_repr,
length: ipv4_packet.payload().len(),
data: data
reason: reason,
header: ipv4_repr,
data: data
};
let ipv4_reply_repr = Ipv4Repr {
src_addr: dst_addr,
dst_addr: src_addr,
protocol: IpProtocol::Icmp,
payload_len: icmp_reply_repr.buffer_len()
};
response = Response::Icmpv4(ipv4_reply_repr, icmp_reply_repr)
}
@ -277,16 +279,15 @@ impl<'a, 'b, 'c, DeviceT: Device + 'a> Interface<'a, 'b, 'c, DeviceT> {
}
macro_rules! ip_response {
($tx_buffer:ident, $frame:ident, $ip_repr:ident, $length:expr) => ({
($tx_buffer:ident, $frame:ident, $ip_repr:ident) => ({
let dst_hardware_addr =
match self.arp_cache.lookup(&$ip_repr.dst_addr.into()) {
None => return Err(Error::Unaddressable),
Some(hardware_addr) => hardware_addr
};
let payload_len = $length;
let frame_len = EthernetFrame::<&[u8]>::buffer_len($ip_repr.buffer_len() +
payload_len);
$ip_repr.payload_len);
$tx_buffer = try!(self.device.transmit(frame_len));
$frame = try!(EthernetFrame::new(&mut $tx_buffer));
$frame.set_src_addr(self.hardware_addr);
@ -294,7 +295,7 @@ impl<'a, 'b, 'c, DeviceT: Device + 'a> Interface<'a, 'b, 'c, DeviceT> {
$frame.set_ethertype(EthernetProtocol::Ipv4);
let mut ip_packet = try!(Ipv4Packet::new($frame.payload_mut()));
$ip_repr.emit(&mut ip_packet, payload_len);
$ip_repr.emit(&mut ip_packet);
ip_packet
})
}
@ -320,8 +321,7 @@ impl<'a, 'b, 'c, DeviceT: Device + 'a> Interface<'a, 'b, 'c, DeviceT> {
Response::Icmpv4(ip_repr, icmp_repr) => {
let mut tx_buffer;
let mut frame;
let mut ip_packet = ip_response!(tx_buffer, frame, ip_repr,
icmp_repr.buffer_len());
let mut ip_packet = ip_response!(tx_buffer, frame, ip_repr);
let mut icmp_packet = try!(Icmpv4Packet::new(ip_packet.payload_mut()));
icmp_repr.emit(&mut icmp_packet);
Ok(())
@ -330,8 +330,7 @@ impl<'a, 'b, 'c, DeviceT: Device + 'a> Interface<'a, 'b, 'c, DeviceT> {
Response::Tcpv4(ip_repr, tcp_repr) => {
let mut tx_buffer;
let mut frame;
let mut ip_packet = ip_response!(tx_buffer, frame, ip_repr,
tcp_repr.buffer_len());
let mut ip_packet = ip_response!(tx_buffer, frame, ip_repr);
let mut tcp_packet = try!(TcpPacket::new(ip_packet.payload_mut()));
tcp_repr.emit(&mut tcp_packet,
&IpAddress::Ipv4(ip_repr.src_addr),
@ -372,7 +371,7 @@ impl<'a, 'b, 'c, DeviceT: Device + 'a> Interface<'a, 'b, 'c, DeviceT> {
frame.set_dst_addr(dst_hardware_addr);
frame.set_ethertype(EthernetProtocol::Ipv4);
repr.emit(frame.payload_mut(), payload.buffer_len());
repr.emit(frame.payload_mut());
let mut ip_packet = try!(Ipv4Packet::new(frame.payload_mut()));
payload.emit(&repr, ip_packet.payload_mut());

View File

@ -513,7 +513,7 @@ impl<'a> TcpSocket<'a> {
payload: &[u8]) -> Result<(), Error> {
if ip_repr.protocol() != IpProtocol::Tcp { return Err(Error::Rejected) }
let packet = try!(TcpPacket::new(payload));
let packet = try!(TcpPacket::new(&payload[..ip_repr.payload_len()]));
let repr = try!(TcpRepr::parse(&packet, &ip_repr.src_addr(), &ip_repr.dst_addr()));
// Reject packets with a wrong destination.
@ -749,11 +749,6 @@ impl<'a> TcpSocket<'a> {
where F: FnMut(&IpRepr, &IpPayload) -> Result<R, Error> {
if self.remote_endpoint.is_unspecified() { return Err(Error::Exhausted) }
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,
@ -864,6 +859,12 @@ impl<'a> TcpSocket<'a> {
repr.ack_number = Some(ack_number);
self.remote_last_ack = ack_number;
let ip_repr = IpRepr::Unspecified {
src_addr: self.local_endpoint.addr,
dst_addr: self.remote_endpoint.addr,
protocol: IpProtocol::Tcp,
payload_len: repr.buffer_len()
};
emit(&ip_repr, &repr)
} else {
Err(Error::Exhausted)
@ -963,9 +964,10 @@ mod test {
let mut packet = TcpPacket::new(&mut buffer).unwrap();
repr.emit(&mut packet, &REMOTE_IP, &LOCAL_IP);
let ip_repr = IpRepr::Unspecified {
src_addr: REMOTE_IP,
dst_addr: LOCAL_IP,
protocol: IpProtocol::Tcp
src_addr: REMOTE_IP,
dst_addr: LOCAL_IP,
protocol: IpProtocol::Tcp,
payload_len: repr.buffer_len()
};
socket.process(timestamp, &ip_repr, &packet.into_inner()[..])
}

View File

@ -196,7 +196,7 @@ impl<'a, 'b> UdpSocket<'a, 'b> {
payload: &[u8]) -> Result<(), Error> {
if ip_repr.protocol() != IpProtocol::Udp { return Err(Error::Rejected) }
let packet = try!(UdpPacket::new(payload));
let packet = try!(UdpPacket::new(&payload[..ip_repr.payload_len()]));
let repr = try!(UdpRepr::parse(&packet, &ip_repr.src_addr(), &ip_repr.dst_addr()));
if repr.dst_port != self.endpoint.port { return Err(Error::Rejected) }
@ -219,17 +219,18 @@ impl<'a, 'b> UdpSocket<'a, 'b> {
let packet_buf = try!(self.tx_buffer.dequeue().map_err(|()| Error::Exhausted));
net_trace!("udp:{}:{}: sending {} octets",
self.endpoint, packet_buf.endpoint, packet_buf.size);
let ip_repr = IpRepr::Unspecified {
src_addr: self.endpoint.addr,
dst_addr: packet_buf.endpoint.addr,
protocol: IpProtocol::Udp
};
let payload = UdpRepr {
let repr = UdpRepr {
src_port: self.endpoint.port,
dst_port: packet_buf.endpoint.port,
payload: &packet_buf.as_ref()[..]
};
emit(&ip_repr, &payload)
let ip_repr = IpRepr::Unspecified {
src_addr: self.endpoint.addr,
dst_addr: packet_buf.endpoint.addr,
protocol: IpProtocol::Udp,
payload_len: repr.buffer_len()
};
emit(&ip_repr, &repr)
}
}

View File

@ -352,7 +352,6 @@ pub enum Repr<'a> {
DstUnreachable {
reason: DstUnreachable,
header: Ipv4Repr,
length: usize,
data: [u8; 8]
},
#[doc(hidden)]
@ -389,12 +388,9 @@ impl<'a> Repr<'a> {
if payload.len() < data.len() { return Err(Error::Truncated) }
data.copy_from_slice(&payload[0..8]);
let length = ip_packet.total_len() as usize - ip_packet.header_len() as usize;
Ok(Repr::DstUnreachable {
reason: DstUnreachable::from(code),
header: ip_repr,
length: length,
data: data
})
}
@ -439,13 +435,13 @@ impl<'a> Repr<'a> {
packet.data_mut()[..data_len].copy_from_slice(&data[..data_len])
},
&Repr::DstUnreachable { reason, header, length, data } => {
&Repr::DstUnreachable { reason, header, data } => {
packet.set_msg_type(Message::DstUnreachable);
packet.set_msg_code(reason.into());
let mut ip_packet = Ipv4Packet::new(packet.data_mut())
.expect("undersized data");
header.emit(&mut ip_packet, length);
header.emit(&mut ip_packet);
let mut payload = &mut ip_packet.into_inner()[header.buffer_len()..];
payload.copy_from_slice(&data[..])
}

View File

@ -120,9 +120,10 @@ impl From<u16> for Endpoint {
#[derive(Debug, Clone)]
pub enum IpRepr {
Unspecified {
src_addr: Address,
dst_addr: Address,
protocol: Protocol
src_addr: Address,
dst_addr: Address,
protocol: Protocol,
payload_len: usize
},
Ipv4(Ipv4Repr),
#[doc(hidden)]
@ -157,6 +158,15 @@ impl IpRepr {
}
}
/// Return the payload length.
pub fn payload_len(&self) -> usize {
match self {
&IpRepr::Unspecified { payload_len, .. } => payload_len,
&IpRepr::Ipv4(repr) => repr.payload_len,
&IpRepr::__Nonexhaustive => unreachable!()
}
}
/// Convert an unspecified representation into a concrete one, or return
/// `Err(Error::Unaddressable)` if not possible.
///
@ -168,19 +178,20 @@ impl IpRepr {
&IpRepr::Unspecified {
src_addr: Address::Ipv4(src_addr),
dst_addr: Address::Ipv4(dst_addr),
protocol
protocol, payload_len
} => {
Ok(IpRepr::Ipv4(Ipv4Repr {
src_addr: src_addr,
dst_addr: dst_addr,
protocol: protocol
src_addr: src_addr,
dst_addr: dst_addr,
protocol: protocol,
payload_len: payload_len
}))
}
&IpRepr::Unspecified {
src_addr: Address::Unspecified,
dst_addr: Address::Ipv4(dst_addr),
protocol
protocol, payload_len
} => {
let mut src_addr = None;
for addr in fallback_src_addrs {
@ -193,9 +204,10 @@ impl IpRepr {
}
}
Ok(IpRepr::Ipv4(Ipv4Repr {
src_addr: try!(src_addr.ok_or(Error::Unaddressable)),
dst_addr: dst_addr,
protocol: protocol
src_addr: try!(src_addr.ok_or(Error::Unaddressable)),
dst_addr: dst_addr,
protocol: protocol,
payload_len: payload_len
}))
}
@ -225,12 +237,12 @@ impl IpRepr {
///
/// # Panics
/// This function panics if invoked on an unspecified representation.
pub fn emit<T: AsRef<[u8]> + AsMut<[u8]>>(&self, buffer: T, payload_len: usize) {
pub fn emit<T: AsRef<[u8]> + AsMut<[u8]>>(&self, buffer: T) {
match self {
&IpRepr::Unspecified { .. } => panic!("unspecified IP representation"),
&IpRepr::Ipv4(repr) => {
let mut packet = Ipv4Packet::new(buffer).expect("undersized buffer");
repr.emit(&mut packet, payload_len)
repr.emit(&mut packet)
}
&IpRepr::__Nonexhaustive => unreachable!()
}

View File

@ -367,9 +367,10 @@ impl<'a, T: AsRef<[u8]> + AsMut<[u8]> + ?Sized> Packet<&'a mut T> {
/// A high-level representation of an Internet Protocol version 4 packet header.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct Repr {
pub src_addr: Address,
pub dst_addr: Address,
pub protocol: Protocol
pub src_addr: Address,
pub dst_addr: Address,
pub protocol: Protocol,
pub payload_len: usize
}
impl Repr {
@ -391,9 +392,10 @@ impl Repr {
// All ECN values are acceptable, since ECN requires opt-in from both endpoints.
// All TTL values are acceptable, since we do not perform routing.
Ok(Repr {
src_addr: packet.src_addr(),
dst_addr: packet.dst_addr(),
protocol: packet.protocol()
src_addr: packet.src_addr(),
dst_addr: packet.dst_addr(),
protocol: packet.protocol(),
payload_len: payload_len
})
}
@ -404,13 +406,12 @@ impl Repr {
}
/// Emit a high-level representation into an Internet Protocol version 4 packet.
pub fn emit<T: AsRef<[u8]> + AsMut<[u8]>>(&self, packet: &mut Packet<T>,
payload_len: usize) {
pub fn emit<T: AsRef<[u8]> + AsMut<[u8]>>(&self, packet: &mut Packet<T>) {
packet.set_version(4);
packet.set_header_len(field::DST_ADDR.end as u8);
packet.set_dscp(0);
packet.set_ecn(0);
let total_len = packet.header_len() as u16 + payload_len as u16;
let total_len = packet.header_len() as u16 + self.payload_len as u16;
packet.set_total_len(total_len);
packet.set_ident(0);
packet.clear_flags();
@ -476,23 +477,28 @@ use super::pretty_print::{PrettyPrint, PrettyIndent};
impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
fn pretty_print(buffer: &AsRef<[u8]>, f: &mut fmt::Formatter,
indent: &mut PrettyIndent) -> fmt::Result {
let ip_packet = match Packet::new(buffer) {
let (ip_repr, payload) = match Packet::new(buffer) {
Err(err) => return write!(f, "{}({})\n", indent, err),
Ok(ip_packet) => ip_packet
Ok(ip_packet) => {
match Repr::parse(&ip_packet) {
Err(err) => return write!(f, "{}{} ({})\n", indent, ip_packet, err),
Ok(ip_repr) => (ip_repr, &ip_packet.payload()[..ip_repr.payload_len])
}
}
};
try!(write!(f, "{}{}\n", indent, ip_packet));
try!(write!(f, "{}{}\n", indent, ip_repr));
indent.increase();
match ip_packet.protocol() {
match ip_repr.protocol {
Protocol::Icmp =>
super::Icmpv4Packet::<&[u8]>::pretty_print(&ip_packet.payload(), f, indent),
super::Icmpv4Packet::<&[u8]>::pretty_print(&payload, f, indent),
Protocol::Udp => {
match super::UdpPacket::new(&ip_packet.payload()) {
match super::UdpPacket::new(payload) {
Err(err) => write!(f, "{}({})\n", indent, err),
Ok(udp_packet) => {
match super::UdpRepr::parse(&udp_packet,
&IpAddress::from(ip_packet.src_addr()),
&IpAddress::from(ip_packet.dst_addr())) {
&IpAddress::from(ip_repr.src_addr),
&IpAddress::from(ip_repr.dst_addr)) {
Err(err) => write!(f, "{}{} ({})\n", indent, udp_packet, err),
Ok(udp_repr) => write!(f, "{}{}\n", indent, udp_repr)
}
@ -500,12 +506,12 @@ impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
}
}
Protocol::Tcp => {
match super::TcpPacket::new(&ip_packet.payload()) {
match super::TcpPacket::new(payload) {
Err(err) => write!(f, "{}({})\n", indent, err),
Ok(tcp_packet) => {
match super::TcpRepr::parse(&tcp_packet,
&IpAddress::from(ip_packet.src_addr()),
&IpAddress::from(ip_packet.dst_addr())) {
&IpAddress::from(ip_repr.src_addr),
&IpAddress::from(ip_repr.dst_addr)) {
Err(err) => write!(f, "{}{} ({})\n", indent, tcp_packet, err),
Ok(tcp_repr) => write!(f, "{}{}\n", indent, tcp_repr)
}
@ -592,9 +598,10 @@ mod test {
fn packet_repr() -> Repr {
Repr {
src_addr: Address([0x11, 0x12, 0x13, 0x14]),
dst_addr: Address([0x21, 0x22, 0x23, 0x24]),
protocol: Protocol::Icmp
src_addr: Address([0x11, 0x12, 0x13, 0x14]),
dst_addr: Address([0x21, 0x22, 0x23, 0x24]),
protocol: Protocol::Icmp,
payload_len: 4
}
}
@ -610,7 +617,7 @@ mod test {
let repr = packet_repr();
let mut bytes = vec![0; repr.buffer_len() + REPR_PAYLOAD_BYTES.len()];
let mut packet = Packet::new(&mut bytes).unwrap();
repr.emit(&mut packet, REPR_PAYLOAD_BYTES.len());
repr.emit(&mut packet);
packet.payload_mut().copy_from_slice(&REPR_PAYLOAD_BYTES);
assert_eq!(&packet.into_inner()[..], &REPR_PACKET_BYTES[..]);
}

View File

@ -29,17 +29,18 @@
```rust
use smoltcp::wire::*;
let repr = Ipv4Repr {
src_addr: Ipv4Address::new(10, 0, 0, 1),
dst_addr: Ipv4Address::new(10, 0, 0, 2),
protocol: IpProtocol::Tcp
src_addr: Ipv4Address::new(10, 0, 0, 1),
dst_addr: Ipv4Address::new(10, 0, 0, 2),
protocol: IpProtocol::Tcp,
payload_len: 10
};
let mut buffer = vec![0; repr.buffer_len()];
let mut buffer = vec![0; repr.buffer_len() + 10];
{ // emission
let mut packet = Ipv4Packet::new(&mut buffer).unwrap();
repr.emit(&mut packet, /*payload size*/ 0);
repr.emit(&mut packet);
}
{ // parsing
let mut packet = Ipv4Packet::new(&buffer).unwrap();
let packet = Ipv4Packet::new(&buffer).unwrap();
let parsed = Ipv4Repr::parse(&packet).unwrap();
assert_eq!(repr, parsed);
}