Skip to content

Commit 62aa692

Browse files
committed
Workaround broken join_multicast_v4 on ESP-IDF
1 parent e8babed commit 62aa692

File tree

4 files changed

+90
-28
lines changed

4 files changed

+90
-28
lines changed

examples/onoff_light/src/main.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -77,13 +77,14 @@ fn run() -> Result<(), Error> {
7777
device_name: "OnOff Light",
7878
};
7979

80-
let (ipv4_addr, ipv6_addr) = initialize_network()?;
80+
let (ipv4_addr, ipv6_addr, interface) = initialize_network()?;
8181

8282
let mdns = DefaultMdns::new(
8383
0,
8484
"matter-demo",
8585
ipv4_addr.octets(),
8686
Some(ipv6_addr.octets()),
87+
interface,
8788
&dev_det,
8889
matter::MATTER_PORT,
8990
);
@@ -231,7 +232,7 @@ fn initialize_logger() {
231232

232233
#[cfg(not(target_os = "espidf"))]
233234
#[inline(never)]
234-
fn initialize_network() -> Result<(Ipv4Addr, Ipv6Addr), Error> {
235+
fn initialize_network() -> Result<(Ipv4Addr, Ipv6Addr, u32), Error> {
235236
use log::error;
236237
use matter::error::ErrorCode;
237238
use nix::{net::if_::InterfaceFlags, sys::socket::SockaddrIn6};
@@ -276,7 +277,7 @@ fn initialize_network() -> Result<(Ipv4Addr, Ipv6Addr), Error> {
276277
iname, ip, ipv6
277278
);
278279

279-
Ok((ip, ipv6))
280+
Ok((ip, ipv6, 0 as _))
280281
}
281282

282283
#[cfg(target_os = "espidf")]
@@ -287,7 +288,7 @@ fn initialize_logger() {
287288

288289
#[cfg(target_os = "espidf")]
289290
#[inline(never)]
290-
fn initialize_network() -> Result<(Ipv4Addr, Ipv6Addr), Error> {
291+
fn initialize_network() -> Result<(Ipv4Addr, Ipv6Addr, u32), Error> {
291292
use core::time::Duration;
292293

293294
use embedded_svc::wifi::{AuthMethod, ClientConfiguration, Configuration};
@@ -379,9 +380,11 @@ fn initialize_network() -> Result<(Ipv4Addr, Ipv6Addr), Error> {
379380
ipv6.addr[3].to_le_bytes()[3],
380381
];
381382

383+
let interface = wifi.sta_netif().get_index();
384+
382385
// Not OK of course, but for a demo this is good enough
383386
// Wifi will continue to be available and working in the background
384387
core::mem::forget(wifi);
385388

386-
Ok((ipv4_octets.into(), ipv6_octets.into()))
389+
Ok((ipv4_octets.into(), ipv6_octets.into(), interface))
387390
}

matter/src/mdns/astro.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ impl<'a> Mdns<'a> {
2323
_hostname: &str,
2424
_ip: [u8; 4],
2525
_ipv6: Option<[u8; 16]>,
26+
_interface: u32,
2627
dev_det: &'a BasicInfoConfig<'a>,
2728
matter_port: u16,
2829
) -> Self {

matter/src/mdns/builtin.rs

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,21 +19,19 @@ use super::{
1919
Service, ServiceMode,
2020
};
2121

22-
const IP_BROADCAST_ADDRS: [(IpAddr, u16); 2] = [
23-
(IpAddr::V4(Ipv4Addr::new(224, 0, 0, 251)), 5353),
24-
(
25-
IpAddr::V6(Ipv6Addr::new(0xff02, 0, 0, 0, 0, 0, 0, 0x00fb)),
26-
5353,
27-
),
28-
];
22+
const IP_BIND_ADDR: IpAddr = IpAddr::V6(Ipv6Addr::UNSPECIFIED);
2923

30-
const IP_BIND_ADDR: (IpAddr, u16) = (IpAddr::V6(Ipv6Addr::UNSPECIFIED), 5353);
24+
const IP_BROADCAST_ADDR: Ipv4Addr = Ipv4Addr::new(224, 0, 0, 251);
25+
const IPV6_BROADCAST_ADDR: Ipv6Addr = Ipv6Addr::new(0xff02, 0, 0, 0, 0, 0, 0, 0x00fb);
26+
27+
const PORT: u16 = 5353;
3128

3229
type MdnsTxBuf = MaybeUninit<[u8; MAX_TX_BUF_SIZE]>;
3330
type MdnsRxBuf = MaybeUninit<[u8; MAX_RX_BUF_SIZE]>;
3431

3532
pub struct Mdns<'a> {
3633
host: Host<'a>,
34+
interface: u32,
3735
dev_det: &'a BasicInfoConfig<'a>,
3836
matter_port: u16,
3937
services: RefCell<heapless::Vec<(heapless::String<40>, ServiceMode), 4>>,
@@ -47,6 +45,7 @@ impl<'a> Mdns<'a> {
4745
hostname: &'a str,
4846
ip: [u8; 4],
4947
ipv6: Option<[u8; 16]>,
48+
interface: u32,
5049
dev_det: &'a BasicInfoConfig<'a>,
5150
matter_port: u16,
5251
) -> Self {
@@ -57,6 +56,7 @@ impl<'a> Mdns<'a> {
5756
ip,
5857
ipv6,
5958
},
59+
interface,
6060
dev_det,
6161
matter_port,
6262
services: RefCell::new(heapless::Vec::new()),
@@ -121,11 +121,10 @@ impl<'a> MdnsRunner<'a> {
121121
let tx_pipe = &tx_pipe;
122122
let rx_pipe = &rx_pipe;
123123

124-
let mut udp = UdpListener::new(SocketAddr::new(IP_BIND_ADDR.0, IP_BIND_ADDR.1)).await?;
124+
let mut udp = UdpListener::new(SocketAddr::new(IP_BIND_ADDR, PORT)).await?;
125125

126-
for (ip, _) in IP_BROADCAST_ADDRS {
127-
udp.join_multicast(ip).await?;
128-
}
126+
udp.join_multicast_v6(IPV6_BROADCAST_ADDR, self.0.interface)?;
127+
udp.join_multicast_v4(IP_BROADCAST_ADDR, Ipv4Addr::from(self.0.host.ip))?;
129128

130129
let udp = &udp;
131130

@@ -188,7 +187,10 @@ impl<'a> MdnsRunner<'a> {
188187
)
189188
.await;
190189

191-
for (addr, port) in IP_BROADCAST_ADDRS {
190+
for addr in [
191+
IpAddr::V4(IP_BROADCAST_ADDR),
192+
IpAddr::V6(IPV6_BROADCAST_ADDR),
193+
] {
192194
loop {
193195
let sent = {
194196
let mut data = tx_pipe.data.lock().await;
@@ -197,12 +199,12 @@ impl<'a> MdnsRunner<'a> {
197199
let len = self.0.host.broadcast(&self.0, data.buf, 60)?;
198200

199201
if len > 0 {
200-
info!("Broadasting mDNS entry to {}:{}", addr, port);
202+
info!("Broadasting mDNS entry to {}:{}", addr, PORT);
201203

202204
data.chunk = Some(Chunk {
203205
start: 0,
204206
end: len,
205-
addr: Address::Udp(SocketAddr::new(addr, port)),
207+
addr: Address::Udp(SocketAddr::new(addr, PORT)),
206208
});
207209

208210
tx_pipe.data_supplied_notification.signal(());

matter/src/transport/udp.rs

Lines changed: 64 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ mod smol_udp {
2727
use log::{debug, info, warn};
2828
use smol::net::UdpSocket;
2929

30-
use crate::transport::network::{IpAddr, Ipv4Addr, SocketAddr};
30+
use crate::transport::network::{Ipv4Addr, Ipv6Addr, SocketAddr};
3131

3232
pub struct UdpListener {
3333
socket: UdpSocket,
@@ -44,15 +44,71 @@ mod smol_udp {
4444
Ok(listener)
4545
}
4646

47-
pub async fn join_multicast(&mut self, ip_addr: IpAddr) -> Result<(), Error> {
48-
match ip_addr {
49-
IpAddr::V4(ip_addr) => self
50-
.socket
51-
.join_multicast_v4(ip_addr, Ipv4Addr::UNSPECIFIED)?,
52-
IpAddr::V6(ip_addr) => self.socket.join_multicast_v6(&ip_addr, 0)?,
47+
pub fn join_multicast_v6(
48+
&mut self,
49+
multiaddr: Ipv6Addr,
50+
interface: u32,
51+
) -> Result<(), Error> {
52+
self.socket.join_multicast_v6(&multiaddr, interface)?;
53+
54+
info!("Joined IPV6 multicast {}/{}", multiaddr, interface);
55+
56+
Ok(())
57+
}
58+
59+
pub fn join_multicast_v4(
60+
&mut self,
61+
multiaddr: Ipv4Addr,
62+
interface: Ipv4Addr,
63+
) -> Result<(), Error> {
64+
#[cfg(not(target_os = "espidf"))]
65+
self.socket.join_multicast_v4(multiaddr, interface)?;
66+
67+
// join_multicast_v4() is broken for ESP-IDF, most likely due to wrong `ip_mreq` signature in the `libc` crate
68+
// Note that also most *_multicast_v4 and *_multicast_v6 methods are broken as well in Rust STD for the ESP-IDF
69+
// due to mismatch w.r.t. sizes (u8 expected but u32 passed to setsockopt() and sometimes the other way around)
70+
#[cfg(target_os = "espidf")]
71+
{
72+
fn esp_setsockopt<T>(
73+
socket: &mut UdpSocket,
74+
proto: u32,
75+
option: u32,
76+
value: T,
77+
) -> Result<(), Error> {
78+
use std::os::fd::AsRawFd;
79+
80+
esp_idf_sys::esp!(unsafe {
81+
esp_idf_sys::lwip_setsockopt(
82+
socket.as_raw_fd(),
83+
proto as _,
84+
option as _,
85+
&value as *const _ as *const _,
86+
core::mem::size_of::<T>() as _,
87+
)
88+
})
89+
.map_err(|_| ErrorCode::StdIoError)?;
90+
91+
Ok(())
92+
}
93+
94+
let mreq = esp_idf_sys::ip_mreq {
95+
imr_multiaddr: esp_idf_sys::in_addr {
96+
s_addr: u32::from_ne_bytes(multiaddr.octets()),
97+
},
98+
imr_interface: esp_idf_sys::in_addr {
99+
s_addr: u32::from_ne_bytes(interface.octets()),
100+
},
101+
};
102+
103+
esp_setsockopt(
104+
&mut self.socket,
105+
esp_idf_sys::IPPROTO_IP,
106+
esp_idf_sys::IP_ADD_MEMBERSHIP,
107+
mreq,
108+
)?;
53109
}
54110

55-
info!("Joining multicast on {:?}", ip_addr);
111+
info!("Joined IP multicast {}/{}", multiaddr, interface);
56112

57113
Ok(())
58114
}

0 commit comments

Comments
 (0)