Skip to content

Commit

Permalink
#55 Rebranding to netdev
Browse files Browse the repository at this point in the history
  • Loading branch information
shellrow committed Feb 19, 2024
1 parent 441016d commit d97fc3a
Show file tree
Hide file tree
Showing 24 changed files with 511 additions and 312 deletions.
12 changes: 6 additions & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
[package]
name = "default-net"
version = "0.22.0"
authors = ["shellrow <shellrow@protonmail.com>"]
name = "netdev"
version = "0.23.0"
authors = ["shellrow <shellrow@foctet.com>"]
edition = "2021"
description = "Cross-platform library for network interface and gateway"
repository = "https://github.com/shellrow/default-net"
description = "Cross-platform library for network interface"
repository = "https://github.com/shellrow/netdev"
readme = "README.md"
keywords = ["network"]
categories = ["network-programming"]
Expand All @@ -31,7 +31,7 @@ version = "0.48.0"
features = ["Win32_Foundation","Win32_NetworkManagement_IpHelper", "Win32_Networking_WinSock", "Win32_NetworkManagement_Ndis"]

[target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies]
system-configuration = "0.5.1"
system-configuration = "0.6"

[dev-dependencies]
serde_json = "1.0"
Expand Down
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2021 shellrow
Copyright (c) 2024 shellrow

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
44 changes: 25 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,34 +1,40 @@
[crates-badge]: https://img.shields.io/crates/v/default-net.svg
[crates-url]: https://crates.io/crates/default-net
[license-badge]: https://img.shields.io/crates/l/default-net.svg
[examples-url]: https://github.com/shellrow/default-net/tree/main/examples
[crates-badge]: https://img.shields.io/crates/v/netdev.svg
[crates-url]: https://crates.io/crates/netdev
[license-badge]: https://img.shields.io/crates/l/netdev.svg
[examples-url]: https://github.com/shellrow/netdev/tree/main/examples
[doc-url]: https://docs.rs/netdev/latest/netdev
[doc-interface-url]: https://docs.rs/netdev/latest/netdev/interface/struct.Interface.html
[netdev-github-url]: https://github.com/shellrow/netdev
[netdev-crates-io-url]: https://crates.io/crates/netdev
[default-net-github-url]: https://github.com/shellrow/default-net
[default-net-crates-io-url]: https://crates.io/crates/default-net

# Notice
- This project has been rebranded to `netdev` and repository has been moved to the https://github.com/shellrow/netdev
- This crate has been moved to [netdev][netdev-crates-io-url] from `0.23`

# default-net [![Crates.io][crates-badge]][crates-url] ![License][license-badge]
# netdev [![Crates.io][crates-badge]][crates-url] ![License][license-badge]

`default-net` provides a cross-platform API for network interface and gateway.
`netdev` provides a cross-platform API for network interface.

## Key Features
- Get list of available network interfaces
- Get default network interface
- Access additional information related to network interface

Please refer to the [Interface][doc-interface-url] struct documentation for detail.

- Get default Network Interface and Gateway information
- Get list of available Network Interfaces
## Notice
This project was rebranded from [default-net][default-net-crates-io-url] by the author myself for future expansion, continuation, and better alignment with naming conventions.

## Supported platform
- Linux
- macOS
- Windows

## Usage
Add `default-net` to your dependencies
Add `netdev` to your dependencies
```toml:Cargo.toml
[dependencies]
default-net = "0.22"
netdev = "0.23"
```

For more details, see [examples][examples-url] or doc.
For more details, see [examples][examples-url] or [doc][doc-url].

## Tested on
- Linux
Expand All @@ -45,6 +51,6 @@ For more details, see [examples][examples-url] or doc.
- 13.4.1
- 11.6
- Windows
- 11 Pro 22H2 22621.1848
- 11 21H2 22000.493
- 10 21H2 19044.1586
- 11 Pro 22H2 22621.3155
- 11 22H2 22621.3155
- 10 21H2 19044.1586
5 changes: 3 additions & 2 deletions examples/default_gateway.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
// This example shows how to get the default gateway and its properties.

