概述

Netfilter是Linux内核中的包过滤框架,为防火墙、NAT、包修改等网络功能提供了强大的基础设施。Netfilter框架的设计原理、钩子机制、iptables实现以及连接跟踪系统的完整实现。

1. Netfilter框架架构

1.1 Netfilter的核心职责

Netfilter框架在Linux网络栈中承担以下关键功能:

  • 包过滤:基于规则的数据包过滤和丢弃
  • 网络地址转换:实现SNAT、DNAT等NAT功能
  • 连接跟踪:跟踪网络连接状态和关系
  • 包修改:修改数据包头部和内容
  • 流量整形:控制网络流量的速率和优先级
  • 安全策略:实现各种网络安全策略

1.2 Netfilter框架架构图

graph TB
    subgraph "用户空间"
        IPTABLES[iptables工具]
        IP6TABLES[ip6tables工具]
        NFTABLES[nftables工具]
        CONNTRACK[conntrack工具]
    end
    
    subgraph "Netfilter内核框架"
        subgraph "钩子系统"
            HOOK_PRE[PRE_ROUTING钩子]
            HOOK_LOCAL_IN[LOCAL_IN钩子]
            HOOK_FORWARD[FORWARD钩子]
            HOOK_LOCAL_OUT[LOCAL_OUT钩子]
            HOOK_POST[POST_ROUTING钩子]
        end
        
        subgraph "表和链管理"
            FILTER_TABLE[filter表]
            NAT_TABLE[nat表]
            MANGLE_TABLE[mangle表]
            RAW_TABLE[raw表]
            SECURITY_TABLE[security表]
        end
        
        subgraph "目标和匹配"
            TARGETS[目标模块]
            MATCHES[匹配模块]
            EXTENSIONS[扩展模块]
        end
        
        subgraph "连接跟踪"
            CONNTRACK_CORE[连接跟踪核心]
            CT_HASH[连接跟踪哈希表]
            CT_TIMEOUT[连接超时管理]
            CT_HELPER[连接跟踪助手]
        end
        
        subgraph "NAT系统"
            NAT_CORE[NAT核心]
            SNAT[源NAT]
            DNAT[目标NAT]
            MASQUERADE[IP伪装]
        end
    end
    
    subgraph "网络协议栈"
        IP_LAYER[IP层]
        TCP_LAYER[TCP层]
        UDP_LAYER[UDP层]
        ICMP_LAYER[ICMP层]
    end
    
    %% 用户空间到内核连接
    IPTABLES --> FILTER_TABLE
    IPTABLES --> NAT_TABLE
    IPTABLES --> MANGLE_TABLE
    IP6TABLES --> FILTER_TABLE
    NFTABLES --> TARGETS
    CONNTRACK --> CONNTRACK_CORE
    
    %% 钩子连接到协议栈
    IP_LAYER --> HOOK_PRE
    IP_LAYER --> HOOK_LOCAL_IN
    IP_LAYER --> HOOK_FORWARD
    IP_LAYER --> HOOK_LOCAL_OUT
    IP_LAYER --> HOOK_POST
    
    %% 表连接到钩子
    HOOK_PRE --> RAW_TABLE
    HOOK_PRE --> MANGLE_TABLE
    HOOK_PRE --> NAT_TABLE
    
    HOOK_LOCAL_IN --> MANGLE_TABLE
    HOOK_LOCAL_IN --> FILTER_TABLE
    HOOK_LOCAL_IN --> SECURITY_TABLE
    
    HOOK_FORWARD --> MANGLE_TABLE
    HOOK_FORWARD --> FILTER_TABLE
    HOOK_FORWARD --> SECURITY_TABLE
    
    HOOK_LOCAL_OUT --> RAW_TABLE
    HOOK_LOCAL_OUT --> MANGLE_TABLE
    HOOK_LOCAL_OUT --> NAT_TABLE
    HOOK_LOCAL_OUT --> FILTER_TABLE
    HOOK_LOCAL_OUT --> SECURITY_TABLE
    
    HOOK_POST --> MANGLE_TABLE
    HOOK_POST --> NAT_TABLE
    
    %% 表连接到匹配和目标
    FILTER_TABLE --> MATCHES
    NAT_TABLE --> TARGETS
    MANGLE_TABLE --> EXTENSIONS
    
    %% 连接跟踪集成
    HOOK_PRE --> CONNTRACK_CORE
    CONNTRACK_CORE --> CT_HASH
    CONNTRACK_CORE --> CT_TIMEOUT
    CT_HELPER --> TCP_LAYER
    CT_HELPER --> UDP_LAYER
    
    %% NAT系统连接
    NAT_TABLE --> NAT_CORE
    NAT_CORE --> SNAT
    NAT_CORE --> DNAT
    NAT_CORE --> MASQUERADE
    NAT_CORE --> CONNTRACK_CORE
    
    style HOOK_PRE fill:#e1f5fe
    style CONNTRACK_CORE fill:#f3e5f5
    style NAT_CORE fill:#e8f5e8
    style FILTER_TABLE fill:#fff3e0

2. Netfilter钩子机制

2.1 钩子点定义和注册

/**
 * Netfilter钩子点定义
 * 
 * 这些钩子点定义了数据包在网络栈中的关键处理位置
 */
enum nf_inet_hooks {
    NF_INET_PRE_ROUTING,        /* 路由前处理 */
    NF_INET_LOCAL_IN,           /* 本地输入 */
    NF_INET_FORWARD,            /* 转发处理 */
    NF_INET_LOCAL_OUT,          /* 本地输出 */
    NF_INET_POST_ROUTING,       /* 路由后处理 */
    NF_INET_NUMHOOKS,           /* 钩子数量 */
    NF_INET_INGRESS = NF_INET_NUMHOOKS, /* 入口钩子 */
};

/**
 * nf_hook_ops - Netfilter钩子操作结构
 * 
 * 定义在特定钩子点注册的处理函数
 */
struct nf_hook_ops {
    /* 用户填充的字段 */
    nf_hookfn               *hook;      /* 钩子处理函数 */
    struct net_device       *dev;       /* 关联设备(可选) */
    void                    *priv;      /* 私有数据 */
    u_int8_t                pf;         /* 协议族 */
    unsigned int            hooknum;    /* 钩子编号 */
    /* 钩子的优先级。 较低的数字 = 较早调用。 */
    int                     priority;   /* 优先级 */
    
    /* 由netfilter核心填充 */
    struct list_head        list;       /* 钩子链表 */
};

/**
 * nf_hook_state - 钩子状态信息
 * 
 * 传递给钩子函数的上下文信息
 */
struct nf_hook_state {
    unsigned int hook;                  /* 当前钩子编号 */
    u_int8_t pf;                       /* 协议族 */
    struct net_device *in;             /* 输入设备 */
    struct net_device *out;            /* 输出设备 */
    struct sock *sk;                   /* 关联套接字 */
    struct net *net;                   /* 网络命名空间 */
    int (*okfn)(struct net *, struct sock *, struct sk_buff *); /* 继续处理函数 */
};

/**
 * nf_register_net_hook - 注册网络钩子
 * @net: 网络命名空间
 * @reg: 钩子注册信息
 * 
 * 在指定网络命名空间中注册netfilter钩子
 * 返回值:成功返回0,失败返回负错误码
 */
int nf_register_net_hook(struct net *net, const struct nf_hook_ops *reg)
{
    struct nf_hook_ops *elem;
    struct nf_hook_entries *new_hooks;
    struct nf_hook_entries __rcu **pp;
    
    if (reg->pf == NFPROTO_NETDEV) {
        if (reg->hooknum == NF_NETDEV_INGRESS)
            return nf_register_netdev_hook(net, reg);
    }
    
    pp = nf_hook_entry_head(net, reg->pf, reg->hooknum, reg->dev);
    if (!pp)
        return -EINVAL;
    
    mutex_lock(&nf_hook_mutex);
    
    new_hooks = nf_hook_entries_grow(rcu_dereference_protected(*pp, lockdep_is_held(&nf_hook_mutex)), reg);
    if (IS_ERR(new_hooks)) {
        mutex_unlock(&nf_hook_mutex);
        return PTR_ERR(new_hooks);
    }
    
    rcu_assign_pointer(*pp, new_hooks);
    
    mutex_unlock(&nf_hook_mutex);
    
#ifdef CONFIG_NETFILTER_INGRESS
    if (reg->pf == NFPROTO_NETDEV && reg->hooknum == NF_NETDEV_INGRESS)
        net_inc_ingress_queue();
#endif
    
    return 0;
}

