Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move interface logic into their logical files #889

Merged
merged 9 commits into from
Jan 5, 2024
13 changes: 1 addition & 12 deletions src/iface/interface/ethernet.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,6 @@
use super::check;
use super::DispatchError;
use super::EthernetPacket;
use super::FragmentsBuffer;
use super::InterfaceInner;
use super::SocketSet;
use core::result::Result;

use crate::phy::TxToken;
use crate::wire::*;
use super::*;

impl InterfaceInner {
#[cfg(feature = "medium-ethernet")]
pub(super) fn process_ethernet<'frame>(
&mut self,
sockets: &mut SocketSet,
Expand Down Expand Up @@ -49,7 +39,6 @@ impl InterfaceInner {
}
}

#[cfg(feature = "medium-ethernet")]
pub(super) fn dispatch_ethernet<Tx, F>(
&mut self,
tx_token: Tx,
Expand Down
12 changes: 10 additions & 2 deletions src/iface/interface/ieee802154.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
use super::*;

impl InterfaceInner {
/// Return the next IEEE802.15.4 sequence number.
#[cfg(feature = "medium-ieee802154")]
pub(super) fn next_ieee802154_seq_number(&mut self) -> u8 {
let no = self.sequence_no;
self.sequence_no = self.sequence_no.wrapping_add(1);
no
}

pub(super) fn process_ieee802154<'output, 'payload: 'output>(
&mut self,
sockets: &mut SocketSet,
Expand Down Expand Up @@ -54,7 +62,7 @@ impl InterfaceInner {
security_enabled: false,
frame_pending: false,
ack_request: false,
sequence_number: Some(self.get_sequence_number()),
sequence_number: Some(self.next_ieee802154_seq_number()),
pan_id_compression: true,
frame_version: Ieee802154FrameVersion::Ieee802154_2003,
dst_pan_id: self.pan_id,
Expand All @@ -78,7 +86,7 @@ impl InterfaceInner {
security_enabled: false,
frame_pending: false,
ack_request: false,
sequence_number: Some(self.get_sequence_number()),
sequence_number: Some(self.next_ieee802154_seq_number()),
pan_id_compression: true,
frame_version: Ieee802154FrameVersion::Ieee802154_2003,
dst_pan_id: self.pan_id,
Expand Down
6 changes: 0 additions & 6 deletions src/iface/interface/igmp.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,5 @@
use super::*;

use crate::phy::{Device, PacketMeta};
use crate::time::{Duration, Instant};
use crate::wire::*;

use core::result::Result;

/// Error type for `join_multicast_group`, `leave_multicast_group`.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
Expand Down
96 changes: 87 additions & 9 deletions src/iface/interface/ipv4.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,92 @@
use super::*;

#[cfg(feature = "socket-dhcpv4")]
use crate::socket::dhcpv4;
#[cfg(feature = "socket-icmp")]
use crate::socket::icmp;
use crate::socket::AnySocket;
impl Interface {
/// Process fragments that still need to be sent for IPv4 packets.
///
/// This function returns a boolean value indicating whether any packets were
/// processed or emitted, and thus, whether the readiness of any socket might
/// have changed.
#[cfg(feature = "proto-ipv4-fragmentation")]
pub(super) fn ipv4_egress<D>(&mut self, device: &mut D) -> bool
where
D: Device + ?Sized,
{
// Reset the buffer when we transmitted everything.
if self.fragmenter.finished() {
self.fragmenter.reset();
}

if self.fragmenter.is_empty() {
return false;
}

use crate::phy::{Medium, TxToken};
use crate::time::Instant;
use crate::wire::*;
let pkt = &self.fragmenter;
if pkt.packet_len > pkt.sent_bytes {
if let Some(tx_token) = device.transmit(self.inner.now) {
self.inner
.dispatch_ipv4_frag(tx_token, &mut self.fragmenter);
return true;
}
}
false
}
}

impl InterfaceInner {
/// Get the next IPv4 fragment identifier.
#[cfg(feature = "proto-ipv4-fragmentation")]
pub(super) fn next_ipv4_frag_ident(&mut self) -> u16 {
let ipv4_id = self.ipv4_id;
self.ipv4_id = self.ipv4_id.wrapping_add(1);
ipv4_id
}

/// Get an IPv4 source address based on a destination address.
///
/// **NOTE**: unlike for IPv6, no specific selection algorithm is implemented. The first IPv4
/// address from the interface is returned.
#[allow(unused)]
pub(crate) fn get_source_address_ipv4(&self, _dst_addr: &Ipv4Address) -> Option<Ipv4Address> {
for cidr in self.ip_addrs.iter() {
#[allow(irrefutable_let_patterns)] // if only ipv4 is enabled
if let IpCidr::Ipv4(cidr) = cidr {
return Some(cidr.address());
}
}
None
}

/// Checks if an address is broadcast, taking into account ipv4 subnet-local
/// broadcast addresses.
pub(crate) fn is_broadcast_v4(&self, address: Ipv4Address) -> bool {
if address.is_broadcast() {
return true;
}

self.ip_addrs
.iter()
.filter_map(|own_cidr| match own_cidr {
IpCidr::Ipv4(own_ip) => Some(own_ip.broadcast()?),
#[cfg(feature = "proto-ipv6")]
IpCidr::Ipv6(_) => None,
})
.any(|broadcast_address| address == broadcast_address)
}

/// Checks if an ipv4 address is unicast, taking into account subnet broadcast addresses
fn is_unicast_v4(&self, address: Ipv4Address) -> bool {
address.is_unicast() && !self.is_broadcast_v4(address)
}

/// Get the first IPv4 address of the interface.
pub fn ipv4_addr(&self) -> Option<Ipv4Address> {
self.ip_addrs.iter().find_map(|addr| match *addr {
IpCidr::Ipv4(cidr) => Some(cidr.address()),
#[allow(unreachable_patterns)]
_ => None,
})
}

pub(super) fn process_ipv4<'a>(
&mut self,
sockets: &mut SocketSet,
Expand Down Expand Up @@ -75,13 +151,15 @@ impl InterfaceInner {

#[cfg(feature = "socket-dhcpv4")]
{
use crate::socket::dhcpv4::Socket as Dhcpv4Socket;

if ipv4_repr.next_header == IpProtocol::Udp
&& matches!(self.caps.medium, Medium::Ethernet)
{
let udp_packet = check!(UdpPacket::new_checked(ip_payload));
if let Some(dhcp_socket) = sockets
.items_mut()
.find_map(|i| dhcpv4::Socket::downcast_mut(&mut i.socket))
.find_map(|i| Dhcpv4Socket::downcast_mut(&mut i.socket))
{
// First check for source and dest ports, then do `UdpRepr::parse` if they match.
// This way we avoid validating the UDP checksum twice for all non-DHCP UDP packets (one here, one in `process_udp`)
Expand Down
149 changes: 136 additions & 13 deletions src/iface/interface/ipv6.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,5 @@
use super::*;

#[cfg(feature = "socket-icmp")]
use crate::socket::icmp;
use crate::socket::AnySocket;

use crate::phy::PacketMeta;
use crate::wire::*;

/// Enum used for the process_hopbyhop function. In some cases, when discarding a packet, an ICMMP
/// parameter problem message needs to be transmitted to the source of the address. In other cases,
/// the processing of the IP packet can continue.
Expand All @@ -26,6 +19,133 @@ impl Default for HopByHopResponse<'_> {
}

impl InterfaceInner {
/// Return the IPv6 address that is a candidate source address for the given destination
/// address, based on RFC 6724.
#[allow(unused)]
pub(crate) fn get_source_address_ipv6(&self, dst_addr: &Ipv6Address) -> Option<Ipv6Address> {
// See RFC 6724 Section 4: Candidate source address
fn is_candidate_source_address(dst_addr: &Ipv6Address, src_addr: &Ipv6Address) -> bool {
// For all multicast and link-local destination addresses, the candidate address MUST
// only be an address from the same link.
if dst_addr.is_link_local() && !src_addr.is_link_local() {
return false;
}

if dst_addr.is_multicast()
&& matches!(dst_addr.scope(), Ipv6AddressScope::LinkLocal)
&& src_addr.is_multicast()
&& !matches!(src_addr.scope(), Ipv6AddressScope::LinkLocal)
{
return false;
}

// Loopback addresses and multicast address can not be in the candidate source address
// list. Except when the destination multicast address has a link-local scope, then the
// source address can also be link-local multicast.
if src_addr.is_loopback() || src_addr.is_multicast() {
return false;
}

true
}

// See RFC 6724 Section 2.2: Common Prefix Length
fn common_prefix_length(dst_addr: &Ipv6Cidr, src_addr: &Ipv6Address) -> usize {
let addr = dst_addr.address();
let mut bits = 0;
for (l, r) in addr.as_bytes().iter().zip(src_addr.as_bytes().iter()) {
if l == r {
bits += 8;
} else {
bits += (l ^ r).leading_zeros();
break;
}
}

bits = bits.min(dst_addr.prefix_len() as u32);

bits as usize
}

// Get the first address that is a candidate address.
let mut candidate = self
.ip_addrs
.iter()
.filter_map(|a| match a {
#[cfg(feature = "proto-ipv4")]
IpCidr::Ipv4(_) => None,
#[cfg(feature = "proto-ipv6")]
IpCidr::Ipv6(a) => Some(a),
})
.find(|a| is_candidate_source_address(dst_addr, &a.address()))
.unwrap();

for addr in self.ip_addrs.iter().filter_map(|a| match a {
#[cfg(feature = "proto-ipv4")]
IpCidr::Ipv4(_) => None,
#[cfg(feature = "proto-ipv6")]
IpCidr::Ipv6(a) => Some(a),
}) {
if !is_candidate_source_address(dst_addr, &addr.address()) {
continue;
}

// Rule 1: prefer the address that is the same as the output destination address.
if candidate.address() != *dst_addr && addr.address() == *dst_addr {
candidate = addr;
}

// Rule 2: prefer appropriate scope.
if (candidate.address().scope() as u8) < (addr.address().scope() as u8) {
if (candidate.address().scope() as u8) < (dst_addr.scope() as u8) {
candidate = addr;
}
} else if (addr.address().scope() as u8) > (dst_addr.scope() as u8) {
candidate = addr;
}

// Rule 3: avoid deprecated addresses (TODO)
// Rule 4: prefer home addresses (TODO)
// Rule 5: prefer outgoing interfaces (TODO)
// Rule 5.5: prefer addresses in a prefix advertises by the next-hop (TODO).
// Rule 6: prefer matching label (TODO)
// Rule 7: prefer temporary addresses (TODO)
// Rule 8: use longest matching prefix
if common_prefix_length(candidate, dst_addr) < common_prefix_length(addr, dst_addr) {
candidate = addr;
}
}

Some(candidate.address())
}

/// Determine if the given `Ipv6Address` is the solicited node
/// multicast address for a IPv6 addresses assigned to the interface.
/// See [RFC 4291 § 2.7.1] for more details.
///
/// [RFC 4291 § 2.7.1]: https://tools.ietf.org/html/rfc4291#section-2.7.1
pub fn has_solicited_node(&self, addr: Ipv6Address) -> bool {
self.ip_addrs.iter().any(|cidr| {
match *cidr {
IpCidr::Ipv6(cidr) if cidr.address() != Ipv6Address::LOOPBACK => {
// Take the lower order 24 bits of the IPv6 address and
// append those bits to FF02:0:0:0:0:1:FF00::/104.
addr.as_bytes()[14..] == cidr.address().as_bytes()[14..]
}
_ => false,
}
})
}

/// Get the first IPv6 address if present.
pub fn ipv6_addr(&self) -> Option<Ipv6Address> {
self.ip_addrs.iter().find_map(|addr| match *addr {
IpCidr::Ipv6(cidr) => Some(cidr.address()),
#[allow(unreachable_patterns)]
_ => None,
})
}

pub(super) fn process_ipv6<'frame>(
&mut self,
sockets: &mut SocketSet,
Expand Down Expand Up @@ -191,13 +311,16 @@ impl InterfaceInner {
let mut handled_by_icmp_socket = false;

#[cfg(feature = "socket-icmp")]
for icmp_socket in _sockets
.items_mut()
.filter_map(|i| icmp::Socket::downcast_mut(&mut i.socket))
{
if icmp_socket.accepts(self, &ip_repr, &icmp_repr.into()) {
icmp_socket.process(self, &ip_repr, &icmp_repr.into());
handled_by_icmp_socket = true;
use crate::socket::icmp::Socket as IcmpSocket;
for icmp_socket in _sockets
.items_mut()
.filter_map(|i| IcmpSocket::downcast_mut(&mut i.socket))
{
if icmp_socket.accepts(self, &ip_repr, &icmp_repr.into()) {
icmp_socket.process(self, &ip_repr, &icmp_repr.into());
handled_by_icmp_socket = true;
}
}
}

Expand Down
Loading
Loading