summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBart De Schuymer <bdschuym@pandora.be>2002-06-23 08:15:51 +0000
committerBart De Schuymer <bdschuym@pandora.be>2002-06-23 08:15:51 +0000
commit6578107e9bc618df787a14875cff07f298816392 (patch)
tree05700b5b024516b8e8e278bbb014b474f1f083b1
parent0ceadf9abe94b3774b0b1fa7af9d9d932af8c9c3 (diff)
base patch for user defined chains support
-rw-r--r--kernel/linux/include/linux/netfilter_bridge/ebt_nat.h4
-rw-r--r--kernel/linux/include/linux/netfilter_bridge/ebt_redirect.h4
-rw-r--r--kernel/linux/include/linux/netfilter_bridge/ebtables.h51
-rw-r--r--kernel/linux/net/bridge/netfilter/ebt_arp.c2
-rw-r--r--kernel/linux/net/bridge/netfilter/ebt_dnat.c8
-rw-r--r--kernel/linux/net/bridge/netfilter/ebt_ip.c2
-rw-r--r--kernel/linux/net/bridge/netfilter/ebt_log.c2
-rw-r--r--kernel/linux/net/bridge/netfilter/ebt_redirect.c6
-rw-r--r--kernel/linux/net/bridge/netfilter/ebt_snat.c4
-rw-r--r--kernel/linux/net/bridge/netfilter/ebt_vlan.c2
-rw-r--r--kernel/linux/net/bridge/netfilter/ebtable_broute.c5
-rw-r--r--kernel/linux/net/bridge/netfilter/ebtable_filter.c13
-rw-r--r--kernel/linux/net/bridge/netfilter/ebtable_nat.c9
-rw-r--r--kernel/linux/net/bridge/netfilter/ebtables.c304
-rw-r--r--userspace/ebtables2/include/ebtables_u.h24
15 files changed, 343 insertions, 97 deletions
diff --git a/kernel/linux/include/linux/netfilter_bridge/ebt_nat.h b/kernel/linux/include/linux/netfilter_bridge/ebt_nat.h
index 53c81d2..1dadb26 100644
--- a/kernel/linux/include/linux/netfilter_bridge/ebt_nat.h
+++ b/kernel/linux/include/linux/netfilter_bridge/ebt_nat.h
@@ -4,8 +4,8 @@
struct ebt_nat_info
{
unsigned char mac[ETH_ALEN];
- // EBT_ACCEPT, EBT_DROP or EBT_CONTINUE
- __u8 target;
+ // EBT_ACCEPT, EBT_DROP or EBT_CONTINUE or EBT_RETURN
+ int target;
};
#define EBT_SNAT_TARGET "snat"
#define EBT_DNAT_TARGET "dnat"
diff --git a/kernel/linux/include/linux/netfilter_bridge/ebt_redirect.h b/kernel/linux/include/linux/netfilter_bridge/ebt_redirect.h
index 82cd309..c741521 100644
--- a/kernel/linux/include/linux/netfilter_bridge/ebt_redirect.h
+++ b/kernel/linux/include/linux/netfilter_bridge/ebt_redirect.h
@@ -3,8 +3,8 @@
struct ebt_redirect_info
{
- // EBT_ACCEPT, EBT_DROP or EBT_CONTINUE
- __u8 target;
+ // EBT_ACCEPT, EBT_DROP or EBT_CONTINUE or EBT_RETURN
+ int target;
};
#define EBT_REDIRECT_TARGET "redirect"
diff --git a/kernel/linux/include/linux/netfilter_bridge/ebtables.h b/kernel/linux/include/linux/netfilter_bridge/ebtables.h
index f4f9e90..e3ea071 100644
--- a/kernel/linux/include/linux/netfilter_bridge/ebtables.h
+++ b/kernel/linux/include/linux/netfilter_bridge/ebtables.h
@@ -17,6 +17,7 @@
#include <linux/if_ether.h> // ETH_ALEN
#define EBT_TABLE_MAXNAMELEN 32
+#define EBT_CHAIN_MAXNAMELEN EBT_TABLE_MAXNAMELEN
#define EBT_FUNCTION_MAXNAMELEN EBT_TABLE_MAXNAMELEN
// [gs]etsockopt numbers
@@ -30,18 +31,29 @@
#define EBT_SO_GET_ENTRIES (EBT_SO_GET_INFO+1)
#define EBT_SO_GET_MAX (EBT_SO_GET_ENTRIES+1)
-#define EBT_ACCEPT 0
-#define EBT_DROP 1
-#define EBT_CONTINUE 2
-#define NUM_STANDARD_TARGETS 3
+// verdicts >0 are "branches"
+#define EBT_ACCEPT -1
+#define EBT_DROP -2
+#define EBT_CONTINUE -3
+#define EBT_RETURN -4
+#define NUM_STANDARD_TARGETS 4
+
+struct ebt_counter
+{
+ __u64 pcnt;
+};
struct ebt_entries {
// this field is always set to zero (including userspace).
// See EBT_ENTRY_OR_ENTRIES.
// Must be same size as ebt_entry.bitmask
__u32 distinguisher;
- // one standard (accept or drop) per hook
- __u8 policy;
+ // 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
__u32 nentries;
// entry list
@@ -76,11 +88,6 @@ struct ebt_entries {
#define EBT_INV_MASK (EBT_IPROTO | EBT_IIN | EBT_IOUT | EBT_ILOGICALIN \
| EBT_ILOGICALOUT | EBT_ISOURCE | EBT_IDEST)
-struct ebt_counter
-{
- __u64 pcnt;
-};
-
struct ebt_entry_match
{
union {
@@ -118,7 +125,7 @@ struct ebt_entry_target
struct ebt_standard_target
{
struct ebt_entry_target target;
- __u8 verdict;
+ int verdict;
};
// one entry
@@ -158,8 +165,6 @@ struct ebt_replace
unsigned int entries_size;
// start of the chains
struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
- // how many counters in front of it?
- unsigned int counter_entry[NF_BR_NUMHOOKS];
// nr of counters userspace expects back
unsigned int num_counters;
// where the kernel will put the old counters
@@ -178,7 +183,7 @@ struct ebt_match
const struct net_device *out, const void *matchdata,
unsigned int datalen, const struct ebt_counter *c);
// 0 == let it in
- int (*check)(const char *tablename, unsigned int hooknr,
+ int (*check)(const char *tablename, unsigned int hookmask,
const struct ebt_entry *e, void *matchdata, unsigned int datalen);
void (*destroy)(void *matchdata, unsigned int datalen);
struct module *me;
@@ -192,7 +197,7 @@ struct ebt_watcher
const struct net_device *out, const void *watcherdata,
unsigned int datalen, const struct ebt_counter *c);
// 0 == let it in
- int (*check)(const char *tablename, unsigned int hooknr,
+ int (*check)(const char *tablename, unsigned int hookmask,
const struct ebt_entry *e, void *watcherdata, unsigned int datalen);
void (*destroy)(void *watcherdata, unsigned int datalen);
struct module *me;
@@ -210,12 +215,20 @@ struct ebt_target
const void *targetdata,
unsigned int datalen);
// 0 == let it in
- int (*check)(const char *tablename, unsigned int hooknr,
+ int (*check)(const char *tablename, unsigned int hookmask,
const struct ebt_entry *e, void *targetdata, unsigned int datalen);
void (*destroy)(void *targetdata, unsigned int datalen);
struct module *me;
};
+// used for jumping from and into user defined chains (udc)
+struct ebt_chainstack
+{
+ struct ebt_entries *chaininfo; // pointer to chain data
+ struct ebt_entry *e; // pointer to entry data
+ unsigned int n; // n'th entry
+};
+
struct ebt_table_info
{
// total size of the entries
@@ -223,9 +236,9 @@ struct ebt_table_info
unsigned int nentries;
// pointers to the start of the chains
struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
- // how many counters in front of the counters bolonging to a chain
- unsigned int counter_entry[NF_BR_NUMHOOKS];
struct ebt_counter *counters;
+ // room to maintain the stack used for jumping from and into udc
+ struct ebt_chainstack *chainstack;
char *entries;
};
diff --git a/kernel/linux/net/bridge/netfilter/ebt_arp.c b/kernel/linux/net/bridge/netfilter/ebt_arp.c
index 44c65c4..8900a0c 100644
--- a/kernel/linux/net/bridge/netfilter/ebt_arp.c
+++ b/kernel/linux/net/bridge/netfilter/ebt_arp.c
@@ -68,7 +68,7 @@ static int ebt_filter_arp(const struct sk_buff *skb,
return 0;
}
-static int ebt_arp_check(const char *tablename, unsigned int hooknr,
+static int ebt_arp_check(const char *tablename, unsigned int hookmask,
const struct ebt_entry *e, void *data, unsigned int datalen)
{
struct ebt_arp_info *infostuff = (struct ebt_arp_info *) data;
diff --git a/kernel/linux/net/bridge/netfilter/ebt_dnat.c b/kernel/linux/net/bridge/netfilter/ebt_dnat.c
index 353b8c8..cfa2519 100644
--- a/kernel/linux/net/bridge/netfilter/ebt_dnat.c
+++ b/kernel/linux/net/bridge/netfilter/ebt_dnat.c
@@ -26,14 +26,14 @@ static __u8 ebt_target_dnat(struct sk_buff **pskb, unsigned int hooknr,
return infostuff->target;
}
-static int ebt_target_dnat_check(const char *tablename, unsigned int hooknr,
+static int ebt_target_dnat_check(const char *tablename, unsigned int hookmask,
const struct ebt_entry *e, void *data, unsigned int datalen)
{
struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
- if ( (strcmp(tablename, "nat") ||
- (hooknr != NF_BR_PRE_ROUTING && hooknr != NF_BR_LOCAL_OUT)) &&
- (strcmp(tablename, "broute") || hooknr != NF_BR_BROUTING) )
+ if ( (strcmp(tablename, "nat") ||
+ (hookmask & ~(1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT))) &&
+ (strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) )
return -EINVAL;
if (datalen != sizeof(struct ebt_nat_info))
return -EINVAL;
diff --git a/kernel/linux/net/bridge/netfilter/ebt_ip.c b/kernel/linux/net/bridge/netfilter/ebt_ip.c
index c34b1b5..e69ee0c 100644
--- a/kernel/linux/net/bridge/netfilter/ebt_ip.c
+++ b/kernel/linux/net/bridge/netfilter/ebt_ip.c
@@ -39,7 +39,7 @@ static int ebt_filter_ip(const struct sk_buff *skb,
return 0;
}
-static int ebt_ip_check(const char *tablename, unsigned int hooknr,
+static int ebt_ip_check(const char *tablename, unsigned int hookmask,
const struct ebt_entry *e, void *data, unsigned int datalen)
{
struct ebt_ip_info *infostuff = (struct ebt_ip_info *) data;
diff --git a/kernel/linux/net/bridge/netfilter/ebt_log.c b/kernel/linux/net/bridge/netfilter/ebt_log.c
index e7f0506..7d7ed83 100644
--- a/kernel/linux/net/bridge/netfilter/ebt_log.c
+++ b/kernel/linux/net/bridge/netfilter/ebt_log.c
@@ -17,7 +17,7 @@
static spinlock_t ebt_log_lock = SPIN_LOCK_UNLOCKED;
-static int ebt_log_check(const char *tablename, unsigned int hooknr,
+static int ebt_log_check(const char *tablename, unsigned int hookmask,
const struct ebt_entry *e, void *data, unsigned int datalen)
{
struct ebt_log_info *loginfo = (struct ebt_log_info *)data;
diff --git a/kernel/linux/net/bridge/netfilter/ebt_redirect.c b/kernel/linux/net/bridge/netfilter/ebt_redirect.c
index c26d57b..35b838e 100644
--- a/kernel/linux/net/bridge/netfilter/ebt_redirect.c
+++ b/kernel/linux/net/bridge/netfilter/ebt_redirect.c
@@ -28,13 +28,13 @@ static __u8 ebt_target_redirect(struct sk_buff **pskb, unsigned int hooknr,
return infostuff->target;
}
-static int ebt_target_redirect_check(const char *tablename, unsigned int hooknr,
+static int ebt_target_redirect_check(const char *tablename, unsigned int hookmask,
const struct ebt_entry *e, void *data, unsigned int datalen)
{
struct ebt_redirect_info *infostuff = (struct ebt_redirect_info *) data;
- if ( (strcmp(tablename, "nat") || hooknr != NF_BR_PRE_ROUTING) &&
- (strcmp(tablename, "broute") || hooknr != NF_BR_BROUTING) )
+ if ( (strcmp(tablename, "nat") || hookmask & ~(1 << NF_BR_PRE_ROUTING)) &&
+ (strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) )
return -EINVAL;
if (datalen != sizeof(struct ebt_redirect_info))
return -EINVAL;
diff --git a/kernel/linux/net/bridge/netfilter/ebt_snat.c b/kernel/linux/net/bridge/netfilter/ebt_snat.c
index cfe7e18..9e90114 100644
--- a/kernel/linux/net/bridge/netfilter/ebt_snat.c
+++ b/kernel/linux/net/bridge/netfilter/ebt_snat.c
@@ -26,7 +26,7 @@ static __u8 ebt_target_snat(struct sk_buff **pskb, unsigned int hooknr,
return infostuff->target;
}
-static int ebt_target_snat_check(const char *tablename, unsigned int hooknr,
+static int ebt_target_snat_check(const char *tablename, unsigned int hookmask,
const struct ebt_entry *e, void *data, unsigned int datalen)
{
struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
@@ -35,7 +35,7 @@ static int ebt_target_snat_check(const char *tablename, unsigned int hooknr,
return -EINVAL;
if (datalen != sizeof(struct ebt_nat_info))
return -EINVAL;
- if (hooknr != NF_BR_POST_ROUTING)
+ if (hookmask & ~(1 << NF_BR_POST_ROUTING))
return -EINVAL;
if (infostuff->target >= NUM_STANDARD_TARGETS)
return -EINVAL;
diff --git a/kernel/linux/net/bridge/netfilter/ebt_vlan.c b/kernel/linux/net/bridge/netfilter/ebt_vlan.c
index f7e2c16..b7fcb3f 100644
--- a/kernel/linux/net/bridge/netfilter/ebt_vlan.c
+++ b/kernel/linux/net/bridge/netfilter/ebt_vlan.c
@@ -99,7 +99,7 @@ static int ebt_filter_vlan (const struct sk_buff *skb,
* ebt_vlan_check() is called when userspace delivers the table to the kernel,
* * it is called to check that userspace doesn't give a bad table.
*/
-static int ebt_vlan_check (const char *tablename, unsigned int hooknr,
+static int ebt_vlan_check (const char *tablename, unsigned int hookmask,
const struct ebt_entry *e, void *data,
unsigned int datalen)
{
diff --git a/kernel/linux/net/bridge/netfilter/ebtable_broute.c b/kernel/linux/net/bridge/netfilter/ebtable_broute.c
index ce880a2..200ec41 100644
--- a/kernel/linux/net/bridge/netfilter/ebtable_broute.c
+++ b/kernel/linux/net/bridge/netfilter/ebtable_broute.c
@@ -21,13 +21,12 @@
// EBT_ACCEPT means the frame will be bridged
// EBT_DROP means the frame will be routed
static struct ebt_entries initial_chain =
- {0, EBT_ACCEPT, 0};
+ {0, "BROUTE", 0, EBT_ACCEPT, 0};
static struct ebt_replace initial_table =
{
"broute", 1 << NF_BR_BROUTING, 0, sizeof(struct ebt_entries),
- { [NF_BR_BROUTING]&initial_chain}, {},
- 0, NULL, (char *)&initial_chain
+ { [NF_BR_BROUTING]&initial_chain}, 0, NULL, (char *)&initial_chain
};
static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
diff --git a/kernel/linux/net/bridge/netfilter/ebtable_filter.c b/kernel/linux/net/bridge/netfilter/ebtable_filter.c
index e16f696..8b72f8e 100644
--- a/kernel/linux/net/bridge/netfilter/ebtable_filter.c
+++ b/kernel/linux/net/bridge/netfilter/ebtable_filter.c
@@ -17,17 +17,16 @@
static struct ebt_entries initial_chains[] =
{
- {0, EBT_ACCEPT, 0},
- {0, EBT_ACCEPT, 0},
- {0, EBT_ACCEPT, 0}
+ {0, "INPUT", 0, EBT_ACCEPT, 0},
+ {0, "FORWARD", 0, EBT_ACCEPT, 0},
+ {0, "OUTPUT", 0, EBT_ACCEPT, 0}
};
-static struct ebt_replace initial_table =
-{
+static struct ebt_replace initial_table =
+{
"filter", FILTER_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
{ [NF_BR_LOCAL_IN]&initial_chains[0], [NF_BR_FORWARD]&initial_chains[1],
- [NF_BR_LOCAL_OUT]&initial_chains[2] },{},
- 0, NULL, (char *)initial_chains
+ [NF_BR_LOCAL_OUT]&initial_chains[2] }, 0, NULL, (char *)initial_chains
};
static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
diff --git a/kernel/linux/net/bridge/netfilter/ebtable_nat.c b/kernel/linux/net/bridge/netfilter/ebtable_nat.c
index b99db09..b093df3 100644
--- a/kernel/linux/net/bridge/netfilter/ebtable_nat.c
+++ b/kernel/linux/net/bridge/netfilter/ebtable_nat.c
@@ -17,17 +17,16 @@
static struct ebt_entries initial_chains[] =
{
- {0, EBT_ACCEPT, 0},
- {0, EBT_ACCEPT, 0},
- {0, EBT_ACCEPT, 0}
+ {0, "PREROUTING", 0, EBT_ACCEPT, 0},
+ {0, "OUTPUT", 0, EBT_ACCEPT, 0},
+ {0, "POSTROUTING", 0, EBT_ACCEPT, 0}
};
static struct ebt_replace initial_table =
{
"nat", NAT_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
{ [NF_BR_PRE_ROUTING]&initial_chains[0], [NF_BR_LOCAL_OUT]&initial_chains[1],
- [NF_BR_POST_ROUTING]&initial_chains[2] }, {},
- 0, NULL, (char *)initial_chains
+ [NF_BR_POST_ROUTING]&initial_chains[2] }, 0, NULL, (char *)initial_chains
};
static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
diff --git a/kernel/linux/net/bridge/netfilter/ebtables.c b/kernel/linux/net/bridge/netfilter/ebtables.c
index 34f5dee..9c62580 100644
--- a/kernel/linux/net/bridge/netfilter/ebtables.c
+++ b/kernel/linux/net/bridge/netfilter/ebtables.c
@@ -90,7 +90,7 @@ static inline int ebt_dev_check(char *entry, const struct net_device *device)
if (!device)
return 1;
return strncmp(entry, device->name, IFNAMSIZ);
-}
+}
// Do some firewalling
unsigned int ebt_do_table (unsigned int hook, struct sk_buff **pskb,
@@ -101,16 +101,21 @@ unsigned int ebt_do_table (unsigned int hook, struct sk_buff **pskb,
struct ebt_entry *point;
struct ebt_counter *counter_base;
struct ebt_entry_target *t;
- __u8 verdict;
+ int verdict, sp = 0;
+ struct ebt_chainstack *cs;
+ struct ebt_entries *chaininfo;
read_lock_bh(&table->lock);
+ cs = table->private->chainstack;
+ chaininfo = table->private->hook_entry[hook];
nentries = table->private->hook_entry[hook]->nentries;
point = (struct ebt_entry *)(table->private->hook_entry[hook]->data);
- counter_base = table->private->counters +
- cpu_number_map(smp_processor_id()) * table->private->nentries +
- table->private->counter_entry[hook];
+ #define cb_base table->private->counters + \
+ cpu_number_map(smp_processor_id()) * table->private->nentries
+ counter_base = cb_base + table->private->hook_entry[hook]->counter_offset;
#define FWINV(bool,invflg) ((bool) ^ !!(point->invflags & invflg))
- for (i = 0; i < nentries; i++) {
+ i = 0;
+ while (i < nentries) {
if ( ( point->bitmask & EBT_NOPROTO ||
FWINV(point->ethproto == ((**pskb).mac.ethernet)->h_proto,
EBT_IPROTO)
@@ -175,20 +180,58 @@ unsigned int ebt_do_table (unsigned int hook, struct sk_buff **pskb,
read_unlock_bh(&table->lock);
return NF_DROP;
}
- if (verdict != EBT_CONTINUE) {
+ if (verdict == EBT_RETURN) {
+letsreturn:
+ if (sp == 0) {
+ BUGPRINT("return target on base chain\n");
+ // No oopsen, hopefully
+ return NF_DROP;
+ }
+ 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;
+ if (verdict < 0) {
+ BUGPRINT("bogus standard verdict\n");
read_unlock_bh(&table->lock);
- BUGPRINT("Illegal target while "
- "firewalling!!\n");
- // Try not to get oopsen
return NF_DROP;
}
+ // 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 *) (((char *)chaininfo) + verdict);
+ if (chaininfo->distinguisher) {
+ BUGPRINT("jump to non-chain\n");
+ read_unlock_bh(&table->lock);
+ return NF_DROP;
+ }
+ 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++;
}
- if ( table->private->hook_entry[hook]->policy == EBT_ACCEPT ) {
+ // I actually like this :)
+ if (chaininfo->policy == EBT_RETURN)
+ goto letsreturn;
+ if (chaininfo->policy == EBT_ACCEPT) {
read_unlock_bh(&table->lock);
return NF_ACCEPT;
}
@@ -324,7 +367,7 @@ static inline int
ebt_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 valid_hooks)
+ unsigned int *totalcnt, unsigned int *udc_cnt, unsigned int valid_hooks)
{
int i;
@@ -336,7 +379,8 @@ ebt_check_entry_size_and_hooks(struct ebt_entry *e,
break;
}
// beginning of a new chain
- if (i != NF_BR_NUMHOOKS) {
+ // if i == NF_BR_NUMHOOKS it must be a user defined chain
+ if (i != NF_BR_NUMHOOKS || !(e->bitmask & EBT_ENTRY_OR_ENTRIES)) {
if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) != 0) {
// we make userspace set this right,
// so there is no misunderstanding
@@ -359,13 +403,23 @@ ebt_check_entry_size_and_hooks(struct ebt_entry *e,
}
if (((struct ebt_entries *)e)->policy != EBT_DROP &&
((struct ebt_entries *)e)->policy != EBT_ACCEPT) {
- BUGPRINT("bad policy\n");
+ // 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)++;
+ else
+ newinfo->hook_entry[i] = (struct ebt_entries *)e;
+ if (((struct ebt_entries *)e)->counter_offset != *totalcnt) {
+ BUGPRINT("counter_offset != totalcnt");
return -EINVAL;
}
*n = ((struct ebt_entries *)e)->nentries;
*cnt = 0;
- newinfo->hook_entry[i] = (struct ebt_entries *)e;
- newinfo->counter_entry[i] = *totalcnt;
return 0;
}
// a plain old entry, heh
@@ -375,21 +429,55 @@ ebt_check_entry_size_and_hooks(struct ebt_entry *e,
BUGPRINT("entry offsets not in right order\n");
return -EINVAL;
}
- if (((char *)e) + e->next_offset - newinfo->entries > limit - base) {
- BUGPRINT("entry offsets point too far\n");
+ // 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;
}
- if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0) {
- BUGPRINT("EBT_ENTRY_OR_ENTRIES should be set in "
- "bitmask for an entry\n");
- return -EINVAL;
- }
(*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 int
+ebt_get_udc_positions(struct ebt_entry *e, struct ebt_table_info *newinfo,
+ struct ebt_entries **hook_entries, unsigned int *n, unsigned int valid_hooks,
+ struct ebt_cl_stack *udc)
+{
+ int i;
+
+ // we're only interested in chain starts
+ if (e->bitmask & EBT_ENTRY_OR_ENTRIES)
+ return 0;
+ for (i = 0; i < NF_BR_NUMHOOKS; i++) {
+ if ((valid_hooks & (1 << i)) == 0)
+ continue;
+ 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 int
ebt_cleanup_match(struct ebt_entry_match *m, unsigned int *i)
{
@@ -418,11 +506,12 @@ ebt_cleanup_watcher(struct ebt_entry_watcher *w, unsigned int *i)
static inline int
ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo,
- const char *name, unsigned int *cnt, unsigned int valid_hooks)
+ const char *name, unsigned int *cnt, unsigned int valid_hooks,
+ 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;
+ unsigned int i, j, hook = 0, hookmask = 0;
int ret;
// Don't mess with the struct ebt_entries
@@ -454,18 +543,34 @@ ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo,
else
break;
}
+ if (i < NF_BR_NUMHOOKS)
+ hookmask = (1 << hook);
+ else {
+ for (i = 0; i < udc_cnt; i++) {
+ if ((char *)(cl_s[i].cs.chaininfo) < (char *)e)
+ hook = i;
+ else
+ break;
+ }
+ // sanity check
+ if (i == udc_cnt) {
+ BUGPRINT("serious trouble\n");
+ return -EFAULT;
+ }
+ hookmask = cl_s[i].hookmask;
+ }
i = 0;
- ret = EBT_MATCH_ITERATE(e, ebt_check_match, e, name, hook, &i);
+ 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, hook, &j);
+ 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);
t->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
target = find_target_lock(t->u.name, &ret, &ebt_mutex);
- if (!target)
+ if (!target)
goto cleanup_watchers;
if (target->me)
__MOD_INC_USE_COUNT(target->me);
@@ -479,14 +584,14 @@ ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo,
ret = -EFAULT;
goto cleanup_watchers;
}
- if (((struct ebt_standard_target *)t)->verdict >=
- NUM_STANDARD_TARGETS) {
+ if (((struct ebt_standard_target *)t)->verdict <
+ -NUM_STANDARD_TARGETS) {
BUGPRINT("Invalid standard target\n");
ret = -EFAULT;
goto cleanup_watchers;
}
} else if (t->u.target->check &&
- t->u.target->check(name, hook, e, t->data,
+ t->u.target->check(name, hookmask, e, t->data,
t->target_size) != 0) {
if (t->u.target->me)
__MOD_DEC_USE_COUNT(t->u.target->me);
@@ -523,12 +628,78 @@ ebt_cleanup_entry(struct ebt_entry *e, unsigned int *cnt)
return 0;
}
+// 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
+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;
+ nentries = cl_s[cl_s[chain_nr].from].cs.chaininfo->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;
+ }
+ t = (struct ebt_entry_target *)
+ (((char *)e) + e->target_offset);
+ t->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
+ 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;
+ }
+ 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(struct ebt_replace *repl,
struct ebt_table_info *newinfo)
{
- unsigned int i, j, k;
+ 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 && !(repl->valid_hooks & (1 << i)))
@@ -553,10 +724,8 @@ static int translate_table(struct ebt_replace *repl,
i = j;
}
- for (i = 0; i < NF_BR_NUMHOOKS; i++) {
+ for (i = 0; i < NF_BR_NUMHOOKS; i++)
newinfo->hook_entry[i] = NULL;
- newinfo->counter_entry[i] = 0;
- }
newinfo->entries_size = repl->entries_size;
newinfo->nentries = repl->nentries;
@@ -566,10 +735,11 @@ static int translate_table(struct ebt_replace *repl,
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, repl->entries,
repl->entries + repl->entries_size, repl->hook_entry, &i, &j, &k,
- repl->valid_hooks);
+ &udc_cnt, repl->valid_hooks);
if (ret != 0)
return ret;
@@ -587,22 +757,70 @@ static int translate_table(struct ebt_replace *repl,
// check if all valid hooks have a chain
for (i = 0; i < NF_BR_NUMHOOKS; i++) {
if (newinfo->hook_entry[i] == NULL &&
- (repl->valid_hooks & (1 << i))){
+ (repl->valid_hooks & (1 << i))) {
BUGPRINT("Valid hook without chain\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 = (struct ebt_chainstack *)
+ vmalloc(udc_cnt * sizeof(struct ebt_chainstack));
+ if (!newinfo->chainstack)
+ return -ENOMEM;
+ cl_s = (struct ebt_cl_stack *)
+ vmalloc(udc_cnt * sizeof(struct ebt_cl_stack));
+ if (!cl_s)
+ return -ENOMEM;
+ i = 0; // the i'th udc
+ EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
+ ebt_get_udc_positions, newinfo, repl->hook_entry, &i,
+ repl->valid_hooks, 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 (repl->valid_hooks & (1 << i))
+ if (check_chainloops(newinfo->hook_entry[i],
+ cl_s, udc_cnt, i, newinfo->entries)) {
+ if (cl_s)
+ 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.
+
// we just don't trust anything
repl->name[EBT_TABLE_MAXNAMELEN - 1] = '\0';
// 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, repl->name, &i, repl->valid_hooks);
+ ebt_check_entry, newinfo, repl->name, &i, repl->valid_hooks,
+ cl_s, udc_cnt);
if (ret != 0) {
EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
ebt_cleanup_entry, &i);
}
+ if (cl_s)
+ vfree(cl_s);
return ret;
}
@@ -690,6 +908,8 @@ static int do_replace(void *user, unsigned int len)
else
counterstmp = NULL;
+ // this can get initialized by translate_table()
+ newinfo->chainstack = NULL;
ret = translate_table(&tmp, newinfo);
if (ret != 0)
@@ -752,6 +972,9 @@ free_unlock:
free_counterstmp:
if (counterstmp)
vfree(counterstmp);
+ // can be initialized in translate_table()
+ if (newinfo->chainstack)
+ vfree(newinfo->chainstack);
free_entries:
if (newinfo->entries)
vfree(newinfo->entries);
@@ -877,6 +1100,7 @@ int ebt_register_table(struct ebt_table *table)
newinfo->counters = NULL;
// fill in newinfo and parse the entries
+ newinfo->chainstack = NULL;
ret = translate_table(table->table, newinfo);
if (ret != 0) {
BUGPRINT("Translate_table failed\n");
@@ -909,6 +1133,8 @@ free_unlock:
free_counters:
if (newinfo->counters)
vfree(newinfo->counters);
+ if (newinfo->chainstack)
+ vfree(newinfo->chainstack);
free_entries:
vfree(newinfo->entries);
free_newinfo:
@@ -1091,8 +1317,6 @@ static int copy_everything_to_user(struct ebt_table *t, void *user, int *len)
return -EFAULT;
}
// make userspace's life easier
- memcpy(tmp.counter_entry, info->counter_entry,
- NF_BR_NUMHOOKS * sizeof(int));
memcpy(tmp.hook_entry, info->hook_entry,
NF_BR_NUMHOOKS * sizeof(struct ebt_entries *));
for (i = 0; i < NF_BR_NUMHOOKS; i++)
diff --git a/userspace/ebtables2/include/ebtables_u.h b/userspace/ebtables2/include/ebtables_u.h
index 6b23fa3..4c4168b 100644
--- a/userspace/ebtables2/include/ebtables_u.h
+++ b/userspace/ebtables2/include/ebtables_u.h
@@ -28,11 +28,23 @@
struct ebt_u_entries
{
- __u8 policy;
+ int policy;
__u32 nentries;
+ // counter offset for this chain
+ unsigned int counter_offset;
+ // used for udc
+ unsigned int hook_mask;
+ char name[EBT_CHAIN_MAXNAMELEN];
struct ebt_u_entry *entries;
};
+struct ebt_u_chain_list
+{
+ struct ebt_u_entries *udc;
+ struct ebt_u_chain_list *next;
+ // this is only used internally, in communications.c
+ char *kernel_start;
+};
struct ebt_u_replace
{
@@ -41,8 +53,8 @@ struct ebt_u_replace
// nr of rules in the table
unsigned int nentries;
struct ebt_u_entries *hook_entry[NF_BR_NUMHOOKS];
- // how many counters in front of it?
- unsigned int counter_entry[NF_BR_NUMHOOKS];
+ // user defined chains (udc) list
+ struct ebt_u_chain_list *udc;
// nr of counters userspace expects back
unsigned int num_counters;
// where the kernel will put the old counters
@@ -107,7 +119,7 @@ struct ebt_u_match
struct ebt_entry_match **match);
void (*final_check)(const struct ebt_u_entry *entry,
const struct ebt_entry_match *match,
- const char *name, unsigned int hook);
+ const char *name, unsigned int hook_mask);
void (*print)(const struct ebt_u_entry *entry,
const struct ebt_entry_match *match);
int (*compare)(const struct ebt_entry_match *m1,
@@ -134,7 +146,7 @@ struct ebt_u_watcher
struct ebt_entry_watcher **watcher);
void (*final_check)(const struct ebt_u_entry *entry,
const struct ebt_entry_watcher *watch, const char *name,
- unsigned int hook);
+ unsigned int hook_mask);
void (*print)(const struct ebt_u_entry *entry,
const struct ebt_entry_watcher *watcher);
int (*compare)(const struct ebt_entry_watcher *w1,
@@ -158,7 +170,7 @@ struct ebt_u_target
struct ebt_entry_target **target);
void (*final_check)(const struct ebt_u_entry *entry,
const struct ebt_entry_target *target, const char *name,
- unsigned int hook);
+ unsigned int hook_mask);
void (*print)(const struct ebt_u_entry *entry,
const struct ebt_entry_target *target);
int (*compare)(const struct ebt_entry_target *t1,