Skip to content

获取网络接口

pnet

pnet是Rust语言中的一个网络库,主要用于构建网络应用程序。

pnet的主要功能和作用包括:

  • 提供低级网络接口,可以直接操作网络包、协议等。
  • 支持常见网络协议如TCP、UDP等。
  • 实现网络数据包的封装和解析功能。
  • 提供跨平台支持,同时支持Linux、Windows和MacOS。
  • 与其他Rust网络库如Tokio等很好集成。

Rust的libpnet库底层使用了libpcap库来实现网络数据包捕获和处理的功能。

libpnet是一个基于Rust语言的网络编程库,提供了对网络协议的解析、构建和发送功能。它建立在libpcap(或者Windows上的WinPcap)之上,通过调用libpcap提供的底层功能来进行网络数据包捕获。

libpcap(Packet Capture Library)是一个跨平台的网络数据包捕获库,广泛用于网络分析和网络安全领域。它提供了一组API,允许开发人员在应用程序中以编程方式捕获和处理网络数据包。

libpnet库在其底层实现中使用libpcap来访问网络接口、捕获数据包、解析协议以及构建和发送数据包。这使得libpnet能够提供强大的网络编程功能,并且可以与现有的网络工具和库进行集成。

使用libpnet库时,需要确保安装了libpcap库及其开发包,以便在编译和运行时能够正确地链接和使用libpcap

获取本机活跃的网口名称(其实就是网卡,有实体的,也有虚拟的)

本部分内容参考自 rust 使用pnet获取本地活动的网卡

Rust
use std::net::Ipv4Addr; // 导入Ipv4Addr结构体
use pnet::datalink; // 导入datalink模块
use pnet::ipnetwork; // 导入ipnetwork模块

fn main() {
    let interfaces = datalink::interfaces(); // 获取所有网络接口信息

    for interface in interfaces {
        let ip: Vec<Ipv4Addr> = interface.ips.iter().map(|ip| match ip {
            ipnetwork::IpNetwork::V4(ref ipv4) => Ok(ipv4.ip()), // 提取IPv4地址
            _ => Err(""), // 其他类型的地址暂时忽略
        }).filter_map(Result::ok).collect(); // 过滤出成功匹配的IPv4地址,并收集到向量中

        #[cfg(unix)] // Unix系统条件编译
        if !ip.is_empty() && !interface.is_loopback() && interface.is_running() && interface.is_up() {
            println!("{}", interface.name); // 打印接口名称
        }

        #[cfg(not(unix))] // 非Unix系统条件编译
        if !ip.is_empty() && !interface.is_loopback() && interface.is_running() && interface.is_up() {
            println!("{}", interface.name); // 打印接口名称
        }
    }
}

关闭VPN

img

开启VPN

img

上面这段代码的作用是获取本地计算机上的网络接口信息,并打印出满足特定条件的接口的名称。

  1. 使用datalink::interfaces()函数获取本地计算机上的所有网络接口信息,并将其存储在interfaces变量中。
  2. 针对每个网络接口进行迭代处理。
  3. 对于每个接口,提取其中的IPv4地址,并将其存储在ip变量中。
  4. 根据操作系统类型(Unix或非Unix),在满足以下条件的情况下打印接口的名称:
    1. 接口的IPv4地址列表非空。
    2. 接口不是回环接口(Loopback)。
    3. 接口正在运行且处于启用状态。
  5. 如果满足条件,将打印出满足条件的接口的名称。

用于获取本地计算机上的活跃网络接口,并输出满足特定条件的接口的名称。这在诸如网络监控、网络配置等应用场景非常有用。

关于"eth0"和"tun3",这是两种不同类型的网络接口,简言之,"eth0"是一种物理以太网接口,通常用于常规的网络通信,而"tun3"是一种虚拟网络接口,通常用于建立安全的隧道连接。

二者详细的不同功能和特点:

  1. eth0:
    1. "eth0"是一种以太网接口,通常用于连接本地计算机与局域网或广域网的物理网络连接。
    2. 它是基于以太网协议(Ethernet)的网络接口,支持传输各种类型的数据包,如IP、TCP、UDP等。
    3. "eth0"通常用于常规的网络通信,如通过网络访问互联网、与其他计算机进行通信等。
  2. tun3:
    1. "tun3"是一种虚拟网络接口,通常用于建立虚拟私有网络(VPN)或隧道连接。
    2. 它是在操作系统内核中创建的虚拟接口,可用于在公共网络上创建安全的、私密的通信通道。
    3. "tun3"接口通过将数据包封装在其他协议中(如IPsec、OpenVPN等)来实现安全的通信。
    4. "tun3"通常用于远程访问、跨网络连接、保护敏感数据等场景。

监听指定网络接口上的网络流量,并对接收到的数据包进行解析和处理

