From 21712ee95e8ce1551abde8ccc9ebd4640495b679 Mon Sep 17 00:00:00 2001 From: Andrey Dyldin Date: Thu, 4 Mar 2021 13:31:58 +0200 Subject: [PATCH] net device --- examples/cainfo.rs | 81 ++++++++++++++++++++++++-- examples/netinfo.rs | 48 +++++++++++++++ src/ca/mod.rs | 14 +++-- src/lib.rs | 5 ++ src/net/mod.rs | 138 ++++++++++++++++++++++++++++++++++++++++++++ src/net/sys.rs | 26 +++++++++ 6 files changed, 303 insertions(+), 9 deletions(-) create mode 100644 examples/netinfo.rs create mode 100644 src/net/mod.rs create mode 100644 src/net/sys.rs diff --git a/examples/cainfo.rs b/examples/cainfo.rs index 3d0e5fb..8bab4e3 100644 --- a/examples/cainfo.rs +++ b/examples/cainfo.rs @@ -1,10 +1,32 @@ use { std::{ path::Path, + os::unix::io::AsRawFd, }, anyhow::Result, + nix::{ + poll::{ + PollFd, + PollFlags, + poll, + }, + sys::{ + timerfd::{ + ClockId, + Expiration, + TimerFd, + TimerFlags, + TimerSetTimeFlags, + }, + time::{ + TimeSpec, + TimeValLike, + }, + }, + }, + libdvb::{ CaDevice, }, @@ -14,13 +36,64 @@ use { fn check_ca(path: &Path) -> Result<()> { println!("CA: {}", path.display()); - let mut ca = CaDevice::open(path, 0)?; + // let mut ca = CaDevice::open(path, 0)?; - // loop for about 3s - for _ in 0 .. 30 { - ca.poll()?; + let timer = TimerFd::new( + ClockId::CLOCK_MONOTONIC, + TimerFlags::all() + )?; + + timer.set( + Expiration::Interval( + TimeSpec::milliseconds(100) + ), + TimerSetTimeFlags::empty() + )?; + + let mut pool: Vec = Vec::new(); + + pool.push(PollFd::new( + timer.as_raw_fd(), + PollFlags::POLLIN + )); + + let instant = std::time::Instant::now(); + + for _ in 0 .. 10 { + let mut total = poll(&mut pool, -1)?; + // less than 0 not needed to check we got error in this case + // equal to 0 not needed to check we have no timeout + + for (i, item) in pool.iter().enumerate() { + let revent = item.revents().unwrap_or_else(PollFlags::empty); + if revent.is_empty() { + continue; + } + + if i == 0 { + timer.wait()?; + dbg!(instant.elapsed()); + } + + if total > 1 { + total -= 1; + } else { + break; + } + } + + // loop } + // let fd = PollFd::new( + // ca.as_raw_fd(), + // PollFlags::POLLIN, + // ); + + // TODO: CaPool + // TODO: timer CA_DELAY -> poll_timer() + // TODO: self.as_raw_fd() -> poll_event() + Ok(()) } diff --git a/examples/netinfo.rs b/examples/netinfo.rs new file mode 100644 index 0000000..4ff9dfe --- /dev/null +++ b/examples/netinfo.rs @@ -0,0 +1,48 @@ +use { + std::{ + path::Path, + }, + + anyhow::Result, + + libdvb::NetDevice, +}; + + +fn check_net(path: &Path) -> Result<()> { + println!("NET: {}", path.display()); + + let dev = NetDevice::open(path)?; + + let mut info = libdvb::net::sys::DvbNetIf { + pid: 0, + if_num: 0, + feedtype: libdvb::net::sys::DVB_NET_FEEDTYPE_MPE, + }; + + dev.add_if(&mut info)?; + println!("Interface: {}", dev.get_name()); + + let mac = match dev.get_mac() { + Ok(v) => v, + Err(e) => e.to_string(), + }; + println!("MAC: {}", mac); + + dev.remove_if(&info)?; + + Ok(()) +} + + +fn main() -> Result<()> { + let mut args = std::env::args().skip(1); + if let Some(path) = args.next() { + let path = Path::new(&path); + check_net(&path)?; + } else { + eprintln!("path to ca device is not defined"); + } + + Ok(()) +} diff --git a/src/ca/mod.rs b/src/ca/mod.rs index 63891b8..0608e7f 100644 --- a/src/ca/mod.rs +++ b/src/ca/mod.rs @@ -139,9 +139,7 @@ impl CaDevice { Ok(ca) } - pub fn poll(&mut self) -> Result<()> { - thread::sleep(CA_DELAY); - + fn poll_timer(&mut self) -> Result<()> { let flags = self.slot.flags; self.get_slot_info()?; @@ -166,8 +164,14 @@ impl CaDevice { } }; - // TODO: poll self.as_raw_fd() + // TODO: check queue? - unimplemented!() + Ok(()) + } + + fn poll_event(&mut self) -> Result<()> { + // TODO: tpdu read + + Ok(()) } } diff --git a/src/lib.rs b/src/lib.rs index 986748b..8246a2b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,6 +4,7 @@ extern crate anyhow; pub mod ca; pub mod fe; +pub mod net; pub use { @@ -15,4 +16,8 @@ pub use { FeDevice, FeStatus, }, + + net::{ + NetDevice, + }, }; diff --git a/src/net/mod.rs b/src/net/mod.rs new file mode 100644 index 0000000..d5dcdaa --- /dev/null +++ b/src/net/mod.rs @@ -0,0 +1,138 @@ +pub mod sys; + + +use { + std::{ + io::{ + Read, + }, + fs::{ + File, + OpenOptions, + }, + os::unix::{ + fs::{ + OpenOptionsExt, + }, + io::{ + AsRawFd, + RawFd, + }, + }, + path::Path, + }, + + anyhow::{ + Context, + Result, + }, + + nix::{ + fcntl::{ + readlink, + }, + sys::{ + stat::{ + fstat, + major, + minor, + }, + }, + + ioctl_readwrite, + ioctl_write_int_bad, + request_code_none, + }, + + sys::*, +}; + + +/// A reference to the network device +#[derive(Debug)] +pub struct NetDevice { + file: File, + + /// Interface name + name: String, +} + + +impl AsRawFd for NetDevice { + #[inline] + fn as_raw_fd(&self) -> RawFd { self.file.as_raw_fd() } +} + + +impl NetDevice { + /// Attempts to open a network device in read-write mode + pub fn open>(path: P) -> Result { + let file = OpenOptions::new() + .read(true) + .write(true) + .custom_flags(::nix::libc::O_NONBLOCK) + .open(path) + .context("NET: open")?; + + let s = fstat(file.as_raw_fd())?; + let sys_path = format!( + "/sys/dev/char/{}:{}", + major(s.st_rdev), + minor(s.st_rdev) + ); + let name = readlink(sys_path.as_str())? + .to_str() + .unwrap_or_default() + .rsplit('/') + .next() + .unwrap_or_default() + .split(".net") + .collect::>() + .join("_"); + + let net = NetDevice { + file, + name, + }; + + Ok(net) + } + + /// Returns interface name in format `dvb{0}_{1}` where `{0}` is adapter number + /// and `{1}` is a device number + pub fn get_name(&self) -> &str { self.name.as_str() } + + /// Reads and returns interface MAC address + pub fn get_mac(&self) -> Result { + let path = format!("/sys/class/net/{}/address", self.get_name()); + + let len = 2 * 6 + 5; + let file = File::open(&path)?; + let mut result = String::with_capacity(len); + file.take(2 * 6 + 5).read_to_string(&mut result)?; + + Ok(result) + } + + /// Creates a new network interface + pub fn add_if(&self, data: &mut DvbNetIf) -> Result<()> { + // NET_ADD_IF + ioctl_readwrite!(#[inline] ioctl_call, b'o', 52, DvbNetIf); + unsafe { + ioctl_call(self.as_raw_fd(), data as *mut _) + }.context("NET: add if")?; + + Ok(()) + } + + /// Removes a network interface + pub fn remove_if(&self, data: &DvbNetIf) -> Result<()> { + // NET_REMOVE_IF + ioctl_write_int_bad!(#[inline] ioctl_call, request_code_none!(b'o', 53)); + unsafe { + ioctl_call(self.as_raw_fd(), data.if_num as _) + }.context("NET: remove if")?; + + Ok(()) + } +} diff --git a/src/net/sys.rs b/src/net/sys.rs new file mode 100644 index 0000000..52546a2 --- /dev/null +++ b/src/net/sys.rs @@ -0,0 +1,26 @@ + +pub use { + feed_type::*, +}; + + +mod feed_type { + /// Multi Protocol Encapsulation (MPE) encoding + pub const DVB_NET_FEEDTYPE_MPE: u8 = 0; + /// Ultra Lightweight Encapsulation (ULE) encoding + pub const DVB_NET_FEEDTYPE_ULE: u8 = 1; +} + + +/// Describes a DVB network interface +/// Configures adapter to decapsulate IP packets from MPEG-TS stream +#[repr(C)] +#[derive(Debug)] +pub struct DvbNetIf { + /// Packet ID (PID) of the MPEG-TS that contains data + pub pid: u16, + /// Number of the Digital TV interface + pub if_num: u16, + /// Encapsulation type of the feed + pub feedtype: u8, +}