]> bbs.cooldavid.org Git - net-next-2.6.git/blame - net/ipv4/netfilter/ip_tables.c
netfilter: xtables: make use of xt_request_find_target
[net-next-2.6.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 323{
bb70dfa5
JE
324#define tb_comefrom ((struct ipt_entry *)table_base)->comefrom
325
1da177e4 326 static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long))));
5452e425 327 const struct iphdr *ip;
cff533ac 328 bool hotdrop = false;
1da177e4
LT
329 /* Initializing verdict to NF_DROP keeps gcc happy. */
330 unsigned int verdict = NF_DROP;
331 const char *indev, *outdev;
d5d1baa1 332 const void *table_base;
1da177e4 333 struct ipt_entry *e, *back;
d5d1baa1 334 const struct xt_table_info *private;
f7108a20 335 struct xt_match_param mtpar;
7eb35586 336 struct xt_target_param tgpar;
1da177e4
LT
337
338 /* Initialization */
3db05fea 339 ip = ip_hdr(skb);
1da177e4
LT
340 indev = in ? in->name : nulldevname;
341 outdev = out ? out->name : nulldevname;
342 /* We handle fragments by dealing with the first fragment as
343 * if it was a normal packet. All other fragments are treated
344 * normally, except that they will NEVER match rules that ask
345 * things we don't know, ie. tcp syn flag or ports). If the
346 * rule is also a fragment-specific rule, non-fragments won't
347 * match it. */
f7108a20
JE
348 mtpar.fragoff = ntohs(ip->frag_off) & IP_OFFSET;
349 mtpar.thoff = ip_hdrlen(skb);
350 mtpar.hotdrop = &hotdrop;
7eb35586
JE
351 mtpar.in = tgpar.in = in;
352 mtpar.out = tgpar.out = out;
916a917d 353 mtpar.family = tgpar.family = NFPROTO_IPV4;
a5e78820 354 mtpar.hooknum = tgpar.hooknum = hook;
1da177e4 355
1da177e4 356 IP_NF_ASSERT(table->valid_hooks & (1 << hook));
942e4a2b
SH
357 xt_info_rdlock_bh();
358 private = table->private;
359 table_base = private->entries[smp_processor_id()];
78454473 360
2e4e6a17 361 e = get_entry(table_base, private->hook_entry[hook]);
1da177e4
LT
362
363 /* For return from builtin chain */
2e4e6a17 364 back = get_entry(table_base, private->underflow[hook]);
1da177e4
LT
365
366 do {
d5d1baa1 367 const struct ipt_entry_target *t;
dcea992a 368 const struct xt_entry_match *ematch;
a1ff4ac8 369
1da177e4
LT
370 IP_NF_ASSERT(e);
371 IP_NF_ASSERT(back);
a1ff4ac8 372 if (!ip_packet_match(ip, indev, outdev,
dcea992a
JE
373 &e->ip, mtpar.fragoff)) {
374 no_match:
a1ff4ac8
JE
375 e = ipt_next_entry(e);
376 continue;
377 }
1da177e4 378
dcea992a
JE
379 xt_ematch_foreach(ematch, e)
380 if (do_match(ematch, skb, &mtpar) != 0)
381 goto no_match;
382
a1ff4ac8 383 ADD_COUNTER(e->counters, ntohs(ip->tot_len), 1);
1da177e4 384
a1ff4ac8
JE
385 t = ipt_get_target(e);
386 IP_NF_ASSERT(t->u.kernel.target);
ba9dda3a
JK
387
388#if defined(CONFIG_NETFILTER_XT_TARGET_TRACE) || \
389 defined(CONFIG_NETFILTER_XT_TARGET_TRACE_MODULE)
a1ff4ac8
JE
390 /* The packet is traced: log it */
391 if (unlikely(skb->nf_trace))
392 trace_packet(skb, hook, in, out,
393 table->name, private, e);
ba9dda3a 394#endif
a1ff4ac8
JE
395 /* Standard target? */
396 if (!t->u.kernel.target->target) {
397 int v;
398
399 v = ((struct ipt_standard_target *)t)->verdict;
400 if (v < 0) {
401 /* Pop from stack? */
402 if (v != IPT_RETURN) {
403 verdict = (unsigned)(-v) - 1;
404 break;
1da177e4 405 }
a1ff4ac8
JE
406 e = back;
407 back = get_entry(table_base, back->comefrom);
408 continue;
409 }
3666ed1c
JP
410 if (table_base + v != ipt_next_entry(e) &&
411 !(e->ip.flags & IPT_F_GOTO)) {
a1ff4ac8
JE
412 /* Save old back ptr in next entry */
413 struct ipt_entry *next = ipt_next_entry(e);
414 next->comefrom = (void *)back - table_base;
415 /* set back pointer to next entry */
416 back = next;
417 }
1da177e4 418
a1ff4ac8 419 e = get_entry(table_base, v);
7a6b1c46
JE
420 continue;
421 }
422
423 /* Targets which reenter must return
424 abs. verdicts */
425 tgpar.target = t->u.kernel.target;
426 tgpar.targinfo = t->data;
bb70dfa5
JE
427
428
1da177e4 429#ifdef CONFIG_NETFILTER_DEBUG
bb70dfa5 430 tb_comefrom = 0xeeeeeeec;
1da177e4 431#endif
7a6b1c46 432 verdict = t->u.kernel.target->target(skb, &tgpar);
1da177e4 433#ifdef CONFIG_NETFILTER_DEBUG
24992eac 434 if (tb_comefrom != 0xeeeeeeec && verdict == IPT_CONTINUE) {
7a6b1c46
JE
435 printk("Target %s reentered!\n",
436 t->u.kernel.target->name);
437 verdict = NF_DROP;
1da177e4 438 }
bb70dfa5 439 tb_comefrom = 0x57acc001;
7a6b1c46
JE
440#endif
441 /* Target might have changed stuff. */
442 ip = ip_hdr(skb);
7a6b1c46
JE
443 if (verdict == IPT_CONTINUE)
444 e = ipt_next_entry(e);
445 else
446 /* Verdict */
447 break;
1da177e4 448 } while (!hotdrop);
942e4a2b 449 xt_info_rdunlock_bh();
1da177e4
LT
450
451#ifdef DEBUG_ALLOW_ALL
452 return NF_ACCEPT;
453#else
454 if (hotdrop)
455 return NF_DROP;
456 else return verdict;
457#endif
bb70dfa5
JE
458
459#undef tb_comefrom
1da177e4
LT
460}
461
1da177e4
LT
462/* Figures out from what hook each rule can be called: returns 0 if
463 there are loops. Puts hook bitmask in comefrom. */
464static int
d5d1baa1 465mark_source_chains(const struct xt_table_info *newinfo,
31836064 466 unsigned int valid_hooks, void *entry0)
1da177e4
LT
467{
468 unsigned int hook;
469
470 /* No recursion; use packet counter to save back ptrs (reset
471 to 0 as we leave), and comefrom to save source hook bitmask */
6e23ae2a 472 for (hook = 0; hook < NF_INET_NUMHOOKS; hook++) {
1da177e4 473 unsigned int pos = newinfo->hook_entry[hook];
9c547959 474 struct ipt_entry *e = (struct ipt_entry *)(entry0 + pos);
1da177e4
LT
475
476 if (!(valid_hooks & (1 << hook)))
477 continue;
478
479 /* Set initial back pointer. */
480 e->counters.pcnt = pos;
481
482 for (;;) {
d5d1baa1
JE
483 const struct ipt_standard_target *t
484 = (void *)ipt_get_target_c(e);
e1b4b9f3 485 int visited = e->comefrom & (1 << hook);
1da177e4 486
6e23ae2a 487 if (e->comefrom & (1 << NF_INET_NUMHOOKS)) {
1da177e4
LT
488 printk("iptables: loop hook %u pos %u %08X.\n",
489 hook, pos, e->comefrom);
490 return 0;
491 }
9c547959 492 e->comefrom |= ((1 << hook) | (1 << NF_INET_NUMHOOKS));
1da177e4
LT
493
494 /* Unconditional return/END. */
3666ed1c
JP
495 if ((e->target_offset == sizeof(struct ipt_entry) &&
496 (strcmp(t->target.u.user.name,
497 IPT_STANDARD_TARGET) == 0) &&
498 t->verdict < 0 && unconditional(&e->ip)) ||
499 visited) {
1da177e4
LT
500 unsigned int oldpos, size;
501
1f9352ae
PM
502 if ((strcmp(t->target.u.user.name,
503 IPT_STANDARD_TARGET) == 0) &&
504 t->verdict < -NF_MAX_VERDICT - 1) {
74c9c0c1
DM
505 duprintf("mark_source_chains: bad "
506 "negative verdict (%i)\n",
507 t->verdict);
508 return 0;
509 }
510
1da177e4
LT
511 /* Return: backtrack through the last
512 big jump. */
513 do {
6e23ae2a 514 e->comefrom ^= (1<<NF_INET_NUMHOOKS);
1da177e4
LT
515#ifdef DEBUG_IP_FIREWALL_USER
516 if (e->comefrom
6e23ae2a 517 & (1 << NF_INET_NUMHOOKS)) {
1da177e4
LT
518 duprintf("Back unset "
519 "on hook %u "
520 "rule %u\n",
521 hook, pos);
522 }
523#endif
524 oldpos = pos;
525 pos = e->counters.pcnt;
526 e->counters.pcnt = 0;
527
528 /* We're at the start. */
529 if (pos == oldpos)
530 goto next;
531
532 e = (struct ipt_entry *)
31836064 533 (entry0 + pos);
1da177e4
LT
534 } while (oldpos == pos + e->next_offset);
535
536 /* Move along one */
537 size = e->next_offset;
538 e = (struct ipt_entry *)
31836064 539 (entry0 + pos + size);
1da177e4
LT
540 e->counters.pcnt = pos;
541 pos += size;
542 } else {
543 int newpos = t->verdict;
544
545 if (strcmp(t->target.u.user.name,
3666ed1c
JP
546 IPT_STANDARD_TARGET) == 0 &&
547 newpos >= 0) {
74c9c0c1
DM
548 if (newpos > newinfo->size -
549 sizeof(struct ipt_entry)) {
550 duprintf("mark_source_chains: "
551 "bad verdict (%i)\n",
552 newpos);
553 return 0;
554 }
1da177e4
LT
555 /* This a jump; chase it. */
556 duprintf("Jump rule %u -> %u\n",
557 pos, newpos);
558 } else {
559 /* ... this is a fallthru */
560 newpos = pos + e->next_offset;
561 }
562 e = (struct ipt_entry *)
31836064 563 (entry0 + newpos);
1da177e4
LT
564 e->counters.pcnt = pos;
565 pos = newpos;
566 }
567 }
568 next:
569 duprintf("Finished chain %u\n", hook);
570 }
571 return 1;
572}
573
6bdb331b 574static void cleanup_match(struct ipt_entry_match *m, struct net *net)
1da177e4 575{
6be3d859
JE
576 struct xt_mtdtor_param par;
577
f54e9367 578 par.net = net;
6be3d859
JE
579 par.match = m->u.kernel.match;
580 par.matchinfo = m->data;
916a917d 581 par.family = NFPROTO_IPV4;
6be3d859
JE
582 if (par.match->destroy != NULL)
583 par.match->destroy(&par);
584 module_put(par.match->me);
1da177e4
LT
585}
586
022748a9 587static int
d5d1baa1 588check_entry(const struct ipt_entry *e, const char *name)
a96be246 589{
d5d1baa1 590 const struct ipt_entry_target *t;
a96be246
DM
591
592 if (!ip_checkentry(&e->ip)) {
ff67e4e4 593 duprintf("ip check failed %p %s.\n", e, name);
a96be246
DM
594 return -EINVAL;
595 }
596
9c547959
PM
597 if (e->target_offset + sizeof(struct ipt_entry_target) >
598 e->next_offset)
a96be246
DM
599 return -EINVAL;
600
d5d1baa1 601 t = ipt_get_target_c(e);
a96be246
DM
602 if (e->target_offset + t->u.target_size > e->next_offset)
603 return -EINVAL;
604
605 return 0;
606}
607
022748a9 608static int
6bdb331b 609check_match(struct ipt_entry_match *m, struct xt_mtchk_param *par)
a96be246 610{
9b4fce7a 611 const struct ipt_ip *ip = par->entryinfo;
a96be246
DM
612 int ret;
613
9b4fce7a
JE
614 par->match = m->u.kernel.match;
615 par->matchinfo = m->data;
616
916a917d 617 ret = xt_check_match(par, m->u.match_size - sizeof(*m),
9b4fce7a 618 ip->proto, ip->invflags & IPT_INV_PROTO);
367c6790 619 if (ret < 0) {
ff67e4e4 620 duprintf("check failed for `%s'.\n", par.match->name);
367c6790 621 return ret;
a96be246 622 }
367c6790 623 return 0;
a96be246
DM
624}
625
022748a9 626static int
6bdb331b 627find_check_match(struct ipt_entry_match *m, struct xt_mtchk_param *par)
1da177e4 628{
6709dbbb 629 struct xt_match *match;
3cdc7c95 630 int ret;
1da177e4 631
2e4e6a17 632 match = try_then_request_module(xt_find_match(AF_INET, m->u.user.name,
9c547959 633 m->u.user.revision),
1da177e4
LT
634 "ipt_%s", m->u.user.name);
635 if (IS_ERR(match) || !match) {
a96be246 636 duprintf("find_check_match: `%s' not found\n", m->u.user.name);
1da177e4
LT
637 return match ? PTR_ERR(match) : -ENOENT;
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;
72b2b1dd 842 }
1da177e4 843
0f234214 844 if (i != repl->num_entries) {
1da177e4 845 duprintf("translate_table: %u not %u entries\n",
0f234214 846 i, repl->num_entries);
1da177e4
LT
847 return -EINVAL;
848 }
849
850 /* Check hooks all assigned */
6e23ae2a 851 for (i = 0; i < NF_INET_NUMHOOKS; i++) {
1da177e4 852 /* Only hooks which are valid */
0f234214 853 if (!(repl->valid_hooks & (1 << i)))
1da177e4
LT
854 continue;
855 if (newinfo->hook_entry[i] == 0xFFFFFFFF) {
856 duprintf("Invalid hook entry %u %u\n",
0f234214 857 i, repl->hook_entry[i]);
1da177e4
LT
858 return -EINVAL;
859 }
860 if (newinfo->underflow[i] == 0xFFFFFFFF) {
861 duprintf("Invalid underflow %u %u\n",
0f234214 862 i, repl->underflow[i]);
1da177e4
LT
863 return -EINVAL;
864 }
865 }
866
0f234214 867 if (!mark_source_chains(newinfo, repl->valid_hooks, entry0))
74c9c0c1
DM
868 return -ELOOP;
869
1da177e4
LT
870 /* Finally, each sanity check must pass */
871 i = 0;
72b2b1dd 872 xt_entry_foreach(iter, entry0, newinfo->size) {
0f234214 873 ret = find_check_entry(iter, net, repl->name, repl->size);
72b2b1dd
JE
874 if (ret != 0)
875 break;
0559518b 876 ++i;
72b2b1dd 877 }
1da177e4 878
74c9c0c1 879 if (ret != 0) {
0559518b
JE
880 xt_entry_foreach(iter, entry0, newinfo->size) {
881 if (i-- == 0)
72b2b1dd 882 break;
0559518b
JE
883 cleanup_entry(iter, net);
884 }
74c9c0c1
DM
885 return ret;
886 }
1da177e4
LT
887
888 /* And one copy for every other CPU */
6f912042 889 for_each_possible_cpu(i) {
31836064
ED
890 if (newinfo->entries[i] && newinfo->entries[i] != entry0)
891 memcpy(newinfo->entries[i], entry0, newinfo->size);
1da177e4
LT
892 }
893
894 return ret;
895}
896
1da177e4 897static void
2e4e6a17
HW
898get_counters(const struct xt_table_info *t,
899 struct xt_counters counters[])
1da177e4 900{
72b2b1dd 901 struct ipt_entry *iter;
1da177e4
LT
902 unsigned int cpu;
903 unsigned int i;
31836064
ED
904 unsigned int curcpu;
905
906 /* Instead of clearing (by a previous call to memset())
907 * the counters and using adds, we set the counters
942e4a2b
SH
908 * with data used by 'current' CPU.
909 *
910 * Bottom half has to be disabled to prevent deadlock
911 * if new softirq were to run and call ipt_do_table
31836064 912 */
942e4a2b
SH
913 local_bh_disable();
914 curcpu = smp_processor_id();
31836064
ED
915
916 i = 0;
0559518b
JE
917 xt_entry_foreach(iter, t->entries[curcpu], t->size) {
918 SET_COUNTER(counters[i], iter->counters.bcnt,
6b4ff2d7 919 iter->counters.pcnt);
0559518b
JE
920 ++i;
921 }
1da177e4 922
6f912042 923 for_each_possible_cpu(cpu) {
31836064
ED
924 if (cpu == curcpu)
925 continue;
1da177e4 926 i = 0;
942e4a2b 927 xt_info_wrlock(cpu);
0559518b
JE
928 xt_entry_foreach(iter, t->entries[cpu], t->size) {
929 ADD_COUNTER(counters[i], iter->counters.bcnt,
6b4ff2d7 930 iter->counters.pcnt);
0559518b
JE
931 ++i; /* macro does multi eval of i */
932 }
942e4a2b 933 xt_info_wrunlock(cpu);
1da177e4 934 }
78454473
SH
935 local_bh_enable();
936}
937
d5d1baa1 938static struct xt_counters *alloc_counters(const struct xt_table *table)
1da177e4 939{
2722971c 940 unsigned int countersize;
2e4e6a17 941 struct xt_counters *counters;
d5d1baa1 942 const struct xt_table_info *private = table->private;
1da177e4
LT
943
944 /* We need atomic snapshot of counters: rest doesn't change
945 (other than comefrom, which userspace doesn't care
946 about). */
2e4e6a17 947 countersize = sizeof(struct xt_counters) * private->number;
31836064 948 counters = vmalloc_node(countersize, numa_node_id());
1da177e4
LT
949
950 if (counters == NULL)
942e4a2b 951 return ERR_PTR(-ENOMEM);
78454473 952
942e4a2b 953 get_counters(private, counters);
1da177e4 954
2722971c
DM
955 return counters;
956}
957
958static int
959copy_entries_to_user(unsigned int total_size,
d5d1baa1 960 const struct xt_table *table,
2722971c
DM
961 void __user *userptr)
962{
963 unsigned int off, num;
d5d1baa1 964 const struct ipt_entry *e;
2722971c 965 struct xt_counters *counters;
5452e425 966 const struct xt_table_info *private = table->private;
2722971c 967 int ret = 0;
5452e425 968 const void *loc_cpu_entry;
2722971c
DM
969
970 counters = alloc_counters(table);
971 if (IS_ERR(counters))
972 return PTR_ERR(counters);
973
31836064
ED
974 /* choose the copy that is on our node/cpu, ...
975 * This choice is lazy (because current thread is
976 * allowed to migrate to another cpu)
977 */
2e4e6a17 978 loc_cpu_entry = private->entries[raw_smp_processor_id()];
31836064 979 if (copy_to_user(userptr, loc_cpu_entry, total_size) != 0) {
1da177e4
LT
980 ret = -EFAULT;
981 goto free_counters;
982 }
983
984 /* FIXME: use iterator macros --RR */
985 /* ... then go back and fix counters and names */
986 for (off = 0, num = 0; off < total_size; off += e->next_offset, num++){
987 unsigned int i;
5452e425
JE
988 const struct ipt_entry_match *m;
989 const struct ipt_entry_target *t;
1da177e4 990
31836064 991 e = (struct ipt_entry *)(loc_cpu_entry + off);
1da177e4
LT
992 if (copy_to_user(userptr + off
993 + offsetof(struct ipt_entry, counters),
994 &counters[num],
995 sizeof(counters[num])) != 0) {
996 ret = -EFAULT;
997 goto free_counters;
998 }
999
1000 for (i = sizeof(struct ipt_entry);
1001 i < e->target_offset;
1002 i += m->u.match_size) {
1003 m = (void *)e + i;
1004
1005 if (copy_to_user(userptr + off + i
1006 + offsetof(struct ipt_entry_match,
1007 u.user.name),
1008 m->u.kernel.match->name,
1009 strlen(m->u.kernel.match->name)+1)
1010 != 0) {
1011 ret = -EFAULT;
1012 goto free_counters;
1013 }
1014 }
1015
d5d1baa1 1016 t = ipt_get_target_c(e);
1da177e4
LT
1017 if (copy_to_user(userptr + off + e->target_offset
1018 + offsetof(struct ipt_entry_target,
1019 u.user.name),
1020 t->u.kernel.target->name,
1021 strlen(t->u.kernel.target->name)+1) != 0) {
1022 ret = -EFAULT;
1023 goto free_counters;
1024 }
1025 }
1026
1027 free_counters:
1028 vfree(counters);
1029 return ret;
1030}
1031
2722971c 1032#ifdef CONFIG_COMPAT
739674fb 1033static void compat_standard_from_user(void *dst, const void *src)
2722971c 1034{
9fa492cd 1035 int v = *(compat_int_t *)src;
2722971c 1036
9fa492cd 1037 if (v > 0)
b386d9f5 1038 v += xt_compat_calc_jump(AF_INET, v);
9fa492cd
PM
1039 memcpy(dst, &v, sizeof(v));
1040}
46c5ea3c 1041
739674fb 1042static int compat_standard_to_user(void __user *dst, const void *src)
2722971c 1043{
9fa492cd 1044 compat_int_t cv = *(int *)src;
2722971c 1045
9fa492cd 1046 if (cv > 0)
b386d9f5 1047 cv -= xt_compat_calc_jump(AF_INET, cv);
9fa492cd 1048 return copy_to_user(dst, &cv, sizeof(cv)) ? -EFAULT : 0;
2722971c
DM
1049}
1050
d5d1baa1 1051static int compat_calc_entry(const struct ipt_entry *e,
4b478248 1052 const struct xt_table_info *info,
d5d1baa1 1053 const void *base, struct xt_table_info *newinfo)
2722971c 1054{
dcea992a 1055 const struct xt_entry_match *ematch;
d5d1baa1 1056 const struct ipt_entry_target *t;
e5b5ef7d 1057 unsigned int entry_offset;
2722971c
DM
1058 int off, i, ret;
1059
30c08c41 1060 off = sizeof(struct ipt_entry) - sizeof(struct compat_ipt_entry);
2722971c 1061 entry_offset = (void *)e - base;
dcea992a 1062 xt_ematch_foreach(ematch, e)
6bdb331b 1063 off += xt_compat_match_offset(ematch->u.kernel.match);
d5d1baa1 1064 t = ipt_get_target_c(e);
9fa492cd 1065 off += xt_compat_target_offset(t->u.kernel.target);
2722971c 1066 newinfo->size -= off;
b386d9f5 1067 ret = xt_compat_add_offset(AF_INET, entry_offset, off);
2722971c
DM
1068 if (ret)
1069 return ret;
1070
6e23ae2a 1071 for (i = 0; i < NF_INET_NUMHOOKS; i++) {
4b478248
PM
1072 if (info->hook_entry[i] &&
1073 (e < (struct ipt_entry *)(base + info->hook_entry[i])))
2722971c 1074 newinfo->hook_entry[i] -= off;
4b478248
PM
1075 if (info->underflow[i] &&
1076 (e < (struct ipt_entry *)(base + info->underflow[i])))
2722971c
DM
1077 newinfo->underflow[i] -= off;
1078 }
1079 return 0;
1080}
1081
259d4e41 1082static int compat_table_info(const struct xt_table_info *info,
4b478248 1083 struct xt_table_info *newinfo)
2722971c 1084{
72b2b1dd 1085 struct ipt_entry *iter;
2722971c 1086 void *loc_cpu_entry;
0559518b 1087 int ret;
2722971c
DM
1088
1089 if (!newinfo || !info)
1090 return -EINVAL;
1091
259d4e41
ED
1092 /* we dont care about newinfo->entries[] */
1093 memcpy(newinfo, info, offsetof(struct xt_table_info, entries));
1094 newinfo->initial_entries = 0;
2722971c 1095 loc_cpu_entry = info->entries[raw_smp_processor_id()];
72b2b1dd
JE
1096 xt_entry_foreach(iter, loc_cpu_entry, info->size) {
1097 ret = compat_calc_entry(iter, info, loc_cpu_entry, newinfo);
1098 if (ret != 0)
0559518b 1099 return ret;
72b2b1dd 1100 }
0559518b 1101 return 0;
2722971c
DM
1102}
1103#endif
1104
d5d1baa1
JE
1105static int get_info(struct net *net, void __user *user,
1106 const int *len, int compat)
2722971c
DM
1107{
1108 char name[IPT_TABLE_MAXNAMELEN];
e60a13e0 1109 struct xt_table *t;
2722971c
DM
1110 int ret;
1111
1112 if (*len != sizeof(struct ipt_getinfo)) {
c9d8fe13
PM
1113 duprintf("length %u != %zu\n", *len,
1114 sizeof(struct ipt_getinfo));
2722971c
DM
1115 return -EINVAL;
1116 }
1117
1118 if (copy_from_user(name, user, sizeof(name)) != 0)
1119 return -EFAULT;
1120
1121 name[IPT_TABLE_MAXNAMELEN-1] = '\0';
1122#ifdef CONFIG_COMPAT
1123 if (compat)
1124 xt_compat_lock(AF_INET);
1125#endif
34bd137b 1126 t = try_then_request_module(xt_find_table_lock(net, AF_INET, name),
4b478248 1127 "iptable_%s", name);
2722971c
DM
1128 if (t && !IS_ERR(t)) {
1129 struct ipt_getinfo info;
5452e425 1130 const struct xt_table_info *private = t->private;
2722971c 1131#ifdef CONFIG_COMPAT
14c7dbe0
AD
1132 struct xt_table_info tmp;
1133
2722971c 1134 if (compat) {
2722971c 1135 ret = compat_table_info(private, &tmp);
b386d9f5 1136 xt_compat_flush_offsets(AF_INET);
4b478248 1137 private = &tmp;
2722971c
DM
1138 }
1139#endif
1140 info.valid_hooks = t->valid_hooks;
1141 memcpy(info.hook_entry, private->hook_entry,
4b478248 1142 sizeof(info.hook_entry));
2722971c 1143 memcpy(info.underflow, private->underflow,
4b478248 1144 sizeof(info.underflow));
2722971c
DM
1145 info.num_entries = private->number;
1146 info.size = private->size;
1147 strcpy(info.name, name);
1148
1149 if (copy_to_user(user, &info, *len) != 0)
1150 ret = -EFAULT;
1151 else
1152 ret = 0;
1153
1154 xt_table_unlock(t);
1155 module_put(t->me);
1156 } else
1157 ret = t ? PTR_ERR(t) : -ENOENT;
1158#ifdef CONFIG_COMPAT
1159 if (compat)
1160 xt_compat_unlock(AF_INET);
1161#endif
1162 return ret;
1163}
1164
1165static int
d5d1baa1
JE
1166get_entries(struct net *net, struct ipt_get_entries __user *uptr,
1167 const int *len)
2722971c
DM
1168{
1169 int ret;
1170 struct ipt_get_entries get;
e60a13e0 1171 struct xt_table *t;
2722971c
DM
1172
1173 if (*len < sizeof(get)) {
c9d8fe13 1174 duprintf("get_entries: %u < %zu\n", *len, sizeof(get));
2722971c
DM
1175 return -EINVAL;
1176 }
1177 if (copy_from_user(&get, uptr, sizeof(get)) != 0)
1178 return -EFAULT;
1179 if (*len != sizeof(struct ipt_get_entries) + get.size) {
c9d8fe13
PM
1180 duprintf("get_entries: %u != %zu\n",
1181 *len, sizeof(get) + get.size);
2722971c
DM
1182 return -EINVAL;
1183 }
1184
34bd137b 1185 t = xt_find_table_lock(net, AF_INET, get.name);
2722971c 1186 if (t && !IS_ERR(t)) {
5452e425 1187 const struct xt_table_info *private = t->private;
9c547959 1188 duprintf("t->private->number = %u\n", private->number);
2722971c
DM
1189 if (get.size == private->size)
1190 ret = copy_entries_to_user(private->size,
1191 t, uptr->entrytable);
1192 else {
1193 duprintf("get_entries: I've got %u not %u!\n",
9c547959 1194 private->size, get.size);
544473c1 1195 ret = -EAGAIN;
2722971c
DM
1196 }
1197 module_put(t->me);
1198 xt_table_unlock(t);
1199 } else
1200 ret = t ? PTR_ERR(t) : -ENOENT;
1201
1202 return ret;
1203}
1204
1205static int
34bd137b 1206__do_replace(struct net *net, const char *name, unsigned int valid_hooks,
4b478248
PM
1207 struct xt_table_info *newinfo, unsigned int num_counters,
1208 void __user *counters_ptr)
2722971c
DM
1209{
1210 int ret;
e60a13e0 1211 struct xt_table *t;
2722971c
DM
1212 struct xt_table_info *oldinfo;
1213 struct xt_counters *counters;
1214 void *loc_cpu_old_entry;
72b2b1dd 1215 struct ipt_entry *iter;
2722971c
DM
1216
1217 ret = 0;
1218 counters = vmalloc(num_counters * sizeof(struct xt_counters));
1219 if (!counters) {
1220 ret = -ENOMEM;
1221 goto out;
1222 }
1223
34bd137b 1224 t = try_then_request_module(xt_find_table_lock(net, AF_INET, name),
2722971c
DM
1225 "iptable_%s", name);
1226 if (!t || IS_ERR(t)) {
1227 ret = t ? PTR_ERR(t) : -ENOENT;
1228 goto free_newinfo_counters_untrans;
1229 }
1230
1231 /* You lied! */
1232 if (valid_hooks != t->valid_hooks) {
1233 duprintf("Valid hook crap: %08X vs %08X\n",
1234 valid_hooks, t->valid_hooks);
1235 ret = -EINVAL;
1236 goto put_module;
1237 }
1238
1239 oldinfo = xt_replace_table(t, num_counters, newinfo, &ret);
1240 if (!oldinfo)
1241 goto put_module;
1242
1243 /* Update module usage count based on number of rules */
1244 duprintf("do_replace: oldnum=%u, initnum=%u, newnum=%u\n",
1245 oldinfo->number, oldinfo->initial_entries, newinfo->number);
1246 if ((oldinfo->number > oldinfo->initial_entries) ||
1247 (newinfo->number <= oldinfo->initial_entries))
1248 module_put(t->me);
1249 if ((oldinfo->number > oldinfo->initial_entries) &&
1250 (newinfo->number <= oldinfo->initial_entries))
1251 module_put(t->me);
1252
942e4a2b 1253 /* Get the old counters, and synchronize with replace */
2722971c 1254 get_counters(oldinfo, counters);
942e4a2b 1255
2722971c
DM
1256 /* Decrease module usage counts and free resource */
1257 loc_cpu_old_entry = oldinfo->entries[raw_smp_processor_id()];
72b2b1dd 1258 xt_entry_foreach(iter, loc_cpu_old_entry, oldinfo->size)
0559518b 1259 cleanup_entry(iter, net);
72b2b1dd 1260
2722971c
DM
1261 xt_free_table_info(oldinfo);
1262 if (copy_to_user(counters_ptr, counters,
1263 sizeof(struct xt_counters) * num_counters) != 0)
1264 ret = -EFAULT;
1265 vfree(counters);
1266 xt_table_unlock(t);
1267 return ret;
1268
1269 put_module:
1270 module_put(t->me);
1271 xt_table_unlock(t);
1272 free_newinfo_counters_untrans:
1273 vfree(counters);
1274 out:
1275 return ret;
1276}
1277
1278static int
d5d1baa1 1279do_replace(struct net *net, const void __user *user, unsigned int len)
2722971c
DM
1280{
1281 int ret;
1282 struct ipt_replace tmp;
1283 struct xt_table_info *newinfo;
1284 void *loc_cpu_entry;
72b2b1dd 1285 struct ipt_entry *iter;
2722971c
DM
1286
1287 if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1288 return -EFAULT;
1289
2722971c 1290 /* overflow check */
2722971c
DM
1291 if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters))
1292 return -ENOMEM;
1293
1294 newinfo = xt_alloc_table_info(tmp.size);
1295 if (!newinfo)
1296 return -ENOMEM;
1297
9c547959 1298 /* choose the copy that is on our node/cpu */
2722971c
DM
1299 loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
1300 if (copy_from_user(loc_cpu_entry, user + sizeof(tmp),
1301 tmp.size) != 0) {
1302 ret = -EFAULT;
1303 goto free_newinfo;
1304 }
1305
0f234214 1306 ret = translate_table(net, newinfo, loc_cpu_entry, &tmp);
2722971c
DM
1307 if (ret != 0)
1308 goto free_newinfo;
1309
ff67e4e4 1310 duprintf("Translated table\n");
2722971c 1311
34bd137b 1312 ret = __do_replace(net, tmp.name, tmp.valid_hooks, newinfo,
4b478248 1313 tmp.num_counters, tmp.counters);
2722971c
DM
1314 if (ret)
1315 goto free_newinfo_untrans;
1316 return 0;
1317
1318 free_newinfo_untrans:
72b2b1dd 1319 xt_entry_foreach(iter, loc_cpu_entry, newinfo->size)
0559518b 1320 cleanup_entry(iter, net);
2722971c
DM
1321 free_newinfo:
1322 xt_free_table_info(newinfo);
1323 return ret;
1324}
1325
2722971c 1326static int
d5d1baa1
JE
1327do_add_counters(struct net *net, const void __user *user,
1328 unsigned int len, int compat)
2722971c 1329{
942e4a2b 1330 unsigned int i, curcpu;
2722971c
DM
1331 struct xt_counters_info tmp;
1332 struct xt_counters *paddc;
1333 unsigned int num_counters;
5452e425 1334 const char *name;
2722971c
DM
1335 int size;
1336 void *ptmp;
e60a13e0 1337 struct xt_table *t;
5452e425 1338 const struct xt_table_info *private;
2722971c
DM
1339 int ret = 0;
1340 void *loc_cpu_entry;
72b2b1dd 1341 struct ipt_entry *iter;
2722971c
DM
1342#ifdef CONFIG_COMPAT
1343 struct compat_xt_counters_info compat_tmp;
1344
1345 if (compat) {
1346 ptmp = &compat_tmp;
1347 size = sizeof(struct compat_xt_counters_info);
1348 } else
1349#endif
1350 {
1351 ptmp = &tmp;
1352 size = sizeof(struct xt_counters_info);
1353 }
1354
1355 if (copy_from_user(ptmp, user, size) != 0)
1356 return -EFAULT;
1357
1358#ifdef CONFIG_COMPAT
1359 if (compat) {
1360 num_counters = compat_tmp.num_counters;
1361 name = compat_tmp.name;
1362 } else
1363#endif
1364 {
1365 num_counters = tmp.num_counters;
1366 name = tmp.name;
1367 }
1368
1369 if (len != size + num_counters * sizeof(struct xt_counters))
1370 return -EINVAL;
1371
1372 paddc = vmalloc_node(len - size, numa_node_id());
1373 if (!paddc)
1374 return -ENOMEM;
1375
1376 if (copy_from_user(paddc, user + size, len - size) != 0) {
1377 ret = -EFAULT;
1378 goto free;
1379 }
1380
34bd137b 1381 t = xt_find_table_lock(net, AF_INET, name);
2722971c
DM
1382 if (!t || IS_ERR(t)) {
1383 ret = t ? PTR_ERR(t) : -ENOENT;
1384 goto free;
1385 }
1386
942e4a2b 1387 local_bh_disable();
2722971c
DM
1388 private = t->private;
1389 if (private->number != num_counters) {
1390 ret = -EINVAL;
1391 goto unlock_up_free;
1392 }
1393
1394 i = 0;
1395 /* Choose the copy that is on our node */
942e4a2b
SH
1396 curcpu = smp_processor_id();
1397 loc_cpu_entry = private->entries[curcpu];
1398 xt_info_wrlock(curcpu);
0559518b
JE
1399 xt_entry_foreach(iter, loc_cpu_entry, private->size) {
1400 ADD_COUNTER(iter->counters, paddc[i].bcnt, paddc[i].pcnt);
1401 ++i;
1402 }
942e4a2b 1403 xt_info_wrunlock(curcpu);
2722971c 1404 unlock_up_free:
942e4a2b 1405 local_bh_enable();
2722971c
DM
1406 xt_table_unlock(t);
1407 module_put(t->me);
1408 free:
1409 vfree(paddc);
1410
1411 return ret;
1412}
1413
1414#ifdef CONFIG_COMPAT
1415struct compat_ipt_replace {
1416 char name[IPT_TABLE_MAXNAMELEN];
1417 u32 valid_hooks;
1418 u32 num_entries;
1419 u32 size;
6e23ae2a
PM
1420 u32 hook_entry[NF_INET_NUMHOOKS];
1421 u32 underflow[NF_INET_NUMHOOKS];
2722971c
DM
1422 u32 num_counters;
1423 compat_uptr_t counters; /* struct ipt_counters * */
1424 struct compat_ipt_entry entries[0];
1425};
1426
a18aa31b
PM
1427static int
1428compat_copy_entry_to_user(struct ipt_entry *e, void __user **dstptr,
b0a6363c 1429 unsigned int *size, struct xt_counters *counters,
0559518b 1430 unsigned int i)
2722971c 1431{
3e597c60 1432 struct ipt_entry_target *t;
2722971c
DM
1433 struct compat_ipt_entry __user *ce;
1434 u_int16_t target_offset, next_offset;
1435 compat_uint_t origsize;
dcea992a
JE
1436 const struct xt_entry_match *ematch;
1437 int ret = 0;
2722971c 1438
2722971c
DM
1439 origsize = *size;
1440 ce = (struct compat_ipt_entry __user *)*dstptr;
0559518b
JE
1441 if (copy_to_user(ce, e, sizeof(struct ipt_entry)) != 0 ||
1442 copy_to_user(&ce->counters, &counters[i],
1443 sizeof(counters[i])) != 0)
1444 return -EFAULT;
a18aa31b 1445
2722971c 1446 *dstptr += sizeof(struct compat_ipt_entry);
30c08c41
PM
1447 *size -= sizeof(struct ipt_entry) - sizeof(struct compat_ipt_entry);
1448
dcea992a
JE
1449 xt_ematch_foreach(ematch, e) {
1450 ret = xt_compat_match_to_user(ematch, dstptr, size);
1451 if (ret != 0)
6bdb331b 1452 return ret;
dcea992a 1453 }
2722971c 1454 target_offset = e->target_offset - (origsize - *size);
2722971c 1455 t = ipt_get_target(e);
9fa492cd 1456 ret = xt_compat_target_to_user(t, dstptr, size);
2722971c 1457 if (ret)
0559518b 1458 return ret;
2722971c 1459 next_offset = e->next_offset - (origsize - *size);
0559518b
JE
1460 if (put_user(target_offset, &ce->target_offset) != 0 ||
1461 put_user(next_offset, &ce->next_offset) != 0)
1462 return -EFAULT;
2722971c 1463 return 0;
2722971c
DM
1464}
1465
022748a9 1466static int
4c1b52bc 1467compat_find_calc_match(struct ipt_entry_match *m,
4b478248
PM
1468 const char *name,
1469 const struct ipt_ip *ip,
1470 unsigned int hookmask,
6bdb331b 1471 int *size)
2722971c 1472{
6709dbbb 1473 struct xt_match *match;
2722971c
DM
1474
1475 match = try_then_request_module(xt_find_match(AF_INET, m->u.user.name,
4b478248 1476 m->u.user.revision),
2722971c
DM
1477 "ipt_%s", m->u.user.name);
1478 if (IS_ERR(match) || !match) {
1479 duprintf("compat_check_calc_match: `%s' not found\n",
4b478248 1480 m->u.user.name);
2722971c
DM
1481 return match ? PTR_ERR(match) : -ENOENT;
1482 }
1483 m->u.kernel.match = match;
9fa492cd 1484 *size += xt_compat_match_offset(match);
4c1b52bc
DM
1485 return 0;
1486}
1487
0559518b 1488static void compat_release_entry(struct compat_ipt_entry *e)
4c1b52bc
DM
1489{
1490 struct ipt_entry_target *t;
dcea992a 1491 struct xt_entry_match *ematch;
4c1b52bc 1492
4c1b52bc 1493 /* Cleanup all matches */
dcea992a 1494 xt_ematch_foreach(ematch, e)
6bdb331b 1495 module_put(ematch->u.kernel.match->me);
73cd598d 1496 t = compat_ipt_get_target(e);
4c1b52bc 1497 module_put(t->u.kernel.target->me);
4c1b52bc
DM
1498}
1499
022748a9 1500static int
73cd598d 1501check_compat_entry_size_and_hooks(struct compat_ipt_entry *e,
4b478248
PM
1502 struct xt_table_info *newinfo,
1503 unsigned int *size,
d5d1baa1
JE
1504 const unsigned char *base,
1505 const unsigned char *limit,
1506 const unsigned int *hook_entries,
1507 const unsigned int *underflows,
4b478248 1508 const char *name)
2722971c 1509{
dcea992a 1510 struct xt_entry_match *ematch;
2722971c 1511 struct ipt_entry_target *t;
6709dbbb 1512 struct xt_target *target;
e5b5ef7d 1513 unsigned int entry_offset;
b0a6363c
PM
1514 unsigned int j;
1515 int ret, off, h;
2722971c
DM
1516
1517 duprintf("check_compat_entry_size_and_hooks %p\n", e);
3666ed1c
JP
1518 if ((unsigned long)e % __alignof__(struct compat_ipt_entry) != 0 ||
1519 (unsigned char *)e + sizeof(struct compat_ipt_entry) >= limit) {
2722971c
DM
1520 duprintf("Bad offset %p, limit = %p\n", e, limit);
1521 return -EINVAL;
1522 }
1523
1524 if (e->next_offset < sizeof(struct compat_ipt_entry) +
4b478248 1525 sizeof(struct compat_xt_entry_target)) {
2722971c
DM
1526 duprintf("checking: element %p size %u\n",
1527 e, e->next_offset);
1528 return -EINVAL;
1529 }
1530
73cd598d
PM
1531 /* For purposes of check_entry casting the compat entry is fine */
1532 ret = check_entry((struct ipt_entry *)e, name);
a96be246
DM
1533 if (ret)
1534 return ret;
590bdf7f 1535
30c08c41 1536 off = sizeof(struct ipt_entry) - sizeof(struct compat_ipt_entry);
2722971c
DM
1537 entry_offset = (void *)e - (void *)base;
1538 j = 0;
dcea992a
JE
1539 xt_ematch_foreach(ematch, e) {
1540 ret = compat_find_calc_match(ematch, name,
6b4ff2d7 1541 &e->ip, e->comefrom, &off);
dcea992a 1542 if (ret != 0)
6bdb331b
JE
1543 goto release_matches;
1544 ++j;
dcea992a 1545 }
2722971c 1546
73cd598d 1547 t = compat_ipt_get_target(e);
d2a7b6ba
JE
1548 target = xt_request_find_target(NFPROTO_IPV4, t->u.user.name,
1549 t->u.user.revision);
1550 if (IS_ERR(target)) {
a96be246 1551 duprintf("check_compat_entry_size_and_hooks: `%s' not found\n",
4b478248 1552 t->u.user.name);
d2a7b6ba 1553 ret = PTR_ERR(target);
4c1b52bc 1554 goto release_matches;
2722971c
DM
1555 }
1556 t->u.kernel.target = target;
1557
9fa492cd 1558 off += xt_compat_target_offset(target);
2722971c 1559 *size += off;
b386d9f5 1560 ret = xt_compat_add_offset(AF_INET, entry_offset, off);
2722971c
DM
1561 if (ret)
1562 goto out;
1563
1564 /* Check hooks & underflows */
6e23ae2a 1565 for (h = 0; h < NF_INET_NUMHOOKS; h++) {
2722971c
DM
1566 if ((unsigned char *)e - base == hook_entries[h])
1567 newinfo->hook_entry[h] = hook_entries[h];
1568 if ((unsigned char *)e - base == underflows[h])
1569 newinfo->underflow[h] = underflows[h];
1570 }
1571
1572 /* Clear counters and comefrom */
73cd598d 1573 memset(&e->counters, 0, sizeof(e->counters));
2722971c 1574 e->comefrom = 0;
2722971c 1575 return 0;
bec71b16 1576
2722971c 1577out:
bec71b16 1578 module_put(t->u.kernel.target->me);
4c1b52bc 1579release_matches:
6bdb331b
JE
1580 xt_ematch_foreach(ematch, e) {
1581 if (j-- == 0)
dcea992a 1582 break;
6bdb331b
JE
1583 module_put(ematch->u.kernel.match->me);
1584 }
2722971c
DM
1585 return ret;
1586}
1587
4b478248 1588static int
73cd598d 1589compat_copy_entry_from_user(struct compat_ipt_entry *e, void **dstptr,
4b478248
PM
1590 unsigned int *size, const char *name,
1591 struct xt_table_info *newinfo, unsigned char *base)
2722971c
DM
1592{
1593 struct ipt_entry_target *t;
6709dbbb 1594 struct xt_target *target;
2722971c
DM
1595 struct ipt_entry *de;
1596 unsigned int origsize;
920b868a 1597 int ret, h;
dcea992a 1598 struct xt_entry_match *ematch;
2722971c
DM
1599
1600 ret = 0;
1601 origsize = *size;
1602 de = (struct ipt_entry *)*dstptr;
1603 memcpy(de, e, sizeof(struct ipt_entry));
73cd598d 1604 memcpy(&de->counters, &e->counters, sizeof(e->counters));
2722971c 1605
73cd598d 1606 *dstptr += sizeof(struct ipt_entry);
30c08c41
PM
1607 *size += sizeof(struct ipt_entry) - sizeof(struct compat_ipt_entry);
1608
dcea992a
JE
1609 xt_ematch_foreach(ematch, e) {
1610 ret = xt_compat_match_from_user(ematch, dstptr, size);
1611 if (ret != 0)
6bdb331b 1612 return ret;
dcea992a 1613 }
2722971c 1614 de->target_offset = e->target_offset - (origsize - *size);
73cd598d 1615 t = compat_ipt_get_target(e);
2722971c 1616 target = t->u.kernel.target;
9fa492cd 1617 xt_compat_target_from_user(t, dstptr, size);
2722971c
DM
1618
1619 de->next_offset = e->next_offset - (origsize - *size);
6e23ae2a 1620 for (h = 0; h < NF_INET_NUMHOOKS; h++) {
2722971c
DM
1621 if ((unsigned char *)de - base < newinfo->hook_entry[h])
1622 newinfo->hook_entry[h] -= origsize - *size;
1623 if ((unsigned char *)de - base < newinfo->underflow[h])
1624 newinfo->underflow[h] -= origsize - *size;
1625 }
f6677f43
DM
1626 return ret;
1627}
1628
022748a9 1629static int
0559518b 1630compat_check_entry(struct ipt_entry *e, struct net *net, const char *name)
f6677f43 1631{
dcea992a 1632 struct xt_entry_match *ematch;
9b4fce7a 1633 struct xt_mtchk_param mtpar;
b0a6363c 1634 unsigned int j;
dcea992a 1635 int ret = 0;
f6677f43 1636
4c1b52bc 1637 j = 0;
a83d8e8d 1638 mtpar.net = net;
9b4fce7a
JE
1639 mtpar.table = name;
1640 mtpar.entryinfo = &e->ip;
1641 mtpar.hook_mask = e->comefrom;
916a917d 1642 mtpar.family = NFPROTO_IPV4;
dcea992a 1643 xt_ematch_foreach(ematch, e) {
6bdb331b 1644 ret = check_match(ematch, &mtpar);
dcea992a 1645 if (ret != 0)
6bdb331b
JE
1646 goto cleanup_matches;
1647 ++j;
dcea992a 1648 }
4c1b52bc 1649
add67461 1650 ret = check_target(e, net, name);
4c1b52bc
DM
1651 if (ret)
1652 goto cleanup_matches;
4c1b52bc
DM
1653 return 0;
1654
1655 cleanup_matches:
6bdb331b
JE
1656 xt_ematch_foreach(ematch, e) {
1657 if (j-- == 0)
dcea992a 1658 break;
6bdb331b
JE
1659 cleanup_match(ematch, net);
1660 }
4c1b52bc 1661 return ret;
f6677f43
DM
1662}
1663
1da177e4 1664static int
a83d8e8d
AD
1665translate_compat_table(struct net *net,
1666 const char *name,
4b478248
PM
1667 unsigned int valid_hooks,
1668 struct xt_table_info **pinfo,
1669 void **pentry0,
1670 unsigned int total_size,
1671 unsigned int number,
1672 unsigned int *hook_entries,
1673 unsigned int *underflows)
1da177e4 1674{
920b868a 1675 unsigned int i, j;
2722971c
DM
1676 struct xt_table_info *newinfo, *info;
1677 void *pos, *entry0, *entry1;
72b2b1dd
JE
1678 struct compat_ipt_entry *iter0;
1679 struct ipt_entry *iter1;
2722971c 1680 unsigned int size;
0559518b 1681 int ret;
1da177e4 1682
2722971c
DM
1683 info = *pinfo;
1684 entry0 = *pentry0;
1685 size = total_size;
1686 info->number = number;
1687
1688 /* Init all hooks to impossible value. */
6e23ae2a 1689 for (i = 0; i < NF_INET_NUMHOOKS; i++) {
2722971c
DM
1690 info->hook_entry[i] = 0xFFFFFFFF;
1691 info->underflow[i] = 0xFFFFFFFF;
1692 }
1693
1694 duprintf("translate_compat_table: size %u\n", info->size);
920b868a 1695 j = 0;
2722971c
DM
1696 xt_compat_lock(AF_INET);
1697 /* Walk through entries, checking offsets. */
72b2b1dd
JE
1698 xt_entry_foreach(iter0, entry0, total_size) {
1699 ret = check_compat_entry_size_and_hooks(iter0, info, &size,
6b4ff2d7
JE
1700 entry0,
1701 entry0 + total_size,
1702 hook_entries,
1703 underflows,
1704 name);
72b2b1dd 1705 if (ret != 0)
0559518b
JE
1706 goto out_unlock;
1707 ++j;
72b2b1dd 1708 }
2722971c
DM
1709
1710 ret = -EINVAL;
920b868a 1711 if (j != number) {
2722971c 1712 duprintf("translate_compat_table: %u not %u entries\n",
920b868a 1713 j, number);
2722971c
DM
1714 goto out_unlock;
1715 }
1716
1717 /* Check hooks all assigned */
6e23ae2a 1718 for (i = 0; i < NF_INET_NUMHOOKS; i++) {
2722971c
DM
1719 /* Only hooks which are valid */
1720 if (!(valid_hooks & (1 << i)))
1721 continue;
1722 if (info->hook_entry[i] == 0xFFFFFFFF) {
1723 duprintf("Invalid hook entry %u %u\n",
1724 i, hook_entries[i]);
1725 goto out_unlock;
1da177e4 1726 }
2722971c
DM
1727 if (info->underflow[i] == 0xFFFFFFFF) {
1728 duprintf("Invalid underflow %u %u\n",
1729 i, underflows[i]);
1730 goto out_unlock;
1731 }
1732 }
1733
1734 ret = -ENOMEM;
1735 newinfo = xt_alloc_table_info(size);
1736 if (!newinfo)
1737 goto out_unlock;
1738
1739 newinfo->number = number;
6e23ae2a 1740 for (i = 0; i < NF_INET_NUMHOOKS; i++) {
2722971c
DM
1741 newinfo->hook_entry[i] = info->hook_entry[i];
1742 newinfo->underflow[i] = info->underflow[i];
1743 }
1744 entry1 = newinfo->entries[raw_smp_processor_id()];
1745 pos = entry1;
4b478248 1746 size = total_size;
72b2b1dd 1747 xt_entry_foreach(iter0, entry0, total_size) {
6b4ff2d7
JE
1748 ret = compat_copy_entry_from_user(iter0, &pos, &size,
1749 name, newinfo, entry1);
72b2b1dd
JE
1750 if (ret != 0)
1751 break;
1752 }
b386d9f5 1753 xt_compat_flush_offsets(AF_INET);
2722971c
DM
1754 xt_compat_unlock(AF_INET);
1755 if (ret)
1756 goto free_newinfo;
1757
1758 ret = -ELOOP;
1759 if (!mark_source_chains(newinfo, valid_hooks, entry1))
1760 goto free_newinfo;
1761
4c1b52bc 1762 i = 0;
72b2b1dd 1763 xt_entry_foreach(iter1, entry1, newinfo->size) {
0559518b 1764 ret = compat_check_entry(iter1, net, name);
72b2b1dd
JE
1765 if (ret != 0)
1766 break;
0559518b 1767 ++i;
72b2b1dd 1768 }
4c1b52bc 1769 if (ret) {
72b2b1dd
JE
1770 /*
1771 * The first i matches need cleanup_entry (calls ->destroy)
1772 * because they had called ->check already. The other j-i
1773 * entries need only release.
1774 */
1775 int skip = i;
4c1b52bc 1776 j -= i;
72b2b1dd
JE
1777 xt_entry_foreach(iter0, entry0, newinfo->size) {
1778 if (skip-- > 0)
1779 continue;
0559518b 1780 if (j-- == 0)
72b2b1dd 1781 break;
0559518b 1782 compat_release_entry(iter0);
72b2b1dd 1783 }
0559518b
JE
1784 xt_entry_foreach(iter1, entry1, newinfo->size) {
1785 if (i-- == 0)
72b2b1dd 1786 break;
0559518b
JE
1787 cleanup_entry(iter1, net);
1788 }
4c1b52bc
DM
1789 xt_free_table_info(newinfo);
1790 return ret;
1791 }
f6677f43 1792
2722971c 1793 /* And one copy for every other CPU */
fb1bb34d 1794 for_each_possible_cpu(i)
2722971c
DM
1795 if (newinfo->entries[i] && newinfo->entries[i] != entry1)
1796 memcpy(newinfo->entries[i], entry1, newinfo->size);
1797
1798 *pinfo = newinfo;
1799 *pentry0 = entry1;
1800 xt_free_table_info(info);
1801 return 0;
1da177e4 1802
2722971c
DM
1803free_newinfo:
1804 xt_free_table_info(newinfo);
1805out:
0559518b
JE
1806 xt_entry_foreach(iter0, entry0, total_size) {
1807 if (j-- == 0)
72b2b1dd 1808 break;
0559518b
JE
1809 compat_release_entry(iter0);
1810 }
1da177e4 1811 return ret;
2722971c 1812out_unlock:
b386d9f5 1813 xt_compat_flush_offsets(AF_INET);
2722971c
DM
1814 xt_compat_unlock(AF_INET);
1815 goto out;
1da177e4
LT
1816}
1817
1818static int
34bd137b 1819compat_do_replace(struct net *net, void __user *user, unsigned int len)
1da177e4
LT
1820{
1821 int ret;
2722971c
DM
1822 struct compat_ipt_replace tmp;
1823 struct xt_table_info *newinfo;
1824 void *loc_cpu_entry;
72b2b1dd 1825 struct ipt_entry *iter;
1da177e4
LT
1826
1827 if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1828 return -EFAULT;
1829
ee4bb818 1830 /* overflow check */
259d4e41 1831 if (tmp.size >= INT_MAX / num_possible_cpus())
ee4bb818
KK
1832 return -ENOMEM;
1833 if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters))
1834 return -ENOMEM;
1835
2e4e6a17 1836 newinfo = xt_alloc_table_info(tmp.size);
1da177e4
LT
1837 if (!newinfo)
1838 return -ENOMEM;
1839
9c547959 1840 /* choose the copy that is on our node/cpu */
31836064
ED
1841 loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
1842 if (copy_from_user(loc_cpu_entry, user + sizeof(tmp),
1da177e4
LT
1843 tmp.size) != 0) {
1844 ret = -EFAULT;
1845 goto free_newinfo;
1846 }
1847
a83d8e8d 1848 ret = translate_compat_table(net, tmp.name, tmp.valid_hooks,
4b478248
PM
1849 &newinfo, &loc_cpu_entry, tmp.size,
1850 tmp.num_entries, tmp.hook_entry,
1851 tmp.underflow);
2722971c 1852 if (ret != 0)
1da177e4 1853 goto free_newinfo;
1da177e4 1854
2722971c 1855 duprintf("compat_do_replace: Translated table\n");
1da177e4 1856
34bd137b 1857 ret = __do_replace(net, tmp.name, tmp.valid_hooks, newinfo,
4b478248 1858 tmp.num_counters, compat_ptr(tmp.counters));
2722971c
DM
1859 if (ret)
1860 goto free_newinfo_untrans;
1861 return 0;
1da177e4 1862
2722971c 1863 free_newinfo_untrans:
72b2b1dd 1864 xt_entry_foreach(iter, loc_cpu_entry, newinfo->size)
0559518b 1865 cleanup_entry(iter, net);
2722971c
DM
1866 free_newinfo:
1867 xt_free_table_info(newinfo);
1868 return ret;
1869}
1da177e4 1870
2722971c
DM
1871static int
1872compat_do_ipt_set_ctl(struct sock *sk, int cmd, void __user *user,
4b478248 1873 unsigned int len)
2722971c
DM
1874{
1875 int ret;
1da177e4 1876
2722971c
DM
1877 if (!capable(CAP_NET_ADMIN))
1878 return -EPERM;
1da177e4 1879
2722971c
DM
1880 switch (cmd) {
1881 case IPT_SO_SET_REPLACE:
3b1e0a65 1882 ret = compat_do_replace(sock_net(sk), user, len);
2722971c 1883 break;
1da177e4 1884
2722971c 1885 case IPT_SO_SET_ADD_COUNTERS:
3b1e0a65 1886 ret = do_add_counters(sock_net(sk), user, len, 1);
2722971c
DM
1887 break;
1888
1889 default:
1890 duprintf("do_ipt_set_ctl: unknown request %i\n", cmd);
1891 ret = -EINVAL;
1892 }
1da177e4 1893
1da177e4
LT
1894 return ret;
1895}
1896
4b478248 1897struct compat_ipt_get_entries {
2722971c
DM
1898 char name[IPT_TABLE_MAXNAMELEN];
1899 compat_uint_t size;
1900 struct compat_ipt_entry entrytable[0];
1901};
1da177e4 1902
4b478248
PM
1903static int
1904compat_copy_entries_to_user(unsigned int total_size, struct xt_table *table,
1905 void __user *userptr)
2722971c 1906{
2722971c 1907 struct xt_counters *counters;
5452e425 1908 const struct xt_table_info *private = table->private;
2722971c
DM
1909 void __user *pos;
1910 unsigned int size;
1911 int ret = 0;
5452e425 1912 const void *loc_cpu_entry;
a18aa31b 1913 unsigned int i = 0;
72b2b1dd 1914 struct ipt_entry *iter;
1da177e4 1915
2722971c
DM
1916 counters = alloc_counters(table);
1917 if (IS_ERR(counters))
1918 return PTR_ERR(counters);
1919
1920 /* choose the copy that is on our node/cpu, ...
1921 * This choice is lazy (because current thread is
1922 * allowed to migrate to another cpu)
1923 */
1924 loc_cpu_entry = private->entries[raw_smp_processor_id()];
1925 pos = userptr;
1926 size = total_size;
72b2b1dd
JE
1927 xt_entry_foreach(iter, loc_cpu_entry, total_size) {
1928 ret = compat_copy_entry_to_user(iter, &pos,
6b4ff2d7 1929 &size, counters, i++);
72b2b1dd
JE
1930 if (ret != 0)
1931 break;
1932 }
2722971c 1933
2722971c
DM
1934 vfree(counters);
1935 return ret;
1da177e4
LT
1936}
1937
1938static int
34bd137b
AD
1939compat_get_entries(struct net *net, struct compat_ipt_get_entries __user *uptr,
1940 int *len)
1da177e4 1941{
2722971c
DM
1942 int ret;
1943 struct compat_ipt_get_entries get;
e60a13e0 1944 struct xt_table *t;
1da177e4 1945
2722971c 1946 if (*len < sizeof(get)) {
c9d8fe13 1947 duprintf("compat_get_entries: %u < %zu\n", *len, sizeof(get));
1da177e4 1948 return -EINVAL;
2722971c 1949 }
1da177e4 1950
2722971c
DM
1951 if (copy_from_user(&get, uptr, sizeof(get)) != 0)
1952 return -EFAULT;
1da177e4 1953
2722971c 1954 if (*len != sizeof(struct compat_ipt_get_entries) + get.size) {
c9d8fe13
PM
1955 duprintf("compat_get_entries: %u != %zu\n",
1956 *len, sizeof(get) + get.size);
2722971c 1957 return -EINVAL;
1da177e4
LT
1958 }
1959
2722971c 1960 xt_compat_lock(AF_INET);
34bd137b 1961 t = xt_find_table_lock(net, AF_INET, get.name);
2722971c 1962 if (t && !IS_ERR(t)) {
5452e425 1963 const struct xt_table_info *private = t->private;
2722971c 1964 struct xt_table_info info;
9c547959 1965 duprintf("t->private->number = %u\n", private->number);
2722971c
DM
1966 ret = compat_table_info(private, &info);
1967 if (!ret && get.size == info.size) {
1968 ret = compat_copy_entries_to_user(private->size,
4b478248 1969 t, uptr->entrytable);
2722971c
DM
1970 } else if (!ret) {
1971 duprintf("compat_get_entries: I've got %u not %u!\n",
9c547959 1972 private->size, get.size);
544473c1 1973 ret = -EAGAIN;
2722971c 1974 }
b386d9f5 1975 xt_compat_flush_offsets(AF_INET);
2722971c
DM
1976 module_put(t->me);
1977 xt_table_unlock(t);
1978 } else
1da177e4 1979 ret = t ? PTR_ERR(t) : -ENOENT;
1da177e4 1980
2722971c
DM
1981 xt_compat_unlock(AF_INET);
1982 return ret;
1983}
1da177e4 1984
79030ed0
PM
1985static int do_ipt_get_ctl(struct sock *, int, void __user *, int *);
1986
2722971c
DM
1987static int
1988compat_do_ipt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
1989{
1990 int ret;
1da177e4 1991
82fac054
BS
1992 if (!capable(CAP_NET_ADMIN))
1993 return -EPERM;
1994
2722971c
DM
1995 switch (cmd) {
1996 case IPT_SO_GET_INFO:
3b1e0a65 1997 ret = get_info(sock_net(sk), user, len, 1);
2722971c
DM
1998 break;
1999 case IPT_SO_GET_ENTRIES:
3b1e0a65 2000 ret = compat_get_entries(sock_net(sk), user, len);
2722971c
DM
2001 break;
2002 default:
79030ed0 2003 ret = do_ipt_get_ctl(sk, cmd, user, len);
2722971c 2004 }
1da177e4
LT
2005 return ret;
2006}
2722971c 2007#endif
1da177e4
LT
2008
2009static int
9c547959 2010do_ipt_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len)
1da177e4
LT
2011{
2012 int ret;
2013
2014 if (!capable(CAP_NET_ADMIN))
2015 return -EPERM;
2016
2017 switch (cmd) {
2018 case IPT_SO_SET_REPLACE:
3b1e0a65 2019 ret = do_replace(sock_net(sk), user, len);
1da177e4
LT
2020 break;
2021
2022 case IPT_SO_SET_ADD_COUNTERS:
3b1e0a65 2023 ret = do_add_counters(sock_net(sk), user, len, 0);
1da177e4
LT
2024 break;
2025
2026 default:
2027 duprintf("do_ipt_set_ctl: unknown request %i\n", cmd);
2028 ret = -EINVAL;
2029 }
2030
2031 return ret;
2032}
2033
2034static int
2035do_ipt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
2036{
2037 int ret;
2038
2039 if (!capable(CAP_NET_ADMIN))
2040 return -EPERM;
2041
2042 switch (cmd) {
2722971c 2043 case IPT_SO_GET_INFO:
3b1e0a65 2044 ret = get_info(sock_net(sk), user, len, 0);
2722971c 2045 break;
1da177e4 2046
2722971c 2047 case IPT_SO_GET_ENTRIES:
3b1e0a65 2048 ret = get_entries(sock_net(sk), user, len);
1da177e4 2049 break;
1da177e4
LT
2050
2051 case IPT_SO_GET_REVISION_MATCH:
2052 case IPT_SO_GET_REVISION_TARGET: {
2053 struct ipt_get_revision rev;
2e4e6a17 2054 int target;
1da177e4
LT
2055
2056 if (*len != sizeof(rev)) {
2057 ret = -EINVAL;
2058 break;
2059 }
2060 if (copy_from_user(&rev, user, sizeof(rev)) != 0) {
2061 ret = -EFAULT;
2062 break;
2063 }
2064
2065 if (cmd == IPT_SO_GET_REVISION_TARGET)
2e4e6a17 2066 target = 1;
1da177e4 2067 else
2e4e6a17 2068 target = 0;
1da177e4 2069
2e4e6a17
HW
2070 try_then_request_module(xt_find_revision(AF_INET, rev.name,
2071 rev.revision,
2072 target, &ret),
1da177e4
LT
2073 "ipt_%s", rev.name);
2074 break;
2075 }
2076
2077 default:
2078 duprintf("do_ipt_get_ctl: unknown request %i\n", cmd);
2079 ret = -EINVAL;
2080 }
2081
2082 return ret;
2083}
2084
35aad0ff
JE
2085struct xt_table *ipt_register_table(struct net *net,
2086 const struct xt_table *table,
44d34e72 2087 const struct ipt_replace *repl)
1da177e4
LT
2088{
2089 int ret;
2e4e6a17 2090 struct xt_table_info *newinfo;
259d4e41 2091 struct xt_table_info bootstrap
1da177e4 2092 = { 0, 0, 0, { 0 }, { 0 }, { } };
31836064 2093 void *loc_cpu_entry;
a98da11d 2094 struct xt_table *new_table;
1da177e4 2095
2e4e6a17 2096 newinfo = xt_alloc_table_info(repl->size);
44d34e72
AD
2097 if (!newinfo) {
2098 ret = -ENOMEM;
2099 goto out;
2100 }
1da177e4 2101
9c547959 2102 /* choose the copy on our node/cpu, but dont care about preemption */
31836064
ED
2103 loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
2104 memcpy(loc_cpu_entry, repl->entries, repl->size);
1da177e4 2105
0f234214 2106 ret = translate_table(net, newinfo, loc_cpu_entry, repl);
44d34e72
AD
2107 if (ret != 0)
2108 goto out_free;
1da177e4 2109
44d34e72 2110 new_table = xt_register_table(net, table, &bootstrap, newinfo);
a98da11d 2111 if (IS_ERR(new_table)) {
44d34e72
AD
2112 ret = PTR_ERR(new_table);
2113 goto out_free;
1da177e4
LT
2114 }
2115
44d34e72
AD
2116 return new_table;
2117
2118out_free:
2119 xt_free_table_info(newinfo);
2120out:
2121 return ERR_PTR(ret);
1da177e4
LT
2122}
2123
f54e9367 2124void ipt_unregister_table(struct net *net, struct xt_table *table)
1da177e4 2125{
2e4e6a17 2126 struct xt_table_info *private;
31836064 2127 void *loc_cpu_entry;
df200969 2128 struct module *table_owner = table->me;
72b2b1dd 2129 struct ipt_entry *iter;
31836064 2130
e905a9ed 2131 private = xt_unregister_table(table);
1da177e4
LT
2132
2133 /* Decrease module usage counts and free resources */
2e4e6a17 2134 loc_cpu_entry = private->entries[raw_smp_processor_id()];
72b2b1dd 2135 xt_entry_foreach(iter, loc_cpu_entry, private->size)
0559518b 2136 cleanup_entry(iter, net);
df200969
AD
2137 if (private->number > private->initial_entries)
2138 module_put(table_owner);
2e4e6a17 2139 xt_free_table_info(private);
1da177e4
LT
2140}
2141
2142/* Returns 1 if the type and code is matched by the range, 0 otherwise */
1d93a9cb 2143static inline bool
1da177e4
LT
2144icmp_type_code_match(u_int8_t test_type, u_int8_t min_code, u_int8_t max_code,
2145 u_int8_t type, u_int8_t code,
1d93a9cb 2146 bool invert)
1da177e4 2147{
9c547959
PM
2148 return ((test_type == 0xFF) ||
2149 (type == test_type && code >= min_code && code <= max_code))
1da177e4
LT
2150 ^ invert;
2151}
2152
1d93a9cb 2153static bool
f7108a20 2154icmp_match(const struct sk_buff *skb, const struct xt_match_param *par)
1da177e4 2155{
5452e425
JE
2156 const struct icmphdr *ic;
2157 struct icmphdr _icmph;
f7108a20 2158 const struct ipt_icmp *icmpinfo = par->matchinfo;
1da177e4
LT
2159
2160 /* Must not be a fragment. */
f7108a20 2161 if (par->fragoff != 0)
1d93a9cb 2162 return false;
1da177e4 2163
f7108a20 2164 ic = skb_header_pointer(skb, par->thoff, sizeof(_icmph), &_icmph);
1da177e4
LT
2165 if (ic == NULL) {
2166 /* We've been asked to examine this packet, and we
2167 * can't. Hence, no choice but to drop.
2168 */
2169 duprintf("Dropping evil ICMP tinygram.\n");
f7108a20 2170 *par->hotdrop = true;
1d93a9cb 2171 return false;
1da177e4
LT
2172 }
2173
2174 return icmp_type_code_match(icmpinfo->type,
2175 icmpinfo->code[0],
2176 icmpinfo->code[1],
2177 ic->type, ic->code,
2178 !!(icmpinfo->invflags&IPT_ICMP_INV));
2179}
2180
9b4fce7a 2181static bool icmp_checkentry(const struct xt_mtchk_param *par)
1da177e4 2182{
9b4fce7a 2183 const struct ipt_icmp *icmpinfo = par->matchinfo;
1da177e4 2184
1d5cd909
PM
2185 /* Must specify no unknown invflags */
2186 return !(icmpinfo->invflags & ~IPT_ICMP_INV);
1da177e4
LT
2187}
2188
2189/* The built-in targets: standard (NULL) and error. */
9f15c530 2190static struct xt_target ipt_standard_target __read_mostly = {
1da177e4 2191 .name = IPT_STANDARD_TARGET,
1d5cd909 2192 .targetsize = sizeof(int),
4ba351cf 2193 .family = NFPROTO_IPV4,
2722971c 2194#ifdef CONFIG_COMPAT
9fa492cd
PM
2195 .compatsize = sizeof(compat_int_t),
2196 .compat_from_user = compat_standard_from_user,
2197 .compat_to_user = compat_standard_to_user,
2722971c 2198#endif
1da177e4
LT
2199};
2200
9f15c530 2201static struct xt_target ipt_error_target __read_mostly = {
1da177e4
LT
2202 .name = IPT_ERROR_TARGET,
2203 .target = ipt_error,
1d5cd909 2204 .targetsize = IPT_FUNCTION_MAXNAMELEN,
4ba351cf 2205 .family = NFPROTO_IPV4,
1da177e4
LT
2206};
2207
2208static struct nf_sockopt_ops ipt_sockopts = {
2209 .pf = PF_INET,
2210 .set_optmin = IPT_BASE_CTL,
2211 .set_optmax = IPT_SO_SET_MAX+1,
2212 .set = do_ipt_set_ctl,
2722971c
DM
2213#ifdef CONFIG_COMPAT
2214 .compat_set = compat_do_ipt_set_ctl,
2215#endif
1da177e4
LT
2216 .get_optmin = IPT_BASE_CTL,
2217 .get_optmax = IPT_SO_GET_MAX+1,
2218 .get = do_ipt_get_ctl,
2722971c
DM
2219#ifdef CONFIG_COMPAT
2220 .compat_get = compat_do_ipt_get_ctl,
2221#endif
16fcec35 2222 .owner = THIS_MODULE,
1da177e4
LT
2223};
2224
9f15c530 2225static struct xt_match icmp_matchstruct __read_mostly = {
1da177e4 2226 .name = "icmp",
1d5cd909
PM
2227 .match = icmp_match,
2228 .matchsize = sizeof(struct ipt_icmp),
9c547959 2229 .checkentry = icmp_checkentry,
1d5cd909 2230 .proto = IPPROTO_ICMP,
4ba351cf 2231 .family = NFPROTO_IPV4,
1da177e4
LT
2232};
2233
3cb609d5
AD
2234static int __net_init ip_tables_net_init(struct net *net)
2235{
383ca5b8 2236 return xt_proto_init(net, NFPROTO_IPV4);
3cb609d5
AD
2237}
2238
2239static void __net_exit ip_tables_net_exit(struct net *net)
2240{
383ca5b8 2241 xt_proto_fini(net, NFPROTO_IPV4);
3cb609d5
AD
2242}
2243
2244static struct pernet_operations ip_tables_net_ops = {
2245 .init = ip_tables_net_init,
2246 .exit = ip_tables_net_exit,
2247};
2248
65b4b4e8 2249static int __init ip_tables_init(void)
1da177e4
LT
2250{
2251 int ret;
2252
3cb609d5 2253 ret = register_pernet_subsys(&ip_tables_net_ops);
0eff66e6
PM
2254 if (ret < 0)
2255 goto err1;
2e4e6a17 2256
1da177e4 2257 /* Noone else will be downing sem now, so we won't sleep */
0eff66e6
PM
2258 ret = xt_register_target(&ipt_standard_target);
2259 if (ret < 0)
2260 goto err2;
2261 ret = xt_register_target(&ipt_error_target);
2262 if (ret < 0)
2263 goto err3;
2264 ret = xt_register_match(&icmp_matchstruct);
2265 if (ret < 0)
2266 goto err4;
1da177e4
LT
2267
2268 /* Register setsockopt */
2269 ret = nf_register_sockopt(&ipt_sockopts);
0eff66e6
PM
2270 if (ret < 0)
2271 goto err5;
1da177e4 2272
ff67e4e4 2273 pr_info("(C) 2000-2006 Netfilter Core Team\n");
1da177e4 2274 return 0;
0eff66e6
PM
2275
2276err5:
2277 xt_unregister_match(&icmp_matchstruct);
2278err4:
2279 xt_unregister_target(&ipt_error_target);
2280err3:
2281 xt_unregister_target(&ipt_standard_target);
2282err2:
3cb609d5 2283 unregister_pernet_subsys(&ip_tables_net_ops);
0eff66e6
PM
2284err1:
2285 return ret;
1da177e4
LT
2286}
2287
65b4b4e8 2288static void __exit ip_tables_fini(void)
1da177e4
LT
2289{
2290 nf_unregister_sockopt(&ipt_sockopts);
2e4e6a17 2291
a45049c5
PNA
2292 xt_unregister_match(&icmp_matchstruct);
2293 xt_unregister_target(&ipt_error_target);
2294 xt_unregister_target(&ipt_standard_target);
2e4e6a17 2295
3cb609d5 2296 unregister_pernet_subsys(&ip_tables_net_ops);
1da177e4
LT
2297}
2298
2299EXPORT_SYMBOL(ipt_register_table);
2300EXPORT_SYMBOL(ipt_unregister_table);
1da177e4 2301EXPORT_SYMBOL(ipt_do_table);
65b4b4e8
AM
2302module_init(ip_tables_init);
2303module_exit(ip_tables_fini);