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 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 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 struct xt_match hbh_mt6_reg[] __read_mostly; 45 46 static bool 47 hbh_mt6(const struct sk_buff *skb, struct xt_action_param *par) 48 { 49 struct ipv6_opt_hdr _optsh; 50 const struct ipv6_opt_hdr *oh; 51 const struct ip6t_opts *optinfo = par->matchinfo; 52 unsigned int temp; 53 unsigned int ptr; 54 unsigned int hdrlen = 0; 55 bool ret = false; 56 u8 _opttype; 57 u8 _optlen; 58 const u_int8_t *tp = NULL; 59 const u_int8_t *lp = NULL; 60 unsigned int optlen; 61 int err; 62 63 err = ipv6_find_hdr(skb, &ptr, 64 (par->match == &hbh_mt6_reg[0]) ? 65 NEXTHDR_HOP : NEXTHDR_DEST, NULL); 66 if (err < 0) { 67 if (err != -ENOENT) 68 par->hotdrop = true; 69 return false; 70 } 71 72 oh = skb_header_pointer(skb, ptr, sizeof(_optsh), &_optsh); 73 if (oh == NULL) { 74 par->hotdrop = true; 75 return false; 76 } 77 78 hdrlen = ipv6_optlen(oh); 79 if (skb->len - ptr < hdrlen) { 80 /* Packet smaller than it's length field */ 81 return false; 82 } 83 84 pr_debug("IPv6 OPTS LEN %u %u ", hdrlen, oh->hdrlen); 85 86 pr_debug("len %02X %04X %02X ", 87 optinfo->hdrlen, hdrlen, 88 (!(optinfo->flags & IP6T_OPTS_LEN) || 89 ((optinfo->hdrlen == hdrlen) ^ 90 !!(optinfo->invflags & IP6T_OPTS_INV_LEN)))); 91 92 ret = (oh != NULL) && 93 (!(optinfo->flags & IP6T_OPTS_LEN) || 94 ((optinfo->hdrlen == hdrlen) ^ 95 !!(optinfo->invflags & IP6T_OPTS_INV_LEN))); 96 97 ptr += 2; 98 hdrlen -= 2; 99 if (!(optinfo->flags & IP6T_OPTS_OPTS)) { 100 return ret; 101 } else { 102 pr_debug("Strict "); 103 pr_debug("#%d ", optinfo->optsnr); 104 for (temp = 0; temp < optinfo->optsnr; temp++) { 105 /* type field exists ? */ 106 if (hdrlen < 1) 107 break; 108 tp = skb_header_pointer(skb, ptr, sizeof(_opttype), 109 &_opttype); 110 if (tp == NULL) 111 break; 112 113 /* Type check */ 114 if (*tp != (optinfo->opts[temp] & 0xFF00) >> 8) { 115 pr_debug("Tbad %02X %02X\n", *tp, 116 (optinfo->opts[temp] & 0xFF00) >> 8); 117 return false; 118 } else { 119 pr_debug("Tok "); 120 } 121 /* Length check */ 122 if (*tp) { 123 u16 spec_len; 124 125 /* length field exists ? */ 126 if (hdrlen < 2) 127 break; 128 lp = skb_header_pointer(skb, ptr + 1, 129 sizeof(_optlen), 130 &_optlen); 131 if (lp == NULL) 132 break; 133 spec_len = optinfo->opts[temp] & 0x00FF; 134 135 if (spec_len != 0x00FF && spec_len != *lp) { 136 pr_debug("Lbad %02X %04X\n", *lp, 137 spec_len); 138 return false; 139 } 140 pr_debug("Lok "); 141 optlen = *lp + 2; 142 } else { 143 pr_debug("Pad1\n"); 144 optlen = 1; 145 } 146 147 /* Step to the next */ 148 pr_debug("len%04X\n", optlen); 149 150 if ((ptr > skb->len - optlen || hdrlen < optlen) && 151 temp < optinfo->optsnr - 1) { 152 pr_debug("new pointer is too large!\n"); 153 break; 154 } 155 ptr += optlen; 156 hdrlen -= optlen; 157 } 158 if (temp == optinfo->optsnr) 159 return ret; 160 else 161 return false; 162 } 163 164 return false; 165 } 166 167 static int hbh_mt6_check(const struct xt_mtchk_param *par) 168 { 169 const struct ip6t_opts *optsinfo = par->matchinfo; 170 171 if (optsinfo->invflags & ~IP6T_OPTS_INV_MASK) { 172 pr_debug("unknown flags %X\n", optsinfo->invflags); 173 return -EINVAL; 174 } 175 176 if (optsinfo->flags & IP6T_OPTS_NSTRICT) { 177 pr_debug("Not strict - not implemented"); 178 return -EINVAL; 179 } 180 181 return 0; 182 } 183 184 static struct xt_match hbh_mt6_reg[] __read_mostly = { 185 { 186 /* Note, hbh_mt6 relies on the order of hbh_mt6_reg */ 187 .name = "hbh", 188 .family = NFPROTO_IPV6, 189 .match = hbh_mt6, 190 .matchsize = sizeof(struct ip6t_opts), 191 .checkentry = hbh_mt6_check, 192 .me = THIS_MODULE, 193 }, 194 { 195 .name = "dst", 196 .family = NFPROTO_IPV6, 197 .match = hbh_mt6, 198 .matchsize = sizeof(struct ip6t_opts), 199 .checkentry = hbh_mt6_check, 200 .me = THIS_MODULE, 201 }, 202 }; 203 204 static int __init hbh_mt6_init(void) 205 { 206 return xt_register_matches(hbh_mt6_reg, ARRAY_SIZE(hbh_mt6_reg)); 207 } 208 209 static void __exit hbh_mt6_exit(void) 210 { 211 xt_unregister_matches(hbh_mt6_reg, ARRAY_SIZE(hbh_mt6_reg)); 212 } 213 214 module_init(hbh_mt6_init); 215 module_exit(hbh_mt6_exit); 216