Skip to content

Linux Kernel Netfilter 子系统分析文档 ​

目录 ​

  1. Netfilter 概述
  2. 核心数据结构
  3. Hook 机制
  4. Connection Tracking (连接跟踪)
  5. iptables
  6. nftables
  7. Xtables 框架
  8. 数据包处理流程
  9. 关键代码索引

1. Netfilter 概述 ​

Netfilter 是 Linux 内核提供的网络数据包过滤框架,位于网络栈的关键路径上,允许内核模块在网络数据包处理的不同阶段拦截和修改数据包。

1.1 HOOK 点 ​

Netfilter 在网络协议栈的不同位置定义了多个 HOOK 点:

                                    ┌─────────────────────────────────────┐
                                    │          Network Stack              │
                                    │                                     │
  ┌──────────┐    ┌──────────┐    ┌──────────┐    ┌──────────┐    ┌──────────┐    ┌──────────┐
  │ Ingress  │    │PREROUTING│    │          │    │ FORWARD  │    │          │    │POSTROUTING│
  │(NF_NETDEV│───▶│ (NF_INET │───▶│ 路由判决  │───▶│ (NF_INET │───▶│ 路由判决  │───▶│ (NF_INET  │
  │ _INGRESS)│    │ _PRE_ROUTING)│    │          │    │ _FORWARD)│    │          │    │ _POST_ROUTING)│
  └──────────┘    └──────────┘    └──────────┘    └──────────┘    └──────────┘    └──────────┘
                                          │                                           │
                                          │                                           │
                                    ┌─────▼─────┐                              ┌──────▼─────┐
                                    │  LOCAL_IN │                              │ LOCAL_OUT  │
                                    │(NF_INET   │                              │ (NF_INET   │
                                    │ _LOCAL_IN)│                              │ _LOCAL_OUT)│
                                    └─────┬─────┘                              └──────┬─────┘
                                          │                                           │
                                          ▼                                           ▼
                                    ┌──────────┐                              ┌──────────┐
                                    │  应用层  │                              │  应用层   │
                                    └──────────┘                              └──────────┘

Hook 点定义 (include/uapi/linux/netfilter.h:42-50):

c
enum nf_inet_hooks {
    NF_INET_PRE_ROUTING,    // 0 - 路由之前,所有入站数据包
    NF_INET_LOCAL_IN,       // 1 - 已路由,目标是本机
    NF_INET_FORWARD,        // 2 - 已路由,需要转发
    NF_INET_LOCAL_OUT,      // 3 - 本机产生的数据包
    NF_INET_POST_ROUTING,   // 4 - 即将发送之前
    NF_INET_NUMHOOKS,
    NF_INET_INGRESS = NF_INET_NUMHOOKS,  // 5 - 设备 Ingress
};

1.2 Verdict (判决值) ​

Hook 函数返回以下判决值 (include/uapi/linux/netfilter.h:11-17):

c
#define NF_DROP 0       // 丢弃数据包
#define NF_ACCEPT 1     // 接受数据包,继续下一个 hook
#define NF_STOLEN 2     // 数据包被"偷走",不再继续处理
#define NF_QUEUE 3      // 将数据包放入队列供用户空间处理
#define NF_REPEAT 4     // 重新调用当前 hook
#define NF_STOP 5       // 已废弃,为兼容用户空间
#define NF_MAX_VERDICT NF_STOP

1.3 协议族 ​

Netfilter 支持多种协议族 (include/uapi/linux/netfilter.h:58-70):

c
enum {
    NFPROTO_UNSPEC   =  0,
    NFPROTO_INET     =  1,  // 通用 INET (横跨 IPv4/IPv6)
    NFPROTO_IPV4     =  2,
    NFPROTO_ARP      =  3,
    NFPROTO_NETDEV   =  5,
    NFPROTO_BRIDGE   =  7,
    NFPROTO_IPV6     = 10,
    NFPROTO_NUMPROTO,
};

2. 核心数据结构 ​

2.1 struct nf_hook_ops ​

Hook 操作结构体,定义在 include/linux/netfilter.h:98-111:

c
struct nf_hook_ops {
    struct list_head    list;       // 用于链表中链接
    struct rcu_head     rcu;        // RCU 机制

    /* 用户填充的字段 */
    nf_hookfn          *hook;       // Hook 回调函数
    struct net_device  *dev;         // 绑定的网络设备 (可选)
    void               *priv;       // 私有数据
    u8                 pf;          // 协议族 (NFPROTO_*)
    enum nf_hook_ops_type hook_ops_type:8;  // Hook 操作类型
    unsigned int       hooknum;      // Hook 编号 (NF_INET_*)
    /* Hook 按优先级升序排列 */
    int                priority;     // 优先级,越小越高
};

Hook 回调函数类型 (include/linux/netfilter.h:88-90):

c
typedef unsigned int nf_hookfn(void *priv,
                               struct sk_buff *skb,
                               const struct nf_hook_state *state);

2.2 struct nf_hook_state ​

Hook 状态结构,定义在 include/linux/netfilter.h:78-86:

c
struct nf_hook_state {
    u8              hook;       // 当前 hook 编号
    u8              pf;         // 协议族
    struct net_device *in;      // 输入设备
    struct net_device *out;     // 输出设备
    struct sock     *sk;        // 关联的 sock (可选)
    struct net      *net;       // 网络命名空间
    int (*okfn)(struct net *, struct sock *, struct sk_buff *);  // 继续处理函数
};

2.3 struct nf_hook_entries ​

Hook 条目集合,定义在 include/linux/netfilter.h:123-141:

