zenith-stack 0.1.0

Zenith 全协议栈框架:AF_XDP + eBPF + TLS 1.3 + HTTP/1-2-3 + Web + Proxy + WAF,按需导入
Documentation
/* SPDX-License-Identifier: GPL-2.0 */
/*
 * Zenith XDP 主程序 - 最小化导流与前置校验
 *
 * 功能:
 * 1. 帧合法性前置校验(长度、以太网头、IP头)
 * 2. 极简 L2/L3/L4 字段提取
 * 3. 队列归属与 XSKMAP 重定向
 * 4. 双 Bank 热更新支撑
 * 5. 极简统计计数(per-CPU Array Map)
 *
 * 设计原则:
 * - 内核态只做可证安全的最小导流
 * - 禁止实现 TCP 状态机、TLS、HTTP、WAF 等复杂逻辑
 * - 禁止使用哈希 Map、动态扩容 Map、无界数据结构
 * - 禁止无界循环、递归、复杂函数调用链
 */

#include "xdp_common.h"

/*
 * XDP 主入口函数
 * 返回值:
 *   XDP_REDIRECT - 成功重定向到 AF_XDP
 *   XDP_DROP - 丢弃(异常包)
 *   XDP_PASS - 放行到内核协议栈(回退模式)
 */
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;

    /* ============ 0. 读取运行时配置 ============ */
    fail_closed = get_config_u32(CONFIG_KEY_FAIL_CLOSED);
    mtu = get_config_u32(CONFIG_KEY_MTU);

    /* ============ 1. 基础长度校验 ============ */
    /* 以太网帧最小长度校验由 check_eth_header 内部完成
     * (ETH_HLEN == sizeof(struct ethhdr) == 14,无需重复检查) */

    /* ============ 1.1 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;
        }
    }

    /* ============ 2. 以太网头校验 ============ */
    if (!check_eth_header(ctx, data, data_end)) {
        update_stats(STATS_RX_DROP_BAD_ETH, 1);
        return XDP_DROP;
    }

    /* ============ 3. 获取队列 ID ============ */
    queue_id = ctx->rx_queue_index;

    /* ============ 4. 查找 XSK 映射 ============ */
    u32 *xsk_fd;
    xsk_fd = bpf_map_lookup_elem(&xsk_map, &queue_id);
    if (!xsk_fd) {
        /* 无对应 XSK 映射 */
        update_stats(STATS_RX_DROP_NO_XSK, 1);
        if (fail_closed)
            return XDP_DROP;
        return XDP_PASS;
    }

    /* ============ 5. IP 头校验(可选) + VLAN 解封装 ============ */
    struct ethhdr *eth = data;
    __u16 l3_proto = eth->h_proto;
    void *l3_hdr = (void *)(eth + 1);

    /*
     * VLAN 解封装(单层 802.1Q):若 h_proto == ETH_P_VLAN(0x8100),
     * 跳过 4 字节 VLAN tag(2B TCI + 2B 内层 EtherType),重新读 EtherType。
     * 仅解封装一层(BPF verifier 友好,无循环),QinQ 嵌套由用户态二次校验。
     */
    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;
        }
        /*
         * 协议白名单校验(CONFIG_MAP 位图,bit N = 协议号)
         * check_ipv4_header 已保证 IPv4 头完整,protocol 字段可安全读取。
         * 不在白名单:fail_closed 时 DROP,否则 PASS 回退内核协议栈。
         */
        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;
        }
        /*
         * NET-011:IPv6 扩展头未解析。
         * 若 next header 为扩展头(hop-by-hop/routing/fragment/ESP/AH/dest-opts),
         * 真实 L4 协议号在扩展头链之后,用固定头 nexthdr 做白名单/端口校验会错位。
         * 最小导流原则下不解析扩展头链,直接 PASS 交用户态做完整 L4 解析与校验。
         */
        struct ipv6hdr *ip6h = l3_hdr;
        if (is_ipv6_extension_header(ip6h->nexthdr)) {
            update_stats(STATS_RX_VALID, 1);
            return XDP_PASS;
        }
        /*
         * 协议白名单校验:以 IPv6 固定头 nexthdr 为准
         * (确认非扩展头后,nexthdr 即真实 L4 协议号)
         */
        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)) {
        /* ARP 包直接放行到内核协议栈;
         * STATS_RX_VALID 含放行到内核栈的 ARP 包 */
        update_stats(STATS_RX_VALID, 1);
        return XDP_PASS;
    } else {
        /*
         * 未知 EtherType:不在 IPv4/IPv6/ARP 处理范围内。
         * fail_closed 时 DROP,否则 PASS 回退内核协议栈。
         * 禁止直接 fall-through 到 bpf_redirect_map,否则非 IP/非 ARP
         * 流量将绕过 fail_closed 直接重定向到 AF_XDP。
         * 注:STATS_RX_DROP_PROTO 计数器名含 DROP,但 fail_closed=0 时
         * 实际执行 PASS,此计数器包含放行到内核栈的包。
         */
        update_stats(STATS_RX_DROP_PROTO, 1);
        if (fail_closed)
            return XDP_DROP;
        return XDP_PASS;
    }

    /*
     * === 期望过滤 (EXPECTATION_MAP) ===
     *
     * 从 expectation_map 读取 80 字节 ExpectationConfig,执行:
     * 1. TTL 最低值检查(byte 9: min_ttl, 0=不检查)
     * 2. 分片策略检查(byte 8: fragment_policy, 仅 IPv4)
     * 3. TCP/UDP 端口白名单检查(tcp_ports[14..46] / udp_ports[48..80])
     *
     * 字节布局与 zenith-ebpf/src/maps.rs ExpectationConfig::to_bytes() 严格一致。
     * 内核态做最小检查,用户态做完整校验。
     */
    {
        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;
                /* ihl 已由 check_ipv4_header 校验 >= 5 (20 bytes) */
                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));
            }

            /* --- TTL 检查 (byte 9: min_ttl, 0=不检查) --- */
            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;
            }

            /* --- 分片策略检查 (byte 8: fragment_policy, 仅 IPv4) --- */
            if (l3_proto == bpf_htons(ETH_P_IP)) {
                struct iphdr *iph = l3_hdr;
                u8 frag_policy = exp_cfg[8];
                /* 分片判定:frag_off 低 13 位为片偏移,bit 13 (0x2000) 为 MF。
                 * 首分片的偏移为 0 但 MF=1,必须一并判定,否则"丢弃所有分片"
                 * 策略会漏放首分片(偏移=0+MF=1 逃逸)。
                 * 0x3FFF = 0x1FFF(偏移) | 0x2000(MF),不含 0x4000(DF,非分片标志)。
                 *
                 * NET-010:策略 1(丢弃重叠分片)未实现有状态重叠检测,
                 * 内核态按 fail-closed 处理为与策略 2 相同(丢弃所有分片),
                 * 绝不静默放行重叠分片。用户态 validate() 亦拒绝策略 1 配置。
                 */
                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;
                }
            }

            /* --- TCP/UDP 端口白名单检查 --- */
            if (proto == IPPROTO_TCP || proto == IPPROTO_UDP) {
                /* L4 头边界检查: src_port(2B) + dst_port(2B) = 4B */
                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) {
                    /* tcp_port_count at byte offset 12, tcp_ports at offset 14 */
                    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 {
                    /* UDP: udp_port_count at byte offset 46, udp_ports at offset 48 */
                    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;
                        }
                    }
                }
            }
        }
    }

    /* ============ 6. 重定向到 AF_XDP ============ */
    update_stats(STATS_RX_PACKETS, 1);
    update_stats(STATS_REDIRECTED, 1);

    /* 原子重定向到 XSK,传递帧所有权 */
    return bpf_redirect_map(&xsk_map, queue_id, XDP_PASS);
}

char _license[] SEC("license") = "GPL";