本部分内容参考自 007 Rust 网络编程,libpnet 库介绍

使用pnet库来实现网络数据包的捕获和解析

Rust
use pnet::datalink::Channel::Ethernet; // 导入以太网通道
use pnet::datalink::{self, NetworkInterface}; // 导入datalink模块中的相关项
use pnet::packet::ethernet::{EtherTypes, EthernetPacket}; // 导入以太网数据包相关项
use pnet::packet::ip::IpNextHeaderProtocols; // 导入IP协议相关项
use pnet::packet::ipv4::Ipv4Packet; // 导入IPv4数据包相关项
use pnet::packet::tcp::TcpPacket; // 导入TCP数据包相关项
use pnet::packet::Packet; // 导入数据包trait

use std::env; // 导入env模块

fn handle_packet(ethernet: &EthernetPacket) {
    // 对Ipv4的包按层解析
    match ethernet.get_ethertype() {
        EtherTypes::Ipv4 => {
            // 如果是IPv4数据包
            let header = Ipv4Packet::new(ethernet.payload()); // 解析IPv4头部
            if let Some(header) = header {
                match header.get_next_level_protocol() {
                    IpNextHeaderProtocols::Tcp => {
                        // 如果是TCP协议
                        let tcp = TcpPacket::new(header.payload()); // 解析TCP头部
                        if let Some(tcp) = tcp {
                            println!(
                                "Got a TCP packet {}:{} to {}:{}",
                                header.get_source(),
                                tcp.get_source(),
                                header.get_destination(),
                                tcp.get_destination()
                            );
                        }
                    }
                    _ => println!("Ignoring non TCP packet"), // 忽略其他非TCP协议
                }
            }
        }
        _ => println!("Ignoring non IPv4 packet"), // 忽略非IPv4数据包
    }
}

fn main() {
    let interface_name = env::args().nth(1).unwrap(); // 获取命令行参数中的接口名称

    // 获取网卡列表
    let interfaces = datalink::interfaces();
    let interface = interfaces
        .into_iter()
        .filter(|iface: &NetworkInterface| iface.name == interface_name) // 根据接口名称过滤网卡列表
        .next()
        .expect("Error getting interface"); // 如果找不到匹配的接口,打印错误消息并退出

    let (_tx, mut rx) = match datalink::channel(&interface, Default::default()) {
        // 创建数据链路层通道,用于接收和发送数据包
        Ok(Ethernet(tx, rx)) => (tx, rx), // 如果通道类型是以太网通道,则将发送和接收通道分别赋值给_tx和rx
        Ok(_) => panic!("Unhandled channel type"), // 如果是其他类型的通道,抛出错误
        Err(e) => panic!(
            "An error occurred when creating the datalink channel: {}",
            e
        ), // 如果创建通道时发生错误,打印错误消息并退出
    };

    loop {
        // 获取收到的包
        match rx.next() {
            Ok(packet) => {
                let packet = EthernetPacket::new(packet).unwrap(); // 解析以太网数据包
                handle_packet(&packet); // 处理接收到的数据包
            }
            Err(e) => {
                panic!("An error occurred while reading: {}", e); // 如果读取数据包时发生错误,打印错误消息并退出
            }
        }
    }
}

img

其中en0是要监听的网卡名称,可以通过ifconfig命令,或者第一部分的代码拿到

代码的执行流程如下:

  1. 导入所需的库和模块。
  2. 定义了一个handle_packet函数,用于处理接收到的数据包。在函数内部,它首先检查数据包的以太网类型,如果是IPv4数据包,则进一步解析IPv4头部。如果是TCP协议的数据包,则解析TCP头部,并打印源IP地址、源端口、目的IP地址和目的端口。
  3. main函数中,获取命令行参数中指定的网络接口名称。
  4. 调用datalink::interfaces()函数获取所有可用的网络接口列表,并根据指定的接口名称过滤出匹配的接口。
  5. 使用过滤得到的接口,调用datalink::channel函数创建一个以太网通道,用于接收数据包。
  6. 进入一个无限循环,在循环中不断接收数据包并调用handle_packet函数进行处理。
  7. 如果在接收数据包或处理过程中发生错误,将打印错误消息并退出程序。

通过这些,该代码就可以用于实时监听和分析指定网络接口上的TCP流量。

如果用来监听vpn创建的隧道,则会报错:

Rust
thread 'main' panicked at /Users/hb24050/.cargo/registry/src/index.crates.io-6f17d22bba15001f/pnet_datalink-0.34.0/src/bpf.rs:416:44:
misaligned pointer dereference: address must be a multiple of 0x4 but is 0x14100980e
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
thread caused non-unwinding panic. aborting.
[1]    31049 abort      sudo cargo run utun6

img

前端知识体系 · wcrane