c
struct nf_hook_entries {
    u16                 num_hook_entries;  // Hook 条目数量
    /* 变长数组,包含实际的 hook 函数 */
    struct nf_hook_entry hooks[];

    /* 尾部:原始 ops 指针数组 (仅在删除时需要) */
    /* struct nf_hook_ops *orig_ops[] */
    /* struct nf_hook_entries_rcu_head head */
};

2.4 struct sk_buff ​

Socket buffer,是网络数据包的核心结构体,定义在 include/linux/skbuff.h。关键字段:

c
struct sk_buff {
    struct sk_buff      *next;          // 下一个 skb
    struct sk_buff      *prev;          // 上一个 skb
    struct sock         *sk;            // 所属 socket
    struct net_device   *dev;           // 关联的网络设备
    unsigned int        len;            // 数据包长度
    unsigned int        data_len;       // 数据部分长度
    __u16               protocol;       // 协议号 (以太网帧类型)
    void                *data;          // 数据指针
    void                *tail;          // 数据尾部指针
    /* Netfilter 相关字段 */
    unsigned long       _nfct;          // 连接跟踪信息
    __u32              mark;            // Netfilter mark
    __u8               nf_trace;       // 跟踪标志
    /* ... 其他字段 ... */
};

2.5 struct nf_conn ​

连接跟踪结构,定义在 include/net/netfilter/nf_conntrack.h:74-125:

c
struct nf_conn {
    struct nf_conntrack ct_general;     // 通用连接跟踪结构

    spinlock_t         lock;            // 保护此结构的锁
    u32                timeout;         // 过期时间 (jiffies)

#ifdef CONFIG_NF_CONNTRACK_ZONES
    struct nf_conntrack_zone zone;      // 连接跟踪区域
#endif

    /* 原始方向和回复方向的 tuple */
    struct nf_conntrack_tuple_hash tuplehash[IP_CT_DIR_MAX];

    unsigned long      status;          // 连接状态位图

    possible_net_t     ct_net;          // 网络命名空间

#if IS_ENABLED(CONFIG_NF_NAT)
    struct hlist_node  nat_bysource;   // NAT 源查找
#endif

    struct nf_conn     *master;         // 主连接 (用于期望连接)

#if defined(CONFIG_NF_CONNTRACK_MARK)
    u_int32_t         mark;            // 连接标记
#endif

#ifdef CONFIG_NF_CONNTRACK_SECMARK
    u_int32_t         secmark;         // 安全标记
#endif

    struct nf_ct_ext   *ext;           // 扩展数据

    /* 协议私有数据 (必须是最后一个成员) */
    union nf_conntrack_proto proto;
};

2.6 struct nf_conntrack_tuple ​

连接跟踪的 tuple,定义在 include/net/netfilter/nf_conntrack_tuple.h:37-76:

c
struct nf_conntrack_tuple {
    struct nf_conntrack_man src;  // 源地址信息 (可被 NAT 修改)

    struct {
        union nf_inet_addr u3;    // 目标地址
        union {
            __be16 all;
            struct { __be16 port; } tcp;
            struct { __be16 port; } udp;
            struct { __u8 type, code; } icmp;
            struct { __be16 port; } dccp;
            struct { __be16 port; } sctp;
            struct { __be16 key; } gre;
        } u;

        u_int8_t protonum;         // L4 协议号
        u_int8_t dir;             // 方向
    } dst;
};

2.7 Connection Tracking 状态 ​

定义在 include/uapi/linux/netfilter/nf_conntrack_common.h:7-35:

c
enum ip_conntrack_info {
    IP_CT_ESTABLISHED,     // 0 - 已建立连接 (任意方向)
    IP_CT_RELATED,         // 1 - 相关连接 (如 FTP 数据连接)
    IP_CT_NEW,             // 2 - 新连接
    IP_CT_IS_REPLY,        // 3 - 回复方向标志位
    IP_CT_ESTABLISHED_REPLY = IP_CT_ESTABLISHED + IP_CT_IS_REPLY,  // 4
    IP_CT_RELATED_REPLY = IP_CT_RELATED + IP_CT_IS_REPLY,          // 5
    IP_CT_NUMBER,          // 6 - 连接类型数量
    IP_CT_UNTRACKED = 7,   // 未经跟踪的连接
};

2.8 Connection Tracking 状态位 ​

定义在 include/uapi/linux/netfilter/nf_conntrack_common.h:42-130:

c
enum ip_conntrack_status {
    IPS_EXPECTED_BIT = 0,      // 期望连接
    IPS_SEEN_REPLY_BIT = 1,    // 见过回复
    IPS_ASSURED_BIT = 2,       // 确认连接
    IPS_CONFIRMED_BIT = 3,     // 已确认 (已离开本机)
    IPS_SRC_NAT_BIT = 4,       // 需要源 NAT
    IPS_DST_NAT_BIT = 5,       // 需要目标 NAT
    IPS_SEQ_ADJUST_BIT = 6,    // 需要 TCP 序列号调整
    IPS_SRC_NAT_DONE_BIT = 7,  // 源 NAT 已完成
    IPS_DST_NAT_DONE_BIT = 8,  // 目标 NAT 已完成
    IPS_DYING_BIT = 9,         // 连接正在销毁
    IPS_FIXED_TIMEOUT_BIT = 10,// 固定超时
    IPS_TEMPLATE_BIT = 11,     // 模板连接
    IPS_UNTRACKED_BIT = 12,    // 未跟踪
    IPS_NAT_CLASH_BIT = 12,    // NAT 冲突 (重用)
    IPS_HELPER_BIT = 13,       // 已设置辅助模块
    IPS_OFFLOAD_BIT = 14,      // 已卸载到流表
    IPS_HW_OFFLOAD_BIT = 15,   // 已卸载到硬件
};

