#packet #security #linux #pcap

af_packet

Rust 的 AF_PACKET 绑定,主要用于高性能网络安全应用

15 个版本

使用旧的 Rust 2015

0.3.1 2020 年 6 月 2 日
0.3.0 2018 年 6 月 20 日
0.2.7 2018 年 6 月 12 日
0.2.5 2018 年 5 月 31 日
0.1.1 2017 年 12 月 29 日

#660Unix API

GPL-3.0 许可证

25KB
554

高性能的 Rust AF_PACKET 绑定

Crates.io License: GPL v3 af_packet

这个库旨在提供一个高效、安全且易用的方式,在多线程中读取接口上的原始数据包。其主要用途是网络安全和监控应用,结合类似 nom (https://github.com/Geal/nom) 的 crate 来构建协议解码器和更复杂的分析引擎。

40 行代码的多线程原始接收器

Linux 内核提供基于散列元组的流平衡,因此线程不需要相互通信即可进行流重组。然而,这种行为是可以配置的。在 https://github.com/DominoTree/rs-dns-sniffer/blob/master/src/main.rs》可以看到一个约 70 行代码的全多线程 DNS 嗅探器。它在一台八核机器上每秒可以解码超过 120,000 条 DNS 消息而不会丢失任何帧,并且已经测试超过每秒 1,500,000 条记录。

extern crate af_packet;
extern crate num_cpus;

use std::env;
use std::thread;

fn main() {
    //get args
    let args: Vec<String> = env::args().collect();
    
    //create a vec for file descriptors to poll for statistics
    let mut fds = Vec::<i32>::new();

    //spawn one thread per CPU
    for _ in 0..num_cpus::get() {
        let interface = args[1].clone();
        
        //open an mmap()ed ring buffer for this thread
        let mut ring = af_packet::Ring::from_if_name(&interface).unwrap();
        
        //store the fd from the ring to get stats later
        fds.push(ring.fd);
        
        thread::spawn(move || {
            //move struct into the thread
            //receive blocks and process them
            loop {
                let mut block = ring.get_block();
                for _packet in block.get_raw_packets() {
                    //process frame data here
                }
                block.mark_as_consumed();
            }
        });
    }

    //Below is to print statistics only
    let mut packets: u64 = 0;
    let mut drops: u64 = 0;

    loop {
        let mut stats: (u64, u64) = (0, 0);
        for fd in &fds {
            let ring_stats = af_packet::get_rx_statistics(*fd).unwrap();
            stats.0 += ring_stats.tp_drops as u64;
            stats.1 += ring_stats.tp_packets as u64;
        }
        drops += stats.0;
        packets += stats.1;
        eprintln!("{} frames received per second, {} dropped. {} total drops of {} total packets ({}%)", stats.1, stats.0, drops, packets, format!("{:.*}", 4, drops as f64 / packets as f64 * 100 as f64));
        thread::sleep(std::time::Duration::from_secs(1));
    }
}

基于 Tom Karpiniec (http://thomask.sdf.org/blog/2017/09/01/layer-2-raw-sockets-on-rustlinux.html) 和 Herman Radtke (http://hermanradtke.com/2016/03/17/unions-rust-ffi.html) 的工作

依赖项

~1MB
~16K SLoC