Rust网络过滤库nftnl-sys的使用:高效操作Linux netfilter规则集的Rust绑定

Rust网络过滤库nftnl-sys的使用:高效操作Linux netfilter规则集的Rust绑定

nftnl-sys是libnftnl的低级FFI绑定,libnftnl是一个用户空间库,提供对内核nf_tables子系统的低级netlink编程接口(API)。

链接到libmnl和libnftnl

默认情况下,此crate使用pkg-config查找并链接到其C依赖项libmnl和libnftnl。要手动配置查找这些库的位置,请设置环境变量LIBMNL_LIB_DIR和LIBNFTNL_LIB_DIR指向libmnl.so(或libmnl.a)和libnftnl.so(或libnftnl.a)所在的目录。

选择libnftnl版本

此crate具有大多数libnftnl版本的绑定。所有绑定都由bindgen通过此存储库中的generate_bindings.sh脚本生成。

只能通过此crate暴露一个版本的libnftnl。默认情况下,crate导出最旧支持版本的绑定(libnftnl-1.0.6)。要获取较新版本,请激活相应功能。有关可用功能/版本,请参见Cargo.toml。

例如,要获取libnftnl-1.0.9的绑定,请像这样依赖此crate:

[dependencies]
nftnl-sys = { version = "0.1", features = ["nftnl-1-0-9"] }

完整示例代码

以下是一个更完整的nftnl-sys使用示例,展示了如何创建表、链和规则:

use nftnl_sys as nftnl;
use std::ptr;
use std::os::raw::c_int;
use libc::{AF_NETLINK, SOCK_RAW, NETLINK_NETFILTER, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP};

fn main() -> Result<(), String> {
    unsafe {
        // 创建netlink套接字
        let sock = libc::socket(AF_NETLINK, SOCK_RAW | libc::SOCK_CLOEXEC, NETLINK_NETFILTER as c_int);
        if sock < 0 {
            return Err("Failed to create socket".to_string());
        }

        // 绑定套接字
        let mut addr = libc::sockaddr_nl {
            nl_family: AF_NETLINK as u16,
            nl_pad: 0,
            nl_pid: 0,
            nl_groups: 0,
        };

        if libc::bind(sock, &addr as *const _ as *const _, std::mem::size_of_val(&addr) as u32) < 0 {
            return Err("Failed to bind socket".to_string());
        }

        // 创建批处理
        let batch = nftnl::nftnl_batch_alloc();
        if batch.is_null() {
            return Err("Failed to allocate batch".to_string());
        }

        // 创建表
        let table = nftnl::nftnl_table_alloc();
        nftnl::nftnl_table_set(table, nftnl::NFTNL_TABLE_NAME, "mytable\0".as_ptr() as *const _);
        nftnl::nftnl_table_set_u32(table, nftnl::NFTNL_TABLE_FAMILY, nftnl::NFPROTO_IPV4 as u32);

        // 创建链
        let chain = nftnl::nftnl_chain_alloc();
        nftnl::nftnl_chain_set(chain, nftnl::NFTNL_CHAIN_NAME, "mychain\0".as_ptr() as *const _);
        nftnl::nftnl_chain_set(chain, nftnl::NFTNL_CHAIN_TABLE, "mytable\0".as_ptr() as *const _);
        nftnl::nftnl_chain_set_u32(chain, nftnl::NFTNL_CHAIN_FAMILY, nftnl::NFPROTO_IPV4 as u32);
        nftnl::nftnl_chain_set_u32(chain, nftnl::NFTNL_CHAIN_TYPE, nftnl::NFT_CHAIN_T_FILTER as u32);
        nftnl::nftnl_chain_set_u32(chain, nftnl::NFTNL_CHAIN_HOOKNUM, 0); // 输入钩子
        nftnl::nftnl_chain_set_u32(chain, nftnl::NFTNL_CHAIN_PRIO, 0); // 优先级

        // 创建规则
        let rule = nftnl::nftnl_rule_alloc();
        nftnl::nftnl_rule_set(rule, nftnl::NFTNL_RULE_TABLE, "mytable\0".as_ptr() as *const _);
        nftnl::nftnl_rule_set(rule, nftnl::NFTNL_RULE_CHAIN, "mychain\0".as_ptr() as *const _);
        nftnl::nftnl_rule_set_u32(rule, nftnl::NFTNL_RULE_FAMILY, nftnl::NFPROTO_IPV4 as u32);

        // 添加计数器表达式
        let expr = nftnl::nftnl_expr_alloc("counter\0".as_ptr() as *const _);
        nftnl::nftnl_rule_add_expr(rule, expr);

        // 构建并发送netlink消息
        let mut nlh = nftnl::nftnl_nlmsg_build_hdr(
            ptr::null_mut(),
            nftnl::NFT_MSG_NEWTABLE as u16,
            nftnl::NFPROTO_IPV4 as u16,
            0,
            0
        );
        nftnl::nftnl_table_nlmsg_build_payload(nlh, table);

        // 清理资源
        nftnl::nftnl_table_free(table);
        nftnl::nftnl_chain_free(chain);
        nftnl::nftnl_rule_free(rule);
        nftnl::nftnl_batch_free(batch);

        libc::close(sock);
    }

    Ok(())
}

