前言
Linux kernel 版本:5.4.1
Netfilter特指内核中的netfilter框架
iptables指用户空间的配置工具
概念
Linux 上最常用的防火墙工具是 iptables。iptables 与协议栈内有包过滤功能的 hook 交 互来完成工作。这些内核 hook 构成了 netfilter 框架。
每个进入网络系统的包(接收或发送)在经过协议栈时都会触发这些 hook,程序 可以通过注册 hook 函数的方式在一些关键路径上处理网络流量。iptables 相关的内核模 块在这些 hook 点注册了处理函数,因此可以通过配置 iptables 规则来使得网络流量符合 防火墙规则。
Iptable/netfilter的表和链(Table and Chains)
Iptable/netfilter使用table来组织规则,根据**用来做什么**的判断标准,将规则分为不同table。
| Table名 | 作用描述 |
|---|---|
| Filter | 用于判断是否允许一个包通过,最常用的table。 |
| NAT | 用于实现网络地址转换规则,一般用于将数据包路由到无法直接访问的网络。 |
| Mangle | 用于修改包的IP头。例如,可以修改包的TTL。 |
| Raw | 控制数据包不被connecting tracking所追踪。 |
| Security | 用于给包打上SELinux标记,以此影响SELinux或其他可以解读SELinux安全上线问的系统处理包的行为。 |
而在每个table内部,规则被进一步组织成chain,内置的chain是由内置的hook触发的。chain基本上能决定规则**何时**被触发。
| Chain名称 | HOOK宏 |
|---|---|
| PREROUTING | NF_INET_PRE_ROUTING |
| INPUT | NF_INET_LOCAL_IN |
| FORWARD | NF_INET_FORWARD |
| OUTPUT | NF_INET_LOCAL_OUT |
| POSTROUTING | NF_INET_POST_ROUTING |
每个table里面的chain不是相同的,即数据包在不同的处理流程的位置,Iptable/netfilter的处理方法不一样,具体见每个table的宏定义。
- Filter
/* File: linux-5.4.1\net\ipv4\netfilter\iptable_filter.c */
#define FILTER_VALID_HOOKS ((1 << NF_INET_LOCAL_IN) | \
(1 << NF_INET_FORWARD) | \
(1 << NF_INET_LOCAL_OUT))
- NAT
/* File: linux-5.4.1\net\ipv4\netfilter\iptable_nat.c */
static const struct xt_table nf_nat_ipv4_table = {
.name = "nat",
.valid_hooks = (1 << NF_INET_PRE_ROUTING) |
(1 << NF_INET_POST_ROUTING) |
(1 << NF_INET_LOCAL_OUT) |
(1 << NF_INET_LOCAL_IN),
.me = THIS_MODULE,
.af = NFPROTO_IPV4,
.table_init = iptable_nat_table_init,
};
- Mangle
/* File: linux-5.4.1\net\ipv4\netfilter\iptable_mangle.c */
#define MANGLE_VALID_HOOKS ((1 << NF_INET_PRE_ROUTING) | \
(1 << NF_INET_LOCAL_IN) | \
(1 << NF_INET_FORWARD) | \
(1 << NF_INET_LOCAL_OUT) | \
(1 << NF_INET_POST_ROUTING))
- Raw
/* File: X:\linux-5.4.1\net\ipv4\netfilter\iptable_raw.c */
#define RAW_VALID_HOOKS ((1 << NF_INET_PRE_ROUTING) | (1 << NF_INET_LOCAL_OUT))
- Security
/* File: X:\linux-5.4.1\net\ipv4\netfilter\iptable_security.c */
#define SECURITY_VALID_HOOKS (1 << NF_INET_LOCAL_IN) | \
(1 << NF_INET_FORWARD) | \
(1 << NF_INET_LOCAL_OUT)
| Chains/Table | Filter | NAT | Mangle | Raw | Security |
|---|---|---|---|---|---|
| PREROUTING | Y | Y | Y | ||
| INPUT | Y | Y | Y | Y | |
| FORWARD | Y | Y | Y | ||
| OUTPUT | Y | Y | Y | Y | Y |
| POSTROUTING | Y | Y |
当一个包触发 netfilter hook 时,处理过程存在先后顺序和处理条件。 触发哪个 hook (列)和包的方向(ingress/egress)、路由判断、过滤条件等相关。
特定事件会导致 table 的 chain 被跳过。例如,只有每个连接的第一个包会去匹配 NAT 规则,对这个包的动作会应用于此连接后面的所有包。到这个连接的应答包会被自动应用反 方向的 NAT 规则。
具体的包触发流程如下:

