Преглед на файлове

iface: move igmp code to a separate mod.

Dario Nieuwenhuis преди 2 години
родител
ревизия
f4a823ad37
променени са 3 файла, в които са добавени 239 реда и са изтрити 230 реда
  1. 233 0
      src/iface/interface/igmp.rs
  2. 0 69
      src/iface/interface/ipv4.rs
  3. 6 161
      src/iface/interface/mod.rs

+ 233 - 0
src/iface/interface/igmp.rs

@@ -0,0 +1,233 @@
+use super::{check, IgmpReportState, Interface, InterfaceInner, IpPacket};
+use crate::phy::Device;
+use crate::time::{Duration, Instant};
+use crate::wire::*;
+use crate::{Error, Result};
+
+impl<'a> Interface<'a> {
+    /// Add an address to a list of subscribed multicast IP addresses.
+    ///
+    /// Returns `Ok(announce_sent)` if the address was added successfully, where `annouce_sent`
+    /// indicates whether an initial immediate announcement has been sent.
+    pub fn join_multicast_group<D, T: Into<IpAddress>>(
+        &mut self,
+        device: &mut D,
+        addr: T,
+        timestamp: Instant,
+    ) -> Result<bool>
+    where
+        D: Device + ?Sized,
+    {
+        self.inner.now = timestamp;
+
+        match addr.into() {
+            IpAddress::Ipv4(addr) => {
+                let is_not_new = self
+                    .inner
+                    .ipv4_multicast_groups
+                    .insert(addr, ())
+                    .map_err(|_| Error::Exhausted)?
+                    .is_some();
+                if is_not_new {
+                    Ok(false)
+                } else if let Some(pkt) = self.inner.igmp_report_packet(IgmpVersion::Version2, addr)
+                {
+                    // Send initial membership report
+                    let tx_token = device.transmit(timestamp).ok_or(Error::Exhausted)?;
+                    self.inner.dispatch_ip(tx_token, pkt, None)?;
+                    Ok(true)
+                } else {
+                    Ok(false)
+                }
+            }
+            // Multicast is not yet implemented for other address families
+            #[allow(unreachable_patterns)]
+            _ => Err(Error::Unaddressable),
+        }
+    }
+
+    /// Remove an address from the subscribed multicast IP addresses.
+    ///
+    /// Returns `Ok(leave_sent)` if the address was removed successfully, where `leave_sent`
+    /// indicates whether an immediate leave packet has been sent.
+    pub fn leave_multicast_group<D, T: Into<IpAddress>>(
+        &mut self,
+        device: &mut D,
+        addr: T,
+        timestamp: Instant,
+    ) -> Result<bool>
+    where
+        D: Device + ?Sized,
+    {
+        self.inner.now = timestamp;
+
+        match addr.into() {
+            IpAddress::Ipv4(addr) => {
+                let was_not_present = self.inner.ipv4_multicast_groups.remove(&addr).is_none();
+                if was_not_present {
+                    Ok(false)
+                } else if let Some(pkt) = self.inner.igmp_leave_packet(addr) {
+                    // Send group leave packet
+                    let tx_token = device.transmit(timestamp).ok_or(Error::Exhausted)?;
+                    self.inner.dispatch_ip(tx_token, pkt, None)?;
+                    Ok(true)
+                } else {
+                    Ok(false)
+                }
+            }
+            // Multicast is not yet implemented for other address families
+            #[allow(unreachable_patterns)]
+            _ => Err(Error::Unaddressable),
+        }
+    }
+
+    /// Check whether the interface listens to given destination multicast IP address.
+    pub fn has_multicast_group<T: Into<IpAddress>>(&self, addr: T) -> bool {
+        self.inner.has_multicast_group(addr)
+    }
+
+    /// Depending on `igmp_report_state` and the therein contained
+    /// timeouts, send IGMP membership reports.
+    pub(crate) fn igmp_egress<D>(&mut self, device: &mut D) -> Result<bool>
+    where
+        D: Device + ?Sized,
+    {
+        match self.inner.igmp_report_state {
+            IgmpReportState::ToSpecificQuery {
+                version,
+                timeout,
+                group,
+            } if self.inner.now >= timeout => {
+                if let Some(pkt) = self.inner.igmp_report_packet(version, group) {
+                    // Send initial membership report
+                    let tx_token = device.transmit(self.inner.now).ok_or(Error::Exhausted)?;
+                    self.inner.dispatch_ip(tx_token, pkt, None)?;
+                }
+
+                self.inner.igmp_report_state = IgmpReportState::Inactive;
+                Ok(true)
+            }
+            IgmpReportState::ToGeneralQuery {
+                version,
+                timeout,
+                interval,
+                next_index,
+            } if self.inner.now >= timeout => {
+                let addr = self
+                    .inner
+                    .ipv4_multicast_groups
+                    .iter()
+                    .nth(next_index)
+                    .map(|(addr, ())| *addr);
+
+                match addr {
+                    Some(addr) => {
+                        if let Some(pkt) = self.inner.igmp_report_packet(version, addr) {
+                            // Send initial membership report
+                            let tx_token =
+                                device.transmit(self.inner.now).ok_or(Error::Exhausted)?;
+                            self.inner.dispatch_ip(tx_token, pkt, None)?;
+                        }
+
+                        let next_timeout = (timeout + interval).max(self.inner.now);
+                        self.inner.igmp_report_state = IgmpReportState::ToGeneralQuery {
+                            version,
+                            timeout: next_timeout,
+                            interval,
+                            next_index: next_index + 1,
+                        };
+                        Ok(true)
+                    }
+
+                    None => {
+                        self.inner.igmp_report_state = IgmpReportState::Inactive;
+                        Ok(false)
+                    }
+                }
+            }
+            _ => Ok(false),
+        }
+    }
+}
+
+impl<'a> InterfaceInner<'a> {
+    /// Check whether the interface listens to given destination multicast IP address.
+    ///
+    /// If built without feature `proto-igmp` this function will
+    /// always return `false`.
+    pub fn has_multicast_group<T: Into<IpAddress>>(&self, addr: T) -> bool {
+        match addr.into() {
+            IpAddress::Ipv4(key) => {
+                key == Ipv4Address::MULTICAST_ALL_SYSTEMS
+                    || self.ipv4_multicast_groups.get(&key).is_some()
+            }
+            #[allow(unreachable_patterns)]
+            _ => false,
+        }
+    }
+
+    /// Host duties of the **IGMPv2** protocol.
+    ///
+    /// Sets up `igmp_report_state` for responding to IGMP general/specific membership queries.
+    /// Membership must not be reported immediately in order to avoid flooding the network
+    /// after a query is broadcasted by a router; this is not currently done.
+    pub(super) fn process_igmp<'frame>(
+        &mut self,
+        ipv4_repr: Ipv4Repr,
+        ip_payload: &'frame [u8],
+    ) -> Option<IpPacket<'frame>> {
+        let igmp_packet = check!(IgmpPacket::new_checked(ip_payload));
+        let igmp_repr = check!(IgmpRepr::parse(&igmp_packet));
+
+        // FIXME: report membership after a delay
+        match igmp_repr {
+            IgmpRepr::MembershipQuery {
+                group_addr,
+                version,
+                max_resp_time,
+            } => {
+                // General query
+                if group_addr.is_unspecified()
+                    && ipv4_repr.dst_addr == Ipv4Address::MULTICAST_ALL_SYSTEMS
+                {
+                    // Are we member in any groups?
+                    if self.ipv4_multicast_groups.iter().next().is_some() {
+                        let interval = match version {
+                            IgmpVersion::Version1 => Duration::from_millis(100),
+                            IgmpVersion::Version2 => {
+                                // No dependence on a random generator
+                                // (see [#24](https://github.com/m-labs/smoltcp/issues/24))
+                                // but at least spread reports evenly across max_resp_time.
+                                let intervals = self.ipv4_multicast_groups.len() as u32 + 1;
+                                max_resp_time / intervals
+                            }
+                        };
+                        self.igmp_report_state = IgmpReportState::ToGeneralQuery {
+                            version,
+                            timeout: self.now + interval,
+                            interval,
+                            next_index: 0,
+                        };
+                    }
+                } else {
+                    // Group-specific query
+                    if self.has_multicast_group(group_addr) && ipv4_repr.dst_addr == group_addr {
+                        // Don't respond immediately
+                        let timeout = max_resp_time / 4;
+                        self.igmp_report_state = IgmpReportState::ToSpecificQuery {
+                            version,
+                            timeout: self.now + timeout,
+                            group: group_addr,
+                        };
+                    }
+                }
+            }
+            // Ignore membership reports
+            IgmpRepr::MembershipReport { .. } => (),
+            // Ignore hosts leaving groups
+            IgmpRepr::LeaveGroup { .. } => (),
+        }
+
+        None
+    }
+}