fn main() {
match default_net::get_default_gateway() {
match netdev::get_default_gateway() {
Ok(gateway) => {
println!("Default Gateway");
println!("\tMAC Address: {}", gateway.mac_addr);
println!("\tIP Address: {}", gateway.ip_addr);
println!("\tIPv4: {:?}", gateway.ipv4);
println!("\tIPv6: {:?}", gateway.ipv6);
}
Err(e) => {
println!("Error: {}", e);
Expand Down
7 changes: 5 additions & 2 deletions examples/default_interface.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// This example shows how to get the default network interface and its properties.

fn main() {
match default_net::get_default_interface() {
match netdev::get_default_interface() {
Ok(interface) => {
println!("Default Interface:");
println!("\tIndex: {}", interface.index);
Expand All @@ -28,10 +28,13 @@ fn main() {
if let Some(gateway) = interface.gateway {
println!("Default Gateway");
println!("\tMAC Address: {}", gateway.mac_addr);
println!("\tIP Address: {}", gateway.ip_addr);
println!("\tIPv4: {:?}", gateway.ipv4);
println!("\tIPv6: {:?}", gateway.ipv6);
} else {
println!("Default Gateway: (Not found)");
}
println!("DNS Servers: {:?}", interface.dns_servers);
println!("Default: {}", interface.default);
}
Err(e) => {
println!("Error: {}", e);
Expand Down
7 changes: 5 additions & 2 deletions examples/list_interfaces.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// This example shows all interfaces and their properties.

fn main() {
let interfaces = default_net::get_interfaces();
let interfaces = netdev::get_interfaces();
for interface in interfaces {
println!("Interface:");
println!("\tIndex: {}", interface.index);
Expand All @@ -28,10 +28,13 @@ fn main() {
if let Some(gateway) = interface.gateway {
println!("Gateway");
println!("\tMAC Address: {}", gateway.mac_addr);
println!("\tIP Address: {}", gateway.ip_addr);
println!("\tIPv4 Address: {:?}", gateway.ipv4);
println!("\tIPv6 Address: {:?}", gateway.ipv6);
} else {
println!("Gateway: (Not found)");
}
println!("DNS Servers: {:?}", interface.dns_servers);
println!("Default: {}", interface.default);
println!();
}
}
2 changes: 1 addition & 1 deletion examples/serialize.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// This example shows how to use serde feature to serialize the default network interface to JSON.
fn main() {
match default_net::get_default_interface() {
match netdev::get_default_interface() {
Ok(interface) => match serde_json::to_string_pretty(&interface) {
Ok(json) => {
println!("{}", json);
Expand Down
14 changes: 7 additions & 7 deletions src/bpf/unix.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use super::binding;
use crate::interface::Interface;
use crate::socket::{DataLinkReceiver, DataLinkSender};
use crate::socket::{FrameReceiver, FrameSender};

use std::collections::VecDeque;
use std::ffi::CString;
Expand Down Expand Up @@ -187,7 +187,7 @@ pub fn channel(interface_name: String, config: Config) -> io::Result<crate::sock
}

let fd = Arc::new(FileDesc { fd: fd });
let mut sender = Box::new(DataLinkSenderImpl {
let mut sender = Box::new(FrameSenderImpl {
fd: fd.clone(),
fd_set: unsafe { mem::zeroed() },
write_buffer: vec![0; config.write_buffer_size],
Expand All @@ -198,7 +198,7 @@ pub fn channel(interface_name: String, config: Config) -> io::Result<crate::sock
libc::FD_ZERO(&mut sender.fd_set as *mut libc::fd_set);
libc::FD_SET(fd.fd, &mut sender.fd_set as *mut libc::fd_set);
}
let mut receiver = Box::new(DataLinkReceiverImpl {
let mut receiver = Box::new(FrameReceiverImpl {
fd: fd.clone(),
fd_set: unsafe { mem::zeroed() },
read_buffer: vec![0; allocated_read_buffer_size],
Expand All @@ -213,15 +213,15 @@ pub fn channel(interface_name: String, config: Config) -> io::Result<crate::sock
Ok(crate::socket::Channel::Ethernet(sender, receiver))
}

struct DataLinkSenderImpl {
struct FrameSenderImpl {
fd: Arc<FileDesc>,
fd_set: libc::fd_set,
write_buffer: Vec<u8>,
loopback: bool,
timeout: Option<libc::timespec>,
}

impl DataLinkSender for DataLinkSenderImpl {
impl FrameSender for FrameSenderImpl {
#[inline]
fn build_and_send(
&mut self,
Expand Down Expand Up @@ -315,7 +315,7 @@ impl DataLinkSender for DataLinkSenderImpl {
}
}

struct DataLinkReceiverImpl {
struct FrameReceiverImpl {
fd: Arc<FileDesc>,
fd_set: libc::fd_set,
read_buffer: Vec<u8>,
Expand All @@ -324,7 +324,7 @@ struct DataLinkReceiverImpl {
packets: VecDeque<(usize, usize)>,
}

impl DataLinkReceiver for DataLinkReceiverImpl {
impl FrameReceiver for FrameReceiverImpl {
fn next(&mut self) -> io::Result<&[u8]> {
let (header_size, buffer_offset) = if self.loopback {
(4, ETHERNET_HEADER_SIZE)
Expand Down
28 changes: 28 additions & 0 deletions src/device.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};

use crate::mac::MacAddr;
use std::net::{Ipv4Addr, Ipv6Addr};

/// Structure of NetworkDevice information
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct NetworkDevice {
/// MAC address of the device
pub mac_addr: MacAddr,
/// List of IPv4 address of the device
pub ipv4: Vec<Ipv4Addr>,
/// List of IPv6 address of the device
pub ipv6: Vec<Ipv6Addr>,
}

impl NetworkDevice {
/// Construct a new NetworkDevice instance
pub fn new() -> NetworkDevice {
NetworkDevice {
mac_addr: MacAddr::zero(),
ipv4: Vec::new(),
ipv6: Vec::new(),
}
}
}
97 changes: 73 additions & 24 deletions src/gateway/linux.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
use super::Gateway;
use crate::device::NetworkDevice;
use crate::mac::MacAddr;
use std::collections::HashMap;
use std::fs::read_to_string;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use std::net::{Ipv4Addr, Ipv6Addr};
use std::str::FromStr;

const PATH_PROC_NET_ROUTE: &str = "/proc/net/route";
const PATH_PROC_NET_IPV6_ROUTE: &str = "/proc/net/ipv6_route";
const PATH_PROC_NET_ARP: &str = "/proc/net/arp";

fn convert_hex_ipv4(hex_ip: &str) -> Ipv4Addr {
Expand All @@ -17,7 +20,6 @@ fn convert_hex_ipv4(hex_ip: &str) -> Ipv4Addr {
Ipv4Addr::new(o1, o2, o3, o4)
}

#[allow(dead_code)]
fn convert_hex_ipv6(hex_ip: &str) -> Ipv6Addr {
if hex_ip.len() != 32 {
return Ipv6Addr::UNSPECIFIED;
Expand All @@ -33,44 +35,91 @@ fn convert_hex_ipv6(hex_ip: &str) -> Ipv6Addr {
Ipv6Addr::new(h1, h2, h3, h4, h5, h6, h7, h8)
}

pub fn get_default_gateway(interface_name: String) -> Result<Gateway, String> {
match super::send_udp_packet() {
Ok(_) => {}
Err(e) => return Err(format!("Failed to send UDP packet {}", e)),
fn get_arp_map() -> HashMap<Ipv4Addr, MacAddr> {
let mut arp_map: HashMap<Ipv4Addr, MacAddr> = HashMap::new();
let arp_data = read_to_string(PATH_PROC_NET_ARP);
let arp_text = match arp_data {
Ok(content) => content,
Err(_) => String::new(),
};
let arp_table: Vec<&str> = arp_text.trim().split("\n").collect();
for row in arp_table {
let mut fields: Vec<&str> = row.split(" ").collect();
fields.retain(|value| *value != "");
if fields.len() >= 6 {
// fields[0]: IP Address
// fields[3]: MAC Address (colon-separated string of hex format)
// fields[5]: Interface Name
match Ipv4Addr::from_str(fields[0]) {
Ok(ipv4_addr) => {
arp_map.insert(ipv4_addr, MacAddr::from_hex_format(fields[3]));
}
Err(_) => {}
}
}
}
arp_map
}

fn get_ipv4_gateway_map() -> HashMap<String, Ipv4Addr> {
let mut ipv4_gateway_map: HashMap<String, Ipv4Addr> = HashMap::new();
let route_data = read_to_string(PATH_PROC_NET_ROUTE);
let route_text = match route_data {
Ok(content) => content,
Err(_) => String::new(),
};
let route_table: Vec<&str> = route_text.trim().split("\n").collect();
let mut gateway_ip: IpAddr = IpAddr::V4(Ipv4Addr::UNSPECIFIED);
for row in route_table {
let fields: Vec<&str> = row.split("\t").collect();
if fields.len() >= 3 {
if fields[0] == interface_name && fields[2] != "00000000" {
gateway_ip = IpAddr::V4(convert_hex_ipv4(fields[2]));
// fields[0]: Interface Name
// fields[2]: IPv4 Address 8 hex chars
if fields[2] != "00000000" {
ipv4_gateway_map.insert(fields[0].to_string(), convert_hex_ipv4(fields[2]));
}
}
}
let arp_data = read_to_string(PATH_PROC_NET_ARP);
let arp_text = match arp_data {
ipv4_gateway_map
}

fn get_ipv6_gateway_map() -> HashMap<String, Ipv6Addr> {
let mut ipv6_gateway_map: HashMap<String, Ipv6Addr> = HashMap::new();
let route_data = read_to_string(PATH_PROC_NET_IPV6_ROUTE);
let route_text = match route_data {
Ok(content) => content,
Err(_) => String::new(),
};
let arp_table: Vec<&str> = arp_text.trim().split("\n").collect();
for row in arp_table {
let mut fields: Vec<&str> = row.split(" ").collect();
fields.retain(|value| *value != "");
if fields.len() >= 6 {
if fields[0] == gateway_ip.to_string() && fields[5] == interface_name {
let gateway: Gateway = Gateway {
mac_addr: MacAddr::from_hex_format(fields[3]),
ip_addr: gateway_ip,
};
return Ok(gateway);
let route_table: Vec<&str> = route_text.trim().split("\n").collect();
for row in route_table {
let fields: Vec<&str> = row.split("\t").collect();
if fields.len() >= 10 {
// fields[4]: IPv6 Address 32 hex chars without colons
// fields[9]: Interface Name
if fields[4] != "00000000000000000000000000000000" {
ipv6_gateway_map.insert(fields[9].to_string(), convert_hex_ipv6(fields[4]));
}
}
}
Err(String::new())
ipv6_gateway_map
}

pub fn get_gateway_map() -> HashMap<String, NetworkDevice> {
match super::send_udp_packet() {
Ok(_) => {}
Err(_) => {}
}
let mut gateway_map: HashMap<String, NetworkDevice> = HashMap::new();
let arp_map: HashMap<Ipv4Addr, MacAddr> = get_arp_map();
for (if_name, ipv4_addr) in get_ipv4_gateway_map() {
let gateway = gateway_map.entry(if_name).or_insert(NetworkDevice::new());
if let Some(mac_addr) = arp_map.get(&ipv4_addr) {
gateway.mac_addr = mac_addr.clone();
}
gateway.ipv4.push(ipv4_addr);
}
for (if_name, ipv6_addr) in get_ipv6_gateway_map() {
let gateway = gateway_map.entry(if_name).or_insert(NetworkDevice::new());
gateway.ipv6.push(ipv6_addr);
}
gateway_map
}
Loading

0 comments on commit d97fc3a

Please sign in to comment.