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