use crate::from_iter;
use crate::ProcResult;
use std::collections::HashMap;
use crate::FileWrapper;
use byteorder::{ByteOrder, NativeEndian, NetworkEndian};
use hex;
use std::io::{BufRead, BufReader, Read};
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
use std::path::PathBuf;
#[derive(Debug, PartialEq)]
pub enum TcpState {
Established = 1,
SynSent,
SynRecv,
FinWait1,
FinWait2,
TimeWait,
Close,
CloseWait,
LastAck,
Listen,
Closing,
NewSynRecv,
}
impl TcpState {
pub fn from_u8(num: u8) -> Option<TcpState> {
match num {
0x01 => Some(TcpState::Established),
0x02 => Some(TcpState::SynSent),
0x03 => Some(TcpState::SynRecv),
0x04 => Some(TcpState::FinWait1),
0x05 => Some(TcpState::FinWait2),
0x06 => Some(TcpState::TimeWait),
0x07 => Some(TcpState::Close),
0x08 => Some(TcpState::CloseWait),
0x09 => Some(TcpState::LastAck),
0x0A => Some(TcpState::Listen),
0x0B => Some(TcpState::Closing),
0x0C => Some(TcpState::NewSynRecv),
_ => None,
}
}
pub fn to_u8(&self) -> u8 {
match self {
TcpState::Established => 0x01,
TcpState::SynSent => 0x02,
TcpState::SynRecv => 0x03,
TcpState::FinWait1 => 0x04,
TcpState::FinWait2 => 0x05,
TcpState::TimeWait => 0x06,
TcpState::Close => 0x07,
TcpState::CloseWait => 0x08,
TcpState::LastAck => 0x09,
TcpState::Listen => 0x0A,
TcpState::Closing => 0x0B,
TcpState::NewSynRecv => 0x0C,
}
}
}
#[derive(Debug, PartialEq)]
pub enum UdpState {
Established = 1,
Close = 7,
}
impl UdpState {
pub fn from_u8(num: u8) -> Option<UdpState> {
match num {
0x01 => Some(UdpState::Established),
0x07 => Some(UdpState::Close),
_ => None,
}
}
pub fn to_u8(&self) -> u8 {
match self {
UdpState::Established => 0x01,
UdpState::Close => 0x07,
}
}
}
#[derive(Debug, PartialEq)]
pub enum UnixState {
UNCONNECTED = 1,
CONNECTING = 2,
CONNECTED = 3,
DISCONNECTING = 4,
}
impl UnixState {
pub fn from_u8(num: u8) -> Option<UnixState> {
match num {
0x01 => Some(UnixState::UNCONNECTED),
0x02 => Some(UnixState::CONNECTING),
0x03 => Some(UnixState::CONNECTED),
0x04 => Some(UnixState::DISCONNECTING),
_ => None,
}
}
pub fn to_u8(&self) -> u8 {
match self {
UnixState::UNCONNECTED => 0x01,
UnixState::CONNECTING => 0x02,
UnixState::CONNECTED => 0x03,
UnixState::DISCONNECTING => 0x04,
}
}
}
#[derive(Debug)]
pub struct TcpNetEntry {
pub local_address: SocketAddr,
pub remote_address: SocketAddr,
pub state: TcpState,
pub rx_queue: u32,
pub tx_queue: u32,
pub inode: u32,
}
#[derive(Debug)]
pub struct UdpNetEntry {
pub local_address: SocketAddr,
pub remote_address: SocketAddr,
pub state: UdpState,
pub rx_queue: u32,
pub tx_queue: u32,
pub inode: u32,
}
#[derive(Debug)]
pub struct UnixNetEntry {
pub ref_count: u32,
pub socket_type: u16,
pub state: UnixState,
pub inode: u32,
pub path: Option<PathBuf>,
}
fn parse_addressport_str(s: &str) -> ProcResult<SocketAddr> {
let mut las = s.split(':');
let ip_part = expect!(las.next(), "ip_part");
let port = expect!(las.next(), "port");
let port = from_str!(u16, port, 16);
if ip_part.len() == 8 {
let bytes = expect!(hex::decode(&ip_part));
let ip_u32 = NetworkEndian::read_u32(&bytes);
let ip = Ipv4Addr::new(
(ip_u32 & 0xff) as u8,
((ip_u32 & 0xff << 8) >> 8) as u8,
((ip_u32 & 0xff << 16) >> 16) as u8,
((ip_u32 & 0xff << 24) >> 24) as u8,
);
Ok(SocketAddr::V4(SocketAddrV4::new(ip, port)))
} else if ip_part.len() == 32 {
let bytes = expect!(hex::decode(&ip_part));
let ip_a = NativeEndian::read_u32(&bytes[0..]);
let ip_b = NativeEndian::read_u32(&bytes[4..]);
let ip_c = NativeEndian::read_u32(&bytes[8..]);
let ip_d = NativeEndian::read_u32(&bytes[12..]);
let ip = Ipv6Addr::new(
((ip_a >> 16) & 0xffff) as u16,
((ip_a >> 0) & 0xffff) as u16,
((ip_b >> 16) & 0xffff) as u16,
((ip_b >> 0) & 0xffff) as u16,
((ip_c >> 16) & 0xffff) as u16,
((ip_c >> 0) & 0xffff) as u16,
((ip_d >> 16) & 0xffff) as u16,
((ip_d >> 0) & 0xffff) as u16,
);
Ok(SocketAddr::V6(SocketAddrV6::new(ip, port, 0, 0)))
} else {
Err(build_internal_error!(format!(
"Unable to parse {:?} as an address:port",
s
)))
}
}
pub fn read_tcp_table<R: Read>(reader: BufReader<R>) -> ProcResult<Vec<TcpNetEntry>> {
let mut vec = Vec::new();
for line in reader.lines().skip(1) {
let line = line?;
let mut s = line.split_whitespace();
s.next();
let local_address = expect!(s.next(), "tcp::local_address");
let rem_address = expect!(s.next(), "tcp::rem_address");
let state = expect!(s.next(), "tcp::st");
let mut tx_rx_queue = expect!(s.next(), "tcp::tx_queue:rx_queue").splitn(2, ':');
let tx_queue = from_str!(u32, expect!(tx_rx_queue.next(), "tcp::tx_queue"), 16);
let rx_queue = from_str!(u32, expect!(tx_rx_queue.next(), "tcp::rx_queue"), 16);
s.next();
s.next();
s.next();
s.next();
let inode = expect!(s.next(), "tcp::inode");
vec.push(TcpNetEntry {
local_address: parse_addressport_str(local_address)?,
remote_address: parse_addressport_str(rem_address)?,
rx_queue,
tx_queue,
state: expect!(TcpState::from_u8(from_str!(u8, state, 16))),
inode: from_str!(u32, inode),
});
}
Ok(vec)
}
pub fn read_udp_table<R: Read>(reader: BufReader<R>) -> ProcResult<Vec<UdpNetEntry>> {
let mut vec = Vec::new();
for line in reader.lines().skip(1) {
let line = line?;
let mut s = line.split_whitespace();
s.next();
let local_address = expect!(s.next(), "udp::local_address");
let rem_address = expect!(s.next(), "udp::rem_address");
let state = expect!(s.next(), "udp::st");
let mut tx_rx_queue = expect!(s.next(), "udp::tx_queue:rx_queue").splitn(2, ':');
let tx_queue: u32 = from_str!(u32, expect!(tx_rx_queue.next(), "udp::tx_queue"), 16);
let rx_queue: u32 = from_str!(u32, expect!(tx_rx_queue.next(), "udp::rx_queue"), 16);
s.next();
s.next();
s.next();
s.next();
let inode = expect!(s.next(), "udp::inode");
vec.push(UdpNetEntry {
local_address: parse_addressport_str(local_address)?,
remote_address: parse_addressport_str(rem_address)?,
rx_queue,
tx_queue,
state: expect!(UdpState::from_u8(from_str!(u8, state, 16))),
inode: from_str!(u32, inode),
});
}
Ok(vec)
}
pub fn tcp() -> ProcResult<Vec<TcpNetEntry>> {
let file = FileWrapper::open("/proc/net/tcp")?;
read_tcp_table(BufReader::new(file))
}
pub fn tcp6() -> ProcResult<Vec<TcpNetEntry>> {
let file = FileWrapper::open("/proc/net/tcp6")?;
read_tcp_table(BufReader::new(file))
}
pub fn udp() -> ProcResult<Vec<UdpNetEntry>> {
let file = FileWrapper::open("/proc/net/udp")?;
read_udp_table(BufReader::new(file))
}
pub fn udp6() -> ProcResult<Vec<UdpNetEntry>> {
let file = FileWrapper::open("/proc/net/udp6")?;
read_udp_table(BufReader::new(file))
}
pub fn unix() -> ProcResult<Vec<UnixNetEntry>> {
let file = FileWrapper::open("/proc/net/unix")?;
let reader = BufReader::new(file);
let mut vec = Vec::new();
for line in reader.lines().skip(1) {
let line = line?;
let mut s = line.split_whitespace();
s.next();
let ref_count = from_str!(u32, expect!(s.next()), 16);
s.next();
s.next();
let socket_type = from_str!(u16, expect!(s.next()), 16);
let state = from_str!(u8, expect!(s.next()), 16);
let inode = from_str!(u32, expect!(s.next()));
let path = s.next().map(|s| PathBuf::from(s));
vec.push(UnixNetEntry {
ref_count,
socket_type,
inode,
state: expect!(UnixState::from_u8(state)),
path,
});
}
Ok(vec)
}
#[derive(Debug, Clone)]
pub struct DeviceStatus {
pub name: String,
pub recv_bytes: u64,
pub recv_packets: u64,
pub recv_errs: u64,
pub recv_drop: u64,
pub recv_fifo: u64,
pub recv_frame: u64,
pub recv_compressed: u64,
pub recv_multicast: u64,
pub sent_bytes: u64,
pub sent_packets: u64,
pub sent_errs: u64,
pub sent_drop: u64,
pub sent_fifo: u64,
pub sent_colls: u64,
pub sent_carrier: u64,
pub sent_compressed: u64,
}
impl DeviceStatus {
fn from_str(s: &str) -> ProcResult<DeviceStatus> {
let mut split = s.trim().split_whitespace();
let name: String = expect!(from_iter(&mut split));
let recv_bytes = expect!(from_iter(&mut split));
let recv_packets = expect!(from_iter(&mut split));
let recv_errs = expect!(from_iter(&mut split));
let recv_drop = expect!(from_iter(&mut split));
let recv_fifo = expect!(from_iter(&mut split));
let recv_frame = expect!(from_iter(&mut split));
let recv_compressed = expect!(from_iter(&mut split));
let recv_multicast = expect!(from_iter(&mut split));
let sent_bytes = expect!(from_iter(&mut split));
let sent_packets = expect!(from_iter(&mut split));
let sent_errs = expect!(from_iter(&mut split));
let sent_drop = expect!(from_iter(&mut split));
let sent_fifo = expect!(from_iter(&mut split));
let sent_colls = expect!(from_iter(&mut split));
let sent_carrier = expect!(from_iter(&mut split));
let sent_compressed = expect!(from_iter(&mut split));
Ok(DeviceStatus {
name: name.trim_end_matches(':').to_owned(),
recv_bytes,
recv_packets,
recv_errs,
recv_drop,
recv_fifo,
recv_frame,
recv_compressed,
recv_multicast,
sent_bytes,
sent_packets,
sent_errs,
sent_drop,
sent_fifo,
sent_colls,
sent_carrier,
sent_compressed,
})
}
}
pub fn dev_status() -> ProcResult<HashMap<String, DeviceStatus>> {
let file = FileWrapper::open("/proc/net/dev")?;
let buf = BufReader::new(file);
let mut map = HashMap::new();
for line in buf.lines().skip(2) {
let dev = DeviceStatus::from_str(&line?)?;
map.insert(dev.name.clone(), dev);
}
Ok(map)
}
#[cfg(test)]
mod tests {
use super::*;
use std::net::IpAddr;
#[test]
fn test_parse_ipaddr() {
use std::str::FromStr;
let addr = parse_addressport_str("0100007F:1234").unwrap();
assert_eq!(addr.port(), 0x1234);
match addr.ip() {
IpAddr::V4(addr) => assert_eq!(addr, Ipv4Addr::new(127, 0, 0, 1)),
_ => panic!("Not IPv4"),
}
let addr = parse_addressport_str("5014002A14080140000000000E200000:0050").unwrap();
assert_eq!(addr.port(), 80);
match addr.ip() {
IpAddr::V6(addr) => assert_eq!(
addr,
Ipv6Addr::from_str("2a00:1450:4001:814::200e").unwrap()
),
_ => panic!("Not IPv6"),
}
let addr = parse_addressport_str("B80D01200000000067452301EFCDAB89:0").unwrap();
assert_eq!(addr.port(), 0);
match addr.ip() {
IpAddr::V6(addr) => assert_eq!(
addr,
Ipv6Addr::from_str("2001:db8::123:4567:89ab:cdef").unwrap()
),
_ => panic!("Not IPv6"),
}
let addr = parse_addressport_str("1234:1234");
assert!(addr.is_err());
}
#[test]
fn test_tcpstate_from() {
assert_eq!(TcpState::from_u8(0xA).unwrap(), TcpState::Listen);
}
#[test]
fn test_tcp() {
for entry in tcp().unwrap() {
println!("{:?}", entry);
}
}
#[test]
fn test_tcp6() {
for entry in tcp6().unwrap() {
println!("{:?}", entry);
}
}
#[test]
fn test_udp() {
for entry in udp().unwrap() {
println!("{:?}", entry);
}
}
#[test]
fn test_udp6() {
for entry in udp6().unwrap() {
println!("{:?}", entry);
}
}
#[test]
fn test_unix() {
for entry in unix().unwrap() {
println!("{:?}", entry);
}
}
#[test]
fn test_dev_status() {
let status = dev_status().unwrap();
println!("{:#?}", status);
}
}