linux kernel ebtables接口


继续上篇文章 《linux etables tool》,看一下kernel中ebtables 框架。
代码目录:\net\bridge\netfilter

框架

ebtables core负责hook机制的实现,并且向上提供socket接口,供用户空间配置和查询过滤配置选项,向下提供注册接口,供各个table,规则,动作等过滤配置的注册。
在这里插入图片描述

数据结构

struct ebt_replace为用户空间调用socket接口配置内核时接口参数结构,描述了用户配置的过滤配置。

struct ebt_replace {
	char name[EBT_TABLE_MAXNAMELEN];
	unsigned int valid_hooks;
	/* nr of rules in the table */
	unsigned int nentries;
	/* total size of the entries */
	unsigned int entries_size;
	/* start of the chains */
	struct ebt_entries __user *hook_entry[NF_BR_NUMHOOKS];//指向每个链上规则首指针
	/* nr of counters userspace expects back */
	unsigned int num_counters;
	/* where the kernel will put the old counters */
	struct ebt_counter __user *counters;
	char __user *entries;//每个链上规则,可以多个规则,上面hook_entry[]指向每个链上第一个规则
};

struct ebt_table_info 是kernel中存储用户过滤配置的结构,就是把上面ebt_replace转换为ebt_table_info 中存储

struct ebt_table_info {
	/* total size of the entries */
	unsigned int entries_size;
	unsigned int nentries;
	/* pointers to the start of the chains */
	struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];//指向每个链上规则首指针
	/* room to maintain the stack used for jumping from and into udc */
	struct ebt_chainstack **chainstack;
	char *entries;//每个链上规则,可以多个规则,上面hook_entry[]指向每个链上第一个规则
	struct ebt_counter counters[0] ____cacheline_aligned;
};

struct ebt_table为过滤配置表,有broute,nat,filter三种,其中的struct ebt_table_info *private;指向了上面的过滤配置信息

struct ebt_table {
	struct list_head list;
	char name[EBT_TABLE_MAXNAMELEN];
	struct ebt_replace_kernel *table;
	unsigned int valid_hooks;
	rwlock_t lock;
	/* e.g. could be the table explicitly only allows certain
	 * matches, targets, ... 0 == let it in */
	int (*check)(const struct ebt_table_info *info,
	   unsigned int valid_hooks);
	/* the data used by the kernel */
	struct ebt_table_info *private;
	struct module *me;
};

struct ebt_entries 为ebt_table_info 中char *entries;每个链上规则,可以多个规则,

struct ebt_entries {
	/* this field is always set to zero
	 * See EBT_ENTRY_OR_ENTRIES.
	 * Must be same size as ebt_entry.bitmask */
	unsigned int distinguisher;
	/* the chain name */
	char name[EBT_CHAIN_MAXNAMELEN];
	/* counter offset for this chain */
	unsigned int counter_offset;
	/* one standard (accept, drop, return) per hook */
	int policy;
	/* nr. of entries */
	unsigned int nentries;
	/* entry list */
	char data[0] __attribute__ ((aligned (__alignof__(struct ebt_replace))));
};

struct ebt_entry为ebt_entries 中char data[0];指向具体匹配规则选项,可以有多个匹配规则
上面hook_entry[]指向每个链上第一个规则

/* one entry */
struct ebt_entry {
	/* this needs to be the first field */
	unsigned int bitmask;
	unsigned int invflags;
	__be16 ethproto;
	/* the physical in-dev */
	char in[IFNAMSIZ];
	/* the logical in-dev */
	char logical_in[IFNAMSIZ];
	/* the physical out-dev */
	char out[IFNAMSIZ];
	/* the logical out-dev */
	char logical_out[IFNAMSIZ];
	unsigned char sourcemac[ETH_ALEN];
	unsigned char sourcemsk[ETH_ALEN];
	unsigned char destmac[ETH_ALEN];
	unsigned char destmsk[ETH_ALEN];
	/* sizeof ebt_entry + matches */
	unsigned int watchers_offset; 跨过所有的ebt_entry_match指向第一个ebt_entry_watcher
	/* sizeof ebt_entry + matches + watchers */
	unsigned int target_offset;跨过所有的ebt_entry_match,ebt_entry_watcher指向第一个ebt_entry_target
	/* sizeof ebt_entry + matches + watchers + target */
	unsigned int next_offset; 指向下一个ebt_entry 
	unsigned char elems[0] __attribute__ ((aligned (__alignof__(struct ebt_replace))));// elems[0]指向ebt_entry_match
};

