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