1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777 |
- // Heads up! Before working on this file you should read the parts
- // of RFC 1122 that discuss Ethernet, ARP and IP for any IPv4 work
- // and RFCs 8200 and 4861 for any IPv6 and NDISC work.
- #[cfg(test)]
- mod tests;
- #[cfg(feature = "medium-ethernet")]
- mod ethernet;
- #[cfg(feature = "medium-ieee802154")]
- mod ieee802154;
- #[cfg(feature = "proto-ipv4")]
- mod ipv4;
- #[cfg(feature = "proto-ipv6")]
- mod ipv6;
- #[cfg(feature = "proto-sixlowpan")]
- mod sixlowpan;
- #[cfg(feature = "proto-igmp")]
- mod igmp;
- #[cfg(feature = "proto-igmp")]
- pub use igmp::MulticastError;
- use super::packet::*;
- use core::result::Result;
- use heapless::{LinearMap, Vec};
- #[cfg(any(feature = "proto-ipv4", feature = "proto-sixlowpan"))]
- use super::fragmentation::PacketAssemblerSet;
- #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
- use super::neighbor::{Answer as NeighborAnswer, Cache as NeighborCache};
- use super::socket_set::SocketSet;
- use crate::config::{
- FRAGMENTATION_BUFFER_SIZE, IFACE_MAX_ADDR_COUNT, IFACE_MAX_MULTICAST_GROUP_COUNT,
- IFACE_MAX_SIXLOWPAN_ADDRESS_CONTEXT_COUNT,
- };
- use crate::iface::Routes;
- use crate::phy::PacketMeta;
- use crate::phy::{ChecksumCapabilities, Device, DeviceCapabilities, Medium, RxToken, TxToken};
- use crate::rand::Rand;
- #[cfg(feature = "socket-dns")]
- use crate::socket::dns;
- use crate::socket::*;
- use crate::time::{Duration, Instant};
- use crate::wire::*;
- #[cfg(feature = "_proto-fragmentation")]
- #[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Clone, Copy)]
- #[cfg_attr(feature = "defmt", derive(defmt::Format))]
- pub(crate) enum FragKey {
- #[cfg(feature = "proto-ipv4-fragmentation")]
- Ipv4(Ipv4FragKey),
- #[cfg(feature = "proto-sixlowpan-fragmentation")]
- Sixlowpan(SixlowpanFragKey),
- }
- pub(crate) struct FragmentsBuffer {
- #[cfg(feature = "proto-sixlowpan")]
- decompress_buf: [u8; sixlowpan::MAX_DECOMPRESSED_LEN],
- #[cfg(feature = "_proto-fragmentation")]
- pub(crate) assembler: PacketAssemblerSet<FragKey>,
- #[cfg(feature = "_proto-fragmentation")]
- reassembly_timeout: Duration,
- }
- #[cfg(not(feature = "_proto-fragmentation"))]
- pub(crate) struct Fragmenter {}
- #[cfg(not(feature = "_proto-fragmentation"))]
- impl Fragmenter {
- pub(crate) fn new() -> Self {
- Self {}
- }
- }
- #[cfg(feature = "_proto-fragmentation")]
- pub(crate) struct Fragmenter {
- /// The buffer that holds the unfragmented 6LoWPAN packet.
- buffer: [u8; FRAGMENTATION_BUFFER_SIZE],
- /// The size of the packet without the IEEE802.15.4 header and the fragmentation headers.
- packet_len: usize,
- /// The amount of bytes that already have been transmitted.
- sent_bytes: usize,
- #[cfg(feature = "proto-ipv4-fragmentation")]
- ipv4: Ipv4Fragmenter,
- #[cfg(feature = "proto-sixlowpan-fragmentation")]
- sixlowpan: SixlowpanFragmenter,
- }
- #[cfg(feature = "proto-ipv4-fragmentation")]
- pub(crate) struct Ipv4Fragmenter {
- /// The IPv4 representation.
- repr: Ipv4Repr,
- /// The destination hardware address.
- #[cfg(feature = "medium-ethernet")]
- dst_hardware_addr: EthernetAddress,
- /// The offset of the next fragment.
- frag_offset: u16,
- /// The identifier of the stream.
- ident: u16,
- }
- #[cfg(feature = "proto-sixlowpan-fragmentation")]
- pub(crate) struct SixlowpanFragmenter {
- /// The datagram size that is used for the fragmentation headers.
- datagram_size: u16,
- /// The datagram tag that is used for the fragmentation headers.
- datagram_tag: u16,
- datagram_offset: usize,
- /// The size of the FRAG_N packets.
- fragn_size: usize,
- /// The link layer IEEE802.15.4 source address.
- ll_dst_addr: Ieee802154Address,
- /// The link layer IEEE802.15.4 source address.
- ll_src_addr: Ieee802154Address,
- }
- #[cfg(feature = "_proto-fragmentation")]
- impl Fragmenter {
- pub(crate) fn new() -> Self {
- Self {
- buffer: [0u8; FRAGMENTATION_BUFFER_SIZE],
- packet_len: 0,
- sent_bytes: 0,
- #[cfg(feature = "proto-ipv4-fragmentation")]
- ipv4: Ipv4Fragmenter {
- repr: Ipv4Repr {
- src_addr: Ipv4Address::default(),
- dst_addr: Ipv4Address::default(),
- next_header: IpProtocol::Unknown(0),
- payload_len: 0,
- hop_limit: 0,
- },
- #[cfg(feature = "medium-ethernet")]
- dst_hardware_addr: EthernetAddress::default(),
- frag_offset: 0,
- ident: 0,
- },
- #[cfg(feature = "proto-sixlowpan-fragmentation")]
- sixlowpan: SixlowpanFragmenter {
- datagram_size: 0,
- datagram_tag: 0,
- datagram_offset: 0,
- fragn_size: 0,
- ll_dst_addr: Ieee802154Address::Absent,
- ll_src_addr: Ieee802154Address::Absent,
- },
- }
- }
- /// Return `true` when everything is transmitted.
- #[inline]
- fn finished(&self) -> bool {
- self.packet_len == self.sent_bytes
- }
- /// Returns `true` when there is nothing to transmit.
- #[inline]
- fn is_empty(&self) -> bool {
- self.packet_len == 0
- }
- // Reset the buffer.
- fn reset(&mut self) {
- self.packet_len = 0;
- self.sent_bytes = 0;
- #[cfg(feature = "proto-ipv4-fragmentation")]
- {
- self.ipv4.repr = Ipv4Repr {
- src_addr: Ipv4Address::default(),
- dst_addr: Ipv4Address::default(),
- next_header: IpProtocol::Unknown(0),
- payload_len: 0,
- hop_limit: 0,
- };
- #[cfg(feature = "medium-ethernet")]
- {
- self.ipv4.dst_hardware_addr = EthernetAddress::default();
- }
- }
- #[cfg(feature = "proto-sixlowpan-fragmentation")]
- {
- self.sixlowpan.datagram_size = 0;
- self.sixlowpan.datagram_tag = 0;
- self.sixlowpan.fragn_size = 0;
- self.sixlowpan.ll_dst_addr = Ieee802154Address::Absent;
- self.sixlowpan.ll_src_addr = Ieee802154Address::Absent;
- }
- }
- }
- macro_rules! check {
- ($e:expr) => {
- match $e {
- Ok(x) => x,
- Err(_) => {
- // concat!/stringify! doesn't work with defmt macros
- #[cfg(not(feature = "defmt"))]
- net_trace!(concat!("iface: malformed ", stringify!($e)));
- #[cfg(feature = "defmt")]
- net_trace!("iface: malformed");
- return Default::default();
- }
- }
- };
- }
- use check;
- /// A network interface.
- ///
- /// The network interface logically owns a number of other data structures; to avoid
- /// a dependency on heap allocation, it instead owns a `BorrowMut<[T]>`, which can be
- /// a `&mut [T]`, or `Vec<T>` if a heap is available.
- pub struct Interface {
- pub(crate) inner: InterfaceInner,
- fragments: FragmentsBuffer,
- fragmenter: Fragmenter,
- }
- /// The device independent part of an Ethernet network interface.
- ///
- /// Separating the device from the data required for processing and dispatching makes
- /// it possible to borrow them independently. For example, the tx and rx tokens borrow
- /// the `device` mutably until they're used, which makes it impossible to call other
- /// methods on the `Interface` in this time (since its `device` field is borrowed
- /// exclusively). However, it is still possible to call methods on its `inner` field.
- pub struct InterfaceInner {
- caps: DeviceCapabilities,
- now: Instant,
- rand: Rand,
- #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
- neighbor_cache: NeighborCache,
- hardware_addr: HardwareAddress,
- #[cfg(feature = "medium-ieee802154")]
- sequence_no: u8,
- #[cfg(feature = "medium-ieee802154")]
- pan_id: Option<Ieee802154Pan>,
- #[cfg(feature = "proto-ipv4-fragmentation")]
- ipv4_id: u16,
- #[cfg(feature = "proto-sixlowpan")]
- sixlowpan_address_context:
- Vec<SixlowpanAddressContext, IFACE_MAX_SIXLOWPAN_ADDRESS_CONTEXT_COUNT>,
- #[cfg(feature = "proto-sixlowpan-fragmentation")]
- tag: u16,
- ip_addrs: Vec<IpCidr, IFACE_MAX_ADDR_COUNT>,
- #[cfg(feature = "proto-ipv4")]
- any_ip: bool,
- routes: Routes,
- #[cfg(feature = "proto-igmp")]
- ipv4_multicast_groups: LinearMap<Ipv4Address, (), IFACE_MAX_MULTICAST_GROUP_COUNT>,
- /// When to report for (all or) the next multicast group membership via IGMP
- #[cfg(feature = "proto-igmp")]
- igmp_report_state: IgmpReportState,
- }
- /// Configuration structure used for creating a network interface.
- #[non_exhaustive]
- pub struct Config {
- /// Random seed.
- ///
- /// It is strongly recommended that the random seed is different on each boot,
- /// to avoid problems with TCP port/sequence collisions.
- ///
- /// The seed doesn't have to be cryptographically secure.
- pub random_seed: u64,
- /// Set the Hardware address the interface will use.
- ///
- /// # Panics
- /// Creating the interface panics if the address is not unicast.
- pub hardware_addr: HardwareAddress,
- /// Set the IEEE802.15.4 PAN ID the interface will use.
- ///
- /// **NOTE**: we use the same PAN ID for destination and source.
- #[cfg(feature = "medium-ieee802154")]
- pub pan_id: Option<Ieee802154Pan>,
- }
- impl Config {
- pub fn new(hardware_addr: HardwareAddress) -> Self {
- Config {
- random_seed: 0,
- hardware_addr,
- #[cfg(feature = "medium-ieee802154")]
- pan_id: None,
- }
- }
- }
- impl Interface {
- /// Create a network interface using the previously provided configuration.
- ///
- /// # Panics
- /// This function panics if the [`Config::hardware_address`] does not match
- /// the medium of the device.
- pub fn new<D>(config: Config, device: &mut D, now: Instant) -> Self
- where
- D: Device + ?Sized,
- {
- let caps = device.capabilities();
- assert_eq!(
- config.hardware_addr.medium(),
- caps.medium,
- "The hardware address does not match the medium of the interface."
- );
- let mut rand = Rand::new(config.random_seed);
- #[cfg(feature = "medium-ieee802154")]
- let mut sequence_no;
- #[cfg(feature = "medium-ieee802154")]
- loop {
- sequence_no = (rand.rand_u32() & 0xff) as u8;
- if sequence_no != 0 {
- break;
- }
- }
- #[cfg(feature = "proto-sixlowpan")]
- let mut tag;
- #[cfg(feature = "proto-sixlowpan")]
- loop {
- tag = rand.rand_u16();
- if tag != 0 {
- break;
- }
- }
- #[cfg(feature = "proto-ipv4")]
- let mut ipv4_id;
- #[cfg(feature = "proto-ipv4")]
- loop {
- ipv4_id = rand.rand_u16();
- if ipv4_id != 0 {
- break;
- }
- }
- Interface {
- fragments: FragmentsBuffer {
- #[cfg(feature = "proto-sixlowpan")]
- decompress_buf: [0u8; sixlowpan::MAX_DECOMPRESSED_LEN],
- #[cfg(feature = "_proto-fragmentation")]
- assembler: PacketAssemblerSet::new(),
- #[cfg(feature = "_proto-fragmentation")]
- reassembly_timeout: Duration::from_secs(60),
- },
- fragmenter: Fragmenter::new(),
- inner: InterfaceInner {
- now,
- caps,
- hardware_addr: config.hardware_addr,
- ip_addrs: Vec::new(),
- #[cfg(feature = "proto-ipv4")]
- any_ip: false,
- routes: Routes::new(),
- #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
- neighbor_cache: NeighborCache::new(),
- #[cfg(feature = "proto-igmp")]
- ipv4_multicast_groups: LinearMap::new(),
- #[cfg(feature = "proto-igmp")]
- igmp_report_state: IgmpReportState::Inactive,
- #[cfg(feature = "medium-ieee802154")]
- sequence_no,
- #[cfg(feature = "medium-ieee802154")]
- pan_id: config.pan_id,
- #[cfg(feature = "proto-sixlowpan-fragmentation")]
- tag,
- #[cfg(feature = "proto-ipv4-fragmentation")]
- ipv4_id,
- #[cfg(feature = "proto-sixlowpan")]
- sixlowpan_address_context: Vec::new(),
- rand,
- },
- }
- }
- /// Get the socket context.
- ///
- /// The context is needed for some socket methods.
- pub fn context(&mut self) -> &mut InterfaceInner {
- &mut self.inner
- }
- /// Get the HardwareAddress address of the interface.
- ///
- /// # Panics
- /// This function panics if the medium is not Ethernet or Ieee802154.
- #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
- pub fn hardware_addr(&self) -> HardwareAddress {
- #[cfg(all(feature = "medium-ethernet", not(feature = "medium-ieee802154")))]
- assert!(self.inner.caps.medium == Medium::Ethernet);
- #[cfg(all(feature = "medium-ieee802154", not(feature = "medium-ethernet")))]
- assert!(self.inner.caps.medium == Medium::Ieee802154);
- #[cfg(all(feature = "medium-ieee802154", feature = "medium-ethernet"))]
- assert!(
- self.inner.caps.medium == Medium::Ethernet
- || self.inner.caps.medium == Medium::Ieee802154
- );
- self.inner.hardware_addr
- }
- /// Set the HardwareAddress address of the interface.
- ///
- /// # Panics
- /// This function panics if the address is not unicast, and if the medium is not Ethernet or
- /// Ieee802154.
- #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
- pub fn set_hardware_addr(&mut self, addr: HardwareAddress) {
- #[cfg(all(feature = "medium-ethernet", not(feature = "medium-ieee802154")))]
- assert!(self.inner.caps.medium == Medium::Ethernet);
- #[cfg(all(feature = "medium-ieee802154", not(feature = "medium-ethernet")))]
- assert!(self.inner.caps.medium == Medium::Ieee802154);
- #[cfg(all(feature = "medium-ieee802154", feature = "medium-ethernet"))]
- assert!(
- self.inner.caps.medium == Medium::Ethernet
- || self.inner.caps.medium == Medium::Ieee802154
- );
- InterfaceInner::check_hardware_addr(&addr);
- self.inner.hardware_addr = addr;
- }
- /// Get the IP addresses of the interface.
- pub fn ip_addrs(&self) -> &[IpCidr] {
- self.inner.ip_addrs.as_ref()
- }
- /// Get the first IPv4 address if present.
- #[cfg(feature = "proto-ipv4")]
- pub fn ipv4_addr(&self) -> Option<Ipv4Address> {
- self.inner.ipv4_addr()
- }
- /// Get the first IPv6 address if present.
- #[cfg(feature = "proto-ipv6")]
- pub fn ipv6_addr(&self) -> Option<Ipv6Address> {
- self.inner.ipv6_addr()
- }
- /// Get an address from the interface that could be used as source address. For IPv4, this is
- /// the first IPv4 address from the list of addresses. For IPv6, the address is based on the
- /// destination address and uses RFC6724 for selecting the source address.
- pub fn get_source_address(&self, dst_addr: &IpAddress) -> Option<IpAddress> {
- self.inner.get_source_address(dst_addr)
- }
- /// Get an address from the interface that could be used as source address. This is the first
- /// IPv4 address from the list of addresses in the interface.
- #[cfg(feature = "proto-ipv4")]
- pub fn get_source_address_ipv4(&self, dst_addr: &Ipv4Address) -> Option<Ipv4Address> {
- self.inner.get_source_address_ipv4(dst_addr)
- }
- /// Get an address from the interface that could be used as source address. The selection is
- /// based on RFC6724.
- #[cfg(feature = "proto-ipv6")]
- pub fn get_source_address_ipv6(&self, dst_addr: &Ipv6Address) -> Option<Ipv6Address> {
- self.inner.get_source_address_ipv6(dst_addr)
- }
- /// Update the IP addresses of the interface.
- ///
- /// # Panics
- /// This function panics if any of the addresses are not unicast.
- pub fn update_ip_addrs<F: FnOnce(&mut Vec<IpCidr, IFACE_MAX_ADDR_COUNT>)>(&mut self, f: F) {
- f(&mut self.inner.ip_addrs);
- InterfaceInner::flush_cache(&mut self.inner);
- InterfaceInner::check_ip_addrs(&self.inner.ip_addrs)
- }
- /// Check whether the interface has the given IP address assigned.
- pub fn has_ip_addr<T: Into<IpAddress>>(&self, addr: T) -> bool {
- self.inner.has_ip_addr(addr)
- }
- pub fn routes(&self) -> &Routes {
- &self.inner.routes
- }
- pub fn routes_mut(&mut self) -> &mut Routes {
- &mut self.inner.routes
- }
- /// Enable or disable the AnyIP capability.
- ///
- /// AnyIP allowins packets to be received
- /// locally on IPv4 addresses other than the interface's configured [ip_addrs].
- /// When AnyIP is enabled and a route prefix in [`routes`](Self::routes) specifies one of
- /// the interface's [`ip_addrs`](Self::ip_addrs) as its gateway, the interface will accept
- /// packets addressed to that prefix.
- ///
- /// # IPv6
- ///
- /// This option is not available or required for IPv6 as packets sent to
- /// the interface are not filtered by IPv6 address.
- #[cfg(feature = "proto-ipv4")]
- pub fn set_any_ip(&mut self, any_ip: bool) {
- self.inner.any_ip = any_ip;
- }
- /// Get whether AnyIP is enabled.
- ///
- /// See [`set_any_ip`](Self::set_any_ip) for details on AnyIP
- #[cfg(feature = "proto-ipv4")]
- pub fn any_ip(&self) -> bool {
- self.inner.any_ip
- }
- /// Get the 6LoWPAN address contexts.
- #[cfg(feature = "proto-sixlowpan")]
- pub fn sixlowpan_address_context(
- &self,
- ) -> &Vec<SixlowpanAddressContext, IFACE_MAX_SIXLOWPAN_ADDRESS_CONTEXT_COUNT> {
- &self.inner.sixlowpan_address_context
- }
- /// Get a mutable reference to the 6LoWPAN address contexts.
- #[cfg(feature = "proto-sixlowpan")]
- pub fn sixlowpan_address_context_mut(
- &mut self,
- ) -> &mut Vec<SixlowpanAddressContext, IFACE_MAX_SIXLOWPAN_ADDRESS_CONTEXT_COUNT> {
- &mut self.inner.sixlowpan_address_context
- }
- /// Get the packet reassembly timeout.
- #[cfg(feature = "_proto-fragmentation")]
- pub fn reassembly_timeout(&self) -> Duration {
- self.fragments.reassembly_timeout
- }
- /// Set the packet reassembly timeout.
- #[cfg(feature = "_proto-fragmentation")]
- pub fn set_reassembly_timeout(&mut self, timeout: Duration) {
- if timeout > Duration::from_secs(60) {
- net_debug!("RFC 4944 specifies that the reassembly timeout MUST be set to a maximum of 60 seconds");
- }
- self.fragments.reassembly_timeout = timeout;
- }
- /// Transmit packets queued in the given sockets, and receive packets queued
- /// in the device.
- ///
- /// 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.
- pub fn poll<D>(
- &mut self,
- timestamp: Instant,
- device: &mut D,
- sockets: &mut SocketSet<'_>,
- ) -> bool
- where
- D: Device + ?Sized,
- {
- self.inner.now = timestamp;
- #[cfg(feature = "_proto-fragmentation")]
- self.fragments.assembler.remove_expired(timestamp);
- match self.inner.caps.medium {
- #[cfg(feature = "medium-ieee802154")]
- Medium::Ieee802154 =>
- {
- #[cfg(feature = "proto-sixlowpan-fragmentation")]
- if self.sixlowpan_egress(device) {
- return true;
- }
- }
- #[cfg(any(feature = "medium-ethernet", feature = "medium-ip"))]
- _ =>
- {
- #[cfg(feature = "proto-ipv4-fragmentation")]
- if self.ipv4_egress(device) {
- return true;
- }
- }
- }
- let mut readiness_may_have_changed = false;
- loop {
- let mut did_something = false;
- did_something |= self.socket_ingress(device, sockets);
- did_something |= self.socket_egress(device, sockets);
- #[cfg(feature = "proto-igmp")]
- {
- did_something |= self.igmp_egress(device);
- }
- if did_something {
- readiness_may_have_changed = true;
- } else {
- break;
- }
- }
- readiness_may_have_changed
- }
- /// Return a _soft deadline_ for calling [poll] the next time.
- /// The [Instant] returned is the time at which you should call [poll] next.
- /// It is harmless (but wastes energy) to call it before the [Instant], and
- /// potentially harmful (impacting quality of service) to call it after the
- /// [Instant]
- ///
- /// [poll]: #method.poll
- /// [Instant]: struct.Instant.html
- pub fn poll_at(&mut self, timestamp: Instant, sockets: &SocketSet<'_>) -> Option<Instant> {
- self.inner.now = timestamp;
- #[cfg(feature = "_proto-fragmentation")]
- if !self.fragmenter.is_empty() {
- return Some(Instant::from_millis(0));
- }
- let inner = &mut self.inner;
- sockets
- .items()
- .filter_map(move |item| {
- let socket_poll_at = item.socket.poll_at(inner);
- match item
- .meta
- .poll_at(socket_poll_at, |ip_addr| inner.has_neighbor(&ip_addr))
- {
- PollAt::Ingress => None,
- PollAt::Time(instant) => Some(instant),
- PollAt::Now => Some(Instant::from_millis(0)),
- }
- })
- .min()
- }
- /// Return an _advisory wait time_ for calling [poll] the next time.
- /// The [Duration] returned is the time left to wait before calling [poll] next.
- /// It is harmless (but wastes energy) to call it before the [Duration] has passed,
- /// and potentially harmful (impacting quality of service) to call it after the
- /// [Duration] has passed.
- ///
- /// [poll]: #method.poll
- /// [Duration]: struct.Duration.html
- pub fn poll_delay(&mut self, timestamp: Instant, sockets: &SocketSet<'_>) -> Option<Duration> {
- match self.poll_at(timestamp, sockets) {
- Some(poll_at) if timestamp < poll_at => Some(poll_at - timestamp),
- Some(_) => Some(Duration::from_millis(0)),
- _ => None,
- }
- }
- fn socket_ingress<D>(&mut self, device: &mut D, sockets: &mut SocketSet<'_>) -> bool
- where
- D: Device + ?Sized,
- {
- let mut processed_any = false;
- while let Some((rx_token, tx_token)) = device.receive(self.inner.now) {
- let rx_meta = rx_token.meta();
- rx_token.consume(|frame| {
- if frame.is_empty() {
- return;
- }
- match self.inner.caps.medium {
- #[cfg(feature = "medium-ethernet")]
- Medium::Ethernet => {
- if let Some(packet) = self.inner.process_ethernet(
- sockets,
- rx_meta,
- frame,
- &mut self.fragments,
- ) {
- if let Err(err) =
- self.inner.dispatch(tx_token, packet, &mut self.fragmenter)
- {
- net_debug!("Failed to send response: {:?}", err);
- }
- }
- }
- #[cfg(feature = "medium-ip")]
- Medium::Ip => {
- if let Some(packet) =
- self.inner
- .process_ip(sockets, rx_meta, frame, &mut self.fragments)
- {
- if let Err(err) = self.inner.dispatch_ip(
- tx_token,
- PacketMeta::default(),
- packet,
- &mut self.fragmenter,
- ) {
- net_debug!("Failed to send response: {:?}", err);
- }
- }
- }
- #[cfg(feature = "medium-ieee802154")]
- Medium::Ieee802154 => {
- if let Some(packet) = self.inner.process_ieee802154(
- sockets,
- rx_meta,
- frame,
- &mut self.fragments,
- ) {
- if let Err(err) = self.inner.dispatch_ip(
- tx_token,
- PacketMeta::default(),
- packet,
- &mut self.fragmenter,
- ) {
- net_debug!("Failed to send response: {:?}", err);
- }
- }
- }
- }
- processed_any = true;
- });
- }
- processed_any
- }
- fn socket_egress<D>(&mut self, device: &mut D, sockets: &mut SocketSet<'_>) -> bool
- where
- D: Device + ?Sized,
- {
- let _caps = device.capabilities();
- enum EgressError {
- Exhausted,
- Dispatch(DispatchError),
- }
- let mut emitted_any = false;
- for item in sockets.items_mut() {
- if !item
- .meta
- .egress_permitted(self.inner.now, |ip_addr| self.inner.has_neighbor(&ip_addr))
- {
- continue;
- }
- let mut neighbor_addr = None;
- let mut respond = |inner: &mut InterfaceInner, meta: PacketMeta, response: Packet| {
- neighbor_addr = Some(response.ip_repr().dst_addr());
- let t = device.transmit(inner.now).ok_or_else(|| {
- net_debug!("failed to transmit IP: device exhausted");
- EgressError::Exhausted
- })?;
- inner
- .dispatch_ip(t, meta, response, &mut self.fragmenter)
- .map_err(EgressError::Dispatch)?;
- emitted_any = true;
- Ok(())
- };
- let result = match &mut item.socket {
- #[cfg(feature = "socket-raw")]
- Socket::Raw(socket) => socket.dispatch(&mut self.inner, |inner, (ip, raw)| {
- respond(
- inner,
- PacketMeta::default(),
- Packet::new(ip, IpPayload::Raw(raw)),
- )
- }),
- #[cfg(feature = "socket-icmp")]
- Socket::Icmp(socket) => {
- socket.dispatch(&mut self.inner, |inner, response| match response {
- #[cfg(feature = "proto-ipv4")]
- (IpRepr::Ipv4(ipv4_repr), IcmpRepr::Ipv4(icmpv4_repr)) => respond(
- inner,
- PacketMeta::default(),
- Packet::new_ipv4(ipv4_repr, IpPayload::Icmpv4(icmpv4_repr)),
- ),
- #[cfg(feature = "proto-ipv6")]
- (IpRepr::Ipv6(ipv6_repr), IcmpRepr::Ipv6(icmpv6_repr)) => respond(
- inner,
- PacketMeta::default(),
- Packet::new_ipv6(ipv6_repr, IpPayload::Icmpv6(icmpv6_repr)),
- ),
- #[allow(unreachable_patterns)]
- _ => unreachable!(),
- })
- }
- #[cfg(feature = "socket-udp")]
- Socket::Udp(socket) => {
- socket.dispatch(&mut self.inner, |inner, meta, (ip, udp, payload)| {
- respond(inner, meta, Packet::new(ip, IpPayload::Udp(udp, payload)))
- })
- }
- #[cfg(feature = "socket-tcp")]
- Socket::Tcp(socket) => socket.dispatch(&mut self.inner, |inner, (ip, tcp)| {
- respond(
- inner,
- PacketMeta::default(),
- Packet::new(ip, IpPayload::Tcp(tcp)),
- )
- }),
- #[cfg(feature = "socket-dhcpv4")]
- Socket::Dhcpv4(socket) => {
- socket.dispatch(&mut self.inner, |inner, (ip, udp, dhcp)| {
- respond(
- inner,
- PacketMeta::default(),
- Packet::new_ipv4(ip, IpPayload::Dhcpv4(udp, dhcp)),
- )
- })
- }
- #[cfg(feature = "socket-dns")]
- Socket::Dns(socket) => socket.dispatch(&mut self.inner, |inner, (ip, udp, dns)| {
- respond(
- inner,
- PacketMeta::default(),
- Packet::new(ip, IpPayload::Udp(udp, dns)),
- )
- }),
- };
- match result {
- Err(EgressError::Exhausted) => break, // Device buffer full.
- Err(EgressError::Dispatch(_)) => {
- // `NeighborCache` already takes care of rate limiting the neighbor discovery
- // requests from the socket. However, without an additional rate limiting
- // mechanism, we would spin on every socket that has yet to discover its
- // neighbor.
- item.meta.neighbor_missing(
- self.inner.now,
- neighbor_addr.expect("non-IP response packet"),
- );
- }
- Ok(()) => {}
- }
- }
- emitted_any
- }
- /// 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")]
- 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;
- }
- 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
- }
- /// Process fragments that still need to be sent for 6LoWPAN 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-sixlowpan-fragmentation")]
- fn sixlowpan_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;
- }
- let pkt = &self.fragmenter;
- if pkt.packet_len > pkt.sent_bytes {
- if let Some(tx_token) = device.transmit(self.inner.now) {
- self.inner
- .dispatch_ieee802154_frag(tx_token, &mut self.fragmenter);
- return true;
- }
- }
- false
- }
- }
- impl InterfaceInner {
- #[allow(unused)] // unused depending on which sockets are enabled
- pub(crate) fn now(&self) -> Instant {
- self.now
- }
- #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
- #[allow(unused)] // unused depending on which sockets are enabled
- pub(crate) fn hardware_addr(&self) -> HardwareAddress {
- self.hardware_addr
- }
- #[allow(unused)] // unused depending on which sockets are enabled
- pub(crate) fn checksum_caps(&self) -> ChecksumCapabilities {
- self.caps.checksum.clone()
- }
- #[allow(unused)] // unused depending on which sockets are enabled
- pub(crate) fn ip_mtu(&self) -> usize {
- self.caps.ip_mtu()
- }
- #[allow(unused)] // unused depending on which sockets are enabled, and in tests
- pub(crate) fn rand(&mut self) -> &mut Rand {
- &mut self.rand
- }
- #[allow(unused)] // unused depending on which sockets are enabled
- pub(crate) fn get_source_address(&self, dst_addr: &IpAddress) -> Option<IpAddress> {
- match dst_addr {
- #[cfg(feature = "proto-ipv4")]
- IpAddress::Ipv4(addr) => self.get_source_address_ipv4(addr).map(|a| a.into()),
- #[cfg(feature = "proto-ipv6")]
- IpAddress::Ipv6(addr) => self.get_source_address_ipv6(addr).map(|a| a.into()),
- }
- }
- #[cfg(feature = "proto-ipv4")]
- #[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
- }
- #[cfg(feature = "proto-ipv6")]
- #[allow(unused)]
- pub(crate) fn get_source_address_ipv6(&self, dst_addr: &Ipv6Address) -> Option<Ipv6Address> {
- // RFC 6724 describes how to select the correct source address depending on the destination
- // address.
- // 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())
- }
- #[cfg(test)]
- #[allow(unused)] // unused depending on which sockets are enabled
- pub(crate) fn set_now(&mut self, now: Instant) {
- self.now = now
- }
- #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
- fn check_hardware_addr(addr: &HardwareAddress) {
- if !addr.is_unicast() {
- panic!("Hardware address {addr} is not unicast")
- }
- }
- fn check_ip_addrs(addrs: &[IpCidr]) {
- for cidr in addrs {
- if !cidr.address().is_unicast() && !cidr.address().is_unspecified() {
- panic!("IP address {} is not unicast", cidr.address())
- }
- }
- }
- #[cfg(feature = "medium-ieee802154")]
- fn get_sequence_number(&mut self) -> u8 {
- let no = self.sequence_no;
- self.sequence_no = self.sequence_no.wrapping_add(1);
- no
- }
- #[cfg(feature = "proto-ipv4-fragmentation")]
- fn get_ipv4_ident(&mut self) -> u16 {
- let ipv4_id = self.ipv4_id;
- self.ipv4_id = self.ipv4_id.wrapping_add(1);
- ipv4_id
- }
- #[cfg(feature = "proto-sixlowpan-fragmentation")]
- fn get_sixlowpan_fragment_tag(&mut self) -> u16 {
- let tag = self.tag;
- self.tag = self.tag.wrapping_add(1);
- tag
- }
- /// 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
- #[cfg(feature = "proto-ipv6")]
- 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,
- }
- })
- }
- /// Check whether the interface has the given IP address assigned.
- fn has_ip_addr<T: Into<IpAddress>>(&self, addr: T) -> bool {
- let addr = addr.into();
- self.ip_addrs.iter().any(|probe| probe.address() == addr)
- }
- /// Get the first IPv4 address of the interface.
- #[cfg(feature = "proto-ipv4")]
- 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,
- })
- }
- /// Get the first IPv6 address if present.
- #[cfg(feature = "proto-ipv6")]
- 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,
- })
- }
- #[cfg(not(feature = "proto-igmp"))]
- fn has_multicast_group<T: Into<IpAddress>>(&self, addr: T) -> bool {
- false
- }
- #[cfg(feature = "medium-ip")]
- fn process_ip<'frame>(
- &mut self,
- sockets: &mut SocketSet,
- meta: PacketMeta,
- ip_payload: &'frame [u8],
- frag: &'frame mut FragmentsBuffer,
- ) -> Option<Packet<'frame>> {
- match IpVersion::of_packet(ip_payload) {
- #[cfg(feature = "proto-ipv4")]
- Ok(IpVersion::Ipv4) => {
- let ipv4_packet = check!(Ipv4Packet::new_checked(ip_payload));
- self.process_ipv4(sockets, meta, &ipv4_packet, frag)
- }
- #[cfg(feature = "proto-ipv6")]
- Ok(IpVersion::Ipv6) => {
- let ipv6_packet = check!(Ipv6Packet::new_checked(ip_payload));
- self.process_ipv6(sockets, meta, &ipv6_packet)
- }
- // Drop all other traffic.
- _ => None,
- }
- }
- #[cfg(feature = "socket-raw")]
- fn raw_socket_filter(
- &mut self,
- sockets: &mut SocketSet,
- ip_repr: &IpRepr,
- ip_payload: &[u8],
- ) -> bool {
- let mut handled_by_raw_socket = false;
- // Pass every IP packet to all raw sockets we have registered.
- for raw_socket in sockets
- .items_mut()
- .filter_map(|i| raw::Socket::downcast_mut(&mut i.socket))
- {
- if raw_socket.accepts(ip_repr) {
- raw_socket.process(self, ip_repr, ip_payload);
- handled_by_raw_socket = true;
- }
- }
- handled_by_raw_socket
- }
- /// Checks if an address is broadcast, taking into account ipv4 subnet-local
- /// broadcast addresses.
- pub(crate) fn is_broadcast(&self, address: &IpAddress) -> bool {
- match address {
- #[cfg(feature = "proto-ipv4")]
- IpAddress::Ipv4(address) => self.is_broadcast_v4(*address),
- #[cfg(feature = "proto-ipv6")]
- IpAddress::Ipv6(_) => false,
- }
- }
- /// Checks if an address is broadcast, taking into account ipv4 subnet-local
- /// broadcast addresses.
- #[cfg(feature = "proto-ipv4")]
- 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
- #[cfg(feature = "proto-ipv4")]
- fn is_unicast_v4(&self, address: Ipv4Address) -> bool {
- address.is_unicast() && !self.is_broadcast_v4(address)
- }
- #[cfg(any(feature = "socket-udp", feature = "socket-dns"))]
- #[allow(clippy::too_many_arguments)]
- fn process_udp<'frame>(
- &mut self,
- sockets: &mut SocketSet,
- meta: PacketMeta,
- ip_repr: IpRepr,
- udp_repr: UdpRepr,
- handled_by_raw_socket: bool,
- udp_payload: &'frame [u8],
- ip_payload: &'frame [u8],
- ) -> Option<Packet<'frame>> {
- #[cfg(feature = "socket-udp")]
- for udp_socket in sockets
- .items_mut()
- .filter_map(|i| udp::Socket::downcast_mut(&mut i.socket))
- {
- if udp_socket.accepts(self, &ip_repr, &udp_repr) {
- udp_socket.process(self, meta, &ip_repr, &udp_repr, udp_payload);
- return None;
- }
- }
- #[cfg(feature = "socket-dns")]
- for dns_socket in sockets
- .items_mut()
- .filter_map(|i| dns::Socket::downcast_mut(&mut i.socket))
- {
- if dns_socket.accepts(&ip_repr, &udp_repr) {
- dns_socket.process(self, &ip_repr, &udp_repr, udp_payload);
- return None;
- }
- }
- // The packet wasn't handled by a socket, send an ICMP port unreachable packet.
- match ip_repr {
- #[cfg(feature = "proto-ipv4")]
- IpRepr::Ipv4(_) if handled_by_raw_socket => None,
- #[cfg(feature = "proto-ipv6")]
- IpRepr::Ipv6(_) if handled_by_raw_socket => None,
- #[cfg(feature = "proto-ipv4")]
- IpRepr::Ipv4(ipv4_repr) => {
- let payload_len =
- icmp_reply_payload_len(ip_payload.len(), IPV4_MIN_MTU, ipv4_repr.buffer_len());
- let icmpv4_reply_repr = Icmpv4Repr::DstUnreachable {
- reason: Icmpv4DstUnreachable::PortUnreachable,
- header: ipv4_repr,
- data: &ip_payload[0..payload_len],
- };
- self.icmpv4_reply(ipv4_repr, icmpv4_reply_repr)
- }
- #[cfg(feature = "proto-ipv6")]
- IpRepr::Ipv6(ipv6_repr) => {
- let payload_len =
- icmp_reply_payload_len(ip_payload.len(), IPV6_MIN_MTU, ipv6_repr.buffer_len());
- let icmpv6_reply_repr = Icmpv6Repr::DstUnreachable {
- reason: Icmpv6DstUnreachable::PortUnreachable,
- header: ipv6_repr,
- data: &ip_payload[0..payload_len],
- };
- self.icmpv6_reply(ipv6_repr, icmpv6_reply_repr)
- }
- }
- }
- #[cfg(feature = "socket-tcp")]
- pub(crate) fn process_tcp<'frame>(
- &mut self,
- sockets: &mut SocketSet,
- ip_repr: IpRepr,
- ip_payload: &'frame [u8],
- ) -> Option<Packet<'frame>> {
- let (src_addr, dst_addr) = (ip_repr.src_addr(), ip_repr.dst_addr());
- let tcp_packet = check!(TcpPacket::new_checked(ip_payload));
- let tcp_repr = check!(TcpRepr::parse(
- &tcp_packet,
- &src_addr,
- &dst_addr,
- &self.caps.checksum
- ));
- for tcp_socket in sockets
- .items_mut()
- .filter_map(|i| tcp::Socket::downcast_mut(&mut i.socket))
- {
- if tcp_socket.accepts(self, &ip_repr, &tcp_repr) {
- return tcp_socket
- .process(self, &ip_repr, &tcp_repr)
- .map(|(ip, tcp)| Packet::new(ip, IpPayload::Tcp(tcp)));
- }
- }
- if tcp_repr.control == TcpControl::Rst
- || ip_repr.dst_addr().is_unspecified()
- || ip_repr.src_addr().is_unspecified()
- {
- // Never reply to a TCP RST packet with another TCP RST packet. We also never want to
- // send a TCP RST packet with unspecified addresses.
- None
- } else {
- // The packet wasn't handled by a socket, send a TCP RST packet.
- let (ip, tcp) = tcp::Socket::rst_reply(&ip_repr, &tcp_repr);
- Some(Packet::new(ip, IpPayload::Tcp(tcp)))
- }
- }
- #[cfg(feature = "medium-ethernet")]
- fn dispatch<Tx>(
- &mut self,
- tx_token: Tx,
- packet: EthernetPacket,
- frag: &mut Fragmenter,
- ) -> Result<(), DispatchError>
- where
- Tx: TxToken,
- {
- match packet {
- #[cfg(feature = "proto-ipv4")]
- EthernetPacket::Arp(arp_repr) => {
- let dst_hardware_addr = match arp_repr {
- ArpRepr::EthernetIpv4 {
- target_hardware_addr,
- ..
- } => target_hardware_addr,
- };
- self.dispatch_ethernet(tx_token, arp_repr.buffer_len(), |mut frame| {
- frame.set_dst_addr(dst_hardware_addr);
- frame.set_ethertype(EthernetProtocol::Arp);
- let mut packet = ArpPacket::new_unchecked(frame.payload_mut());
- arp_repr.emit(&mut packet);
- })
- }
- EthernetPacket::Ip(packet) => {
- self.dispatch_ip(tx_token, PacketMeta::default(), packet, frag)
- }
- }
- }
- fn in_same_network(&self, addr: &IpAddress) -> bool {
- self.ip_addrs.iter().any(|cidr| cidr.contains_addr(addr))
- }
- fn route(&self, addr: &IpAddress, timestamp: Instant) -> Option<IpAddress> {
- // Send directly.
- // note: no need to use `self.is_broadcast()` to check for subnet-local broadcast addrs
- // here because `in_same_network` will already return true.
- if self.in_same_network(addr) || addr.is_broadcast() {
- return Some(*addr);
- }
- // Route via a router.
- self.routes.lookup(addr, timestamp)
- }
- fn has_neighbor(&self, addr: &IpAddress) -> bool {
- match self.route(addr, self.now) {
- Some(_routed_addr) => match self.caps.medium {
- #[cfg(feature = "medium-ethernet")]
- Medium::Ethernet => self.neighbor_cache.lookup(&_routed_addr, self.now).found(),
- #[cfg(feature = "medium-ieee802154")]
- Medium::Ieee802154 => self.neighbor_cache.lookup(&_routed_addr, self.now).found(),
- #[cfg(feature = "medium-ip")]
- Medium::Ip => true,
- },
- None => false,
- }
- }
- #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
- fn lookup_hardware_addr<Tx>(
- &mut self,
- tx_token: Tx,
- src_addr: &IpAddress,
- dst_addr: &IpAddress,
- fragmenter: &mut Fragmenter,
- ) -> Result<(HardwareAddress, Tx), DispatchError>
- where
- Tx: TxToken,
- {
- if self.is_broadcast(dst_addr) {
- let hardware_addr = match self.caps.medium {
- #[cfg(feature = "medium-ethernet")]
- Medium::Ethernet => HardwareAddress::Ethernet(EthernetAddress::BROADCAST),
- #[cfg(feature = "medium-ieee802154")]
- Medium::Ieee802154 => HardwareAddress::Ieee802154(Ieee802154Address::BROADCAST),
- #[cfg(feature = "medium-ip")]
- Medium::Ip => unreachable!(),
- };
- return Ok((hardware_addr, tx_token));
- }
- if dst_addr.is_multicast() {
- let b = dst_addr.as_bytes();
- let hardware_addr = match *dst_addr {
- #[cfg(feature = "proto-ipv4")]
- IpAddress::Ipv4(_addr) => match self.caps.medium {
- #[cfg(feature = "medium-ethernet")]
- Medium::Ethernet => HardwareAddress::Ethernet(EthernetAddress::from_bytes(&[
- 0x01,
- 0x00,
- 0x5e,
- b[1] & 0x7F,
- b[2],
- b[3],
- ])),
- #[cfg(feature = "medium-ieee802154")]
- Medium::Ieee802154 => unreachable!(),
- #[cfg(feature = "medium-ip")]
- Medium::Ip => unreachable!(),
- },
- #[cfg(feature = "proto-ipv6")]
- IpAddress::Ipv6(_addr) => match self.caps.medium {
- #[cfg(feature = "medium-ethernet")]
- Medium::Ethernet => HardwareAddress::Ethernet(EthernetAddress::from_bytes(&[
- 0x33, 0x33, b[12], b[13], b[14], b[15],
- ])),
- #[cfg(feature = "medium-ieee802154")]
- Medium::Ieee802154 => {
- // Not sure if this is correct
- HardwareAddress::Ieee802154(Ieee802154Address::BROADCAST)
- }
- #[cfg(feature = "medium-ip")]
- Medium::Ip => unreachable!(),
- },
- };
- return Ok((hardware_addr, tx_token));
- }
- let dst_addr = self
- .route(dst_addr, self.now)
- .ok_or(DispatchError::NoRoute)?;
- match self.neighbor_cache.lookup(&dst_addr, self.now) {
- NeighborAnswer::Found(hardware_addr) => return Ok((hardware_addr, tx_token)),
- NeighborAnswer::RateLimited => return Err(DispatchError::NeighborPending),
- _ => (), // XXX
- }
- match (src_addr, dst_addr) {
- #[cfg(all(feature = "medium-ethernet", feature = "proto-ipv4"))]
- (&IpAddress::Ipv4(src_addr), IpAddress::Ipv4(dst_addr))
- if matches!(self.caps.medium, Medium::Ethernet) =>
- {
- net_debug!(
- "address {} not in neighbor cache, sending ARP request",
- dst_addr
- );
- let src_hardware_addr = self.hardware_addr.ethernet_or_panic();
- let arp_repr = ArpRepr::EthernetIpv4 {
- operation: ArpOperation::Request,
- source_hardware_addr: src_hardware_addr,
- source_protocol_addr: src_addr,
- target_hardware_addr: EthernetAddress::BROADCAST,
- target_protocol_addr: dst_addr,
- };
- if let Err(e) =
- self.dispatch_ethernet(tx_token, arp_repr.buffer_len(), |mut frame| {
- frame.set_dst_addr(EthernetAddress::BROADCAST);
- frame.set_ethertype(EthernetProtocol::Arp);
- arp_repr.emit(&mut ArpPacket::new_unchecked(frame.payload_mut()))
- })
- {
- net_debug!("Failed to dispatch ARP request: {:?}", e);
- return Err(DispatchError::NeighborPending);
- }
- }
- #[cfg(feature = "proto-ipv6")]
- (&IpAddress::Ipv6(src_addr), IpAddress::Ipv6(dst_addr)) => {
- net_debug!(
- "address {} not in neighbor cache, sending Neighbor Solicitation",
- dst_addr
- );
- let solicit = Icmpv6Repr::Ndisc(NdiscRepr::NeighborSolicit {
- target_addr: dst_addr,
- lladdr: Some(self.hardware_addr.into()),
- });
- let packet = Packet::new_ipv6(
- Ipv6Repr {
- src_addr,
- dst_addr: dst_addr.solicited_node(),
- next_header: IpProtocol::Icmpv6,
- payload_len: solicit.buffer_len(),
- hop_limit: 0xff,
- },
- IpPayload::Icmpv6(solicit),
- );
- if let Err(e) =
- self.dispatch_ip(tx_token, PacketMeta::default(), packet, fragmenter)
- {
- net_debug!("Failed to dispatch NDISC solicit: {:?}", e);
- return Err(DispatchError::NeighborPending);
- }
- }
- #[allow(unreachable_patterns)]
- _ => (),
- }
- // The request got dispatched, limit the rate on the cache.
- self.neighbor_cache.limit_rate(self.now);
- Err(DispatchError::NeighborPending)
- }
- fn flush_cache(&mut self) {
- #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
- self.neighbor_cache.flush()
- }
- fn dispatch_ip<Tx: TxToken>(
- &mut self,
- // NOTE(unused_mut): tx_token isn't always mutated, depending on
- // the feature set that is used.
- #[allow(unused_mut)] mut tx_token: Tx,
- meta: PacketMeta,
- packet: Packet,
- frag: &mut Fragmenter,
- ) -> Result<(), DispatchError> {
- let mut ip_repr = packet.ip_repr();
- assert!(!ip_repr.dst_addr().is_unspecified());
- // Dispatch IEEE802.15.4:
- #[cfg(feature = "medium-ieee802154")]
- if matches!(self.caps.medium, Medium::Ieee802154) {
- let (addr, tx_token) = self.lookup_hardware_addr(
- tx_token,
- &ip_repr.src_addr(),
- &ip_repr.dst_addr(),
- frag,
- )?;
- let addr = addr.ieee802154_or_panic();
- self.dispatch_ieee802154(addr, tx_token, meta, packet, frag);
- return Ok(());
- }
- // Dispatch IP/Ethernet:
- let caps = self.caps.clone();
- #[cfg(feature = "proto-ipv4-fragmentation")]
- let ipv4_id = self.get_ipv4_ident();
- // First we calculate the total length that we will have to emit.
- let mut total_len = ip_repr.buffer_len();
- // Add the size of the Ethernet header if the medium is Ethernet.
- #[cfg(feature = "medium-ethernet")]
- if matches!(self.caps.medium, Medium::Ethernet) {
- total_len = EthernetFrame::<&[u8]>::buffer_len(total_len);
- }
- // If the medium is Ethernet, then we need to retrieve the destination hardware address.
- #[cfg(feature = "medium-ethernet")]
- let (dst_hardware_addr, mut tx_token) = match self.caps.medium {
- Medium::Ethernet => {
- match self.lookup_hardware_addr(
- tx_token,
- &ip_repr.src_addr(),
- &ip_repr.dst_addr(),
- frag,
- )? {
- (HardwareAddress::Ethernet(addr), tx_token) => (addr, tx_token),
- (_, _) => unreachable!(),
- }
- }
- _ => (EthernetAddress([0; 6]), tx_token),
- };
- // Emit function for the Ethernet header.
- #[cfg(feature = "medium-ethernet")]
- let emit_ethernet = |repr: &IpRepr, tx_buffer: &mut [u8]| {
- let mut frame = EthernetFrame::new_unchecked(tx_buffer);
- let src_addr = self.hardware_addr.ethernet_or_panic();
- frame.set_src_addr(src_addr);
- frame.set_dst_addr(dst_hardware_addr);
- match repr.version() {
- #[cfg(feature = "proto-ipv4")]
- IpVersion::Ipv4 => frame.set_ethertype(EthernetProtocol::Ipv4),
- #[cfg(feature = "proto-ipv6")]
- IpVersion::Ipv6 => frame.set_ethertype(EthernetProtocol::Ipv6),
- }
- Ok(())
- };
- // Emit function for the IP header and payload.
- let emit_ip = |repr: &IpRepr, mut tx_buffer: &mut [u8]| {
- repr.emit(&mut tx_buffer, &self.caps.checksum);
- let payload = &mut tx_buffer[repr.header_len()..];
- packet.emit_payload(repr, payload, &caps)
- };
- let total_ip_len = ip_repr.buffer_len();
- match &mut ip_repr {
- #[cfg(feature = "proto-ipv4")]
- IpRepr::Ipv4(repr) => {
- // If we have an IPv4 packet, then we need to check if we need to fragment it.
- if total_ip_len > self.caps.max_transmission_unit {
- #[cfg(feature = "proto-ipv4-fragmentation")]
- {
- net_debug!("start fragmentation");
- // Calculate how much we will send now (including the Ethernet header).
- let tx_len = self.caps.max_transmission_unit;
- let ip_header_len = repr.buffer_len();
- let first_frag_ip_len = self.caps.ip_mtu();
- if frag.buffer.len() < total_ip_len {
- net_debug!(
- "Fragmentation buffer is too small, at least {} needed. Dropping",
- total_ip_len
- );
- return Ok(());
- }
- #[cfg(feature = "medium-ethernet")]
- {
- frag.ipv4.dst_hardware_addr = dst_hardware_addr;
- }
- // Save the total packet len (without the Ethernet header, but with the first
- // IP header).
- frag.packet_len = total_ip_len;
- // Save the IP header for other fragments.
- frag.ipv4.repr = *repr;
- // Save how much bytes we will send now.
- frag.sent_bytes = first_frag_ip_len;
- // Modify the IP header
- repr.payload_len = first_frag_ip_len - repr.buffer_len();
- // Emit the IP header to the buffer.
- emit_ip(&ip_repr, &mut frag.buffer);
- let mut ipv4_packet = Ipv4Packet::new_unchecked(&mut frag.buffer[..]);
- frag.ipv4.ident = ipv4_id;
- ipv4_packet.set_ident(ipv4_id);
- ipv4_packet.set_more_frags(true);
- ipv4_packet.set_dont_frag(false);
- ipv4_packet.set_frag_offset(0);
- if caps.checksum.ipv4.tx() {
- ipv4_packet.fill_checksum();
- }
- // Transmit the first packet.
- tx_token.consume(tx_len, |mut tx_buffer| {
- #[cfg(feature = "medium-ethernet")]
- if matches!(self.caps.medium, Medium::Ethernet) {
- emit_ethernet(&ip_repr, tx_buffer)?;
- tx_buffer = &mut tx_buffer[EthernetFrame::<&[u8]>::header_len()..];
- }
- // Change the offset for the next packet.
- frag.ipv4.frag_offset = (first_frag_ip_len - ip_header_len) as u16;
- // Copy the IP header and the payload.
- tx_buffer[..first_frag_ip_len]
- .copy_from_slice(&frag.buffer[..first_frag_ip_len]);
- Ok(())
- })
- }
- #[cfg(not(feature = "proto-ipv4-fragmentation"))]
- {
- net_debug!("Enable the `proto-ipv4-fragmentation` feature for fragmentation support.");
- Ok(())
- }
- } else {
- tx_token.set_meta(meta);
- // No fragmentation is required.
- tx_token.consume(total_len, |mut tx_buffer| {
- #[cfg(feature = "medium-ethernet")]
- if matches!(self.caps.medium, Medium::Ethernet) {
- emit_ethernet(&ip_repr, tx_buffer)?;
- tx_buffer = &mut tx_buffer[EthernetFrame::<&[u8]>::header_len()..];
- }
- emit_ip(&ip_repr, tx_buffer);
- Ok(())
- })
- }
- }
- // We don't support IPv6 fragmentation yet.
- #[cfg(feature = "proto-ipv6")]
- IpRepr::Ipv6(_) => tx_token.consume(total_len, |mut tx_buffer| {
- #[cfg(feature = "medium-ethernet")]
- if matches!(self.caps.medium, Medium::Ethernet) {
- emit_ethernet(&ip_repr, tx_buffer)?;
- tx_buffer = &mut tx_buffer[EthernetFrame::<&[u8]>::header_len()..];
- }
- emit_ip(&ip_repr, tx_buffer);
- Ok(())
- }),
- }
- }
- }
- #[derive(Debug, Clone, Copy, PartialEq, Eq)]
- #[cfg_attr(feature = "defmt", derive(defmt::Format))]
- enum DispatchError {
- /// No route to dispatch this packet. Retrying won't help unless
- /// configuration is changed.
- NoRoute,
- /// We do have a route to dispatch this packet, but we haven't discovered
- /// the neighbor for it yet. Discovery has been initiated, dispatch
- /// should be retried later.
- NeighborPending,
- }
|