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",
|
device_name: "OnOff Light",
|
||||||
};
|
};
|
||||||
|
|
||||||
let (ipv4_addr, ipv6_addr) = initialize_network()?;
|
let (ipv4_addr, ipv6_addr, interface) = initialize_network()?;
|
||||||
|
|
||||||
let mdns = DefaultMdns::new(
|
let mdns = DefaultMdns::new(
|
||||||
0,
|
0,
|
||||||
"matter-demo",
|
"matter-demo",
|
||||||
ipv4_addr.octets(),
|
ipv4_addr.octets(),
|
||||||
Some(ipv6_addr.octets()),
|
Some(ipv6_addr.octets()),
|
||||||
|
interface,
|
||||||
&dev_det,
|
&dev_det,
|
||||||
matter::MATTER_PORT,
|
matter::MATTER_PORT,
|
||||||
);
|
);
|
||||||
|
@ -231,7 +232,7 @@ fn initialize_logger() {
|
||||||
|
|
||||||
#[cfg(not(target_os = "espidf"))]
|
#[cfg(not(target_os = "espidf"))]
|
||||||
#[inline(never)]
|
#[inline(never)]
|
||||||
fn initialize_network() -> Result<(Ipv4Addr, Ipv6Addr), Error> {
|
fn initialize_network() -> Result<(Ipv4Addr, Ipv6Addr, u32), Error> {
|
||||||
use log::error;
|
use log::error;
|
||||||
use matter::error::ErrorCode;
|
use matter::error::ErrorCode;
|
||||||
use nix::{net::if_::InterfaceFlags, sys::socket::SockaddrIn6};
|
use nix::{net::if_::InterfaceFlags, sys::socket::SockaddrIn6};
|
||||||
|
@ -276,7 +277,7 @@ fn initialize_network() -> Result<(Ipv4Addr, Ipv6Addr), Error> {
|
||||||
iname, ip, ipv6
|
iname, ip, ipv6
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok((ip, ipv6))
|
Ok((ip, ipv6, 0 as _))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "espidf")]
|
#[cfg(target_os = "espidf")]
|
||||||
|
@ -287,7 +288,7 @@ fn initialize_logger() {
|
||||||
|
|
||||||
#[cfg(target_os = "espidf")]
|
#[cfg(target_os = "espidf")]
|
||||||
#[inline(never)]
|
#[inline(never)]
|
||||||
fn initialize_network() -> Result<(Ipv4Addr, Ipv6Addr), Error> {
|
fn initialize_network() -> Result<(Ipv4Addr, Ipv6Addr, u32), Error> {
|
||||||
use core::time::Duration;
|
use core::time::Duration;
|
||||||
|
|
||||||
use embedded_svc::wifi::{AuthMethod, ClientConfiguration, Configuration};
|
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],
|
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
|
// Not OK of course, but for a demo this is good enough
|
||||||
// Wifi will continue to be available and working in the background
|
// Wifi will continue to be available and working in the background
|
||||||
core::mem::forget(wifi);
|
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,
|
_hostname: &str,
|
||||||
_ip: [u8; 4],
|
_ip: [u8; 4],
|
||||||
_ipv6: Option<[u8; 16]>,
|
_ipv6: Option<[u8; 16]>,
|
||||||
|
_interface: u32,
|
||||||
dev_det: &'a BasicInfoConfig<'a>,
|
dev_det: &'a BasicInfoConfig<'a>,
|
||||||
matter_port: u16,
|
matter_port: u16,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
|
|
@ -19,21 +19,19 @@ use super::{
|
||||||
Service, ServiceMode,
|
Service, ServiceMode,
|
||||||
};
|
};
|
||||||
|
|
||||||
const IP_BROADCAST_ADDRS: [(IpAddr, u16); 2] = [
|
const IP_BIND_ADDR: IpAddr = IpAddr::V6(Ipv6Addr::UNSPECIFIED);
|
||||||
(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, 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 MdnsTxBuf = MaybeUninit<[u8; MAX_TX_BUF_SIZE]>;
|
||||||
type MdnsRxBuf = MaybeUninit<[u8; MAX_RX_BUF_SIZE]>;
|
type MdnsRxBuf = MaybeUninit<[u8; MAX_RX_BUF_SIZE]>;
|
||||||
|
|
||||||
pub struct Mdns<'a> {
|
pub struct Mdns<'a> {
|
||||||
host: Host<'a>,
|
host: Host<'a>,
|
||||||
|
interface: u32,
|
||||||
dev_det: &'a BasicInfoConfig<'a>,
|
dev_det: &'a BasicInfoConfig<'a>,
|
||||||
matter_port: u16,
|
matter_port: u16,
|
||||||
services: RefCell<heapless::Vec<(heapless::String<40>, ServiceMode), 4>>,
|
services: RefCell<heapless::Vec<(heapless::String<40>, ServiceMode), 4>>,
|
||||||
|
@ -47,6 +45,7 @@ impl<'a> Mdns<'a> {
|
||||||
hostname: &'a str,
|
hostname: &'a str,
|
||||||
ip: [u8; 4],
|
ip: [u8; 4],
|
||||||
ipv6: Option<[u8; 16]>,
|
ipv6: Option<[u8; 16]>,
|
||||||
|
interface: u32,
|
||||||
dev_det: &'a BasicInfoConfig<'a>,
|
dev_det: &'a BasicInfoConfig<'a>,
|
||||||
matter_port: u16,
|
matter_port: u16,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
@ -57,6 +56,7 @@ impl<'a> Mdns<'a> {
|
||||||
ip,
|
ip,
|
||||||
ipv6,
|
ipv6,
|
||||||
},
|
},
|
||||||
|
interface,
|
||||||
dev_det,
|
dev_det,
|
||||||
matter_port,
|
matter_port,
|
||||||
services: RefCell::new(heapless::Vec::new()),
|
services: RefCell::new(heapless::Vec::new()),
|
||||||
|
@ -121,11 +121,10 @@ impl<'a> MdnsRunner<'a> {
|
||||||
let tx_pipe = &tx_pipe;
|
let tx_pipe = &tx_pipe;
|
||||||
let rx_pipe = &rx_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_v6(IPV6_BROADCAST_ADDR, self.0.interface)?;
|
||||||
udp.join_multicast(ip).await?;
|
udp.join_multicast_v4(IP_BROADCAST_ADDR, Ipv4Addr::from(self.0.host.ip))?;
|
||||||
}
|
|
||||||
|
|
||||||
let udp = &udp;
|
let udp = &udp;
|
||||||
|
|
||||||
|
@ -188,7 +187,10 @@ impl<'a> MdnsRunner<'a> {
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
for (addr, port) in IP_BROADCAST_ADDRS {
|
for addr in [
|
||||||
|
IpAddr::V4(IP_BROADCAST_ADDR),
|
||||||
|
IpAddr::V6(IPV6_BROADCAST_ADDR),
|
||||||
|
] {
|
||||||
loop {
|
loop {
|
||||||
let sent = {
|
let sent = {
|
||||||
let mut data = tx_pipe.data.lock().await;
|
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)?;
|
let len = self.0.host.broadcast(&self.0, data.buf, 60)?;
|
||||||
|
|
||||||
if len > 0 {
|
if len > 0 {
|
||||||
info!("Broadasting mDNS entry to {}:{}", addr, port);
|
info!("Broadasting mDNS entry to {}:{}", addr, PORT);
|
||||||
|
|
||||||
data.chunk = Some(Chunk {
|
data.chunk = Some(Chunk {
|
||||||
start: 0,
|
start: 0,
|
||||||
end: len,
|
end: len,
|
||||||
addr: Address::Udp(SocketAddr::new(addr, port)),
|
addr: Address::Udp(SocketAddr::new(addr, PORT)),
|
||||||
});
|
});
|
||||||
|
|
||||||
tx_pipe.data_supplied_notification.signal(());
|
tx_pipe.data_supplied_notification.signal(());
|
||||||
|
|
|
@ -27,7 +27,7 @@ mod smol_udp {
|
||||||
use log::{debug, info, warn};
|
use log::{debug, info, warn};
|
||||||
use smol::net::UdpSocket;
|
use smol::net::UdpSocket;
|
||||||
|
|
||||||
use crate::transport::network::{IpAddr, Ipv4Addr, SocketAddr};
|
use crate::transport::network::{Ipv4Addr, Ipv6Addr, SocketAddr};
|
||||||
|
|
||||||
pub struct UdpListener {
|
pub struct UdpListener {
|
||||||
socket: UdpSocket,
|
socket: UdpSocket,
|
||||||
|
@ -44,15 +44,71 @@ mod smol_udp {
|
||||||
Ok(listener)
|
Ok(listener)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn join_multicast(&mut self, ip_addr: IpAddr) -> Result<(), Error> {
|
pub fn join_multicast_v6(
|
||||||
match ip_addr {
|
&mut self,
|
||||||
IpAddr::V4(ip_addr) => self
|
multiaddr: Ipv6Addr,
|
||||||
.socket
|
interface: u32,
|
||||||
.join_multicast_v4(ip_addr, Ipv4Addr::UNSPECIFIED)?,
|
) -> Result<(), Error> {
|
||||||
IpAddr::V6(ip_addr) => self.socket.join_multicast_v6(&ip_addr, 0)?,
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue