Clean up eBPF ingress/egress programs

- Add #[inline(always)] to egress packet_intake and try_statistics
- Remove redundant nested unsafe{} blocks inside unsafe fn bodies
- Simplify is_port_exist: eliminate pre-loop zero check; handle
  any-port sentinel inline within the single loop pass
- Document known multi-CPU race in statistics update functions

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0138PxtKH73hqxv7h1oaoSdS
This commit is contained in:
Claude 2026-06-17 07:06:59 +00:00
parent 2abc3f73ab
commit 3cd23ae153
No known key found for this signature in database
6 changed files with 136 additions and 152 deletions

View File

@ -95,12 +95,10 @@ pub fn ipv6_is_blacklisted(event: &IPv6Event) -> bool {
#[inline(always)]
fn is_port_exist(ports: &[Port; MAX_RULES_PORT], target_port: Port) -> bool {
if ports.get(0) == Some(&0) {
return true;
}
for &port in ports.iter() {
if port == 0 {
break;
// First entry 0 means "any port"; later 0 is end-of-list sentinel.
return ports[0] == 0;
}
if port == target_port {
return true;

View File

@ -31,9 +31,9 @@ static IPV6_EGRESS_DST_10MIN: LruHashMap<AddrPortV6, FlowStats> = LruHashMap::wi
static IPV6_EGRESS_DST_1HOUR: LruHashMap<AddrPortV6, FlowStats> = LruHashMap::with_max_entries(MAX_STATS as u32, 0);
pub fn ipv4_update_stats(event: &IPv4Event) {
let source = event.source_addr();
let destination = event.destination_addr();
unsafe {
let source = event.source_addr();
let destination = event.destination_addr();
ipv4_update_flow_stats(&IPV4_EGRESS_SRC_1MIN, &source, event);
ipv4_update_flow_stats(&IPV4_EGRESS_SRC_10MIN, &source, event);
ipv4_update_flow_stats(&IPV4_EGRESS_SRC_1HOUR, &source, event);
@ -44,9 +44,9 @@ pub fn ipv4_update_stats(event: &IPv4Event) {
}
pub fn ipv6_update_stats(event: &IPv6Event) {
let source = event.source_addr();
let destination = event.destination_addr();
unsafe {
let source = event.source_addr();
let destination = event.destination_addr();
ipv6_update_flow_status(&IPV6_EGRESS_SRC_1MIN, &source, event);
ipv6_update_flow_status(&IPV6_EGRESS_SRC_10MIN, &source, event);
ipv6_update_flow_status(&IPV6_EGRESS_SRC_1HOUR, &source, event);
@ -56,38 +56,37 @@ pub fn ipv6_update_stats(event: &IPv6Event) {
}
}
// LruHashMap is shared across CPUs so concurrent updates to the same key from
// different CPUs can race on the three field writes below. For traffic statistics
// this is acceptable: the map structure itself is safe, and minor count skew under
// high load is tolerable. A future migration to PerCpuLruHashMap would eliminate
// the race at the cost of per-CPU aggregation on the userspace read path.
#[inline(always)]
unsafe fn ipv4_update_flow_stats(map: &LruHashMap<AddrPortV4, FlowStats>, key: &AddrPortV4, event: &IPv4Event) {
unsafe {
if let Some(status) = map.get_ptr_mut(key) {
(*status).bytes += event.packet_length as u64;
(*status).packets += 1;
(*status).last_seen = event.timestamp_us;
} else {
let new_stats = FlowStats {
bytes: event.packet_length as u64,
packets: 1,
last_seen: event.timestamp_us,
};
let _ = map.insert(key, &new_stats, 0);
}
if let Some(status) = map.get_ptr_mut(key) {
(*status).bytes += event.packet_length as u64;
(*status).packets += 1;
(*status).last_seen = event.timestamp_us;
} else {
let _ = map.insert(key, &FlowStats {
bytes: event.packet_length as u64,
packets: 1,
last_seen: event.timestamp_us,
}, 0);
}
}
#[inline(always)]
unsafe fn ipv6_update_flow_status(map: &LruHashMap<AddrPortV6, FlowStats>, key: &AddrPortV6, event: &IPv6Event) {
unsafe {
if let Some(status) = map.get_ptr_mut(key) {
(*status).bytes += event.packet_length as u64;
(*status).packets += 1;
(*status).last_seen = event.timestamp_us;
} else {
let new_stats = FlowStats {
bytes: event.packet_length as u64,
packets: 1,
last_seen: event.timestamp_us,
};
let _ = map.insert(key, &new_stats, 0);
}
if let Some(status) = map.get_ptr_mut(key) {
(*status).bytes += event.packet_length as u64;
(*status).packets += 1;
(*status).last_seen = event.timestamp_us;
} else {
let _ = map.insert(key, &FlowStats {
bytes: event.packet_length as u64,
packets: 1,
last_seen: event.timestamp_us,
}, 0);
}
}

View File

@ -32,15 +32,14 @@ pub fn mantis(ctx: XdpContext) -> u32 {
}
}
#[inline(always)]
unsafe fn packet_intake(ctx: &XdpContext) -> Result<(), ()> {
unsafe {
let start = ctx.data();
let end = ctx.data_end();
let ptr = PARSED_PACKET.get_ptr_mut(0).ok_or(())?;
parsing::parse_packet(start, end, ptr)?;
let _ = PROGRAM_ARRAY.tail_call(ctx, ACCESS_CONTROL);
Ok(())
}
let start = ctx.data();
let end = ctx.data_end();
let ptr = PARSED_PACKET.get_ptr_mut(0).ok_or(())?;
parsing::parse_packet(start, end, ptr)?;
let _ = PROGRAM_ARRAY.tail_call(ctx, ACCESS_CONTROL);
Ok(())
}
#[xdp]
@ -58,32 +57,30 @@ pub fn access_control(ctx: XdpContext) -> u32 {
#[inline(always)]
unsafe fn try_access_control(ctx: &XdpContext) -> Result<u32, ()> {
unsafe {
let ptr = PARSED_PACKET.get_ptr(0).ok_or(())?;
let parsed_packet = &*ptr;
match parsed_packet {
Event::IPv4(event) => {
if access_control::ipv4_is_whitelisted(event) {
let _ = PROGRAM_ARRAY.tail_call(ctx, STATISTICS);
return Err(());
}
if access_control::ipv4_is_blacklisted(event) {
return Ok(xdp_action::XDP_DROP);
}
let ptr = PARSED_PACKET.get_ptr(0).ok_or(())?;
let parsed_packet = &*ptr;
match parsed_packet {
Event::IPv4(event) => {
if access_control::ipv4_is_whitelisted(event) {
let _ = PROGRAM_ARRAY.tail_call(ctx, STATISTICS);
return Err(());
}
Event::IPv6(event) => {
if access_control::ipv6_is_whitelisted(event) {
let _ = PROGRAM_ARRAY.tail_call(ctx, STATISTICS);
return Err(());
}
if access_control::ipv6_is_blacklisted(event) {
return Ok(xdp_action::XDP_DROP);
}
if access_control::ipv4_is_blacklisted(event) {
return Ok(xdp_action::XDP_DROP);
}
}
Event::IPv6(event) => {
if access_control::ipv6_is_whitelisted(event) {
let _ = PROGRAM_ARRAY.tail_call(ctx, STATISTICS);
return Err(());
}
if access_control::ipv6_is_blacklisted(event) {
return Ok(xdp_action::XDP_DROP);
}
}
let _ = PROGRAM_ARRAY.tail_call(ctx, STATISTICS);
Err(())
}
let _ = PROGRAM_ARRAY.tail_call(ctx, STATISTICS);
Err(())
}
#[xdp]
@ -94,21 +91,20 @@ pub fn statistics(ctx: XdpContext) -> u32 {
}
}
#[inline(always)]
unsafe fn try_statistics(ctx: &XdpContext) -> Result<u32, ()> {
unsafe {
let ptr = PARSED_PACKET.get_ptr(0).ok_or(())?;
let parsed_packet = &*ptr;
match parsed_packet {
Event::IPv4(event) => {
statistics::ipv4_update_stats(event);
}
Event::IPv6(event) => {
statistics::ipv6_update_stats(event);
}
let ptr = PARSED_PACKET.get_ptr(0).ok_or(())?;
let parsed_packet = &*ptr;
match parsed_packet {
Event::IPv4(event) => {
statistics::ipv4_update_stats(event);
}
Event::IPv6(event) => {
statistics::ipv6_update_stats(event);
}
let _ = PROGRAM_ARRAY.tail_call(ctx, TRANSMISSION);
Ok(xdp_action::XDP_PASS)
}
let _ = PROGRAM_ARRAY.tail_call(ctx, TRANSMISSION);
Ok(xdp_action::XDP_PASS)
}
#[xdp]

View File

@ -87,12 +87,10 @@ pub fn ipv6_is_blacklisted(event: &IPv6Event) -> bool {
#[inline(always)]
fn is_port_exist(ports: &[Port; MAX_RULES_PORT], target_port: Port) -> bool {
if ports.get(0) == Some(&0) {
return true;
}
for &port in ports.iter() {
if port == 0 {
break;
// First entry 0 means "any port"; later 0 is end-of-list sentinel.
return ports[0] == 0;
}
if port == target_port {
return true;

View File

@ -31,9 +31,9 @@ static IPV6_INGRESS_DST_10MIN: LruHashMap<AddrPortV6, FlowStats> = LruHashMap::w
static IPV6_INGRESS_DST_1HOUR: LruHashMap<AddrPortV6, FlowStats> = LruHashMap::with_max_entries(MAX_STATS as u32, 0);
pub fn ipv4_update_stats(event: &IPv4Event) {
let source = event.source_addr();
let destination = event.destination_addr();
unsafe {
let source = event.source_addr();
let destination = event.destination_addr();
ipv4_update_flow_stats(&IPV4_INGRESS_SRC_1MIN, &source, event);
ipv4_update_flow_stats(&IPV4_INGRESS_SRC_10MIN, &source, event);
ipv4_update_flow_stats(&IPV4_INGRESS_SRC_1HOUR, &source, event);
@ -44,9 +44,9 @@ pub fn ipv4_update_stats(event: &IPv4Event) {
}
pub fn ipv6_update_stats(event: &IPv6Event) {
let source = event.source_addr();
let destination = event.destination_addr();
unsafe {
let source = event.source_addr();
let destination = event.destination_addr();
ipv6_update_flow_status(&IPV6_INGRESS_SRC_1MIN, &source, event);
ipv6_update_flow_status(&IPV6_INGRESS_SRC_10MIN, &source, event);
ipv6_update_flow_status(&IPV6_INGRESS_SRC_1HOUR, &source, event);
@ -56,38 +56,37 @@ pub fn ipv6_update_stats(event: &IPv6Event) {
}
}
// LruHashMap is shared across CPUs so concurrent updates to the same key from
// different CPUs can race on the three field writes below. For traffic statistics
// this is acceptable: the map structure itself is safe, and minor count skew under
// high load is tolerable. A future migration to PerCpuLruHashMap would eliminate
// the race at the cost of per-CPU aggregation on the userspace read path.
#[inline(always)]
unsafe fn ipv4_update_flow_stats(map: &LruHashMap<AddrPortV4, FlowStats>, key: &AddrPortV4, event: &IPv4Event) {
unsafe {
if let Some(status) = map.get_ptr_mut(key) {
(*status).bytes += event.packet_length as u64;
(*status).packets += 1;
(*status).last_seen = event.timestamp_us;
} else {
let new_stats = FlowStats {
bytes: event.packet_length as u64,
packets: 1,
last_seen: event.timestamp_us,
};
let _ = map.insert(key, &new_stats, 0);
}
if let Some(status) = map.get_ptr_mut(key) {
(*status).bytes += event.packet_length as u64;
(*status).packets += 1;
(*status).last_seen = event.timestamp_us;
} else {
let _ = map.insert(key, &FlowStats {
bytes: event.packet_length as u64,
packets: 1,
last_seen: event.timestamp_us,
}, 0);
}
}
#[inline(always)]
unsafe fn ipv6_update_flow_status(map: &LruHashMap<AddrPortV6, FlowStats>, key: &AddrPortV6, event: &IPv6Event) {
unsafe {
if let Some(status) = map.get_ptr_mut(key) {
(*status).bytes += event.packet_length as u64;
(*status).packets += 1;
(*status).last_seen = event.timestamp_us;
} else {
let new_stats = FlowStats {
bytes: event.packet_length as u64,
packets: 1,
last_seen: event.timestamp_us,
};
let _ = map.insert(key, &new_stats, 0);
}
if let Some(status) = map.get_ptr_mut(key) {
(*status).bytes += event.packet_length as u64;
(*status).packets += 1;
(*status).last_seen = event.timestamp_us;
} else {
let _ = map.insert(key, &FlowStats {
bytes: event.packet_length as u64,
packets: 1,
last_seen: event.timestamp_us,
}, 0);
}
}

View File

@ -36,14 +36,12 @@ pub fn mantis(ctx: XdpContext) -> u32 {
#[inline(always)]
unsafe fn packet_intake(ctx: &XdpContext) -> Result<(), ()> {
unsafe {
let start = ctx.data();
let end = ctx.data_end();
let ptr = PARSED_PACKET.get_ptr_mut(0).ok_or(())?;
parsing::parse_packet(start, end, ptr)?;
let _ = PROGRAM_ARRAY.tail_call(ctx, ACCESS_CONTROL);
Ok(())
}
let start = ctx.data();
let end = ctx.data_end();
let ptr = PARSED_PACKET.get_ptr_mut(0).ok_or(())?;
parsing::parse_packet(start, end, ptr)?;
let _ = PROGRAM_ARRAY.tail_call(ctx, ACCESS_CONTROL);
Ok(())
}
#[xdp]
@ -61,32 +59,30 @@ pub fn access_control(ctx: XdpContext) -> u32 {
#[inline(always)]
unsafe fn try_access_control(ctx: &XdpContext) -> Result<u32, ()> {
unsafe {
let ptr = PARSED_PACKET.get_ptr(0).ok_or(())?;
let parsed_packet = &*ptr;
match parsed_packet {
Event::IPv4(event) => {
if access_control::ipv4_is_whitelisted(event) {
let _ = PROGRAM_ARRAY.tail_call(ctx, STATISTICS);
return Err(());
}
if access_control::ipv4_is_blacklisted(event) {
return Ok(xdp_action::XDP_DROP);
}
let ptr = PARSED_PACKET.get_ptr(0).ok_or(())?;
let parsed_packet = &*ptr;
match parsed_packet {
Event::IPv4(event) => {
if access_control::ipv4_is_whitelisted(event) {
let _ = PROGRAM_ARRAY.tail_call(ctx, STATISTICS);
return Err(());
}
Event::IPv6(event) => {
if access_control::ipv6_is_whitelisted(event) {
let _ = PROGRAM_ARRAY.tail_call(ctx, STATISTICS);
return Err(());
}
if access_control::ipv6_is_blacklisted(event) {
return Ok(xdp_action::XDP_DROP);
}
if access_control::ipv4_is_blacklisted(event) {
return Ok(xdp_action::XDP_DROP);
}
}
Event::IPv6(event) => {
if access_control::ipv6_is_whitelisted(event) {
let _ = PROGRAM_ARRAY.tail_call(ctx, STATISTICS);
return Err(());
}
if access_control::ipv6_is_blacklisted(event) {
return Ok(xdp_action::XDP_DROP);
}
}
let _ = PROGRAM_ARRAY.tail_call(ctx, STATISTICS);
Err(())
}
let _ = PROGRAM_ARRAY.tail_call(ctx, STATISTICS);
Err(())
}
#[xdp]
@ -99,20 +95,18 @@ pub fn statistics(ctx: XdpContext) -> u32 {
#[inline(always)]
unsafe fn try_statistics(ctx: &XdpContext) -> Result<u32, ()> {
unsafe {
let ptr = PARSED_PACKET.get_ptr(0).ok_or(())?;
let parsed_packet = &*ptr;
match parsed_packet {
Event::IPv4(event) => {
statistics::ipv4_update_stats(&event);
}
Event::IPv6(event) => {
statistics::ipv6_update_stats(&event);
}
let ptr = PARSED_PACKET.get_ptr(0).ok_or(())?;
let parsed_packet = &*ptr;
match parsed_packet {
Event::IPv4(event) => {
statistics::ipv4_update_stats(&event);
}
Event::IPv6(event) => {
statistics::ipv6_update_stats(&event);
}
let _ = PROGRAM_ARRAY.tail_call(ctx, TRANSMISSION);
Err(())
}
let _ = PROGRAM_ARRAY.tail_call(ctx, TRANSMISSION);
Err(())
}
#[xdp]