在这里插入图片描述

ebt_entry_match中data指向具体过滤配置参数,例如,ip为struct ebt_ip_info

ebt_match ,ebt_target 描述过滤配置匹配规则和执行的动作

struct ebt_match {
	struct list_head list;
	const char name[EBT_FUNCTION_MAXNAMELEN];
	bool (*match)(const struct sk_buff *skb, const struct net_device *in,
		const struct net_device *out, const struct xt_match *match,
		const void *matchinfo, int offset, unsigned int protoff,
		bool *hotdrop);
	bool (*checkentry)(const char *table, const void *entry,
		const struct xt_match *match, void *matchinfo,
		unsigned int hook_mask);
	void (*destroy)(const struct xt_match *match, void *matchinfo);
	unsigned int matchsize;
	u_int8_t revision;
	u_int8_t family;
	struct module *me;
};

struct ebt_watcher {
	struct list_head list;
	const char name[EBT_FUNCTION_MAXNAMELEN];
	unsigned int (*target)(struct sk_buff *skb,
		const struct net_device *in, const struct net_device *out,
		unsigned int hook_num, const struct xt_target *target,
		const void *targinfo);
	bool (*checkentry)(const char *table, const void *entry,
		const struct xt_target *target, void *targinfo,
		unsigned int hook_mask);
	void (*destroy)(const struct xt_target *target, void *targinfo);
	unsigned int targetsize;
	u_int8_t revision;
	u_int8_t family;
	struct module *me;
};

struct ebt_target {
	struct list_head list;
	const char name[EBT_FUNCTION_MAXNAMELEN];
	/* returns one of the standard EBT_* verdicts */
	unsigned int (*target)(struct sk_buff *skb,
		const struct net_device *in, const struct net_device *out,
		unsigned int hook_num, const struct xt_target *target,
		const void *targinfo);
	bool (*checkentry)(const char *table, const void *entry,
		const struct xt_target *target, void *targinfo,
		unsigned int hook_mask);
	void (*destroy)(const struct xt_target *target, void *targinfo);
	unsigned int targetsize;
	u_int8_t revision;
	u_int8_t family;
	struct module *me;
};

ebtables_init

ebtables初始化,ebtables.c

static int __init ebtables_init(void)
{
	int ret;

	ret = xt_register_target(&ebt_standard_target);
	if (ret < 0)
		return ret;
	#def 注册socket,提供给user空间的socket接口
	ret = nf_register_sockopt(&ebt_sockopts); 
	if (ret < 0) {
		xt_unregister_target(&ebt_standard_target);
		return ret;
	}

	printk(KERN_INFO "Ebtables v2.0 registered\n");
	return 0;
}

static struct nf_sockopt_ops ebt_sockopts =
{
	.pf		= PF_INET,
	.set_optmin	= EBT_BASE_CTL,  //能够处理的set 选项索引最小值
	.set_optmax	= EBT_SO_SET_MAX + 1,//能够处理的set 选项索引最大值
	.set		= do_ebt_set_ctl,
#ifdef CONFIG_COMPAT
	.compat_set	= compat_do_ebt_set_ctl,
#endif
	.get_optmin	= EBT_BASE_CTL,
	.get_optmax	= EBT_SO_GET_MAX + 1,
	.get		= do_ebt_get_ctl,
#ifdef CONFIG_COMPAT
	.compat_get	= compat_do_ebt_get_ctl,
#endif
	.owner		= THIS_MODULE,
};

用户空间ebtables tool调用
sockfd = socket(AF_INET, SOCK_RAW, PF_INET); 创建SOCK_RAW
setsockopt(sockfd, IPPROTO_IP, EBT_SO_SET_ENTRIES, repl, optlen) 通过EBT_SO_SET_ENTRIES把过滤配置传递给kernel。
对应调用

