Skip to content

Commit

Permalink
Merge pull request #976 from smoltcp-rs/multicast-single-map
Browse files Browse the repository at this point in the history
multicast: use a single map for both ipv4 and ipv6.
  • Loading branch information
thvdveld authored Aug 29, 2024
2 parents 8e3ea5c + 4990fb9 commit de2dfe1
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 50 deletions.
77 changes: 39 additions & 38 deletions src/iface/interface/igmp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,20 +39,22 @@ impl Interface {
where
D: Device + ?Sized,
{
let addr = addr.into();
self.inner.now = timestamp;

match addr.into() {
let is_not_new = self
.inner
.multicast_groups
.insert(addr, ())
.map_err(|_| MulticastError::GroupTableFull)?
.is_some();
if is_not_new {
return Ok(false);
}

match addr {
IpAddress::Ipv4(addr) => {
let is_not_new = self
.inner
.ipv4_multicast_groups
.insert(addr, ())
.map_err(|_| MulticastError::GroupTableFull)?
.is_some();
if is_not_new {
Ok(false)
} else if let Some(pkt) = self.inner.igmp_report_packet(IgmpVersion::Version2, addr)
{
if let Some(pkt) = self.inner.igmp_report_packet(IgmpVersion::Version2, addr) {
// Send initial membership report
let tx_token = device
.transmit(timestamp)
Expand All @@ -71,19 +73,10 @@ impl Interface {
#[cfg(feature = "proto-ipv6")]
IpAddress::Ipv6(addr) => {
// Build report packet containing this new address
let report_record = &[MldAddressRecordRepr::new(
if let Some(pkt) = self.inner.mldv2_report_packet(&[MldAddressRecordRepr::new(
MldRecordType::ChangeToInclude,
addr,
)];
let is_not_new = self
.inner
.ipv6_multicast_groups
.insert(addr, ())
.map_err(|_| MulticastError::GroupTableFull)?
.is_some();
if is_not_new {
Ok(false)
} else if let Some(pkt) = self.inner.mldv2_report_packet(report_record) {
)]) {
// Send initial membership report
let tx_token = device
.transmit(timestamp)
Expand Down Expand Up @@ -117,14 +110,16 @@ impl Interface {
where
D: Device + ?Sized,
{
let addr = addr.into();
self.inner.now = timestamp;
let was_not_present = self.inner.multicast_groups.remove(&addr).is_none();
if was_not_present {
return Ok(false);
}

match addr.into() {
match addr {
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) {
if let Some(pkt) = self.inner.igmp_leave_packet(addr) {
// Send group leave packet
let tx_token = device
.transmit(timestamp)
Expand All @@ -142,14 +137,10 @@ impl Interface {
}
#[cfg(feature = "proto-ipv6")]
IpAddress::Ipv6(addr) => {
let report_record = &[MldAddressRecordRepr::new(
if let Some(pkt) = self.inner.mldv2_report_packet(&[MldAddressRecordRepr::new(
MldRecordType::ChangeToExclude,
addr,
)];
let was_not_present = self.inner.ipv6_multicast_groups.remove(&addr).is_none();
if was_not_present {
Ok(false)
} else if let Some(pkt) = self.inner.mldv2_report_packet(report_record) {
)]) {
// Send group leave packet
let tx_token = device
.transmit(timestamp)
Expand Down Expand Up @@ -210,10 +201,14 @@ impl Interface {
} if self.inner.now >= timeout => {
let addr = self
.inner
.ipv4_multicast_groups
.multicast_groups
.iter()
.nth(next_index)
.map(|(addr, ())| *addr);
.filter_map(|(addr, _)| match addr {
IpAddress::Ipv4(addr) => Some(*addr),
#[allow(unreachable_patterns)]
_ => None,
})
.nth(next_index);

match addr {
Some(addr) => {
Expand Down Expand Up @@ -280,15 +275,21 @@ impl InterfaceInner {
if group_addr.is_unspecified()
&& ipv4_repr.dst_addr == Ipv4Address::MULTICAST_ALL_SYSTEMS
{
let ipv4_multicast_group_count = self
.multicast_groups
.keys()
.filter(|a| matches!(a, IpAddress::Ipv4(_)))
.count();

// Are we member in any groups?
if self.ipv4_multicast_groups.iter().next().is_some() {
if ipv4_multicast_group_count != 0 {
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;
let intervals = ipv4_multicast_group_count as u32 + 1;
max_resp_time / intervals
}
};
Expand Down
20 changes: 8 additions & 12 deletions src/iface/interface/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,10 +111,8 @@ pub struct InterfaceInner {
ip_addrs: Vec<IpCidr, IFACE_MAX_ADDR_COUNT>,
any_ip: bool,
routes: Routes,
#[cfg(feature = "proto-igmp")]
ipv4_multicast_groups: LinearMap<Ipv4Address, (), IFACE_MAX_MULTICAST_GROUP_COUNT>,
#[cfg(feature = "proto-ipv6")]
ipv6_multicast_groups: LinearMap<Ipv6Address, (), IFACE_MAX_MULTICAST_GROUP_COUNT>,
#[cfg(any(feature = "proto-igmp", feature = "proto-ipv6"))]
multicast_groups: LinearMap<IpAddress, (), 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,
Expand Down Expand Up @@ -226,10 +224,8 @@ impl Interface {
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-ipv6")]
ipv6_multicast_groups: LinearMap::new(),
#[cfg(any(feature = "proto-igmp", feature = "proto-ipv6"))]
multicast_groups: LinearMap::new(),
#[cfg(feature = "proto-igmp")]
igmp_report_state: IgmpReportState::Inactive,
#[cfg(feature = "medium-ieee802154")]
Expand Down Expand Up @@ -753,17 +749,18 @@ impl InterfaceInner {
/// If built without feature `proto-igmp` this function will
/// always return `false` when using IPv4.
fn has_multicast_group<T: Into<IpAddress>>(&self, addr: T) -> bool {
match addr.into() {
let addr = addr.into();
match addr {
#[cfg(feature = "proto-igmp")]
IpAddress::Ipv4(key) => {
key == Ipv4Address::MULTICAST_ALL_SYSTEMS
|| self.ipv4_multicast_groups.get(&key).is_some()
|| self.multicast_groups.get(&addr).is_some()
}
#[cfg(feature = "proto-ipv6")]
IpAddress::Ipv6(key) => {
key == Ipv6Address::LINK_LOCAL_ALL_NODES
|| self.has_solicited_node(key)
|| self.ipv6_multicast_groups.get(&key).is_some()
|| self.multicast_groups.get(&addr).is_some()
}
#[cfg(feature = "proto-rpl")]
IpAddress::Ipv6(Ipv6Address::LINK_LOCAL_ALL_RPL_NODES) => true,
Expand All @@ -784,7 +781,6 @@ impl InterfaceInner {
#[cfg(feature = "proto-ipv4")]
Ok(IpVersion::Ipv4) => {
let ipv4_packet = check!(Ipv4Packet::new_checked(ip_payload));

self.process_ipv4(sockets, meta, HardwareAddress::Ip, &ipv4_packet, frag)
}
#[cfg(feature = "proto-ipv6")]
Expand Down

0 comments on commit de2dfe1

Please sign in to comment.