这个完整示例展示了如何使用nftnl-sys:

  1. 创建netlink套接字
  2. 创建批处理对象
  3. 创建表、链和规则
  4. 添加计数器表达式
  5. 构建netlink消息
  6. 清理资源

注意:实际使用时需要添加更多的错误处理和规则配置。完整实现还需要处理netlink消息的发送和接收。

License: MIT/Apache-2.0


1 回复

Rust网络过滤库nftnl-sys的使用指南

简介

nftnl-sys是Rust对Linux netfilter用户空间库(libnftnl)的低级绑定,允许开发者高效操作Linux netfilter规则集。它提供了与Linux内核网络过滤子系统交互的能力,是构建防火墙、NAT等网络工具的基础。

主要特性

  • 提供对nftables规则的完全控制
  • 支持创建、修改和删除规则集
  • 低开销、高性能的网络过滤操作
  • 与Linux内核netfilter子系统直接交互

使用方法

添加依赖

在Cargo.toml中添加:

[dependencies]
nftnl-sys = "0.1"
libc = "0.2"

基本示例

use nftnl_sys as nftnl;
use libc::{c_void, c_char, size_t};

fn main() {
    // 创建新的规则集
    let batch = unsafe { nftnl::nftnl_batch_alloc() };
    
    // 创建新表
    let table = unsafe { nftnl::nftnl_table_alloc() };
    unsafe {
        nftnl::nftnl_table_set_u8(table, nftnl::NFTNL_TABLE_FAMILY as u16, libc::AF_INET as u8);
        nftnl::nftnl_table_set_str(table, nftnl::NFTNL_TABLE_NAME as u16, b"mytable\0".as_ptr() as *const c_char);
    }
    
    // 将表添加到batch
    unsafe {
        nftnl::nftnl_batch_begin(batch, nftnl::NFTNL_MSG_NEWTABLE as i32);
        nftnl::nftnl_batch_table_add(batch, nftnl::NFTNL_MSG_NEWTABLE as i32, table);
        nftnl::nftnl_batch_end(batch, nftnl::NFTNL_MSG_NEWTABLE as i32);
    }
    
    // 清理资源
    unsafe {
        nftnl::nftnl_table_free(table);
        nftnl::nftnl_batch_free(batch);
    }
}

创建规则链示例