+ 0 - 69
src/iface/interface/ipv4.rs

@@ -5,9 +5,6 @@ use super::IpPacket;
 use super::PacketAssemblerSet;
 use super::SocketSet;
 
-#[cfg(feature = "proto-igmp")]
-use super::IgmpReportState;
-
 #[cfg(feature = "medium-ethernet")]
 use super::EthernetPacket;
 
@@ -249,72 +246,6 @@ impl<'a> InterfaceInner<'a> {
         }
     }
 
-    /// Host duties of the **IGMPv2** protocol.
-    ///
-    /// Sets up `igmp_report_state` for responding to IGMP general/specific membership queries.
-    /// Membership must not be reported immediately in order to avoid flooding the network
-    /// after a query is broadcasted by a router; this is not currently done.
-    #[cfg(feature = "proto-igmp")]
-    pub(super) fn process_igmp<'frame>(
-        &mut self,
-        ipv4_repr: Ipv4Repr,
-        ip_payload: &'frame [u8],
-    ) -> Option<IpPacket<'frame>> {
-        let igmp_packet = check!(IgmpPacket::new_checked(ip_payload));
-        let igmp_repr = check!(IgmpRepr::parse(&igmp_packet));
-
-        // FIXME: report membership after a delay
-        match igmp_repr {
-            IgmpRepr::MembershipQuery {
-                group_addr,
-                version,
-                max_resp_time,
-            } => {
-                // General query
-                if group_addr.is_unspecified()
-                    && ipv4_repr.dst_addr == Ipv4Address::MULTICAST_ALL_SYSTEMS
-                {
-                    // Are we member in any groups?
-                    if self.ipv4_multicast_groups.iter().next().is_some() {
-                        let interval = match version {
-                            IgmpVersion::Version1 => Duration::from_millis(100),
-                            IgmpVersion::Version2 => {
-                                // No dependence on a random generator
-                                // (see [#24](https://github.com/m-labs/smoltcp/issues/24))
-                                // but at least spread reports evenly across max_resp_time.
-                                let intervals = self.ipv4_multicast_groups.len() as u32 + 1;
-                                max_resp_time / intervals
-                            }
-                        };
-                        self.igmp_report_state = IgmpReportState::ToGeneralQuery {
-                            version,
-                            timeout: self.now + interval,
-                            interval,
-                            next_index: 0,
-                        };
-                    }
-                } else {
-                    // Group-specific query
-                    if self.has_multicast_group(group_addr) && ipv4_repr.dst_addr == group_addr {
-                        // Don't respond immediately
-                        let timeout = max_resp_time / 4;
-                        self.igmp_report_state = IgmpReportState::ToSpecificQuery {
-                            version,
-                            timeout: self.now + timeout,
-                            group: group_addr,
-                        };
-                    }
-                }
-            }
-            // Ignore membership reports
-            IgmpRepr::MembershipReport { .. } => (),
-            // Ignore hosts leaving groups
-            IgmpRepr::LeaveGroup { .. } => (),
-        }
-
-        None
-    }
-
     pub(super) fn process_icmpv4<'frame>(
         &mut self,
         _sockets: &mut SocketSet,