\net\ipv4\raw.c
raw_setsockopt
\net\ipv4\ip_sockglue.c
ip_setsockopt
nf_sockopt.c
nf_setsockopt
nf_sockopt
do_ebt_set_ctl

static int do_ebt_set_ctl(struct sock *sk,
	int cmd, void __user *user, unsigned int len)
{
	int ret;
	struct net *net = sock_net(sk);

	if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
		return -EPERM;

	switch(cmd) {
	#def 找到了EBT_SO_SET_ENTRIES 配置地方
	case EBT_SO_SET_ENTRIES:
		ret = do_replace(net, user, len);
		break;
	case EBT_SO_SET_COUNTERS:
		ret = update_counters(net, user, len);
		break;
	default:
		ret = -EINVAL;
	}
	return ret;
}

do_replace把用户空间过滤配置存储到kernel

/* replace the table */
static int do_replace(struct net *net, const void __user *user,
		      unsigned int len)
{
	int ret, countersize;
	struct ebt_table_info *newinfo;
	struct ebt_replace tmp;
	#def 先把用户空间数据copy到kernel空间
	if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
		return -EFAULT;



	tmp.name[sizeof(tmp.name) - 1] = 0;

	countersize = COUNTER_OFFSET(tmp.nentries) * nr_cpu_ids;
	#def 申请ebt_table_info 结构存储空间,用于存储过滤配置
	newinfo = vmalloc(sizeof(*newinfo) + countersize);
	if (!newinfo)
		return -ENOMEM;

	if (countersize)
		memset(newinfo->counters, 0, countersize);

	newinfo->entries = vmalloc(tmp.entries_size);

	#def 复制到ebt_table_info  表中规则和执行动作
	if (copy_from_user(
	   newinfo->entries, tmp.entries, tmp.entries_size) != 0) {
		BUGPRINT("Couldn't copy entries from userspace\n");
		ret = -EFAULT;
		goto free_entries;
	}

	ret = do_replace_finish(net, &tmp, newinfo);

}
static int do_replace_finish(struct net *net, struct ebt_replace *repl,
			      struct ebt_table_info *newinfo)
{
	int ret, i;
	struct ebt_counter *counterstmp = NULL;
	/* used to be able to unlock earlier */
	struct ebt_table_info *table;
	struct ebt_table *t;

	/* the user wants counters back
	   the check on the size is done later, when we have the lock */
	if (repl->num_counters) {
		unsigned long size = repl->num_counters * sizeof(*counterstmp);
		counterstmp = vmalloc(size);
		if (!counterstmp)
			return -ENOMEM;
	}

	newinfo->chainstack = NULL;
	ret = ebt_verify_pointers(repl, newinfo);
	if (ret != 0)
		goto free_counterstmp;
    
    //用户空间和kernel配置表信息转换
	ret = translate_table(net, repl->name, newinfo);

	if (ret != 0)
		goto free_counterstmp;
    #def 根据name找到table,比如broute,nat,filter表等
	t = find_table_lock(net, repl->name, &ret, &ebt_mutex);
	if (!t) {
		ret = -ENOENT;
		goto free_iterate;
	}

	/* the table doesn't like it */
	if (t->check && (ret = t->check(newinfo, repl->valid_hooks)))
		goto free_unlock;

	if (repl->num_counters && repl->num_counters != t->private->nentries) {
		BUGPRINT("Wrong nr. of counters requested\n");
		ret = -EINVAL;
		goto free_unlock;
	}

	/* we have the mutex lock, so no danger in reading this pointer */
	table = t->private;
	/* make sure the table can only be rmmod'ed if it contains no rules */
	if (!table->nentries && newinfo->nentries && !try_module_get(t->me)) {
		ret = -ENOENT;
		goto free_unlock;
	} else if (table->nentries && !newinfo->nentries)
		module_put(t->me);
	/* we need an atomic snapshot of the counters */
	write_lock_bh(&t->lock);
	if (repl->num_counters)
		get_counters(t->private->counters, counterstmp,
		   t->private->nentries);
    #def 存储到private 中
	t->private = newinfo;
	write_unlock_bh(&t->lock);
	mutex_unlock(&ebt_mutex);
	/* so, a user can change the chains while having messed up her counter
	   allocation. Only reason why this is done is because this way the lock
	   is held only once, while this doesn't bring the kernel into a
	   dangerous state. */
	if (repl->num_counters &&
	   copy_to_user(repl->counters, counterstmp,
	   repl->num_counters * sizeof(struct ebt_counter))) {
		/* Silent error, can't fail, new table is already in place */
		net_warn_ratelimited("ebtables: counters copy to user failed while replacing table\n");
	}

	/* decrease module count and free resources */
	EBT_ENTRY_ITERATE(table->entries, table->entries_size,
			  ebt_cleanup_entry, net, NULL);

	vfree(table->entries);
	if (table->chainstack) {
		for_each_possible_cpu(i)
			vfree(table->chainstack[i]);
		vfree(table->chainstack);
	}
	vfree(table);

	vfree(counterstmp);
	return ret;

free_unlock:
	mutex_unlock(&ebt_mutex);
free_iterate:
	EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
			  ebt_cleanup_entry, net, NULL);
free_counterstmp:
	vfree(counterstmp);
	/* can be initialized in translate_table() */
	if (newinfo->chainstack) {
		for_each_possible_cpu(i)
			vfree(newinfo->chainstack[i]);
		vfree(newinfo->chainstack);
	}
	return ret;
}

translate_table

static int translate_table(struct net *net, const char *name,
			   struct ebt_table_info *newinfo)
{


	ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
	   ebt_check_entry, net, newinfo, name, &i, cl_s, udc_cnt);
}

ebt_check_entry--》ebt_check_match
	match = xt_request_find_match(NFPROTO_BRIDGE, m->u.name, 0);//从全局变量xt[af].match链表中查找,后面介绍如何注册的

	m->u.match = match; //赋值到m->u.match,也就是struct ebt_entry_match结构中u.match,供匹配时使用

	par->match     = match;
	par->matchinfo = m->data;


ebt_check_entry--》ebt_check_watcher
	watcher = xt_request_find_target(NFPROTO_BRIDGE, w->u.name, 0);//从全局变量xt[af].watcher 链表中查找,后面介绍如何注册的

	w->u.watcher = watcher;//赋值到m->u.match,也就是struct ebt_entry_watcher结构中u.watcher,供匹配时使用

	par->target   = watcher;
	par->targinfo = w->data;

ebt_check_entry--》
	target = xt_request_find_target(NFPROTO_BRIDGE, t->u.name, 0);//从全局变量xt[af].target链表中查找,后面介绍如何注册的
	t->u.target = target;//赋值到t->u.target,也就是struct ebt_entry_target结构中u.watcher,供匹配时执行动作使用

上面复用了union变量,name和match 或watcher或target,用户空间传进来为name,kernel根据name查找到后,赋值match 或watcher或target

find_table_lock

static inline struct ebt_table *
find_table_lock(struct net *net, const char *name, int *error,
		struct mutex *mutex)
{
	#def 从net->xt.tables[NFPROTO_BRIDGE]查找表ebt_table 
	return find_inlist_lock(&net->xt.tables[NFPROTO_BRIDGE], name,
				"ebtable_", error, mutex);
}

过滤配置表注册

ebt_register_table
kernel中过滤配置表通过ebt_register_table注册到net->xt.tables[NFPROTO_BRIDGE] 链表中

struct ebt_table *
ebt_register_table(struct net *net, const struct ebt_table *input_table)
{
	struct ebt_table_info *newinfo;
	struct ebt_table *t, *table;
	struct ebt_replace_kernel *repl;
	int ret, i, countersize;
	void *p;

	if (input_table == NULL || (repl = input_table->table) == NULL ||
	    repl->entries == NULL || repl->entries_size == 0 ||
	    repl->counters != NULL || input_table->private != NULL) {
		BUGPRINT("Bad table data for ebt_register_table!!!\n");
		return ERR_PTR(-EINVAL);
	}

	/* Don't add one table to multiple lists. */
	table = kmemdup(input_table, sizeof(struct ebt_table), GFP_KERNEL);
	if (!table) {
		ret = -ENOMEM;
		goto out;
	}

	countersize = COUNTER_OFFSET(repl->nentries) * nr_cpu_ids;
	newinfo = vmalloc(sizeof(*newinfo) + countersize);
	ret = -ENOMEM;
	if (!newinfo)
		goto free_table;

	p = vmalloc(repl->entries_size);
	if (!p)
		goto free_newinfo;

	memcpy(p, repl->entries, repl->entries_size);
	newinfo->entries = p;

	newinfo->entries_size = repl->entries_size;
	newinfo->nentries = repl->nentries;

	if (countersize)
		memset(newinfo->counters, 0, countersize);

	/* fill in newinfo and parse the entries */
	newinfo->chainstack = NULL;
	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
		if ((repl->valid_hooks & (1 << i)) == 0)
			newinfo->hook_entry[i] = NULL;
		else
			newinfo->hook_entry[i] = p +
				((char *)repl->hook_entry[i] - repl->entries);
	}
	ret = translate_table(net, repl->name, newinfo);
	if (ret != 0) {
		BUGPRINT("Translate_table failed\n");
		goto free_chainstack;
	}

	if (table->check && table->check(newinfo, table->valid_hooks)) {
		BUGPRINT("The table doesn't like its own initial data, lol\n");
		ret = -EINVAL;
		goto free_chainstack;
	}

	table->private = newinfo;
	rwlock_init(&table->lock);
	ret = mutex_lock_interruptible(&ebt_mutex);
	if (ret != 0)
		goto free_chainstack;

	list_for_each_entry(t, &net->xt.tables[NFPROTO_BRIDGE], list) {
		if (strcmp(t->name, table->name) == 0) {
			ret = -EEXIST;
			BUGPRINT("Table name already exists\n");
			goto free_unlock;
		}
	}

	/* Hold a reference count if the chains aren't empty */
	if (newinfo->nentries && !try_module_get(table->me)) {
		ret = -ENOENT;
		goto free_unlock;
	}
	#def 添加到net->xt.tables[NFPROTO_BRIDGE] 中
	list_add(&table->list, &net->xt.tables[NFPROTO_BRIDGE]);
	mutex_unlock(&ebt_mutex);
	return table;

	return ERR_PTR(ret);
}

ebtable_broute.c


static struct pernet_operations broute_net_ops = {
	.init = broute_net_init,
	.exit = broute_net_exit,
};

static int __init ebtable_broute_init(void)
{
	int ret;
    #def 注册broute 操作函数
	ret = register_pernet_subsys(&broute_net_ops);
	if (ret < 0)
		return ret;
	/* see br_input.c */
	#def 设置br_should_route_hook指针 ebt_broute,在br_input中调用
	RCU_INIT_POINTER(br_should_route_hook,
			   (br_should_route_hook_t *)ebt_broute);
	return 0;
}

static int __net_init broute_net_init(struct net *net)
{	
	#def 注册broute到net->xt.tables[NFPROTO_BRIDGE] 中
	net->xt.broute_table = ebt_register_table(net, &broute_table);
	return PTR_RET(net->xt.broute_table);
}

/* EBT_ACCEPT means the frame will be bridged
 * EBT_DROP means the frame will be routed
 */
 #def 默认规则EBT_ACCEPT  will be bridged
static struct ebt_entries initial_chain = {
	.name		= "BROUTING",
	.policy		= EBT_ACCEPT,
};

#def 初始化表,默认使用的
static struct ebt_replace_kernel initial_table =
{
	.name		= "broute",
	.valid_hooks	= 1 << NF_BR_BROUTING,
	.entries_size	= sizeof(struct ebt_entries),
	.hook_entry	= {
		[NF_BR_BROUTING]	= &initial_chain,
	},
	.entries	= (char *)&initial_chain,
};

#def broute_table表
static const struct ebt_table broute_table =
{
	.name		= "broute",
	.table		= &initial_table, //初始化表,默认使用的
	.valid_hooks	= 1 << NF_BR_BROUTING, //在NF_BR_BROUTING hook上
	.check		= check,
	.me		= THIS_MODULE,
};

过滤配置规则的注册

内核中提供过滤配置规则,如ip,ipv6,arp,802.3,nat,vlan,stp等等
在目录下net\bridge\netfilter各个文件

数据结构

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. */
	bool (*match)(const struct sk_buff *skb,
		      struct xt_action_param *);

	/* Called when user tries to insert an entry of this type. */
	int (*checkentry)(const struct xt_mtchk_param *);

	/* Called when entry of this type deleted. */
	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;
#ifdef CONFIG_COMPAT
	unsigned int compatsize;
#endif
	unsigned int hooks;
	unsigned short proto;

	unsigned short family;
};

/* 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. */
	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). */
	int (*checkentry)(const struct xt_tgchk_param *);

	/* Called when entry of this type deleted. */
	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 */
	struct module *me;

	const char *table;
	unsigned int targetsize;
#ifdef CONFIG_COMPAT
	unsigned int compatsize;
#endif
	unsigned int hooks;
	unsigned short proto;

	unsigned short family;
};

/* Furniture shopping... */
struct xt_table {
	struct list_head list;

	/* What hooks you will enter on */
	unsigned int valid_hooks;

	/* Man behind the curtain... */
	struct xt_table_info *private;

	/* Set this to THIS_MODULE if you are a module, otherwise NULL */
	struct module *me;

	u_int8_t af;		/* address/protocol family */
	int priority;		/* hook order */

	/* A unique name... */
	const char name[XT_TABLE_MAXNAMELEN];
};

ip过滤匹配

ebt_ip.c

ip的匹配规则结构
static struct xt_match ebt_ip_mt_reg __read_mostly = {
	.name		= "ip",
	.revision	= 0,
	.family		= NFPROTO_BRIDGE,
	.match		= ebt_ip_mt,
	.checkentry	= ebt_ip_mt_check,
	.matchsize	= sizeof(struct ebt_ip_info),
	.me		= THIS_MODULE,
};

static int __init ebt_ip_init(void)
{
	return xt_register_match(&ebt_ip_mt_reg); //注册到core中static struct xt_af *xt;链表中
}

static void __exit ebt_ip_fini(void)
{
	xt_unregister_match(&ebt_ip_mt_reg);
}

module_init(ebt_ip_init);// 注册到.init段,kernel启动过程中自动调用
module_exit(ebt_ip_fini);
int
xt_register_match(struct xt_match *match)
{
	u_int8_t af = match->family;
	int ret;

	ret = mutex_lock_interruptible(&xt[af].mutex);
	if (ret != 0)
		return ret;

	list_add(&match->list, &xt[af].match);
	mutex_unlock(&xt[af].mutex);

	return ret;
}

ebt_do_table

每个hook点调用ebt_do_table ,进行过滤。
在这里插入图片描述

/* Do some firewalling */
unsigned int ebt_do_table (unsigned int hook, struct sk_buff *skb,
   const struct net_device *in, const struct net_device *out,
   struct ebt_table *table)
{
	int i, nentries;
	struct ebt_entry *point;
	struct ebt_counter *counter_base, *cb_base;
	const struct ebt_entry_target *t;
	int verdict, sp = 0;
	struct ebt_chainstack *cs;
	struct ebt_entries *chaininfo;
	const char *base;
	const struct ebt_table_info *private;
	struct xt_action_param acpar;

	acpar.family  = NFPROTO_BRIDGE;
	acpar.in      = in;
	acpar.out     = out;
	acpar.hotdrop = false;
	acpar.hooknum = hook;

	read_lock_bh(&table->lock);
	private = table->private; //private中存储ebt_table_info ,前面已介绍
	cb_base = COUNTER_BASE(private->counters, private->nentries,
	   smp_processor_id());
	if (private->chainstack)
		cs = private->chainstack[smp_processor_id()];
	else
		cs = NULL;
	chaininfo = private->hook_entry[hook]; //对应hook 链信息
	nentries = private->hook_entry[hook]->nentries; //过滤配置规则个数
	point = (struct ebt_entry *)(private->hook_entry[hook]->data); //执行具体过滤规则匹配项
	counter_base = cb_base + private->hook_entry[hook]->counter_offset;
	/* base for chain jumps */
	base = private->entries;
	i = 0;
	while (i < nentries) {
		if (ebt_basic_match(point, skb, in, out))
			goto letscontinue;

		if (EBT_MATCH_ITERATE(point, ebt_do_match, skb, &acpar) != 0) //过滤规则匹配,循环查找,发现name相同,完成
			goto letscontinue;
		if (acpar.hotdrop) {
			read_unlock_bh(&table->lock);
			return NF_DROP;
		}

		/* increase counter */
		(*(counter_base + i)).pcnt++;
		(*(counter_base + i)).bcnt += skb->len;

		/* these should only watch: not modify, nor tell us
		   what to do with the packet */
		EBT_WATCHER_ITERATE(point, ebt_do_watcher, skb, &acpar);//匹配watcher
        //前面EBT_MATCH_ITERATE没有匹配到,才走这里,先看释放上层配置了t->u.target->target,如果
        //没有配置,就使用默认ebt_standard_target 
		t = (struct ebt_entry_target *)
		   (((char *)point) + point->target_offset);
		/* standard target */
		if (!t->u.target->target)
			verdict = ((struct ebt_standard_target *)t)->verdict;
		else {
			acpar.target   = t->u.target;
			acpar.targinfo = t->data;
			verdict = t->u.target->target(skb, &acpar);
		}
		if (verdict == EBT_ACCEPT) { //接收
			read_unlock_bh(&table->lock);
			return NF_ACCEPT;
		}
		if (verdict == EBT_DROP) { //丢包
			read_unlock_bh(&table->lock);
			return NF_DROP;
		}
		if (verdict == EBT_RETURN) {
letsreturn:
#ifdef CONFIG_NETFILTER_DEBUG
			if (sp == 0) {
				BUGPRINT("RETURN on base chain");
				/* act like this is EBT_CONTINUE */
				goto letscontinue;
			}
#endif
			sp--;
			/* put all the local variables right */
			i = cs[sp].n;
			chaininfo = cs[sp].chaininfo;
			nentries = chaininfo->nentries;
			point = cs[sp].e;
			counter_base = cb_base +
			   chaininfo->counter_offset;
			continue;
		}
		if (verdict == EBT_CONTINUE)
			goto letscontinue;
#ifdef CONFIG_NETFILTER_DEBUG
		if (verdict < 0) {
			BUGPRINT("bogus standard verdict\n");
			read_unlock_bh(&table->lock);
			return NF_DROP;
		}
#endif
		/* jump to a udc */
		cs[sp].n = i + 1;
		cs[sp].chaininfo = chaininfo;
		cs[sp].e = ebt_next_entry(point);
		i = 0;
		chaininfo = (struct ebt_entries *) (base + verdict);
#ifdef CONFIG_NETFILTER_DEBUG
		if (chaininfo->distinguisher) {
			BUGPRINT("jump to non-chain\n");
			read_unlock_bh(&table->lock);
			return NF_DROP;
		}
#endif
		nentries = chaininfo->nentries;
		point = (struct ebt_entry *)chaininfo->data;
		counter_base = cb_base + chaininfo->counter_offset;
		sp++;
		continue;
letscontinue:
		point = ebt_next_entry(point);
		i++;
	}

	/* I actually like this :) */
	//如果上层没有配置匹配过滤规则,走hook 链默认配置
	if (chaininfo->policy == EBT_RETURN)
		goto letsreturn;
	if (chaininfo->policy == EBT_ACCEPT) {
		read_unlock_bh(&table->lock);
		return NF_ACCEPT;
	}
	read_unlock_bh(&table->lock);
	return NF_DROP;
}

EBT_MATCH_ITERATE

/* blatently stolen from ip_tables.h
 * fn returns 0 to continue iteration */
#define EBT_MATCH_ITERATE(e, fn, args...)                   \
({                                                          \
	unsigned int __i;                                   \
	int __ret = 0;                                      \
	struct ebt_entry_match *__match;                    \
	                                                    \
	for (__i = sizeof(struct ebt_entry);                \
	     __i < (e)->watchers_offset;    //直到 watchers_offset,前面数据结构中已经介绍               \
	     __i += __match->match_size +                   \
	     sizeof(struct ebt_entry_match)) {              \
		__match = (void *)(e) + __i;                \
		                                            \
		__ret = fn(__match , ## args);              \
		if (__ret != 0)                             \
			break;                              \
	}                                                   \
	if (__ret == 0) {                                   \
		if (__i != (e)->watchers_offset)            \
			__ret = -EINVAL;                    \
	}                                                   \
	__ret;                                              \
})

ebt_do_match

static inline int
ebt_do_match(struct ebt_entry_match *m, const struct sk_buff *skb,
	     struct xt_action_param *par)
{
	par->match     = m->u.match;
	par->matchinfo = m->data;
	return m->u.match->match(skb, par) ? EBT_MATCH : EBT_NOMATCH; //调用具体过滤匹配match
}

ebt_ip_mt

举例看看ip的过滤匹配规则

static struct xt_match ebt_ip_mt_reg __read_mostly = {
	.name		= "ip",
	.revision	= 0,
	.family		= NFPROTO_BRIDGE,
	.match		= ebt_ip_mt, //上面的m->u.match->match(skb, par)
	.checkentry	= ebt_ip_mt_check,
	.matchsize	= sizeof(struct ebt_ip_info),
	.me		= THIS_MODULE,
};
/* the same values are used for the invflags */
struct ebt_ip_info {
	__be32 saddr;
	__be32 daddr;
	__be32 smsk;
	__be32 dmsk;
	__u8  tos;
	__u8  protocol;
	__u8  bitmask; //存储了上层配置了哪些匹配选项,比如源ip地址,目的端口号等
	__u8  invflags;
	__u16 sport[2];
	__u16 dport[2];
};
static bool
ebt_ip_mt(const struct sk_buff *skb, struct xt_action_param *par)
{
	const struct ebt_ip_info *info = par->matchinfo; //匹配信息,上层配置信息保存
	const struct iphdr *ih;
	struct iphdr _iph;
	const struct tcpudphdr *pptr;
	struct tcpudphdr _ports;

	ih = skb_header_pointer(skb, 0, sizeof(_iph), &_iph);
	if (ih == NULL)
		return false;
		//根据bitmask 中配置的选项,进行匹配
	if (info->bitmask & EBT_IP_TOS &&
	   FWINV(info->tos != ih->tos, EBT_IP_TOS))
		return false;
	if (info->bitmask & EBT_IP_SOURCE &&
	   FWINV((ih->saddr & info->smsk) !=
	   info->saddr, EBT_IP_SOURCE))
		return false;
	if ((info->bitmask & EBT_IP_DEST) &&
	   FWINV((ih->daddr & info->dmsk) !=
	   info->daddr, EBT_IP_DEST))
		return false;
	if (info->bitmask & EBT_IP_PROTO) {
		if (FWINV(info->protocol != ih->protocol, EBT_IP_PROTO))
			return false;
		if (!(info->bitmask & EBT_IP_DPORT) &&
		    !(info->bitmask & EBT_IP_SPORT))
			return true;
		if (ntohs(ih->frag_off) & IP_OFFSET)
			return false;
		pptr = skb_header_pointer(skb, ih->ihl*4,
					  sizeof(_ports), &_ports);
		if (pptr == NULL)
			return false;
		if (info->bitmask & EBT_IP_DPORT) {
			u32 dst = ntohs(pptr->dst);
			if (FWINV(dst < info->dport[0] ||
				  dst > info->dport[1],
				  EBT_IP_DPORT))
			return false;
		}
		if (info->bitmask & EBT_IP_SPORT) {
			u32 src = ntohs(pptr->src);
			if (FWINV(src < info->sport[0] ||
				  src > info->sport[1],
				  EBT_IP_SPORT))
			return false;
		}
	}
	return true;
}
©️2020 CSDN 皮肤主题: 深蓝海洋 设计师:CSDN官方博客 返回首页