ip6t_hbh.c 4.8 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
/* Kernel module to match Hop-by-Hop and Destination parameters. */

/* (C) 2001-2002 Andras Kis-Szabo <kisza@sch.bme.hu>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#include <linux/module.h>
#include <linux/skbuff.h>
#include <linux/ipv6.h>
#include <linux/types.h>
#include <net/checksum.h>
#include <net/ipv6.h>

#include <asm/byteorder.h>

#include <linux/netfilter_ipv6/ip6_tables.h>
#include <linux/netfilter_ipv6/ip6t_opts.h>

#define HOPBYHOP	1

MODULE_LICENSE("GPL");
#if HOPBYHOP
MODULE_DESCRIPTION("IPv6 HbH match");
#else
MODULE_DESCRIPTION("IPv6 DST match");
#endif
MODULE_AUTHOR("Andras Kis-Szabo <kisza@sch.bme.hu>");

#if 0
#define DEBUGP printk
#else
#define DEBUGP(format, args...)
#endif

/*
39 40 41 42 43
 *  (Type & 0xC0) >> 6
 *	0	-> ignorable
 *	1	-> must drop the packet
 *	2	-> send ICMP PARM PROB regardless and drop packet
 *	3	-> Send ICMP if not a multicast address and drop packet
L
Linus Torvalds 已提交
44
 *  (Type & 0x20) >> 5
45 46
 *	0	-> invariant
 *	1	-> can change the routing
L
Linus Torvalds 已提交
47
 *  (Type & 0x1F) Type
48 49 50 51
 *	0	-> Pad1 (only 1 byte!)
 *	1	-> PadN LENGTH info (total length = length + 2)
 *	C0 | 2	-> JUMBO 4 x x x x ( xxxx > 64k )
 *	5	-> RTALERT 2 x x
L
Linus Torvalds 已提交
52 53 54 55 56 57
 */

