| Crates.io | network-types |
| lib.rs | network-types |
| version | 0.1.0 |
| created_at | 2022-11-15 07:11:49.195607+00 |
| updated_at | 2025-08-20 11:14:25.441512+00 |
| description | Rust structs representing network-related types in Linux. |
| homepage | |
| repository | https://github.com/vadorovsky/network-types |
| max_upload_size | |
| id | 715496 |
| size | 195,605 |
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.
An example of an XDP program logging information about addresses and ports for incoming packets:
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() {
Ok(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_bytes(unsafe { (*tcphdr).source })
}
IpProto::Udp => {
let udphdr: *const UdpHdr =
unsafe { ptr_at(&ctx, EthHdr::LEN + Ipv4Hdr::LEN) }?;
unsafe { (*udphdr).src_port() }
}
_ => return Ok(xdp_action::XDP_PASS),
};
info!(&ctx, "SRC IP: {:i}, SRC PORT: {}", source_addr, source_port);
}
Ok(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_bytes(unsafe { (*tcphdr).source })
}
IpProto::Udp => {
let udphdr: *const UdpHdr =
unsafe { ptr_at(&ctx, EthHdr::LEN + Ipv6Hdr::LEN) }?;
unsafe { (*udphdr).src_port() }
}
_ => return Ok(xdp_action::XDP_PASS),
};
info!(&ctx, "SRC IP: {:i}, SRC PORT: {}", source_addr, source_port);
}
_ => {},
}
Ok(xdp_action::XDP_PASS)
}
When naming stucts and fields, we are trying to stick to the following principles:
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._. In general, use snake_case for field names.source -> srcdestination -> dstaddress -> addrchecksum -> checkSerde support can be enabled through the serde
feature flag. It is intended to be used with binary serialization libraries
like bincode that leverage Serde's
infrastructure.
License: MIT