3. Hook 机制 ​

3.1 Hook 注册 ​

Hook 通过 nf_register_net_hook() 注册到内核:

c
// include/linux/netfilter.h:199
int nf_register_net_hook(struct net *net, const struct nf_hook_ops *ops);

内部实现 (net/netfilter/core.c):

c
// net/netfilter/core.c:100-200
static struct nf_hook_entries *
nf_hook_entries_grow(const struct nf_hook_entries *old,
                     const struct nf_hook_ops *reg)
{
    // 1. 分配新的 hook 条目数组
    // 2. 按优先级插入新的 hook
    // 3. 返回更新后的条目集合
}

// 实现代码流程:
static int __nf_register_net_hook(struct net *net, int pf,
                                   unsigned int hooknum,
                                   struct nf_hook_ops *ops)
{
    struct nf_hook_entries **pp;
    // 根据协议族选择正确的 hook 数组
    switch (pf) {
    case NFPROTO_IPV4:
        pp = &net->nf.hooks_ipv4[hooknum];
        break;
    case NFPROTO_IPV6:
        pp = &net->nf.hooks_ipv6[hooknum];
        break;
    // ...
    }
    // 调用 nf_hook_entries_grow 添加 hook
}

3.2 Hook 调用流程 ​

c
// include/linux/netfilter.h:227-278
static inline int nf_hook(u_int8_t pf, unsigned int hook, struct net *net,
                          struct sock *sk, struct sk_buff *skb,
                          struct net_device *indev, struct net_device *outdev,
                          int (*okfn)(struct net *, struct sock *, struct sk_buff *))
{
    struct nf_hook_entries *hook_head = NULL;
    int ret = 1;

    // 使用 Jump Label 快速路径 (如果 hook 未注册)
#ifdef CONFIG_JUMP_LABEL
    if (__builtin_constant_p(pf) &&
        __builtin_constant_p(hook) &&
        !static_key_false(&nf_hooks_needed[pf][hook]))
        return 1;
#endif

    rcu_read_lock();
    // 获取对应协议族和 hook 的条目
    switch (pf) {
    case NFPROTO_IPV4:
        hook_head = rcu_dereference(net->nf.hooks_ipv4[hook]);
        break;
    case NFPROTO_IPV6:
        hook_head = rcu_dereference(net->nf.hooks_ipv6[hook]);
        break;
    // ...
    }

    if (hook_head) {
        struct nf_hook_state state;
        nf_hook_state_init(&state, hook, pf, indev, outdev, sk, net, okfn);
        ret = nf_hook_slow(skb, &state, hook_head, 0);
    }
    rcu_read_unlock();

    return ret;
}

3.3 nf_hook_slow 函数 ​

核心 hook 处理函数 (net/netfilter/core.c:616-648):

c
int nf_hook_slow(struct sk_buff *skb, struct nf_hook_state *state,
                 const struct nf_hook_entries *e, unsigned int s)
{
    unsigned int verdict;
    int ret;

    // 遍历所有注册的 hook 函数
    for (; s < e->num_hook_entries; s++) {
        // 调用单个 hook 函数
        verdict = nf_hook_entry_hookfn(&e->hooks[s], skb, state);

        switch (verdict & NF_VERDICT_MASK) {
        case NF_ACCEPT:
            break;                          // 继续下一个 hook
        case NF_DROP:
            kfree_skb_reason(skb, SKB_DROP_REASON_NETFILTER_DROP);
            ret = NF_DROP_GETERR(verdict);
            if (ret == 0)
                ret = -EPERM;
            return ret;                     // 丢弃数据包
        case NF_QUEUE:
            ret = nf_queue(skb, state, s, verdict);
            if (ret == 1)
                continue;                   // 队列处理后继续
            return ret;
        case NF_STOLEN:
            return NF_DROP_GETERR(verdict); // 数据包被偷走
        default:
            WARN_ON_ONCE(1);
            return 0;
        }
    }

    return 1;  // 所有 hook 都通过,返回 1 表示调用 okfn
}

4. Connection Tracking (连接跟踪) ​

4.1 概述 ​

Connection Tracking (conntrack) 是 Netfilter 的核心组件,用于跟踪网络连接的状态。它使得 NAT 和有状态防火墙成为可能。

4.2 连接状态流转 ​

                    ┌────────────────────────────────────────────────────────┐
                    │                   连接状态流转                          │
                    └────────────────────────────────────────────────────────┘

    ┌─────────┐     NEW (新建)      ┌─────────────┐
    │  数据包  │ ──────────────────▶│  NEW        │
    │  到达    │                    │  (IP_CT_NEW)│
    └─────────┘                    └──────┬──────┘
                                           │
                                           │ 创建 conntrack 条目
                                           │ 双向tuple哈希
                                           ▼
                                  ┌─────────────────┐
                                  │  双向通信后      │
                                  │  → ESTABLISHED  │
                                  │  (IP_CT_ESTABLISHED)│
                                  └────────┬────────┘
                                           │
                                           │ 特殊协议 (如 FTP)
                                           ▼
                                  ┌─────────────────┐
                                  │  RELATED        │
                                  │  (IP_CT_RELATED)│
                                  └─────────────────┘

    状态判断:
    - NEW:       第一个数据包,创建新跟踪条目
    - ESTABLISHED: 见过双向通信
    - RELATED:   与现有连接相关 (如 FTP 数据连接、ICMP 错误)
    - INVALID:   无法识别的数据包

