1 /*
2  * Packet matching code.
3  *
4  * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
5  * Copyright (C) 2000-2002 Netfilter core team <coreteam@netfilter.org>
6  *
7  * 19 Jan 2002 Harald Welte <laforge@gnumonks.org>
8  * 	- increase module usage count as soon as we have rules inside
9  * 	  a table
10  * 06 Jun 2002 Andras Kis-Szabo <kisza@sch.bme.hu>
11  *      - new extension header parser code
12  */
13 #include <linux/config.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/tcp.h>
20 #include <linux/udp.h>
21 #include <linux/icmpv6.h>
22 #include <net/ip.h>
23 #include <net/ipv6.h>
24 #include <asm/uaccess.h>
25 #include <asm/semaphore.h>
26 #include <linux/proc_fs.h>
27 
28 #include <linux/netfilter_ipv6/ip6_tables.h>
29 
30 #define IPV6_HDR_LEN	(sizeof(struct ipv6hdr))
31 #define IPV6_OPTHDR_LEN	(sizeof(struct ipv6_opt_hdr))
32 
33 /*#define DEBUG_IP_FIREWALL*/
34 /*#define DEBUG_ALLOW_ALL*/ /* Useful for remote debugging */
35 /*#define DEBUG_IP_FIREWALL_USER*/
36 
37 #ifdef DEBUG_IP_FIREWALL
38 #define dprintf(format, args...)  printk(format , ## args)
39 #else
40 #define dprintf(format, args...)
41 #endif
42 
43 #ifdef DEBUG_IP_FIREWALL_USER
44 #define duprintf(format, args...) printk(format , ## args)
45 #else
46 #define duprintf(format, args...)
47 #endif
48 
49 #ifdef CONFIG_NETFILTER_DEBUG
50 #define IP_NF_ASSERT(x)						\
51 do {								\
52 	if (!(x))						\
53 		printk("IP_NF_ASSERT: %s:%s:%u\n",		\
54 		       __FUNCTION__, __FILE__, __LINE__);	\
55 } while(0)
56 #else
57 #define IP_NF_ASSERT(x)
58 #endif
59 #define SMP_ALIGN(x) (((x) + SMP_CACHE_BYTES-1) & ~(SMP_CACHE_BYTES-1))
60 
61 static DECLARE_MUTEX(ip6t_mutex);
62 
63 /* Must have mutex */
64 #define ASSERT_READ_LOCK(x) IP_NF_ASSERT(down_trylock(&ip6t_mutex) != 0)
65 #define ASSERT_WRITE_LOCK(x) IP_NF_ASSERT(down_trylock(&ip6t_mutex) != 0)
66 #include <linux/netfilter_ipv4/lockhelp.h>
67 #include <linux/netfilter_ipv4/listhelp.h>
68 
69 #if 0
70 /* All the better to debug you with... */
71 #define static
72 #define inline
73 #endif
74 
75 /* Locking is simple: we assume at worst case there will be one packet
76    in user context and one from bottom halves (or soft irq if Alexey's
77    softnet patch was applied).
78 
79    We keep a set of rules for each CPU, so we can avoid write-locking
80    them; doing a readlock_bh() stops packets coming through if we're
81    in user context.
82 
83    To be cache friendly on SMP, we arrange them like so:
84    [ n-entries ]
85    ... cache-align padding ...
86    [ n-entries ]
87 
88    Hence the start of any table is given by get_table() below.  */
89 
90 /* The table itself */
91 struct ip6t_table_info
92 {
93 	/* Size per table */
94 	unsigned int size;
95 	/* Number of entries: FIXME. --RR */
96 	unsigned int number;
97 	/* Initial number of entries. Needed for module usage count */
98 	unsigned int initial_entries;
99 
100 	/* Entry points and underflows */
101 	unsigned int hook_entry[NF_IP6_NUMHOOKS];
102 	unsigned int underflow[NF_IP6_NUMHOOKS];
103 
104 	/* ip6t_entry tables: one per CPU */
105 	char entries[0] ____cacheline_aligned;
106 };
107 
108 static LIST_HEAD(ip6t_target);
109 static LIST_HEAD(ip6t_match);
110 static LIST_HEAD(ip6t_tables);
111 #define ADD_COUNTER(c,b,p) do { (c).bcnt += (b); (c).pcnt += (p); } while(0)
112 
113 #ifdef CONFIG_SMP
114 #define TABLE_OFFSET(t,p) (SMP_ALIGN((t)->size)*(p))
115 #else
116 #define TABLE_OFFSET(t,p) 0
117 #endif
118 
119 #if 0
120 #define down(x) do { printk("DOWN:%u:" #x "\n", __LINE__); down(x); } while(0)
121 #define down_interruptible(x) ({ int __r; printk("DOWNi:%u:" #x "\n", __LINE__); __r = down_interruptible(x); if (__r != 0) printk("ABORT-DOWNi:%u\n", __LINE__); __r; })
122 #define up(x) do { printk("UP:%u:" #x "\n", __LINE__); up(x); } while(0)
123 #endif
124 
ip6_masked_addrcmp(struct in6_addr addr1,struct in6_addr mask,struct in6_addr addr2)125 static int ip6_masked_addrcmp(struct in6_addr addr1, struct in6_addr mask,
126 			      struct in6_addr addr2)
127 {
128 	int i;
129 	for( i = 0; i < 16; i++){
130 		if((addr1.s6_addr[i] & mask.s6_addr[i]) !=
131 		   (addr2.s6_addr[i] & mask.s6_addr[i]))
132 			return 1;
133 	}
134 	return 0;
135 }
136 
137 /* Check for an extension */
138 int
ip6t_ext_hdr(u8 nexthdr)139 ip6t_ext_hdr(u8 nexthdr)
140 {
141         return ( (nexthdr == IPPROTO_HOPOPTS)   ||
142                  (nexthdr == IPPROTO_ROUTING)   ||
143                  (nexthdr == IPPROTO_FRAGMENT)  ||
144                  (nexthdr == IPPROTO_ESP)       ||
145                  (nexthdr == IPPROTO_AH)        ||
146                  (nexthdr == IPPROTO_NONE)      ||
147                  (nexthdr == IPPROTO_DSTOPTS) );
148 }
149 
150 /* Returns whether matches rule or not. */
151 static inline int
ip6_packet_match(const struct sk_buff * skb,const char * indev,const char * outdev,const struct ip6t_ip6 * ip6info,unsigned int * protoff,int * fragoff)152 ip6_packet_match(const struct sk_buff *skb,
153 		 const char *indev,
154 		 const char *outdev,
155 		 const struct ip6t_ip6 *ip6info,
156 		 unsigned int *protoff,
157 		 int *fragoff)
158 {
159 	size_t i;
160 	unsigned long ret;
161 	const struct ipv6hdr *ipv6 = skb->nh.ipv6h;
162 
163 #define FWINV(bool,invflg) ((bool) ^ !!(ip6info->invflags & invflg))
164 
165 	if (FWINV(ip6_masked_addrcmp(ipv6->saddr,ip6info->smsk,ip6info->src),
166 		  IP6T_INV_SRCIP)
167 	    || FWINV(ip6_masked_addrcmp(ipv6->daddr,ip6info->dmsk,ip6info->dst),
168 		     IP6T_INV_DSTIP)) {
169 		dprintf("Source or dest mismatch.\n");
170 /*
171 		dprintf("SRC: %u. Mask: %u. Target: %u.%s\n", ip->saddr,
172 			ipinfo->smsk.s_addr, ipinfo->src.s_addr,
173 			ipinfo->invflags & IP6T_INV_SRCIP ? " (INV)" : "");
174 		dprintf("DST: %u. Mask: %u. Target: %u.%s\n", ip->daddr,
175 			ipinfo->dmsk.s_addr, ipinfo->dst.s_addr,
176 			ipinfo->invflags & IP6T_INV_DSTIP ? " (INV)" : "");*/
177 		return 0;
178 	}
179 
180 	/* Look for ifname matches; this should unroll nicely. */
181 	for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
182 		ret |= (((const unsigned long *)indev)[i]
183 			^ ((const unsigned long *)ip6info->iniface)[i])
184 			& ((const unsigned long *)ip6info->iniface_mask)[i];
185 	}
186 
187 	if (FWINV(ret != 0, IP6T_INV_VIA_IN)) {
188 		dprintf("VIA in mismatch (%s vs %s).%s\n",
189 			indev, ip6info->iniface,
190 			ip6info->invflags&IP6T_INV_VIA_IN ?" (INV)":"");
191 		return 0;
192 	}
193 
194 	for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
195 		ret |= (((const unsigned long *)outdev)[i]
196 			^ ((const unsigned long *)ip6info->outiface)[i])
197 			& ((const unsigned long *)ip6info->outiface_mask)[i];
198 	}
199 
200 	if (FWINV(ret != 0, IP6T_INV_VIA_OUT)) {
201 		dprintf("VIA out mismatch (%s vs %s).%s\n",
202 			outdev, ip6info->outiface,
203 			ip6info->invflags&IP6T_INV_VIA_OUT ?" (INV)":"");
204 		return 0;
205 	}
206 
207 /* ... might want to do something with class and flowlabel here ... */
208 
209 	/* look for the desired protocol header */
210 	if((ip6info->flags & IP6T_F_PROTO)) {
211 		u_int8_t currenthdr = ipv6->nexthdr;
212 		struct ipv6_opt_hdr *hdrptr;
213 		u_int16_t ptr;		/* Header offset in skb */
214 		u_int16_t hdrlen;	/* Header */
215 		u_int16_t foff = 0;
216 
217 		ptr = IPV6_HDR_LEN;
218 
219 		while (ip6t_ext_hdr(currenthdr)) {
220 	                /* Is there enough space for the next ext header? */
221 	                if (skb->len - ptr < IPV6_OPTHDR_LEN)
222 	                        return 0;
223 
224 			/* NONE or ESP: there isn't protocol part */
225 			/* If we want to count these packets in '-p all',
226 			 * we will change the return 0 to 1*/
227 			if ((currenthdr == IPPROTO_NONE) ||
228 				(currenthdr == IPPROTO_ESP))
229 				break;
230 
231 	                hdrptr = (struct ipv6_opt_hdr *)(skb->data + ptr);
232 
233 			/* Size calculation */
234 	                if (currenthdr == IPPROTO_FRAGMENT) {
235 				unsigned int foff_off;
236 
237 				foff_off = ptr + offsetof(struct frag_hdr,
238 							  frag_off);
239 				if (skb->len - foff_off < sizeof(foff))
240 					return 0;
241 
242 				foff = ntohs(*(u_int16_t *)(skb->data
243 							    + foff_off))
244 				       & ~0x7;
245 	                        hdrlen = 8;
246 	                } else if (currenthdr == IPPROTO_AH)
247 	                        hdrlen = (hdrptr->hdrlen+2)<<2;
248 	                else
249 	                        hdrlen = ipv6_optlen(hdrptr);
250 
251 			currenthdr = hdrptr->nexthdr;
252 	                ptr += hdrlen;
253 			/* ptr is too large */
254 	                if ( ptr > skb->len )
255 				return 0;
256 			if (foff) {
257 				if (ip6t_ext_hdr(currenthdr))
258 					return 0;
259 				break;
260 			}
261 		}
262 
263 		*protoff = ptr;
264 		*fragoff = foff;
265 
266 		/* currenthdr contains the protocol header */
267 
268 		dprintf("Packet protocol %hi ?= %s%hi.\n",
269 				currenthdr,
270 				ip6info->invflags & IP6T_INV_PROTO ? "!":"",
271 				ip6info->proto);
272 
273 		if (ip6info->proto == currenthdr) {
274 			if(ip6info->invflags & IP6T_INV_PROTO) {
275 				return 0;
276 			}
277 			return 1;
278 		}
279 
280 		/* We need match for the '-p all', too! */
281 		if ((ip6info->proto != 0) &&
282 			!(ip6info->invflags & IP6T_INV_PROTO))
283 			return 0;
284 	}
285 	return 1;
286 }
287 
288 /* should be ip6 safe */
289 static inline int
ip6_checkentry(const struct ip6t_ip6 * ipv6)290 ip6_checkentry(const struct ip6t_ip6 *ipv6)
291 {
292 	if (ipv6->flags & ~IP6T_F_MASK) {
293 		duprintf("Unknown flag bits set: %08X\n",
294 			 ipv6->flags & ~IP6T_F_MASK);
295 		return 0;
296 	}
297 	if (ipv6->invflags & ~IP6T_INV_MASK) {
298 		duprintf("Unknown invflag bits set: %08X\n",
299 			 ipv6->invflags & ~IP6T_INV_MASK);
300 		return 0;
301 	}
302 	return 1;
303 }
304 
305 static unsigned int
ip6t_error(struct sk_buff ** pskb,unsigned int hooknum,const struct net_device * in,const struct net_device * out,const void * targinfo,void * userinfo)306 ip6t_error(struct sk_buff **pskb,
307 	  unsigned int hooknum,
308 	  const struct net_device *in,
309 	  const struct net_device *out,
310 	  const void *targinfo,
311 	  void *userinfo)
312 {
313 	if (net_ratelimit())
314 		printk("ip6_tables: error: `%s'\n", (char *)targinfo);
315 
316 	return NF_DROP;
317 }
318 
319 static inline
do_match(struct ip6t_entry_match * m,const struct sk_buff * skb,const struct net_device * in,const struct net_device * out,int offset,const void * hdr,u_int16_t datalen,int * hotdrop)320 int do_match(struct ip6t_entry_match *m,
321 	     const struct sk_buff *skb,
322 	     const struct net_device *in,
323 	     const struct net_device *out,
324 	     int offset,
325 	     const void *hdr,
326 	     u_int16_t datalen,
327 	     int *hotdrop)
328 {
329 	/* Stop iteration if it doesn't match */
330 	if (!m->u.kernel.match->match(skb, in, out, m->data,
331 				      offset, hdr, datalen, hotdrop))
332 		return 1;
333 	else
334 		return 0;
335 }
336 
337 static inline struct ip6t_entry *
get_entry(void * base,unsigned int offset)338 get_entry(void *base, unsigned int offset)
339 {
340 	return (struct ip6t_entry *)(base + offset);
341 }
342 
343 /* Returns one of the generic firewall policies, like NF_ACCEPT. */
344 unsigned int
ip6t_do_table(struct sk_buff ** pskb,unsigned int hook,const struct net_device * in,const struct net_device * out,struct ip6t_table * table,void * userdata)345 ip6t_do_table(struct sk_buff **pskb,
346 	      unsigned int hook,
347 	      const struct net_device *in,
348 	      const struct net_device *out,
349 	      struct ip6t_table *table,
350 	      void *userdata)
351 {
352 	static const char nulldevname[IFNAMSIZ] = { 0 };
353 	int offset = 0;
354 	unsigned int protoff = 0;
355 	int hotdrop = 0;
356 	/* Initializing verdict to NF_DROP keeps gcc happy. */
357 	unsigned int verdict = NF_DROP;
358 	const char *indev, *outdev;
359 	void *table_base;
360 	struct ip6t_entry *e, *back;
361 
362 	/* Initialization */
363 	indev = in ? in->name : nulldevname;
364 	outdev = out ? out->name : nulldevname;
365 
366 	/* We handle fragments by dealing with the first fragment as
367 	 * if it was a normal packet.  All other fragments are treated
368 	 * normally, except that they will NEVER match rules that ask
369 	 * things we don't know, ie. tcp syn flag or ports).  If the
370 	 * rule is also a fragment-specific rule, non-fragments won't
371 	 * match it. */
372 
373 	read_lock_bh(&table->lock);
374 	IP_NF_ASSERT(table->valid_hooks & (1 << hook));
375 	table_base = (void *)table->private->entries
376 		+ TABLE_OFFSET(table->private,
377 				cpu_number_map(smp_processor_id()));
378 	e = get_entry(table_base, table->private->hook_entry[hook]);
379 
380 #ifdef CONFIG_NETFILTER_DEBUG
381 	/* Check noone else using our table */
382 	if (((struct ip6t_entry *)table_base)->comefrom != 0xdead57ac
383 	    && ((struct ip6t_entry *)table_base)->comefrom != 0xeeeeeeec) {
384 		printk("ASSERT: CPU #%u, %s comefrom(%p) = %X\n",
385 		       smp_processor_id(),
386 		       table->name,
387 		       &((struct ip6t_entry *)table_base)->comefrom,
388 		       ((struct ip6t_entry *)table_base)->comefrom);
389 	}
390 	((struct ip6t_entry *)table_base)->comefrom = 0x57acc001;
391 #endif
392 
393 	/* For return from builtin chain */
394 	back = get_entry(table_base, table->private->underflow[hook]);
395 
396 	do {
397 		IP_NF_ASSERT(e);
398 		IP_NF_ASSERT(back);
399 		(*pskb)->nfcache |= e->nfcache;
400 		if (ip6_packet_match(*pskb, indev, outdev, &e->ipv6,
401 				     &protoff, &offset)) {
402 			struct ip6t_entry_target *t;
403 
404 			if (IP6T_MATCH_ITERATE(e, do_match,
405 					       *pskb, in, out,
406 					       offset,
407 					       (void *)((*pskb)->data
408 							+ protoff),
409 					       (*pskb)->len - protoff,
410 					       &hotdrop) != 0)
411 				goto no_match;
412 
413 			ADD_COUNTER(e->counters, ntohs((*pskb)->nh.ipv6h->payload_len) + IPV6_HDR_LEN, 1);
414 
415 			t = ip6t_get_target(e);
416 			IP_NF_ASSERT(t->u.kernel.target);
417 			/* Standard target? */
418 			if (!t->u.kernel.target->target) {
419 				int v;
420 
421 				v = ((struct ip6t_standard_target *)t)->verdict;
422 				if (v < 0) {
423 					/* Pop from stack? */
424 					if (v != IP6T_RETURN) {
425 						verdict = (unsigned)(-v) - 1;
426 						break;
427 					}
428 					e = back;
429 					back = get_entry(table_base,
430 							 back->comefrom);
431 					continue;
432 				}
433 				if (table_base + v
434 				    != (void *)e + e->next_offset) {
435 					/* Save old back ptr in next entry */
436 					struct ip6t_entry *next
437 						= (void *)e + e->next_offset;
438 					next->comefrom
439 						= (void *)back - table_base;
440 					/* set back pointer to next entry */
441 					back = next;
442 				}
443 
444 				e = get_entry(table_base, v);
445 			} else {
446 				/* Targets which reenter must return
447                                    abs. verdicts */
448 #ifdef CONFIG_NETFILTER_DEBUG
449 				((struct ip6t_entry *)table_base)->comefrom
450 					= 0xeeeeeeec;
451 #endif
452 				verdict = t->u.kernel.target->target(pskb,
453 								     hook,
454 								     in, out,
455 								     t->data,
456 								     userdata);
457 
458 #ifdef CONFIG_NETFILTER_DEBUG
459 				if (((struct ip6t_entry *)table_base)->comefrom
460 				    != 0xeeeeeeec
461 				    && verdict == IP6T_CONTINUE) {
462 					printk("Target %s reentered!\n",
463 					       t->u.kernel.target->name);
464 					verdict = NF_DROP;
465 				}
466 				((struct ip6t_entry *)table_base)->comefrom
467 					= 0x57acc001;
468 #endif
469 				if (verdict == IP6T_CONTINUE)
470 					e = (void *)e + e->next_offset;
471 				else
472 					/* Verdict */
473 					break;
474 			}
475 		} else {
476 
477 		no_match:
478 			e = (void *)e + e->next_offset;
479 		}
480 	} while (!hotdrop);
481 
482 #ifdef CONFIG_NETFILTER_DEBUG
483 	((struct ip6t_entry *)table_base)->comefrom = 0xdead57ac;
484 #endif
485 	read_unlock_bh(&table->lock);
486 
487 #ifdef DEBUG_ALLOW_ALL
488 	return NF_ACCEPT;
489 #else
490 	if (hotdrop)
491 		return NF_DROP;
492 	else return verdict;
493 #endif
494 }
495 
496 /* If it succeeds, returns element and locks mutex */
497 static inline void *
find_inlist_lock_noload(struct list_head * head,const char * name,int * error,struct semaphore * mutex)498 find_inlist_lock_noload(struct list_head *head,
499 			const char *name,
500 			int *error,
501 			struct semaphore *mutex)
502 {
503 	void *ret;
504 
505 #if 1
506 	duprintf("find_inlist: searching for `%s' in %s.\n",
507 		 name, head == &ip6t_target ? "ip6t_target"
508 		 : head == &ip6t_match ? "ip6t_match"
509 		 : head == &ip6t_tables ? "ip6t_tables" : "UNKNOWN");
510 #endif
511 
512 	*error = down_interruptible(mutex);
513 	if (*error != 0)
514 		return NULL;
515 
516 	ret = list_named_find(head, name);
517 	if (!ret) {
518 		*error = -ENOENT;
519 		up(mutex);
520 	}
521 	return ret;
522 }
523 
524 #ifndef CONFIG_KMOD
525 #define find_inlist_lock(h,n,p,e,m) find_inlist_lock_noload((h),(n),(e),(m))
526 #else
527 static void *
find_inlist_lock(struct list_head * head,const char * name,const char * prefix,int * error,struct semaphore * mutex)528 find_inlist_lock(struct list_head *head,
529 		 const char *name,
530 		 const char *prefix,
531 		 int *error,
532 		 struct semaphore *mutex)
533 {
534 	void *ret;
535 
536 	ret = find_inlist_lock_noload(head, name, error, mutex);
537 	if (!ret) {
538 		char modulename[IP6T_FUNCTION_MAXNAMELEN + strlen(prefix) + 1];
539 		strcpy(modulename, prefix);
540 		strcat(modulename, name);
541 		duprintf("find_inlist: loading `%s'.\n", modulename);
542 		request_module(modulename);
543 		ret = find_inlist_lock_noload(head, name, error, mutex);
544 	}
545 
546 	return ret;
547 }
548 #endif
549 
550 static inline struct ip6t_table *
ip6t_find_table_lock(const char * name,int * error,struct semaphore * mutex)551 ip6t_find_table_lock(const char *name, int *error, struct semaphore *mutex)
552 {
553 	return find_inlist_lock(&ip6t_tables, name, "ip6table_", error, mutex);
554 }
555 
556 static inline struct ip6t_match *
find_match_lock(const char * name,int * error,struct semaphore * mutex)557 find_match_lock(const char *name, int *error, struct semaphore *mutex)
558 {
559 	return find_inlist_lock(&ip6t_match, name, "ip6t_", error, mutex);
560 }
561 
562 struct ip6t_target *
ip6t_find_target_lock(const char * name,int * error,struct semaphore * mutex)563 ip6t_find_target_lock(const char *name, int *error, struct semaphore *mutex)
564 {
565 	return find_inlist_lock(&ip6t_target, name, "ip6t_", error, mutex);
566 }
567 
568 /* All zeroes == unconditional rule. */
569 static inline int
unconditional(const struct ip6t_ip6 * ipv6)570 unconditional(const struct ip6t_ip6 *ipv6)
571 {
572 	unsigned int i;
573 
574 	for (i = 0; i < sizeof(*ipv6); i++)
575 		if (((char *)ipv6)[i])
576 			break;
577 
578 	return (i == sizeof(*ipv6));
579 }
580 
581 /* Figures out from what hook each rule can be called: returns 0 if
582    there are loops.  Puts hook bitmask in comefrom. */
583 static int
mark_source_chains(struct ip6t_table_info * newinfo,unsigned int valid_hooks)584 mark_source_chains(struct ip6t_table_info *newinfo, unsigned int valid_hooks)
585 {
586 	unsigned int hook;
587 
588 	/* No recursion; use packet counter to save back ptrs (reset
589 	   to 0 as we leave), and comefrom to save source hook bitmask */
590 	for (hook = 0; hook < NF_IP6_NUMHOOKS; hook++) {
591 		unsigned int pos = newinfo->hook_entry[hook];
592 		struct ip6t_entry *e
593 			= (struct ip6t_entry *)(newinfo->entries + pos);
594 
595 		if (!(valid_hooks & (1 << hook)))
596 			continue;
597 
598 		/* Set initial back pointer. */
599 		e->counters.pcnt = pos;
600 
601 		for (;;) {
602 			struct ip6t_standard_target *t
603 				= (void *)ip6t_get_target(e);
604 
605 			if (e->comefrom & (1 << NF_IP6_NUMHOOKS)) {
606 				printk("iptables: loop hook %u pos %u %08X.\n",
607 				       hook, pos, e->comefrom);
608 				return 0;
609 			}
610 			e->comefrom
611 				|= ((1 << hook) | (1 << NF_IP6_NUMHOOKS));
612 
613 			/* Unconditional return/END. */
614 			if (e->target_offset == sizeof(struct ip6t_entry)
615 			    && (strcmp(t->target.u.user.name,
616 				       IP6T_STANDARD_TARGET) == 0)
617 			    && t->verdict < 0
618 			    && unconditional(&e->ipv6)) {
619 				unsigned int oldpos, size;
620 
621 				/* Return: backtrack through the last
622 				   big jump. */
623 				do {
624 					e->comefrom ^= (1<<NF_IP6_NUMHOOKS);
625 #ifdef DEBUG_IP_FIREWALL_USER
626 					if (e->comefrom
627 					    & (1 << NF_IP6_NUMHOOKS)) {
628 						duprintf("Back unset "
629 							 "on hook %u "
630 							 "rule %u\n",
631 							 hook, pos);
632 					}
633 #endif
634 					oldpos = pos;
635 					pos = e->counters.pcnt;
636 					e->counters.pcnt = 0;
637 
638 					/* We're at the start. */
639 					if (pos == oldpos)
640 						goto next;
641 
642 					e = (struct ip6t_entry *)
643 						(newinfo->entries + pos);
644 				} while (oldpos == pos + e->next_offset);
645 
646 				/* Move along one */
647 				size = e->next_offset;
648 				e = (struct ip6t_entry *)
649 					(newinfo->entries + pos + size);
650 				e->counters.pcnt = pos;
651 				pos += size;
652 			} else {
653 				int newpos = t->verdict;
654 
655 				if (strcmp(t->target.u.user.name,
656 					   IP6T_STANDARD_TARGET) == 0
657 				    && newpos >= 0) {
658 					/* This a jump; chase it. */
659 					duprintf("Jump rule %u -> %u\n",
660 						 pos, newpos);
661 				} else {
662 					/* ... this is a fallthru */
663 					newpos = pos + e->next_offset;
664 				}
665 				e = (struct ip6t_entry *)
666 					(newinfo->entries + newpos);
667 				e->counters.pcnt = pos;
668 				pos = newpos;
669 			}
670 		}
671 		next:
672 		duprintf("Finished chain %u\n", hook);
673 	}
674 	return 1;
675 }
676 
677 static inline int
cleanup_match(struct ip6t_entry_match * m,unsigned int * i)678 cleanup_match(struct ip6t_entry_match *m, unsigned int *i)
679 {
680 	if (i && (*i)-- == 0)
681 		return 1;
682 
683 	if (m->u.kernel.match->destroy)
684 		m->u.kernel.match->destroy(m->data,
685 					   m->u.match_size - sizeof(*m));
686 
687 	if (m->u.kernel.match->me)
688 		__MOD_DEC_USE_COUNT(m->u.kernel.match->me);
689 
690 	return 0;
691 }
692 
693 static inline int
standard_check(const struct ip6t_entry_target * t,unsigned int max_offset)694 standard_check(const struct ip6t_entry_target *t,
695 	       unsigned int max_offset)
696 {
697 	struct ip6t_standard_target *targ = (void *)t;
698 
699 	/* Check standard info. */
700 	if (t->u.target_size
701 	    != IP6T_ALIGN(sizeof(struct ip6t_standard_target))) {
702 		duprintf("standard_check: target size %u != %u\n",
703 			 t->u.target_size,
704 			 IP6T_ALIGN(sizeof(struct ip6t_standard_target)));
705 		return 0;
706 	}
707 
708 	if (targ->verdict >= 0
709 	    && targ->verdict > max_offset - sizeof(struct ip6t_entry)) {
710 		duprintf("ip6t_standard_check: bad verdict (%i)\n",
711 			 targ->verdict);
712 		return 0;
713 	}
714 
715 	if (targ->verdict < -NF_MAX_VERDICT - 1) {
716 		duprintf("ip6t_standard_check: bad negative verdict (%i)\n",
717 			 targ->verdict);
718 		return 0;
719 	}
720 	return 1;
721 }
722 
723 static inline int
check_match(struct ip6t_entry_match * m,const char * name,const struct ip6t_ip6 * ipv6,unsigned int hookmask,unsigned int * i)724 check_match(struct ip6t_entry_match *m,
725 	    const char *name,
726 	    const struct ip6t_ip6 *ipv6,
727 	    unsigned int hookmask,
728 	    unsigned int *i)
729 {
730 	int ret;
731 	struct ip6t_match *match;
732 
733 	match = find_match_lock(m->u.user.name, &ret, &ip6t_mutex);
734 	if (!match) {
735 	  //		duprintf("check_match: `%s' not found\n", m->u.name);
736 		return ret;
737 	}
738 	if (match->me)
739 		__MOD_INC_USE_COUNT(match->me);
740 	m->u.kernel.match = match;
741 	up(&ip6t_mutex);
742 
743 	if (m->u.kernel.match->checkentry
744 	    && !m->u.kernel.match->checkentry(name, ipv6, m->data,
745 					      m->u.match_size - sizeof(*m),
746 					      hookmask)) {
747 		if (m->u.kernel.match->me)
748 			__MOD_DEC_USE_COUNT(m->u.kernel.match->me);
749 		duprintf("ip_tables: check failed for `%s'.\n",
750 			 m->u.kernel.match->name);
751 		return -EINVAL;
752 	}
753 
754 	(*i)++;
755 	return 0;
756 }
757 
758 static struct ip6t_target ip6t_standard_target;
759 
760 static inline int
check_entry(struct ip6t_entry * e,const char * name,unsigned int size,unsigned int * i)761 check_entry(struct ip6t_entry *e, const char *name, unsigned int size,
762 	    unsigned int *i)
763 {
764 	struct ip6t_entry_target *t;
765 	struct ip6t_target *target;
766 	int ret;
767 	unsigned int j;
768 
769 	if (!ip6_checkentry(&e->ipv6)) {
770 		duprintf("ip_tables: ip check failed %p %s.\n", e, name);
771 		return -EINVAL;
772 	}
773 
774 	j = 0;
775 	ret = IP6T_MATCH_ITERATE(e, check_match, name, &e->ipv6, e->comefrom, &j);
776 	if (ret != 0)
777 		goto cleanup_matches;
778 
779 	t = ip6t_get_target(e);
780 	target = ip6t_find_target_lock(t->u.user.name, &ret, &ip6t_mutex);
781 	if (!target) {
782 		duprintf("check_entry: `%s' not found\n", t->u.user.name);
783 		goto cleanup_matches;
784 	}
785 	if (target->me)
786 		__MOD_INC_USE_COUNT(target->me);
787 	t->u.kernel.target = target;
788 	up(&ip6t_mutex);
789 
790 	if (t->u.kernel.target == &ip6t_standard_target) {
791 		if (!standard_check(t, size)) {
792 			ret = -EINVAL;
793 			goto cleanup_matches;
794 		}
795 	} else if (t->u.kernel.target->checkentry
796 		   && !t->u.kernel.target->checkentry(name, e, t->data,
797 						      t->u.target_size
798 						      - sizeof(*t),
799 						      e->comefrom)) {
800 		if (t->u.kernel.target->me)
801 			__MOD_DEC_USE_COUNT(t->u.kernel.target->me);
802 		duprintf("ip_tables: check failed for `%s'.\n",
803 			 t->u.kernel.target->name);
804 		ret = -EINVAL;
805 		goto cleanup_matches;
806 	}
807 
808 	(*i)++;
809 	return 0;
810 
811  cleanup_matches:
812 	IP6T_MATCH_ITERATE(e, cleanup_match, &j);
813 	return ret;
814 }
815 
816 static inline int
check_entry_size_and_hooks(struct ip6t_entry * e,struct ip6t_table_info * newinfo,unsigned char * base,unsigned char * limit,const unsigned int * hook_entries,const unsigned int * underflows,unsigned int * i)817 check_entry_size_and_hooks(struct ip6t_entry *e,
818 			   struct ip6t_table_info *newinfo,
819 			   unsigned char *base,
820 			   unsigned char *limit,
821 			   const unsigned int *hook_entries,
822 			   const unsigned int *underflows,
823 			   unsigned int *i)
824 {
825 	unsigned int h;
826 
827 	if ((unsigned long)e % __alignof__(struct ip6t_entry) != 0
828 	    || (unsigned char *)e + sizeof(struct ip6t_entry) >= limit) {
829 		duprintf("Bad offset %p\n", e);
830 		return -EINVAL;
831 	}
832 
833 	if (e->next_offset
834 	    < sizeof(struct ip6t_entry) + sizeof(struct ip6t_entry_target)) {
835 		duprintf("checking: element %p size %u\n",
836 			 e, e->next_offset);
837 		return -EINVAL;
838 	}
839 
840 	/* Check hooks & underflows */
841 	for (h = 0; h < NF_IP6_NUMHOOKS; h++) {
842 		if ((unsigned char *)e - base == hook_entries[h])
843 			newinfo->hook_entry[h] = hook_entries[h];
844 		if ((unsigned char *)e - base == underflows[h])
845 			newinfo->underflow[h] = underflows[h];
846 	}
847 
848 	/* FIXME: underflows must be unconditional, standard verdicts
849            < 0 (not IP6T_RETURN). --RR */
850 
851 	/* Clear counters and comefrom */
852 	e->counters = ((struct ip6t_counters) { 0, 0 });
853 	e->comefrom = 0;
854 
855 	(*i)++;
856 	return 0;
857 }
858 
859 static inline int
cleanup_entry(struct ip6t_entry * e,unsigned int * i)860 cleanup_entry(struct ip6t_entry *e, unsigned int *i)
861 {
862 	struct ip6t_entry_target *t;
863 
864 	if (i && (*i)-- == 0)
865 		return 1;
866 
867 	/* Cleanup all matches */
868 	IP6T_MATCH_ITERATE(e, cleanup_match, NULL);
869 	t = ip6t_get_target(e);
870 	if (t->u.kernel.target->destroy)
871 		t->u.kernel.target->destroy(t->data,
872 					    t->u.target_size - sizeof(*t));
873 	if (t->u.kernel.target->me)
874 		__MOD_DEC_USE_COUNT(t->u.kernel.target->me);
875 
876 	return 0;
877 }
878 
879 /* Checks and translates the user-supplied table segment (held in
880    newinfo) */
881 static int
translate_table(const char * name,unsigned int valid_hooks,struct ip6t_table_info * newinfo,unsigned int size,unsigned int number,const unsigned int * hook_entries,const unsigned int * underflows)882 translate_table(const char *name,
883 		unsigned int valid_hooks,
884 		struct ip6t_table_info *newinfo,
885 		unsigned int size,
886 		unsigned int number,
887 		const unsigned int *hook_entries,
888 		const unsigned int *underflows)
889 {
890 	unsigned int i;
891 	int ret;
892 
893 	newinfo->size = size;
894 	newinfo->number = number;
895 
896 	/* Init all hooks to impossible value. */
897 	for (i = 0; i < NF_IP6_NUMHOOKS; i++) {
898 		newinfo->hook_entry[i] = 0xFFFFFFFF;
899 		newinfo->underflow[i] = 0xFFFFFFFF;
900 	}
901 
902 	duprintf("translate_table: size %u\n", newinfo->size);
903 	i = 0;
904 	/* Walk through entries, checking offsets. */
905 	ret = IP6T_ENTRY_ITERATE(newinfo->entries, newinfo->size,
906 				check_entry_size_and_hooks,
907 				newinfo,
908 				newinfo->entries,
909 				newinfo->entries + size,
910 				hook_entries, underflows, &i);
911 	if (ret != 0)
912 		return ret;
913 
914 	if (i != number) {
915 		duprintf("translate_table: %u not %u entries\n",
916 			 i, number);
917 		return -EINVAL;
918 	}
919 
920 	/* Check hooks all assigned */
921 	for (i = 0; i < NF_IP6_NUMHOOKS; i++) {
922 		/* Only hooks which are valid */
923 		if (!(valid_hooks & (1 << i)))
924 			continue;
925 		if (newinfo->hook_entry[i] == 0xFFFFFFFF) {
926 			duprintf("Invalid hook entry %u %u\n",
927 				 i, hook_entries[i]);
928 			return -EINVAL;
929 		}
930 		if (newinfo->underflow[i] == 0xFFFFFFFF) {
931 			duprintf("Invalid underflow %u %u\n",
932 				 i, underflows[i]);
933 			return -EINVAL;
934 		}
935 	}
936 
937 	if (!mark_source_chains(newinfo, valid_hooks))
938 		return -ELOOP;
939 
940 	/* Finally, each sanity check must pass */
941 	i = 0;
942 	ret = IP6T_ENTRY_ITERATE(newinfo->entries, newinfo->size,
943 				check_entry, name, size, &i);
944 
945 	if (ret != 0) {
946 		IP6T_ENTRY_ITERATE(newinfo->entries, newinfo->size,
947 				  cleanup_entry, &i);
948 		return ret;
949 	}
950 
951 	/* And one copy for every other CPU */
952 	for (i = 1; i < smp_num_cpus; i++) {
953 		memcpy(newinfo->entries + SMP_ALIGN(newinfo->size)*i,
954 		       newinfo->entries,
955 		       SMP_ALIGN(newinfo->size));
956 	}
957 
958 	return ret;
959 }
960 
961 static struct ip6t_table_info *
replace_table(struct ip6t_table * table,unsigned int num_counters,struct ip6t_table_info * newinfo,int * error)962 replace_table(struct ip6t_table *table,
963 	      unsigned int num_counters,
964 	      struct ip6t_table_info *newinfo,
965 	      int *error)
966 {
967 	struct ip6t_table_info *oldinfo;
968 
969 #ifdef CONFIG_NETFILTER_DEBUG
970 	{
971 		struct ip6t_entry *table_base;
972 		unsigned int i;
973 
974 		for (i = 0; i < smp_num_cpus; i++) {
975 			table_base =
976 				(void *)newinfo->entries
977 				+ TABLE_OFFSET(newinfo, i);
978 
979 			table_base->comefrom = 0xdead57ac;
980 		}
981 	}
982 #endif
983 
984 	/* Do the substitution. */
985 	write_lock_bh(&table->lock);
986 	/* Check inside lock: is the old number correct? */
987 	if (num_counters != table->private->number) {
988 		duprintf("num_counters != table->private->number (%u/%u)\n",
989 			 num_counters, table->private->number);
990 		write_unlock_bh(&table->lock);
991 		*error = -EAGAIN;
992 		return NULL;
993 	}
994 	oldinfo = table->private;
995 	table->private = newinfo;
996 	newinfo->initial_entries = oldinfo->initial_entries;
997 	write_unlock_bh(&table->lock);
998 
999 	return oldinfo;
1000 }
1001 
1002 /* Gets counters. */
1003 static inline int
add_entry_to_counter(const struct ip6t_entry * e,struct ip6t_counters total[],unsigned int * i)1004 add_entry_to_counter(const struct ip6t_entry *e,
1005 		     struct ip6t_counters total[],
1006 		     unsigned int *i)
1007 {
1008 	ADD_COUNTER(total[*i], e->counters.bcnt, e->counters.pcnt);
1009 
1010 	(*i)++;
1011 	return 0;
1012 }
1013 
1014 static void
get_counters(const struct ip6t_table_info * t,struct ip6t_counters counters[])1015 get_counters(const struct ip6t_table_info *t,
1016 	     struct ip6t_counters counters[])
1017 {
1018 	unsigned int cpu;
1019 	unsigned int i;
1020 
1021 	for (cpu = 0; cpu < smp_num_cpus; cpu++) {
1022 		i = 0;
1023 		IP6T_ENTRY_ITERATE(t->entries + TABLE_OFFSET(t, cpu),
1024 				  t->size,
1025 				  add_entry_to_counter,
1026 				  counters,
1027 				  &i);
1028 	}
1029 }
1030 
1031 static int
copy_entries_to_user(unsigned int total_size,struct ip6t_table * table,void * userptr)1032 copy_entries_to_user(unsigned int total_size,
1033 		     struct ip6t_table *table,
1034 		     void *userptr)
1035 {
1036 	unsigned int off, num, countersize;
1037 	struct ip6t_entry *e;
1038 	struct ip6t_counters *counters;
1039 	int ret = 0;
1040 
1041 	/* We need atomic snapshot of counters: rest doesn't change
1042 	   (other than comefrom, which userspace doesn't care
1043 	   about). */
1044 	countersize = sizeof(struct ip6t_counters) * table->private->number;
1045 	counters = vmalloc(countersize);
1046 
1047 	if (counters == NULL)
1048 		return -ENOMEM;
1049 
1050 	/* First, sum counters... */
1051 	memset(counters, 0, countersize);
1052 	write_lock_bh(&table->lock);
1053 	get_counters(table->private, counters);
1054 	write_unlock_bh(&table->lock);
1055 
1056 	/* ... then copy entire thing from CPU 0... */
1057 	if (copy_to_user(userptr, table->private->entries, total_size) != 0) {
1058 		ret = -EFAULT;
1059 		goto free_counters;
1060 	}
1061 
1062 	/* FIXME: use iterator macros --RR */
1063 	/* ... then go back and fix counters and names */
1064 	for (off = 0, num = 0; off < total_size; off += e->next_offset, num++){
1065 		unsigned int i;
1066 		struct ip6t_entry_match *m;
1067 		struct ip6t_entry_target *t;
1068 
1069 		e = (struct ip6t_entry *)(table->private->entries + off);
1070 		if (copy_to_user(userptr + off
1071 				 + offsetof(struct ip6t_entry, counters),
1072 				 &counters[num],
1073 				 sizeof(counters[num])) != 0) {
1074 			ret = -EFAULT;
1075 			goto free_counters;
1076 		}
1077 
1078 		for (i = sizeof(struct ip6t_entry);
1079 		     i < e->target_offset;
1080 		     i += m->u.match_size) {
1081 			m = (void *)e + i;
1082 
1083 			if (copy_to_user(userptr + off + i
1084 					 + offsetof(struct ip6t_entry_match,
1085 						    u.user.name),
1086 					 m->u.kernel.match->name,
1087 					 strlen(m->u.kernel.match->name)+1)
1088 			    != 0) {
1089 				ret = -EFAULT;
1090 				goto free_counters;
1091 			}
1092 		}
1093 
1094 		t = ip6t_get_target(e);
1095 		if (copy_to_user(userptr + off + e->target_offset
1096 				 + offsetof(struct ip6t_entry_target,
1097 					    u.user.name),
1098 				 t->u.kernel.target->name,
1099 				 strlen(t->u.kernel.target->name)+1) != 0) {
1100 			ret = -EFAULT;
1101 			goto free_counters;
1102 		}
1103 	}
1104 
1105  free_counters:
1106 	vfree(counters);
1107 	return ret;
1108 }
1109 
1110 static int
get_entries(const struct ip6t_get_entries * entries,struct ip6t_get_entries * uptr)1111 get_entries(const struct ip6t_get_entries *entries,
1112 	    struct ip6t_get_entries *uptr)
1113 {
1114 	int ret;
1115 	struct ip6t_table *t;
1116 
1117 	t = ip6t_find_table_lock(entries->name, &ret, &ip6t_mutex);
1118 	if (t) {
1119 		duprintf("t->private->number = %u\n",
1120 			 t->private->number);
1121 		if (entries->size == t->private->size)
1122 			ret = copy_entries_to_user(t->private->size,
1123 						   t, uptr->entrytable);
1124 		else {
1125 			duprintf("get_entries: I've got %u not %u!\n",
1126 				 t->private->size,
1127 				 entries->size);
1128 			ret = -EINVAL;
1129 		}
1130 		up(&ip6t_mutex);
1131 	} else
1132 		duprintf("get_entries: Can't find %s!\n",
1133 			 entries->name);
1134 
1135 	return ret;
1136 }
1137 
1138 static int
do_replace(void * user,unsigned int len)1139 do_replace(void *user, unsigned int len)
1140 {
1141 	int ret;
1142 	struct ip6t_replace tmp;
1143 	struct ip6t_table *t;
1144 	struct ip6t_table_info *newinfo, *oldinfo;
1145 	struct ip6t_counters *counters;
1146 
1147 	if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1148 		return -EFAULT;
1149 
1150 	/* Pedantry: prevent them from hitting BUG() in vmalloc.c --RR */
1151 	if ((SMP_ALIGN(tmp.size) >> PAGE_SHIFT) + 2 > num_physpages)
1152 		return -ENOMEM;
1153 
1154 	/* overflow check */
1155 	if (tmp.size >= (INT_MAX - sizeof(struct ip6t_table_info)) / NR_CPUS -
1156 			SMP_CACHE_BYTES)
1157 		return -ENOMEM;
1158 	if (tmp.num_counters >= INT_MAX / sizeof(struct ip6t_counters))
1159 		return -ENOMEM;
1160 
1161 	newinfo = vmalloc(sizeof(struct ip6t_table_info)
1162 			  + SMP_ALIGN(tmp.size) * smp_num_cpus);
1163 	if (!newinfo)
1164 		return -ENOMEM;
1165 
1166 	if (copy_from_user(newinfo->entries, user + sizeof(tmp),
1167 			   tmp.size) != 0) {
1168 		ret = -EFAULT;
1169 		goto free_newinfo;
1170 	}
1171 
1172 	counters = vmalloc(tmp.num_counters * sizeof(struct ip6t_counters));
1173 	if (!counters) {
1174 		ret = -ENOMEM;
1175 		goto free_newinfo;
1176 	}
1177 	memset(counters, 0, tmp.num_counters * sizeof(struct ip6t_counters));
1178 
1179 	ret = translate_table(tmp.name, tmp.valid_hooks,
1180 			      newinfo, tmp.size, tmp.num_entries,
1181 			      tmp.hook_entry, tmp.underflow);
1182 	if (ret != 0)
1183 		goto free_newinfo_counters;
1184 
1185 	duprintf("ip_tables: Translated table\n");
1186 
1187 	t = ip6t_find_table_lock(tmp.name, &ret, &ip6t_mutex);
1188 	if (!t)
1189 		goto free_newinfo_counters_untrans;
1190 
1191 	/* You lied! */
1192 	if (tmp.valid_hooks != t->valid_hooks) {
1193 		duprintf("Valid hook crap: %08X vs %08X\n",
1194 			 tmp.valid_hooks, t->valid_hooks);
1195 		ret = -EINVAL;
1196 		goto free_newinfo_counters_untrans_unlock;
1197 	}
1198 
1199 	oldinfo = replace_table(t, tmp.num_counters, newinfo, &ret);
1200 	if (!oldinfo)
1201 		goto free_newinfo_counters_untrans_unlock;
1202 
1203 	/* Update module usage count based on number of rules */
1204 	duprintf("do_replace: oldnum=%u, initnum=%u, newnum=%u\n",
1205 		oldinfo->number, oldinfo->initial_entries, newinfo->number);
1206 	if (t->me && (oldinfo->number <= oldinfo->initial_entries) &&
1207  	    (newinfo->number > oldinfo->initial_entries))
1208 		__MOD_INC_USE_COUNT(t->me);
1209 	else if (t->me && (oldinfo->number > oldinfo->initial_entries) &&
1210 	 	 (newinfo->number <= oldinfo->initial_entries))
1211 		__MOD_DEC_USE_COUNT(t->me);
1212 
1213 	/* Get the old counters. */
1214 	get_counters(oldinfo, counters);
1215 	/* Decrease module usage counts and free resource */
1216 	IP6T_ENTRY_ITERATE(oldinfo->entries, oldinfo->size, cleanup_entry,NULL);
1217 	vfree(oldinfo);
1218 	/* Silent error: too late now. */
1219 	copy_to_user(tmp.counters, counters,
1220 		     sizeof(struct ip6t_counters) * tmp.num_counters);
1221 	vfree(counters);
1222 	up(&ip6t_mutex);
1223 	return 0;
1224 
1225  free_newinfo_counters_untrans_unlock:
1226 	up(&ip6t_mutex);
1227  free_newinfo_counters_untrans:
1228 	IP6T_ENTRY_ITERATE(newinfo->entries, newinfo->size, cleanup_entry,NULL);
1229  free_newinfo_counters:
1230 	vfree(counters);
1231  free_newinfo:
1232 	vfree(newinfo);
1233 	return ret;
1234 }
1235 
1236 /* We're lazy, and add to the first CPU; overflow works its fey magic
1237  * and everything is OK. */
1238 static inline int
add_counter_to_entry(struct ip6t_entry * e,const struct ip6t_counters addme[],unsigned int * i)1239 add_counter_to_entry(struct ip6t_entry *e,
1240 		     const struct ip6t_counters addme[],
1241 		     unsigned int *i)
1242 {
1243 #if 0
1244 	duprintf("add_counter: Entry %u %lu/%lu + %lu/%lu\n",
1245 		 *i,
1246 		 (long unsigned int)e->counters.pcnt,
1247 		 (long unsigned int)e->counters.bcnt,
1248 		 (long unsigned int)addme[*i].pcnt,
1249 		 (long unsigned int)addme[*i].bcnt);
1250 #endif
1251 
1252 	ADD_COUNTER(e->counters, addme[*i].bcnt, addme[*i].pcnt);
1253 
1254 	(*i)++;
1255 	return 0;
1256 }
1257 
1258 static int
do_add_counters(void * user,unsigned int len)1259 do_add_counters(void *user, unsigned int len)
1260 {
1261 	unsigned int i;
1262 	struct ip6t_counters_info tmp, *paddc;
1263 	struct ip6t_table *t;
1264 	int ret;
1265 
1266 	if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1267 		return -EFAULT;
1268 
1269 	if (len != sizeof(tmp) + tmp.num_counters*sizeof(struct ip6t_counters))
1270 		return -EINVAL;
1271 
1272 	paddc = vmalloc(len);
1273 	if (!paddc)
1274 		return -ENOMEM;
1275 
1276 	if (copy_from_user(paddc, user, len) != 0) {
1277 		ret = -EFAULT;
1278 		goto free;
1279 	}
1280 
1281 	t = ip6t_find_table_lock(tmp.name, &ret, &ip6t_mutex);
1282 	if (!t)
1283 		goto free;
1284 
1285 	write_lock_bh(&t->lock);
1286 	if (t->private->number != tmp.num_counters) {
1287 		ret = -EINVAL;
1288 		goto unlock_up_free;
1289 	}
1290 
1291 	i = 0;
1292 	IP6T_ENTRY_ITERATE(t->private->entries,
1293 			  t->private->size,
1294 			  add_counter_to_entry,
1295 			  paddc->counters,
1296 			  &i);
1297  unlock_up_free:
1298 	write_unlock_bh(&t->lock);
1299 	up(&ip6t_mutex);
1300  free:
1301 	vfree(paddc);
1302 
1303 	return ret;
1304 }
1305 
1306 static int
do_ip6t_set_ctl(struct sock * sk,int cmd,void * user,unsigned int len)1307 do_ip6t_set_ctl(struct sock *sk,	int cmd, void *user, unsigned int len)
1308 {
1309 	int ret;
1310 
1311 	if (!capable(CAP_NET_ADMIN))
1312 		return -EPERM;
1313 
1314 	switch (cmd) {
1315 	case IP6T_SO_SET_REPLACE:
1316 		ret = do_replace(user, len);
1317 		break;
1318 
1319 	case IP6T_SO_SET_ADD_COUNTERS:
1320 		ret = do_add_counters(user, len);
1321 		break;
1322 
1323 	default:
1324 		duprintf("do_ip6t_set_ctl:  unknown request %i\n", cmd);
1325 		ret = -EINVAL;
1326 	}
1327 
1328 	return ret;
1329 }
1330 
1331 static int
do_ip6t_get_ctl(struct sock * sk,int cmd,void * user,int * len)1332 do_ip6t_get_ctl(struct sock *sk, int cmd, void *user, int *len)
1333 {
1334 	int ret;
1335 
1336 	if (!capable(CAP_NET_ADMIN))
1337 		return -EPERM;
1338 
1339 	switch (cmd) {
1340 	case IP6T_SO_GET_INFO: {
1341 		char name[IP6T_TABLE_MAXNAMELEN];
1342 		struct ip6t_table *t;
1343 
1344 		if (*len != sizeof(struct ip6t_getinfo)) {
1345 			duprintf("length %u != %u\n", *len,
1346 				 sizeof(struct ip6t_getinfo));
1347 			ret = -EINVAL;
1348 			break;
1349 		}
1350 
1351 		if (copy_from_user(name, user, sizeof(name)) != 0) {
1352 			ret = -EFAULT;
1353 			break;
1354 		}
1355 		name[IP6T_TABLE_MAXNAMELEN-1] = '\0';
1356 		t = ip6t_find_table_lock(name, &ret, &ip6t_mutex);
1357 		if (t) {
1358 			struct ip6t_getinfo info;
1359 
1360 			info.valid_hooks = t->valid_hooks;
1361 			memcpy(info.hook_entry, t->private->hook_entry,
1362 			       sizeof(info.hook_entry));
1363 			memcpy(info.underflow, t->private->underflow,
1364 			       sizeof(info.underflow));
1365 			info.num_entries = t->private->number;
1366 			info.size = t->private->size;
1367 			memcpy(info.name, name, sizeof(info.name));
1368 
1369 			if (copy_to_user(user, &info, *len) != 0)
1370 				ret = -EFAULT;
1371 			else
1372 				ret = 0;
1373 
1374 			up(&ip6t_mutex);
1375 		}
1376 	}
1377 	break;
1378 
1379 	case IP6T_SO_GET_ENTRIES: {
1380 		struct ip6t_get_entries get;
1381 
1382 		if (*len < sizeof(get)) {
1383 			duprintf("get_entries: %u < %u\n", *len, sizeof(get));
1384 			ret = -EINVAL;
1385 		} else if (copy_from_user(&get, user, sizeof(get)) != 0) {
1386 			ret = -EFAULT;
1387 		} else if (*len != sizeof(struct ip6t_get_entries) + get.size) {
1388 			duprintf("get_entries: %u != %u\n", *len,
1389 				 sizeof(struct ip6t_get_entries) + get.size);
1390 			ret = -EINVAL;
1391 		} else
1392 			ret = get_entries(&get, user);
1393 		break;
1394 	}
1395 
1396 	default:
1397 		duprintf("do_ip6t_get_ctl: unknown request %i\n", cmd);
1398 		ret = -EINVAL;
1399 	}
1400 
1401 	return ret;
1402 }
1403 
1404 /* Registration hooks for targets. */
1405 int
ip6t_register_target(struct ip6t_target * target)1406 ip6t_register_target(struct ip6t_target *target)
1407 {
1408 	int ret;
1409 
1410 	MOD_INC_USE_COUNT;
1411 	ret = down_interruptible(&ip6t_mutex);
1412 	if (ret != 0) {
1413 		MOD_DEC_USE_COUNT;
1414 		return ret;
1415 	}
1416 	if (!list_named_insert(&ip6t_target, target)) {
1417 		duprintf("ip6t_register_target: `%s' already in list!\n",
1418 			 target->name);
1419 		ret = -EINVAL;
1420 		MOD_DEC_USE_COUNT;
1421 	}
1422 	up(&ip6t_mutex);
1423 	return ret;
1424 }
1425 
1426 void
ip6t_unregister_target(struct ip6t_target * target)1427 ip6t_unregister_target(struct ip6t_target *target)
1428 {
1429 	down(&ip6t_mutex);
1430 	LIST_DELETE(&ip6t_target, target);
1431 	up(&ip6t_mutex);
1432 	MOD_DEC_USE_COUNT;
1433 }
1434 
1435 int
ip6t_register_match(struct ip6t_match * match)1436 ip6t_register_match(struct ip6t_match *match)
1437 {
1438 	int ret;
1439 
1440 	MOD_INC_USE_COUNT;
1441 	ret = down_interruptible(&ip6t_mutex);
1442 	if (ret != 0) {
1443 		MOD_DEC_USE_COUNT;
1444 		return ret;
1445 	}
1446 	if (!list_named_insert(&ip6t_match, match)) {
1447 		duprintf("ip6t_register_match: `%s' already in list!\n",
1448 			 match->name);
1449 		MOD_DEC_USE_COUNT;
1450 		ret = -EINVAL;
1451 	}
1452 	up(&ip6t_mutex);
1453 
1454 	return ret;
1455 }
1456 
1457 void
ip6t_unregister_match(struct ip6t_match * match)1458 ip6t_unregister_match(struct ip6t_match *match)
1459 {
1460 	down(&ip6t_mutex);
1461 	LIST_DELETE(&ip6t_match, match);
1462 	up(&ip6t_mutex);
1463 	MOD_DEC_USE_COUNT;
1464 }
1465 
ip6t_register_table(struct ip6t_table * table)1466 int ip6t_register_table(struct ip6t_table *table)
1467 {
1468 	int ret;
1469 	struct ip6t_table_info *newinfo;
1470 	static struct ip6t_table_info bootstrap
1471 		= { 0, 0, 0, { 0 }, { 0 }, { } };
1472 
1473 	MOD_INC_USE_COUNT;
1474 	newinfo = vmalloc(sizeof(struct ip6t_table_info)
1475 			  + SMP_ALIGN(table->table->size) * smp_num_cpus);
1476 	if (!newinfo) {
1477 		ret = -ENOMEM;
1478 		MOD_DEC_USE_COUNT;
1479 		return ret;
1480 	}
1481 	memcpy(newinfo->entries, table->table->entries, table->table->size);
1482 
1483 	ret = translate_table(table->name, table->valid_hooks,
1484 			      newinfo, table->table->size,
1485 			      table->table->num_entries,
1486 			      table->table->hook_entry,
1487 			      table->table->underflow);
1488 	if (ret != 0) {
1489 		vfree(newinfo);
1490 		MOD_DEC_USE_COUNT;
1491 		return ret;
1492 	}
1493 
1494 	ret = down_interruptible(&ip6t_mutex);
1495 	if (ret != 0) {
1496 		vfree(newinfo);
1497 		MOD_DEC_USE_COUNT;
1498 		return ret;
1499 	}
1500 
1501 	/* Don't autoload: we'd eat our tail... */
1502 	if (list_named_find(&ip6t_tables, table->name)) {
1503 		ret = -EEXIST;
1504 		goto free_unlock;
1505 	}
1506 
1507 	/* Simplifies replace_table code. */
1508 	table->private = &bootstrap;
1509 	if (!replace_table(table, 0, newinfo, &ret))
1510 		goto free_unlock;
1511 
1512 	duprintf("table->private->number = %u\n",
1513 		 table->private->number);
1514 
1515 	/* save number of initial entries */
1516 	table->private->initial_entries = table->private->number;
1517 
1518 	table->lock = RW_LOCK_UNLOCKED;
1519 	list_prepend(&ip6t_tables, table);
1520 
1521  unlock:
1522 	up(&ip6t_mutex);
1523 	return ret;
1524 
1525  free_unlock:
1526 	vfree(newinfo);
1527 	MOD_DEC_USE_COUNT;
1528 	goto unlock;
1529 }
1530 
ip6t_unregister_table(struct ip6t_table * table)1531 void ip6t_unregister_table(struct ip6t_table *table)
1532 {
1533 	down(&ip6t_mutex);
1534 	LIST_DELETE(&ip6t_tables, table);
1535 	up(&ip6t_mutex);
1536 
1537 	/* Decrease module usage counts and free resources */
1538 	IP6T_ENTRY_ITERATE(table->private->entries, table->private->size,
1539 			  cleanup_entry, NULL);
1540 	vfree(table->private);
1541 	MOD_DEC_USE_COUNT;
1542 }
1543 
1544 /* Returns 1 if the port is matched by the range, 0 otherwise */
1545 static inline int
port_match(u_int16_t min,u_int16_t max,u_int16_t port,int invert)1546 port_match(u_int16_t min, u_int16_t max, u_int16_t port, int invert)
1547 {
1548 	int ret;
1549 
1550 	ret = (port >= min && port <= max) ^ invert;
1551 	return ret;
1552 }
1553 
1554 static int
tcp_find_option(u_int8_t option,const struct tcphdr * tcp,u_int16_t datalen,int invert,int * hotdrop)1555 tcp_find_option(u_int8_t option,
1556 		const struct tcphdr *tcp,
1557 		u_int16_t datalen,
1558 		int invert,
1559 		int *hotdrop)
1560 {
1561 	unsigned int i = sizeof(struct tcphdr);
1562 	const u_int8_t *opt = (u_int8_t *)tcp;
1563 
1564 	duprintf("tcp_match: finding option\n");
1565 	/* If we don't have the whole header, drop packet. */
1566 	if (tcp->doff * 4 < sizeof(struct tcphdr) ||
1567 	    tcp->doff * 4 > datalen) {
1568 		*hotdrop = 1;
1569 		return 0;
1570 	}
1571 
1572 	while (i < tcp->doff * 4) {
1573 		if (opt[i] == option) return !invert;
1574 		if (opt[i] < 2) i++;
1575 		else i += opt[i+1]?:1;
1576 	}
1577 
1578 	return invert;
1579 }
1580 
1581 static int
tcp_match(const struct sk_buff * skb,const struct net_device * in,const struct net_device * out,const void * matchinfo,int offset,const void * hdr,u_int16_t datalen,int * hotdrop)1582 tcp_match(const struct sk_buff *skb,
1583 	  const struct net_device *in,
1584 	  const struct net_device *out,
1585 	  const void *matchinfo,
1586 	  int offset,
1587 	  const void *hdr,
1588 	  u_int16_t datalen,
1589 	  int *hotdrop)
1590 {
1591 	const struct tcphdr *tcp = hdr;
1592 	const struct ip6t_tcp *tcpinfo = matchinfo;
1593 
1594 	/* To quote Alan:
1595 
1596 	   Don't allow a fragment of TCP 8 bytes in. Nobody normal
1597 	   causes this. Its a cracker trying to break in by doing a
1598 	   flag overwrite to pass the direction checks.
1599 	*/
1600 
1601 	if (offset == 1) {
1602 		duprintf("Dropping evil TCP offset=1 frag.\n");
1603 		*hotdrop = 1;
1604 		return 0;
1605 	} else if (offset == 0 && datalen < sizeof(struct tcphdr)) {
1606 		/* We've been asked to examine this packet, and we
1607 		   can't.  Hence, no choice but to drop. */
1608 		duprintf("Dropping evil TCP offset=0 tinygram.\n");
1609 		*hotdrop = 1;
1610 		return 0;
1611 	}
1612 
1613 	/* FIXME: Try tcp doff >> packet len against various stacks --RR */
1614 
1615 #define FWINVTCP(bool,invflg) ((bool) ^ !!(tcpinfo->invflags & invflg))
1616 
1617 	/* Must not be a fragment. */
1618 	return !offset
1619 		&& port_match(tcpinfo->spts[0], tcpinfo->spts[1],
1620 			      ntohs(tcp->source),
1621 			      !!(tcpinfo->invflags & IP6T_TCP_INV_SRCPT))
1622 		&& port_match(tcpinfo->dpts[0], tcpinfo->dpts[1],
1623 			      ntohs(tcp->dest),
1624 			      !!(tcpinfo->invflags & IP6T_TCP_INV_DSTPT))
1625 		&& FWINVTCP((((unsigned char *)tcp)[13]
1626 			     & tcpinfo->flg_mask)
1627 			    == tcpinfo->flg_cmp,
1628 			    IP6T_TCP_INV_FLAGS)
1629 		&& (!tcpinfo->option
1630 		    || tcp_find_option(tcpinfo->option, tcp, datalen,
1631 				       tcpinfo->invflags
1632 				       & IP6T_TCP_INV_OPTION,
1633 				       hotdrop));
1634 }
1635 
1636 /* Called when user tries to insert an entry of this type. */
1637 static int
tcp_checkentry(const char * tablename,const struct ip6t_ip6 * ipv6,void * matchinfo,unsigned int matchsize,unsigned int hook_mask)1638 tcp_checkentry(const char *tablename,
1639 	       const struct ip6t_ip6 *ipv6,
1640 	       void *matchinfo,
1641 	       unsigned int matchsize,
1642 	       unsigned int hook_mask)
1643 {
1644 	const struct ip6t_tcp *tcpinfo = matchinfo;
1645 
1646 	/* Must specify proto == TCP, and no unknown invflags */
1647 	return ipv6->proto == IPPROTO_TCP
1648 		&& !(ipv6->invflags & IP6T_INV_PROTO)
1649 		&& matchsize == IP6T_ALIGN(sizeof(struct ip6t_tcp))
1650 		&& !(tcpinfo->invflags & ~IP6T_TCP_INV_MASK);
1651 }
1652 
1653 static int
udp_match(const struct sk_buff * skb,const struct net_device * in,const struct net_device * out,const void * matchinfo,int offset,const void * hdr,u_int16_t datalen,int * hotdrop)1654 udp_match(const struct sk_buff *skb,
1655 	  const struct net_device *in,
1656 	  const struct net_device *out,
1657 	  const void *matchinfo,
1658 	  int offset,
1659 	  const void *hdr,
1660 	  u_int16_t datalen,
1661 	  int *hotdrop)
1662 {
1663 	const struct udphdr *udp = hdr;
1664 	const struct ip6t_udp *udpinfo = matchinfo;
1665 
1666 	if (offset == 0 && datalen < sizeof(struct udphdr)) {
1667 		/* We've been asked to examine this packet, and we
1668 		   can't.  Hence, no choice but to drop. */
1669 		duprintf("Dropping evil UDP tinygram.\n");
1670 		*hotdrop = 1;
1671 		return 0;
1672 	}
1673 
1674 	/* Must not be a fragment. */
1675 	return !offset
1676 		&& port_match(udpinfo->spts[0], udpinfo->spts[1],
1677 			      ntohs(udp->source),
1678 			      !!(udpinfo->invflags & IP6T_UDP_INV_SRCPT))
1679 		&& port_match(udpinfo->dpts[0], udpinfo->dpts[1],
1680 			      ntohs(udp->dest),
1681 			      !!(udpinfo->invflags & IP6T_UDP_INV_DSTPT));
1682 }
1683 
1684 /* Called when user tries to insert an entry of this type. */
1685 static int
udp_checkentry(const char * tablename,const struct ip6t_ip6 * ipv6,void * matchinfo,unsigned int matchinfosize,unsigned int hook_mask)1686 udp_checkentry(const char *tablename,
1687 	       const struct ip6t_ip6 *ipv6,
1688 	       void *matchinfo,
1689 	       unsigned int matchinfosize,
1690 	       unsigned int hook_mask)
1691 {
1692 	const struct ip6t_udp *udpinfo = matchinfo;
1693 
1694 	/* Must specify proto == UDP, and no unknown invflags */
1695 	if (ipv6->proto != IPPROTO_UDP || (ipv6->invflags & IP6T_INV_PROTO)) {
1696 		duprintf("ip6t_udp: Protocol %u != %u\n", ipv6->proto,
1697 			 IPPROTO_UDP);
1698 		return 0;
1699 	}
1700 	if (matchinfosize != IP6T_ALIGN(sizeof(struct ip6t_udp))) {
1701 		duprintf("ip6t_udp: matchsize %u != %u\n",
1702 			 matchinfosize, IP6T_ALIGN(sizeof(struct ip6t_udp)));
1703 		return 0;
1704 	}
1705 	if (udpinfo->invflags & ~IP6T_UDP_INV_MASK) {
1706 		duprintf("ip6t_udp: unknown flags %X\n",
1707 			 udpinfo->invflags);
1708 		return 0;
1709 	}
1710 
1711 	return 1;
1712 }
1713 
1714 /* Returns 1 if the type and code is matched by the range, 0 otherwise */
1715 static inline int
icmp6_type_code_match(u_int8_t test_type,u_int8_t min_code,u_int8_t max_code,u_int8_t type,u_int8_t code,int invert)1716 icmp6_type_code_match(u_int8_t test_type, u_int8_t min_code, u_int8_t max_code,
1717 		     u_int8_t type, u_int8_t code,
1718 		     int invert)
1719 {
1720 	return (type == test_type && code >= min_code && code <= max_code)
1721 		^ invert;
1722 }
1723 
1724 static int
icmp6_match(const struct sk_buff * skb,const struct net_device * in,const struct net_device * out,const void * matchinfo,int offset,const void * hdr,u_int16_t datalen,int * hotdrop)1725 icmp6_match(const struct sk_buff *skb,
1726 	   const struct net_device *in,
1727 	   const struct net_device *out,
1728 	   const void *matchinfo,
1729 	   int offset,
1730 	   const void *hdr,
1731 	   u_int16_t datalen,
1732 	   int *hotdrop)
1733 {
1734 	const struct icmp6hdr *icmp = hdr;
1735 	const struct ip6t_icmp *icmpinfo = matchinfo;
1736 
1737 	if (offset == 0 && datalen < 2) {
1738 		/* We've been asked to examine this packet, and we
1739 		   can't.  Hence, no choice but to drop. */
1740 		duprintf("Dropping evil ICMP tinygram.\n");
1741 		*hotdrop = 1;
1742 		return 0;
1743 	}
1744 
1745 	/* Must not be a fragment. */
1746 	return !offset
1747 		&& icmp6_type_code_match(icmpinfo->type,
1748 					icmpinfo->code[0],
1749 					icmpinfo->code[1],
1750 					icmp->icmp6_type, icmp->icmp6_code,
1751 					!!(icmpinfo->invflags&IP6T_ICMP_INV));
1752 }
1753 
1754 /* Called when user tries to insert an entry of this type. */
1755 static int
icmp6_checkentry(const char * tablename,const struct ip6t_ip6 * ipv6,void * matchinfo,unsigned int matchsize,unsigned int hook_mask)1756 icmp6_checkentry(const char *tablename,
1757 	   const struct ip6t_ip6 *ipv6,
1758 	   void *matchinfo,
1759 	   unsigned int matchsize,
1760 	   unsigned int hook_mask)
1761 {
1762 	const struct ip6t_icmp *icmpinfo = matchinfo;
1763 
1764 	/* Must specify proto == ICMP, and no unknown invflags */
1765 	return ipv6->proto == IPPROTO_ICMPV6
1766 		&& !(ipv6->invflags & IP6T_INV_PROTO)
1767 		&& matchsize == IP6T_ALIGN(sizeof(struct ip6t_icmp))
1768 		&& !(icmpinfo->invflags & ~IP6T_ICMP_INV);
1769 }
1770 
1771 /* The built-in targets: standard (NULL) and error. */
1772 static struct ip6t_target ip6t_standard_target
1773 = { { NULL, NULL }, IP6T_STANDARD_TARGET, NULL, NULL, NULL };
1774 static struct ip6t_target ip6t_error_target
1775 = { { NULL, NULL }, IP6T_ERROR_TARGET, ip6t_error, NULL, NULL };
1776 
1777 static struct nf_sockopt_ops ip6t_sockopts
1778 = { { NULL, NULL }, PF_INET6, IP6T_BASE_CTL, IP6T_SO_SET_MAX+1, do_ip6t_set_ctl,
1779     IP6T_BASE_CTL, IP6T_SO_GET_MAX+1, do_ip6t_get_ctl, 0, NULL  };
1780 
1781 static struct ip6t_match tcp_matchstruct
1782 = { { NULL, NULL }, "tcp", &tcp_match, &tcp_checkentry, NULL };
1783 static struct ip6t_match udp_matchstruct
1784 = { { NULL, NULL }, "udp", &udp_match, &udp_checkentry, NULL };
1785 static struct ip6t_match icmp6_matchstruct
1786 = { { NULL, NULL }, "icmp6", &icmp6_match, &icmp6_checkentry, NULL };
1787 
1788 #ifdef CONFIG_PROC_FS
print_name(const char * i,off_t start_offset,char * buffer,int length,off_t * pos,unsigned int * count)1789 static inline int print_name(const char *i,
1790 			     off_t start_offset, char *buffer, int length,
1791 			     off_t *pos, unsigned int *count)
1792 {
1793 	if ((*count)++ >= start_offset) {
1794 		unsigned int namelen;
1795 
1796 		namelen = sprintf(buffer + *pos, "%s\n",
1797 				  i + sizeof(struct list_head));
1798 		if (*pos + namelen > length) {
1799 			/* Stop iterating */
1800 			return 1;
1801 		}
1802 		*pos += namelen;
1803 	}
1804 	return 0;
1805 }
1806 
print_target(const struct ip6t_target * t,off_t start_offset,char * buffer,int length,off_t * pos,unsigned int * count)1807 static inline int print_target(const struct ip6t_target *t,
1808                                off_t start_offset, char *buffer, int length,
1809                                off_t *pos, unsigned int *count)
1810 {
1811 	if (t == &ip6t_standard_target || t == &ip6t_error_target)
1812 		return 0;
1813 	return print_name((char *)t, start_offset, buffer, length, pos, count);
1814 }
1815 
ip6t_get_tables(char * buffer,char ** start,off_t offset,int length)1816 static int ip6t_get_tables(char *buffer, char **start, off_t offset, int length)
1817 {
1818 	off_t pos = 0;
1819 	unsigned int count = 0;
1820 
1821 	if (down_interruptible(&ip6t_mutex) != 0)
1822 		return 0;
1823 
1824 	LIST_FIND(&ip6t_tables, print_name, char *,
1825 		  offset, buffer, length, &pos, &count);
1826 
1827 	up(&ip6t_mutex);
1828 
1829 	/* `start' hack - see fs/proc/generic.c line ~105 */
1830 	*start=(char *)((unsigned long)count-offset);
1831 	return pos;
1832 }
1833 
ip6t_get_targets(char * buffer,char ** start,off_t offset,int length)1834 static int ip6t_get_targets(char *buffer, char **start, off_t offset, int length)
1835 {
1836 	off_t pos = 0;
1837 	unsigned int count = 0;
1838 
1839 	if (down_interruptible(&ip6t_mutex) != 0)
1840 		return 0;
1841 
1842 	LIST_FIND(&ip6t_target, print_target, struct ip6t_target *,
1843 		  offset, buffer, length, &pos, &count);
1844 
1845 	up(&ip6t_mutex);
1846 
1847 	*start = (char *)((unsigned long)count - offset);
1848 	return pos;
1849 }
1850 
ip6t_get_matches(char * buffer,char ** start,off_t offset,int length)1851 static int ip6t_get_matches(char *buffer, char **start, off_t offset, int length)
1852 {
1853 	off_t pos = 0;
1854 	unsigned int count = 0;
1855 
1856 	if (down_interruptible(&ip6t_mutex) != 0)
1857 		return 0;
1858 
1859 	LIST_FIND(&ip6t_match, print_name, char *,
1860 		  offset, buffer, length, &pos, &count);
1861 
1862 	up(&ip6t_mutex);
1863 
1864 	*start = (char *)((unsigned long)count - offset);
1865 	return pos;
1866 }
1867 
1868 static struct { char *name; get_info_t *get_info; } ip6t_proc_entry[] =
1869 { { "ip6_tables_names", ip6t_get_tables },
1870   { "ip6_tables_targets", ip6t_get_targets },
1871   { "ip6_tables_matches", ip6t_get_matches },
1872   { NULL, NULL} };
1873 #endif /*CONFIG_PROC_FS*/
1874 
init(void)1875 static int __init init(void)
1876 {
1877 	int ret;
1878 
1879 	/* Noone else will be downing sem now, so we won't sleep */
1880 	down(&ip6t_mutex);
1881 	list_append(&ip6t_target, &ip6t_standard_target);
1882 	list_append(&ip6t_target, &ip6t_error_target);
1883 	list_append(&ip6t_match, &tcp_matchstruct);
1884 	list_append(&ip6t_match, &udp_matchstruct);
1885 	list_append(&ip6t_match, &icmp6_matchstruct);
1886 	up(&ip6t_mutex);
1887 
1888 	/* Register setsockopt */
1889 	ret = nf_register_sockopt(&ip6t_sockopts);
1890 	if (ret < 0) {
1891 		duprintf("Unable to register sockopts.\n");
1892 		return ret;
1893 	}
1894 
1895 #ifdef CONFIG_PROC_FS
1896 	{
1897 		struct proc_dir_entry *proc;
1898 		int i;
1899 
1900 		for (i = 0; ip6t_proc_entry[i].name; i++) {
1901 			proc = proc_net_create(ip6t_proc_entry[i].name, 0,
1902 					       ip6t_proc_entry[i].get_info);
1903 			if (!proc) {
1904 				while (--i >= 0)
1905 				       proc_net_remove(ip6t_proc_entry[i].name);
1906 				nf_unregister_sockopt(&ip6t_sockopts);
1907 				return -ENOMEM;
1908 			}
1909 			proc->owner = THIS_MODULE;
1910 		}
1911 	}
1912 #endif
1913 
1914 	printk("ip6_tables: (C) 2000-2002 Netfilter core team\n");
1915 	return 0;
1916 }
1917 
fini(void)1918 static void __exit fini(void)
1919 {
1920 	nf_unregister_sockopt(&ip6t_sockopts);
1921 #ifdef CONFIG_PROC_FS
1922 	{
1923 		int i;
1924 		for (i = 0; ip6t_proc_entry[i].name; i++)
1925 			proc_net_remove(ip6t_proc_entry[i].name);
1926 	}
1927 #endif
1928 }
1929 
1930 EXPORT_SYMBOL(ip6t_register_table);
1931 EXPORT_SYMBOL(ip6t_unregister_table);
1932 EXPORT_SYMBOL(ip6t_do_table);
1933 EXPORT_SYMBOL(ip6t_find_target_lock);
1934 EXPORT_SYMBOL(ip6t_register_match);
1935 EXPORT_SYMBOL(ip6t_unregister_match);
1936 EXPORT_SYMBOL(ip6t_register_target);
1937 EXPORT_SYMBOL(ip6t_unregister_target);
1938 EXPORT_SYMBOL(ip6t_ext_hdr);
1939 
1940 module_init(init);
1941 module_exit(fini);
1942 MODULE_LICENSE("GPL");
1943