/**
 * NF_HOOK - Netfilter钩子调用宏
 * @pf: 协议族
 * @hook: 钩子编号
 * @net: 网络命名空间
 * @sk: 套接字
 * @skb: 数据包
 * @in: 输入设备
 * @out: 输出设备
 * @okfn: 继续处理函数
 * 
 * 在指定钩子点执行注册的处理函数
 */
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 *in, struct net_device *out,
                         int (*okfn)(struct net *, struct sock *, struct sk_buff *))
{
    int ret = nf_hook(pf, hook, net, sk, skb, in, out, okfn);
    if (ret == 1)
        ret = okfn(net, sk, skb);
    return ret;
}

/**
 * nf_hook - 执行netfilter钩子
 * @pf: 协议族
 * @hook: 钩子编号
 * @net: 网络命名空间
 * @sk: 套接字
 * @skb: 数据包
 * @indev: 输入设备
 * @outdev: 输出设备
 * @okfn: 继续处理函数
 * 
 * 执行指定钩子点的所有注册处理函数
 * 返回值:处理结果
 */
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 *))
{
    const struct nf_hook_entries *hook_head = NULL;
    int ret = 1;
    
#ifdef HAVE_JUMP_LABEL
    if (static_key_false(&nf_hooks_needed[pf][hook])) {
#endif
        rcu_read_lock();
        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;
        case NFPROTO_ARP:
            if (WARN_ON_ONCE(hook >= ARRAY_SIZE(net->nf.hooks_arp)))
                break;
            hook_head = rcu_dereference(net->nf.hooks_arp[hook]);
            break;
        case NFPROTO_BRIDGE:
            hook_head = rcu_dereference(net->nf.hooks_bridge[hook]);
            break;
        default:
            WARN_ON_ONCE(1);
            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();
#ifdef HAVE_JUMP_LABEL
    }
#endif
    
    return ret;
}

2.2 钩子处理函数实现

/**
 * nf_hook_slow - 慢路径钩子处理
 * @skb: 数据包
 * @state: 钩子状态
 * @e: 钩子条目
 * @i: 起始索引
 * 
 * 执行钩子链中的所有处理函数
 * 返回值:处理结果
 */
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;
    
    for (; s < e->num_hook_entries; s++) {
        verdict = nf_hook_entry_hookfn(&e->hooks[s], skb, state);
        switch (verdict & NF_VERDICT_MASK) {
        case NF_ACCEPT:
            continue;
        case NF_DROP:
            kfree_skb(skb);
            ret = NF_DROP_GETERR(verdict);
            return ret ? : -EPERM;
        case NF_QUEUE:
            ret = nf_queue(skb, state, s, verdict);
            if (ret == 1)
                continue;
            return ret;
        default:
            /* 假设这是NF_STOLEN或者其他无效的verdict。
             * 钩子函数必须释放skb。
             */
            return 0;
        }
    }
    
    return 1;
}

/**
 * 钩子处理函数的返回值定义
 */
#define NF_DROP         0       /* 丢弃数据包 */
#define NF_ACCEPT       1       /* 接受数据包,继续处理 */
#define NF_STOLEN       2       /* 钩子函数接管数据包 */
#define NF_QUEUE        3       /* 将数据包排队到用户空间 */
#define NF_REPEAT       4       /* 再次调用此钩子 */
#define NF_STOP         5       /* 停止遍历钩子链 */
#define NF_MAX_VERDICT  NF_STOP /* 最大判决值 */

/**
 * nf_hook_state_init - 初始化钩子状态
 * @p: 钩子状态指针
 * @hook: 钩子编号
 * @pf: 协议族
 * @indev: 输入设备
 * @outdev: 输出设备
 * @sk: 套接字
 * @net: 网络命名空间
 * @okfn: 继续处理函数
 * 
 * 初始化传递给钩子函数的状态信息
 */
static inline void nf_hook_state_init(struct nf_hook_state *p,
                                     unsigned int hook,
                                     u_int8_t pf,
                                     struct net_device *indev,
                                     struct net_device *outdev,
                                     struct sock *sk,
                                     struct net *net,
                                     int (*okfn)(struct net *, struct sock *, struct sk_buff *))
{
    p->hook = hook;
    p->pf = pf;
    p->in = indev;
    p->out = outdev;
    p->sk = sk;
    p->net = net;
    p->okfn = okfn;
}

3. iptables规则处理

3.1 iptables表结构

/**
 * xt_table - xtables表结构
 * 
 * 代表iptables中的一个表(如filter、nat、mangle等)
 */
struct xt_table {
    struct list_head list;              /* 表链表 */
    
    /* 表的基本信息 */
    unsigned int valid_hooks;           /* 有效钩子掩码 */
    
    /* 表私有数据和元信息 */
    struct xt_table_info *private;      /* 表私有信息 */
    
    /* 挂载到实际钩子点的处理函数 */
    struct module *me;                  /* 所属模块 */
    
    u_int8_t af;                       /* 地址族 */
    int priority;                      /* 优先级 */
    
    /* 用于注册/注销钩子 */
    const char name[XT_TABLE_MAXNAMELEN]; /* 表名称 */
};

/**
 * xt_table_info - 表信息结构
 * 
 * 包含表的具体规则和配置信息
 */
struct xt_table_info {
    unsigned int size;                  /* 表大小 */
    unsigned int number;                /* 规则数量 */
    unsigned int initial_entries;       /* 初始条目数 */
    
    /* 每个钩子的条目数和偏移 */
    unsigned int hook_entry[NF_INET_NUMHOOKS];   /* 钩子入口偏移 */
    unsigned int underflow[NF_INET_NUMHOOKS];    /* 下溢偏移 */
    
    /*
     * 每CPU的跳转栈 - 数据跟随在entries[]之后
     */
    unsigned int stacksize;             /* 栈大小 */
    void ***jumpstack;                  /* 跳转栈 */
    
    unsigned char entries[0] __aligned(8); /* 规则条目 */
};

/**
 * ipt_entry - IPv4 iptables规则条目
 * 
 * 代表iptables中的一条规则
 */
struct ipt_entry {
    struct ipt_ip ip;                   /* IP匹配条件 */
    
    /* 标记位和计数器 */
    unsigned int nfcache;               /* netfilter缓存 */
    
    __u16 target_offset;                /* 目标偏移 */
    __u16 next_offset;                  /* 下一个规则偏移 */
    
    /* 回退计数器 */
    unsigned int comefrom;              /* 来源标记 */
    
    /* 数据包和字节计数器 */
    struct xt_counters counters;        /* 计数器 */
    
    /* 可变长度的匹配和目标数据 */
    unsigned char elems[0];             /* 元素数据 */
};

/**
 * ipt_ip - IPv4匹配条件
 * 
 * 定义IPv4数据包的匹配条件
 */
struct ipt_ip {
    struct in_addr src, dst;            /* 源和目标IP地址 */
    struct in_addr smsk, dmsk;          /* 源和目标掩码 */
    char iniface[IFNAMSIZ], outiface[IFNAMSIZ]; /* 接口名称 */
    unsigned char iniface_mask[IFNAMSIZ], outiface_mask[IFNAMSIZ]; /* 接口掩码 */
    
    __u16 proto;                        /* 协议 */
    
    __u8 flags;                         /* 标志 */
    __u8 invflags;                      /* 反转标志 */
};

/**
 * ipt_do_table - 执行iptables表处理
 * @skb: 数据包
 * @state: 钩子状态
 * @table: iptables表
 * 
 * 遍历iptables表中的规则并执行匹配和目标操作
 * 返回值:处理结果
 */
unsigned int ipt_do_table(struct sk_buff *skb,
                         const struct nf_hook_state *state,
                         struct xt_table *table)
{
    unsigned int hook = state->hook;
    static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long))));
    const struct iphdr *ip;
    /* 初始化位置:规则0 */
    unsigned int addend = xt_write_recseq_begin();
    const struct ipt_entry *e, **jumpstack;
    const char *indev, *outdev;
    const void *table_base;
    struct xt_table_info *private;
    int cpu = smp_processor_id();
    
    /* 读取表私有数据 */
    private = READ_ONCE(table->private);
    table_base = private->entries;
    
    jumpstack = (const struct ipt_entry **)private->jumpstack[cpu];
    
    /* 获取设备名称 */
    indev = state->in ? state->in->name : nulldevname;
    outdev = state->out ? state->out->name : nulldevname;
    
    ip = ip_hdr(skb);
    
    e = get_entry(table_base, private->hook_entry[hook]);
    
    do {
        const struct xt_entry_target *t;
        const struct xt_entry_match *ematch;
        struct xt_acct_state acct;
        
        WARN_ON(!e);
        acct.pos = e;
        if (!ip_packet_match(ip, indev, outdev,
                            &e->ip, state->fragoff)) {
            no_match:
                e = ipt_next_entry(e);
                continue;
        }
        
        xt_ematch_foreach(ematch, e) {
            acct.match = ematch->u.kernel.match;
            acct.matchinfo = ematch->data;
            acct.thoff = ip_hdrlen(skb);
            acct.fragoff = state->fragoff;
            if (!acct.match->match(skb, &acct))
                goto no_match;
        }
        
        ADD_COUNTER(e->counters, skb->len, 1);
        
        t = ipt_get_target_c(e);
        WARN_ON(!t->u.kernel.target);
        
#if IS_ENABLED(CONFIG_NETFILTER_XT_TARGET_TRACE)
        /* 跟踪匹配的规则 */
        if (unlikely(skb->nf_trace))
            trace_packet(state->net, skb, hook, state->in,
                        state->out, table->name, private, e);
#endif
        /* 标准目标 */
        if (!t->u.kernel.target->target) {
            int v;
            
            v = ((struct xt_standard_target *)t)->verdict;
            if (v < 0) {
                /* 弹出跳转栈 */
                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))
                jumpstack[stackidx++] = e;
            
            e = get_entry(table_base, v);
            continue;
        }
        
        acct.target   = t->u.kernel.target;
        acct.targinfo = t->data;
        acct.net      = state->net;
        
        verdict = t->u.kernel.target->target(skb, &acct);
        if (verdict == XT_CONTINUE) {
            /* 目标说继续,从下一个规则开始 */
            e = ipt_next_entry(e);
        } else {
            /* 终决或跳转目标 */
            return verdict;
        }
    } while (!acct.hotdrop);
    
    xt_write_recseq_end(addend);
    return NF_ACCEPT;
}

4. 连接跟踪系统

4.1 连接跟踪核心结构

/**
 * nf_conn - 连接跟踪结构
 * 
 * 表示一个被跟踪的网络连接,包含连接的所有状态信息
 */
struct nf_conn {
    /* 使用它来区分不同的连接 */
    struct nf_conntrack ct_general;
    
    spinlock_t          lock;           /* 连接锁 */
    u16                 cpu;            /* 绑定的CPU */
    
#ifdef CONFIG_NF_CONNTRACK_ZONES
    struct nf_conntrack_zone zone;      /* 连接区域 */
#endif
    
    /* 连接元组信息:原始方向和回复方向 */
    struct nf_conntrack_tuple_hash tuplehash[IP_CT_DIR_MAX];
    
    /* 当前状态 */
    unsigned long status;               /* 连接状态位 */
    
    u32 timeout;                        /* 超时时间 */
    
    possible_net_t ct_net;              /* 网络命名空间 */
    
#if IS_ENABLED(CONFIG_NF_NAT)
    struct hlist_node nat_bysource;     /* NAT源链表 */
#endif
    
    /* 存储私有数据(helper、nat等) */
    union nf_conntrack_proto proto;     /* 协议特定数据 */
    
#if IS_ENABLED(CONFIG_NF_CONNTRACK_HELPER)
    struct nf_conn_help *help;          /* 连接助手 */
#endif
    
    struct nf_conntrack_tstamp *tstamp; /* 时间戳 */
    
#ifdef CONFIG_NF_CONNTRACK_COUNTER
    struct nf_conn_counter *counters;   /* 计数器 */
#endif
    
#ifdef CONFIG_NF_CONNTRACK_EVENTS
    struct nf_conntrack_ecache *ecache; /* 事件缓存 */
#endif
    
#ifdef CONFIG_NF_CONNTRACK_LABELS
    struct nf_conn_labels *labels;      /* 连接标签 */
#endif
    
    /* 协议特定数据在这里 */
    union nf_conntrack_proto proto;
};

/**
 * nf_conntrack_tuple - 连接元组
 * 
 * 唯一标识一个网络连接的关键信息
 */
struct nf_conntrack_tuple {
    struct nf_conntrack_man src;        /* 源地址和端口 */
    
    /* 目标端的这些部分不会改变。 */
    struct {
        union nf_inet_addr u3;          /* 目标地址 */
        union {
            /* 在这里添加其他协议。 */
            __be16 all;                 /* 通用端口 */
            
            struct {
                __be16 port;            /* TCP/UDP端口 */
            } tcp;
            struct {
                __be16 port;            /* UDP端口 */
            } udp;
            struct {
                u_int8_t type, code;    /* ICMP类型和代码 */
            } icmp;
            struct {
                __be16 port;            /* SCTP端口 */
            } sctp;
            struct {
                __be16 port;            /* DCCP端口 */
            } dccp;
            struct {
                __be16 port;            /* GRE key的上16位 */
                __be16 key;             /* GRE key的下16位 */
            } gre;
        } u;
        
        /* 协议部分。 */
        u_int8_t protonum;              /* 协议号 */
        
        /* 方向(对于NAT/查找的目的) */
        u_int8_t dir;                   /* 连接方向 */
    } dst;
};

/**
 * nf_conntrack_get - 查找连接跟踪条目
 * @net: 网络命名空间
 * @zone: 连接区域
 * @tuple: 连接元组
 * @hash: 哈希值
 * 
 * 根据连接元组查找现有的连接跟踪条目
 * 返回值:连接跟踪条目或NULL
 */
struct nf_conntrack_tuple_hash *
nf_conntrack_find_get(struct net *net,
                     const struct nf_conntrack_zone *zone,
                     const struct nf_conntrack_tuple *tuple)
{
    unsigned int rid, hash, hsize;
    struct nf_conntrack_tuple_hash *h;
    
    rid = nf_ct_zone_id(zone, IP_CT_DIR_ORIGINAL);
    hash = hash_conntrack_raw(tuple, net);
    
    hsize = nf_conntrack_htable_size;
    smp_rmb(); /* 与哈希表大小配对 */
    
    h = __nf_conntrack_find_get(net, zone, tuple, hash, hsize);
    if (h) {
        /* 增加引用计数 */
        if (unlikely(!refcount_inc_not_zero(&h->ct->ct_general.use)))
            h = NULL;
        else {
            /* 重新验证元组 */
            if (nf_ct_tuple_equal(tuple, &h->tuple) &&
                nf_ct_zone_equal(nf_ct_zone(nf_ct_tuplehash_to_ctrack(h)),
                                zone, IP_CT_DIR_ORIGINAL))
                NF_CT_STAT_INC_ATOMIC(net, found);
            else {
                nf_ct_put(nf_ct_tuplehash_to_ctrack(h));
                h = NULL;
            }
        }
    }
    
    if (!h)
        NF_CT_STAT_INC_ATOMIC(net, searched);
    
    return h;
}

/**
 * nf_conntrack_in - 连接跟踪输入处理
 * @skb: 数据包
 * @state: 钩子状态
 * 
 * 对输入数据包进行连接跟踪处理
 * 返回值:处理结果
 */
unsigned int nf_conntrack_in(struct sk_buff *skb,
                             const struct nf_hook_state *state)
{
    enum ip_conntrack_info ctinfo;
    struct nf_conn *ct, *tmpl;
    u_int8_t protonum;
    int dataoff, ret;
    
    tmpl = nf_ct_get(skb, &ctinfo);
    if (tmpl || ctinfo == IP_CT_UNTRACKED) {
        /* 之前已经被跟踪过了 */
        if ((tmpl && !nf_ct_is_template(tmpl)) ||
             ctinfo == IP_CT_UNTRACKED) {
            NF_CT_STAT_INC_ATOMIC(state->net, ignore);
            return NF_ACCEPT;
        }
        skb->_nfct = 0;
    }
    
    /* rcu_read_lock()ed由nf_hook_thresh */
    dataoff = get_l4proto(skb, skb_network_offset(skb), state->pf, &protonum);
    if (dataoff <= 0) {
        pr_debug("not prepared to track yet or error occurred\n");
        NF_CT_STAT_INC_ATOMIC(state->net, error);
        NF_CT_STAT_INC_ATOMIC(state->net, invalid);
        ret = -NF_ACCEPT;
        goto out;
    }
    
    /* 查找现有连接或创建新连接 */
    ct = resolve_normal_ct(state->net, tmpl, skb, dataoff, state->pf, protonum,
                          &ctinfo);
    if (!ct) {
        /* 连接无法被跟踪 */
        nf_conntrack_put(tmpl);
        skb->_nfct = 0;
        NF_CT_STAT_INC_ATOMIC(state->net, invalid);
        if (ret == -NF_DROP)
            NF_CT_STAT_INC_ATOMIC(state->net, drop);
        ret = -ret;
        goto out;
    }
    
    /* 决定什么时候计时器应该被刷新 */
    if (test_bit(IPS_SEEN_REPLY_BIT, &ct->status)) {
        nf_ct_offload_timeout(ct);
        nf_ct_acct_update(ct, CTINFO2DIR(ctinfo), skb->len);
    }
    
    return ret;
out:
    if (ret < 0) {
        /* 无效:反转回退,但期望未跟踪。 */
        if (ret == -NF_DROP)
            return NF_DROP;
        return NF_ACCEPT;
    }
    
    return ret;
}

4.2 连接状态管理

/**
 * TCP连接状态定义
 */
enum tcp_conntrack {
    TCP_CONNTRACK_NONE,         /* 无连接 */
    TCP_CONNTRACK_SYN_SENT,     /* SYN已发送 */
    TCP_CONNTRACK_SYN_RECV,     /* SYN已接收 */
    TCP_CONNTRACK_ESTABLISHED,  /* 连接已建立 */
    TCP_CONNTRACK_FIN_WAIT,     /* FIN等待 */
    TCP_CONNTRACK_CLOSE_WAIT,   /* 关闭等待 */
    TCP_CONNTRACK_LAST_ACK,     /* 最后ACK */
    TCP_CONNTRACK_TIME_WAIT,    /* TIME_WAIT */
    TCP_CONNTRACK_CLOSE,        /* 连接关闭 */
    TCP_CONNTRACK_LISTEN,       /* 监听状态 */
    TCP_CONNTRACK_MAX,          /* 最大状态数 */
    TCP_CONNTRACK_IGNORE,       /* 忽略状态 */
    TCP_CONNTRACK_RETRANS,      /* 重传状态 */
    TCP_CONNTRACK_UNACK,        /* 未确认状态 */
    TCP_CONNTRACK_TIMEOUT_MAX   /* 超时最大值 */
};

/**
 * tcp_new - 处理新的TCP连接
 * @ct: 连接跟踪条目
 * @skb: 数据包
 * @dataoff: 数据偏移
 * @state: 钩子状态
 * 
 * 处理新建立的TCP连接跟踪
 * 返回值:处理结果
 */
static bool tcp_new(struct nf_conn *ct, const struct sk_buff *skb,
                   unsigned int dataoff, const struct nf_hook_state *state)
{
    enum tcp_conntrack new_state;
    const struct tcphdr *th;
    struct tcphdr _tcph;
    struct ip_ct_tcp *ct_tcp = nf_ct_ext_find(ct, NF_CT_EXT_TCP);
    
    th = skb_header_pointer(skb, dataoff, sizeof(_tcph), &_tcph);
    if (th == NULL) {
        return false;
    }
    
    /* 不要跟踪窗口为0的连接,它们被认为是无效的 */
    if (th->window == 0)
        return false;
    
    /* 根据TCP标志确定初始状态 */
    if (th->syn) {
        new_state = TCP_CONNTRACK_SYN_SENT;
        /* SYN标志的基本检查 */
        if (th->ack)
            return false;
    } else if (th->ack) {
        new_state = TCP_CONNTRACK_ESTABLISHED;
    } else {
        return false;
    }
    
    ct->proto.tcp.state = new_state;
    ct->proto.tcp.seen[0].flags = th->window;
    ct->proto.tcp.seen[0].flags |= IP_CT_TCP_FLAG_WINDOW_SCALE;
    
    memset(&ct->proto.tcp.seen[1], 0, sizeof(struct ip_ct_tcp_state));
    
    /* tcp_packet函数将设置剩余字段 */
    return true;
}

/**
 * tcp_packet - 处理TCP数据包的连接跟踪
 * @ct: 连接跟踪条目
 * @conntrack: 连接跟踪信息
 * @skb: 数据包
 * @dataoff: 数据偏移
 * @state: 钩子状态
 * 
 * 更新TCP连接的跟踪状态
 * 返回值:处理结果
 */
static int tcp_packet(struct nf_conn *ct,
                     struct sk_buff *skb,
                     unsigned int dataoff,
                     enum ip_conntrack_info ctinfo,
                     const struct nf_hook_state *state)
{
    struct net *net = nf_ct_net(ct);
    struct nf_tcp_net *tn = nf_tcp_pernet(net);
    struct tcphdr *th, _tcph;
    unsigned long timeout;
    unsigned int index;
    enum tcp_conntrack new_state, old_state;
    unsigned int dir = CTINFO2DIR(ctinfo);
    
    th = skb_header_pointer(skb, dataoff, sizeof(_tcph), &_tcph);
    if (th == NULL)
        return NF_ACCEPT;
    
    spin_lock_bh(&ct->lock);
    old_state = ct->proto.tcp.state;
    dir = CTINFO2DIR(ctinfo);
    index = get_conntrack_index(th);
    new_state = tcp_conntracks[dir][index][old_state];
    
    switch (new_state) {
    case TCP_CONNTRACK_SYN_SENT:
        if (old_state < TCP_CONNTRACK_TIME_WAIT)
            break;
        /* 重新开始连接(回复SYN) */
        if ((ct->proto.tcp.seen[!dir].flags & IP_CT_TCP_FLAG_CLOSE_INIT) ||
            (ct->proto.tcp.last_dir == dir &&
             ct->proto.tcp.last_seq == ntohl(th->seq))) {
            /* 尝试回复连接:删除旧状态 */
            ct->proto.tcp.state = TCP_CONNTRACK_NONE;
            ct->proto.tcp.seen[0].flags = 0;
            ct->proto.tcp.seen[1].flags = 0;
            ct->proto.tcp.last_win = 0;
            ct->proto.tcp.last_seq = 0;
            ct->proto.tcp.last_ack = 0;
            ct->proto.tcp.last_end = 0;
            ct->proto.tcp.last_index = TCP_NONE_SET;
        }
        break;
    case TCP_CONNTRACK_IGNORE:
        /* 忽略此数据包 */
        spin_unlock_bh(&ct->lock);
        return NF_ACCEPT;
    case TCP_CONNTRACK_MAX:
        /* 无效状态 */
        pr_debug("nf_ct_tcp: Invalid dir=%i index=%u ostate=%u\n",
                dir, get_conntrack_index(th), old_state);
        spin_unlock_bh(&ct->lock);
        return -NF_ACCEPT;
    case TCP_CONNTRACK_CLOSE:
        if (index == TCP_RST_SET && test_bit(IPS_SEEN_REPLY_BIT, &ct->status)) {
            /* RST关闭连接,快速处理 */
            nf_ct_kill_acct(ct, ctinfo, skb);
            return NF_ACCEPT;
        }
        break;
    default:
        break;
    }
    
    if (!tcp_in_window(ct, &ct->proto.tcp, dir, index,
                      skb, dataoff, th, state->pf)) {
        spin_unlock_bh(&ct->lock);
        return -NF_ACCEPT;
    }
    in_window:
    
    /* 从这里开始,我们有有效的数据包 */
    ct->proto.tcp.state = new_state;
    if (old_state != new_state &&
        new_state == TCP_CONNTRACK_FIN_WAIT)
        ct->proto.tcp.seen[dir].flags |= IP_CT_TCP_FLAG_CLOSE_INIT;
    
    timeout = tcp_timeouts[new_state];
    nf_ct_refresh_acct(ct, ctinfo, skb, timeout);
    
    spin_unlock_bh(&ct->lock);
    
    return NF_ACCEPT;
}

5. NAT(网络地址转换)实现

5.1 NAT核心结构

/**
 * nf_nat_range2 - NAT范围定义
 * 
 * 定义NAT转换的地址和端口范围
 */
struct nf_nat_range2 {
    unsigned int flags;                 /* NAT标志 */
    union nf_inet_addr min_addr;        /* 最小地址 */
    union nf_inet_addr max_addr;        /* 最大地址 */
    union nf_conntrack_man_proto min_proto; /* 最小协议端口 */
    union nf_conntrack_man_proto max_proto; /* 最大协议端口 */
};

/* NAT标志定义 */
#define NF_NAT_RANGE_MAP_IPS            (1 << 0)  /* 映射IP地址 */
#define NF_NAT_RANGE_PROTO_SPECIFIED    (1 << 1)  /* 指定协议范围 */
#define NF_NAT_RANGE_PROTO_RANDOM       (1 << 2)  /* 随机协议端口 */
#define NF_NAT_RANGE_PERSISTENT         (1 << 3)  /* 持久化映射 */
#define NF_NAT_RANGE_PROTO_RANDOM_FULLY (1 << 4)  /* 完全随机端口 */
#define NF_NAT_RANGE_PROTO_OFFSET       (1 << 5)  /* 协议偏移 */
#define NF_NAT_RANGE_NETMAP             (1 << 6)  /* 网络映射 */

/**
 * nf_nat_lookup_hook - NAT查找钩子函数
 * @skb: 数据包
 * @state: 钩子状态
 * 
 * 在PRE_ROUTING和LOCAL_OUT钩子点进行NAT查找
 * 返回值:处理结果
 */
unsigned int nf_nat_inet_fn(void *priv, struct sk_buff *skb,
                           const struct nf_hook_state *state)
{
    struct nf_conn *ct;
    enum ip_conntrack_info ctinfo;
    struct nf_conn_nat *nat;
    enum nf_nat_manip_type maniptype = HOOK2MANIP(state->hook);
    __be16 frag_off;
    int hdrlen;
    
    ct = nf_ct_get(skb, &ctinfo);
    /* 如果没有连接跟踪,跳过NAT */
    if (!ct || nf_ct_is_untracked(ct))
        return NF_ACCEPT;
    
    /* 不要NAT UNTRACKED连接 */
    if (ctinfo == IP_CT_UNTRACKED)
        return NF_ACCEPT;
    
    nat = nf_ct_nat_ext_add(ct);
    if (nat == NULL)
        return NF_ACCEPT;
    
    switch (ctinfo) {
    case IP_CT_RELATED:
    case IP_CT_RELATED_REPLY:
        /* 相关连接的特殊处理 */
        if (skb_network_protocol(skb) == htons(ETH_P_IP)) {
            if (!nf_nat_ipv4_csum_update(skb, &ip_hdr(skb)->check,
                                        maniptype == NF_NAT_MANIP_SRC))
                return NF_DROP;
        }
        break;
        
    default:
        /* 常规连接处理 */
        if (nf_ct_is_confirmed(ct))
            break;
        
        /* 需要进行NAT设置 */
        BUG_ON(ctinfo != IP_CT_NEW);
        
        /* 查看我们是否应该对这个连接进行NAT。 */
        ret = nf_nat_rule_find(skb, state->hook, state->in, state->out, ct);
        if (ret != NF_ACCEPT)
            return ret;
        
        ret = nf_nat_alloc_null_binding(ct, state->hook);
        if (ret != NF_ACCEPT)
            return ret;
    }
    
    return nf_nat_packet(ct, ctinfo, state->hook, skb);
}

/**
 * nf_nat_packet - 执行NAT数据包转换
 * @ct: 连接跟踪条目
 * @ctinfo: 连接跟踪信息
 * @hooknum: 钩子编号
 * @skb: 数据包
 * 
 * 对数据包执行实际的NAT转换操作
 * 返回值:转换结果
 */
unsigned int nf_nat_packet(struct nf_conn *ct,
                          enum ip_conntrack_info ctinfo,
                          unsigned int hooknum,
                          struct sk_buff *skb)
{
    const struct nf_nat_l3proto *l3proto;
    const struct nf_nat_l4proto *l4proto;
    struct nf_conntrack_tuple target;
    enum nf_nat_manip_type mtype = HOOK2MANIP(hooknum);
    
    /* 我们操作的是已确认的连接 */
    if (!nf_ct_is_confirmed(ct))
        BUG();
    
    l3proto = __nf_nat_l3proto_find(nf_ct_l3num(ct));
    if (l3proto == NULL)
        return NF_ACCEPT;
    
    l4proto = __nf_nat_l4proto_find(nf_ct_l3num(ct), nf_ct_protonum(ct));
    if (l4proto == NULL)
        return NF_ACCEPT;
    
    /* 更改元组的src部分和dst部分。 */
    if (!nf_ct_tuple_src_mask_cmp(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple,
                                 &target, &ct->nat.mask)) {
        /* 不在范围内 */
        return NF_ACCEPT;
    }
    
    return l3proto->manip_pkt(skb, 0, l4proto, &target, mtype);
}

/**
 * nf_nat_ipv4_manip_pkt - IPv4 NAT数据包操作
 * @skb: 数据包
 * @iphdroff: IP头偏移
 * @l4proto: L4协议处理器
 * @target: 目标元组
 * @maniptype: 操作类型
 * 
 * 对IPv4数据包执行NAT转换
 * 返回值:转换结果
 */
static bool nf_nat_ipv4_manip_pkt(struct sk_buff *skb,
                                 unsigned int iphdroff,
                                 const struct nf_nat_l4proto *l4proto,
                                 const struct nf_conntrack_tuple *target,
                                 enum nf_nat_manip_type maniptype)
{
    struct iphdr *iph;
    unsigned int hdroff;
    
    if (skb_ensure_writable(skb, iphdroff + sizeof(*iph)))
        return false;
    
    iph = (void *)skb->data + iphdroff;
    hdroff = iphdroff + iph->ihl * 4;
    
    if (!l4proto->manip_pkt(skb, &nf_nat_l3proto_ipv4, iphdroff, hdroff,
                           target, maniptype))
        return false;
    iph = (void *)skb->data + iphdroff;
    
    if (maniptype == NF_NAT_MANIP_SRC) {
        csum_replace4(&iph->check, iph->saddr, target->src.u3.ip);
        iph->saddr = target->src.u3.ip;
    } else {
        csum_replace4(&iph->check, iph->daddr, target->dst.u3.ip);
        iph->daddr = target->dst.u3.ip;
    }
    return true;
}

/**
 * nf_nat_tcp_manip_pkt - TCP NAT数据包操作
 * @skb: 数据包
 * @l3proto: L3协议处理器
 * @iphdroff: IP头偏移
 * @hdroff: TCP头偏移
 * @target: 目标元组
 * @maniptype: 操作类型
 * 
 * 对TCP数据包执行NAT端口转换
 * 返回值:转换结果
 */
