Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
656 changes: 634 additions & 22 deletions Cargo.lock

Large diffs are not rendered by default.

21 changes: 16 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
[package]
name = "nifa"
version = "0.3.1"
version = "0.4.0"
edition = "2024"
authors = ["shellrow <shellrow@foctal.com>"]
description = "Cross-platform CLI tool for network information"
description = "Cross-platform network inspection tool"
repository = "https://github.com/shellrow/nifa"
homepage = "https://github.com/shellrow/nifa"
documentation = "https://github.com/shellrow/nifa"
Expand All @@ -14,28 +14,39 @@ license = "MIT"

[dependencies]
anyhow = { version = "1" }
chrono = { version = "0.4" }
tracing = { version = "0.1" }
tracing-subscriber = { version = "0.3", features = ["time", "chrono"] }
serde = { version = "1", features = ["derive"] }
serde_json = { version = "1" }
serde_yaml = { version = "0.9" }
netdev = { version = "0.38", features = ["serde"] }
mac-addr = { version = "0.3.0" }
netdev = { version = "0.39", features = ["serde"] }
netroute = { version = "0.3", features = ["serde"] }
netsock = { version = "0.5", features = ["serde"] }
tokio = { version = "1", features = ["rt-multi-thread", "macros", "time"] }
reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls", "rustls-tls-native-roots"] }
clap = { version = "4.5", features = ["derive", "cargo"] }
termtree = { version = "0.5" }
hostname = { version = "0.4" }
os_info = { version = "3.12" }
ndb-oui = { version = "0.3", features = ["bundled"] }
ndb-oui = { version = "0.4", features = ["bundled"] }
ratatui = "0.25"
crossterm = "0.27"
humansize = "2.1"
url = "2.5"
#tracing-subscriber = { version = "0.3", features = ["time", "chrono"] }
comfy-table = "7.2"
#home = { version = "0.5" }

[target.'cfg(unix)'.dependencies]
libc = "0.2"

[target.'cfg(target_os = "linux")'.dependencies]
netlink-packet-core = "0.8"
netlink-packet-route = "0.25"
netlink-sys = "0.8"
rtnetlink = "0.18"

[target.'cfg(target_os = "windows")'.dependencies]
windows-sys = { version = "0.59", features = ["Win32_System_SystemInformation", "Wdk_System_SystemServices"] }

Expand Down
37 changes: 20 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,18 @@
[license-badge]: https://img.shields.io/crates/l/nifa.svg

# nifa [![Crates.io][crates-badge]][crates-url] ![License][license-badge]
Cross-platform CLI tool for network information
Cross-platform network inspection tool - a modern, read-only alternative to classic network commands.

## Features
- List all network interfaces with detailed information
- Show complete details of a specific interface
- Monitor live traffic statistics in TUI
- Export snapshot in JSON or YAML for automation
- Inspect a specific interface with full metadata (addresses, DNS, gateway, speeds, flags, stats)
- View routing tables (IPv4/IPv6)
- View neighbor table (ARP/NDP) with optional vendor (OUI) lookup
- Inspect open TCP/UDP sockets with process association
- Monitor live per-interface traffic statistics in a TUI
- Fetch your public IPv4/IPv6
- Display system information along with default interface
- Display OS, kernel, proxy, permission capabilities, and default interface info
- Export view as JSON/YAML for automation

## Supported Platforms
- **Linux**
Expand All @@ -33,7 +36,7 @@ powershell -ExecutionPolicy Bypass -c "irm https://github.com/shellrow/nifa/rele
```

### From Releases
You can download archives of precompiled binaries from the [releases](https://github.com/shellrow/nifa/releases)
You can download precompiled binaries from the [releases](https://github.com/shellrow/nifa/releases)

### Using Cargo

Expand All @@ -46,24 +49,24 @@ cargo install nifa
Usage: nifa [OPTIONS] [COMMAND]

Commands:
list Show all interfaces
show Show details for specified interface
monitor Monitor traffic statistics for all interfaces
os Show OS/network stack/permission information
export Export snapshot as JSON/YAML
ifaces Show all interfaces
iface Show details for specified interface
monitor Monitor traffic statistics for interfaces in TUI
route Show routing tables (IPv4/IPv6)
neigh Show neighbor table (ARP/NDP)
socket Show open TCP/UDP sockets and associated processes
public Show public IP information
system Show OS / kernel / proxy / default interface
help Print this message or the help of the given subcommand(s)

Options:
-d, --default Show only default interface
-f, --format <FORMAT> Output format [default: tree] [possible values: tree, json, yaml]
--with-vendor With vendor info (OUI lookup)
-h, --help Print help
-V, --version Print version
-l, --log-level <LOG_LEVEL> Set log level [default: error] [possible values: error, warn, info, debug, trace]
-h, --help Print help
-V, --version Print version
```

