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

📄 ebtables.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
	(*cnt)++;	(*totalcnt)++;	return 0;}struct ebt_cl_stack{	struct ebt_chainstack cs;	int from;	unsigned int hookmask;};/* * we need these positions to check that the jumps to a different part of the * entries is a jump to the beginning of a new chain. */static inline intebt_get_udc_positions(struct ebt_entry *e, struct ebt_table_info *newinfo,   unsigned int *n, struct ebt_cl_stack *udc){	int i;	/* we're only interested in chain starts */	if (e->bitmask)		return 0;	for (i = 0; i < NF_BR_NUMHOOKS; i++) {		if (newinfo->hook_entry[i] == (struct ebt_entries *)e)			break;	}	/* only care about udc */	if (i != NF_BR_NUMHOOKS)		return 0;	udc[*n].cs.chaininfo = (struct ebt_entries *)e;	/* these initialisations are depended on later in check_chainloops() */	udc[*n].cs.n = 0;	udc[*n].hookmask = 0;	(*n)++;	return 0;}static inline intebt_cleanup_match(struct ebt_entry_match *m, unsigned int *i){	if (i && (*i)-- == 0)		return 1;	if (m->u.match->destroy)		m->u.match->destroy(m->data, m->match_size);	module_put(m->u.match->me);	return 0;}static inline intebt_cleanup_watcher(struct ebt_entry_watcher *w, unsigned int *i){	if (i && (*i)-- == 0)		return 1;	if (w->u.watcher->destroy)		w->u.watcher->destroy(w->data, w->watcher_size);	module_put(w->u.watcher->me);	return 0;}static inline intebt_cleanup_entry(struct ebt_entry *e, unsigned int *cnt){	struct ebt_entry_target *t;	if (e->bitmask == 0)		return 0;	/* we're done */	if (cnt && (*cnt)-- == 0)		return 1;	EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, NULL);	EBT_MATCH_ITERATE(e, ebt_cleanup_match, NULL);	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);	if (t->u.target->destroy)		t->u.target->destroy(t->data, t->target_size);	module_put(t->u.target->me);	return 0;}static inline intebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo,   const char *name, unsigned int *cnt,   struct ebt_cl_stack *cl_s, unsigned int udc_cnt){	struct ebt_entry_target *t;	struct ebt_target *target;	unsigned int i, j, hook = 0, hookmask = 0;	size_t gap;	int ret;	/* don't mess with the struct ebt_entries */	if (e->bitmask == 0)		return 0;	if (e->bitmask & ~EBT_F_MASK) {		BUGPRINT("Unknown flag for bitmask\n");		return -EINVAL;	}	if (e->invflags & ~EBT_INV_MASK) {		BUGPRINT("Unknown flag for inv bitmask\n");		return -EINVAL;	}	if ( (e->bitmask & EBT_NOPROTO) && (e->bitmask & EBT_802_3) ) {		BUGPRINT("NOPROTO & 802_3 not allowed\n");		return -EINVAL;	}	/* what hook do we belong to? */	for (i = 0; i < NF_BR_NUMHOOKS; i++) {		if (!newinfo->hook_entry[i])			continue;		if ((char *)newinfo->hook_entry[i] < (char *)e)			hook = i;		else			break;	}	/* (1 << NF_BR_NUMHOOKS) tells the check functions the rule is on	   a base chain */	if (i < NF_BR_NUMHOOKS)		hookmask = (1 << hook) | (1 << NF_BR_NUMHOOKS);	else {		for (i = 0; i < udc_cnt; i++)			if ((char *)(cl_s[i].cs.chaininfo) > (char *)e)				break;		if (i == 0)			hookmask = (1 << hook) | (1 << NF_BR_NUMHOOKS);		else			hookmask = cl_s[i - 1].hookmask;	}	i = 0;	ret = EBT_MATCH_ITERATE(e, ebt_check_match, e, name, hookmask, &i);	if (ret != 0)		goto cleanup_matches;	j = 0;	ret = EBT_WATCHER_ITERATE(e, ebt_check_watcher, e, name, hookmask, &j);	if (ret != 0)		goto cleanup_watchers;	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);	gap = e->next_offset - e->target_offset;	target = find_target_lock(t->u.name, &ret, &ebt_mutex);	if (!target)		goto cleanup_watchers;	if (!try_module_get(target->me)) {		mutex_unlock(&ebt_mutex);		ret = -ENOENT;		goto cleanup_watchers;	}	mutex_unlock(&ebt_mutex);	t->u.target = target;	if (t->u.target == &ebt_standard_target) {		if (gap < sizeof(struct ebt_standard_target)) {			BUGPRINT("Standard target size too big\n");			ret = -EFAULT;			goto cleanup_watchers;		}		if (((struct ebt_standard_target *)t)->verdict <		   -NUM_STANDARD_TARGETS) {			BUGPRINT("Invalid standard target\n");			ret = -EFAULT;			goto cleanup_watchers;		}	} else if (t->target_size > gap - sizeof(struct ebt_entry_target) ||	   (t->u.target->check &&	   t->u.target->check(name, hookmask, e, t->data, t->target_size) != 0)){		module_put(t->u.target->me);		ret = -EFAULT;		goto cleanup_watchers;	}	(*cnt)++;	return 0;cleanup_watchers:	EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, &j);cleanup_matches:	EBT_MATCH_ITERATE(e, ebt_cleanup_match, &i);	return ret;}/* * checks for loops and sets the hook mask for udc * the hook mask for udc tells us from which base chains the udc can be * accessed. This mask is a parameter to the check() functions of the extensions */static int check_chainloops(struct ebt_entries *chain, struct ebt_cl_stack *cl_s,   unsigned int udc_cnt, unsigned int hooknr, char *base){	int i, chain_nr = -1, pos = 0, nentries = chain->nentries, verdict;	struct ebt_entry *e = (struct ebt_entry *)chain->data;	struct ebt_entry_target *t;	while (pos < nentries || chain_nr != -1) {		/* end of udc, go back one 'recursion' step */		if (pos == nentries) {			/* put back values of the time when this chain was called */			e = cl_s[chain_nr].cs.e;			if (cl_s[chain_nr].from != -1)				nentries =				cl_s[cl_s[chain_nr].from].cs.chaininfo->nentries;			else				nentries = chain->nentries;			pos = cl_s[chain_nr].cs.n;			/* make sure we won't see a loop that isn't one */			cl_s[chain_nr].cs.n = 0;			chain_nr = cl_s[chain_nr].from;			if (pos == nentries)				continue;		}		t = (struct ebt_entry_target *)		   (((char *)e) + e->target_offset);		if (strcmp(t->u.name, EBT_STANDARD_TARGET))			goto letscontinue;		if (e->target_offset + sizeof(struct ebt_standard_target) >		   e->next_offset) {			BUGPRINT("Standard target size too big\n");			return -1;		}		verdict = ((struct ebt_standard_target *)t)->verdict;		if (verdict >= 0) { /* jump to another chain */			struct ebt_entries *hlp2 =			   (struct ebt_entries *)(base + verdict);			for (i = 0; i < udc_cnt; i++)				if (hlp2 == cl_s[i].cs.chaininfo)					break;			/* bad destination or loop */			if (i == udc_cnt) {				BUGPRINT("bad destination\n");				return -1;			}			if (cl_s[i].cs.n) {				BUGPRINT("loop\n");				return -1;			}			if (cl_s[i].hookmask & (1 << hooknr))				goto letscontinue;			/* this can't be 0, so the loop test is correct */			cl_s[i].cs.n = pos + 1;			pos = 0;			cl_s[i].cs.e = ((void *)e + e->next_offset);			e = (struct ebt_entry *)(hlp2->data);			nentries = hlp2->nentries;			cl_s[i].from = chain_nr;			chain_nr = i;			/* this udc is accessible from the base chain for hooknr */			cl_s[i].hookmask |= (1 << hooknr);			continue;		}letscontinue:		e = (void *)e + e->next_offset;		pos++;	}	return 0;}/* do the parsing of the table/chains/entries/matches/watchers/targets, heh */static int translate_table(char *name, struct ebt_table_info *newinfo){	unsigned int i, j, k, udc_cnt;	int ret;	struct ebt_cl_stack *cl_s = NULL; /* used in the checking for chain loops */	i = 0;	while (i < NF_BR_NUMHOOKS && !newinfo->hook_entry[i])		i++;	if (i == NF_BR_NUMHOOKS) {		BUGPRINT("No valid hooks specified\n");		return -EINVAL;	}	if (newinfo->hook_entry[i] != (struct ebt_entries *)newinfo->entries) {		BUGPRINT("Chains don't start at beginning\n");		return -EINVAL;	}	/* make sure chains are ordered after each other in same order	   as their corresponding hooks */	for (j = i + 1; j < NF_BR_NUMHOOKS; j++) {		if (!newinfo->hook_entry[j])			continue;		if (newinfo->hook_entry[j] <= newinfo->hook_entry[i]) {			BUGPRINT("Hook order must be followed\n");			return -EINVAL;		}		i = j;	}	/* do some early checkings and initialize some things */	i = 0; /* holds the expected nr. of entries for the chain */	j = 0; /* holds the up to now counted entries for the chain */	k = 0; /* holds the total nr. of entries, should equal		  newinfo->nentries afterwards */	udc_cnt = 0; /* will hold the nr. of user defined chains (udc) */	ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,	   ebt_check_entry_size_and_hooks, newinfo,	   &i, &j, &k, &udc_cnt);	if (ret != 0)		return ret;	if (i != j) {		BUGPRINT("nentries does not equal the nr of entries in the "			 "(last) chain\n");		return -EINVAL;	}	if (k != newinfo->nentries) {		BUGPRINT("Total nentries is wrong\n");		return -EINVAL;	}	/* get the location of the udc, put them in an array	   while we're at it, allocate the chainstack */	if (udc_cnt) {		/* this will get free'd in do_replace()/ebt_register_table()		   if an error occurs */		newinfo->chainstack =			vmalloc(nr_cpu_ids * sizeof(*(newinfo->chainstack)));		if (!newinfo->chainstack)			return -ENOMEM;		for_each_possible_cpu(i) {			newinfo->chainstack[i] =			  vmalloc(udc_cnt * sizeof(*(newinfo->chainstack[0])));			if (!newinfo->chainstack[i]) {				while (i)					vfree(newinfo->chainstack[--i]);				vfree(newinfo->chainstack);				newinfo->chainstack = NULL;				return -ENOMEM;			}		}		cl_s = vmalloc(udc_cnt * sizeof(*cl_s));		if (!cl_s)			return -ENOMEM;		i = 0; /* the i'th udc */		EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,		   ebt_get_udc_positions, newinfo, &i, cl_s);		/* sanity check */		if (i != udc_cnt) {			BUGPRINT("i != udc_cnt\n");			vfree(cl_s);			return -EFAULT;		}	}	/* Check for loops */	for (i = 0; i < NF_BR_NUMHOOKS; i++)		if (newinfo->hook_entry[i])			if (check_chainloops(newinfo->hook_entry[i],			   cl_s, udc_cnt, i, newinfo->entries)) {				vfree(cl_s);				return -EINVAL;			}	/* we now know the following (along with E=mc²):	   - the nr of entries in each chain is right	   - the size of the allocated space is right	   - all valid hooks have a corresponding chain	   - there are no loops	   - wrong data can still be on the level of a single entry	   - could be there are jumps to places that are not the	     beginning of a chain. This can only occur in chains that	     are not accessible from any base chains, so we don't care. */	/* used to know what we need to clean up if something goes wrong */	i = 0;	ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,	   ebt_check_entry, newinfo, name, &i, cl_s, udc_cnt);	if (ret != 0) {		EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,		   ebt_cleanup_entry, &i);	}	vfree(cl_s);	return ret;}/* called under write_lock */static void get_counters(struct ebt_counter *oldcounters,   struct ebt_counter *counters, unsigned int nentries){	int i, cpu;	struct ebt_counter *counter_base;	/* counters of cpu 0 */	memcpy(counters, oldcounters,	       sizeof(struct ebt_counter) * nentries);	/* add other counters to those of cpu 0 */	for_each_possible_cpu(cpu) {		if (cpu == 0)			continue;		counter_base = COUNTER_BASE(oldcounters, nentries, cpu);		for (i = 0; i < nentries; i++) {			counters[i].pcnt += counter_base[i].pcnt;			counters[i].bcnt += counter_base[i].bcnt;		}	}}/* replace the table */static int do_replace(void __user *user, unsigned int len){	int ret, i, countersize;	struct ebt_table_info *newinfo;	struct ebt_replace tmp;	struct ebt_table *t;	struct ebt_counter *counterstmp = NULL;	/* used to be able to unlock earlier */	struct ebt_table_info *table;	if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)		return -EFAULT;	if (len != sizeof(tmp) + tmp.entries_size) {		BUGPRINT("Wrong len argument\n");		return -EINVAL;	}	if (tmp.entries_size == 0) {		BUGPRINT("Entries_size never zero\n");		return -EINVAL;	}	/* overflow check */	if (tmp.nentries >= ((INT_MAX - sizeof(struct ebt_table_info)) / NR_CPUS -			SMP_CACHE_BYTES) / sizeof(struct ebt_counter))		return -ENOMEM;	if (tmp.num_counters >= INT_MAX / sizeof(struct ebt_counter))		return -ENOMEM;	countersize = COUNTER_OFFSET(tmp.nentries) * nr_cpu_ids;	newinfo = vmalloc(sizeof(*newinfo) + countersize);	if (!newinfo)		return -ENOMEM;	if (countersize)		memset(newinfo->counters, 0, countersize);	newinfo->entries = vmalloc(tmp.entries_size);	if (!newinfo->entries) {		ret = -ENOMEM;		goto free_newinfo;	}	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;	}	/* the user wants counters back	   the check on the size is done later, when we have the lock */	if (tmp.num_counters) {		counterstmp = vmalloc(tmp.num_counters * sizeof(*counterstmp));		if (!counterstmp) {			ret = -ENOMEM;			goto free_entries;		}	}	else		counterstmp = NULL;	/* this can get initialized by translate_table() */	newinfo->chainstack = NULL;	ret = ebt_verify_pointers(&tmp, newinfo);	if (ret != 0)		goto free_counterstmp;	ret = translate_table(tmp.name, newinfo);	if (ret != 0)		goto free_counterstmp;	t = find_table_lock(tmp.name, &ret, &ebt_mutex);	if (!t) {		ret = -ENOENT;		goto free_iterate;	}	/* the table doesn't like it */	if (t->check && (ret = t->check(newinfo, tmp.valid_hooks)))		goto free_unlock;	if (tmp.num_counters && tmp.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 (tmp.num_counters)		get_counters(t->private->counters, counterstmp,		   t->private->nentries);	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 (tmp.num_counters &&	   copy_to_user(tmp.counters, counterstmp,	   tmp.num_counters * sizeof(struct ebt_counter))) {		BUGPRINT("Couldn't copy counters to userspace\n");		ret = -EFAULT;	}

⌨️ 快捷键说明

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