fn create_chain() {
    let batch = unsafe { nftnl::nftnl_batch_alloc() };
    let chain = unsafe { nftnl::nftnl_chain_alloc() };
    
    unsafe {
        // 设置链属性
        nftnl::nftnl_chain_set_u8(chain, nftnl::NFTNL_CHAIN_FAMILY as u16, libc::AF_INET as u8);
        nftnl::nftnl_chain_set_str(chain, nftnl::NFTNL_CHAIN_TABLE as u16, b"mytable\0".as_ptr() as *const c_char);
        nftnl::nftnl_chain_set_str(chain, nftnl::NFTNL_CHAIN_NAME as u16, b"input\0".as_ptr() as *const c_char);
        
        // 添加到batch
        nftnl::nftnl_batch_begin(batch, nftnl::NFTNL_MSG_NEWCHAIN as i32);
        nftnl::nftnl_batch_chain_add(batch, nftnl::NFTNL_MSG_NEWCHAIN as i32, chain);
        nftnl::nftnl_batch_end(batch, nftnl::NFTNL_MSG_NEWCHAIN as i32);
    }
    
    // 发送到内核netlink接口
    // 这里需要实现netlink通信代码
    
    unsafe {
        nftnl::nftnl_chain_free(chain);
        nftnl::nftnl_batch_free(batch);
    }
}

高级用法

创建过滤规则

fn create_filter_rule() {
    let rule = unsafe { nftnl::nftnl_rule_alloc() };
    
    unsafe {
        // 设置规则基本属性
        nftnl::nftnl_rule_set_u8(rule, nftnl::NFTNL_RULE_FAMILY as u16, libc::AF_INET as u8);
        nftnl::nftnl_rule_set_str(rule, nftnl::NFTNL_RULE_TABLE as u16, b"mytable\0".as_ptr() as *const c_char);
        nftnl::nftnl_rule_set_str(rule, nftnl::NFTNL_RULE_CHAIN as u16, b"input\0".as_ptr() as *const c_char);
        
        // 添加匹配条件 (例如匹配TCP端口80)
        let expr = nftnl::nftnl_expr_alloc("match");
        // 这里需要设置expr的具体参数...
        
        // 添加动作 (例如接受数据包)
        let expr_action = nftnl::nftnl_expr_alloc("target");
        // 这里需要设置expr_action的具体参数...
        
        // 将表达式添加到规则
        nftnl::nftnl_rule_add_expr(rule, expr);
        nftnl::nftnl_rule_add_expr(rule, expr_action);
    }
    
    // 将规则添加到batch并发送到内核...
    
    unsafe {
        nftnl::nftnl_rule_free(rule);
    }
}

完整示例demo

以下是一个完整的nftnl-sys使用示例,展示了如何创建表、链和规则:

use nftnl_sys as nftnl;
use libc::{c_char, c_void, AF_INET};

