diff --git a/README.md b/README.md index a0ea697..9332847 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,6 @@ The only supported medium is Ethernet. * ARP packets (including gratuitous requests and replies) are supported. * 802.3 and 802.1Q are **not** supported. * Jumbo frames are **not** supported. - * Frame check sequence calculation is **not** supported. ### IP layer diff --git a/src/iface/ethernet.rs b/src/iface/ethernet.rs index dd707d0..4275c4d 100644 --- a/src/iface/ethernet.rs +++ b/src/iface/ethernet.rs @@ -216,10 +216,10 @@ impl<'a, 'b: 'a, _ => return Err(Error::Unrecognized) } - let tx_size = self.device.mtu(); match response { Response::Arp(repr) => { - let mut tx_buffer = try!(self.device.transmit(tx_size)); + let tx_len = EthernetFrame::<&[u8]>::buffer_len(repr.buffer_len()); + let mut tx_buffer = try!(self.device.transmit(tx_len)); let mut frame = try!(EthernetFrame::new(&mut tx_buffer)); frame.set_src_addr(self.hardware_addr); frame.set_dst_addr(match repr { @@ -239,14 +239,16 @@ impl<'a, 'b: 'a, Some(hardware_addr) => hardware_addr }; - let mut tx_buffer = try!(self.device.transmit(tx_size)); + let tx_len = EthernetFrame::<&[u8]>::buffer_len(ip_repr.buffer_len() + + icmp_repr.buffer_len()); + let mut tx_buffer = try!(self.device.transmit(tx_len)); let mut frame = try!(EthernetFrame::new(&mut tx_buffer)); frame.set_src_addr(self.hardware_addr); frame.set_dst_addr(dst_hardware_addr); frame.set_ethertype(EthernetProtocolType::Ipv4); let mut ip_packet = try!(Ipv4Packet::new(frame.payload_mut())); - ip_repr.emit(&mut ip_packet, icmp_repr.len()); + ip_repr.emit(&mut ip_packet, icmp_repr.buffer_len()); let mut icmp_packet = try!(Icmpv4Packet::new(ip_packet.payload_mut())); icmp_repr.emit(&mut icmp_packet); @@ -261,19 +263,6 @@ impl<'a, 'b: 'a, for socket in self.sockets.borrow_mut() { let result = socket.dispatch(&mut |src_addr, dst_addr, protocol, payload| { - let dst_hardware_addr = - match arp_cache.lookup(*dst_addr) { - None => return Err(Error::Unaddressable), - Some(hardware_addr) => hardware_addr - }; - - let mut tx_buffer = try!(device.transmit(tx_size)); - let mut frame = try!(EthernetFrame::new(&mut tx_buffer)); - frame.set_src_addr(src_hardware_addr); - frame.set_dst_addr(dst_hardware_addr); - frame.set_ethertype(EthernetProtocolType::Ipv4); - - let mut ip_packet = try!(Ipv4Packet::new(frame.payload_mut())); let ip_repr = match (src_addr, dst_addr) { (&InternetAddress::Ipv4(src_addr), @@ -286,7 +275,24 @@ impl<'a, 'b: 'a, }, _ => unreachable!() }; - ip_repr.emit(&mut ip_packet, payload.len()); + + let dst_hardware_addr = + match arp_cache.lookup(*dst_addr) { + None => return Err(Error::Unaddressable), + Some(hardware_addr) => hardware_addr + }; + + let tx_len = EthernetFrame::<&[u8]>::buffer_len(ip_repr.buffer_len() + + payload.buffer_len()); + let mut tx_buffer = try!(device.transmit(tx_len)); + let mut frame = try!(EthernetFrame::new(&mut tx_buffer)); + frame.set_src_addr(src_hardware_addr); + frame.set_dst_addr(dst_hardware_addr); + frame.set_ethertype(EthernetProtocolType::Ipv4); + + let mut ip_packet = try!(Ipv4Packet::new(frame.payload_mut())); + ip_repr.emit(&mut ip_packet, payload.buffer_len()); + payload.emit(src_addr, dst_addr, ip_packet.payload_mut()); Ok(()) diff --git a/src/phy/raw_socket.rs b/src/phy/raw_socket.rs index 0c4bb8a..ec562ae 100644 --- a/src/phy/raw_socket.rs +++ b/src/phy/raw_socket.rs @@ -38,7 +38,8 @@ impl Device for RawSocket { fn receive(&mut self) -> Result { let mut lower = self.lower.borrow_mut(); let mut buffer = vec![0; self.mtu]; - lower.recv(&mut buffer[..]).unwrap(); + let size = lower.recv(&mut buffer[..]).unwrap(); + buffer.resize(size, 0); Ok(buffer) } diff --git a/src/phy/tap_interface.rs b/src/phy/tap_interface.rs index 7fede5f..5e3bbcf 100644 --- a/src/phy/tap_interface.rs +++ b/src/phy/tap_interface.rs @@ -38,7 +38,8 @@ impl Device for TapInterface { fn receive(&mut self) -> Result { let mut lower = self.lower.borrow_mut(); let mut buffer = vec![0; self.mtu]; - lower.recv(&mut buffer[..]).unwrap(); + let size = lower.recv(&mut buffer[..]).unwrap(); + buffer.resize(size, 0); Ok(buffer) } diff --git a/src/socket/mod.rs b/src/socket/mod.rs index 0cf3da1..7050090 100644 --- a/src/socket/mod.rs +++ b/src/socket/mod.rs @@ -27,8 +27,8 @@ pub use self::tcp::Buffer as TcpBuffer; /// This interface abstracts the various types of packets layered under the IP protocol, /// and serves as an accessory to [trait Socket](trait.Socket.html). pub trait PacketRepr { - /// Return the length required to serialize this high-level representation. - fn len(&self) -> usize; + /// Return the length of the buffer required to serialize this high-level representation. + fn buffer_len(&self) -> usize; /// Emit this high-level representation into a sequence of octets. fn emit(&self, src_addr: &Address, dst_addr: &Address, payload: &mut [u8]); diff --git a/src/socket/udp.rs b/src/socket/udp.rs index 7bd2cc1..86759b1 100644 --- a/src/socket/udp.rs +++ b/src/socket/udp.rs @@ -201,8 +201,8 @@ impl<'a, 'b> UdpSocket<'a, 'b> { } impl<'a> PacketRepr for UdpRepr<'a> { - fn len(&self) -> usize { - self.len() + fn buffer_len(&self) -> usize { + self.buffer_len() } fn emit(&self, src_addr: &Address, dst_addr: &Address, payload: &mut [u8]) { diff --git a/src/wire/arp.rs b/src/wire/arp.rs index a184d94..3008c38 100644 --- a/src/wire/arp.rs +++ b/src/wire/arp.rs @@ -263,6 +263,14 @@ impl Repr { } } + /// Return the length of a packet that will be emitted from this high-level representation. + pub fn buffer_len(&self) -> usize { + match self { + &Repr::EthernetIpv4 { .. } => field::TPA(6, 4).end, + &Repr::__Nonexhaustive => unreachable!() + } + } + /// Emit a high-level representation into an Address Resolution Protocol packet. pub fn emit + AsMut<[u8]>>(&self, packet: &mut Packet) { match self { diff --git a/src/wire/ethernet.rs b/src/wire/ethernet.rs index 3019d7a..1207237 100644 --- a/src/wire/ethernet.rs +++ b/src/wire/ethernet.rs @@ -1,5 +1,7 @@ +use core::cmp; use core::fmt; use byteorder::{ByteOrder, NetworkEndian}; + use Error; enum_with_unknown! { @@ -95,6 +97,14 @@ impl> Frame { self.buffer } + /// Return the length of a buffer required to hold a packet with the payload + /// of a given length. + pub fn buffer_len(payload_len: usize) -> usize { + // Minimal frame size is 64, but that includes FCS, which the network device + // is taking care of for us. + cmp::max(field::PAYLOAD.start + payload_len, 60) + } + /// Return the destination address field. #[inline(always)] pub fn dst_addr(&self) -> Address { diff --git a/src/wire/icmpv4.rs b/src/wire/icmpv4.rs index 04f243a..48a61d3 100644 --- a/src/wire/icmpv4.rs +++ b/src/wire/icmpv4.rs @@ -332,7 +332,7 @@ impl<'a> Repr<'a> { } /// Return the length of a packet that will be emitted from this high-level representation. - pub fn len(&self) -> usize { + pub fn buffer_len(&self) -> usize { match self { &Repr::EchoRequest { data, .. } | &Repr::EchoReply { data, .. } => { @@ -384,10 +384,12 @@ impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Packet<&'a T> { impl<'a> fmt::Display for Repr<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - &Repr::EchoRequest { ident, seq_no, .. } => - write!(f, "ICMPv4 Echo Request id={} seq={}", ident, seq_no), - &Repr::EchoReply { ident, seq_no, .. } => - write!(f, "ICMPv4 Echo Reply id={} seq={}", ident, seq_no), + &Repr::EchoRequest { ident, seq_no, data } => + write!(f, "ICMPv4 Echo Request id={} seq={} len={}", + ident, seq_no, data.len()), + &Repr::EchoReply { ident, seq_no, data } => + write!(f, "ICMPv4 Echo Reply id={} seq={} len={}", + ident, seq_no, data.len()), &Repr::__Nonexhaustive => unreachable!() } } diff --git a/src/wire/ipv4.rs b/src/wire/ipv4.rs index 2430f30..285a16a 100644 --- a/src/wire/ipv4.rs +++ b/src/wire/ipv4.rs @@ -382,6 +382,12 @@ impl Repr { }) } + /// Return the length of a header that will be emitted from this high-level representation. + pub fn buffer_len(&self) -> usize { + // We never emit any options. + field::DST_ADDR.end + } + /// Emit a high-level representation into an Internet Protocol version 4 packet. pub fn emit + AsMut<[u8]>>(&self, packet: &mut Packet, payload_len: usize) { diff --git a/src/wire/udp.rs b/src/wire/udp.rs index 71690fe..8880fb4 100644 --- a/src/wire/udp.rs +++ b/src/wire/udp.rs @@ -197,7 +197,7 @@ impl<'a> Repr<'a> { } /// Return the length of a packet that will be emitted from this high-level representation. - pub fn len(&self) -> usize { + pub fn buffer_len(&self) -> usize { field::CHECKSUM.end + self.payload.len() }