如何使用ioctl与SIOCGIWESSID读取当前连接的SSID?
问题:使用
nix::ioctl_read读取当前连接SSID时遭遇ENOTTY错误 我想用ioctl读取当前连接的SSID,尝试通过nix::ioctl_read实现,但调用ioctl时遇到了“ENOTTY”错误,怀疑是传递给nix::ioctl_read的参数不正确。以下是我的代码:
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!() }
请问该传递什么参数才能让它正常工作?或者有没有其他可行的解决方案?
解决方案
ENOTTY错误的核心原因是ioctl命令与文件描述符不匹配,或结构体内存布局与C标准不一致。以下是具体修正步骤和替代方案:
1. 修正ioctl命令码定义
SIOCGIWESSID的正确值是0x8B1B,而非0x8B。nix的ioctl_read!宏的正确调用格式是:
const SIOCGIWESSID: u32 = 0x8B1B; ioctl_read!(read_essid, SIOCGIWESSID, Iwreq);
2. 修正C结构体映射
Linux的iwreq结构体有严格的内存布局要求:
ifr_name长度为IFNAMSIZ(通常是16字节,不是33)u是共用体,其中essid对应iw_point结构体,length为s32类型
修正后的结构体定义:
use nix::sys::socket::sockaddr; #[repr(C)] pub struct IwEssidData { length: i32, data: [u8; 33], // SSID最大32字节,预留1字节存终止符 flags: u32, } #[repr(C)] pub union IwReqUnion { essid: IwEssidData, addr: sockaddr, // 保留共用体的其他字段占位 } #[repr(C)] pub struct Iwreq { ifr_name: [u8; 16], u: IwReqUnion, }
3. 修正socket创建方式
无线扩展ioctl需要绑定IP协议的UDP socket,指定Protocol::Ip确保兼容性:
let fd = socket( AddressFamily::Inet, SockType::Datagram, SockFlag::empty(), Some(Protocol::Ip), ).unwrap();
4. 完整可运行代码
use nix::sys::socket::{AddressFamily, SockType, SockFlag, Protocol, socket}; use nix::ioctl_read; const SIOCGIWESSID: u32 = 0x8B1B; #[repr(C)] pub struct IwEssidData { length: i32, data: [u8; 33], flags: u32, } #[repr(C)] pub union IwReqUnion { essid: IwEssidData, } #[repr(C)] pub struct Iwreq { ifr_name: [u8; 16], u: IwReqUnion, } ioctl_read!(read_essid, SIOCGIWESSID, Iwreq); fn get_essid() -> Result<String, Box<dyn std::error::Error>> { const WESSID_MAX_LEN: usize = 32; const IFNAMSIZ: usize = 16; // 打开socket let fd = socket( AddressFamily::Inet, SockType::Datagram, SockFlag::empty(), Some(Protocol::Ip), )?; // 初始化接口名字段 let mut ifr_name = [0u8; IFNAMSIZ]; let interface_name = "wlp2s0\0"; ifr_name[..interface_name.len()].copy_from_slice(interface_name.as_bytes()); // 初始化iwreq结构体 let mut data = Iwreq { ifr_name, u: IwReqUnion { essid: IwEssidData { length: (WESSID_MAX_LEN + 1) as i32, data: [0; 33], flags: 0, }, }, }; // 调用ioctl unsafe { read_essid(fd, &mut data)?; } // 提取并转换SSID为字符串 let essid_len = data.u.essid.length as usize; let essid_bytes = &data.u.essid.data[..essid_len]; Ok(String::from_utf8_lossy(essid_bytes).trim_end_matches('\0').to_string()) } fn main() { match get_essid() { Ok(essid) => println!("当前连接SSID: {}", essid), Err(e) => eprintln!("错误: {}", e), } }
替代方案
如果不想直接操作ioctl,可以使用现成的Rust crate简化开发:
- wireless-utils:封装无线扩展操作,一行代码读取SSID:
use wireless_utils::Wireless; fn main() { let wireless = Wireless::new("wlp2s0").unwrap(); let essid = wireless.essid().unwrap(); println!("当前连接SSID: {}", essid); } - netlink-packet-wifi:通过Netlink接口获取无线信息,更现代的Linux原生方案。
内容的提问来源于stack exchange,提问作者Mahesh Bansod
相关产品推荐
相关产品推荐

