Unify EthernetInterface::{send_response,emit} transmit paths.
This commit is contained in:
parent
1d01189278
commit
bab5c0b7aa
|
@ -8,7 +8,7 @@ use wire::{Ipv4Packet, Ipv4Repr};
|
|||
use wire::{Icmpv4Packet, Icmpv4Repr, Icmpv4DstUnreachable};
|
||||
use wire::{IpAddress, IpProtocol, IpRepr};
|
||||
use wire::{TcpPacket, TcpRepr, TcpControl};
|
||||
use socket::{Socket, SocketSet, RawSocket, TcpSocket, UdpSocket, AsSocket};
|
||||
use socket::{Socket, SocketSet, RawSocket, TcpSocket, UdpSocket, AsSocket, IpPayload};
|
||||
use super::ArpCache;
|
||||
|
||||
/// An Ethernet network interface.
|
||||
|
@ -27,7 +27,8 @@ enum Response<'a> {
|
|||
Nop,
|
||||
Arp(ArpRepr),
|
||||
Icmpv4(Ipv4Repr, Icmpv4Repr<'a>),
|
||||
Tcp((IpRepr, TcpRepr<'a>))
|
||||
Tcp((IpRepr, TcpRepr<'a>)),
|
||||
Payload(IpRepr, &'a IpPayload)
|
||||
}
|
||||
|
||||
impl<'a, 'b, 'c, DeviceT: Device + 'a> Interface<'a, 'b, 'c, DeviceT> {
|
||||
|
@ -112,7 +113,7 @@ impl<'a, 'b, 'c, DeviceT: Device + 'a> Interface<'a, 'b, 'c, DeviceT> {
|
|||
pub fn poll(&mut self, sockets: &mut SocketSet, timestamp: u64) -> Result<()> {
|
||||
// First, transmit any outgoing packets.
|
||||
loop {
|
||||
if self.emit(sockets, timestamp)? { break }
|
||||
if self.dispatch(sockets, timestamp)? { break }
|
||||
}
|
||||
|
||||
// Now, receive any incoming packets.
|
||||
|
@ -134,7 +135,7 @@ impl<'a, 'b, 'c, DeviceT: Device + 'a> Interface<'a, 'b, 'c, DeviceT> {
|
|||
_ => return Err(Error::Unrecognized),
|
||||
};
|
||||
|
||||
self.send_response(timestamp, response)
|
||||
self.dispatch_response(timestamp, response)
|
||||
}
|
||||
|
||||
// Snoop all ARP traffic, and respond to ARP packets directed at us.
|
||||
|
@ -334,7 +335,30 @@ impl<'a, 'b, 'c, DeviceT: Device + 'a> Interface<'a, 'b, 'c, DeviceT> {
|
|||
}
|
||||
}
|
||||
|
||||
fn send_response(&mut self, timestamp: u64, response: Response) -> Result<()> {
|
||||
fn dispatch(&mut self, sockets: &mut SocketSet, timestamp: u64) -> Result<bool> {
|
||||
let mut limits = self.device.limits();
|
||||
limits.max_transmission_unit -= EthernetFrame::<&[u8]>::header_len();
|
||||
|
||||
let mut nothing_to_transmit = true;
|
||||
for socket in sockets.iter_mut() {
|
||||
let result = socket.dispatch(timestamp, &limits, |repr, payload| {
|
||||
self.dispatch_response(timestamp, Response::Payload(repr.clone(), payload))
|
||||
});
|
||||
|
||||
match result {
|
||||
Ok(()) => {
|
||||
nothing_to_transmit = false;
|
||||
break
|
||||
}
|
||||
Err(Error::Exhausted) => continue,
|
||||
Err(e) => return Err(e)
|
||||
}
|
||||
}
|
||||
|
||||
Ok(nothing_to_transmit)
|
||||
}
|
||||
|
||||
fn dispatch_response(&mut self, timestamp: u64, response: Response) -> Result<()> {
|
||||
macro_rules! emit_packet {
|
||||
(Ethernet, $buffer_len:expr, |$frame:ident| $code:stmt) => ({
|
||||
let tx_len = EthernetFrame::<&[u8]>::buffer_len($buffer_len);
|
||||
|
@ -351,8 +375,32 @@ impl<'a, 'b, 'c, DeviceT: Device + 'a> Interface<'a, 'b, 'c, DeviceT> {
|
|||
|
||||
(Ip, $ip_repr:expr, |$payload:ident| $code:stmt) => ({
|
||||
let ip_repr = $ip_repr.lower(&self.protocol_addrs)?;
|
||||
|
||||
match self.arp_cache.lookup(&ip_repr.dst_addr()) {
|
||||
None => Err(Error::Unaddressable),
|
||||
None => {
|
||||
match (ip_repr.src_addr(), ip_repr.dst_addr()) {
|
||||
(IpAddress::Ipv4(src_addr), IpAddress::Ipv4(dst_addr)) => {
|
||||
net_debug!("address {} not in ARP cache, sending request",
|
||||
dst_addr);
|
||||
|
||||
let arp_repr = ArpRepr::EthernetIpv4 {
|
||||
operation: ArpOperation::Request,
|
||||
source_hardware_addr: self.hardware_addr,
|
||||
source_protocol_addr: src_addr,
|
||||
target_hardware_addr: EthernetAddress([0xff; 6]),
|
||||
target_protocol_addr: dst_addr,
|
||||
};
|
||||
|
||||
emit_packet!(Ethernet, arp_repr.buffer_len(), |frame| {
|
||||
frame.set_dst_addr(EthernetAddress([0xff; 6]));
|
||||
frame.set_ethertype(EthernetProtocol::Arp);
|
||||
|
||||
arp_repr.emit(&mut ArpPacket::new(frame.payload_mut()));
|
||||
})
|
||||
}
|
||||
_ => unreachable!()
|
||||
}
|
||||
},
|
||||
Some(dst_hardware_addr) => {
|
||||
emit_packet!(Ethernet, ip_repr.total_len(), |frame| {
|
||||
frame.set_dst_addr(dst_hardware_addr);
|
||||
|
@ -398,81 +446,13 @@ impl<'a, 'b, 'c, DeviceT: Device + 'a> Interface<'a, 'b, 'c, DeviceT> {
|
|||
&ip_repr.src_addr(), &ip_repr.dst_addr());
|
||||
})
|
||||
}
|
||||
Response::Payload(ip_repr, ip_payload) => {
|
||||
let ip_repr = ip_repr.lower(&self.protocol_addrs)?;
|
||||
emit_packet!(Ip, ip_repr, |payload| {
|
||||
ip_payload.emit(&ip_repr, payload);
|
||||
})
|
||||
}
|
||||
Response::Nop => Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn emit(&mut self, sockets: &mut SocketSet, timestamp: u64) -> Result<bool> {
|
||||
let mut limits = self.device.limits();
|
||||
limits.max_transmission_unit -= EthernetFrame::<&[u8]>::header_len();
|
||||
|
||||
let mut nothing_to_transmit = true;
|
||||
for socket in sockets.iter_mut() {
|
||||
let result = socket.dispatch(timestamp, &limits, |repr, payload| {
|
||||
let repr = repr.lower(self.protocol_addrs.as_ref())?;
|
||||
|
||||
match self.arp_cache.lookup(&repr.dst_addr()) {
|
||||
Some(dst_hardware_addr) => {
|
||||
let tx_len = EthernetFrame::<&[u8]>::buffer_len(repr.buffer_len() +
|
||||
payload.buffer_len());
|
||||
let mut tx_buffer = self.device.transmit(timestamp, tx_len)?;
|
||||
debug_assert!(tx_buffer.as_ref().len() == tx_len);
|
||||
|
||||
let mut frame = EthernetFrame::new(&mut tx_buffer);
|
||||
frame.set_src_addr(self.hardware_addr);
|
||||
frame.set_dst_addr(dst_hardware_addr);
|
||||
frame.set_ethertype(EthernetProtocol::Ipv4);
|
||||
|
||||
repr.emit(frame.payload_mut());
|
||||
|
||||
let mut ip_packet = Ipv4Packet::new(frame.payload_mut());
|
||||
payload.emit(&repr, ip_packet.payload_mut());
|
||||
}
|
||||
|
||||
None => {
|
||||
let (src_addr, dst_addr) =
|
||||
match (repr.src_addr(), repr.dst_addr()) {
|
||||
(IpAddress::Ipv4(src_addr), IpAddress::Ipv4(dst_addr)) =>
|
||||
(src_addr, dst_addr),
|
||||
// We've lowered all addresses to a concrete form.
|
||||
_ => unreachable!()
|
||||
};
|
||||
|
||||
let payload = ArpRepr::EthernetIpv4 {
|
||||
operation: ArpOperation::Request,
|
||||
source_hardware_addr: self.hardware_addr,
|
||||
source_protocol_addr: src_addr,
|
||||
target_hardware_addr: EthernetAddress::default(),
|
||||
target_protocol_addr: dst_addr,
|
||||
};
|
||||
|
||||
let tx_len = EthernetFrame::<&[u8]>::buffer_len(payload.buffer_len());
|
||||
let mut tx_buffer = self.device.transmit(timestamp, tx_len)?;
|
||||
debug_assert!(tx_buffer.as_ref().len() == tx_len);
|
||||
|
||||
let mut frame = EthernetFrame::new(&mut tx_buffer);
|
||||
frame.set_src_addr(self.hardware_addr);
|
||||
frame.set_dst_addr(EthernetAddress([0xff; 6]));
|
||||
frame.set_ethertype(EthernetProtocol::Arp);
|
||||
|
||||
let mut arp_packet = ArpPacket::new(frame.payload_mut());
|
||||
payload.emit(&mut arp_packet);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
});
|
||||
|
||||
match result {
|
||||
Ok(()) => {
|
||||
nothing_to_transmit = false;
|
||||
break
|
||||
}
|
||||
Err(Error::Exhausted) => continue,
|
||||
Err(e) => return Err(e)
|
||||
}
|
||||
}
|
||||
|
||||
Ok(nothing_to_transmit)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue