#[cfg(not(target_os = "linux"))]
use std::io::ErrorKind;
use std::io::{self, Error};
use std::sync::atomic::{AtomicBool, Ordering};
pub(crate) struct NetDev {
pub name: String,
pub ifindex: i32,
}
pub(crate) struct Ax25RawSocket {
#[cfg(target_os = "linux")]
fd: i32,
is_shutdown: AtomicBool,
}
impl Ax25RawSocket {
pub(crate) fn new() -> io::Result<Ax25RawSocket> {
#[cfg(target_os = "linux")]
{
sys::socket_new()
}
#[cfg(not(target_os = "linux"))]
{
Err(Error::new(ErrorKind::NotFound, "only supported on linux"))
}
}
pub(crate) fn list_ax25_interfaces(&self) -> io::Result<Vec<NetDev>> {
#[cfg(target_os = "linux")]
{
sys::socket_list_ax25_interfaces(self)
}
#[cfg(not(target_os = "linux"))]
{
Ok(Vec::new())
}
}
#[allow(unused_variables)]
pub(crate) fn send_frame(&self, frame: &[u8], ifindex: i32) -> io::Result<()> {
#[cfg(target_os = "linux")]
{
sys::socket_send_frame(self, frame, ifindex)
}
#[cfg(not(target_os = "linux"))]
{
Err(Error::new(
ErrorKind::NotConnected,
"only supported on linux",
))
}
}
#[allow(unused_variables)]
pub(crate) fn receive_frame(&self, ifindex: i32) -> io::Result<Vec<u8>> {
#[cfg(target_os = "linux")]
{
sys::socket_receive_frame(self, ifindex)
}
#[cfg(not(target_os = "linux"))]
{
Err(Error::new(
ErrorKind::NotConnected,
"only supported on linux",
))
}
}
pub(crate) fn shutdown(&self) {
if !self.is_shutdown.load(Ordering::SeqCst) {
self.is_shutdown.store(true, Ordering::SeqCst);
#[cfg(target_os = "linux")]
{
let _ = sys::socket_close(self);
}
}
}
}
impl Drop for Ax25RawSocket {
fn drop(&mut self) {
self.shutdown();
}
}
#[cfg(target_os = "linux")]
mod sys {
use super::*;
use libc::{
c_char, c_int, c_ulong, c_void, close, recvfrom, sendto, sockaddr_ll, socket, socklen_t,
AF_AX25, AF_PACKET, SOCK_RAW,
};
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::mem;
const ETH_P_AX25: u16 = 0x0002; const SIOCGIFHWADDR: c_ulong = 0x8927; const SIOCGIFINDEX: c_ulong = 0x8933;
pub(crate) fn socket_new() -> io::Result<Ax25RawSocket> {
match unsafe { socket(AF_PACKET, SOCK_RAW, ETH_P_AX25.to_be() as i32) } {
-1 => Err(Error::last_os_error()),
fd => Ok(Ax25RawSocket {
fd,
is_shutdown: AtomicBool::new(false),
}),
}
}
pub(crate) fn socket_close(socket: &Ax25RawSocket) -> io::Result<()> {
match unsafe { close(socket.fd) } {
-1 => Err(Error::last_os_error()),
_ => Ok(()),
}
}
pub(crate) fn socket_list_ax25_interfaces(socket: &Ax25RawSocket) -> io::Result<Vec<NetDev>> {
let dev_file = File::open("/proc/net/dev")?;
let mut devices: Vec<NetDev> = Vec::new();
let reader = BufReader::new(dev_file);
let lines = reader.lines();
for l in lines.skip(2).flatten() {
let device_name = l.trim().split(':').next().unwrap();
if let Some(net_dev) = get_ax25_netdev(device_name, socket.fd) {
devices.push(net_dev);
}
}
Ok(devices)
}
pub(crate) fn socket_send_frame(
socket: &Ax25RawSocket,
frame: &[u8],
ifindex: i32,
) -> io::Result<()> {
let mut prefixed_frame: Vec<u8> = Vec::with_capacity(frame.len() + 1);
prefixed_frame.push(0);
prefixed_frame.extend(frame.iter().cloned());
let sa = sockaddr_ll {
sll_family: AF_PACKET as u16,
sll_protocol: ETH_P_AX25.to_be(),
sll_ifindex: ifindex,
sll_hatype: 0,
sll_pkttype: 0,
sll_halen: 0,
sll_addr: [0; 8],
};
match unsafe {
let sa_ptr = &sa as *const libc::sockaddr_ll as *const libc::sockaddr;
sendto(
socket.fd,
prefixed_frame.as_ptr() as *const c_void,
prefixed_frame.len(),
0,
sa_ptr,
mem::size_of_val(&sa) as socklen_t,
)
} {
-1 => Err(Error::last_os_error()),
_ => Ok(()),
}
}
pub(crate) fn socket_receive_frame(
socket: &Ax25RawSocket,
ifindex: i32,
) -> io::Result<Vec<u8>> {
let mut buf: [u8; 1024] = [0; 1024];
let mut addr_struct: sockaddr_ll = unsafe { mem::zeroed() };
let mut len: usize;
loop {
unsafe {
let sa_ptr = &mut addr_struct as *mut libc::sockaddr_ll as *mut libc::sockaddr;
let mut sa_in_sz: socklen_t = mem::size_of::<sockaddr_ll>() as socklen_t;
len = match recvfrom(
socket.fd,
buf.as_mut_ptr() as *mut c_void,
buf.len(),
0,
sa_ptr,
&mut sa_in_sz,
) {
-1 => return Err(Error::last_os_error()),
len => len as usize,
};
if addr_struct.sll_ifindex == ifindex {
break;
}
}
}
let valid_buf = &buf[0..len];
let filtered: Vec<u8> = valid_buf.iter().skip_while(|&c| *c == 0).cloned().collect();
Ok(filtered)
}
fn get_ax25_netdev(name: &str, fd: i32) -> Option<NetDev> {
let mut req = ifreq::default();
let if_name = name.to_owned();
for (d, s) in req.ifr_name.iter_mut().zip(if_name.as_bytes()) {
*d = *s as c_char;
}
if unsafe { ioctl(fd, SIOCGIFHWADDR, &mut req) } == -1 {
return None;
}
if req.data.address_family() as i32 != AF_AX25 {
return None;
}
let hw_addr = req.data.ax25_address()?;
if unsafe { ioctl(fd, SIOCGIFINDEX, &mut req) } == -1 {
return None;
}
let ifindex = req.data.ifindex();
Some(NetDev {
name: hw_addr,
ifindex,
})
}
extern "C" {
fn ioctl(fd: c_int, request: c_ulong, ifreq: *mut ifreq) -> c_int;
}
#[derive(Default)]
#[repr(C)]
struct ifreq {
ifr_name: [c_char; 16],
data: ifreq_union,
}
#[derive(Default)]
#[repr(C)]
struct ifreq_union {
data: [u8; 24],
}
impl ifreq_union {
fn ifindex(&self) -> c_int {
c_int::from_be(
((self.data[0] as c_int) << 24)
+ ((self.data[1] as c_int) << 16)
+ ((self.data[2] as c_int) << 8)
+ (self.data[3] as c_int),
)
}
fn address_family(&self) -> u16 {
u16::from_be(((self.data[0] as u16) << 8) + (self.data[1] as u16))
}
fn ax25_address(&self) -> Option<String> {
let mut addr_utf8: Vec<u8> = self.data[2..8]
.iter()
.rev()
.map(|&c| c >> 1)
.skip_while(|&c| c == 0)
.collect::<Vec<u8>>();
addr_utf8.reverse();
let ssid = (self.data[8] >> 1) & 0x0f;
if let Ok(addr) = String::from_utf8(addr_utf8) {
return Some(format!("{}-{}", addr, ssid));
}
None
}
}
}