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

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

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

~ [ 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