⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ebtables.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
/* *  ebtables * *  Author: *  Bart De Schuymer		<bdschuym@pandora.be> * *  ebtables.c,v 2.0, July, 2002 * *  This code is stongly inspired on the iptables code which is *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling * *  This program is free software; you can redistribute it and/or *  modify it under the terms of the GNU General Public License *  as published by the Free Software Foundation; either version *  2 of the License, or (at your option) any later version. *//* used for print_string */#include <linux/tty.h>#include <linux/kmod.h>#include <linux/module.h>#include <linux/vmalloc.h>#include <linux/netfilter_bridge/ebtables.h>#include <linux/spinlock.h>#include <linux/mutex.h>#include <asm/uaccess.h>#include <linux/smp.h>#include <linux/cpumask.h>#include <net/sock.h>/* needed for logical [in,out]-dev filtering */#include "../br_private.h"#define BUGPRINT(format, args...) printk("kernel msg: ebtables bug: please "\					 "report to author: "format, ## args)/* #define BUGPRINT(format, args...) */#define MEMPRINT(format, args...) printk("kernel msg: ebtables "\					 ": out of memory: "format, ## args)/* #define MEMPRINT(format, args...) *//* * Each cpu has its own set of counters, so there is no need for write_lock in * the softirq * For reading or updating the counters, the user context needs to * get a write_lock *//* The size of each set of counters is altered to get cache alignment */#define SMP_ALIGN(x) (((x) + SMP_CACHE_BYTES-1) & ~(SMP_CACHE_BYTES-1))#define COUNTER_OFFSET(n) (SMP_ALIGN(n * sizeof(struct ebt_counter)))#define COUNTER_BASE(c, n, cpu) ((struct ebt_counter *)(((char *)c) + \   COUNTER_OFFSET(n) * cpu))static DEFINE_MUTEX(ebt_mutex);static LIST_HEAD(ebt_tables);static LIST_HEAD(ebt_targets);static LIST_HEAD(ebt_matches);static LIST_HEAD(ebt_watchers);static struct ebt_target ebt_standard_target ={ {NULL, NULL}, EBT_STANDARD_TARGET, NULL, NULL, NULL, NULL};static inline int ebt_do_watcher (struct ebt_entry_watcher *w,   const struct sk_buff *skb, unsigned int hooknr, const struct net_device *in,   const struct net_device *out){	w->u.watcher->watcher(skb, hooknr, in, out, w->data,	   w->watcher_size);	/* watchers don't give a verdict */	return 0;}static inline int ebt_do_match (struct ebt_entry_match *m,   const struct sk_buff *skb, const struct net_device *in,   const struct net_device *out){	return m->u.match->match(skb, in, out, m->data,	   m->match_size);}static inline int ebt_dev_check(char *entry, const struct net_device *device){	int i = 0;	const char *devname = device->name;	if (*entry == '\0')		return 0;	if (!device)		return 1;	/* 1 is the wildcard token */	while (entry[i] != '\0' && entry[i] != 1 && entry[i] == devname[i])		i++;	return (devname[i] != entry[i] && entry[i] != 1);}#define FWINV2(bool,invflg) ((bool) ^ !!(e->invflags & invflg))/* process standard matches */static inline int ebt_basic_match(struct ebt_entry *e, struct ethhdr *h,   const struct net_device *in, const struct net_device *out){	int verdict, i;	if (e->bitmask & EBT_802_3) {		if (FWINV2(ntohs(h->h_proto) >= 1536, EBT_IPROTO))			return 1;	} else if (!(e->bitmask & EBT_NOPROTO) &&	   FWINV2(e->ethproto != h->h_proto, EBT_IPROTO))		return 1;	if (FWINV2(ebt_dev_check(e->in, in), EBT_IIN))		return 1;	if (FWINV2(ebt_dev_check(e->out, out), EBT_IOUT))		return 1;	if ((!in || !in->br_port) ? 0 : FWINV2(ebt_dev_check(	   e->logical_in, in->br_port->br->dev), EBT_ILOGICALIN))		return 1;	if ((!out || !out->br_port) ? 0 : FWINV2(ebt_dev_check(	   e->logical_out, out->br_port->br->dev), EBT_ILOGICALOUT))		return 1;	if (e->bitmask & EBT_SOURCEMAC) {		verdict = 0;		for (i = 0; i < 6; i++)			verdict |= (h->h_source[i] ^ e->sourcemac[i]) &			   e->sourcemsk[i];		if (FWINV2(verdict != 0, EBT_ISOURCE) )			return 1;	}	if (e->bitmask & EBT_DESTMAC) {		verdict = 0;		for (i = 0; i < 6; i++)			verdict |= (h->h_dest[i] ^ e->destmac[i]) &			   e->destmsk[i];		if (FWINV2(verdict != 0, EBT_IDEST) )			return 1;	}	return 0;}/* 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;	struct ebt_entry_target *t;	int verdict, sp = 0;	struct ebt_chainstack *cs;	struct ebt_entries *chaininfo;	char *base;	struct ebt_table_info *private;	read_lock_bh(&table->lock);	private = table->private;	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];	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, eth_hdr(skb), in, out))			goto letscontinue;		if (EBT_MATCH_ITERATE(point, ebt_do_match, skb, in, out) != 0)			goto letscontinue;		/* 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, hook, in,		   out);		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			verdict = t->u.target->target(skb, hook,			   in, out, t->data, t->target_size);		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 = (struct ebt_entry *)		   (((char *)point) + point->next_offset);		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 = (struct ebt_entry *)		   (((char *)point) + point->next_offset);		i++;	}	/* I actually like this :) */	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;}/* If it succeeds, returns element and locks mutex */static inline void *find_inlist_lock_noload(struct list_head *head, const char *name, int *error,   struct mutex *mutex){	struct {		struct list_head list;		char name[EBT_FUNCTION_MAXNAMELEN];	} *e;	*error = mutex_lock_interruptible(mutex);	if (*error != 0)		return NULL;	list_for_each_entry(e, head, list) {		if (strcmp(e->name, name) == 0)			return e;	}	*error = -ENOENT;	mutex_unlock(mutex);	return NULL;}#ifndef CONFIG_KMOD#define find_inlist_lock(h,n,p,e,m) find_inlist_lock_noload((h),(n),(e),(m))#elsestatic void *find_inlist_lock(struct list_head *head, const char *name, const char *prefix,   int *error, struct mutex *mutex){	void *ret;	ret = find_inlist_lock_noload(head, name, error, mutex);	if (!ret) {		request_module("%s%s", prefix, name);		ret = find_inlist_lock_noload(head, name, error, mutex);	}	return ret;}#endifstatic inline struct ebt_table *find_table_lock(const char *name, int *error, struct mutex *mutex){	return find_inlist_lock(&ebt_tables, name, "ebtable_", error, mutex);}static inline struct ebt_match *find_match_lock(const char *name, int *error, struct mutex *mutex){	return find_inlist_lock(&ebt_matches, name, "ebt_", error, mutex);}static inline struct ebt_watcher *find_watcher_lock(const char *name, int *error, struct mutex *mutex){	return find_inlist_lock(&ebt_watchers, name, "ebt_", error, mutex);}static inline struct ebt_target *find_target_lock(const char *name, int *error, struct mutex *mutex){	return find_inlist_lock(&ebt_targets, name, "ebt_", error, mutex);}static inline intebt_check_match(struct ebt_entry_match *m, struct ebt_entry *e,   const char *name, unsigned int hookmask, unsigned int *cnt){	struct ebt_match *match;	size_t left = ((char *)e + e->watchers_offset) - (char *)m;	int ret;	if (left < sizeof(struct ebt_entry_match) ||	    left - sizeof(struct ebt_entry_match) < m->match_size)		return -EINVAL;	match = find_match_lock(m->u.name, &ret, &ebt_mutex);	if (!match)		return ret;	m->u.match = match;	if (!try_module_get(match->me)) {		mutex_unlock(&ebt_mutex);		return -ENOENT;	}	mutex_unlock(&ebt_mutex);	if (match->check &&	   match->check(name, hookmask, e, m->data, m->match_size) != 0) {		BUGPRINT("match->check failed\n");		module_put(match->me);		return -EINVAL;	}	(*cnt)++;	return 0;}static inline intebt_check_watcher(struct ebt_entry_watcher *w, struct ebt_entry *e,   const char *name, unsigned int hookmask, unsigned int *cnt){	struct ebt_watcher *watcher;	size_t left = ((char *)e + e->target_offset) - (char *)w;	int ret;	if (left < sizeof(struct ebt_entry_watcher) ||	   left - sizeof(struct ebt_entry_watcher) < w->watcher_size)		return -EINVAL;	watcher = find_watcher_lock(w->u.name, &ret, &ebt_mutex);	if (!watcher)		return ret;	w->u.watcher = watcher;	if (!try_module_get(watcher->me)) {		mutex_unlock(&ebt_mutex);		return -ENOENT;	}	mutex_unlock(&ebt_mutex);	if (watcher->check &&	   watcher->check(name, hookmask, e, w->data, w->watcher_size) != 0) {		BUGPRINT("watcher->check failed\n");		module_put(watcher->me);		return -EINVAL;	}	(*cnt)++;	return 0;}static int ebt_verify_pointers(struct ebt_replace *repl,			       struct ebt_table_info *newinfo){	unsigned int limit = repl->entries_size;	unsigned int valid_hooks = repl->valid_hooks;	unsigned int offset = 0;	int i;	for (i = 0; i < NF_BR_NUMHOOKS; i++)		newinfo->hook_entry[i] = NULL;	newinfo->entries_size = repl->entries_size;	newinfo->nentries = repl->nentries;	while (offset < limit) {		size_t left = limit - offset;		struct ebt_entry *e = (void *)newinfo->entries + offset;		if (left < sizeof(unsigned int))			break;		for (i = 0; i < NF_BR_NUMHOOKS; i++) {			if ((valid_hooks & (1 << i)) == 0)				continue;			if ((char __user *)repl->hook_entry[i] ==			     repl->entries + offset)				break;		}		if (i != NF_BR_NUMHOOKS || !(e->bitmask & EBT_ENTRY_OR_ENTRIES)) {			if (e->bitmask != 0) {				/* we make userspace set this right,				   so there is no misunderstanding */				BUGPRINT("EBT_ENTRY_OR_ENTRIES shouldn't be set "					 "in distinguisher\n");				return -EINVAL;			}			if (i != NF_BR_NUMHOOKS)				newinfo->hook_entry[i] = (struct ebt_entries *)e;			if (left < sizeof(struct ebt_entries))				break;			offset += sizeof(struct ebt_entries);		} else {			if (left < sizeof(struct ebt_entry))				break;			if (left < e->next_offset)				break;			offset += e->next_offset;		}	}	if (offset != limit) {		BUGPRINT("entries_size too small\n");		return -EINVAL;	}	/* check if all valid hooks have a chain */	for (i = 0; i < NF_BR_NUMHOOKS; i++) {		if (!newinfo->hook_entry[i] &&		   (valid_hooks & (1 << i))) {			BUGPRINT("Valid hook without chain\n");			return -EINVAL;		}	}	return 0;}/* * this one is very careful, as it is the first function * to parse the userspace data */static inline intebt_check_entry_size_and_hooks(struct ebt_entry *e,   struct ebt_table_info *newinfo,   unsigned int *n, unsigned int *cnt,   unsigned int *totalcnt, unsigned int *udc_cnt){	int i;	for (i = 0; i < NF_BR_NUMHOOKS; i++) {		if ((void *)e == (void *)newinfo->hook_entry[i])			break;	}	/* beginning of a new chain	   if i == NF_BR_NUMHOOKS it must be a user defined chain */	if (i != NF_BR_NUMHOOKS || !e->bitmask) {		/* this checks if the previous chain has as many entries		   as it said it has */		if (*n != *cnt) {			BUGPRINT("nentries does not equal the nr of entries "				 "in the chain\n");			return -EINVAL;		}		if (((struct ebt_entries *)e)->policy != EBT_DROP &&		   ((struct ebt_entries *)e)->policy != EBT_ACCEPT) {			/* only RETURN from udc */			if (i != NF_BR_NUMHOOKS ||			   ((struct ebt_entries *)e)->policy != EBT_RETURN) {				BUGPRINT("bad policy\n");				return -EINVAL;			}		}		if (i == NF_BR_NUMHOOKS) /* it's a user defined chain */			(*udc_cnt)++;		if (((struct ebt_entries *)e)->counter_offset != *totalcnt) {			BUGPRINT("counter_offset != totalcnt");			return -EINVAL;		}		*n = ((struct ebt_entries *)e)->nentries;		*cnt = 0;		return 0;	}	/* a plain old entry, heh */	if (sizeof(struct ebt_entry) > e->watchers_offset ||	   e->watchers_offset > e->target_offset ||	   e->target_offset >= e->next_offset) {		BUGPRINT("entry offsets not in right order\n");		return -EINVAL;	}	/* this is not checked anywhere else */	if (e->next_offset - e->target_offset < sizeof(struct ebt_entry_target)) {		BUGPRINT("target size too small\n");		return -EINVAL;	}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -