Loading...
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 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 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 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 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 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 | /* 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
/*
* (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
* (Type & 0x20) >> 5
* 0 -> invariant
* 1 -> can change the routing
* (Type & 0x1F) Type
* 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
*/
static int
match(const struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
const struct xt_match *match,
const void *matchinfo,
int offset,
unsigned int protoff,
int *hotdrop)
{
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;
#if HOPBYHOP
if (ipv6_find_hdr(skb, &ptr, NEXTHDR_HOP, NULL) < 0)
#else
if (ipv6_find_hdr(skb, &ptr, NEXTHDR_DEST, NULL) < 0)
#endif
return 0;
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;
} else if (optinfo->flags & IP6T_OPTS_NSTRICT) {
DEBUGP("Not strict - not implemented");
} else {
DEBUGP("Strict ");
DEBUGP("#%d ", optinfo->optsnr);
for (temp = 0; temp < optinfo->optsnr; temp++) {
/* type field exists ? */
if (hdrlen < 1)
break;
tp = skb_header_pointer(skb, ptr, sizeof(_opttype),
&_opttype);
if (tp == NULL)
break;
/* Type check */
if (*tp != (optinfo->opts[temp] & 0xFF00) >> 8) {
DEBUGP("Tbad %02X %02X\n",
*tp,
(optinfo->opts[temp] & 0xFF00) >> 8);
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;
else
return 0;
}
return 0;
}
/* Called when user tries to insert an entry of this type. */
static int
checkentry(const char *tablename,
const void *entry,
const struct xt_match *match,
void *matchinfo,
unsigned int matchinfosize,
unsigned int hook_mask)
{
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;
}
static struct ip6t_match opts_match = {
#if HOPBYHOP
.name = "hbh",
#else
.name = "dst",
#endif
.match = match,
.matchsize = sizeof(struct ip6t_opts),
.checkentry = checkentry,
.me = THIS_MODULE,
};
static int __init ip6t_hbh_init(void)
{
return ip6t_register_match(&opts_match);
}
static void __exit ip6t_hbh_fini(void)
{
ip6t_unregister_match(&opts_match);
}
module_init(ip6t_hbh_init);
module_exit(ip6t_hbh_fini);
|