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