这里的ct表示的是conntrack
netfilter钩子(hooks)
NF_HOOK()函数
NF_HOOK是协议和Netfilter框架的切入点, 完成netfilter模块的处理后,调用int (*okfn)(struct net *, struct sock *, struct sk_buff *)进行后续处理流程。
而在netfilter模块的处理中,主要的就是对iptables所配置的规则进行匹配,如果这个chain上所有的规则匹配均通过,则可以执行后续的函数。
/* File:linux-5.4.1\include\linux\netfilter.h */
static inline int
NF_HOOK(uint8_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);
/* 根据nf_hook()的返回值,决定要不要执行后续函数 */
if (ret == 1)
ret = okfn(net, sk, skb);
return ret;
}
nf_hook()函数
/* File:linux-5.4.1\include\linux\netfilter.h */
/**
* nf_hook - call a netfilter hook
*
* Returns 1 if the hook has allowed the packet to pass. The function
* okfn must be invoked by the caller in this case. Any other return
* value indicates the packet has been consumed by the hook.
*
*
* 调用hook函数,如果该函数允许数据包通过,返回1
* 在这种情况下,调用方必须调用函数okfn。任何其他的返回值都表示数据包已被hook使用。
*/
static inline int nf_hook(u_int8_t pf, unsigned int hook, struct net *net,
struct sock *sk, struct sk_buff *skb,
struct net_device *indev, struct net_device *outdev,
int (*okfn)(struct net *, struct sock *, struct sk_buff *))
{
struct nf_hook_entries *hook_head = NULL;
int ret = 1;
#ifdef CONFIG_JUMP_LABEL
if (__builtin_constant_p(pf) &&
__builtin_constant_p(hook) &&
!static_key_false(&nf_hooks_needed[pf][hook]))
return 1;
#endif
rcu_read_lock();
/* 判断网络层协议 */
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:
#ifdef CONFIG_NETFILTER_FAMILY_ARP
if (WARN_ON_ONCE(hook >= ARRAY_SIZE(net->nf.hooks_arp)))
break;
hook_head = rcu_dereference(net->nf.hooks_arp[hook]);
#endif
break;
case NFPROTO_BRIDGE:
#ifdef CONFIG_NETFILTER_FAMILY_BRIDGE
hook_head = rcu_dereference(net->nf.hooks_bridge[hook]);
#endif
break;
#if IS_ENABLED(CONFIG_DECNET)
case NFPROTO_DECNET:
hook_head = rcu_dereference(net->nf.hooks_decnet[hook]);
break;
#endif
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);
// 返回1 ,代表NF_ACCEPT,okfn()需要被别的caller执行
// 返回-EPERM,代表NF_DROP,也就是NF_HOOK中,不再执行okfn()函数
}
rcu_read_unlock();
return ret;
}
nf_hook_slow()
具体hook函数中的规则匹配流程,见《规则匹配流程》小节
/* File: linux-5.4.1\net\netfilter\core.c */
/* Returns 1 if okfn() needs to be executed by the caller,
* -EPERM for NF_DROP, 0 otherwise. Caller must hold rcu_read_lock.
*
* nf_hook_slow()用于完成成钩子函数遍历执行,也就是规则匹配,这里的执行顺序是按照table优先级执行
*
*/
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++) {
/* 执行对应的hook函数
* 这里的hook函数,是在iptables中的5个table在init的时候挂载的几个
* 1、Filter : iptable_filter_hook();
* 2、Mangle : iptable_mangle_hook();
* 3、NAT : iptable_nat_do_hook();
* 4、Raw : iptable_raw_hook();
* 5、Security : iptable_security_hook();
*/
verdict = nf_hook_entry_hookfn(&e->hooks[s], skb, state);
switch (verdict & NF_VERDICT_MASK) {
case NF_ACCEPT:
break;
case NF_DROP:
kfree_skb(skb);
ret = NF_DROP_GETERR(verdict);
if (ret == 0)
ret = -EPERM;
return ret;
case NF_QUEUE:
ret = nf_queue(skb, state, s, verdict);
if (ret == 1)
continue;
return ret;
default:
/* Implicit handling for NF_STOLEN, as well as any other
* non conventional verdicts.
*/
return 0;
}
}
return 1;
}
Iptables规则匹配
相关结构体
ipt_entry结构体
/* File: linux-5.4.1\include\uapi\linux\netfilter_ipv4\ip_tables.h */
/* This structure defines each of the firewall rules. Consists of 3
parts which are
1) general IP header stuff
2) match specific stuff
3) the target to perform if the rule matches
标准匹配结构,主要包括:
数据包的源、目的IP
出/入接口
掩码等
*/
struct ipt_entry {
/* 要匹配的报文IP头 */
struct ipt_ip ip;
/* Mark with fields that we care about.
* 位向量,标识本规则关心报文的什么部分
*/
unsigned int nfcache;
/* Size of ipt_entry + matches
target区的偏移,通常target区位于match区之后,而match区则在ipt_entry的末尾;
初始化为sizeof(struct ipt_entry),即假定没有match
*/
__u16 target_offset;
/* Size of ipt_entry + matches + target
下一条规则相对于本规则的偏移,也即本规则所用空间的总和,
初始化为sizeof(struct ipt_entry)+sizeof(struct ipt_target),即没有match
*/
__u16 next_offset;
/* Back pointer
位向量,标记调用本规则的HOOK号,可用于检查规则的有效性 */
unsigned int comefrom;
/* Packet and byte counters.
记录该规则处理过的报文数和报文总字节数 */
struct xt_counters counters;
/* The matches (if any), then the target.
target或者是match的起始位置 */
unsigned char elems[0];
};
ipt_replace结构体
iptables的规则是由iptables的命令创建并添加到对应的链上,用户配置完一条iptables规则之后,传给内核的是一个ipt_replace结构,其中包含了内核所需的所有的内容。
/* File: linux-5.4.1\include\uapi\linux\netfilter_ipv4\ip_tables.h */
/* The argument to IPT_SO_SET_REPLACE. */
/* 用户配置完iptables规则之后,传给内核的时一个ipt-table结构,其中包含了内核所需要的所有内容 */
struct ipt_replace {
/* Which table. 表名 */
char name[XT_TABLE_MAXNAMELEN];
/* Which hook entry points are valid: bitmask. You can't
change this. */
unsigned int valid_hooks;
/* Number of entries 新的entry数 */
unsigned int num_entries;
/* Total size of new entries */
unsigned int size;
/* Hook entry points. */
unsigned int hook_entry[NF_INET_NUMHOOKS];
/* Underflow points. */
unsigned int underflow[NF_INET_NUMHOOKS];
/* Information about old entries: 旧的规则数*/
/* Number of counters (must be equal to current number of entries). */
unsigned int num_counters;
/* The old entries' counters. */
struct xt_counters __user *counters;
/* The entries (hang off end: not really an array). */
/* 规则本身 */
struct ipt_entry entries[0];
};
ipt_match结构体
ipt_match就是xt_match
#define ipt_match xt_match
struct xt_match {
struct list_head list;
const char name[XT_EXTENSION_MAXNAMELEN];
u_int8_t revision;
/* Return true or false: return FALSE and set *hotdrop = 1 to
force immediate packet drop. */
/* Arguments changed since 2.6.9, as this must now handle
non-linear skb, using skb_header_pointer and
skb_ip_make_writable. */
/* 匹配函数,最重要的部分,返回true表示匹配成功,返回false表示匹配失败 */
bool (*match)(const struct sk_buff *skb,
struct xt_action_param *);
/* Called when user tries to insert an entry of this type.
在使用本Match的规则注入表中之前调用,进行有效性检查,如果返回0,规则就不会加入iptables中. */
int (*checkentry)(const struct xt_mtchk_param *);
/* Called when entry of this type deleted.
删除包含本match的entry时调用,与checkentry配合可用于动态内存分配和释放 */
void (*destroy)(const struct xt_mtdtor_param *);
#ifdef CONFIG_COMPAT
/* Called when userspace align differs from kernel space one */
void (*compat_from_user)(void *dst, const void *src);
int (*compat_to_user)(void __user *dst, const void *src);
#endif
/* Set this to THIS_MODULE if you are a module, otherwise NULL
是否为模块 */
struct module *me;
const char *table;
unsigned int matchsize;
unsigned int usersize;
#ifdef CONFIG_COMPAT
unsigned int compatsize;
#endif
unsigned int hooks;
unsigned short proto;
unsigned short family;
};
ipt_target结构体
ipt_target就是xt_target
#define ipt_target
/* Registration hooks for targets. */
struct xt_target {
struct list_head list;
const char name[XT_EXTENSION_MAXNAMELEN];
u_int8_t revision;
/* Returns verdict. Argument order changed since 2.6.9, as this
must now handle non-linear skbs, using skb_copy_bits and
skb_ip_make_writable.
target的模块函数,如果需要继续处理则返回IPT_CONTINUE(-1),否则返回NF_ACCEPT、NF_DROP等值,
它的调用者根据它的返回值来判断如何处理它处理过的报文
*/
unsigned int (*target)(struct sk_buff *skb,
const struct xt_action_param *);
/* Called when user tries to insert an entry of this type:
hook_mask is a bitmask of hooks from which it can be
called. */
/* Should return 0 on success or an error code otherwise (-Exxxx).
在使用本Match的规则注入表中之前调用,进行有效性检查,如果返回0,规则就不会加入iptables中 */
int (*checkentry)(const struct xt_tgchk_param *);
/* Called when entry of this type deleted.
在包含本Target的规则从表中删除时调用,与checkentry配合可用于动态内存分配和释放 */
void (*destroy)(const struct xt_tgdtor_param *);
#ifdef CONFIG_COMPAT
/* Called when userspace align differs from kernel space one */
void (*compat_from_user)(void *dst, const void *src);
int (*compat_to_user)(void __user *dst, const void *src);
#endif
/* Set this to THIS_MODULE if you are a module, otherwise NULL
表示当前Target是否为模块(NULL为否) */
struct module *me;
const char *table;
unsigned int targetsize;
unsigned int usersize;
#ifdef CONFIG_COMPAT
unsigned int compatsize;
#endif
unsigned int hooks;
unsigned short proto;
unsigned short family;
};
对应的命令结构
每一条iptables配置的rule都包含了匹配条件(match)部分和动作(target)。当报文途径HOOK点时,Netfilter会逐个遍历挂在该钩子点上的表的rule,若报文满足rule的匹配条件,内核就会执行动作(target)。

