1pub mod sys;
2
3use {
4 anyhow::{Context, Result},
5 nix::{ioctl_readwrite, ioctl_write_int_bad, request_code_none},
6 std::{
7 fmt,
8 fs::{File, OpenOptions},
9 io::Read,
10 os::unix::{
11 fs::OpenOptionsExt,
12 io::{AsRawFd, RawFd},
13 },
14 },
15 sys::*,
16};
17
18pub const EMPTY_MAC: &str = "00:00:00:00:00:00";
19const MAC_SIZE: usize = EMPTY_MAC.len();
20
21#[derive(Debug)]
23pub struct NetDevice {
24 adapter: u32,
25 device: u32,
26
27 file: File,
28}
29
30impl AsRawFd for NetDevice {
31 #[inline]
32 fn as_raw_fd(&self) -> RawFd {
33 self.file.as_raw_fd()
34 }
35}
36
37impl NetDevice {
38 pub fn open(adapter: u32, device: u32) -> Result<NetDevice> {
40 let path = format!("/dev/dvb/adapter{}/net{}", adapter, device);
41 let file = OpenOptions::new()
42 .read(true)
43 .write(true)
44 .custom_flags(::nix::libc::O_NONBLOCK)
45 .open(&path)
46 .with_context(|| format!("NET: failed to open device {}", &path))?;
47
48 let net = NetDevice {
49 adapter,
50 device,
51 file,
52 };
53
54 Ok(net)
55 }
56
57 pub fn add_if(&self, pid: u16, feedtype: u8) -> Result<NetInterface> {
59 let mut data = DvbNetIf {
60 pid,
61 if_num: 0,
62 feedtype,
63 };
64
65 ioctl_readwrite!(
67 #[inline]
68 ioctl_call,
69 b'o',
70 52,
71 DvbNetIf
72 );
73 unsafe { ioctl_call(self.as_raw_fd(), &mut data as *mut _) }.context("NET: add if")?;
74
75 Ok(NetInterface {
76 net: self,
77 if_num: data.if_num,
78 })
79 }
80
81 pub fn remove_if(&self, interface: NetInterface) -> Result<()> {
83 ioctl_write_int_bad!(
85 #[inline]
86 ioctl_call,
87 request_code_none!(b'o', 53)
88 );
89 unsafe { ioctl_call(self.as_raw_fd(), i32::from(interface.if_num)) }
90 .context("NET: remove if")?;
91
92 Ok(())
93 }
94}
95
96pub struct NetInterface<'a> {
97 net: &'a NetDevice,
98 if_num: u16,
99}
100
101impl<'a> fmt::Display for NetInterface<'a> {
102 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
103 if self.net.device == 0 {
104 write!(f, "dvb{}_{}", self.net.adapter, self.if_num)
105 } else {
106 write!(
107 f,
108 "dvb{}{}{}",
109 self.net.adapter, self.net.device, self.if_num
110 )
111 }
112 }
113}
114
115impl<'a> NetInterface<'a> {
116 pub fn get_mac(&self) -> String {
118 let path = format!("/sys/class/net/{}/address", self);
119 let file = match File::open(&path) {
120 Ok(v) => v,
121 _ => return EMPTY_MAC.to_owned(),
122 };
123
124 let mut mac = String::with_capacity(MAC_SIZE);
125 let result = file.take(MAC_SIZE as u64).read_to_string(&mut mac);
126
127 match result {
128 Ok(MAC_SIZE) => mac,
129 _ => EMPTY_MAC.to_owned(),
130 }
131 }
132}