4.3 Conntrack 哈希表 ​

连接跟踪使用哈希表存储连接条目 (net/netfilter/nf_conntrack_core.c):

c
// net/netfilter/nf_conntrack_core.c:63
struct hlist_nulls_head *nf_conntrack_hash __read_mostly;
EXPORT_SYMBOL_GPL(nf_conntrack_hash);

// 哈希桶数量
unsigned int nf_conntrack_htable_size;
EXPORT_SYMBOL(nf_conntrack_htable_size);

// 最大连接数
unsigned int nf_conntrack_max;
EXPORT_SYMBOL(nf_conntrack_max);

4.4 Conntrack 查找 ​

连接跟踪的查找基于 tuple 的哈希值:

c
// include/net/netfilter/nf_conntrack.h:168-175
static inline struct nf_conn *
nf_ct_get(const struct sk_buff *skb, enum ip_conntrack_info *ctinfo)
{
    unsigned long nfct = skb_get_nfct(skb);
    *ctinfo = nfct & NFCT_INFOMASK;
    return (struct nf_conn *)(nfct & NFCT_PTRMASK);
}

4.5 Conntrack 生命周期 ​

    创建 ──▶ 确认 ──▶ 使用 ──▶ 销毁
      │         │         │
      │         │         └── 双向通信后变为 ASSURED
      │         │
      │         └── 首个数据包确认后
      │
      └── 数据包到达,查找或创建条目

5. iptables ​

5.1 概述 ​

iptables 是传统的 Linux 防火墙工具,使用表 (table)、链 (chain)、规则 (rule) 的三层结构。

5.2 表和链 ​

表名功能包含的链
filter数据包过滤INPUT, FORWARD, OUTPUT
nat网络地址转换PREROUTING, OUTPUT, POSTROUTING
mangle数据包修改所有 5 个基本链
raw关闭连接跟踪PREROUTING, OUTPUT
securitySELinux 标记INPUT, FORWARD, OUTPUT

5.3 struct ipt_entry ​

规则条目结构 (include/uapi/linux/netfilter_ipv4/ip_tables.h:106-125):

c
struct ipt_entry {
    struct ipt_ip ip;              // IP 匹配条件

    unsigned int nfcache;          // 缓存需求

    __u16 target_offset;           // target 在结构中的偏移
    __u16 next_offset;            // 下一个条目在结构中的偏移

    unsigned int comefrom;         // 回跳链的来源

    struct xt_counters counters;   // 统计数据包/字节计数

    /* matches 和 target 数据 */
    unsigned char elems[];
};

5.4 ipt_do_table 函数 ​

iptables 规则处理的核心函数 (net/ipv4/netfilter/ip_tables.c:223-362):

c
unsigned int
ipt_do_table(void *priv, struct sk_buff *skb,
             const struct nf_hook_state *state)
{
    const struct xt_table *table = priv;
    unsigned int hook = state->hook;
    const struct iphdr *ip;
    unsigned int verdict = NF_DROP;
    const char *indev, *outdev;
    const void *table_base;
    struct ipt_entry *e, **jumpstack;
    struct xt_action_param acpar;

    // 1. 初始化
    ip = ip_hdr(skb);
    indev = state->in ? state->in->name : nulldevname;
    outdev = state->out ? state->out->name : nullderdevname;

    acpar.fragoff = ntohs(ip->frag_off) & IP_OFFSET;
    acpar.thoff   = ip_hdrlen(skb);
    acpar.hotdrop = false;
    acpar.state   = state;

    local_bh_disable();
    addend = xt_write_recseq_begin();
    private = READ_ONCE(table->private);
    cpu = smp_processor_id();
    table_base = private->entries;
    jumpstack = private->jumpstack[cpu];

    // 2. 从 hook 入口点开始遍历
    e = get_entry(table_base, private->hook_entry[hook]);

    do {
        // 3. 匹配 IP 头部
        if (!ip_packet_match(ip, indev, outdev, &e->ip, acpar.fragoff)) {
 no_match:
            e = ipt_next_entry(e);
            continue;
        }

        // 4. 遍历所有 match 扩展
        xt_ematch_foreach(ematch, e) {
            acpar.match = ematch->u.kernel.match;
            acpar.matchinfo = ematch->data;
            if (!acpar.match->match(skb, &acpar))
                goto no_match;
        }

        // 5. 更新计数器
        counter = xt_get_this_cpu_counter(&e->counters);
        ADD_COUNTER(*counter, skb->len, 1);

        // 6. 获取 target
        t = ipt_get_target_c(e);

        // 7. 标准 target (verdict)
        if (!t->u.kernel.target->target) {
            v = ((struct xt_standard_target *)t)->verdict;
            if (v < 0) {
                if (v != XT_RETURN) {
                    verdict = (unsigned int)(-v) - 1;
                    break;
                }
                // 处理返回
                if (stackidx == 0) {
                    e = get_entry(table_base, private->underflow[hook]);
                } else {
                    e = jumpstack[--stackidx];
                    e = ipt_next_entry(e);
                }
                continue;
            }
            // 跳转到指定规则
            if (table_base + v != ipt_next_entry(e) &&
                !(e->ip.flags & IPT_F_GOTO)) {
                if (stackidx >= private->stacksize) {
                    verdict = NF_DROP;
                    break;
                }
                jumpstack[stackidx++] = e;
            }
            e = get_entry(table_base, v);
            continue;
        }

        // 8. 调用 target
        acpar.target = t->u.kernel.target;
        acpar.targinfo = t->data;
        verdict = t->u.kernel.target->target(skb, &acpar);

        if (verdict == XT_CONTINUE) {
            ip = ip_hdr(skb);  // target 可能修改了 skb
            e = ipt_next_entry(e);
        } else {
            break;
        }
    } while (!acpar.hotdrop);

    xt_write_recseq_end(addend);
    local_bh_enable();

    return acpar.hotdrop ? NF_DROP : verdict;
}

