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