• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-R7000-V1.0.7.12_1.2.5/components/opensource/linux/linux-2.6.36/net/ipv6/netfilter/
1/* Kernel module to match Hop-by-Hop and Destination parameters. */
2
3/* (C) 2001-2002 Andras Kis-Szabo <kisza@sch.bme.hu>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
8 */
9#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
10#include <linux/module.h>
11#include <linux/skbuff.h>
12#include <linux/ipv6.h>
13#include <linux/types.h>
14#include <net/checksum.h>
15#include <net/ipv6.h>
16
17#include <asm/byteorder.h>
18
19#include <linux/netfilter/x_tables.h>
20#include <linux/netfilter_ipv6/ip6_tables.h>
21#include <linux/netfilter_ipv6/ip6t_opts.h>
22
23MODULE_LICENSE("GPL");
24MODULE_DESCRIPTION("Xtables: IPv6 Hop-By-Hop and Destination Header match");
25MODULE_AUTHOR("Andras Kis-Szabo <kisza@sch.bme.hu>");
26MODULE_ALIAS("ip6t_dst");
27
28
29static struct xt_match hbh_mt6_reg[] __read_mostly;
30
31static bool
32hbh_mt6(const struct sk_buff *skb, struct xt_action_param *par)
33{
34	struct ipv6_opt_hdr _optsh;
35	const struct ipv6_opt_hdr *oh;
36	const struct ip6t_opts *optinfo = par->matchinfo;
37	unsigned int temp;
38	unsigned int ptr;
39	unsigned int hdrlen = 0;
40	bool ret = false;
41	u8 _opttype;
42	u8 _optlen;
43	const u_int8_t *tp = NULL;
44	const u_int8_t *lp = NULL;
45	unsigned int optlen;
46	int err;
47
48	err = ipv6_find_hdr(skb, &ptr,
49			    (par->match == &hbh_mt6_reg[0]) ?
50			    NEXTHDR_HOP : NEXTHDR_DEST, NULL);
51	if (err < 0) {
52		if (err != -ENOENT)
53			par->hotdrop = true;
54		return false;
55	}
56
57	oh = skb_header_pointer(skb, ptr, sizeof(_optsh), &_optsh);
58	if (oh == NULL) {
59		par->hotdrop = true;
60		return false;
61	}
62
63	hdrlen = ipv6_optlen(oh);
64	if (skb->len - ptr < hdrlen) {
65		/* Packet smaller than it's length field */
66		return false;
67	}
68
69	pr_debug("IPv6 OPTS LEN %u %u ", hdrlen, oh->hdrlen);
70
71	pr_debug("len %02X %04X %02X ",
72		 optinfo->hdrlen, hdrlen,
73		 (!(optinfo->flags & IP6T_OPTS_LEN) ||
74		  ((optinfo->hdrlen == hdrlen) ^
75		   !!(optinfo->invflags & IP6T_OPTS_INV_LEN))));
76
77	ret = (oh != NULL) &&
78	      (!(optinfo->flags & IP6T_OPTS_LEN) ||
79	       ((optinfo->hdrlen == hdrlen) ^
80		!!(optinfo->invflags & IP6T_OPTS_INV_LEN)));
81
82	ptr += 2;
83	hdrlen -= 2;
84	if (!(optinfo->flags & IP6T_OPTS_OPTS)) {
85		return ret;
86	} else {
87		pr_debug("Strict ");
88		pr_debug("#%d ", optinfo->optsnr);
89		for (temp = 0; temp < optinfo->optsnr; temp++) {
90			/* type field exists ? */
91			if (hdrlen < 1)
92				break;
93			tp = skb_header_pointer(skb, ptr, sizeof(_opttype),
94						&_opttype);
95			if (tp == NULL)
96				break;
97
98			/* Type check */
99			if (*tp != (optinfo->opts[temp] & 0xFF00) >> 8) {
100				pr_debug("Tbad %02X %02X\n", *tp,
101					 (optinfo->opts[temp] & 0xFF00) >> 8);
102				return false;
103			} else {
104				pr_debug("Tok ");
105			}
106			/* Length check */
107			if (*tp) {
108				u16 spec_len;
109
110				/* length field exists ? */
111				if (hdrlen < 2)
112					break;
113				lp = skb_header_pointer(skb, ptr + 1,
114							sizeof(_optlen),
115							&_optlen);
116				if (lp == NULL)
117					break;
118				spec_len = optinfo->opts[temp] & 0x00FF;
119
120				if (spec_len != 0x00FF && spec_len != *lp) {
121					pr_debug("Lbad %02X %04X\n", *lp,
122						 spec_len);
123					return false;
124				}
125				pr_debug("Lok ");
126				optlen = *lp + 2;
127			} else {
128				pr_debug("Pad1\n");
129				optlen = 1;
130			}
131
132			/* Step to the next */
133			pr_debug("len%04X\n", optlen);
134
135			if ((ptr > skb->len - optlen || hdrlen < optlen) &&
136			    temp < optinfo->optsnr - 1) {
137				pr_debug("new pointer is too large!\n");
138				break;
139			}
140			ptr += optlen;
141			hdrlen -= optlen;
142		}
143		if (temp == optinfo->optsnr)
144			return ret;
145		else
146			return false;
147	}
148
149	return false;
150}
151
152static int hbh_mt6_check(const struct xt_mtchk_param *par)
153{
154	const struct ip6t_opts *optsinfo = par->matchinfo;
155
156	if (optsinfo->invflags & ~IP6T_OPTS_INV_MASK) {
157		pr_debug("unknown flags %X\n", optsinfo->invflags);
158		return -EINVAL;
159	}
160
161	if (optsinfo->flags & IP6T_OPTS_NSTRICT) {
162		pr_debug("Not strict - not implemented");
163		return -EINVAL;
164	}
165
166	return 0;
167}
168
169static struct xt_match hbh_mt6_reg[] __read_mostly = {
170	{
171		/* Note, hbh_mt6 relies on the order of hbh_mt6_reg */
172		.name		= "hbh",
173		.family		= NFPROTO_IPV6,
174		.match		= hbh_mt6,
175		.matchsize	= sizeof(struct ip6t_opts),
176		.checkentry	= hbh_mt6_check,
177		.me		= THIS_MODULE,
178	},
179	{
180		.name		= "dst",
181		.family		= NFPROTO_IPV6,
182		.match		= hbh_mt6,
183		.matchsize	= sizeof(struct ip6t_opts),
184		.checkentry	= hbh_mt6_check,
185		.me		= THIS_MODULE,
186	},
187};
188
189static int __init hbh_mt6_init(void)
190{
191	return xt_register_matches(hbh_mt6_reg, ARRAY_SIZE(hbh_mt6_reg));
192}
193
194static void __exit hbh_mt6_exit(void)
195{
196	xt_unregister_matches(hbh_mt6_reg, ARRAY_SIZE(hbh_mt6_reg));
197}
198
199module_init(hbh_mt6_init);
200module_exit(hbh_mt6_exit);
201