static bool nf_nat_tcp_manip_pkt(struct sk_buff *skb,
                                const struct nf_nat_l3proto *l3proto,
                                unsigned int iphdroff, unsigned int hdroff,
                                const struct nf_conntrack_tuple *target,
                                enum nf_nat_manip_type maniptype)
{
    struct tcphdr *tcph;
    __be16 *portptr, newport, oldport;
    int hdrsize = 8; /* TCP头最小大小 */
    
    /* this could be a inner header returned in icmp packet; in such
       cases we cannot update the checksum field since it is outside of
       the 8 bytes of transport layer headers we are guaranteed */
    if (skb->len >= hdroff + sizeof(struct tcphdr))
        hdrsize = sizeof(struct tcphdr);
    
    if (skb_ensure_writable(skb, hdroff + hdrsize))
        return false;
    
    tcph = (void *)skb->data + hdroff;
    
    if (maniptype == NF_NAT_MANIP_SRC) {
        /* 获取源端口指针 */
        portptr = &tcph->source;
        newport = target->src.u.tcp.port;
    } else {
        /* 获取目标端口指针 */
        portptr = &tcph->dest;
        newport = target->dst.u.tcp.port;
    }
    
    oldport = *portptr;
    *portptr = newport;
    
    if (hdrsize < sizeof(*tcph))
        return true;
    
    l3proto->csum_update(skb, iphdroff, &tcph->check, target, maniptype);
    inet_proto_csum_replace2(&tcph->check, skb, oldport, newport, false);
    
    return true;
}

6. iptables规则匹配引擎

6.1 匹配条件处理

/**
 * ip_packet_match - IPv4数据包匹配
 * @iph: IP头部
 * @indev: 输入设备名称
 * @outdev: 输出设备名称
 * @ipinfo: IP匹配信息
 * @fragoff: 分片偏移
 * 
 * 检查IPv4数据包是否匹配指定条件
 * 返回值:匹配返回true,不匹配返回false
 */
static bool
ip_packet_match(const struct iphdr *iph,
               const char *indev,
               const char *outdev,
               const struct ipt_ip *ipinfo,
               int fragoff)
{
    unsigned long ret;
    
#define FWINV(bool, invflg) ((bool) ^ !!(ipinfo->invflags & (invflg)))
    
    if (FWINV((iph->saddr&ipinfo->smsk.s_addr) != ipinfo->src.s_addr,
             IPT_INV_SRCIP) ||
        FWINV((iph->daddr&ipinfo->dmsk.s_addr) != ipinfo->dst.s_addr,
             IPT_INV_DSTIP))
        return false;
    
    ret = ifname_compare_aligned(indev, ipinfo->iniface, ipinfo->iniface_mask);
    
    if (FWINV(ret != 0, IPT_INV_VIA_IN))
        return false;
    
    ret = ifname_compare_aligned(outdev, ipinfo->outiface, ipinfo->outiface_mask);
    
    if (FWINV(ret != 0, IPT_INV_VIA_OUT))
        return false;
    
    /* 检查协议 */
    if (FWINV(iph->protocol != ipinfo->proto, IPT_INV_PROTO))
        return false;
    
    /* 如果我们有分片规则但数据包未分片,则不匹配 */
    if (FWINV((ipinfo->flags&IPT_F_FRAG) && !fragoff, IPT_INV_FRAG))
        return false;
    
    return true;
}

/**
 * 常用iptables匹配模块示例 - TCP匹配
 */
struct xt_tcp {
    __u16 spts[2];                      /* 源端口范围 */
    __u16 dpts[2];                      /* 目标端口范围 */
    __u8 option;                        /* TCP选项 */
    __u8 flg_mask, flg_cmp;            /* 标志掩码和比较值 */
    __u8 invflags;                      /* 反转标志 */
};

/**
 * tcp_match - TCP匹配函数
 * @skb: 数据包
 * @par: 匹配参数
 * 
 * 检查TCP数据包是否匹配指定条件
 * 返回值:匹配返回true,不匹配返回false
 */
static bool tcp_match(const struct sk_buff *skb, struct xt_action_param *par)
{
    const struct tcphdr *th;
    struct tcphdr _tcph;
    const struct xt_tcp *tcpinfo = par->matchinfo;
    
    if (par->fragoff != 0) {
        /* 为了避免IP分片中的虚假匹配,我们只匹配非分片包。 */
        pr_debug("Dropping non-first fragment\n");
        return false;
    }
    
    th = skb_header_pointer(skb, par->thoff, sizeof(_tcph), &_tcph);
    if (th == NULL) {
        /* 我们已经被调用,说明我们有一个有效的头部
         * 但是在sk_buff内没有足够的连续数据来读取
         * TCP头部的字段。这对于已经验证了
         * skb->len >= ip->ihl*4 + sizeof(tcphdr)的用户来说是一个bug。
         */
        pr_debug("tcp_match: Dropping evil TCP offset=0 tinygram.\n");
        par->hotdrop = true;
        return false;
    }
    
    if (!port_match(tcpinfo->spts[0], tcpinfo->spts[1],
                   ntohs(th->source),
                   !!(tcpinfo->invflags & XT_TCP_INV_SRCPT)))
        return false;
    
    if (!port_match(tcpinfo->dpts[0], tcpinfo->dpts[1],
                   ntohs(th->dest),
                   !!(tcpinfo->invflags & XT_TCP_INV_DSTPT)))
        return false;
    
    if (!FWINVTCP(tcp_find_option(tcpinfo->option, skb, par->thoff,
                                 th, &par->hotdrop),
                 XT_TCP_INV_OPTION))
        return false;
    
    if (tcpinfo->flg_mask) {
        u_int8_t _flg = th->res1 << 1 | th->doff >> 3;
        _flg <<= 4;
        _flg |= th->fin | th->syn << 1 | th->rst << 2 | th->psh << 3 |
               th->ack << 4 | th->urg << 5;
        
        if (!FWINVTCP((_flg & tcpinfo->flg_mask) == tcpinfo->flg_cmp,
                     XT_TCP_INV_FLAGS))
            return false;
    }
    
    return true;
}

6.2 目标动作处理

/**
 * 常用iptables目标模块 - ACCEPT目标
 */
static unsigned int accept_tg(struct sk_buff *skb, const struct xt_action_param *par)
{
    return XT_CONTINUE;
}

/**
 * DROP目标处理
 */
static unsigned int drop_tg(struct sk_buff *skb, const struct xt_action_param *par)
{
    return NF_DROP;
}

/**
 * REJECT目标实现
 */
static unsigned int reject_tg(struct sk_buff *skb, const struct xt_action_param *par)
{
    const struct ipt_reject_info *reject = par->targinfo;
    int hook_mask;
    
    hook_mask = par->state->hook_mask;
    
    /* WARNING: 这个钩子掩码允许用户指定不安全的配置。
     * 这是为了兼容性;一些需要清理的用户空间代码。
     */
    if (hook_mask & (1 << NF_INET_LOCAL_IN)) {
        /* 从本地输入发送拒绝 */
        nf_send_unreach(skb, ICMP_PORT_UNREACH, hook_mask);
    } else if (hook_mask & (1 << NF_INET_FORWARD)) {
        /* 从转发发送拒绝 */
        if (reject->with == IPT_ICMP_HOST_UNREACHABLE)
            nf_send_unreach(skb, ICMP_HOST_UNREACH, hook_mask);
        else if (reject->with == IPT_ICMP_PORT_UNREACHABLE)
            nf_send_unreach(skb, ICMP_PORT_UNREACH, hook_mask);
        else if (reject->with == IPT_ICMP_ADMIN_PROHIBITED)
            nf_send_unreach(skb, ICMP_PKT_FILTERED, hook_mask);
    }
    
    return NF_DROP;
}

/**
 * MASQUERADE目标实现 - IP伪装
 */
static unsigned int masquerade_tg(struct sk_buff *skb, const struct xt_action_param *par)
{
    struct nf_nat_range2 range;
    const struct nf_nat_ipv4_multi_range_compat *mr = par->targinfo;
    
    NF_CT_ASSERT(par->state->hook == NF_INET_POST_ROUTING);
    
    /* 对于伪装,使用输出设备的地址 */
    range.flags = mr->range[0].flags | NF_NAT_RANGE_MAP_IPS;
    range.min_addr.ip = inet_select_addr(par->state->out, 0, RT_SCOPE_UNIVERSE);
    range.max_addr.ip = range.min_addr.ip;
    range.min_proto = mr->range[0].min;
    range.max_proto = mr->range[0].max;
    
    return nf_nat_setup_info(ct, &range, NF_NAT_MANIP_SRC);
}

/**
 * SNAT目标实现 - 源地址转换
 */
static unsigned int snat_tg(struct sk_buff *skb, const struct xt_action_param *par)
{
    struct nf_conn *ct;
    enum ip_conntrack_info ctinfo;
    const struct nf_nat_ipv4_multi_range_compat *mr = par->targinfo;
    struct nf_nat_range2 range;
    unsigned int ret;
    
    ct = nf_ct_get(skb, &ctinfo);
    
    NF_CT_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED ||
                       ctinfo == IP_CT_RELATED_REPLY));
    
    /* 将旧格式的多范围转换为新格式 */
    memset(&range, 0, sizeof(range));
    range.flags = mr->range[0].flags;
    range.min_addr.ip = mr->range[0].min_ip;
    range.max_addr.ip = mr->range[0].max_ip;
    range.min_proto = mr->range[0].min;
    range.max_proto = mr->range[0].max;
    
    return nf_nat_setup_info(ct, &range, NF_NAT_MANIP_SRC);
}

/**
 * DNAT目标实现 - 目标地址转换
 */
static unsigned int dnat_tg(struct sk_buff *skb, const struct xt_action_param *par)
{
    struct nf_conn *ct;
    enum ip_conntrack_info ctinfo;
    const struct nf_nat_ipv4_multi_range_compat *mr = par->targinfo;
    struct nf_nat_range2 range;
    
    ct = nf_ct_get(skb, &ctinfo);
    
    NF_CT_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED));
    
    /* 设置DNAT范围 */
    memset(&range, 0, sizeof(range));
    range.flags = mr->range[0].flags;
    range.min_addr.ip = mr->range[0].min_ip;
    range.max_addr.ip = mr->range[0].max_ip;
    range.min_proto = mr->range[0].min;
    range.max_proto = mr->range[0].max;
    
    return nf_nat_setup_info(ct, &range, NF_NAT_MANIP_DST);
}

7. 连接跟踪超时管理

7.1 超时处理机制

/**
 * nf_ct_timeout - 连接跟踪超时结构
 */
struct nf_ct_timeout {
    __u16                       l3num;      /* L3协议号 */
    const struct nf_ct_l4proto  *l4proto;  /* L4协议处理器 */
    char                        data[0];    /* 协议特定数据 */
};

/**
 * nf_conntrack_tuple_taken - 检查元组是否被占用
 * @net: 网络命名空间
 * @tuple: 连接元组
 * @ignored_conntrack: 忽略的连接
 * 
 * 检查指定的连接元组是否已被其他连接使用
 * 返回值:被占用返回true,可用返回false
 */
bool nf_conntrack_tuple_taken(struct net *net,
                             const struct nf_conntrack_tuple *tuple,
                             const struct nf_conn *ignored_conntrack)
{
    const struct nf_conntrack_zone *zone;
    struct nf_conntrack_tuple_hash *h;
    struct hlist_nulls_head *head;
    unsigned int hash;
    
    zone = nf_ct_zone(ignored_conntrack);
    
    rcu_read_lock();
    begin:
    hash = nf_conntrack_tuple_hash(tuple);
    head = &nf_conntrack_hash[hash];
    
    hlist_nulls_for_each_entry_rcu(h, n, head, hnnode) {
        struct nf_conn *ct = nf_ct_tuplehash_to_ctrack(h);
        
        if (ct != ignored_conntrack &&
            nf_ct_tuple_equal(tuple, &h->tuple) &&
            nf_ct_zone_equal(nf_ct_zone(ct), zone, IP_CT_DIR_ORIGINAL)) {
            NF_CT_STAT_INC_ATOMIC(net, found);
            rcu_read_unlock();
            return true;
        }
    }
    
    if (get_nulls_value(n) != hash &&
        !nulls_list_unhashed(&ignored_conntrack->tuplehash[IP_CT_DIR_ORIGINAL].hnnode))
        goto begin;
    
    rcu_read_unlock();
    
    return false;
}

/**
 * death_by_timeout - 超时死亡处理
 * @work: 延迟工作结构
 * 
 * 处理超时的连接跟踪条目
 */
static void death_by_timeout(struct work_struct *work)
{
    struct nf_conn_timeout *timeout_ext;
    struct nf_conn *ct = container_of(work, struct nf_conn, rcu.work);
    
    timeout_ext = nf_ct_timeout_find(ct);
    
    if (timeout_ext && timeout_ext->timeout)
        nf_ct_kill_timeout(ct, timeout_ext->timeout);
    else
        nf_ct_kill(ct);
}

/**
 * nf_ct_gc_expired - 垃圾回收过期连接
 * @ct: 连接跟踪条目
 * 
 * 检查连接是否过期,如果过期则进行垃圾回收
 * 返回值:过期返回true,未过期返回false
 */
static bool nf_ct_gc_expired(struct nf_conn *ct)
{
    unsigned long timeout = nf_ct_expires(ct);
    
    if (time_before(jiffies, timeout))
        return false;
    
    if (nf_conntrack_max &&
        atomic_read(&nf_conntrack_count) > nf_conntrack_max) {
        /* 强制过期一些连接 */
        if (nf_ct_is_dying(ct))
            return true;
        if (!del_timer(&ct->timeout))
            return true;
    }
    
    return nf_ct_is_expired(ct);
}

/**
 * nf_conntrack_gc_work - 连接跟踪垃圾回收工作
 * @work: 延迟工作结构
 * 
 * 定期执行连接跟踪表的垃圾回收
 */
static void nf_conntrack_gc_work(struct work_struct *work)
{
    unsigned int min_interval = max(HZ / GC_MAX_BUCKETS_DIV, 1u);
    unsigned int i, goal, buckets = 0, expired_count = 0;
    struct conntrack_gc_work *gc_work;
    unsigned int ratio, scanned = 0;
    unsigned long next_run;
    
    gc_work = container_of(work, struct conntrack_gc_work, dwork.work);
    
    goal = nf_conntrack_htable_size / GC_MAX_BUCKETS_DIV;
    i = gc_work->last_bucket;
    if (gc_work->early_drop)
        goal /= 2;
    
    do {
        struct nf_conntrack_tuple_hash *h;
        struct hlist_nulls_head *head;
        struct hlist_nulls_node *n;
        unsigned int hashsz;
        struct nf_conn *tmp;
        
        i++;
        hashsz = nf_conntrack_htable_size;
        if (i >= hashsz)
            i = 0;
        
        head = &nf_conntrack_hash[i];
        
        rcu_read_lock();
        hlist_nulls_for_each_entry_rcu(h, n, head, hnnode) {
            struct nf_conn *ct;
            
            tmp = nf_ct_tuplehash_to_ctrack(h);
            
            scanned++;
            if (test_bit(IPS_OFFLOAD_BIT, &tmp->status)) {
                nf_ct_offload_timeout(tmp);
                continue;
            }
            
            if (nf_ct_gc_expired(tmp)) {
                expired_count++;
                nf_ct_kill(tmp);
            }
        }
        
        /* 如果nulls值发生变化,重新扫描这个bucket */
        if (get_nulls_value(n) != i &&
            !hlist_nulls_empty(head))
            goto rescan;
        rcu_read_unlock();
        
        cond_resched();
    } while (++buckets < goal);
    
    if (gc_work->exiting)
        return;
    
    /*
     * 计算下一次垃圾回收的时间。
     * 如果过期连接较多,增加回收频率。
     */
    ratio = scanned ? expired_count * 100 / scanned : 0;
    if (ratio > 90) {
        /* 过期连接很多,快速回收 */
        next_run = min_interval;
    } else if (ratio > 10) {
        /* 适中数量的过期连接 */
        next_run = min_interval * 2;
    } else {
        /* 很少过期连接,慢速回收 */
        next_run = min_interval * 4;
    }
    
    gc_work->last_bucket = i;
    gc_work->early_drop = false;
    queue_delayed_work(system_power_efficient_wq, &gc_work->dwork, next_run);
}

8. Netfilter性能优化

8.1 规则优化策略

/**
 * 规则优化和JIT编译
 */
struct nf_hook_entries_rcu_head {
    struct rcu_head head;
    void    *allocation;
};

/**
 * nf_hook_entries_grow - 扩展钩子条目数组
 * @old: 旧的钩子条目
 * @reg: 新注册的钩子
 * 
 * 当注册新钩子时扩展条目数组
 * 返回值:新的钩子条目数组或错误指针
 */
static struct nf_hook_entries *nf_hook_entries_grow(const struct nf_hook_entries *old,
                                                   const struct nf_hook_ops *reg)
{
    unsigned int i, alloc_entries, nhooks, old_entries;
    struct nf_hook_ops **orig_ops = NULL;
    struct nf_hook_ops **new_ops;
    struct nf_hook_entries *new;
    bool inserted = false;
    
    alloc_entries = 1;
    old_entries = old ? old->num_hook_entries : 0;
    
    if (old) {
        orig_ops = nf_hook_entries_get_hook_ops(old);
        
        for (i = 0; i < old_entries; i++) {
            if (orig_ops[i] != &dummy_ops)
                alloc_entries++;
        }
    }
    
    if (alloc_entries > MAX_HOOK_COUNT)
        return ERR_PTR(-E2BIG);
    
    new = allocate_hook_entries_size(alloc_entries);
    if (!new)
        return ERR_PTR(-ENOMEM);
    
    new_ops = nf_hook_entries_get_hook_ops(new);
    
    i = 0;
    nhooks = 0;
    while (i < old_entries) {
        if (orig_ops[i] == &dummy_ops) {
            ++i;
            continue;
        }
        
        if (inserted || reg->priority > orig_ops[i]->priority) {
            new_ops[nhooks] = (void *)orig_ops[i];
            new->hooks[nhooks] = old->hooks[i];
            i++;
        } else {
            new_ops[nhooks] = (void *)reg;
            new->hooks[nhooks].hook = reg->hook;
            new->hooks[nhooks].priv = reg->priv;
            inserted = true;
        }
        nhooks++;
    }
    
    if (!inserted) {
        new_ops[nhooks] = (void *)reg;
        new->hooks[nhooks].hook = reg->hook;
        new->hooks[nhooks].priv = reg->priv;
        nhooks++;
    }
    
    return new;
}

8.2 连接跟踪哈希表优化

/**
 * nf_conntrack_hash_resize - 调整连接跟踪哈希表大小
 * @hashsize: 新的哈希表大小
 * 
 * 根据当前连接数动态调整哈希表大小
 * 返回值:成功返回0,失败返回负错误码
 */
int nf_conntrack_hash_resize(unsigned int hashsize)
{
    int i, bucket;
    unsigned int old_size;
    struct hlist_nulls_head *hash, *old_hash;
    struct nf_conntrack_tuple_hash *h;
    struct nf_conn *ct;
    
    if (!hashsize)
        return -EINVAL;
    
    /* 分配新的哈希表 */
    hash = nf_ct_alloc_hashtable(&hashsize, 1);
    if (!hash)
        return -ENOMEM;
    
    old_size = nf_conntrack_htable_size;
    if (old_size == hashsize) {
        nf_ct_free_hashtable(hash, hashsize);
        return 0;
    }
    
    local_bh_disable();
    nf_conntrack_all_lock();
    write_seqcount_begin(&nf_conntrack_generation);
    
    /* 备份旧的哈希表 */
    old_hash = nf_conntrack_hash;
    nf_conntrack_hash = hash;
    nf_conntrack_htable_size = hashsize;
    write_seqcount_end(&nf_conntrack_generation);
    
    nf_conntrack_all_unlock();
    local_bh_enable();
    
    /* 重新哈希现有连接 */
    for (i = 0; i < old_size; i++) {
        while (!hlist_nulls_empty(&old_hash[i])) {
            h = hlist_nulls_entry(old_hash[i].first,
                                 struct nf_conntrack_tuple_hash, hnnode);
            ct = nf_ct_tuplehash_to_ctrack(h);
            hlist_nulls_del_rcu(&h->hnnode);
            
            bucket = __hash_conntrack(nf_ct_net(ct),
                                    &h->tuple, hashsize);
            hlist_nulls_add_head_rcu(&h->hnnode, &hash[bucket]);
        }
    }
    
    synchronize_net();
    nf_ct_free_hashtable(old_hash, old_size);
    return 0;
}

9. Netfilter调试和监控

9.1 连接跟踪统计

#!/bin/bash
# Netfilter性能监控脚本

monitor_netfilter_performance() {
    echo "=== Netfilter性能统计 ==="
    
    # 连接跟踪统计
    echo "连接跟踪统计:"
    cat /proc/net/stat/nf_conntrack
    
    # 当前连接数
    echo -e "\n当前连接数:"
    cat /proc/sys/net/netfilter/nf_conntrack_count
    echo "最大连接数:"
    cat /proc/sys/net/netfilter/nf_conntrack_max
    
    # iptables计数器
    echo -e "\niptables规则计数器:"
    iptables -L -n -v | head -20
    
    # NAT表统计
    echo -e "\nNAT表统计:"
    iptables -t nat -L -n -v | head -20
    
    # 连接跟踪表项
    echo -e "\n连接跟踪表项数量:"
    wc -l /proc/net/nf_conntrack
    
    # 哈希表信息
    echo -e "\n哈希表信息:"
    cat /proc/net/stat/nf_conntrack | awk '{print "哈希表大小:", $2, "使用:", $3}'
}

# 优化Netfilter性能
optimize_netfilter() {
    echo "优化Netfilter配置..."
    
    # 增加连接跟踪表大小
    echo 1048576 > /proc/sys/net/netfilter/nf_conntrack_max
    
    # 调整连接跟踪超时
    echo 120 > /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_established
    echo 60 > /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_time_wait
    echo 30 > /proc/sys/net/netfilter/nf_conntrack_udp_timeout
    
    # 启用连接跟踪帮助器的自动加载
    echo 1 > /proc/sys/net/netfilter/nf_conntrack_helper
    
    # 优化哈希表大小
    modprobe nf_conntrack hashsize=32768
    
    echo "Netfilter优化完成"
}

# 执行优化
optimize_netfilter

# 开始监控
while true; do
    clear
    monitor_netfilter_performance
    sleep 10
done

10. 总结

Linux Netfilter框架是一个功能强大且高度优化的包处理系统,为Linux提供了完整的防火墙、NAT和包过滤功能:

10.1 关键技术特点

  • 灵活的钩子机制:在网络栈关键位置提供处理钩子
  • 高效的规则匹配:优化的规则匹配引擎和JIT编译
  • 完整的连接跟踪:状态化的连接管理和超时处理
  • 强大的NAT功能:支持各种NAT场景和负载均衡
  • 良好的扩展性:支持自定义匹配条件和目标动作

10.2 性能优化要点

  • 合理配置连接跟踪表:根据系统负载调整表大小
  • 优化规则顺序:将常用规则放在前面
  • 使用连接状态:利用连接跟踪减少规则匹配
  • 监控性能指标:定期检查连接数和命中率
  • 调整超时参数:根据应用特点设置合适的超时

Netfilter为Linux网络安全和NAT功能提供了坚实的基础,理解其实现原理对于网络安全管理和性能调优都具有重要意义。