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