I want to read the SSID that I'm connect to.
I'm using ioctl to do so. I'm trying to read it using nix::ioctl_read
.
I'm new to this so I'm unsure if what I'm doing is correct.
Here's what I've tried.
const SIOCGIWESSID: u8 = 0x8B;
ioctl_read!(read_essid, SIOCGIWESSID, 0x8B1B, Iwreq);
#[derive(Debug)]
pub struct Essid {
length: u8,
pointer: [u8; 33],
}
#[derive(Debug)]
pub struct WreqData {
essid: Essid,
}
#[derive(Debug)]
#[repr(C)]
pub struct Iwreq {
frn_name: [u8; 33],
u: WreqData,
}
fn get_essid() -> String {
const WESSID_LEN: usize = 33;
let fd = socket(
AddressFamily::Inet,
SockType::Datagram,
SockFlag::empty(),
None,
)
.unwrap();
let pointer: [u8; WESSID_LEN] = [0; WESSID_LEN];
let mut ifr_tmp = [0u8; WESSID_LEN];
let interface_name = "wlp2s0\0";
ifr_tmp[..interface_name.len()].copy_from_slice(interface_name.as_bytes());
let mut data = Iwreq {
frn_name: ifr_tmp,
u: WreqData {
essid: Essid {
length: WESSID_LEN as u8,
pointer,
},
},
};
println!("{}", unsafe { read_essid(fd, &mut data).unwrap() });
println!("{:?}", data);
unimplemented!()
}
I get the error "ENOTTY" from the ioctl call. I think I'm passing incorrect arguments to ioctl_read.
What can I pass to ioctl_read!
to make it work? Or is there another solution I should be looking into?
Looks like you have two main problems:
struct iwreq
definition from the latest mainline v6.5
release: include/uapi/linux/wireless.h
)ioctl
numbers don't follow the newer convention. They instead use arbitrary hardcoded numbers. The nix
crate documentation refers to these as bad ioctl
s. You can therefore instead use the ioctl_read_bad!
macroHere is a modified version that appears to work on my machine:
#[repr(C)]
pub struct Essid {
pointer: *const c_char,
length: u16,
flags: u16,
}
impl Essid {
const fn new(pointer: *const c_char) -> Self {
Self {
pointer,
length: 33,
flags: 0,
}
}
}
#[repr(C)]
pub struct WreqData {
essid: Essid,
}
#[repr(C)]
pub struct Iwreq {
frn_name: [u8; 16],
u: WreqData,
}
impl Iwreq {
fn new(name: &str, buf: &[c_char; 33]) -> Self {
let mut frn_name = [0; 16];
frn_name[..name.len()].copy_from_slice(name.as_bytes());
Self {
frn_name,
u: WreqData {
essid: Essid::new(buf.as_ptr()),
},
}
}
}
nix::ioctl_read_bad!(read_essid, 0x8B1B, Iwreq);
fn get_essid(name: &str) -> anyhow::Result<String> {
let sock = socket::socket(
AddressFamily::Inet,
SockType::Datagram,
SockFlag::empty(),
None,
)?;
let buf = [0; 33];
let mut data = Iwreq::new(name, &buf);
unsafe { read_essid(sock.as_raw_fd(), &mut data) }?;
let ptr = buf.as_ptr();
Ok(unsafe { CStr::from_ptr(ptr) }.to_str()?.to_owned())
}
Alternatively, the same information could be obtained using netlink sockets. For example:
async fn get_essid(name: &str) -> anyhow::Result<String> {
let (connection, handle, _) = wl_nl80211::new_connection()?;
tokio::spawn(connection);
let mut ifaces = handle.interface().get().execute().await;
while let Ok(Some(iface)) = ifaces.try_next().await {
if !iface
.payload
.nlas
.iter()
.any(|nla| matches!(nla, Nl80211Attr::IfName(n) if n == name))
{
continue;
}
for nla in iface.payload.nlas {
if let Nl80211Attr::Ssid(ssid) = nla {
return Ok(ssid);
}
}
}
bail!("SSID not found")
}
I've uploaded this example/test code to GitLab if you want to look at it in full: https://gitlab.com/harryausten/ssid-query. Hope this helps!