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
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("IPv6 opts match");
25MODULE_AUTHOR("Andras Kis-Szabo <kisza@sch.bme.hu>");
26MODULE_ALIAS("ip6t_dst");
27
28#define DEBUGP(format, args...)
29
30
31static int
32match(const struct sk_buff *skb,
33      const struct net_device *in,
34      const struct net_device *out,
35      const struct xt_match *match,
36      const void *matchinfo,
37      int offset,
38      unsigned int protoff,
39      int *hotdrop)
40{
41	struct ipv6_opt_hdr _optsh, *oh;
42	const struct ip6t_opts *optinfo = matchinfo;
43	unsigned int temp;
44	unsigned int ptr;
45	unsigned int hdrlen = 0;
46	unsigned int ret = 0;
47	u8 _opttype, *tp = NULL;
48	u8 _optlen, *lp = NULL;
49	unsigned int optlen;
50	int err;
51
52	err = ipv6_find_hdr(skb, &ptr, match->data, NULL);
53	if (err < 0) {
54		if (err != -ENOENT)
55			*hotdrop = 1;
56		return 0;
57	}
58
59	oh = skb_header_pointer(skb, ptr, sizeof(_optsh), &_optsh);
60	if (oh == NULL) {
61		*hotdrop = 1;
62		return 0;
63	}
64
65	hdrlen = ipv6_optlen(oh);
66	if (skb->len - ptr < hdrlen) {
67		/* Packet smaller than it's length field */
68		return 0;
69	}
70
71	DEBUGP("IPv6 OPTS LEN %u %u ", hdrlen, oh->hdrlen);
72
73	DEBUGP("len %02X %04X %02X ",
74	       optinfo->hdrlen, hdrlen,
75	       (!(optinfo->flags & IP6T_OPTS_LEN) ||
76		((optinfo->hdrlen == hdrlen) ^
77		 !!(optinfo->invflags & IP6T_OPTS_INV_LEN))));
78
79	ret = (oh != NULL) &&
80	      (!(optinfo->flags & IP6T_OPTS_LEN) ||
81	       ((optinfo->hdrlen == hdrlen) ^
82		!!(optinfo->invflags & IP6T_OPTS_INV_LEN)));
83
84	ptr += 2;
85	hdrlen -= 2;
86	if (!(optinfo->flags & IP6T_OPTS_OPTS)) {
87		return ret;
88	} else if (optinfo->flags & IP6T_OPTS_NSTRICT) {
89		DEBUGP("Not strict - not implemented");
90	} else {
91		DEBUGP("Strict ");
92		DEBUGP("#%d ", optinfo->optsnr);
93		for (temp = 0; temp < optinfo->optsnr; temp++) {
94			/* type field exists ? */
95			if (hdrlen < 1)
96				break;
97			tp = skb_header_pointer(skb, ptr, sizeof(_opttype),
98						&_opttype);
99			if (tp == NULL)
100				break;
101
102			/* Type check */
103			if (*tp != (optinfo->opts[temp] & 0xFF00) >> 8) {
104				DEBUGP("Tbad %02X %02X\n",
105				       *tp,
106				       (optinfo->opts[temp] & 0xFF00) >> 8);
107				return 0;
108			} else {
109				DEBUGP("Tok ");
110			}
111			/* Length check */
112			if (*tp) {
113				u16 spec_len;
114
115				/* length field exists ? */
116				if (hdrlen < 2)
117					break;
118				lp = skb_header_pointer(skb, ptr + 1,
119							sizeof(_optlen),
120							&_optlen);
121				if (lp == NULL)
122					break;
123				spec_len = optinfo->opts[temp] & 0x00FF;
124
125				if (spec_len != 0x00FF && spec_len != *lp) {
126					DEBUGP("Lbad %02X %04X\n", *lp,
127					       spec_len);
128					return 0;
129				}
130				DEBUGP("Lok ");
131				optlen = *lp + 2;
132			} else {
133				DEBUGP("Pad1\n");
134				optlen = 1;
135			}
136
137			/* Step to the next */
138			DEBUGP("len%04X \n", optlen);
139
140			if ((ptr > skb->len - optlen || hdrlen < optlen) &&
141			    (temp < optinfo->optsnr - 1)) {
142				DEBUGP("new pointer is too large! \n");
143				break;
144			}
145			ptr += optlen;
146			hdrlen -= optlen;
147		}
148		if (temp == optinfo->optsnr)
149			return ret;
150		else
151			return 0;
152	}
153
154	return 0;
155}
156
157/* Called when user tries to insert an entry of this type. */
158static int
159checkentry(const char *tablename,
160	   const void *entry,
161	   const struct xt_match *match,
162	   void *matchinfo,
163	   unsigned int hook_mask)
164{
165	const struct ip6t_opts *optsinfo = matchinfo;
166
167	if (optsinfo->invflags & ~IP6T_OPTS_INV_MASK) {
168		DEBUGP("ip6t_opts: unknown flags %X\n", optsinfo->invflags);
169		return 0;
170	}
171	return 1;
172}
173
174static struct xt_match opts_match[] = {
175	{
176		.name		= "hbh",
177		.family		= AF_INET6,
178		.match		= match,
179		.matchsize	= sizeof(struct ip6t_opts),
180		.checkentry	= checkentry,
181		.me		= THIS_MODULE,
182		.data		= NEXTHDR_HOP,
183	},
184	{
185		.name		= "dst",
186		.family		= AF_INET6,
187		.match		= match,
188		.matchsize	= sizeof(struct ip6t_opts),
189		.checkentry	= checkentry,
190		.me		= THIS_MODULE,
191		.data		= NEXTHDR_DEST,
192	},
193};
194
195static int __init ip6t_hbh_init(void)
196{
197	return xt_register_matches(opts_match, ARRAY_SIZE(opts_match));
198}
199
200static void __exit ip6t_hbh_fini(void)
201{
202	xt_unregister_matches(opts_match, ARRAY_SIZE(opts_match));
203}
204
205module_init(ip6t_hbh_init);
206module_exit(ip6t_hbh_fini);
207