+ 6 - 161
src/iface/interface/mod.rs

@@ -15,6 +15,9 @@ mod ipv4;
 #[cfg(feature = "proto-ipv6")]
 mod ipv6;
 
+#[cfg(feature = "proto-igmp")]
+mod igmp;
+
 use core::cmp;
 use core::marker::PhantomData;
 use heapless::{LinearMap, Vec};
@@ -898,89 +901,6 @@ impl<'a> Interface<'a> {
         self.inner.hardware_addr = Some(addr);
     }
 
-    /// Add an address to a list of subscribed multicast IP addresses.
-    ///
-    /// Returns `Ok(announce_sent)` if the address was added successfully, where `annouce_sent`
-    /// indicates whether an initial immediate announcement has been sent.
-    pub fn join_multicast_group<D, T: Into<IpAddress>>(
-        &mut self,
-        device: &mut D,
-        addr: T,
-        timestamp: Instant,
-    ) -> Result<bool>
-    where
-        D: Device + ?Sized,
-    {
-        self.inner.now = timestamp;
-
-        match addr.into() {
-            #[cfg(feature = "proto-igmp")]
-            IpAddress::Ipv4(addr) => {
-                let is_not_new = self
-                    .inner
-                    .ipv4_multicast_groups
-                    .insert(addr, ())
-                    .map_err(|_| Error::Exhausted)?
-                    .is_some();
-                if is_not_new {
-                    Ok(false)
-                } else if let Some(pkt) = self.inner.igmp_report_packet(IgmpVersion::Version2, addr)
-                {
-                    // Send initial membership report
-                    let tx_token = device.transmit(timestamp).ok_or(Error::Exhausted)?;
-                    self.inner.dispatch_ip(tx_token, pkt, None)?;
-                    Ok(true)
-                } else {
-                    Ok(false)
-                }
-            }
-            // Multicast is not yet implemented for other address families
-            #[allow(unreachable_patterns)]
-            _ => Err(Error::Unaddressable),
-        }
-    }
-
-    /// Remove an address from the subscribed multicast IP addresses.
-    ///
-    /// Returns `Ok(leave_sent)` if the address was removed successfully, where `leave_sent`
-    /// indicates whether an immediate leave packet has been sent.
-    pub fn leave_multicast_group<D, T: Into<IpAddress>>(
-        &mut self,
-        device: &mut D,
-        addr: T,
-        timestamp: Instant,
-    ) -> Result<bool>
-    where
-        D: Device + ?Sized,
-    {
-        self.inner.now = timestamp;
-
-        match addr.into() {
-            #[cfg(feature = "proto-igmp")]
-            IpAddress::Ipv4(addr) => {
-                let was_not_present = self.inner.ipv4_multicast_groups.remove(&addr).is_none();
-                if was_not_present {
-                    Ok(false)
-                } else if let Some(pkt) = self.inner.igmp_leave_packet(addr) {
-                    // Send group leave packet
-                    let tx_token = device.transmit(timestamp).ok_or(Error::Exhausted)?;
-                    self.inner.dispatch_ip(tx_token, pkt, None)?;
-                    Ok(true)
-                } else {
-                    Ok(false)
-                }
-            }
-            // Multicast is not yet implemented for other address families
-            #[allow(unreachable_patterns)]
-            _ => Err(Error::Unaddressable),
-        }
-    }
-
-    /// Check whether the interface listens to given destination multicast IP address.
-    pub fn has_multicast_group<T: Into<IpAddress>>(&self, addr: T) -> bool {
-        self.inner.has_multicast_group(addr)
-    }
-
     /// Get the IP addresses of the interface.
     pub fn ip_addrs(&self) -> &[IpCidr] {
         self.inner.ip_addrs.as_ref()
@@ -1315,70 +1235,6 @@ impl<'a> Interface<'a> {
         emitted_any
     }
 
-    /// Depending on `igmp_report_state` and the therein contained
-    /// timeouts, send IGMP membership reports.
-    #[cfg(feature = "proto-igmp")]
-    fn igmp_egress<D>(&mut self, device: &mut D) -> Result<bool>
-    where
-        D: Device + ?Sized,
-    {
-        match self.inner.igmp_report_state {
-            IgmpReportState::ToSpecificQuery {
-                version,
-                timeout,
-                group,
-            } if self.inner.now >= timeout => {
-                if let Some(pkt) = self.inner.igmp_report_packet(version, group) {
-                    // Send initial membership report
-                    let tx_token = device.transmit(self.inner.now).ok_or(Error::Exhausted)?;
-                    self.inner.dispatch_ip(tx_token, pkt, None)?;
-                }
-
-                self.inner.igmp_report_state = IgmpReportState::Inactive;
-                Ok(true)
-            }
-            IgmpReportState::ToGeneralQuery {
-                version,
-                timeout,
-                interval,
-                next_index,
-            } if self.inner.now >= timeout => {
-                let addr = self
-                    .inner
-                    .ipv4_multicast_groups
-                    .iter()
-                    .nth(next_index)
-                    .map(|(addr, ())| *addr);
-
-                match addr {
-                    Some(addr) => {
-                        if let Some(pkt) = self.inner.igmp_report_packet(version, addr) {
-                            // Send initial membership report
-                            let tx_token =
-                                device.transmit(self.inner.now).ok_or(Error::Exhausted)?;
-                            self.inner.dispatch_ip(tx_token, pkt, None)?;
-                        }
-
-                        let next_timeout = (timeout + interval).max(self.inner.now);
-                        self.inner.igmp_report_state = IgmpReportState::ToGeneralQuery {
-                            version,
-                            timeout: next_timeout,
-                            interval,
-                            next_index: next_index + 1,
-                        };
-                        Ok(true)
-                    }
-
-                    None => {
-                        self.inner.igmp_report_state = IgmpReportState::Inactive;
-                        Ok(false)
-                    }
-                }
-            }
-            _ => Ok(false),
-        }
-    }
-
     /// Process fragments that still need to be sent for IPv4 packets.
     ///
     /// This function returns a boolean value indicating whether any packets were
@@ -1683,20 +1539,9 @@ impl<'a> InterfaceInner<'a> {
         })
     }
 
-    /// Check whether the interface listens to given destination multicast IP address.
-    ///
-    /// If built without feature `proto-igmp` this function will
-    /// always return `false`.
-    pub fn has_multicast_group<T: Into<IpAddress>>(&self, addr: T) -> bool {
-        match addr.into() {
-            #[cfg(feature = "proto-igmp")]
-            IpAddress::Ipv4(key) => {
-                key == Ipv4Address::MULTICAST_ALL_SYSTEMS
-                    || self.ipv4_multicast_groups.get(&key).is_some()
-            }
-            #[allow(unreachable_patterns)]
-            _ => false,
-        }
+    #[cfg(not(feature = "proto-igmp"))]
+    fn has_multicast_group<T: Into<IpAddress>>(&self, addr: T) -> bool {
+        false
     }
 
     #[cfg(feature = "medium-ip")]