📄 ebtables.c
字号:
/* * 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/sched.h>#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 <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"/* list_named_find */#define ASSERT_READ_LOCK(x)#define ASSERT_WRITE_LOCK(x)#include <linux/netfilter_ipv4/listhelp.h>#if 0/* use this for remote debugging * Copyright (C) 1998 by Ori Pomerantz * Print the string to the appropriate tty, the one * the current task uses */static void print_string(char *str){ struct tty_struct *my_tty; /* The tty for the current task */ my_tty = current->signal->tty; if (my_tty != NULL) { my_tty->driver->write(my_tty, 0, str, strlen(str)); my_tty->driver->write(my_tty, 0, "\015\012", 2); }}#define BUGPRINT(args) print_string(args);#else#define BUGPRINT(format, args...) printk("kernel msg: ebtables bug: please "\ "report to author: "format, ## args)/* #define BUGPRINT(format, args...) */#endif#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 DECLARE_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; 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 **pskb, 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(*pskb), in, out)) goto letscontinue; if (EBT_MATCH_ITERATE(point, ebt_do_match, *pskb, in, out) != 0) goto letscontinue; /* increase counter */ (*(counter_base + i)).pcnt++; (*(counter_base + i)).bcnt+=(**pskb).len; /* these should only watch: not modify, nor tell us what to do with the packet */ EBT_WATCHER_ITERATE(point, ebt_do_watcher, *pskb, 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(pskb, 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 semaphore *mutex){ void *ret; *error = down_interruptible(mutex); if (*error != 0) return NULL; ret = list_named_find(head, name); if (!ret) { *error = -ENOENT; up(mutex); } return ret;}#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 semaphore *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 semaphore *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 semaphore *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 semaphore *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 semaphore *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; int ret; if (((char *)m) + m->match_size + sizeof(struct ebt_entry_match) > ((char *)e) + e->watchers_offset) 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)) { up(&ebt_mutex); return -ENOENT; } up(&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; int ret; if (((char *)w) + w->watcher_size + sizeof(struct ebt_entry_watcher) > ((char *)e) + e->target_offset) 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)) { up(&ebt_mutex); return -ENOENT; } up(&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;}/* * 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, char *base, char *limit, struct ebt_entries **hook_entries, unsigned int *n, unsigned int *cnt, unsigned int *totalcnt, unsigned int *udc_cnt, unsigned int valid_hooks){ int i; for (i = 0; i < NF_BR_NUMHOOKS; i++) { if ((valid_hooks & (1 << i)) == 0) continue; if ( (char *)hook_entries[i] - base == (char *)e - newinfo->entries) break; } /* beginning of a new chain
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -