#include "xdp_common.h"
SEC("xdp")
int zenith_xdp_main(struct xdp_md *ctx)
{
void *data = (void *)(long)ctx->data;
void *data_end = (void *)(long)ctx->data_end;
u32 queue_id;
u32 fail_closed;
u32 mtu;
fail_closed = get_config_u32(CONFIG_KEY_FAIL_CLOSED);
mtu = get_config_u32(CONFIG_KEY_MTU);
if (mtu > 0) {
u32 pkt_len = (u32)(data_end - data);
if (pkt_len > mtu) {
update_stats(STATS_RX_DROP_SHORT, 1);
return XDP_DROP;
}
}
if (!check_eth_header(ctx, data, data_end)) {
update_stats(STATS_RX_DROP_BAD_ETH, 1);
return XDP_DROP;
}
queue_id = ctx->rx_queue_index;
u32 *xsk_fd;
xsk_fd = bpf_map_lookup_elem(&xsk_map, &queue_id);
if (!xsk_fd) {
update_stats(STATS_RX_DROP_NO_XSK, 1);
if (fail_closed)
return XDP_DROP;
return XDP_PASS;
}
struct ethhdr *eth = data;
__u16 l3_proto = eth->h_proto;
void *l3_hdr = (void *)(eth + 1);
if (eth->h_proto == bpf_htons(ETH_P_VLAN)) {
if ((void *)((u8 *)l3_hdr + 4) > data_end) {
update_stats(STATS_RX_DROP_SHORT, 1);
return XDP_DROP;
}
l3_proto = *(__u16 *)((u8 *)l3_hdr + 2);
l3_hdr = (void *)((u8 *)l3_hdr + 4);
}
if (l3_proto == bpf_htons(ETH_P_IP)) {
if (!check_ipv4_header(l3_hdr, data_end)) {
update_stats(STATS_RX_DROP_BAD_IP, 1);
return XDP_DROP;
}
struct iphdr *iph = l3_hdr;
if (!proto_whitelist_allowed(iph->protocol)) {
update_stats(STATS_RX_DROP_PROTO, 1);
if (fail_closed)
return XDP_DROP;
return XDP_PASS;
}
update_stats(STATS_RX_VALID, 1);
} else if (l3_proto == bpf_htons(ETH_P_IPV6)) {
if (!check_ipv6_header(l3_hdr, data_end)) {
update_stats(STATS_RX_DROP_BAD_IP, 1);
return XDP_DROP;
}
struct ipv6hdr *ip6h = l3_hdr;
if (is_ipv6_extension_header(ip6h->nexthdr)) {
update_stats(STATS_RX_VALID, 1);
return XDP_PASS;
}
if (!proto_whitelist_allowed(ip6h->nexthdr)) {
update_stats(STATS_RX_DROP_PROTO, 1);
if (fail_closed)
return XDP_DROP;
return XDP_PASS;
}
update_stats(STATS_RX_VALID, 1);
} else if (l3_proto == bpf_htons(ETH_P_ARP)) {
update_stats(STATS_RX_VALID, 1);
return XDP_PASS;
} else {
update_stats(STATS_RX_DROP_PROTO, 1);
if (fail_closed)
return XDP_DROP;
return XDP_PASS;
}
{
u32 exp_key = 0;
u8 *exp_cfg = bpf_map_lookup_elem(&expectation_map, &exp_key);
if (exp_cfg) {
u8 proto = 0;
u8 ttl = 0;
void *l4_hdr = NULL;
if (l3_proto == bpf_htons(ETH_P_IP)) {
struct iphdr *iph = l3_hdr;
proto = iph->protocol;
ttl = iph->ttl;
u32 ihl = iph->ihl * 4;
l4_hdr = (void *)((u8 *)l3_hdr + ihl);
} else if (l3_proto == bpf_htons(ETH_P_IPV6)) {
struct ipv6hdr *ip6h = l3_hdr;
proto = ip6h->nexthdr;
ttl = ip6h->hop_limit;
l4_hdr = (void *)((u8 *)l3_hdr + sizeof(struct ipv6hdr));
}
u8 min_ttl = exp_cfg[9];
if (min_ttl > 0 && ttl < min_ttl) {
update_stats(STATS_RX_DROP_PROTO, 1);
if (fail_closed)
return XDP_DROP;
return XDP_PASS;
}
if (l3_proto == bpf_htons(ETH_P_IP)) {
struct iphdr *iph = l3_hdr;
u8 frag_policy = exp_cfg[8];
if ((frag_policy == 1 || frag_policy == 2) &&
(iph->frag_off & bpf_htons(0x3FFF)) != 0) {
update_stats(STATS_RX_DROP_PROTO, 1);
if (fail_closed)
return XDP_DROP;
return XDP_PASS;
}
}
if (proto == IPPROTO_TCP || proto == IPPROTO_UDP) {
if (!l4_hdr) {
update_stats(STATS_RX_DROP_SHORT, 1);
return XDP_DROP;
}
if ((void *)((u8 *)l4_hdr + 4) > data_end) {
update_stats(STATS_RX_DROP_SHORT, 1);
return XDP_DROP;
}
u16 dst_port = bpf_ntohs(*(__u16 *)((u8 *)l4_hdr + 2));
if (proto == IPPROTO_TCP) {
u16 port_count;
__builtin_memcpy(&port_count, &exp_cfg[12], sizeof(port_count));
if (port_count > 16)
port_count = 16;
if (port_count > 0) {
bool port_allowed = false;
#pragma clang loop unroll(full)
for (int i = 0; i < 16; i++) {
if (i < port_count) {
u16 allowed_port;
__builtin_memcpy(&allowed_port,
&exp_cfg[14 + i * 2],
sizeof(allowed_port));
if (dst_port == allowed_port)
port_allowed = true;
}
}
if (!port_allowed) {
update_stats(STATS_RX_DROP_PROTO, 1);
if (fail_closed)
return XDP_DROP;
return XDP_PASS;
}
}
} else {
u16 port_count;
__builtin_memcpy(&port_count, &exp_cfg[46], sizeof(port_count));
if (port_count > 16)
port_count = 16;
if (port_count > 0) {
bool port_allowed = false;
#pragma clang loop unroll(full)
for (int i = 0; i < 16; i++) {
if (i < port_count) {
u16 allowed_port;
__builtin_memcpy(&allowed_port,
&exp_cfg[48 + i * 2],
sizeof(allowed_port));
if (dst_port == allowed_port)
port_allowed = true;
}
}
if (!port_allowed) {
update_stats(STATS_RX_DROP_PROTO, 1);
if (fail_closed)
return XDP_DROP;
return XDP_PASS;
}
}
}
}
}
}
update_stats(STATS_RX_PACKETS, 1);
update_stats(STATS_REDIRECTED, 1);
return bpf_redirect_map(&xsk_map, queue_id, XDP_PASS);
}
char _license[] SEC("license") = "GPL";