~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/net/ipv6/netfilter/ip6_tables.c

Version: ~ [ linux-5.4 ] ~ [ linux-5.3.13 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.86 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.156 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.203 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.202 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.19.8 ] ~ [ linux-3.18.140 ] ~ [ linux-3.17.8 ] ~ [ linux-3.16.78 ] ~ [ linux-3.15.10 ] ~ [ linux-3.14.79 ] ~ [ linux-3.13.11 ] ~ [ linux-3.12.74 ] ~ [ linux-3.11.10 ] ~ [ linux-3.10.108 ] ~ [ linux-3.9.11 ] ~ [ linux-3.8.13 ] ~ [ linux-3.7.10 ] ~ [ linux-3.6.11 ] ~ [ linux-3.5.7 ] ~ [ linux-3.4.113 ] ~ [ linux-3.3.8 ] ~ [ linux-3.2.102 ] ~ [ linux-3.1.10 ] ~ [ linux-3.0.101 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

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

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp