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