5.5 数据包匹配 ​

IP 头部匹配函数 (net/ipv4/netfilter/ip_tables.c:42-79):

c
static inline bool
ip_packet_match(const struct iphdr *ip,
                const char *indev, const char *outdev,
                const struct ipt_ip *ipinfo,
                int isfrag)
{
    // 1. 源/目标 IP 地址匹配
    if (NF_INVF(ipinfo, IPT_INV_SRCIP,
                (ip->saddr & ipinfo->smsk.s_addr) != ipinfo->src.s_addr) ||
        NF_INVF(ipinfo, IPT_INV_DSTIP,
                (ip->daddr & ipinfo->dmsk.s_addr) != ipinfo->dst.s_addr))
        return false;

    // 2. 输入/输出接口匹配
    if (ifname_compare_aligned(indev, ipinfo->iniface, ipinfo->iniface_mask) != 0 &&
        NF_INVF(ipinfo, IPT_INV_VIA_IN, true))
        return false;

    if (ifname_compare_aligned(outdev, ipinfo->outiface, ipinfo->outiface_mask) != 0 &&
        NF_INVF(ipinfo, IPT_INV_VIA_OUT, true))
        return false;

    // 3. 协议匹配
    if (ipinfo->proto &&
        NF_INVF(ipinfo, IPT_INV_PROTO, ip->protocol != ipinfo->proto))
        return false;

    // 4. 片段匹配
    if (NF_INVF(ipinfo, IPT_INV_FRAG,
                (ipinfo->flags & IPT_F_FRAG) && !isfrag))
        return false;

    return true;
}

6. nftables ​

6.1 概述 ​

nftables 是新一代的包过滤框架,于 Linux 3.13 引入,旨在替代 iptables。它使用更灵活的表达式 (expression) 机制,而不是 iptables 的 match/target 扩展。

6.2 架构 ​

    ┌─────────────────────────────────────────────────────────────────┐
    │                         nftables 架构                           │
    └─────────────────────────────────────────────────────────────────┘

    ┌──────────────┐
    │  用户空间     │  libnftnl / nft tool
    └──────┬───────┘
           │ netlink
           ▼
    ┌──────────────┐
    │ nf_tables_api│  内核 netlink 接口
    └──────┬───────┘
           │
           ▼
    ┌─────────────────────────────────────────────────────┐
    │                   Table (表)                         │
    │  ┌─────────────────────────────────────────────┐   │
    │  │              Chain (链)                       │   │
    │  │  ┌─────────────────────────────────────┐    │   │
    │  │  │         Rule (规则)                  │    │   │
    │  │  │  ┌─────────┐ ┌─────────┐ ┌───────┐  │    │   │
    │  │  │  │  Expr   │ │  Expr   │ │ Expr  │  │    │   │
    │  │  │  └─────────┘ └─────────┘ └───────┘  │    │   │
    │  │  └─────────────────────────────────────┘    │   │
    │  └─────────────────────────────────────────────┘   │
    └─────────────────────────────────────────────────────┘

6.3 核心结构 ​

nft_chain ​

c
struct nft_chain {
    struct list_head        list;        // 链表链接
    struct rhlist_node      rhlnode;     // 哈希链表节点
    struct nft_table        *table;      // 所属表
    struct nft_base_chain   *basechain;   // 基础链
    u8                      padding;      // 填充
    u32                     handle;       // 句柄
    u32                     use;          // 引用计数
    char                     *name;       // 链名
    struct nft_rule_dp      __rcu *rules[];  // 规则数组
};

nft_base_chain ​

基础链,绑定到 Netfilter hook:

c
struct nft_base_chain {
    struct nf_hook_ops      ops;          // Netfilter hook 操作
    struct nf_hook_entries  __rcu *rules;  // 规则条目
    u32                     policy;       // 策略 (ACCEPT/DROP)
    struct list_head        cb_list;      // 回调列表
    struct rcu_head         rcu;           // RCU 头
};

6.4 nft_do_chain 函数 ​

nftables 规则处理核心 (net/netfilter/nf_tables_core.c:250-347):

