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