See `nifa <sub-command> -h` for more detail.

## Note for Developers
If you are looking for a Rust library for network interface,
please check out [netdev](https://github.com/shellrow/netdev).
consider using [netdev](https://github.com/shellrow/netdev).
203 changes: 173 additions & 30 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,53 +4,76 @@ use clap::{Args, Parser, Subcommand, ValueEnum};

use crate::cmd::monitor::{SortKey, Unit};

/// nifa - Cross-platform CLI tool for network information
/// nifa - Cross-platform network inspection tool
#[derive(Debug, Parser)]
#[command(name = "nifa", author, version, about = "nifa - Cross-platform CLI tool for network information", long_about = None)]
#[command(name = "nifa", author, version, about = "nifa - Cross-platform network inspection tool", long_about = None)]
pub struct Cli {
/// Show only default interface
#[arg(short, long)]
pub default: bool,

/// Output format
#[arg(short='f', long, value_enum, default_value_t = OutputFormat::Tree)]
pub format: OutputFormat,

/// With vendor info (OUI lookup)
#[arg(long, default_value_t = false)]
pub with_vendor: bool,

/// Set log level
#[arg(short = 'l', long, value_enum, default_value_t = LogLevel::Error)]
pub log_level: LogLevel,
/// Subcommand
#[command(subcommand)]
pub command: Option<Command>,
}

#[derive(Debug, Clone, Copy, ValueEnum)]
pub enum LogLevel {
Error,
Warn,
Info,
Debug,
Trace,
}

impl LogLevel {
pub fn to_tracing_level(&self) -> tracing::Level {
match self {
LogLevel::Error => tracing::Level::ERROR,
LogLevel::Warn => tracing::Level::WARN,
LogLevel::Info => tracing::Level::INFO,
LogLevel::Debug => tracing::Level::DEBUG,
LogLevel::Trace => tracing::Level::TRACE,
}
}
}

#[derive(Debug, Clone, Copy, ValueEnum)]
pub enum OutputFormat {
Tree,
Table,
Json,
Yaml,
}

#[derive(Debug, Clone, Copy, ValueEnum)]
pub enum ExportFormat {
Json,
Yaml,
}

#[derive(Debug, Subcommand)]
pub enum Command {
/// Show all interfaces
List(ListArgs),
Ifaces(IfacesArgs),
/// Show details for specified interface
Show(ShowArgs),
/// Monitor traffic statistics for all interfaces
Iface(IfaceArgs),
/// Monitor traffic statistics for interfaces in TUI
Monitor(MonitorArgs),
/// Show OS/network stack/permission information
Os,
/// Export snapshot as JSON/YAML
Export(ExportArgs),
/// Show routing tables (IPv4/IPv6)
Route(RouteArgs),
/// Show neighbor table (ARP/NDP)
Neigh(NeighArgs),
/// Show open TCP/UDP sockets and associated processes
Socket(SocketArgs),
/// Show public IP information
Public(PublicArgs),
/// Show OS / kernel / proxy / default interface
System(SystemArgs),
}

/// List command arguments
/// Ifaces command arguments
#[derive(Args, Debug)]
pub struct ListArgs {
pub struct IfacesArgs {
/// Filter by name (supports partial match)
#[arg(long)]
pub name_like: Option<String>,
Expand All @@ -72,13 +95,37 @@ pub struct ListArgs {
/// Show interfaces with IPv6 address only
#[arg(long)]
pub ipv6: bool,
/// Output format
#[arg(short='f', long, value_enum, default_value_t = OutputFormat::Tree)]
pub format: OutputFormat,
/// Export data instead of printing to stdout
#[arg(long, value_enum)]
pub export: Option<ExportFormat>,
/// Output file for export
#[arg(short = 'o', long)]
pub output: Option<PathBuf>,
/// With vendor info (OUI lookup)
#[arg(long, default_value_t = false)]
pub vendor: bool,
}

/// Show command arguments
/// Iface command arguments
#[derive(Args, Debug)]
pub struct ShowArgs {
pub struct IfaceArgs {
/// Show details for specified interface
pub iface: String,
/// Output format
#[arg(short='f', long, value_enum, default_value_t = OutputFormat::Tree)]
pub format: OutputFormat,
/// Export data instead of printing to stdout
#[arg(long, value_enum)]
pub export: Option<ExportFormat>,
/// Output file for export
#[arg(short = 'o', long)]
pub output: Option<PathBuf>,
/// With vendor info (OUI lookup)
#[arg(long, default_value_t = false)]
pub vendor: bool,
}

/// Monitor command arguments
Expand All @@ -94,15 +141,21 @@ pub struct MonitorArgs {
#[arg(short = 'd', long, default_value = "1")]
pub interval: u64,
/// Display unit (bytes or bits)
#[arg(long, value_enum, default_value_t=Unit::Bytes)]
#[arg(long, value_enum, default_value_t=Unit::default())]
pub unit: Unit,
}

/// Export command arguments
/// System command arguments
#[derive(Args, Debug)]
pub struct ExportArgs {
/// Output file
#[arg(short, long)]
pub struct SystemArgs {
/// Output format
#[arg(short='f', long, value_enum, default_value_t = OutputFormat::Tree)]
pub format: OutputFormat,
/// Export data instead of printing to stdout
#[arg(long, value_enum)]
pub export: Option<ExportFormat>,
/// Output file for export
#[arg(short = 'o', long)]
pub output: Option<PathBuf>,
}

Expand All @@ -114,4 +167,94 @@ pub struct PublicArgs {
/// Timeout seconds
#[arg(long, default_value_t = 3)]
pub timeout: u64,
/// Output format
#[arg(short='f', long, value_enum, default_value_t = OutputFormat::Tree)]
pub format: OutputFormat,
/// Export data instead of printing to stdout
#[arg(long, value_enum)]
pub export: Option<ExportFormat>,
/// Output file for export
#[arg(short = 'o', long)]
pub output: Option<PathBuf>,
}

#[derive(clap::ValueEnum, Clone, Copy, Debug)]
pub enum RouteFamilyOpt {
All,
Ipv4,
Ipv6,
}

#[derive(Args, Debug)]
pub struct RouteArgs {
/// Family filter
#[arg(long, value_enum, default_value_t = RouteFamilyOpt::All)]
pub family: RouteFamilyOpt,
/// Output format
#[arg(short='f', long, value_enum, default_value_t = OutputFormat::Tree)]
pub format: OutputFormat,
/// Export data instead of printing to stdout
#[arg(long, value_enum)]
pub export: Option<ExportFormat>,
/// Output file for export
#[arg(short = 'o', long)]
pub output: Option<PathBuf>,
}

#[derive(Args, Debug)]
pub struct NeighArgs {
/// Output format
#[arg(short='f', long, value_enum, default_value_t = OutputFormat::Tree)]
pub format: OutputFormat,
/// Export data instead of printing to stdout
#[arg(long, value_enum)]
pub export: Option<ExportFormat>,
/// Output file for export
#[arg(short = 'o', long)]
pub output: Option<PathBuf>,
/// With vendor info (OUI lookup)
#[arg(long, default_value_t = false)]
pub vendor: bool,
}

#[derive(Debug, Clone, Copy, ValueEnum)]
pub enum SocketProto {
Tcp,
Udp,
All,
}

#[derive(Debug, Clone, Copy, ValueEnum)]
pub enum SocketFamily {
Ipv4,
Ipv6,
All,
}

#[derive(Args, Debug)]
pub struct SocketArgs {
/// Protocol filter
#[arg(long, value_enum, default_value = "all")]
pub proto: SocketProto,
/// Address family filter
#[arg(long, value_enum, default_value = "all")]
pub family: SocketFamily,
/// TCP state filter (established, listen, time_wait, all)
#[arg(long)]
pub state: Option<String>,
/// Filter by local or remote port
#[arg(long)]
pub port: Option<u16>,
/// Filter by PID
#[arg(long)]
pub pid: Option<u32>,
/// Output format
#[arg(short='f', long, value_enum, default_value_t = OutputFormat::Tree)]
pub format: OutputFormat,
/// Export data instead of printing to stdout
#[arg(long, value_enum)]
pub export: Option<ExportFormat>,
/// Output file for export
#[arg(short = 'o', long)]
pub output: Option<std::path::PathBuf>,
}
Loading