Rust网络类型库network-types的使用,高效处理多种网络协议和数据格式转换

Rust网络类型库network-types的使用,高效处理多种网络协议和数据格式转换

network-types是用于表示网络协议头(Layer 2、3和4)的Rust结构体库。这个库是no_std的,非常适合使用Aya编写的eBPF程序。

示例

下面是一个XDP程序的示例,记录传入数据包的地址和端口信息:

use core::mem;

use aya_ebpf::{bindings::xdp_action, macros::xdp, programs::XdpContext};
use aya_log_ebpf::info;

use network_types::{
    eth::{EthHdr, EtherType},
    ip::{Ipv4Hdr, Ipv6Hdr, IpProto},
    tcp::TcpHdr,
    udp::UdpHdr,
};

#[xdp]
pub fn xdp_firewall(ctx: XdpContext) -> u32 {
    match try_xdp_firewall(ctx) {
        Ok(ret) => ret,
        Err(_) => xdp_action::XDP_PASS,
    }
}

#[inline(always)]
unsafe fn ptr_at<T>(ctx: &XdpContext, offset: usize) -> Result<*const T, ()> {
    let start = ctx.data();
    let end = ctx.data_end();
    let len = mem::size_of::<T>();

    if start + offset + len > end {
        return Err(());
    }

    Ok((start + offset) as *const T)
}

fn try_xdp_firewall(ctx: XdpContext) -> Result極u32, ()> {
    let ethhdr: *const EthHdr = unsafe { ptr_at(&ctx, 0)? };
    match unsafe { *ethhdr }.ether_type {
        EtherType::Ipv4 => {
            let ipv4hdr: *const Ipv4Hdr = unsafe { ptr_at(&ctx, EthHdr::LEN)? };
            let source_addr = unsafe { (*ipv4hdr).src_addr() };

            let source_port = match unsafe { (*ipv4hdr).proto } {
                IpProto::Tcp => {
                    let tcphdr: *const TcpHdr =
                        unsafe { ptr_at(&ctx, EthHdr::LEN + Ipv4Hdr::LEN)? };
                    u16::from_be(unsafe { (*tcphdr).source })
                }
                IpProto::Udp => {
                    let udphdr: *const UdpHdr =
                        unsafe { ptr_at(&ctx, EthHdr::LEN + Ipv4Hdr::LEN)? };
                    unsafe { (*udphdr).source() }
                }
                _ => return Ok(xdp_action::XDP_PASS),
            };

            info!(&ctx, "SRC IP: {:i}, SRC PORT: {}", source_addr, source_port);
        }
        EtherType::Ipv6 => {
            let ipv6hdr: *const Ipv6Hdr = unsafe { ptr_at(&ctx, EthHdr::LEN)? };
            let source_addr = unsafe { (*ipv6hdr).src_addr() };

            let source_port = match unsafe { (*ipv6hdr).next_hdr } {
                IpProto::Tcp => {
                    let tcphdr: *const TcpHdr =
                        unsafe { ptr_at(&ctx, EthHdr::LEN + Ipv6Hdr::LEN)? };
                    u16::from_be(unsafe { (*tcphdr).source })
                }
                IpProto::Udp => {
                    let udphdr: *const UdpHdr =
                        unsafe { ptr_at(&ctx, EthHdr::LEN + Ipv6Hdr::LEN)? };
                    unsafe { (*udphdr).source() }
                }
                _ => return Ok(xdp_action::XDP_PASS),
            };

            info!(&ctx, "SRC IP: {:i}, SRC PORT: {}", source_addr, source_port);
        }
        _ => {},
    }

    Ok(xdp_action::XDP_PASS)
}

命名约定

在命名结构和字段时,我们遵循以下原则:

  • 使用CamelCase,即使名称通常全部大写(例如Icmp而不是ICMP)
  • 当字段名称(由RFC或其他标准指定)包含空格时,用_替换它们
  • 缩短以下冗长的名称:
    • source -> src
    • destination -> dst
    • address -> addr

功能标志

可以通过serde功能标志启用Serde支持。它旨在与像bincode这样的二进制序列化库一起使用,这些库利用Serde的基础设施。

请注意,启用Serde时会失去no_std支持。

完整示例代码

// 完整示例展示了如何使用network-types解析网络包
use network_types::{
    eth::{EthHdr, EtherType},
    ip::{Ipv4Hdr, IpProto},
    tcp::TcpHdr,
};

// 假设我们有一个原始网络数据包
let raw_packet: &[u8] = get_raw_packet_from_network();

// 解析以太网头
let eth_header = unsafe { &*(raw_packet.as_ptr() as *const EthHdr) };
match eth_header.ether_type {
    EtherType::Ipv4 => {
        // 解析IPv4头
        let ipv4_offset = EthHdr::LEN;
        let ipv4_header = unsafe { 
            &*(raw_packet.as_ptr().add(ipv4_offset) as *const Ipv4Hdr) 
        };
        
        println!("Source IP: {:?}", ipv4_header.src_addr());
        println!("Destination IP: {:?}", ipv4_header.dst_addr());
        
        match ipv4_header.proto {
            IpProto::Tcp => {
                // 解析TCP头
                let tcp_offset = ipv4_offset + Ipv4Hdr::LEN;
                let tcp_header = unsafe { 
                    &*(raw_packet.as_ptr().add(tcp_offset) as *const TcpHdr) 
                };
                
                println!("Source Port: {}", tcp_header.source);
                println!("Destination Port: {}", tcp_header.dest);
            }
            _ => println!("Not a TCP packet"),
        }
    }
    _ => println!("Not an IPv4 packet"),
}