c
unsigned int
nft_do_chain(struct nft_pktinfo *pkt, void *priv)
{
    const struct nft_chain *chain = priv, *basechain = chain;
    const struct net *net = nft_net(pkt);
    const struct nft_expr *expr, *last;
    const struct nft_rule_dp *rule;
    struct nft_regs regs;
    unsigned int stackptr = 0;
    struct nft_jumpstack jumpstack[NFT_JUMP_STACK_SIZE];
    bool genbit = READ_ONCE(net->nft.gencursor);
    struct nft_rule_blob *blob;
    struct nft_traceinfo info;

    info.trace = false;
    if (static_branch_unlikely(&nft_trace_enabled))
        nft_trace_init(&info, pkt, basechain);

do_chain:
    // 获取当前代的规则 blob
    if (genbit)
        blob = rcu_dereference(chain->blob_gen_1);
    else
        blob = rcu_dereference(chain->blob_gen_0);

    rule = (struct nft_rule_dp *)blob->data;

next_rule:
    regs.verdict.code = NFT_CONTINUE;

    // 遍历规则中的表达式
    for (; !rule->is_last; rule = nft_rule_next(rule)) {
        nft_rule_dp_for_each_expr(expr, last, rule) {
            // 快速路径: 内联比较操作
            if (expr->ops == &nft_cmp_fast_ops)
                nft_cmp_fast_eval(expr, &regs);
            else if (expr->ops == &nft_cmp16_fast_ops)
                nft_cmp16_fast_eval(expr, &regs);
            else if (expr->ops == &nft_bitwise_fast_ops)
                nft_bitwise_fast_eval(expr, &regs);
            else if (expr->ops != &nft_payload_fast_ops ||
                     !nft_payload_fast_eval(expr, &regs, pkt))
                expr_call_ops_eval(expr, &regs, pkt);

            // 非 NFT_CONTINUE 时停止
            if (regs.verdict.code != NFT_CONTINUE)
                break;
        }

        switch (regs.verdict.code) {
        case NFT_BREAK:
            regs.verdict.code = NFT_CONTINUE;
            nft_trace_copy_nftrace(pkt, &info);
            continue;
        case NFT_CONTINUE:
            nft_trace_packet(pkt, &regs.verdict, &info, rule, NFT_TRACETYPE_RULE);
            continue;
        }
        break;
    }

    nft_trace_verdict(pkt, &info, rule, &regs);

    // 处理 verdict
    switch (regs.verdict.code & NF_VERDICT_MASK) {
    case NF_ACCEPT:
    case NF_QUEUE:
    case NF_STOLEN:
        return regs.verdict.code;
    case NF_DROP:
        return NF_DROP_REASON(pkt->skb, SKB_DROP_REASON_NETFILTER_DROP, EPERM);
    }

    switch (regs.verdict.code) {
    case NFT_JUMP:
        if (WARN_ON_ONCE(stackptr >= NFT_JUMP_STACK_SIZE))
            return NF_DROP;
        jumpstack[stackptr].rule = nft_rule_next(rule);
        stackptr++;
        fallthrough;
    case NFT_GOTO:
        chain = regs.verdict.chain;  // 直接切换到目标链
        goto do_chain;
    case NFT_CONTINUE:
    case NFT_RETURN:
        break;
    default:
        WARN_ON_ONCE(1);
    }

    // 从跳转栈弹出
    if (stackptr > 0) {
        stackptr--;
        rule = jumpstack[stackptr].rule;
        goto next_rule;
    }

    nft_trace_packet(pkt, &regs.verdict, &info, NULL, NFT_TRACETYPE_POLICY);

    // 链策略
    if (nft_base_chain(basechain)->policy == NF_DROP)
        return NF_DROP_REASON(pkt->skb, SKB_DROP_REASON_NETFILTER_DROP, EPERM);

    return nft_base_chain(basechain)->policy;
}

6.5 nftables Verdict ​

nftables 内部 verdict 定义 (include/uapi/linux/nf_tables.h:64-70):

c
enum nft_verdicts {
    NFT_CONTINUE   = -1,   // 继续下一条规则
    NFT_BREAK      = -2,   // 停止当前规则,继续链中下一条规则
    NFT_JUMP       = -3,   // 跳转到指定链,压栈
    NFT_GOTO       = -4,   // 跳转到指定链,不压栈
    NFT_RETURN     = -5,   // 从当前链返回
};

6.6 nftables 表达式类型 ​

表达式功能
nft_cmp比较操作 (=, !=, <, >, etc.)
nft_payload提取数据包载荷
nft_meta获取元数据 (mark, iif, oif, etc.)
nft_ct连接跟踪信息
nft_lookup集合查找
nft_bitwise位操作
nft_byteorder字节序转换
nft_limit速率限制
nft_counter计数器
nft_natNAT 操作
nft_log日志记录
nft_reject拒绝数据包

7. Xtables 框架 ​

7.1 概述 ​

Xtables 是 iptables/ip6tables/arptables 的通用框架,提供表注册、规则管理等公共功能。

7.2 xt_register_table ​

表注册函数 (net/netfilter/x_tables.c:1475-1523):

c
struct xt_table *xt_register_table(struct net *net,
                                   const struct xt_table *input_table,
                                   struct xt_table_info *bootstrap,
                                   struct xt_table_info *newinfo)
{
    struct xt_pernet *xt_net = net_generic(net, xt_pernet_id);
    struct xt_table_info *private;
    struct xt_table *t, *table;
    int ret;

    // 分配表结构
    table = kmemdup(input_table, sizeof(struct xt_table), GFP_KERNEL);

    mutex_lock(&xt[table->af].mutex);

    // 检查表名是否已存在
    list_for_each_entry(t, &xt_net->tables[table->af], list) {
        if (strcmp(t->name, table->name) == 0) {
            ret = -EEXIST;
            goto unlock;
        }
    }

    // 设置初始表信息
    table->private = bootstrap;

    // 替换为新表
    if (!xt_replace_table(table, 0, newinfo, &ret))
        goto unlock;

    private = table->private;
    private->initial_entries = private->number;

    // 添加到表链表
    list_add(&table->list, &xt_net->tables[table->af]);
    mutex_unlock(&xt[table->af].mutex);

    return table;

unlock:
    mutex_unlock(&xt[table->af].mutex);
    kfree(table);
    return ERR_PTR(ret);
}

7.3 xt_entry_match ​

Match 扩展结构 (include/uapi/linux/netfilter/x_tables.h:11-32):

c
struct xt_entry_match {
    union {
        struct {
            __u16 match_size;
            char name[XT_EXTENSION_MAXNAMELEN];  // "tcp", "udp", etc.
            __u8 revision;
        } user;
        struct {
            __u16 match_size;
            struct xt_match *match;  // 内核指针
        } kernel;
        __u16 match_size;
    } u;

    unsigned char data[];  // match 特定数据
};

7.4 xt_entry_target ​

Target 扩展结构 (include/uapi/linux/netfilter/x_tables.h:34-55):

c
struct xt_entry_target {
    union {
        struct {
            __u16 target_size;
            char name[XT_EXTENSION_MAXNAMELEN];  // "DROP", "ACCEPT", etc.
            __u8 revision;
        } user;
        struct {
            __u16 target_size;
            struct xt_target *target;  // 内核指针
        } kernel;
        __u16 target_size;
    } u;

    unsigned char data[0];  // target 特定数据
};

7.5 xt_standard_target ​

标准 target,用于内置 verdict (include/uapi/linux/netfilter/x_tables.h:65-68):

c
struct xt_standard_target {
    struct xt_entry_target target;
    int verdict;  // 负数表示 verdict,正数表示规则偏移
};

7.6 Verdict 编码 ​

c
// 标准 verdict
NF_ACCEPT = 1
NF_DROP = 0
XT_CONTINUE = 0xFFFFFFFF  // 继续匹配下一规则

// 标准 target verdict (负数)
#define XT_RETURN (-NF_REPEAT - 1)  // = -5, 从当前链返回

// 跳转到规则
verdict > 0: 跳转到 table + verdict 位置的规则

8. 数据包处理流程 ​

8.1 IPv4 数据包入口流程 ​

    ┌─────────────────────────────────────────────────────────────────────┐
    │                    IPv4 数据包处理完整流程                          │
    └─────────────────────────────────────────────────────────────────────┘

    ┌─────────┐
    │ 网络设备 │
    │ 接收    │
    └────┬────┘
         │
         ▼
    ┌───────────────────┐
    │ NAPI poll / 软中断 │
    └─────────┬─────────┘
              │
              ▼
    ┌───────────────────┐
    │ netif_receive_skb │
    └─────────┬─────────┘
              │
              ▼
    ┌───────────────────────────────────────────────────────────────────┐
    │                      NF_INET_INGRESS (Netdev Hook)               │
    │                    (可选, CONFIG_NET_NETFILTER)                   │
    └───────────────────────────────────────────────────────────────────┘
              │
              ▼
    ┌───────────────────────────────────────────────────────────────────┐
    │                    ip_rcv / ip_rcv_core                          │
    └───────────────────────────────────────────────────────────────────┘
              │
              ▼
    ┌───────────────────────────────────────────────────────────────────┐
    │              NF_INET_PRE_ROUTING (PREROUTING Hook)                │
    │                                                                       │
    │  ┌──────────────┐                                                   │
    │  │ conntrack   │ ───▶ NEW ──▶ 创建 conntrack 条目                   │
    │  │ (连接跟踪)  │ ───▶ ESTABLISHED/RELATED ───▶ 查找已有条目          │
    │  └──────────────┘                                                   │
    │                                                                       │
    │  ┌──────────────┐                                                   │
    │  │    nat      │ ───▶ DNAT (目标地址转换)                           │
    │  │  (PREROUTING)│                                                    │
    │  └──────────────┘                                                   │
    └───────────────────────────────────────────────────────────────────┘
              │
              ▼
    ┌───────────────────┐
    │  路由判决 (路由表) │
    │  fib_lookup       │
    └─────────┬─────────┘
              │
       ┌──────┴──────┐
       │             │
       ▼             ▼
    ┌─────────┐  ┌───────────────┐
    │  本机   │  │    转发       │
    │ LOCAL_IN│  │  FORWARD      │
    └────┬────┘  └───────┬───────┘
         │               │
         ▼               ▼
    ┌─────────────┐  ┌───────────────────────────────────────────────────┐
    │  NF_INET    │  │               NF_INET_FORWARD                      │
    │  _LOCAL_IN  │  │                                                   │
    │             │  │  ┌─────────────┐   ┌─────────────┐                 │
    │  mangle     │  │  │  filter    │   │   mangle    │                 │
    │  (INPUT)    │  │  │  (FORWARD) │   │  (FORWARD)  │                 │
    │             │  │  └─────────────┘   └─────────────┘                 │
    └──────┬──────┘  └───────┬───────┘
           │                 │
           ▼                 ▼
    ┌─────────────┐  ┌───────────────────────────────────────────────────┐
    │  ip_local_deliver │      │             NF_INET_POST_ROUTING           │
    │       │           │      │  (POSTROUTING Hook)                       │
    └───────┼───────────┘      │                                          │
            │                  │  ┌─────────────┐  ┌─────────────┐         │
            ▼                  │  │    nat     │  │  mangle     │         │
    ┌───────────────────┐       │  │(POSTROUTING)│  │  (mangle)   │         │
    │  传输层处理       │       │  └─────────────┘  └─────────────┘         │
    │  (TCP/UDP/ICMP)  │       └──────────────────────────────────────────┘
    └───────────────────┘                          │
                                                     ▼
                                              ┌─────────────┐
                                              │  设备输出   │
                                              │ ip_finish_output │
                                              └─────────────┘

