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