添加一条规则的流程
在ip_tables.c中,ip_tables_init函数中调用hf_register_sockopts(&ipt_sockopts)注册对应的get和set方法
/* File: linux-5.4.1\net\ipv4\netfilter\ip_tables.c */
static struct nf_sockopt_ops ipt_sockopts = {
.pf = PF_INET,
.set_optmin = IPT_BASE_CTL,
.set_optmax = IPT_SO_SET_MAX+1,
.set = do_ipt_set_ctl,//set方法
#ifdef CONFIG_COMPAT
.compat_set = compat_do_ipt_set_ctl,
#endif
.get_optmin = IPT_BASE_CTL,
.get_optmax = IPT_SO_GET_MAX+1,
.get = do_ipt_get_ctl,//get方法
#ifdef CONFIG_COMPAT
.compat_get = compat_do_ipt_get_ctl,
#endif
.owner = THIS_MODULE,
};
static int __init ip_tables_init(void)
{
int ret;
ret = register_pernet_subsys(&ip_tables_net_ops);
if (ret < 0)
goto err1;
/* No one else will be downing sem now, so we won't sleep */
ret = xt_register_targets(ipt_builtin_tg, ARRAY_SIZE(ipt_builtin_tg));
if (ret < 0)
goto err2;
ret = xt_register_matches(ipt_builtin_mt, ARRAY_SIZE(ipt_builtin_mt));
if (ret < 0)
goto err4;
/* Register setsockopt */
/* 注册一个socket option,这个option用于读或写iptable的配置,
* 如:Linux的防火墙规则、NAT转换映射最终都是通过这个接口通知内核的
*/
ret = nf_register_sockopt(&ipt_sockopts);
if (ret < 0)
goto err5;
return 0;
err5:
xt_unregister_matches(ipt_builtin_mt, ARRAY_SIZE(ipt_builtin_mt));
err4:
xt_unregister_targets(ipt_builtin_tg, ARRAY_SIZE(ipt_builtin_tg));
err2:
unregister_pernet_subsys(&ip_tables_net_ops);
err1:
return ret;
}
用户改变iptables规则后,如set方法就是将用户空间传过来的ipt_replace来替换旧的iptables规则。
/* File: linux-5.4.1\net\ipv4\netfilter\ip_tables.c */
/* 将用户空间传过来的ipt_replace来替换旧的iptables规则。该工作在do_replace()函数中完成 */
static int
do_ipt_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len)
{
int ret;
if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN))
return -EPERM;
switch (cmd) {
case IPT_SO_SET_REPLACE:
ret = do_replace(sock_net(sk), user, len);
break;
case IPT_SO_SET_ADD_COUNTERS:
ret = do_add_counters(sock_net(sk), user, len, 0);
break;
default:
ret = -EINVAL;
}
return ret;
}
/* File: linux-5.4.1\net\ipv4\netfilter\ip_tables.c */
static int
do_replace(struct net *net, const void __user *user, unsigned int len)
{
int ret;
struct ipt_replace tmp;
struct xt_table_info *newinfo;
void *loc_cpu_entry;
struct ipt_entry *iter;
/* 拷贝用户空间的struct ip_replace结构 */
if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
return -EFAULT;
/* overflow check */
if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters))
return -ENOMEM;
if (tmp.num_counters == 0)
return -EINVAL;
tmp.name[sizeof(tmp.name)-1] = 0;
/* 分配一个xt_table_info结构,并根据ipt_replace.size
给xt_table_info的entry成员分配空间
*/
newinfo = xt_alloc_table_info(tmp.size);
if (!newinfo)
return -ENOMEM;
loc_cpu_entry = newinfo->entries;
/* 将ipt_replace.entries的内容拷贝到内核 */
if (copy_from_user(loc_cpu_entry, user + sizeof(tmp),
tmp.size) != 0) {
ret = -EFAULT;
goto free_newinfo;
}
/* 根据ipt_replace给xt_table_info结构各个成员赋值 */
ret = translate_table(net, newinfo, loc_cpu_entry, &tmp);
if (ret != 0)
goto free_newinfo;
/* 根据ipt_replace的name找到要更新的表,
用上面的xt_table_info为xt_table赋值
*/
ret = __do_replace(net, tmp.name, tmp.valid_hooks, newinfo,
tmp.num_counters, tmp.counters);
if (ret)
goto free_newinfo_untrans;
return 0;
free_newinfo_untrans:
xt_entry_foreach(iter, loc_cpu_entry, newinfo->size)
cleanup_entry(iter, net);
free_newinfo:
xt_free_table_info(newinfo);
return ret;
}
最终的结果是更新了内核中的某个xt_table表,如NAT表。
规则匹配流程
iptables中有5种tables,不同table的对应的rule作用不一致,每个tables都有它对应的hook函数,但最终都是走的ip_do_table()函数来匹配规则。
| Tables | Hook函数 |
|---|---|
| Filter | iptable_filter_hook() |
| NAT | iptable_nat_do_chain() |
| Mangle | iptable_mangle_hook() |
| Raw | iptable_raw_hook() |
| Security | iptable_security_hook() |
/* File: linux-5.4.1\net\ipv4\netfilter\ip_tables.c */
/* Returns one of the generic firewall policies, like NF_ACCEPT. */
/* 遍历钩子链上的所有规则,进行标准匹配和扩展匹配,执行其target操作 */
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;
/* Initializing verdict to NF_DROP keeps gcc happy. */
unsigned int verdict = NF_DROP;
const char *indev, *outdev;
const void *table_base;
struct ipt_entry *e, **jumpstack;
unsigned int stackidx, cpu;
const struct xt_table_info *private;
struct xt_action_param acpar;
unsigned int addend;
/* Initialization */
stackidx = 0;
ip = ip_hdr(skb);
indev = state->in ? state->in->name : nulldevname;
outdev = state->out ? state->out->name : nulldevname;
/* We handle fragments by dealing with the first fragment as
* if it was a normal packet. All other fragments are treated
* normally, except that they will NEVER match rules that ask
* things we don't know, ie. tcp syn flag or ports). If the
* rule is also a fragment-specific rule, non-fragments won't
* match it. */
acpar.fragoff = ntohs(ip->frag_off) & IP_OFFSET;
acpar.thoff = ip_hdrlen(skb);
acpar.hotdrop = false;
acpar.state = state;
WARN_ON(!(table->valid_hooks & (1 << hook)));
local_bh_disable();
addend = xt_write_recseq_begin();
private = READ_ONCE(table->private); /* Address dependency. */
cpu = smp_processor_id();
/* 首个规则地址 */
table_base = private->entries;
jumpstack = (struct ipt_entry **)private->jumpstack[cpu];
/* Switch to alternate jumpstack if we're being invoked via TEE.
* TEE issues XT_CONTINUE verdict on original skb so we must not
* clobber the jumpstack.
*
* For recursion via REJECT or SYNPROXY the stack will be clobbered
* but it is no problem since absolute verdict is issued by these.
*/
if (static_key_false(&xt_tee_enabled))
jumpstack += private->stacksize * __this_cpu_read(nf_skb_duplicated);
/* 获取对应链上的首个匹配规则 */
e = get_entry(table_base, private->hook_entry[hook]);
do {
const struct xt_entry_target *t;
const struct xt_entry_match *ematch;
struct xt_counters *counter;
WARN_ON(!e);
/* 标准match */
if (!ip_packet_match(ip, indev, outdev,
&e->ip, acpar.fragoff)) {
no_match:
/* 未匹配成功,继续下一个规则 */
e = ipt_next_entry(e);
continue;
}
/* 扩展match */
xt_ematch_foreach(ematch, e) {
acpar.match = ematch->u.kernel.match;
acpar.matchinfo = ematch->data;
/* 只要有返回不匹配的,则说明匹配当前规则失败 */
if (!acpar.match->match(skb, &acpar))
goto no_match;
}
counter = xt_get_this_cpu_counter(&e->counters);
ADD_COUNTER(*counter, skb->len, 1);
/* 标准match和扩展match都成功 */
/* 获取target */
t = ipt_get_target_c(e);
WARN_ON(!t->u.kernel.target);
#if IS_ENABLED(CONFIG_NETFILTER_XT_TARGET_TRACE)
/* The packet is traced: log it */
if (unlikely(skb->nf_trace))
trace_packet(state->net, skb, hook, state->in,
state->out, table->name, private, e);
#endif
/* Standard target? */
/* 标准target */
if (!t->u.kernel.target->target) {
int v;
v = ((struct xt_standard_target *)t)->verdict;
/* 不会跳转到用户自定义规则 */
if (v < 0) {
/* Pop from stack? */
/* 不是XT_RETURN,则跳出结果 */
if (v != XT_RETURN) {
verdict = (unsigned int)(-v)

本文详细解析Linux防火墙工具iptables及其与内核netfilter框架的交互机制。阐述了iptables如何利用内核hook进行包过滤,并介绍了不同table(如Filter、NAT、Mangle等)与chain的作用及处理流程。
最低0.47元/天 解锁文章
2613

被折叠的 条评论
为什么被折叠?