static int
match(const struct sk_buff *skb,
      const struct net_device *in,
      const struct net_device *out,
58
      const struct xt_match *match,
L
Linus Torvalds 已提交
59 60 61 62 63
      const void *matchinfo,
      int offset,
      unsigned int protoff,
      int *hotdrop)
{
64 65 66 67 68 69 70 71 72 73
	struct ipv6_opt_hdr _optsh, *oh;
	const struct ip6t_opts *optinfo = matchinfo;
	unsigned int temp;
	unsigned int ptr;
	unsigned int hdrlen = 0;
	unsigned int ret = 0;
	u8 _opttype, *tp = NULL;
	u8 _optlen, *lp = NULL;
	unsigned int optlen;

L
Linus Torvalds 已提交
74
#if HOPBYHOP
75
	if (ipv6_find_hdr(skb, &ptr, NEXTHDR_HOP, NULL) < 0)
L
Linus Torvalds 已提交
76
#else
77
	if (ipv6_find_hdr(skb, &ptr, NEXTHDR_DEST, NULL) < 0)
L
Linus Torvalds 已提交
78
#endif
79
		return 0;
L
Linus Torvalds 已提交
80

81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
	oh = skb_header_pointer(skb, ptr, sizeof(_optsh), &_optsh);
	if (oh == NULL) {
		*hotdrop = 1;
		return 0;
	}

	hdrlen = ipv6_optlen(oh);
	if (skb->len - ptr < hdrlen) {
		/* Packet smaller than it's length field */
		return 0;
	}

	DEBUGP("IPv6 OPTS LEN %u %u ", hdrlen, oh->hdrlen);

	DEBUGP("len %02X %04X %02X ",
	       optinfo->hdrlen, hdrlen,
	       (!(optinfo->flags & IP6T_OPTS_LEN) ||
		((optinfo->hdrlen == hdrlen) ^
		 !!(optinfo->invflags & IP6T_OPTS_INV_LEN))));

	ret = (oh != NULL) &&
	      (!(optinfo->flags & IP6T_OPTS_LEN) ||
	       ((optinfo->hdrlen == hdrlen) ^
		!!(optinfo->invflags & IP6T_OPTS_INV_LEN)));

	ptr += 2;
	hdrlen -= 2;
	if (!(optinfo->flags & IP6T_OPTS_OPTS)) {
		return ret;
L
Linus Torvalds 已提交
110 111 112 113
	} else if (optinfo->flags & IP6T_OPTS_NSTRICT) {
		DEBUGP("Not strict - not implemented");
	} else {
		DEBUGP("Strict ");
114 115
		DEBUGP("#%d ", optinfo->optsnr);
		for (temp = 0; temp < optinfo->optsnr; temp++) {
L
Linus Torvalds 已提交
116 117 118 119 120 121 122 123 124
			/* type field exists ? */
			if (hdrlen < 1)
				break;
			tp = skb_header_pointer(skb, ptr, sizeof(_opttype),
						&_opttype);
			if (tp == NULL)
				break;

			/* Type check */
125
			if (*tp != (optinfo->opts[temp] & 0xFF00) >> 8) {
L
Linus Torvalds 已提交
126 127
				DEBUGP("Tbad %02X %02X\n",
				       *tp,
128
				       (optinfo->opts[temp] & 0xFF00) >> 8);
L
Linus Torvalds 已提交
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
				return 0;
			} else {
				DEBUGP("Tok ");
			}
			/* Length check */
			if (*tp) {
				u16 spec_len;

				/* length field exists ? */
				if (hdrlen < 2)
					break;
				lp = skb_header_pointer(skb, ptr + 1,
							sizeof(_optlen),
							&_optlen);
				if (lp == NULL)
					break;
				spec_len = optinfo->opts[temp] & 0x00FF;

				if (spec_len != 0x00FF && spec_len != *lp) {
					DEBUGP("Lbad %02X %04X\n", *lp,
					       spec_len);
					return 0;
				}
				DEBUGP("Lok ");
				optlen = *lp + 2;
			} else {
				DEBUGP("Pad1\n");
				optlen = 1;
			}

			/* Step to the next */
			DEBUGP("len%04X \n", optlen);

			if ((ptr > skb->len - optlen || hdrlen < optlen) &&
			    (temp < optinfo->optsnr - 1)) {
				DEBUGP("new pointer is too large! \n");
				break;
			}
			ptr += optlen;
			hdrlen -= optlen;
		}
		if (temp == optinfo->optsnr)
			return ret;
172 173
		else
			return 0;
L
Linus Torvalds 已提交
174 175 176 177 178 179 180 181
	}

	return 0;
}

/* Called when user tries to insert an entry of this type. */
static int
checkentry(const char *tablename,
182
	   const void *entry,
183
	   const struct xt_match *match,
184 185 186
	   void *matchinfo,
	   unsigned int matchinfosize,
	   unsigned int hook_mask)
L
Linus Torvalds 已提交
187
{
188 189 190 191 192 193 194
	const struct ip6t_opts *optsinfo = matchinfo;

	if (optsinfo->invflags & ~IP6T_OPTS_INV_MASK) {
		DEBUGP("ip6t_opts: unknown flags %X\n", optsinfo->invflags);
		return 0;
	}
	return 1;
L
Linus Torvalds 已提交
195 196 197 198 199 200 201 202
}

static struct ip6t_match opts_match = {
#if HOPBYHOP
	.name		= "hbh",
#else
	.name		= "dst",
#endif
203 204 205
	.match		= match,
	.matchsize	= sizeof(struct ip6t_opts),
	.checkentry	= checkentry,
L
Linus Torvalds 已提交
206 207 208 209 210
	.me		= THIS_MODULE,
};

static int __init init(void)
{
211
	return ip6t_register_match(&opts_match);
L
Linus Torvalds 已提交
212 213 214 215
}

static void __exit cleanup(void)
{
216
	ip6t_unregister_match(&opts_match);
L
Linus Torvalds 已提交
217 218 219 220
}

module_init(init);
module_exit(cleanup);