iptable_netfilter介绍以及简单代码分析

本文详细解析Linux防火墙工具iptables及其与内核netfilter框架的交互机制。阐述了iptables如何利用内核hook进行包过滤,并介绍了不同table(如Filter、NAT、Mangle等)与chain的作用及处理流程。
部署运行你感兴趣的模型镜像

前言

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) 

您可能感兴趣的与本文相关的镜像

PyTorch 2.6

PyTorch 2.6

PyTorch
Cuda

PyTorch 是一个开源的 Python 机器学习库,基于 Torch 库,底层由 C++ 实现,应用于人工智能领域,如计算机视觉和自然语言处理

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值