fn main() -> Result<(), String> {
    // 1. 创建batch对象
    let batch = unsafe { nftnl::nftnl_batch_alloc() };
    if batch.is_null() {
        return Err("Failed to allocate batch".to_string());
    }

    // 2. 创建并配置表
    let table = unsafe { nftnl::nftnl_table_alloc() };
    if table.is_null() {
        unsafe { nftnl::nftnl_batch_free(batch) };
        return Err("Failed to allocate table".to_string());
    }

    unsafe {
        nftnl::nftnl_table_set_u8(table, nftnl::NFTNL_TABLE_FAMILY as u16, AF_INET as u8);
        nftnl::nftnl_table_set_str(table, nftnl::NFTNL_TABLE_NAME as u16, b"mytable\0".as_ptr() as *const c_char);
    }

    // 3. 创建并配置链
    let chain = unsafe { nftnl::nftnl_chain_alloc() };
    if chain.is_null() {
        unsafe {
            nftnl::nftnl_table_free(table);
            nftnl::nftnl_batch_free(batch);
        }
        return Err("Failed to allocate chain".to_string());
    }

    unsafe {
        nftnl::nftnl_chain_set_u8(chain, nftnl::NFTNL_CHAIN_FAMILY as u16, AF_INET as u8);
        nftnl::nftnl_chain_set_str(chain, nftnl::NFTNL_CHAIN_TABLE as u16, b"mytable\0".as_ptr() as *const c_char);
        nftnl::nftnl_chain_set_str(chain, nftnl::NFTNL_CHAIN_NAME as u16, b"input\0".as_ptr() as *const c_char);
    }

    // 4. 创建并配置规则
    let rule = unsafe { nftnl::nftnl_rule_alloc() };
    if rule.is_null() {
        unsafe {
            nftnl::nftnl_chain_free(chain);
            nftnl::nftnl_table_free(table);
            nftnl::nftnl_batch_free(batch);
        }
        return Err("Failed to allocate rule".to_string());
    }

    unsafe {
        nftnl::nftnl_rule_set_u8(rule, nftnl::NFTNL_RULE_FAMILY as u16, AF_INET as u8);
        nftnl::nftnl_rule_set_str(rule, nftnl::NFTNL_RULE_TABLE as u16, b"mytable\0".as_ptr() as *const c_char);
        nftnl::nftnl_rule_set_str(rule, nftnl::NFTNL_RULE_CHAIN as u16, b"input\0".as_ptr() as *const c_char);

        // 添加匹配TCP端口80的表达式
        let expr = nftnl::nftnl_expr_alloc(b"match\0".as_ptr() as *const c_char);
        if expr.is_null() {
            nftnl::nftnl_rule_free(rule);
            nftnl::nftnl_chain_free(chain);
            nftnl::nftnl_table_free(table);
            nftnl::nftnl_batch_free(batch);
            return Err("Failed to allocate match expression".to_string());
        }
        
        // 这里需要配置具体的匹配参数
        // ...

        // 添加接受数据包的动作
        let expr_action = nftnl::nftnl_expr_alloc(b"target\0".as_ptr() as *const c_char);
        if expr_action.is_null() {
            nftnl::nftnl_expr_free(expr);
            nftnl::nftnl_rule_free(rule);
            nftnl::nftnl_chain_free(chain);
            nftnl::nftnl_table_free(table);
            nftnl::nftnl_batch_free(batch);
            return Err("Failed to allocate target expression".to_string());
        }
        
        // 这里需要配置具体的动作参数
        // ...

        nftnl::nftnl_rule_add_expr(rule, expr);
        nftnl::nftnl_rule_add_expr(rule, expr_action);
    }

    // 5. 将所有对象添加到batch
    unsafe {
        // 添加表
        nftnl::nftnl_batch_begin(batch, nftnl::NFTNL_MSG_NEWTABLE as i32);
        nftnl::nftnl_batch_table_add(batch, nftnl::NFTNL_MSG_NEWTABLE as i32, table);
        nftnl::nftnl_batch_end(batch, nftnl::NFTNL_MSG_NEWTABLE as i32);

        // 添加链
        nftnl::nftnl_batch_begin(batch, nftnl::NFTNL_MSG_NEWCHAIN as i32);
        nftnl::nftnl_batch_chain_add(batch, nftnl::NFTNL_MSG_NEWCHAIN as i32, chain);
        nftnl::nftnl_batch_end(batch, nftnl::NFTNL_MSG_NEWCHAIN as i32);

        // 添加规则
        nftnl::nftnl_batch_begin(batch, nftnl::NFTNL_MSG_NEWRULE as i32);
        nftnl::nftnl_batch_rule_add(batch, nftnl::NFTNL_MSG_NEWRULE as i32, rule);
        nftnl::nftnl_batch_end(batch, nftnl::NFTNL_MSG_NEWRULE as i32);
    }

    // 6. 这里应该添加将batch发送到内核的代码
    // 通常使用netlink socket发送

    // 7. 清理资源
    unsafe {
        nftnl::nftnl_rule_free(rule);
        nftnl::nftnl_chain_free(chain);
        nftnl::nftnl_table_free(table);
        nftnl::nftnl_batch_free(batch);
    }

    Ok(())
}

注意事项

  1. nftnl-sys是低级绑定,使用时需要熟悉Linux netfilter的工作原理
  2. 所有操作都需要unsafe块
  3. 需要正确处理内存管理,避免内存泄漏
  4. 需要root权限才能修改netfilter规则集
  5. 错误处理非常重要,应检查每个API调用的返回值

性能建议

  • 使用batch操作减少内核上下文切换
  • 复用已分配的nftnl对象
  • 尽量减少规则更新频率
  • 使用nftnl提供的批处理接口
回到顶部