#linux-networking #header #packet #osi

no-std network-types

Rust structs representing network-related types in Linux

5 releases

0.0.5 Nov 30, 2023
0.0.4 Jan 9, 2023
0.0.3 Dec 29, 2022
0.0.2 Nov 28, 2022
0.0.1 Nov 15, 2022

#397 in Network programming

Download history 195/week @ 2023-11-23 137/week @ 2023-11-30 224/week @ 2023-12-07 521/week @ 2023-12-14 440/week @ 2023-12-21 490/week @ 2023-12-28 271/week @ 2024-01-04 506/week @ 2024-01-11 511/week @ 2024-01-18 664/week @ 2024-01-25 698/week @ 2024-02-01 517/week @ 2024-02-08 786/week @ 2024-02-15 357/week @ 2024-02-22 669/week @ 2024-02-29 458/week @ 2024-03-07

2,311 downloads per month

MIT license

37KB
768 lines

network-types

Rust structs representing network protocol headers (on Layer 2, 3 and 4).

The crate is no_std, which makes it a great fit for eBPF programs written with Aya.

Examples

An example of an XDP program logging information about addresses and ports for incoming packets:

use core::mem;

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

use network_types::{
    eth::{EthHdr, EtherType},
    ip::{Ipv4Hdr, 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 => {}
        _ => return Ok(xdp_action::XDP_PASS),
    }

    let ipv4hdr: *const Ipv4Hdr = unsafe { ptr_at(&ctx, EthHdr::LEN)? };
    let source_addr = u32::from_be(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) }?;
            u16::from_be(unsafe { *udphdr }.source)
        }
        _ => return Err(()),
    };

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

    Ok(xdp_action::XDP_PASS)
}

Naming conventions

When naming stucts and fields, we are trying to stick to the following principles:

  • Use CamelCase, even for names which normally would be all uppercase (e.g. Icmp instead of ICMP). This is the convention used by the std::net module.
  • Where field names (specified by RFCs or other standards) contain spaces, replace them with _. In general, use snake_case for field names.
  • Shorten the following verbose names:
    • source -> src
    • destination -> dst
    • address -> addr

License: MIT

Dependencies

~180KB