Workaround broken join_multicast_v4 on ESP-IDF
This commit is contained in:
parent
8d9bd1332c
commit
864692845b
4 changed files with 90 additions and 28 deletions
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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(());
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue