netfilter: xtables: fix incorrect return code
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / net / ipv4 / netfilter / ip_tables.c
CommitLineData
1da177e4
LT
1/*
2 * Packet matching code.
3 *
4 * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
2e4e6a17 5 * Copyright (C) 2000-2005 Netfilter Core Team <coreteam@netfilter.org>
1da177e4
LT
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
1da177e4 10 */
90e7d4ab 11#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
1da177e4 12#include <linux/cache.h>
4fc268d2 13#include <linux/capability.h>
1da177e4
LT
14#include <linux/skbuff.h>
15#include <linux/kmod.h>
16#include <linux/vmalloc.h>
17#include <linux/netdevice.h>
18#include <linux/module.h>
1da177e4
LT
19#include <linux/icmp.h>
20#include <net/ip.h>
2722971c 21#include <net/compat.h>
1da177e4 22#include <asm/uaccess.h>
57b47a53 23#include <linux/mutex.h>
1da177e4
LT
24#include <linux/proc_fs.h>
25#include <linux/err.h>
c8923c6b 26#include <linux/cpumask.h>
1da177e4 27
2e4e6a17 28#include <linux/netfilter/x_tables.h>
1da177e4 29#include <linux/netfilter_ipv4/ip_tables.h>
f01ffbd6 30#include <net/netfilter/nf_log.h>
e3eaa991 31#include "../../netfilter/xt_repldata.h"
1da177e4
LT
32
33MODULE_LICENSE("GPL");
34MODULE_AUTHOR("Netfilter Core Team <coreteam@netfilter.org>");
35MODULE_DESCRIPTION("IPv4 packet filter");
36
37/*#define DEBUG_IP_FIREWALL*/
38/*#define DEBUG_ALLOW_ALL*/ /* Useful for remote debugging */
39/*#define DEBUG_IP_FIREWALL_USER*/
40
41#ifdef DEBUG_IP_FIREWALL
ff67e4e4 42#define dprintf(format, args...) pr_info(format , ## args)
1da177e4
LT
43#else
44#define dprintf(format, args...)
45#endif
46
47#ifdef DEBUG_IP_FIREWALL_USER
ff67e4e4 48#define duprintf(format, args...) pr_info(format , ## args)
1da177e4
LT
49#else
50#define duprintf(format, args...)
51#endif
52
53#ifdef CONFIG_NETFILTER_DEBUG
54#define IP_NF_ASSERT(x) \
55do { \
56 if (!(x)) \
57 printk("IP_NF_ASSERT: %s:%s:%u\n", \
0dc47877 58 __func__, __FILE__, __LINE__); \
1da177e4
LT
59} while(0)
60#else
61#define IP_NF_ASSERT(x)
62#endif
1da177e4
LT
63
64#if 0
65/* All the better to debug you with... */
66#define static
67#define inline
68#endif
69
e3eaa991
JE
70void *ipt_alloc_initial_table(const struct xt_table *info)
71{
72 return xt_alloc_initial_table(ipt, IPT);
73}
74EXPORT_SYMBOL_GPL(ipt_alloc_initial_table);
75
1da177e4
LT
76/*
77 We keep a set of rules for each CPU, so we can avoid write-locking
78 them in the softirq when updating the counters and therefore
79 only need to read-lock in the softirq; doing a write_lock_bh() in user
80 context stops packets coming through and allows user context to read
81 the counters or update the rules.
82
1da177e4
LT
83 Hence the start of any table is given by get_table() below. */
84
1da177e4 85/* Returns whether matches rule or not. */
022748a9 86/* Performance critical - called for every packet */
9c547959 87static inline bool
1da177e4
LT
88ip_packet_match(const struct iphdr *ip,
89 const char *indev,
90 const char *outdev,
91 const struct ipt_ip *ipinfo,
92 int isfrag)
93{
1da177e4
LT
94 unsigned long ret;
95
e79ec50b 96#define FWINV(bool, invflg) ((bool) ^ !!(ipinfo->invflags & (invflg)))
1da177e4
LT
97
98 if (FWINV((ip->saddr&ipinfo->smsk.s_addr) != ipinfo->src.s_addr,
3666ed1c
JP
99 IPT_INV_SRCIP) ||
100 FWINV((ip->daddr&ipinfo->dmsk.s_addr) != ipinfo->dst.s_addr,
101 IPT_INV_DSTIP)) {
1da177e4
LT
102 dprintf("Source or dest mismatch.\n");
103
cffee385
HH
104 dprintf("SRC: %pI4. Mask: %pI4. Target: %pI4.%s\n",
105 &ip->saddr, &ipinfo->smsk.s_addr, &ipinfo->src.s_addr,
1da177e4 106 ipinfo->invflags & IPT_INV_SRCIP ? " (INV)" : "");
cffee385
HH
107 dprintf("DST: %pI4 Mask: %pI4 Target: %pI4.%s\n",
108 &ip->daddr, &ipinfo->dmsk.s_addr, &ipinfo->dst.s_addr,
1da177e4 109 ipinfo->invflags & IPT_INV_DSTIP ? " (INV)" : "");
9c547959 110 return false;
1da177e4
LT
111 }
112
b8dfe498 113 ret = ifname_compare_aligned(indev, ipinfo->iniface, ipinfo->iniface_mask);
1da177e4
LT
114
115 if (FWINV(ret != 0, IPT_INV_VIA_IN)) {
116 dprintf("VIA in mismatch (%s vs %s).%s\n",
117 indev, ipinfo->iniface,
118 ipinfo->invflags&IPT_INV_VIA_IN ?" (INV)":"");
9c547959 119 return false;
1da177e4
LT
120 }
121
b8dfe498 122 ret = ifname_compare_aligned(outdev, ipinfo->outiface, ipinfo->outiface_mask);
1da177e4
LT
123
124 if (FWINV(ret != 0, IPT_INV_VIA_OUT)) {
125 dprintf("VIA out mismatch (%s vs %s).%s\n",
126 outdev, ipinfo->outiface,
127 ipinfo->invflags&IPT_INV_VIA_OUT ?" (INV)":"");
9c547959 128 return false;
1da177e4
LT
129 }
130
131 /* Check specific protocol */
3666ed1c
JP
132 if (ipinfo->proto &&
133 FWINV(ip->protocol != ipinfo->proto, IPT_INV_PROTO)) {
1da177e4
LT
134 dprintf("Packet protocol %hi does not match %hi.%s\n",
135 ip->protocol, ipinfo->proto,
136 ipinfo->invflags&IPT_INV_PROTO ? " (INV)":"");
9c547959 137 return false;
1da177e4
LT
138 }
139
140 /* If we have a fragment rule but the packet is not a fragment
141 * then we return zero */
142 if (FWINV((ipinfo->flags&IPT_F_FRAG) && !isfrag, IPT_INV_FRAG)) {
143 dprintf("Fragment rule but not fragment.%s\n",
144 ipinfo->invflags & IPT_INV_FRAG ? " (INV)" : "");
9c547959 145 return false;
1da177e4
LT
146 }
147
9c547959 148 return true;
1da177e4
LT
149}
150
022748a9 151static bool
1da177e4
LT
152ip_checkentry(const struct ipt_ip *ip)
153{
154 if (ip->flags & ~IPT_F_MASK) {
155 duprintf("Unknown flag bits set: %08X\n",
156 ip->flags & ~IPT_F_MASK);
ccb79bdc 157 return false;
1da177e4
LT
158 }
159 if (ip->invflags & ~IPT_INV_MASK) {
160 duprintf("Unknown invflag bits set: %08X\n",
161 ip->invflags & ~IPT_INV_MASK);
ccb79bdc 162 return false;
1da177e4 163 }
ccb79bdc 164 return true;
1da177e4
LT
165}
166
167static unsigned int
7eb35586 168ipt_error(struct sk_buff *skb, const struct xt_target_param *par)
1da177e4
LT
169{
170 if (net_ratelimit())
ff67e4e4 171 pr_info("error: `%s'\n", (const char *)par->targinfo);
1da177e4
LT
172
173 return NF_DROP;
174}
175
022748a9
DV
176/* Performance critical - called for every packet */
177static inline bool
d5d1baa1 178do_match(const struct ipt_entry_match *m, const struct sk_buff *skb,
f7108a20 179 struct xt_match_param *par)
1da177e4 180{
f7108a20
JE
181 par->match = m->u.kernel.match;
182 par->matchinfo = m->data;
183
1da177e4 184 /* Stop iteration if it doesn't match */
f7108a20 185 if (!m->u.kernel.match->match(skb, par))
1d93a9cb 186 return true;
1da177e4 187 else
1d93a9cb 188 return false;
1da177e4
LT
189}
190
022748a9 191/* Performance critical */
1da177e4 192static inline struct ipt_entry *
d5d1baa1 193get_entry(const void *base, unsigned int offset)
1da177e4
LT
194{
195 return (struct ipt_entry *)(base + offset);
196}
197
ba9dda3a 198/* All zeroes == unconditional rule. */
022748a9 199/* Mildly perf critical (only if packet tracing is on) */
47901dc2 200static inline bool unconditional(const struct ipt_ip *ip)
ba9dda3a 201{
47901dc2 202 static const struct ipt_ip uncond;
ba9dda3a 203
47901dc2 204 return memcmp(ip, &uncond, sizeof(uncond)) == 0;
e79ec50b 205#undef FWINV
ba9dda3a
JK
206}
207
d5d1baa1
JE
208/* for const-correctness */
209static inline const struct ipt_entry_target *
210ipt_get_target_c(const struct ipt_entry *e)
211{
212 return ipt_get_target((struct ipt_entry *)e);
213}
214
ba9dda3a
JK
215#if defined(CONFIG_NETFILTER_XT_TARGET_TRACE) || \
216 defined(CONFIG_NETFILTER_XT_TARGET_TRACE_MODULE)
022748a9 217static const char *const hooknames[] = {
6e23ae2a
PM
218 [NF_INET_PRE_ROUTING] = "PREROUTING",
219 [NF_INET_LOCAL_IN] = "INPUT",
9c547959 220 [NF_INET_FORWARD] = "FORWARD",
6e23ae2a
PM
221 [NF_INET_LOCAL_OUT] = "OUTPUT",
222 [NF_INET_POST_ROUTING] = "POSTROUTING",
ba9dda3a
JK
223};
224
225enum nf_ip_trace_comments {
226 NF_IP_TRACE_COMMENT_RULE,
227 NF_IP_TRACE_COMMENT_RETURN,
228 NF_IP_TRACE_COMMENT_POLICY,
229};
230
022748a9 231static const char *const comments[] = {
ba9dda3a
JK
232 [NF_IP_TRACE_COMMENT_RULE] = "rule",
233 [NF_IP_TRACE_COMMENT_RETURN] = "return",
234 [NF_IP_TRACE_COMMENT_POLICY] = "policy",
235};
236
237static struct nf_loginfo trace_loginfo = {
238 .type = NF_LOG_TYPE_LOG,
239 .u = {
240 .log = {
241 .level = 4,
242 .logflags = NF_LOG_MASK,
243 },
244 },
245};
246
022748a9 247/* Mildly perf critical (only if packet tracing is on) */
ba9dda3a 248static inline int
d5d1baa1 249get_chainname_rulenum(const struct ipt_entry *s, const struct ipt_entry *e,
4f2f6f23
JE
250 const char *hookname, const char **chainname,
251 const char **comment, unsigned int *rulenum)
ba9dda3a 252{
d5d1baa1 253 const struct ipt_standard_target *t = (void *)ipt_get_target_c(s);
ba9dda3a
JK
254
255 if (strcmp(t->target.u.kernel.target->name, IPT_ERROR_TARGET) == 0) {
256 /* Head of user chain: ERROR target with chainname */
257 *chainname = t->target.data;
258 (*rulenum) = 0;
259 } else if (s == e) {
260 (*rulenum)++;
261
3666ed1c
JP
262 if (s->target_offset == sizeof(struct ipt_entry) &&
263 strcmp(t->target.u.kernel.target->name,
264 IPT_STANDARD_TARGET) == 0 &&
265 t->verdict < 0 &&
266 unconditional(&s->ip)) {
ba9dda3a
JK
267 /* Tail of chains: STANDARD target (return/policy) */
268 *comment = *chainname == hookname
4f2f6f23
JE
269 ? comments[NF_IP_TRACE_COMMENT_POLICY]
270 : comments[NF_IP_TRACE_COMMENT_RETURN];
ba9dda3a
JK
271 }
272 return 1;
273 } else
274 (*rulenum)++;
275
276 return 0;
277}
278
d5d1baa1 279static void trace_packet(const struct sk_buff *skb,
ba9dda3a
JK
280 unsigned int hook,
281 const struct net_device *in,
282 const struct net_device *out,
ecb6f85e 283 const char *tablename,
d5d1baa1
JE
284 const struct xt_table_info *private,
285 const struct ipt_entry *e)
ba9dda3a 286{
d5d1baa1 287 const void *table_base;
5452e425 288 const struct ipt_entry *root;
4f2f6f23 289 const char *hookname, *chainname, *comment;
72b2b1dd 290 const struct ipt_entry *iter;
ba9dda3a
JK
291 unsigned int rulenum = 0;
292
ccf5bd8c 293 table_base = private->entries[smp_processor_id()];
ba9dda3a
JK
294 root = get_entry(table_base, private->hook_entry[hook]);
295
4f2f6f23
JE
296 hookname = chainname = hooknames[hook];
297 comment = comments[NF_IP_TRACE_COMMENT_RULE];
ba9dda3a 298
72b2b1dd
JE
299 xt_entry_foreach(iter, root, private->size - private->hook_entry[hook])
300 if (get_chainname_rulenum(iter, e, hookname,
301 &chainname, &comment, &rulenum) != 0)
302 break;
ba9dda3a
JK
303
304 nf_log_packet(AF_INET, hook, skb, in, out, &trace_loginfo,
305 "TRACE: %s:%s:%s:%u ",
306 tablename, chainname, comment, rulenum);
307}
308#endif
309
98e86403
JE
310static inline __pure
311struct ipt_entry *ipt_next_entry(const struct ipt_entry *entry)
312{
313 return (void *)entry + entry->next_offset;
314}
315
1da177e4
LT
316/* Returns one of the generic firewall policies, like NF_ACCEPT. */
317unsigned int
3db05fea 318ipt_do_table(struct sk_buff *skb,
1da177e4
LT
319 unsigned int hook,
320 const struct net_device *in,
321 const struct net_device *out,
e60a13e0 322 struct xt_table *table)
1da177e4
LT
323{
324 static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long))));
5452e425 325 const struct iphdr *ip;
cff533ac 326 bool hotdrop = false;
1da177e4
LT
327 /* Initializing verdict to NF_DROP keeps gcc happy. */
328 unsigned int verdict = NF_DROP;
329 const char *indev, *outdev;
d5d1baa1 330 const void *table_base;
f3c5c1bf
JE
331 struct ipt_entry *e, **jumpstack;
332 unsigned int *stackptr, origptr, cpu;
d5d1baa1 333 const struct xt_table_info *private;
f7108a20 334 struct xt_match_param mtpar;
7eb35586 335 struct xt_target_param tgpar;
1da177e4
LT
336
337 /* Initialization */
3db05fea 338 ip = ip_hdr(skb);
1da177e4
LT
339 indev = in ? in->name : nulldevname;
340 outdev = out ? out->name : nulldevname;
341 /* We handle fragments by dealing with the first fragment as
342 * if it was a normal packet. All other fragments are treated
343 * normally, except that they will NEVER match rules that ask
344 * things we don't know, ie. tcp syn flag or ports). If the
345 * rule is also a fragment-specific rule, non-fragments won't
346 * match it. */
f7108a20
JE
347 mtpar.fragoff = ntohs(ip->frag_off) & IP_OFFSET;
348 mtpar.thoff = ip_hdrlen(skb);
349 mtpar.hotdrop = &hotdrop;
7eb35586
JE
350 mtpar.in = tgpar.in = in;
351 mtpar.out = tgpar.out = out;
916a917d 352 mtpar.family = tgpar.family = NFPROTO_IPV4;
a5e78820 353 mtpar.hooknum = tgpar.hooknum = hook;
1da177e4 354
1da177e4 355 IP_NF_ASSERT(table->valid_hooks & (1 << hook));
942e4a2b
SH
356 xt_info_rdlock_bh();
357 private = table->private;
f3c5c1bf
JE
358 cpu = smp_processor_id();
359 table_base = private->entries[cpu];
360 jumpstack = (struct ipt_entry **)private->jumpstack[cpu];
361 stackptr = &private->stackptr[cpu];
362 origptr = *stackptr;
78454473 363
2e4e6a17 364 e = get_entry(table_base, private->hook_entry[hook]);
1da177e4 365
cecc74de 366 pr_debug("Entering %s(hook %u); sp at %u (UF %p)\n",
f3c5c1bf
JE
367 table->name, hook, origptr,
368 get_entry(table_base, private->underflow[hook]));
1da177e4
LT
369
370 do {
d5d1baa1 371 const struct ipt_entry_target *t;
dcea992a 372 const struct xt_entry_match *ematch;
a1ff4ac8 373
1da177e4 374 IP_NF_ASSERT(e);
a1ff4ac8 375 if (!ip_packet_match(ip, indev, outdev,
dcea992a
JE
376 &e->ip, mtpar.fragoff)) {
377 no_match:
a1ff4ac8
JE
378 e = ipt_next_entry(e);
379 continue;
380 }
1da177e4 381
dcea992a
JE
382 xt_ematch_foreach(ematch, e)
383 if (do_match(ematch, skb, &mtpar) != 0)
384 goto no_match;
385
a1ff4ac8 386 ADD_COUNTER(e->counters, ntohs(ip->tot_len), 1);
1da177e4 387
a1ff4ac8
JE
388 t = ipt_get_target(e);
389 IP_NF_ASSERT(t->u.kernel.target);
ba9dda3a
JK
390
391#if defined(CONFIG_NETFILTER_XT_TARGET_TRACE) || \
392 defined(CONFIG_NETFILTER_XT_TARGET_TRACE_MODULE)
a1ff4ac8
JE
393 /* The packet is traced: log it */
394 if (unlikely(skb->nf_trace))
395 trace_packet(skb, hook, in, out,
396 table->name, private, e);
ba9dda3a 397#endif
a1ff4ac8
JE
398 /* Standard target? */
399 if (!t->u.kernel.target->target) {
400 int v;
401
402 v = ((struct ipt_standard_target *)t)->verdict;
403 if (v < 0) {
404 /* Pop from stack? */
405 if (v != IPT_RETURN) {
406 verdict = (unsigned)(-v) - 1;
407 break;
1da177e4 408 }
f3c5c1bf
JE
409 if (*stackptr == 0) {
410 e = get_entry(table_base,
411 private->underflow[hook]);
cecc74de 412 pr_debug("Underflow (this is normal) "
f3c5c1bf
JE
413 "to %p\n", e);
414 } else {
415 e = jumpstack[--*stackptr];
cecc74de 416 pr_debug("Pulled %p out from pos %u\n",
f3c5c1bf
JE
417 e, *stackptr);
418 e = ipt_next_entry(e);
419 }
a1ff4ac8
JE
420 continue;
421 }
3666ed1c
JP
422 if (table_base + v != ipt_next_entry(e) &&
423 !(e->ip.flags & IPT_F_GOTO)) {
f3c5c1bf
JE
424 if (*stackptr >= private->stacksize) {
425 verdict = NF_DROP;
426 break;
427 }
428 jumpstack[(*stackptr)++] = e;
cecc74de 429 pr_debug("Pushed %p into pos %u\n",
f3c5c1bf 430 e, *stackptr - 1);
a1ff4ac8 431 }
1da177e4 432
a1ff4ac8 433 e = get_entry(table_base, v);
7a6b1c46
JE
434 continue;
435 }
436
7a6b1c46
JE
437 tgpar.target = t->u.kernel.target;
438 tgpar.targinfo = t->data;
bb70dfa5
JE
439
440
7a6b1c46 441 verdict = t->u.kernel.target->target(skb, &tgpar);
7a6b1c46
JE
442 /* Target might have changed stuff. */
443 ip = ip_hdr(skb);
7a6b1c46
JE
444 if (verdict == IPT_CONTINUE)
445 e = ipt_next_entry(e);
446 else
447 /* Verdict */
448 break;
1da177e4 449 } while (!hotdrop);
942e4a2b 450 xt_info_rdunlock_bh();
cecc74de 451 pr_debug("Exiting %s; resetting sp from %u to %u\n",
f3c5c1bf
JE
452 __func__, *stackptr, origptr);
453 *stackptr = origptr;
1da177e4
LT
454#ifdef DEBUG_ALLOW_ALL
455 return NF_ACCEPT;
456#else
457 if (hotdrop)
458 return NF_DROP;
459 else return verdict;
460#endif
461}
462
1da177e4
LT
463/* Figures out from what hook each rule can be called: returns 0 if
464 there are loops. Puts hook bitmask in comefrom. */
465static int
d5d1baa1 466mark_source_chains(const struct xt_table_info *newinfo,
31836064 467 unsigned int valid_hooks, void *entry0)
1da177e4
LT
468{
469 unsigned int hook;
470
471 /* No recursion; use packet counter to save back ptrs (reset
472 to 0 as we leave), and comefrom to save source hook bitmask */
6e23ae2a 473 for (hook = 0; hook < NF_INET_NUMHOOKS; hook++) {
1da177e4 474 unsigned int pos = newinfo->hook_entry[hook];
9c547959 475 struct ipt_entry *e = (struct ipt_entry *)(entry0 + pos);
1da177e4
LT
476
477 if (!(valid_hooks & (1 << hook)))
478 continue;
479
480 /* Set initial back pointer. */
481 e->counters.pcnt = pos;
482
483 for (;;) {
d5d1baa1
JE
484 const struct ipt_standard_target *t
485 = (void *)ipt_get_target_c(e);
e1b4b9f3 486 int visited = e->comefrom & (1 << hook);
1da177e4 487
6e23ae2a 488 if (e->comefrom & (1 << NF_INET_NUMHOOKS)) {
1da177e4
LT
489 printk("iptables: loop hook %u pos %u %08X.\n",
490 hook, pos, e->comefrom);
491 return 0;
492 }
9c547959 493 e->comefrom |= ((1 << hook) | (1 << NF_INET_NUMHOOKS));
1da177e4
LT
494
495 /* Unconditional return/END. */
3666ed1c
JP
496 if ((e->target_offset == sizeof(struct ipt_entry) &&
497 (strcmp(t->target.u.user.name,
498 IPT_STANDARD_TARGET) == 0) &&
499 t->verdict < 0 && unconditional(&e->ip)) ||
500 visited) {
1da177e4
LT
501 unsigned int oldpos, size;
502
1f9352ae
PM
503 if ((strcmp(t->target.u.user.name,
504 IPT_STANDARD_TARGET) == 0) &&
505 t->verdict < -NF_MAX_VERDICT - 1) {
74c9c0c1
DM
506 duprintf("mark_source_chains: bad "
507 "negative verdict (%i)\n",
508 t->verdict);
509 return 0;
510 }
511
1da177e4
LT
512 /* Return: backtrack through the last
513 big jump. */
514 do {
6e23ae2a 515 e->comefrom ^= (1<<NF_INET_NUMHOOKS);
1da177e4
LT
516#ifdef DEBUG_IP_FIREWALL_USER
517 if (e->comefrom
6e23ae2a 518 & (1 << NF_INET_NUMHOOKS)) {
1da177e4
LT
519 duprintf("Back unset "
520 "on hook %u "
521 "rule %u\n",
522 hook, pos);
523 }
524#endif
525 oldpos = pos;
526 pos = e->counters.pcnt;
527 e->counters.pcnt = 0;
528
529 /* We're at the start. */
530 if (pos == oldpos)
531 goto next;
532
533 e = (struct ipt_entry *)
31836064 534 (entry0 + pos);
1da177e4
LT
535 } while (oldpos == pos + e->next_offset);
536
537 /* Move along one */
538 size = e->next_offset;
539 e = (struct ipt_entry *)
31836064 540 (entry0 + pos + size);
1da177e4
LT
541 e->counters.pcnt = pos;
542 pos += size;
543 } else {
544 int newpos = t->verdict;
545
546 if (strcmp(t->target.u.user.name,
3666ed1c
JP
547 IPT_STANDARD_TARGET) == 0 &&
548 newpos >= 0) {
74c9c0c1
DM
549 if (newpos > newinfo->size -
550 sizeof(struct ipt_entry)) {
551 duprintf("mark_source_chains: "
552 "bad verdict (%i)\n",
553 newpos);
554 return 0;
555 }
1da177e4
LT
556 /* This a jump; chase it. */
557 duprintf("Jump rule %u -> %u\n",
558 pos, newpos);
559 } else {
560 /* ... this is a fallthru */
561 newpos = pos + e->next_offset;
562 }
563 e = (struct ipt_entry *)
31836064 564 (entry0 + newpos);
1da177e4
LT
565 e->counters.pcnt = pos;
566 pos = newpos;
567 }
568 }
569 next:
570 duprintf("Finished chain %u\n", hook);
571 }
572 return 1;
573}
574
6bdb331b 575static void cleanup_match(struct ipt_entry_match *m, struct net *net)
1da177e4 576{
6be3d859
JE
577 struct xt_mtdtor_param par;
578
f54e9367 579 par.net = net;
6be3d859
JE
580 par.match = m->u.kernel.match;
581 par.matchinfo = m->data;
916a917d 582 par.family = NFPROTO_IPV4;
6be3d859
JE
583 if (par.match->destroy != NULL)
584 par.match->destroy(&par);
585 module_put(par.match->me);
1da177e4
LT
586}
587
022748a9 588static int
d5d1baa1 589check_entry(const struct ipt_entry *e, const char *name)
a96be246 590{
d5d1baa1 591 const struct ipt_entry_target *t;
a96be246
DM
592
593 if (!ip_checkentry(&e->ip)) {
b5cad0df 594 duprintf("ip check failed %p %s.\n", e, par->match->name);
a96be246
DM
595 return -EINVAL;
596 }
597
9c547959
PM
598 if (e->target_offset + sizeof(struct ipt_entry_target) >
599 e->next_offset)
a96be246
DM
600 return -EINVAL;
601
d5d1baa1 602 t = ipt_get_target_c(e);
a96be246
DM
603 if (e->target_offset + t->u.target_size > e->next_offset)
604 return -EINVAL;
605
606 return 0;
607}
608
022748a9 609static int
6bdb331b 610check_match(struct ipt_entry_match *m, struct xt_mtchk_param *par)
a96be246 611{
9b4fce7a 612 const struct ipt_ip *ip = par->entryinfo;
a96be246
DM
613 int ret;
614
9b4fce7a
JE
615 par->match = m->u.kernel.match;
616 par->matchinfo = m->data;
617
916a917d 618 ret = xt_check_match(par, m->u.match_size - sizeof(*m),
9b4fce7a 619 ip->proto, ip->invflags & IPT_INV_PROTO);
367c6790 620 if (ret < 0) {
b5cad0df 621 duprintf("check failed for `%s'.\n", par->match->name);
367c6790 622 return ret;
a96be246 623 }
367c6790 624 return 0;
a96be246
DM
625}
626
022748a9 627static int
6bdb331b 628find_check_match(struct ipt_entry_match *m, struct xt_mtchk_param *par)
1da177e4 629{
6709dbbb 630 struct xt_match *match;
3cdc7c95 631 int ret;
1da177e4 632
fd0ec0e6
JE
633 match = xt_request_find_match(NFPROTO_IPV4, m->u.user.name,
634 m->u.user.revision);
635 if (IS_ERR(match)) {
a96be246 636 duprintf("find_check_match: `%s' not found\n", m->u.user.name);
fd0ec0e6 637 return PTR_ERR(match);
1da177e4
LT
638 }
639 m->u.kernel.match = match;
640
6bdb331b 641 ret = check_match(m, par);
3cdc7c95
PM
642 if (ret)
643 goto err;
644
1da177e4 645 return 0;
3cdc7c95
PM
646err:
647 module_put(m->u.kernel.match->me);
648 return ret;
1da177e4
LT
649}
650
add67461 651static int check_target(struct ipt_entry *e, struct net *net, const char *name)
a96be246 652{
af5d6dc2
JE
653 struct ipt_entry_target *t = ipt_get_target(e);
654 struct xt_tgchk_param par = {
add67461 655 .net = net,
af5d6dc2
JE
656 .table = name,
657 .entryinfo = e,
658 .target = t->u.kernel.target,
659 .targinfo = t->data,
660 .hook_mask = e->comefrom,
916a917d 661 .family = NFPROTO_IPV4,
af5d6dc2 662 };
e905a9ed 663 int ret;
a96be246 664
916a917d 665 ret = xt_check_target(&par, t->u.target_size - sizeof(*t),
af5d6dc2 666 e->ip.proto, e->ip.invflags & IPT_INV_PROTO);
367c6790 667 if (ret < 0) {
ff67e4e4 668 duprintf("check failed for `%s'.\n",
a96be246 669 t->u.kernel.target->name);
367c6790 670 return ret;
a96be246 671 }
367c6790 672 return 0;
a96be246 673}
1da177e4 674
022748a9 675static int
a83d8e8d 676find_check_entry(struct ipt_entry *e, struct net *net, const char *name,
0559518b 677 unsigned int size)
1da177e4
LT
678{
679 struct ipt_entry_target *t;
6709dbbb 680 struct xt_target *target;
1da177e4
LT
681 int ret;
682 unsigned int j;
9b4fce7a 683 struct xt_mtchk_param mtpar;
dcea992a 684 struct xt_entry_match *ematch;
1da177e4 685
a96be246
DM
686 ret = check_entry(e, name);
687 if (ret)
688 return ret;
590bdf7f 689
1da177e4 690 j = 0;
a83d8e8d 691 mtpar.net = net;
9b4fce7a
JE
692 mtpar.table = name;
693 mtpar.entryinfo = &e->ip;
694 mtpar.hook_mask = e->comefrom;
916a917d 695 mtpar.family = NFPROTO_IPV4;
dcea992a 696 xt_ematch_foreach(ematch, e) {
6bdb331b 697 ret = find_check_match(ematch, &mtpar);
dcea992a 698 if (ret != 0)
6bdb331b
JE
699 goto cleanup_matches;
700 ++j;
dcea992a 701 }
1da177e4
LT
702
703 t = ipt_get_target(e);
d2a7b6ba
JE
704 target = xt_request_find_target(NFPROTO_IPV4, t->u.user.name,
705 t->u.user.revision);
706 if (IS_ERR(target)) {
a96be246 707 duprintf("find_check_entry: `%s' not found\n", t->u.user.name);
d2a7b6ba 708 ret = PTR_ERR(target);
1da177e4
LT
709 goto cleanup_matches;
710 }
711 t->u.kernel.target = target;
712
add67461 713 ret = check_target(e, net, name);
3cdc7c95
PM
714 if (ret)
715 goto err;
1da177e4 716 return 0;
3cdc7c95
PM
717 err:
718 module_put(t->u.kernel.target->me);
1da177e4 719 cleanup_matches:
6bdb331b
JE
720 xt_ematch_foreach(ematch, e) {
721 if (j-- == 0)
dcea992a 722 break;
6bdb331b
JE
723 cleanup_match(ematch, net);
724 }
1da177e4
LT
725 return ret;
726}
727
d5d1baa1 728static bool check_underflow(const struct ipt_entry *e)
e2fe35c1
JE
729{
730 const struct ipt_entry_target *t;
731 unsigned int verdict;
732
733 if (!unconditional(&e->ip))
734 return false;
d5d1baa1 735 t = ipt_get_target_c(e);
e2fe35c1
JE
736 if (strcmp(t->u.user.name, XT_STANDARD_TARGET) != 0)
737 return false;
738 verdict = ((struct ipt_standard_target *)t)->verdict;
739 verdict = -verdict - 1;
740 return verdict == NF_DROP || verdict == NF_ACCEPT;
741}
742
022748a9 743static int
1da177e4 744check_entry_size_and_hooks(struct ipt_entry *e,
2e4e6a17 745 struct xt_table_info *newinfo,
d5d1baa1
JE
746 const unsigned char *base,
747 const unsigned char *limit,
1da177e4
LT
748 const unsigned int *hook_entries,
749 const unsigned int *underflows,
0559518b 750 unsigned int valid_hooks)
1da177e4
LT
751{
752 unsigned int h;
753
3666ed1c
JP
754 if ((unsigned long)e % __alignof__(struct ipt_entry) != 0 ||
755 (unsigned char *)e + sizeof(struct ipt_entry) >= limit) {
1da177e4
LT
756 duprintf("Bad offset %p\n", e);
757 return -EINVAL;
758 }
759
760 if (e->next_offset
761 < sizeof(struct ipt_entry) + sizeof(struct ipt_entry_target)) {
762 duprintf("checking: element %p size %u\n",
763 e, e->next_offset);
764 return -EINVAL;
765 }
766
767 /* Check hooks & underflows */
6e23ae2a 768 for (h = 0; h < NF_INET_NUMHOOKS; h++) {
a7d51738
JE
769 if (!(valid_hooks & (1 << h)))
770 continue;
1da177e4
LT
771 if ((unsigned char *)e - base == hook_entries[h])
772 newinfo->hook_entry[h] = hook_entries[h];
90e7d4ab 773 if ((unsigned char *)e - base == underflows[h]) {
e2fe35c1
JE
774 if (!check_underflow(e)) {
775 pr_err("Underflows must be unconditional and "
776 "use the STANDARD target with "
777 "ACCEPT/DROP\n");
90e7d4ab
JE
778 return -EINVAL;
779 }
1da177e4 780 newinfo->underflow[h] = underflows[h];
90e7d4ab 781 }
1da177e4
LT
782 }
783
1da177e4 784 /* Clear counters and comefrom */
2e4e6a17 785 e->counters = ((struct xt_counters) { 0, 0 });
1da177e4 786 e->comefrom = 0;
1da177e4
LT
787 return 0;
788}
789
0559518b
JE
790static void
791cleanup_entry(struct ipt_entry *e, struct net *net)
1da177e4 792{
a2df1648 793 struct xt_tgdtor_param par;
1da177e4 794 struct ipt_entry_target *t;
dcea992a 795 struct xt_entry_match *ematch;
1da177e4 796
1da177e4 797 /* Cleanup all matches */
dcea992a 798 xt_ematch_foreach(ematch, e)
6bdb331b 799 cleanup_match(ematch, net);
1da177e4 800 t = ipt_get_target(e);
a2df1648 801
add67461 802 par.net = net;
a2df1648
JE
803 par.target = t->u.kernel.target;
804 par.targinfo = t->data;
916a917d 805 par.family = NFPROTO_IPV4;
a2df1648
JE
806 if (par.target->destroy != NULL)
807 par.target->destroy(&par);
808 module_put(par.target->me);
1da177e4
LT
809}
810
811/* Checks and translates the user-supplied table segment (held in
812 newinfo) */
813static int
0f234214
JE
814translate_table(struct net *net, struct xt_table_info *newinfo, void *entry0,
815 const struct ipt_replace *repl)
1da177e4 816{
72b2b1dd 817 struct ipt_entry *iter;
1da177e4 818 unsigned int i;
72b2b1dd 819 int ret = 0;
1da177e4 820
0f234214
JE
821 newinfo->size = repl->size;
822 newinfo->number = repl->num_entries;
1da177e4
LT
823
824 /* Init all hooks to impossible value. */
6e23ae2a 825 for (i = 0; i < NF_INET_NUMHOOKS; i++) {
1da177e4
LT
826 newinfo->hook_entry[i] = 0xFFFFFFFF;
827 newinfo->underflow[i] = 0xFFFFFFFF;
828 }
829
830 duprintf("translate_table: size %u\n", newinfo->size);
831 i = 0;
832 /* Walk through entries, checking offsets. */
72b2b1dd
JE
833 xt_entry_foreach(iter, entry0, newinfo->size) {
834 ret = check_entry_size_and_hooks(iter, newinfo, entry0,
6b4ff2d7
JE
835 entry0 + repl->size,
836 repl->hook_entry,
837 repl->underflow,
838 repl->valid_hooks);
72b2b1dd 839 if (ret != 0)
0559518b
JE
840 return ret;
841 ++i;
f3c5c1bf
JE
842 if (strcmp(ipt_get_target(iter)->u.user.name,
843 XT_ERROR_TARGET) == 0)
844 ++newinfo->stacksize;
72b2b1dd 845 }
1da177e4 846
0f234214 847 if (i != repl->num_entries) {
1da177e4 848 duprintf("translate_table: %u not %u entries\n",
0f234214 849 i, repl->num_entries);
1da177e4
LT
850 return -EINVAL;
851 }
852
853 /* Check hooks all assigned */
6e23ae2a 854 for (i = 0; i < NF_INET_NUMHOOKS; i++) {
1da177e4 855 /* Only hooks which are valid */
0f234214 856 if (!(repl->valid_hooks & (1 << i)))
1da177e4
LT
857 continue;
858 if (newinfo->hook_entry[i] == 0xFFFFFFFF) {
859 duprintf("Invalid hook entry %u %u\n",
0f234214 860 i, repl->hook_entry[i]);
1da177e4
LT
861 return -EINVAL;
862 }
863 if (newinfo->underflow[i] == 0xFFFFFFFF) {
864 duprintf("Invalid underflow %u %u\n",
0f234214 865 i, repl->underflow[i]);
1da177e4
LT
866 return -EINVAL;
867 }
868 }
869
0f234214 870 if (!mark_source_chains(newinfo, repl->valid_hooks, entry0))
74c9c0c1
DM
871 return -ELOOP;
872
1da177e4
LT
873 /* Finally, each sanity check must pass */
874 i = 0;
72b2b1dd 875 xt_entry_foreach(iter, entry0, newinfo->size) {
0f234214 876 ret = find_check_entry(iter, net, repl->name, repl->size);
72b2b1dd
JE
877 if (ret != 0)
878 break;
0559518b 879 ++i;
72b2b1dd 880 }
1da177e4 881
74c9c0c1 882 if (ret != 0) {
0559518b
JE
883 xt_entry_foreach(iter, entry0, newinfo->size) {
884 if (i-- == 0)
72b2b1dd 885 break;
0559518b
JE
886 cleanup_entry(iter, net);
887 }
74c9c0c1
DM
888 return ret;
889 }
1da177e4
LT
890
891 /* And one copy for every other CPU */
6f912042 892 for_each_possible_cpu(i) {
31836064
ED
893 if (newinfo->entries[i] && newinfo->entries[i] != entry0)
894 memcpy(newinfo->entries[i], entry0, newinfo->size);
1da177e4
LT
895 }
896
897 return ret;
898}
899
1da177e4 900static void
2e4e6a17
HW
901get_counters(const struct xt_table_info *t,
902 struct xt_counters counters[])
1da177e4 903{
72b2b1dd 904 struct ipt_entry *iter;
1da177e4
LT
905 unsigned int cpu;
906 unsigned int i;
31836064
ED
907 unsigned int curcpu;
908
909 /* Instead of clearing (by a previous call to memset())
910 * the counters and using adds, we set the counters
942e4a2b
SH
911 * with data used by 'current' CPU.
912 *
913 * Bottom half has to be disabled to prevent deadlock
914 * if new softirq were to run and call ipt_do_table
31836064 915 */
942e4a2b
SH
916 local_bh_disable();
917 curcpu = smp_processor_id();
31836064
ED
918
919 i = 0;
0559518b
JE
920 xt_entry_foreach(iter, t->entries[curcpu], t->size) {
921 SET_COUNTER(counters[i], iter->counters.bcnt,
6b4ff2d7 922 iter->counters.pcnt);
0559518b
JE
923 ++i;
924 }
1da177e4 925
6f912042 926 for_each_possible_cpu(cpu) {
31836064
ED
927 if (cpu == curcpu)
928 continue;
1da177e4 929 i = 0;
942e4a2b 930 xt_info_wrlock(cpu);
0559518b
JE
931 xt_entry_foreach(iter, t->entries[cpu], t->size) {
932 ADD_COUNTER(counters[i], iter->counters.bcnt,
6b4ff2d7 933 iter->counters.pcnt);
0559518b
JE
934 ++i; /* macro does multi eval of i */
935 }
942e4a2b 936 xt_info_wrunlock(cpu);
1da177e4 937 }
78454473
SH
938 local_bh_enable();
939}
940
d5d1baa1 941static struct xt_counters *alloc_counters(const struct xt_table *table)
1da177e4 942{
2722971c 943 unsigned int countersize;
2e4e6a17 944 struct xt_counters *counters;
d5d1baa1 945 const struct xt_table_info *private = table->private;
1da177e4
LT
946
947 /* We need atomic snapshot of counters: rest doesn't change
948 (other than comefrom, which userspace doesn't care
949 about). */
2e4e6a17 950 countersize = sizeof(struct xt_counters) * private->number;
31836064 951 counters = vmalloc_node(countersize, numa_node_id());
1da177e4
LT
952
953 if (counters == NULL)
942e4a2b 954 return ERR_PTR(-ENOMEM);
78454473 955
942e4a2b 956 get_counters(private, counters);
1da177e4 957
2722971c
DM
958 return counters;
959}
960
961static int
962copy_entries_to_user(unsigned int total_size,
d5d1baa1 963 const struct xt_table *table,
2722971c
DM
964 void __user *userptr)
965{
966 unsigned int off, num;
d5d1baa1 967 const struct ipt_entry *e;
2722971c 968 struct xt_counters *counters;
5452e425 969 const struct xt_table_info *private = table->private;
2722971c 970 int ret = 0;
5452e425 971 const void *loc_cpu_entry;
2722971c
DM
972
973 counters = alloc_counters(table);
974 if (IS_ERR(counters))
975 return PTR_ERR(counters);
976
31836064
ED
977 /* choose the copy that is on our node/cpu, ...
978 * This choice is lazy (because current thread is
979 * allowed to migrate to another cpu)
980 */
2e4e6a17 981 loc_cpu_entry = private->entries[raw_smp_processor_id()];
31836064 982 if (copy_to_user(userptr, loc_cpu_entry, total_size) != 0) {
1da177e4
LT
983 ret = -EFAULT;
984 goto free_counters;
985 }
986
987 /* FIXME: use iterator macros --RR */
988 /* ... then go back and fix counters and names */
989 for (off = 0, num = 0; off < total_size; off += e->next_offset, num++){
990 unsigned int i;
5452e425
JE
991 const struct ipt_entry_match *m;
992 const struct ipt_entry_target *t;
1da177e4 993
31836064 994 e = (struct ipt_entry *)(loc_cpu_entry + off);
1da177e4
LT
995 if (copy_to_user(userptr + off
996 + offsetof(struct ipt_entry, counters),
997 &counters[num],
998 sizeof(counters[num])) != 0) {
999 ret = -EFAULT;
1000 goto free_counters;
1001 }
1002
1003 for (i = sizeof(struct ipt_entry);
1004 i < e->target_offset;
1005 i += m->u.match_size) {
1006 m = (void *)e + i;
1007
1008 if (copy_to_user(userptr + off + i
1009 + offsetof(struct ipt_entry_match,
1010 u.user.name),
1011 m->u.kernel.match->name,
1012 strlen(m->u.kernel.match->name)+1)
1013 != 0) {
1014 ret = -EFAULT;
1015 goto free_counters;
1016 }
1017 }
1018
d5d1baa1 1019 t = ipt_get_target_c(e);
1da177e4
LT
1020 if (copy_to_user(userptr + off + e->target_offset
1021 + offsetof(struct ipt_entry_target,
1022 u.user.name),
1023 t->u.kernel.target->name,
1024 strlen(t->u.kernel.target->name)+1) != 0) {
1025 ret = -EFAULT;
1026 goto free_counters;
1027 }
1028 }
1029
1030 free_counters:
1031 vfree(counters);
1032 return ret;
1033}
1034
2722971c 1035#ifdef CONFIG_COMPAT
739674fb 1036static void compat_standard_from_user(void *dst, const void *src)
2722971c 1037{
9fa492cd 1038 int v = *(compat_int_t *)src;
2722971c 1039
9fa492cd 1040 if (v > 0)
b386d9f5 1041 v += xt_compat_calc_jump(AF_INET, v);
9fa492cd
PM
1042 memcpy(dst, &v, sizeof(v));
1043}
46c5ea3c 1044
739674fb 1045static int compat_standard_to_user(void __user *dst, const void *src)
2722971c 1046{
9fa492cd 1047 compat_int_t cv = *(int *)src;
2722971c 1048
9fa492cd 1049 if (cv > 0)
b386d9f5 1050 cv -= xt_compat_calc_jump(AF_INET, cv);
9fa492cd 1051 return copy_to_user(dst, &cv, sizeof(cv)) ? -EFAULT : 0;
2722971c
DM
1052}
1053
d5d1baa1 1054static int compat_calc_entry(const struct ipt_entry *e,
4b478248 1055 const struct xt_table_info *info,
d5d1baa1 1056 const void *base, struct xt_table_info *newinfo)
2722971c 1057{
dcea992a 1058 const struct xt_entry_match *ematch;
d5d1baa1 1059 const struct ipt_entry_target *t;
e5b5ef7d 1060 unsigned int entry_offset;
2722971c
DM
1061 int off, i, ret;
1062
30c08c41 1063 off = sizeof(struct ipt_entry) - sizeof(struct compat_ipt_entry);
2722971c 1064 entry_offset = (void *)e - base;
dcea992a 1065 xt_ematch_foreach(ematch, e)
6bdb331b 1066 off += xt_compat_match_offset(ematch->u.kernel.match);
d5d1baa1 1067 t = ipt_get_target_c(e);
9fa492cd 1068 off += xt_compat_target_offset(t->u.kernel.target);
2722971c 1069 newinfo->size -= off;
b386d9f5 1070 ret = xt_compat_add_offset(AF_INET, entry_offset, off);
2722971c
DM
1071 if (ret)
1072 return ret;
1073
6e23ae2a 1074 for (i = 0; i < NF_INET_NUMHOOKS; i++) {
4b478248
PM
1075 if (info->hook_entry[i] &&
1076 (e < (struct ipt_entry *)(base + info->hook_entry[i])))
2722971c 1077 newinfo->hook_entry[i] -= off;
4b478248
PM
1078 if (info->underflow[i] &&
1079 (e < (struct ipt_entry *)(base + info->underflow[i])))
2722971c
DM
1080 newinfo->underflow[i] -= off;
1081 }
1082 return 0;
1083}
1084
259d4e41 1085static int compat_table_info(const struct xt_table_info *info,
4b478248 1086 struct xt_table_info *newinfo)
2722971c 1087{
72b2b1dd 1088 struct ipt_entry *iter;
2722971c 1089 void *loc_cpu_entry;
0559518b 1090 int ret;
2722971c
DM
1091
1092 if (!newinfo || !info)
1093 return -EINVAL;
1094
259d4e41
ED
1095 /* we dont care about newinfo->entries[] */
1096 memcpy(newinfo, info, offsetof(struct xt_table_info, entries));
1097 newinfo->initial_entries = 0;
2722971c 1098 loc_cpu_entry = info->entries[raw_smp_processor_id()];
72b2b1dd
JE
1099 xt_entry_foreach(iter, loc_cpu_entry, info->size) {
1100 ret = compat_calc_entry(iter, info, loc_cpu_entry, newinfo);
1101 if (ret != 0)
0559518b 1102 return ret;
72b2b1dd 1103 }
0559518b 1104 return 0;
2722971c
DM
1105}
1106#endif
1107
d5d1baa1
JE
1108static int get_info(struct net *net, void __user *user,
1109 const int *len, int compat)
2722971c
DM
1110{
1111 char name[IPT_TABLE_MAXNAMELEN];
e60a13e0 1112 struct xt_table *t;
2722971c
DM
1113 int ret;
1114
1115 if (*len != sizeof(struct ipt_getinfo)) {
c9d8fe13
PM
1116 duprintf("length %u != %zu\n", *len,
1117 sizeof(struct ipt_getinfo));
2722971c
DM
1118 return -EINVAL;
1119 }
1120
1121 if (copy_from_user(name, user, sizeof(name)) != 0)
1122 return -EFAULT;
1123
1124 name[IPT_TABLE_MAXNAMELEN-1] = '\0';
1125#ifdef CONFIG_COMPAT
1126 if (compat)
1127 xt_compat_lock(AF_INET);
1128#endif
34bd137b 1129 t = try_then_request_module(xt_find_table_lock(net, AF_INET, name),
4b478248 1130 "iptable_%s", name);
2722971c
DM
1131 if (t && !IS_ERR(t)) {
1132 struct ipt_getinfo info;
5452e425 1133 const struct xt_table_info *private = t->private;
2722971c 1134#ifdef CONFIG_COMPAT
14c7dbe0
AD
1135 struct xt_table_info tmp;
1136
2722971c 1137 if (compat) {
2722971c 1138 ret = compat_table_info(private, &tmp);
b386d9f5 1139 xt_compat_flush_offsets(AF_INET);
4b478248 1140 private = &tmp;
2722971c
DM
1141 }
1142#endif
1143 info.valid_hooks = t->valid_hooks;
1144 memcpy(info.hook_entry, private->hook_entry,
4b478248 1145 sizeof(info.hook_entry));
2722971c 1146 memcpy(info.underflow, private->underflow,
4b478248 1147 sizeof(info.underflow));
2722971c
DM
1148 info.num_entries = private->number;
1149 info.size = private->size;
1150 strcpy(info.name, name);
1151
1152 if (copy_to_user(user, &info, *len) != 0)
1153 ret = -EFAULT;
1154 else
1155 ret = 0;
1156
1157 xt_table_unlock(t);
1158 module_put(t->me);
1159 } else
1160 ret = t ? PTR_ERR(t) : -ENOENT;
1161#ifdef CONFIG_COMPAT
1162 if (compat)
1163 xt_compat_unlock(AF_INET);
1164#endif
1165 return ret;
1166}
1167
1168static int
d5d1baa1
JE
1169get_entries(struct net *net, struct ipt_get_entries __user *uptr,
1170 const int *len)
2722971c
DM
1171{
1172 int ret;
1173 struct ipt_get_entries get;
e60a13e0 1174 struct xt_table *t;
2722971c
DM
1175
1176 if (*len < sizeof(get)) {
c9d8fe13 1177 duprintf("get_entries: %u < %zu\n", *len, sizeof(get));
2722971c
DM
1178 return -EINVAL;
1179 }
1180 if (copy_from_user(&get, uptr, sizeof(get)) != 0)
1181 return -EFAULT;
1182 if (*len != sizeof(struct ipt_get_entries) + get.size) {
c9d8fe13
PM
1183 duprintf("get_entries: %u != %zu\n",
1184 *len, sizeof(get) + get.size);
2722971c
DM
1185 return -EINVAL;
1186 }
1187
34bd137b 1188 t = xt_find_table_lock(net, AF_INET, get.name);
2722971c 1189 if (t && !IS_ERR(t)) {
5452e425 1190 const struct xt_table_info *private = t->private;
9c547959 1191 duprintf("t->private->number = %u\n", private->number);
2722971c
DM
1192 if (get.size == private->size)
1193 ret = copy_entries_to_user(private->size,
1194 t, uptr->entrytable);
1195 else {
1196 duprintf("get_entries: I've got %u not %u!\n",
9c547959 1197 private->size, get.size);
544473c1 1198 ret = -EAGAIN;
2722971c
DM
1199 }
1200 module_put(t->me);
1201 xt_table_unlock(t);
1202 } else
1203 ret = t ? PTR_ERR(t) : -ENOENT;
1204
1205 return ret;
1206}
1207
1208static int
34bd137b 1209__do_replace(struct net *net, const char *name, unsigned int valid_hooks,
4b478248
PM
1210 struct xt_table_info *newinfo, unsigned int num_counters,
1211 void __user *counters_ptr)
2722971c
DM
1212{
1213 int ret;
e60a13e0 1214 struct xt_table *t;
2722971c
DM
1215 struct xt_table_info *oldinfo;
1216 struct xt_counters *counters;
1217 void *loc_cpu_old_entry;
72b2b1dd 1218 struct ipt_entry *iter;
2722971c
DM
1219
1220 ret = 0;
1221 counters = vmalloc(num_counters * sizeof(struct xt_counters));
1222 if (!counters) {
1223 ret = -ENOMEM;
1224 goto out;
1225 }
1226
34bd137b 1227 t = try_then_request_module(xt_find_table_lock(net, AF_INET, name),
2722971c
DM
1228 "iptable_%s", name);
1229 if (!t || IS_ERR(t)) {
1230 ret = t ? PTR_ERR(t) : -ENOENT;
1231 goto free_newinfo_counters_untrans;
1232 }
1233
1234 /* You lied! */
1235 if (valid_hooks != t->valid_hooks) {
1236 duprintf("Valid hook crap: %08X vs %08X\n",
1237 valid_hooks, t->valid_hooks);
1238 ret = -EINVAL;
1239 goto put_module;
1240 }
1241
1242 oldinfo = xt_replace_table(t, num_counters, newinfo, &ret);
1243 if (!oldinfo)
1244 goto put_module;
1245
1246 /* Update module usage count based on number of rules */
1247 duprintf("do_replace: oldnum=%u, initnum=%u, newnum=%u\n",
1248 oldinfo->number, oldinfo->initial_entries, newinfo->number);
1249 if ((oldinfo->number > oldinfo->initial_entries) ||
1250 (newinfo->number <= oldinfo->initial_entries))
1251 module_put(t->me);
1252 if ((oldinfo->number > oldinfo->initial_entries) &&
1253 (newinfo->number <= oldinfo->initial_entries))
1254 module_put(t->me);
1255
942e4a2b 1256 /* Get the old counters, and synchronize with replace */
2722971c 1257 get_counters(oldinfo, counters);
942e4a2b 1258
2722971c
DM
1259 /* Decrease module usage counts and free resource */
1260 loc_cpu_old_entry = oldinfo->entries[raw_smp_processor_id()];
72b2b1dd 1261 xt_entry_foreach(iter, loc_cpu_old_entry, oldinfo->size)
0559518b 1262 cleanup_entry(iter, net);
72b2b1dd 1263
2722971c
DM
1264 xt_free_table_info(oldinfo);
1265 if (copy_to_user(counters_ptr, counters,
1266 sizeof(struct xt_counters) * num_counters) != 0)
1267 ret = -EFAULT;
1268 vfree(counters);
1269 xt_table_unlock(t);
1270 return ret;
1271
1272 put_module:
1273 module_put(t->me);
1274 xt_table_unlock(t);
1275 free_newinfo_counters_untrans:
1276 vfree(counters);
1277 out:
1278 return ret;
1279}
1280
1281static int
d5d1baa1 1282do_replace(struct net *net, const void __user *user, unsigned int len)
2722971c
DM
1283{
1284 int ret;
1285 struct ipt_replace tmp;
1286 struct xt_table_info *newinfo;
1287 void *loc_cpu_entry;
72b2b1dd 1288 struct ipt_entry *iter;
2722971c
DM
1289
1290 if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1291 return -EFAULT;
1292
2722971c 1293 /* overflow check */
2722971c
DM
1294 if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters))
1295 return -ENOMEM;
1296
1297 newinfo = xt_alloc_table_info(tmp.size);
1298 if (!newinfo)
1299 return -ENOMEM;
1300
9c547959 1301 /* choose the copy that is on our node/cpu */
2722971c
DM
1302 loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
1303 if (copy_from_user(loc_cpu_entry, user + sizeof(tmp),
1304 tmp.size) != 0) {
1305 ret = -EFAULT;
1306 goto free_newinfo;
1307 }
1308
0f234214 1309 ret = translate_table(net, newinfo, loc_cpu_entry, &tmp);
2722971c
DM
1310 if (ret != 0)
1311 goto free_newinfo;
1312
ff67e4e4 1313 duprintf("Translated table\n");
2722971c 1314
34bd137b 1315 ret = __do_replace(net, tmp.name, tmp.valid_hooks, newinfo,
4b478248 1316 tmp.num_counters, tmp.counters);
2722971c
DM
1317 if (ret)
1318 goto free_newinfo_untrans;
1319 return 0;
1320
1321 free_newinfo_untrans:
72b2b1dd 1322 xt_entry_foreach(iter, loc_cpu_entry, newinfo->size)
0559518b 1323 cleanup_entry(iter, net);
2722971c
DM
1324 free_newinfo:
1325 xt_free_table_info(newinfo);
1326 return ret;
1327}
1328
2722971c 1329static int
d5d1baa1
JE
1330do_add_counters(struct net *net, const void __user *user,
1331 unsigned int len, int compat)
2722971c 1332{
942e4a2b 1333 unsigned int i, curcpu;
2722971c
DM
1334 struct xt_counters_info tmp;
1335 struct xt_counters *paddc;
1336 unsigned int num_counters;
5452e425 1337 const char *name;
2722971c
DM
1338 int size;
1339 void *ptmp;
e60a13e0 1340 struct xt_table *t;
5452e425 1341 const struct xt_table_info *private;
2722971c
DM
1342 int ret = 0;
1343 void *loc_cpu_entry;
72b2b1dd 1344 struct ipt_entry *iter;
2722971c
DM
1345#ifdef CONFIG_COMPAT
1346 struct compat_xt_counters_info compat_tmp;
1347
1348 if (compat) {
1349 ptmp = &compat_tmp;
1350 size = sizeof(struct compat_xt_counters_info);
1351 } else
1352#endif
1353 {
1354 ptmp = &tmp;
1355 size = sizeof(struct xt_counters_info);
1356 }
1357
1358 if (copy_from_user(ptmp, user, size) != 0)
1359 return -EFAULT;
1360
1361#ifdef CONFIG_COMPAT
1362 if (compat) {
1363 num_counters = compat_tmp.num_counters;
1364 name = compat_tmp.name;
1365 } else
1366#endif
1367 {
1368 num_counters = tmp.num_counters;
1369 name = tmp.name;
1370 }
1371
1372 if (len != size + num_counters * sizeof(struct xt_counters))
1373 return -EINVAL;
1374
1375 paddc = vmalloc_node(len - size, numa_node_id());
1376 if (!paddc)
1377 return -ENOMEM;
1378
1379 if (copy_from_user(paddc, user + size, len - size) != 0) {
1380 ret = -EFAULT;
1381 goto free;
1382 }
1383
34bd137b 1384 t = xt_find_table_lock(net, AF_INET, name);
2722971c
DM
1385 if (!t || IS_ERR(t)) {
1386 ret = t ? PTR_ERR(t) : -ENOENT;
1387 goto free;
1388 }
1389
942e4a2b 1390 local_bh_disable();
2722971c
DM
1391 private = t->private;
1392 if (private->number != num_counters) {
1393 ret = -EINVAL;
1394 goto unlock_up_free;
1395 }
1396
1397 i = 0;
1398 /* Choose the copy that is on our node */
942e4a2b
SH
1399 curcpu = smp_processor_id();
1400 loc_cpu_entry = private->entries[curcpu];
1401 xt_info_wrlock(curcpu);
0559518b
JE
1402 xt_entry_foreach(iter, loc_cpu_entry, private->size) {
1403 ADD_COUNTER(iter->counters, paddc[i].bcnt, paddc[i].pcnt);
1404 ++i;
1405 }
942e4a2b 1406 xt_info_wrunlock(curcpu);
2722971c 1407 unlock_up_free:
942e4a2b 1408 local_bh_enable();
2722971c
DM
1409 xt_table_unlock(t);
1410 module_put(t->me);
1411 free:
1412 vfree(paddc);
1413
1414 return ret;
1415}
1416
1417#ifdef CONFIG_COMPAT
1418struct compat_ipt_replace {
1419 char name[IPT_TABLE_MAXNAMELEN];
1420 u32 valid_hooks;
1421 u32 num_entries;
1422 u32 size;
6e23ae2a
PM
1423 u32 hook_entry[NF_INET_NUMHOOKS];
1424 u32 underflow[NF_INET_NUMHOOKS];
2722971c
DM
1425 u32 num_counters;
1426 compat_uptr_t counters; /* struct ipt_counters * */
1427 struct compat_ipt_entry entries[0];
1428};
1429
a18aa31b
PM
1430static int
1431compat_copy_entry_to_user(struct ipt_entry *e, void __user **dstptr,
b0a6363c 1432 unsigned int *size, struct xt_counters *counters,
0559518b 1433 unsigned int i)
2722971c 1434{
3e597c60 1435 struct ipt_entry_target *t;
2722971c
DM
1436 struct compat_ipt_entry __user *ce;
1437 u_int16_t target_offset, next_offset;
1438 compat_uint_t origsize;
dcea992a
JE
1439 const struct xt_entry_match *ematch;
1440 int ret = 0;
2722971c 1441
2722971c
DM
1442 origsize = *size;
1443 ce = (struct compat_ipt_entry __user *)*dstptr;
0559518b
JE
1444 if (copy_to_user(ce, e, sizeof(struct ipt_entry)) != 0 ||
1445 copy_to_user(&ce->counters, &counters[i],
1446 sizeof(counters[i])) != 0)
1447 return -EFAULT;
a18aa31b 1448
2722971c 1449 *dstptr += sizeof(struct compat_ipt_entry);
30c08c41
PM
1450 *size -= sizeof(struct ipt_entry) - sizeof(struct compat_ipt_entry);
1451
dcea992a
JE
1452 xt_ematch_foreach(ematch, e) {
1453 ret = xt_compat_match_to_user(ematch, dstptr, size);
1454 if (ret != 0)
6bdb331b 1455 return ret;
dcea992a 1456 }
2722971c 1457 target_offset = e->target_offset - (origsize - *size);
2722971c 1458 t = ipt_get_target(e);
9fa492cd 1459 ret = xt_compat_target_to_user(t, dstptr, size);
2722971c 1460 if (ret)
0559518b 1461 return ret;
2722971c 1462 next_offset = e->next_offset - (origsize - *size);
0559518b
JE
1463 if (put_user(target_offset, &ce->target_offset) != 0 ||
1464 put_user(next_offset, &ce->next_offset) != 0)
1465 return -EFAULT;
2722971c 1466 return 0;
2722971c
DM
1467}
1468
022748a9 1469static int
4c1b52bc 1470compat_find_calc_match(struct ipt_entry_match *m,
4b478248
PM
1471 const char *name,
1472 const struct ipt_ip *ip,
1473 unsigned int hookmask,
6bdb331b 1474 int *size)
2722971c 1475{
6709dbbb 1476 struct xt_match *match;
2722971c 1477
fd0ec0e6
JE
1478 match = xt_request_find_match(NFPROTO_IPV4, m->u.user.name,
1479 m->u.user.revision);
1480 if (IS_ERR(match)) {
2722971c 1481 duprintf("compat_check_calc_match: `%s' not found\n",
4b478248 1482 m->u.user.name);
fd0ec0e6 1483 return PTR_ERR(match);
2722971c
DM
1484 }
1485 m->u.kernel.match = match;
9fa492cd 1486 *size += xt_compat_match_offset(match);
4c1b52bc
DM
1487 return 0;
1488}
1489
0559518b 1490static void compat_release_entry(struct compat_ipt_entry *e)
4c1b52bc
DM
1491{
1492 struct ipt_entry_target *t;
dcea992a 1493 struct xt_entry_match *ematch;
4c1b52bc 1494
4c1b52bc 1495 /* Cleanup all matches */
dcea992a 1496 xt_ematch_foreach(ematch, e)
6bdb331b 1497 module_put(ematch->u.kernel.match->me);
73cd598d 1498 t = compat_ipt_get_target(e);
4c1b52bc 1499 module_put(t->u.kernel.target->me);
4c1b52bc
DM
1500}
1501
022748a9 1502static int
73cd598d 1503check_compat_entry_size_and_hooks(struct compat_ipt_entry *e,
4b478248
PM
1504 struct xt_table_info *newinfo,
1505 unsigned int *size,
d5d1baa1
JE
1506 const unsigned char *base,
1507 const unsigned char *limit,
1508 const unsigned int *hook_entries,
1509 const unsigned int *underflows,
4b478248 1510 const char *name)
2722971c 1511{
dcea992a 1512 struct xt_entry_match *ematch;
2722971c 1513 struct ipt_entry_target *t;
6709dbbb 1514 struct xt_target *target;
e5b5ef7d 1515 unsigned int entry_offset;
b0a6363c
PM
1516 unsigned int j;
1517 int ret, off, h;
2722971c
DM
1518
1519 duprintf("check_compat_entry_size_and_hooks %p\n", e);
3666ed1c
JP
1520 if ((unsigned long)e % __alignof__(struct compat_ipt_entry) != 0 ||
1521 (unsigned char *)e + sizeof(struct compat_ipt_entry) >= limit) {
2722971c
DM
1522 duprintf("Bad offset %p, limit = %p\n", e, limit);
1523 return -EINVAL;
1524 }
1525
1526 if (e->next_offset < sizeof(struct compat_ipt_entry) +
4b478248 1527 sizeof(struct compat_xt_entry_target)) {
2722971c
DM
1528 duprintf("checking: element %p size %u\n",
1529 e, e->next_offset);
1530 return -EINVAL;
1531 }
1532
73cd598d
PM
1533 /* For purposes of check_entry casting the compat entry is fine */
1534 ret = check_entry((struct ipt_entry *)e, name);
a96be246
DM
1535 if (ret)
1536 return ret;
590bdf7f 1537
30c08c41 1538 off = sizeof(struct ipt_entry) - sizeof(struct compat_ipt_entry);
2722971c
DM
1539 entry_offset = (void *)e - (void *)base;
1540 j = 0;
dcea992a
JE
1541 xt_ematch_foreach(ematch, e) {
1542 ret = compat_find_calc_match(ematch, name,
6b4ff2d7 1543 &e->ip, e->comefrom, &off);
dcea992a 1544 if (ret != 0)
6bdb331b
JE
1545 goto release_matches;
1546 ++j;
dcea992a 1547 }
2722971c 1548
73cd598d 1549 t = compat_ipt_get_target(e);
d2a7b6ba
JE
1550 target = xt_request_find_target(NFPROTO_IPV4, t->u.user.name,
1551 t->u.user.revision);
1552 if (IS_ERR(target)) {
a96be246 1553 duprintf("check_compat_entry_size_and_hooks: `%s' not found\n",
4b478248 1554 t->u.user.name);
d2a7b6ba 1555 ret = PTR_ERR(target);
4c1b52bc 1556 goto release_matches;
2722971c
DM
1557 }
1558 t->u.kernel.target = target;
1559
9fa492cd 1560 off += xt_compat_target_offset(target);
2722971c 1561 *size += off;
b386d9f5 1562 ret = xt_compat_add_offset(AF_INET, entry_offset, off);
2722971c
DM
1563 if (ret)
1564 goto out;
1565
1566 /* Check hooks & underflows */
6e23ae2a 1567 for (h = 0; h < NF_INET_NUMHOOKS; h++) {
2722971c
DM
1568 if ((unsigned char *)e - base == hook_entries[h])
1569 newinfo->hook_entry[h] = hook_entries[h];
1570 if ((unsigned char *)e - base == underflows[h])
1571 newinfo->underflow[h] = underflows[h];
1572 }
1573
1574 /* Clear counters and comefrom */
73cd598d 1575 memset(&e->counters, 0, sizeof(e->counters));
2722971c 1576 e->comefrom = 0;
2722971c 1577 return 0;
bec71b16 1578
2722971c 1579out:
bec71b16 1580 module_put(t->u.kernel.target->me);
4c1b52bc 1581release_matches:
6bdb331b
JE
1582 xt_ematch_foreach(ematch, e) {
1583 if (j-- == 0)
dcea992a 1584 break;
6bdb331b
JE
1585 module_put(ematch->u.kernel.match->me);
1586 }
2722971c
DM
1587 return ret;
1588}
1589
4b478248 1590static int
73cd598d 1591compat_copy_entry_from_user(struct compat_ipt_entry *e, void **dstptr,
4b478248
PM
1592 unsigned int *size, const char *name,
1593 struct xt_table_info *newinfo, unsigned char *base)
2722971c
DM
1594{
1595 struct ipt_entry_target *t;
6709dbbb 1596 struct xt_target *target;
2722971c
DM
1597 struct ipt_entry *de;
1598 unsigned int origsize;
920b868a 1599 int ret, h;
dcea992a 1600 struct xt_entry_match *ematch;
2722971c
DM
1601
1602 ret = 0;
1603 origsize = *size;
1604 de = (struct ipt_entry *)*dstptr;
1605 memcpy(de, e, sizeof(struct ipt_entry));
73cd598d 1606 memcpy(&de->counters, &e->counters, sizeof(e->counters));
2722971c 1607
73cd598d 1608 *dstptr += sizeof(struct ipt_entry);
30c08c41
PM
1609 *size += sizeof(struct ipt_entry) - sizeof(struct compat_ipt_entry);
1610
dcea992a
JE
1611 xt_ematch_foreach(ematch, e) {
1612 ret = xt_compat_match_from_user(ematch, dstptr, size);
1613 if (ret != 0)
6bdb331b 1614 return ret;
dcea992a 1615 }
2722971c 1616 de->target_offset = e->target_offset - (origsize - *size);
73cd598d 1617 t = compat_ipt_get_target(e);
2722971c 1618 target = t->u.kernel.target;
9fa492cd 1619 xt_compat_target_from_user(t, dstptr, size);
2722971c
DM
1620
1621 de->next_offset = e->next_offset - (origsize - *size);
6e23ae2a 1622 for (h = 0; h < NF_INET_NUMHOOKS; h++) {
2722971c
DM
1623 if ((unsigned char *)de - base < newinfo->hook_entry[h])
1624 newinfo->hook_entry[h] -= origsize - *size;
1625 if ((unsigned char *)de - base < newinfo->underflow[h])
1626 newinfo->underflow[h] -= origsize - *size;
1627 }
f6677f43
DM
1628 return ret;
1629}
1630
022748a9 1631static int
0559518b 1632compat_check_entry(struct ipt_entry *e, struct net *net, const char *name)
f6677f43 1633{
dcea992a 1634 struct xt_entry_match *ematch;
9b4fce7a 1635 struct xt_mtchk_param mtpar;
b0a6363c 1636 unsigned int j;
dcea992a 1637 int ret = 0;
f6677f43 1638
4c1b52bc 1639 j = 0;
a83d8e8d 1640 mtpar.net = net;
9b4fce7a
JE
1641 mtpar.table = name;
1642 mtpar.entryinfo = &e->ip;
1643 mtpar.hook_mask = e->comefrom;
916a917d 1644 mtpar.family = NFPROTO_IPV4;
dcea992a 1645 xt_ematch_foreach(ematch, e) {
6bdb331b 1646 ret = check_match(ematch, &mtpar);
dcea992a 1647 if (ret != 0)
6bdb331b
JE
1648 goto cleanup_matches;
1649 ++j;
dcea992a 1650 }
4c1b52bc 1651
add67461 1652 ret = check_target(e, net, name);
4c1b52bc
DM
1653 if (ret)
1654 goto cleanup_matches;
4c1b52bc
DM
1655 return 0;
1656
1657 cleanup_matches:
6bdb331b
JE
1658 xt_ematch_foreach(ematch, e) {
1659 if (j-- == 0)
dcea992a 1660 break;
6bdb331b
JE
1661 cleanup_match(ematch, net);
1662 }
4c1b52bc 1663 return ret;
f6677f43
DM
1664}
1665
1da177e4 1666static int
a83d8e8d
AD
1667translate_compat_table(struct net *net,
1668 const char *name,
4b478248
PM
1669 unsigned int valid_hooks,
1670 struct xt_table_info **pinfo,
1671 void **pentry0,
1672 unsigned int total_size,
1673 unsigned int number,
1674 unsigned int *hook_entries,
1675 unsigned int *underflows)
1da177e4 1676{
920b868a 1677 unsigned int i, j;
2722971c
DM
1678 struct xt_table_info *newinfo, *info;
1679 void *pos, *entry0, *entry1;
72b2b1dd
JE
1680 struct compat_ipt_entry *iter0;
1681 struct ipt_entry *iter1;
2722971c 1682 unsigned int size;
0559518b 1683 int ret;
1da177e4 1684
2722971c
DM
1685 info = *pinfo;
1686 entry0 = *pentry0;
1687 size = total_size;
1688 info->number = number;
1689
1690 /* Init all hooks to impossible value. */
6e23ae2a 1691 for (i = 0; i < NF_INET_NUMHOOKS; i++) {
2722971c
DM
1692 info->hook_entry[i] = 0xFFFFFFFF;
1693 info->underflow[i] = 0xFFFFFFFF;
1694 }
1695
1696 duprintf("translate_compat_table: size %u\n", info->size);
920b868a 1697 j = 0;
2722971c
DM
1698 xt_compat_lock(AF_INET);
1699 /* Walk through entries, checking offsets. */
72b2b1dd
JE
1700 xt_entry_foreach(iter0, entry0, total_size) {
1701 ret = check_compat_entry_size_and_hooks(iter0, info, &size,
6b4ff2d7
JE
1702 entry0,
1703 entry0 + total_size,
1704 hook_entries,
1705 underflows,
1706 name);
72b2b1dd 1707 if (ret != 0)
0559518b
JE
1708 goto out_unlock;
1709 ++j;
72b2b1dd 1710 }
2722971c
DM
1711
1712 ret = -EINVAL;
920b868a 1713 if (j != number) {
2722971c 1714 duprintf("translate_compat_table: %u not %u entries\n",
920b868a 1715 j, number);
2722971c
DM
1716 goto out_unlock;
1717 }
1718
1719 /* Check hooks all assigned */
6e23ae2a 1720 for (i = 0; i < NF_INET_NUMHOOKS; i++) {
2722971c
DM
1721 /* Only hooks which are valid */
1722 if (!(valid_hooks & (1 << i)))
1723 continue;
1724 if (info->hook_entry[i] == 0xFFFFFFFF) {
1725 duprintf("Invalid hook entry %u %u\n",
1726 i, hook_entries[i]);
1727 goto out_unlock;
1da177e4 1728 }
2722971c
DM
1729 if (info->underflow[i] == 0xFFFFFFFF) {
1730 duprintf("Invalid underflow %u %u\n",
1731 i, underflows[i]);
1732 goto out_unlock;
1733 }
1734 }
1735
1736 ret = -ENOMEM;
1737 newinfo = xt_alloc_table_info(size);
1738 if (!newinfo)
1739 goto out_unlock;
1740
1741 newinfo->number = number;
6e23ae2a 1742 for (i = 0; i < NF_INET_NUMHOOKS; i++) {
2722971c
DM
1743 newinfo->hook_entry[i] = info->hook_entry[i];
1744 newinfo->underflow[i] = info->underflow[i];
1745 }
1746 entry1 = newinfo->entries[raw_smp_processor_id()];
1747 pos = entry1;
4b478248 1748 size = total_size;
72b2b1dd 1749 xt_entry_foreach(iter0, entry0, total_size) {
6b4ff2d7
JE
1750 ret = compat_copy_entry_from_user(iter0, &pos, &size,
1751 name, newinfo, entry1);
72b2b1dd
JE
1752 if (ret != 0)
1753 break;
1754 }
b386d9f5 1755 xt_compat_flush_offsets(AF_INET);
2722971c
DM
1756 xt_compat_unlock(AF_INET);
1757 if (ret)
1758 goto free_newinfo;
1759
1760 ret = -ELOOP;
1761 if (!mark_source_chains(newinfo, valid_hooks, entry1))
1762 goto free_newinfo;
1763
4c1b52bc 1764 i = 0;
72b2b1dd 1765 xt_entry_foreach(iter1, entry1, newinfo->size) {
0559518b 1766 ret = compat_check_entry(iter1, net, name);
72b2b1dd
JE
1767 if (ret != 0)
1768 break;
0559518b 1769 ++i;
72b2b1dd 1770 }
4c1b52bc 1771 if (ret) {
72b2b1dd
JE
1772 /*
1773 * The first i matches need cleanup_entry (calls ->destroy)
1774 * because they had called ->check already. The other j-i
1775 * entries need only release.
1776 */
1777 int skip = i;
4c1b52bc 1778 j -= i;
72b2b1dd
JE
1779 xt_entry_foreach(iter0, entry0, newinfo->size) {
1780 if (skip-- > 0)
1781 continue;
0559518b 1782 if (j-- == 0)
72b2b1dd 1783 break;
0559518b 1784 compat_release_entry(iter0);
72b2b1dd 1785 }
0559518b
JE
1786 xt_entry_foreach(iter1, entry1, newinfo->size) {
1787 if (i-- == 0)
72b2b1dd 1788 break;
0559518b
JE
1789 cleanup_entry(iter1, net);
1790 }
4c1b52bc
DM
1791 xt_free_table_info(newinfo);
1792 return ret;
1793 }
f6677f43 1794
2722971c 1795 /* And one copy for every other CPU */
fb1bb34d 1796 for_each_possible_cpu(i)
2722971c
DM
1797 if (newinfo->entries[i] && newinfo->entries[i] != entry1)
1798 memcpy(newinfo->entries[i], entry1, newinfo->size);
1799
1800 *pinfo = newinfo;
1801 *pentry0 = entry1;
1802 xt_free_table_info(info);
1803 return 0;
1da177e4 1804
2722971c
DM
1805free_newinfo:
1806 xt_free_table_info(newinfo);
1807out:
0559518b
JE
1808 xt_entry_foreach(iter0, entry0, total_size) {
1809 if (j-- == 0)
72b2b1dd 1810 break;
0559518b
JE
1811 compat_release_entry(iter0);
1812 }
1da177e4 1813 return ret;
2722971c 1814out_unlock:
b386d9f5 1815 xt_compat_flush_offsets(AF_INET);
2722971c
DM
1816 xt_compat_unlock(AF_INET);
1817 goto out;
1da177e4
LT
1818}
1819
1820static int
34bd137b 1821compat_do_replace(struct net *net, void __user *user, unsigned int len)
1da177e4
LT
1822{
1823 int ret;
2722971c
DM
1824 struct compat_ipt_replace tmp;
1825 struct xt_table_info *newinfo;
1826 void *loc_cpu_entry;
72b2b1dd 1827 struct ipt_entry *iter;
1da177e4
LT
1828
1829 if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1830 return -EFAULT;
1831
ee4bb818 1832 /* overflow check */
259d4e41 1833 if (tmp.size >= INT_MAX / num_possible_cpus())
ee4bb818
KK
1834 return -ENOMEM;
1835 if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters))
1836 return -ENOMEM;
1837
2e4e6a17 1838 newinfo = xt_alloc_table_info(tmp.size);
1da177e4
LT
1839 if (!newinfo)
1840 return -ENOMEM;
1841
9c547959 1842 /* choose the copy that is on our node/cpu */
31836064
ED
1843 loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
1844 if (copy_from_user(loc_cpu_entry, user + sizeof(tmp),
1da177e4
LT
1845 tmp.size) != 0) {
1846 ret = -EFAULT;
1847 goto free_newinfo;
1848 }
1849
a83d8e8d 1850 ret = translate_compat_table(net, tmp.name, tmp.valid_hooks,
4b478248
PM
1851 &newinfo, &loc_cpu_entry, tmp.size,
1852 tmp.num_entries, tmp.hook_entry,
1853 tmp.underflow);
2722971c 1854 if (ret != 0)
1da177e4 1855 goto free_newinfo;
1da177e4 1856
2722971c 1857 duprintf("compat_do_replace: Translated table\n");
1da177e4 1858
34bd137b 1859 ret = __do_replace(net, tmp.name, tmp.valid_hooks, newinfo,
4b478248 1860 tmp.num_counters, compat_ptr(tmp.counters));
2722971c
DM
1861 if (ret)
1862 goto free_newinfo_untrans;
1863 return 0;
1da177e4 1864
2722971c 1865 free_newinfo_untrans:
72b2b1dd 1866 xt_entry_foreach(iter, loc_cpu_entry, newinfo->size)
0559518b 1867 cleanup_entry(iter, net);
2722971c
DM
1868 free_newinfo:
1869 xt_free_table_info(newinfo);
1870 return ret;
1871}
1da177e4 1872
2722971c
DM
1873static int
1874compat_do_ipt_set_ctl(struct sock *sk, int cmd, void __user *user,
4b478248 1875 unsigned int len)
2722971c
DM
1876{
1877 int ret;
1da177e4 1878
2722971c
DM
1879 if (!capable(CAP_NET_ADMIN))
1880 return -EPERM;
1da177e4 1881
2722971c
DM
1882 switch (cmd) {
1883 case IPT_SO_SET_REPLACE:
3b1e0a65 1884 ret = compat_do_replace(sock_net(sk), user, len);
2722971c 1885 break;
1da177e4 1886
2722971c 1887 case IPT_SO_SET_ADD_COUNTERS:
3b1e0a65 1888 ret = do_add_counters(sock_net(sk), user, len, 1);
2722971c
DM
1889 break;
1890
1891 default:
1892 duprintf("do_ipt_set_ctl: unknown request %i\n", cmd);
1893 ret = -EINVAL;
1894 }
1da177e4 1895
1da177e4
LT
1896 return ret;
1897}
1898
4b478248 1899struct compat_ipt_get_entries {
2722971c
DM
1900 char name[IPT_TABLE_MAXNAMELEN];
1901 compat_uint_t size;
1902 struct compat_ipt_entry entrytable[0];
1903};
1da177e4 1904
4b478248
PM
1905static int
1906compat_copy_entries_to_user(unsigned int total_size, struct xt_table *table,
1907 void __user *userptr)
2722971c 1908{
2722971c 1909 struct xt_counters *counters;
5452e425 1910 const struct xt_table_info *private = table->private;
2722971c
DM
1911 void __user *pos;
1912 unsigned int size;
1913 int ret = 0;
5452e425 1914 const void *loc_cpu_entry;
a18aa31b 1915 unsigned int i = 0;
72b2b1dd 1916 struct ipt_entry *iter;
1da177e4 1917
2722971c
DM
1918 counters = alloc_counters(table);
1919 if (IS_ERR(counters))
1920 return PTR_ERR(counters);
1921
1922 /* choose the copy that is on our node/cpu, ...
1923 * This choice is lazy (because current thread is
1924 * allowed to migrate to another cpu)
1925 */
1926 loc_cpu_entry = private->entries[raw_smp_processor_id()];
1927 pos = userptr;
1928 size = total_size;
72b2b1dd
JE
1929 xt_entry_foreach(iter, loc_cpu_entry, total_size) {
1930 ret = compat_copy_entry_to_user(iter, &pos,
6b4ff2d7 1931 &size, counters, i++);
72b2b1dd
JE
1932 if (ret != 0)
1933 break;
1934 }
2722971c 1935
2722971c
DM
1936 vfree(counters);
1937 return ret;
1da177e4
LT
1938}
1939
1940static int
34bd137b
AD
1941compat_get_entries(struct net *net, struct compat_ipt_get_entries __user *uptr,
1942 int *len)
1da177e4 1943{
2722971c
DM
1944 int ret;
1945 struct compat_ipt_get_entries get;
e60a13e0 1946 struct xt_table *t;
1da177e4 1947
2722971c 1948 if (*len < sizeof(get)) {
c9d8fe13 1949 duprintf("compat_get_entries: %u < %zu\n", *len, sizeof(get));
1da177e4 1950 return -EINVAL;
2722971c 1951 }
1da177e4 1952
2722971c
DM
1953 if (copy_from_user(&get, uptr, sizeof(get)) != 0)
1954 return -EFAULT;
1da177e4 1955
2722971c 1956 if (*len != sizeof(struct compat_ipt_get_entries) + get.size) {
c9d8fe13
PM
1957 duprintf("compat_get_entries: %u != %zu\n",
1958 *len, sizeof(get) + get.size);
2722971c 1959 return -EINVAL;
1da177e4
LT
1960 }
1961
2722971c 1962 xt_compat_lock(AF_INET);
34bd137b 1963 t = xt_find_table_lock(net, AF_INET, get.name);
2722971c 1964 if (t && !IS_ERR(t)) {
5452e425 1965 const struct xt_table_info *private = t->private;
2722971c 1966 struct xt_table_info info;
9c547959 1967 duprintf("t->private->number = %u\n", private->number);
2722971c
DM
1968 ret = compat_table_info(private, &info);
1969 if (!ret && get.size == info.size) {
1970 ret = compat_copy_entries_to_user(private->size,
4b478248 1971 t, uptr->entrytable);
2722971c
DM
1972 } else if (!ret) {
1973 duprintf("compat_get_entries: I've got %u not %u!\n",
9c547959 1974 private->size, get.size);
544473c1 1975 ret = -EAGAIN;
2722971c 1976 }
b386d9f5 1977 xt_compat_flush_offsets(AF_INET);
2722971c
DM
1978 module_put(t->me);
1979 xt_table_unlock(t);
1980 } else
1da177e4 1981 ret = t ? PTR_ERR(t) : -ENOENT;
1da177e4 1982
2722971c
DM
1983 xt_compat_unlock(AF_INET);
1984 return ret;
1985}
1da177e4 1986
79030ed0
PM
1987static int do_ipt_get_ctl(struct sock *, int, void __user *, int *);
1988
2722971c
DM
1989static int
1990compat_do_ipt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
1991{
1992 int ret;
1da177e4 1993
82fac054
BS
1994 if (!capable(CAP_NET_ADMIN))
1995 return -EPERM;
1996
2722971c
DM
1997 switch (cmd) {
1998 case IPT_SO_GET_INFO:
3b1e0a65 1999 ret = get_info(sock_net(sk), user, len, 1);
2722971c
DM
2000 break;
2001 case IPT_SO_GET_ENTRIES:
3b1e0a65 2002 ret = compat_get_entries(sock_net(sk), user, len);
2722971c
DM
2003 break;
2004 default:
79030ed0 2005 ret = do_ipt_get_ctl(sk, cmd, user, len);
2722971c 2006 }
1da177e4
LT
2007 return ret;
2008}
2722971c 2009#endif
1da177e4
LT
2010
2011static int
9c547959 2012do_ipt_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len)
1da177e4
LT
2013{
2014 int ret;
2015
2016 if (!capable(CAP_NET_ADMIN))
2017 return -EPERM;
2018
2019 switch (cmd) {
2020 case IPT_SO_SET_REPLACE:
3b1e0a65 2021 ret = do_replace(sock_net(sk), user, len);
1da177e4
LT
2022 break;
2023
2024 case IPT_SO_SET_ADD_COUNTERS:
3b1e0a65 2025 ret = do_add_counters(sock_net(sk), user, len, 0);
1da177e4
LT
2026 break;
2027
2028 default:
2029 duprintf("do_ipt_set_ctl: unknown request %i\n", cmd);
2030 ret = -EINVAL;
2031 }
2032
2033 return ret;
2034}
2035
2036static int
2037do_ipt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
2038{
2039 int ret;
2040
2041 if (!capable(CAP_NET_ADMIN))
2042 return -EPERM;
2043
2044 switch (cmd) {
2722971c 2045 case IPT_SO_GET_INFO:
3b1e0a65 2046 ret = get_info(sock_net(sk), user, len, 0);
2722971c 2047 break;
1da177e4 2048
2722971c 2049 case IPT_SO_GET_ENTRIES:
3b1e0a65 2050 ret = get_entries(sock_net(sk), user, len);
1da177e4 2051 break;
1da177e4
LT
2052
2053 case IPT_SO_GET_REVISION_MATCH:
2054 case IPT_SO_GET_REVISION_TARGET: {
2055 struct ipt_get_revision rev;
2e4e6a17 2056 int target;
1da177e4
LT
2057
2058 if (*len != sizeof(rev)) {
2059 ret = -EINVAL;
2060 break;
2061 }
2062 if (copy_from_user(&rev, user, sizeof(rev)) != 0) {
2063 ret = -EFAULT;
2064 break;
2065 }
2066
2067 if (cmd == IPT_SO_GET_REVISION_TARGET)
2e4e6a17 2068 target = 1;
1da177e4 2069 else
2e4e6a17 2070 target = 0;
1da177e4 2071
2e4e6a17
HW
2072 try_then_request_module(xt_find_revision(AF_INET, rev.name,
2073 rev.revision,
2074 target, &ret),
1da177e4
LT
2075 "ipt_%s", rev.name);
2076 break;
2077 }
2078
2079 default:
2080 duprintf("do_ipt_get_ctl: unknown request %i\n", cmd);
2081 ret = -EINVAL;
2082 }
2083
2084 return ret;
2085}
2086
35aad0ff
JE
2087struct xt_table *ipt_register_table(struct net *net,
2088 const struct xt_table *table,
44d34e72 2089 const struct ipt_replace *repl)
1da177e4
LT
2090{
2091 int ret;
2e4e6a17 2092 struct xt_table_info *newinfo;
f3c5c1bf 2093 struct xt_table_info bootstrap = {0};
31836064 2094 void *loc_cpu_entry;
a98da11d 2095 struct xt_table *new_table;
1da177e4 2096
2e4e6a17 2097 newinfo = xt_alloc_table_info(repl->size);
44d34e72
AD
2098 if (!newinfo) {
2099 ret = -ENOMEM;
2100 goto out;
2101 }
1da177e4 2102
9c547959 2103 /* choose the copy on our node/cpu, but dont care about preemption */
31836064
ED
2104 loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
2105 memcpy(loc_cpu_entry, repl->entries, repl->size);
1da177e4 2106
0f234214 2107 ret = translate_table(net, newinfo, loc_cpu_entry, repl);
44d34e72
AD
2108 if (ret != 0)
2109 goto out_free;
1da177e4 2110
44d34e72 2111 new_table = xt_register_table(net, table, &bootstrap, newinfo);
a98da11d 2112 if (IS_ERR(new_table)) {
44d34e72
AD
2113 ret = PTR_ERR(new_table);
2114 goto out_free;
1da177e4
LT
2115 }
2116
44d34e72
AD
2117 return new_table;
2118
2119out_free:
2120 xt_free_table_info(newinfo);
2121out:
2122 return ERR_PTR(ret);
1da177e4
LT
2123}
2124
f54e9367 2125void ipt_unregister_table(struct net *net, struct xt_table *table)
1da177e4 2126{
2e4e6a17 2127 struct xt_table_info *private;
31836064 2128 void *loc_cpu_entry;
df200969 2129 struct module *table_owner = table->me;
72b2b1dd 2130 struct ipt_entry *iter;
31836064 2131
e905a9ed 2132 private = xt_unregister_table(table);
1da177e4
LT
2133
2134 /* Decrease module usage counts and free resources */
2e4e6a17 2135 loc_cpu_entry = private->entries[raw_smp_processor_id()];
72b2b1dd 2136 xt_entry_foreach(iter, loc_cpu_entry, private->size)
0559518b 2137 cleanup_entry(iter, net);
df200969
AD
2138 if (private->number > private->initial_entries)
2139 module_put(table_owner);
2e4e6a17 2140 xt_free_table_info(private);
1da177e4
LT
2141}
2142
2143/* Returns 1 if the type and code is matched by the range, 0 otherwise */
1d93a9cb 2144static inline bool
1da177e4
LT
2145icmp_type_code_match(u_int8_t test_type, u_int8_t min_code, u_int8_t max_code,
2146 u_int8_t type, u_int8_t code,
1d93a9cb 2147 bool invert)
1da177e4 2148{
9c547959
PM
2149 return ((test_type == 0xFF) ||
2150 (type == test_type && code >= min_code && code <= max_code))
1da177e4
LT
2151 ^ invert;
2152}
2153
1d93a9cb 2154static bool
f7108a20 2155icmp_match(const struct sk_buff *skb, const struct xt_match_param *par)
1da177e4 2156{
5452e425
JE
2157 const struct icmphdr *ic;
2158 struct icmphdr _icmph;
f7108a20 2159 const struct ipt_icmp *icmpinfo = par->matchinfo;
1da177e4
LT
2160
2161 /* Must not be a fragment. */
f7108a20 2162 if (par->fragoff != 0)
1d93a9cb 2163 return false;
1da177e4 2164
f7108a20 2165 ic = skb_header_pointer(skb, par->thoff, sizeof(_icmph), &_icmph);
1da177e4
LT
2166 if (ic == NULL) {
2167 /* We've been asked to examine this packet, and we
2168 * can't. Hence, no choice but to drop.
2169 */
2170 duprintf("Dropping evil ICMP tinygram.\n");
f7108a20 2171 *par->hotdrop = true;
1d93a9cb 2172 return false;
1da177e4
LT
2173 }
2174
2175 return icmp_type_code_match(icmpinfo->type,
2176 icmpinfo->code[0],
2177 icmpinfo->code[1],
2178 ic->type, ic->code,
2179 !!(icmpinfo->invflags&IPT_ICMP_INV));
2180}
2181
b0f38452 2182static int icmp_checkentry(const struct xt_mtchk_param *par)
1da177e4 2183{
9b4fce7a 2184 const struct ipt_icmp *icmpinfo = par->matchinfo;
1da177e4 2185
1d5cd909 2186 /* Must specify no unknown invflags */
bd414ee6 2187 return (icmpinfo->invflags & ~IPT_ICMP_INV) ? -EINVAL : 0;
1da177e4
LT
2188}
2189
2190/* The built-in targets: standard (NULL) and error. */
9f15c530 2191static struct xt_target ipt_standard_target __read_mostly = {
1da177e4 2192 .name = IPT_STANDARD_TARGET,
1d5cd909 2193 .targetsize = sizeof(int),
4ba351cf 2194 .family = NFPROTO_IPV4,
2722971c 2195#ifdef CONFIG_COMPAT
9fa492cd
PM
2196 .compatsize = sizeof(compat_int_t),
2197 .compat_from_user = compat_standard_from_user,
2198 .compat_to_user = compat_standard_to_user,
2722971c 2199#endif
1da177e4
LT
2200};
2201
9f15c530 2202static struct xt_target ipt_error_target __read_mostly = {
1da177e4
LT
2203 .name = IPT_ERROR_TARGET,
2204 .target = ipt_error,
1d5cd909 2205 .targetsize = IPT_FUNCTION_MAXNAMELEN,
4ba351cf 2206 .family = NFPROTO_IPV4,
1da177e4
LT
2207};
2208
2209static struct nf_sockopt_ops ipt_sockopts = {
2210 .pf = PF_INET,
2211 .set_optmin = IPT_BASE_CTL,
2212 .set_optmax = IPT_SO_SET_MAX+1,
2213 .set = do_ipt_set_ctl,
2722971c
DM
2214#ifdef CONFIG_COMPAT
2215 .compat_set = compat_do_ipt_set_ctl,
2216#endif
1da177e4
LT
2217 .get_optmin = IPT_BASE_CTL,
2218 .get_optmax = IPT_SO_GET_MAX+1,
2219 .get = do_ipt_get_ctl,
2722971c
DM
2220#ifdef CONFIG_COMPAT
2221 .compat_get = compat_do_ipt_get_ctl,
2222#endif
16fcec35 2223 .owner = THIS_MODULE,
1da177e4
LT
2224};
2225
9f15c530 2226static struct xt_match icmp_matchstruct __read_mostly = {
1da177e4 2227 .name = "icmp",
1d5cd909
PM
2228 .match = icmp_match,
2229 .matchsize = sizeof(struct ipt_icmp),
9c547959 2230 .checkentry = icmp_checkentry,
1d5cd909 2231 .proto = IPPROTO_ICMP,
4ba351cf 2232 .family = NFPROTO_IPV4,
1da177e4
LT
2233};
2234
3cb609d5
AD
2235static int __net_init ip_tables_net_init(struct net *net)
2236{
383ca5b8 2237 return xt_proto_init(net, NFPROTO_IPV4);
3cb609d5
AD
2238}
2239
2240static void __net_exit ip_tables_net_exit(struct net *net)
2241{
383ca5b8 2242 xt_proto_fini(net, NFPROTO_IPV4);
3cb609d5
AD
2243}
2244
2245static struct pernet_operations ip_tables_net_ops = {
2246 .init = ip_tables_net_init,
2247 .exit = ip_tables_net_exit,
2248};
2249
65b4b4e8 2250static int __init ip_tables_init(void)
1da177e4
LT
2251{
2252 int ret;
2253
3cb609d5 2254 ret = register_pernet_subsys(&ip_tables_net_ops);
0eff66e6
PM
2255 if (ret < 0)
2256 goto err1;
2e4e6a17 2257
1da177e4 2258 /* Noone else will be downing sem now, so we won't sleep */
0eff66e6
PM
2259 ret = xt_register_target(&ipt_standard_target);
2260 if (ret < 0)
2261 goto err2;
2262 ret = xt_register_target(&ipt_error_target);
2263 if (ret < 0)
2264 goto err3;
2265 ret = xt_register_match(&icmp_matchstruct);
2266 if (ret < 0)
2267 goto err4;
1da177e4
LT
2268
2269 /* Register setsockopt */
2270 ret = nf_register_sockopt(&ipt_sockopts);
0eff66e6
PM
2271 if (ret < 0)
2272 goto err5;
1da177e4 2273
ff67e4e4 2274 pr_info("(C) 2000-2006 Netfilter Core Team\n");
1da177e4 2275 return 0;
0eff66e6
PM
2276
2277err5:
2278 xt_unregister_match(&icmp_matchstruct);
2279err4:
2280 xt_unregister_target(&ipt_error_target);
2281err3:
2282 xt_unregister_target(&ipt_standard_target);
2283err2:
3cb609d5 2284 unregister_pernet_subsys(&ip_tables_net_ops);
0eff66e6
PM
2285err1:
2286 return ret;
1da177e4
LT
2287}
2288
65b4b4e8 2289static void __exit ip_tables_fini(void)
1da177e4
LT
2290{
2291 nf_unregister_sockopt(&ipt_sockopts);
2e4e6a17 2292
a45049c5
PNA
2293 xt_unregister_match(&icmp_matchstruct);
2294 xt_unregister_target(&ipt_error_target);
2295 xt_unregister_target(&ipt_standard_target);
2e4e6a17 2296
3cb609d5 2297 unregister_pernet_subsys(&ip_tables_net_ops);
1da177e4
LT
2298}
2299
2300EXPORT_SYMBOL(ipt_register_table);
2301EXPORT_SYMBOL(ipt_unregister_table);
1da177e4 2302EXPORT_SYMBOL(ipt_do_table);
65b4b4e8
AM
2303module_init(ip_tables_init);
2304module_exit(ip_tables_fini);