Workaround broken join_multicast_v4 on ESP-IDF

This commit is contained in:
ivmarkov 2023-06-13 07:02:37 +00:00
parent 8d9bd1332c
commit 864692845b
4 changed files with 90 additions and 28 deletions

View file

@ -77,13 +77,14 @@ fn run() -> Result<(), Error> {
device_name: "OnOff Light",
};
let (ipv4_addr, ipv6_addr) = initialize_network()?;
let (ipv4_addr, ipv6_addr, interface) = initialize_network()?;
let mdns = DefaultMdns::new(
0,
"matter-demo",
ipv4_addr.octets(),
Some(ipv6_addr.octets()),
interface,
&dev_det,
matter::MATTER_PORT,
);
@ -231,7 +232,7 @@ fn initialize_logger() {
#[cfg(not(target_os = "espidf"))]
#[inline(never)]
fn initialize_network() -> Result<(Ipv4Addr, Ipv6Addr), Error> {
fn initialize_network() -> Result<(Ipv4Addr, Ipv6Addr, u32), Error> {
use log::error;
use matter::error::ErrorCode;
use nix::{net::if_::InterfaceFlags, sys::socket::SockaddrIn6};
@ -276,7 +277,7 @@ fn initialize_network() -> Result<(Ipv4Addr, Ipv6Addr), Error> {
iname, ip, ipv6
);
Ok((ip, ipv6))
Ok((ip, ipv6, 0 as _))
}
#[cfg(target_os = "espidf")]
@ -287,7 +288,7 @@ fn initialize_logger() {
#[cfg(target_os = "espidf")]
#[inline(never)]
fn initialize_network() -> Result<(Ipv4Addr, Ipv6Addr), Error> {
fn initialize_network() -> Result<(Ipv4Addr, Ipv6Addr, u32), Error> {
use core::time::Duration;
use embedded_svc::wifi::{AuthMethod, ClientConfiguration, Configuration};
@ -379,9 +380,11 @@ fn initialize_network() -> Result<(Ipv4Addr, Ipv6Addr), Error> {
ipv6.addr[3].to_le_bytes()[3],
];
let interface = wifi.sta_netif().get_index();
// Not OK of course, but for a demo this is good enough
// Wifi will continue to be available and working in the background
core::mem::forget(wifi);
Ok((ipv4_octets.into(), ipv6_octets.into()))
Ok((ipv4_octets.into(), ipv6_octets.into(), interface))
}

View file

@ -23,6 +23,7 @@ impl<'a> Mdns<'a> {
_hostname: &str,
_ip: [u8; 4],
_ipv6: Option<[u8; 16]>,
_interface: u32,
dev_det: &'a BasicInfoConfig<'a>,
matter_port: u16,
) -> Self {

View file

@ -19,21 +19,19 @@ use super::{
Service, ServiceMode,
};
const IP_BROADCAST_ADDRS: [(IpAddr, u16); 2] = [
(IpAddr::V4(Ipv4Addr::new(224, 0, 0, 251)), 5353),
(
IpAddr::V6(Ipv6Addr::new(0xff02, 0, 0, 0, 0, 0, 0, 0x00fb)),
5353,
),
];
const IP_BIND_ADDR: IpAddr = IpAddr::V6(Ipv6Addr::UNSPECIFIED);
const IP_BIND_ADDR: (IpAddr, u16) = (IpAddr::V6(Ipv6Addr::UNSPECIFIED), 5353);
const IP_BROADCAST_ADDR: Ipv4Addr = Ipv4Addr::new(224, 0, 0, 251);
const IPV6_BROADCAST_ADDR: Ipv6Addr = Ipv6Addr::new(0xff02, 0, 0, 0, 0, 0, 0, 0x00fb);
const PORT: u16 = 5353;
type MdnsTxBuf = MaybeUninit<[u8; MAX_TX_BUF_SIZE]>;
type MdnsRxBuf = MaybeUninit<[u8; MAX_RX_BUF_SIZE]>;
pub struct Mdns<'a> {
host: Host<'a>,
interface: u32,
dev_det: &'a BasicInfoConfig<'a>,
matter_port: u16,
services: RefCell<heapless::Vec<(heapless::String<40>, ServiceMode), 4>>,
@ -47,6 +45,7 @@ impl<'a> Mdns<'a> {
hostname: &'a str,
ip: [u8; 4],
ipv6: Option<[u8; 16]>,
interface: u32,
dev_det: &'a BasicInfoConfig<'a>,
matter_port: u16,
) -> Self {
@ -57,6 +56,7 @@ impl<'a> Mdns<'a> {
ip,
ipv6,
},
interface,
dev_det,
matter_port,
services: RefCell::new(heapless::Vec::new()),
@ -121,11 +121,10 @@ impl<'a> MdnsRunner<'a> {
let tx_pipe = &tx_pipe;
let rx_pipe = &rx_pipe;
let mut udp = UdpListener::new(SocketAddr::new(IP_BIND_ADDR.0, IP_BIND_ADDR.1)).await?;
let mut udp = UdpListener::new(SocketAddr::new(IP_BIND_ADDR, PORT)).await?;
for (ip, _) in IP_BROADCAST_ADDRS {
udp.join_multicast(ip).await?;
}
udp.join_multicast_v6(IPV6_BROADCAST_ADDR, self.0.interface)?;
udp.join_multicast_v4(IP_BROADCAST_ADDR, Ipv4Addr::from(self.0.host.ip))?;
let udp = &udp;
@ -188,7 +187,10 @@ impl<'a> MdnsRunner<'a> {
)
.await;
for (addr, port) in IP_BROADCAST_ADDRS {
for addr in [
IpAddr::V4(IP_BROADCAST_ADDR),
IpAddr::V6(IPV6_BROADCAST_ADDR),
] {
loop {
let sent = {
let mut data = tx_pipe.data.lock().await;
@ -197,12 +199,12 @@ impl<'a> MdnsRunner<'a> {
let len = self.0.host.broadcast(&self.0, data.buf, 60)?;
if len > 0 {
info!("Broadasting mDNS entry to {}:{}", addr, port);
info!("Broadasting mDNS entry to {}:{}", addr, PORT);
data.chunk = Some(Chunk {
start: 0,
end: len,
addr: Address::Udp(SocketAddr::new(addr, port)),
addr: Address::Udp(SocketAddr::new(addr, PORT)),
});
tx_pipe.data_supplied_notification.signal(());

View file

@ -27,7 +27,7 @@ mod smol_udp {
use log::{debug, info, warn};
use smol::net::UdpSocket;
use crate::transport::network::{IpAddr, Ipv4Addr, SocketAddr};
use crate::transport::network::{Ipv4Addr, Ipv6Addr, SocketAddr};
pub struct UdpListener {
socket: UdpSocket,
@ -44,15 +44,71 @@ mod smol_udp {
Ok(listener)
}
pub async fn join_multicast(&mut self, ip_addr: IpAddr) -> Result<(), Error> {
match ip_addr {
IpAddr::V4(ip_addr) => self
.socket
.join_multicast_v4(ip_addr, Ipv4Addr::UNSPECIFIED)?,
IpAddr::V6(ip_addr) => self.socket.join_multicast_v6(&ip_addr, 0)?,
pub fn join_multicast_v6(
&mut self,
multiaddr: Ipv6Addr,
interface: u32,
) -> Result<(), Error> {
self.socket.join_multicast_v6(&multiaddr, interface)?;
info!("Joined IPV6 multicast {}/{}", multiaddr, interface);
Ok(())
}
pub fn join_multicast_v4(
&mut self,
multiaddr: Ipv4Addr,
interface: Ipv4Addr,
) -> Result<(), Error> {
#[cfg(not(target_os = "espidf"))]
self.socket.join_multicast_v4(multiaddr, interface)?;
// join_multicast_v4() is broken for ESP-IDF, most likely due to wrong `ip_mreq` signature in the `libc` crate
// Note that also most *_multicast_v4 and *_multicast_v6 methods are broken as well in Rust STD for the ESP-IDF
// due to mismatch w.r.t. sizes (u8 expected but u32 passed to setsockopt() and sometimes the other way around)
#[cfg(target_os = "espidf")]
{
fn esp_setsockopt<T>(
socket: &mut UdpSocket,
proto: u32,
option: u32,
value: T,
) -> Result<(), Error> {
use std::os::fd::AsRawFd;
esp_idf_sys::esp!(unsafe {
esp_idf_sys::lwip_setsockopt(
socket.as_raw_fd(),
proto as _,
option as _,
&value as *const _ as *const _,
core::mem::size_of::<T>() as _,
)
})
.map_err(|_| ErrorCode::StdIoError)?;
Ok(())
}
let mreq = esp_idf_sys::ip_mreq {
imr_multiaddr: esp_idf_sys::in_addr {
s_addr: u32::from_ne_bytes(multiaddr.octets()),
},
imr_interface: esp_idf_sys::in_addr {
s_addr: u32::from_ne_bytes(interface.octets()),
},
};
esp_setsockopt(
&mut self.socket,
esp_idf_sys::IPPROTO_IP,
esp_idf_sys::IP_ADD_MEMBERSHIP,
mreq,
)?;
}
info!("Joining multicast on {:?}", ip_addr);
info!("Joined IP multicast {}/{}", multiaddr, interface);
Ok(())
}