许可证: MIT


1 回复

Rust网络类型库network-types的使用指南

network-types是一个Rust库,提供了高效处理多种网络协议和数据格式转换的功能。它简化了网络编程中常见的数据类型处理和协议转换任务。

主要特性

  • 支持多种网络协议的标准数据类型
  • 提供高效的数据格式转换功能
  • 类型安全的网络数据处理
  • 零拷贝解析和序列化支持

安装

在Cargo.toml中添加依赖:

[dependencies]
network-types = "0.7"

基本用法

1. IP地址处理

use network_types::ip::{Ipv4Addr, Ipv6Addr};

fn main() {
    let ipv4 = Ipv4Addr::new(192, 168, 1, 1);
    let ipv6 = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1);
    
    println!("IPv4: {}", ipv4);
    println!("IPv6: {}", ipv6);
    
    // 从字符串解析
    let ipv4_from_str: Ipv4Addr = "10.0.0.1".parse().unwrap();
    println!("Parsed IPv4: {}", ipv4_from_str);
}

2. MAC地址处理

use network_types::ethernet::MacAddr;

fn main() {
    let mac = MacAddr::new(0x00, 0x11, 0x22, 0x33, 0x44, 0x55);
    println!("MAC Address: {}", mac);
    
    // 从字节数组创建
    let bytes = [0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff];
    let mac_from_bytes = MacAddr::from_bytes(&bytes);
    println!("MAC from bytes: {}", mac_from_bytes);
}

3. 协议数据包处理

use network_types::{
    ethernet::{EthHdr, EtherType},
    ip::{Ipv4Hdr, Protocol},
};

fn build_ethernet_frame() {
    let eth_hdr = EthHdr {
        dst: MacAddr::new(0x00, 0x11, 0x22, 0x33, 0x44, 0x55),
        src: MacAddr::new(0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff),
        ether_type: EtherType::IPV4,
    };
    
    let ip极客 = Ipv4Hdr {
        version_ihl: 0x45,
        tos: 0,
        total_len: 100,
        id: 0x1234,
        flags_frag_off: 0,
        ttl: 64,
        protocol: Protocol::TCP,
        csum: 0,
        src: Ipv4Addr::new(192, 168, 1, 1),
        dst: Ipv4Addr::new(192, 168, 1, 2),
    };
    
    // 计算校验和
    let ip_hdr = ip_hdr.with_csum();
    
    println!("Ethernet Header: {:?}", eth_hdr);
    println!("IP Header: {:?}", ip_hdr);
}

4. 数据格式转换

use network_types::convert::{NetworkEndian, ToNetwork};

fn convert_to_network_bytes() {
    let value: u32 = 0x12345678;
    let network_bytes = value.to_network::<NetworkEndian>();
    
    println!("Original: 0x{:x}", value);
    println!("Network bytes: {:?}", network_bytes);
    
    // 从网络字节序转换回来
    let host_value = u32::from_network::<NetworkEndian>(network_bytes);
    println!("Converted back: 0x{:x}", host_value);
}

高级用法

1. 自定义协议处理

use network_types::{
    ethernet::EthHdr,
    ip::{Ipv4Hdr, Protocol},
    tcp::TcpHdr,
    udp::UdpHdr,
    convert::AsBytes,
};

fn process_packet(packet: &[u8]) {
    // 解析以太网头部
    let eth_hdr = EthHdr::from_bytes(&packet[..14]).unwrap();
    
    match eth_hdr.ether_type {
        EtherType::IPV4 => {
            let ip_hdr = Ipv4Hdr::from_bytes(&packet[14..34]).unwrap();
            
            match ip_hdr.protocol {
                Protocol::TCP => {
                    let tcp_hdr = TcpHdr::from_bytes(&packet[34..54]).unwrap();
                    println!("TCP Packet: {:?}", tcp_hdr);
                },
                Protocol::UDP => {
                    let udp_hdr = UdpHdr极客::from_bytes(&packet[34..42]).unwrap();
                    println!("UDP Packet: {:?}", udp_hdr);
                },
                _ => println!("Other IP protocol"),
            }
        },
        _ => println!("Non-IPv4 packet"),
    }
}

2. 零拷贝解析

use network_types::{
    ip::Ipv4Hdr,
    tcp::TcpHdr,
    convert::FromBytes,
};

fn zero_copy_parse(packet: &[极客u8]) {
    // 使用零拷贝方式解析IP头部
    let (ip_hdr, ip_rest) = Ipv4Hdr::from_bytes_prefix(packet).unwrap();
    
    if ip_hdr.protocol == Protocol::TCP {
        // 零拷贝解析TCP头部
        let (tcp_hdr, _) = TcpHdr::from_bytes_prefix(ip_rest).unwrap();
        
        println!("TCP Header without copy: {:?}", tcp_hdr);
    }
}

性能提示

  1. 尽可能使用零拷贝解析方法(from_bytes_prefix)
  2. 批量处理数据时重用缓冲区
  3. 对于热路径代码,考虑使用unsafe版本的方法(如果性能分析表明有必要)

network-types库为Rust网络编程提供了强大而高效的类型支持,特别适合需要处理多种网络协议和数据格式转换的应用场景。

回到顶部