8.2 本机发出数据包流程 ​

    ┌─────────────────────────────────────────────────────────────────────┐
    │                    本机发出数据包处理流程                            │
    └─────────────────────────────────────────────────────────────────────┘

    ┌───────────────────┐
    │  应用层 send()    │
    └─────────┬─────────┘
              │
              ▼
    ┌───────────────────┐
    │  传输层           │
    │  tcp_sendmsg etc. │
    └─────────┬─────────┘
              │
              ▼
    ┌───────────────────────────────────────────────────────────────────┐
    │              NF_INET_LOCAL_OUT (OUTPUT Hook)                       │
    │                                                                       │
    │  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐              │
    │  │    raw      │  │   conntrack  │  │    nat      │              │
    │  │  (OUTPUT)   │  │  (OUTPUT)    │  │  (OUTPUT)   │              │
    │  └──────────────┘  └──────────────┘  └──────────────┘              │
    │                                                                       │
    │  ┌──────────────┐  ┌──────────────┐                                │
    │  │   mangle     │  │   filter     │                                │
    │  │  (OUTPUT)    │  │  (OUTPUT)    │                                │
    │  └──────────────┘  └──────────────┘                                │
    └───────────────────────────────────────────────────────────────────┘
              │
              ▼
    ┌───────────────────┐
    │  路由判决         │
    │  ip_route_output  │
    └─────────┬─────────┘
              │
              ▼
    ┌───────────────────────────────────────────────────────────────────┐
    │              NF_INET_POST_ROUTING (POSTROUTING Hook)               │
    │                                                                       │
    │  ┌──────────────┐  ┌──────────────┐                               │
    │  │    nat       │  │   mangle     │                               │
    │  │(POSTROUTING) │  │  (mangle)    │                               │
    │  │ (SNAT/MASQ) │  └──────────────┘                               │
    │  └──────────────┘                                                 │
    └───────────────────────────────────────────────────────────────────┘
              │
              ▼
    ┌───────────────────┐
    │  设备输出         │
    │ dev_queue_xmit    │
    └───────────────────┘

9. 关键代码索引 ​

9.1 核心文件 ​

文件功能
net/netfilter/core.cNetfilter 核心: Hook 注册/调用
net/netfilter/nf_conntrack_core.c连接跟踪核心实现
net/ipv4/netfilter/ip_tables.ciptables 主处理 (ipt_do_table)
net/ipv6/netfilter/ip6_tables.cip6tables 主处理
net/netfilter/x_tables.cXtables 框架
net/netfilter/nf_tables_api.cnftables netlink API
net/netfilter/nf_tables_core.cnftables 核心处理 (nft_do_chain)

9.2 关键头文件 ​

文件内容
include/linux/netfilter.h核心 netfilter 结构
include/uapi/linux/netfilter.h用户空间 API (HOOK/Verdict 定义)
include/uapi/linux/netfilter/x_tables.hXtables 通用结构
include/uapi/linux/netfilter_ipv4/ip_tables.hiptables 结构
include/net/netfilter/nf_conntrack.h连接跟踪结构
include/net/netfilter/nf_conntrack_tuple.hTuple 结构
include/uapi/linux/nf_tables.hnftables 用户空间 API

9.3 Hook 调用位置 ​

协议文件Hook 点
IPv4net/ipv4/ip_input.cip_rcv 后 NF_INET_PRE_ROUTING
IPv4net/ipv4/ip_input.cNF_INET_LOCAL_IN (路由后)
IPv4net/ipv4/ip_forward.cNF_INET_FORWARD
IPv4net/ipv4/ip_output.cNF_INET_LOCAL_OUT
IPv4net/ipv4/ip_output.cNF_INET_POST_ROUTING
IPv6net/ipv6/ip6_input.cNF_INET_PRE_ROUTING
IPv6net/ipv6/ip6_input.cNF_INET_LOCAL_IN
IPv6net/ipv6/ip6_forward.cNF_INET_FORWARD
IPv6net/ipv6/ip6_output.cNF_INET_LOCAL_OUT
IPv6net/ipv6/ip6_output.cNF_INET_POST_ROUTING

附录 A: 常用命令参考 ​

A.1 iptables 命令 ​

bash
# 列出规则
iptables -L -n -v

# 添加规则
iptables -A INPUT -p tcp --dport 22 -j ACCEPT

# 删除规则
iptables -D INPUT -p tcp --dport 22 -j ACCEPT

# NAT 表规则
iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -j MASQUERADE

A.2 nftables 命令 ​

bash
# 列出规则
nft list ruleset

# 添加表
nft add table ip filter

# 添加链
nft add chain ip filter input { type filter hook input priority 0 \; }

# 添加规则
nft add rule ip filter input tcp dport 22 accept

# NAT 规则
nft add rule ip nat postrouting meta oifname eth0 masquerade

A.3 conntrack 命令 ​

bash
# 查看连接跟踪表
conntrack -L

# 查看特定连接
conntrack -L -p tcp --dport 22

# 删除连接
conntrack -D -p tcp --dport 22

附录 B: 调试接口 ​

B.1 Proc 文件系统 ​

/proc/net/nf_conntrack       - 连接跟踪表
/proc/net/ip_tables_names    - 已注册的 iptables 表
/proc/net/ip6_tables_names   - 已注册的 ip6tables 表
/proc/sys/net/netfilter/nf_conntrack_max  - 最大连接数
/proc/sys/net/netfilter/nf_conntrack_tcp_timeout_established - TCP 超时
  • conntrack netlink: /proc/net/netfilter/nfnetlink_queue
  • netfilter netlink: /proc/net/netfilter/nfnetlink

文档版本: 1.0生成日期: 2026-04-26内核版本: Linux Kernel Mainline (基于 master 分支)

基于 VitePress 构建