aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv4/netfilter/ip_tables.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv4/netfilter/ip_tables.c')
-rw-r--r--net/ipv4/netfilter/ip_tables.c260
1 files changed, 115 insertions, 145 deletions
diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c
index b29c66df8d1f..63958f3394a5 100644
--- a/net/ipv4/netfilter/ip_tables.c
+++ b/net/ipv4/netfilter/ip_tables.c
@@ -39,24 +39,19 @@ MODULE_DESCRIPTION("IPv4 packet filter");
/*#define DEBUG_IP_FIREWALL_USER*/
#ifdef DEBUG_IP_FIREWALL
-#define dprintf(format, args...) printk(format , ## args)
+#define dprintf(format, args...) pr_info(format , ## args)
#else
#define dprintf(format, args...)
#endif
#ifdef DEBUG_IP_FIREWALL_USER
-#define duprintf(format, args...) printk(format , ## args)
+#define duprintf(format, args...) pr_info(format , ## args)
#else
#define duprintf(format, args...)
#endif
#ifdef CONFIG_NETFILTER_DEBUG
-#define IP_NF_ASSERT(x) \
-do { \
- if (!(x)) \
- printk("IP_NF_ASSERT: %s:%s:%u\n", \
- __func__, __FILE__, __LINE__); \
-} while(0)
+#define IP_NF_ASSERT(x) WARN_ON(!(x))
#else
#define IP_NF_ASSERT(x)
#endif
@@ -165,30 +160,14 @@ ip_checkentry(const struct ipt_ip *ip)
}
static unsigned int
-ipt_error(struct sk_buff *skb, const struct xt_target_param *par)
+ipt_error(struct sk_buff *skb, const struct xt_action_param *par)
{
if (net_ratelimit())
- printk("ip_tables: error: `%s'\n",
- (const char *)par->targinfo);
+ pr_info("error: `%s'\n", (const char *)par->targinfo);
return NF_DROP;
}
-/* Performance critical - called for every packet */
-static inline bool
-do_match(const struct ipt_entry_match *m, const struct sk_buff *skb,
- struct xt_match_param *par)
-{
- par->match = m->u.kernel.match;
- par->matchinfo = m->data;
-
- /* Stop iteration if it doesn't match */
- if (!m->u.kernel.match->match(skb, par))
- return true;
- else
- return false;
-}
-
/* Performance critical */
static inline struct ipt_entry *
get_entry(const void *base, unsigned int offset)
@@ -322,19 +301,16 @@ ipt_do_table(struct sk_buff *skb,
const struct net_device *out,
struct xt_table *table)
{
-#define tb_comefrom ((struct ipt_entry *)table_base)->comefrom
-
static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long))));
const struct iphdr *ip;
- bool hotdrop = false;
/* Initializing verdict to NF_DROP keeps gcc happy. */
unsigned int verdict = NF_DROP;
const char *indev, *outdev;
const void *table_base;
- struct ipt_entry *e, *back;
+ struct ipt_entry *e, **jumpstack;
+ unsigned int *stackptr, origptr, cpu;
const struct xt_table_info *private;
- struct xt_match_param mtpar;
- struct xt_target_param tgpar;
+ struct xt_action_param acpar;
/* Initialization */
ip = ip_hdr(skb);
@@ -346,40 +322,47 @@ ipt_do_table(struct sk_buff *skb,
* things we don't know, ie. tcp syn flag or ports). If the
* rule is also a fragment-specific rule, non-fragments won't
* match it. */
- mtpar.fragoff = ntohs(ip->frag_off) & IP_OFFSET;
- mtpar.thoff = ip_hdrlen(skb);
- mtpar.hotdrop = &hotdrop;
- mtpar.in = tgpar.in = in;
- mtpar.out = tgpar.out = out;
- mtpar.family = tgpar.family = NFPROTO_IPV4;
- mtpar.hooknum = tgpar.hooknum = hook;
+ acpar.fragoff = ntohs(ip->frag_off) & IP_OFFSET;
+ acpar.thoff = ip_hdrlen(skb);
+ acpar.hotdrop = false;
+ acpar.in = in;
+ acpar.out = out;
+ acpar.family = NFPROTO_IPV4;
+ acpar.hooknum = hook;
IP_NF_ASSERT(table->valid_hooks & (1 << hook));
xt_info_rdlock_bh();
private = table->private;
- table_base = private->entries[smp_processor_id()];
+ cpu = smp_processor_id();
+ table_base = private->entries[cpu];
+ jumpstack = (struct ipt_entry **)private->jumpstack[cpu];
+ stackptr = &private->stackptr[cpu];
+ origptr = *stackptr;
e = get_entry(table_base, private->hook_entry[hook]);
- /* For return from builtin chain */
- back = get_entry(table_base, private->underflow[hook]);
+ pr_debug("Entering %s(hook %u); sp at %u (UF %p)\n",
+ table->name, hook, origptr,
+ get_entry(table_base, private->underflow[hook]));
do {
const struct ipt_entry_target *t;
const struct xt_entry_match *ematch;
IP_NF_ASSERT(e);
- IP_NF_ASSERT(back);
if (!ip_packet_match(ip, indev, outdev,
- &e->ip, mtpar.fragoff)) {
+ &e->ip, acpar.fragoff)) {
no_match:
e = ipt_next_entry(e);
continue;
}
- xt_ematch_foreach(ematch, e)
- if (do_match(ematch, skb, &mtpar) != 0)
+ xt_ematch_foreach(ematch, e) {
+ acpar.match = ematch->u.kernel.match;
+ acpar.matchinfo = ematch->data;
+ if (!acpar.match->match(skb, &acpar))
goto no_match;
+ }
ADD_COUNTER(e->counters, ntohs(ip->tot_len), 1);
@@ -404,41 +387,38 @@ ipt_do_table(struct sk_buff *skb,
verdict = (unsigned)(-v) - 1;
break;
}
- e = back;
- back = get_entry(table_base, back->comefrom);
+ if (*stackptr == 0) {
+ e = get_entry(table_base,
+ private->underflow[hook]);
+ pr_debug("Underflow (this is normal) "
+ "to %p\n", e);
+ } else {
+ e = jumpstack[--*stackptr];
+ pr_debug("Pulled %p out from pos %u\n",
+ e, *stackptr);
+ e = ipt_next_entry(e);
+ }
continue;
}
if (table_base + v != ipt_next_entry(e) &&
!(e->ip.flags & IPT_F_GOTO)) {
- /* Save old back ptr in next entry */
- struct ipt_entry *next = ipt_next_entry(e);
- next->comefrom = (void *)back - table_base;
- /* set back pointer to next entry */
- back = next;
+ if (*stackptr >= private->stacksize) {
+ verdict = NF_DROP;
+ break;
+ }
+ jumpstack[(*stackptr)++] = e;
+ pr_debug("Pushed %p into pos %u\n",
+ e, *stackptr - 1);
}
e = get_entry(table_base, v);
continue;
}
- /* Targets which reenter must return
- abs. verdicts */
- tgpar.target = t->u.kernel.target;
- tgpar.targinfo = t->data;
-
+ acpar.target = t->u.kernel.target;
+ acpar.targinfo = t->data;
-#ifdef CONFIG_NETFILTER_DEBUG
- tb_comefrom = 0xeeeeeeec;
-#endif
- verdict = t->u.kernel.target->target(skb, &tgpar);
-#ifdef CONFIG_NETFILTER_DEBUG
- if (tb_comefrom != 0xeeeeeeec && verdict == IPT_CONTINUE) {
- printk("Target %s reentered!\n",
- t->u.kernel.target->name);
- verdict = NF_DROP;
- }
- tb_comefrom = 0x57acc001;
-#endif
+ verdict = t->u.kernel.target->target(skb, &acpar);
/* Target might have changed stuff. */
ip = ip_hdr(skb);
if (verdict == IPT_CONTINUE)
@@ -446,18 +426,18 @@ ipt_do_table(struct sk_buff *skb,
else
/* Verdict */
break;
- } while (!hotdrop);
+ } while (!acpar.hotdrop);
xt_info_rdunlock_bh();
-
+ pr_debug("Exiting %s; resetting sp from %u to %u\n",
+ __func__, *stackptr, origptr);
+ *stackptr = origptr;
#ifdef DEBUG_ALLOW_ALL
return NF_ACCEPT;
#else
- if (hotdrop)
+ if (acpar.hotdrop)
return NF_DROP;
else return verdict;
#endif
-
-#undef tb_comefrom
}
/* Figures out from what hook each rule can be called: returns 0 if
@@ -486,7 +466,7 @@ mark_source_chains(const struct xt_table_info *newinfo,
int visited = e->comefrom & (1 << hook);
if (e->comefrom & (1 << NF_INET_NUMHOOKS)) {
- printk("iptables: loop hook %u pos %u %08X.\n",
+ pr_err("iptables: loop hook %u pos %u %08X.\n",
hook, pos, e->comefrom);
return 0;
}
@@ -591,7 +571,7 @@ check_entry(const struct ipt_entry *e, const char *name)
const struct ipt_entry_target *t;
if (!ip_checkentry(&e->ip)) {
- duprintf("ip_tables: ip check failed %p %s.\n", e, name);
+ duprintf("ip check failed %p %s.\n", e, par->match->name);
return -EINVAL;
}
@@ -618,8 +598,7 @@ check_match(struct ipt_entry_match *m, struct xt_mtchk_param *par)
ret = xt_check_match(par, m->u.match_size - sizeof(*m),
ip->proto, ip->invflags & IPT_INV_PROTO);
if (ret < 0) {
- duprintf("ip_tables: check failed for `%s'.\n",
- par.match->name);
+ duprintf("check failed for `%s'.\n", par->match->name);
return ret;
}
return 0;
@@ -631,12 +610,11 @@ find_check_match(struct ipt_entry_match *m, struct xt_mtchk_param *par)
struct xt_match *match;
int ret;
- match = try_then_request_module(xt_find_match(AF_INET, m->u.user.name,
- m->u.user.revision),
- "ipt_%s", m->u.user.name);
- if (IS_ERR(match) || !match) {
+ match = xt_request_find_match(NFPROTO_IPV4, m->u.user.name,
+ m->u.user.revision);
+ if (IS_ERR(match)) {
duprintf("find_check_match: `%s' not found\n", m->u.user.name);
- return match ? PTR_ERR(match) : -ENOENT;
+ return PTR_ERR(match);
}
m->u.kernel.match = match;
@@ -667,7 +645,7 @@ static int check_target(struct ipt_entry *e, struct net *net, const char *name)
ret = xt_check_target(&par, t->u.target_size - sizeof(*t),
e->ip.proto, e->ip.invflags & IPT_INV_PROTO);
if (ret < 0) {
- duprintf("ip_tables: check failed for `%s'.\n",
+ duprintf("check failed for `%s'.\n",
t->u.kernel.target->name);
return ret;
}
@@ -703,13 +681,11 @@ find_check_entry(struct ipt_entry *e, struct net *net, const char *name,
}
t = ipt_get_target(e);
- target = try_then_request_module(xt_find_target(AF_INET,
- t->u.user.name,
- t->u.user.revision),
- "ipt_%s", t->u.user.name);
- if (IS_ERR(target) || !target) {
+ target = xt_request_find_target(NFPROTO_IPV4, t->u.user.name,
+ t->u.user.revision);
+ if (IS_ERR(target)) {
duprintf("find_check_entry: `%s' not found\n", t->u.user.name);
- ret = target ? PTR_ERR(target) : -ENOENT;
+ ret = PTR_ERR(target);
goto cleanup_matches;
}
t->u.kernel.target = target;
@@ -843,6 +819,9 @@ translate_table(struct net *net, struct xt_table_info *newinfo, void *entry0,
if (ret != 0)
return ret;
++i;
+ if (strcmp(ipt_get_target(iter)->u.user.name,
+ XT_ERROR_TARGET) == 0)
+ ++newinfo->stacksize;
}
if (i != repl->num_entries) {
@@ -1311,7 +1290,7 @@ do_replace(struct net *net, const void __user *user, unsigned int len)
if (ret != 0)
goto free_newinfo;
- duprintf("ip_tables: Translated table\n");
+ duprintf("Translated table\n");
ret = __do_replace(net, tmp.name, tmp.valid_hooks, newinfo,
tmp.num_counters, tmp.counters);
@@ -1476,13 +1455,12 @@ compat_find_calc_match(struct ipt_entry_match *m,
{
struct xt_match *match;
- match = try_then_request_module(xt_find_match(AF_INET, m->u.user.name,
- m->u.user.revision),
- "ipt_%s", m->u.user.name);
- if (IS_ERR(match) || !match) {
+ match = xt_request_find_match(NFPROTO_IPV4, m->u.user.name,
+ m->u.user.revision);
+ if (IS_ERR(match)) {
duprintf("compat_check_calc_match: `%s' not found\n",
m->u.user.name);
- return match ? PTR_ERR(match) : -ENOENT;
+ return PTR_ERR(match);
}
m->u.kernel.match = match;
*size += xt_compat_match_offset(match);
@@ -1549,14 +1527,12 @@ check_compat_entry_size_and_hooks(struct compat_ipt_entry *e,
}
t = compat_ipt_get_target(e);
- target = try_then_request_module(xt_find_target(AF_INET,
- t->u.user.name,
- t->u.user.revision),
- "ipt_%s", t->u.user.name);
- if (IS_ERR(target) || !target) {
+ target = xt_request_find_target(NFPROTO_IPV4, t->u.user.name,
+ t->u.user.revision);
+ if (IS_ERR(target)) {
duprintf("check_compat_entry_size_and_hooks: `%s' not found\n",
t->u.user.name);
- ret = target ? PTR_ERR(target) : -ENOENT;
+ ret = PTR_ERR(target);
goto release_matches;
}
t->u.kernel.target = target;
@@ -2094,8 +2070,7 @@ struct xt_table *ipt_register_table(struct net *net,
{
int ret;
struct xt_table_info *newinfo;
- struct xt_table_info bootstrap
- = { 0, 0, 0, { 0 }, { 0 }, { } };
+ struct xt_table_info bootstrap = {0};
void *loc_cpu_entry;
struct xt_table *new_table;
@@ -2157,7 +2132,7 @@ icmp_type_code_match(u_int8_t test_type, u_int8_t min_code, u_int8_t max_code,
}
static bool
-icmp_match(const struct sk_buff *skb, const struct xt_match_param *par)
+icmp_match(const struct sk_buff *skb, struct xt_action_param *par)
{
const struct icmphdr *ic;
struct icmphdr _icmph;
@@ -2173,7 +2148,7 @@ icmp_match(const struct sk_buff *skb, const struct xt_match_param *par)
* can't. Hence, no choice but to drop.
*/
duprintf("Dropping evil ICMP tinygram.\n");
- *par->hotdrop = true;
+ par->hotdrop = true;
return false;
}
@@ -2184,31 +2159,31 @@ icmp_match(const struct sk_buff *skb, const struct xt_match_param *par)
!!(icmpinfo->invflags&IPT_ICMP_INV));
}
-static bool icmp_checkentry(const struct xt_mtchk_param *par)
+static int icmp_checkentry(const struct xt_mtchk_param *par)
{
const struct ipt_icmp *icmpinfo = par->matchinfo;
/* Must specify no unknown invflags */
- return !(icmpinfo->invflags & ~IPT_ICMP_INV);
+ return (icmpinfo->invflags & ~IPT_ICMP_INV) ? -EINVAL : 0;
}
-/* The built-in targets: standard (NULL) and error. */
-static struct xt_target ipt_standard_target __read_mostly = {
- .name = IPT_STANDARD_TARGET,
- .targetsize = sizeof(int),
- .family = NFPROTO_IPV4,
+static struct xt_target ipt_builtin_tg[] __read_mostly = {
+ {
+ .name = IPT_STANDARD_TARGET,
+ .targetsize = sizeof(int),
+ .family = NFPROTO_IPV4,
#ifdef CONFIG_COMPAT
- .compatsize = sizeof(compat_int_t),
- .compat_from_user = compat_standard_from_user,
- .compat_to_user = compat_standard_to_user,
+ .compatsize = sizeof(compat_int_t),
+ .compat_from_user = compat_standard_from_user,
+ .compat_to_user = compat_standard_to_user,
#endif
-};
-
-static struct xt_target ipt_error_target __read_mostly = {
- .name = IPT_ERROR_TARGET,
- .target = ipt_error,
- .targetsize = IPT_FUNCTION_MAXNAMELEN,
- .family = NFPROTO_IPV4,
+ },
+ {
+ .name = IPT_ERROR_TARGET,
+ .target = ipt_error,
+ .targetsize = IPT_FUNCTION_MAXNAMELEN,
+ .family = NFPROTO_IPV4,
+ },
};
static struct nf_sockopt_ops ipt_sockopts = {
@@ -2228,13 +2203,15 @@ static struct nf_sockopt_ops ipt_sockopts = {
.owner = THIS_MODULE,
};
-static struct xt_match icmp_matchstruct __read_mostly = {
- .name = "icmp",
- .match = icmp_match,
- .matchsize = sizeof(struct ipt_icmp),
- .checkentry = icmp_checkentry,
- .proto = IPPROTO_ICMP,
- .family = NFPROTO_IPV4,
+static struct xt_match ipt_builtin_mt[] __read_mostly = {
+ {
+ .name = "icmp",
+ .match = icmp_match,
+ .matchsize = sizeof(struct ipt_icmp),
+ .checkentry = icmp_checkentry,
+ .proto = IPPROTO_ICMP,
+ .family = NFPROTO_IPV4,
+ },
};
static int __net_init ip_tables_net_init(struct net *net)
@@ -2261,13 +2238,10 @@ static int __init ip_tables_init(void)
goto err1;
/* Noone else will be downing sem now, so we won't sleep */
- ret = xt_register_target(&ipt_standard_target);
+ ret = xt_register_targets(ipt_builtin_tg, ARRAY_SIZE(ipt_builtin_tg));
if (ret < 0)
goto err2;
- ret = xt_register_target(&ipt_error_target);
- if (ret < 0)
- goto err3;
- ret = xt_register_match(&icmp_matchstruct);
+ ret = xt_register_matches(ipt_builtin_mt, ARRAY_SIZE(ipt_builtin_mt));
if (ret < 0)
goto err4;
@@ -2276,15 +2250,13 @@ static int __init ip_tables_init(void)
if (ret < 0)
goto err5;
- printk(KERN_INFO "ip_tables: (C) 2000-2006 Netfilter Core Team\n");
+ pr_info("(C) 2000-2006 Netfilter Core Team\n");
return 0;
err5:
- xt_unregister_match(&icmp_matchstruct);
+ xt_unregister_matches(ipt_builtin_mt, ARRAY_SIZE(ipt_builtin_mt));
err4:
- xt_unregister_target(&ipt_error_target);
-err3:
- xt_unregister_target(&ipt_standard_target);
+ xt_unregister_targets(ipt_builtin_tg, ARRAY_SIZE(ipt_builtin_tg));
err2:
unregister_pernet_subsys(&ip_tables_net_ops);
err1:
@@ -2295,10 +2267,8 @@ static void __exit ip_tables_fini(void)
{
nf_unregister_sockopt(&ipt_sockopts);
- xt_unregister_match(&icmp_matchstruct);
- xt_unregister_target(&ipt_error_target);
- xt_unregister_target(&ipt_standard_target);
-
+ xt_unregister_matches(ipt_builtin_mt, ARRAY_SIZE(ipt_builtin_mt));
+ xt_unregister_targets(ipt_builtin_tg, ARRAY_SIZE(ipt_builtin_tg));
unregister_pernet_subsys(&ip_tables_net_ops);
}