概述

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 钩子点定义和注册

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
/**
 * 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 钩子处理函数实现

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
/**
 * 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表结构

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
/**
 * 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 连接跟踪核心结构

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
/**
 * 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 连接状态管理

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
/**
 * 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核心结构

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
/**
 * 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 匹配条件处理

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
/**
 * 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 目标动作处理

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
/**
 * 常用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 超时处理机制

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
/**
 * 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 规则优化策略

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
/**
 * 规则优化和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 连接跟踪哈希表优化

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
/**
 * 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 连接跟踪统计

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#!/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功能提供了坚实的基础,理解其实现原理对于网络安全管理和性能调优都具有重要意义。


创建时间: 2023年11月25日

本文为Linux网络栈源码分析系列的Netfilter框架深度解析

参考资料

  • Linux内核源码 (net/netfilter/, net/ipv4/netfilter/)
  • Netfilter官方文档
  • iptables-extensions手册
  • 《Linux防火墙原理与实现》