ip6t_hbh.c (4931B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* Kernel module to match Hop-by-Hop and Destination parameters. */ 3 4/* (C) 2001-2002 Andras Kis-Szabo <kisza@sch.bme.hu> 5 */ 6#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 7#include <linux/module.h> 8#include <linux/skbuff.h> 9#include <linux/ipv6.h> 10#include <linux/types.h> 11#include <net/checksum.h> 12#include <net/ipv6.h> 13 14#include <asm/byteorder.h> 15 16#include <linux/netfilter/x_tables.h> 17#include <linux/netfilter_ipv6/ip6_tables.h> 18#include <linux/netfilter_ipv6/ip6t_opts.h> 19 20MODULE_LICENSE("GPL"); 21MODULE_DESCRIPTION("Xtables: IPv6 Hop-By-Hop and Destination Header match"); 22MODULE_AUTHOR("Andras Kis-Szabo <kisza@sch.bme.hu>"); 23MODULE_ALIAS("ip6t_dst"); 24 25/* 26 * (Type & 0xC0) >> 6 27 * 0 -> ignorable 28 * 1 -> must drop the packet 29 * 2 -> send ICMP PARM PROB regardless and drop packet 30 * 3 -> Send ICMP if not a multicast address and drop packet 31 * (Type & 0x20) >> 5 32 * 0 -> invariant 33 * 1 -> can change the routing 34 * (Type & 0x1F) Type 35 * 0 -> Pad1 (only 1 byte!) 36 * 1 -> PadN LENGTH info (total length = length + 2) 37 * C0 | 2 -> JUMBO 4 x x x x ( xxxx > 64k ) 38 * 5 -> RTALERT 2 x x 39 */ 40 41static struct xt_match hbh_mt6_reg[] __read_mostly; 42 43static bool 44hbh_mt6(const struct sk_buff *skb, struct xt_action_param *par) 45{ 46 struct ipv6_opt_hdr _optsh; 47 const struct ipv6_opt_hdr *oh; 48 const struct ip6t_opts *optinfo = par->matchinfo; 49 unsigned int temp; 50 unsigned int ptr = 0; 51 unsigned int hdrlen = 0; 52 bool ret = false; 53 u8 _opttype; 54 u8 _optlen; 55 const u_int8_t *tp = NULL; 56 const u_int8_t *lp = NULL; 57 unsigned int optlen; 58 int err; 59 60 err = ipv6_find_hdr(skb, &ptr, 61 (par->match == &hbh_mt6_reg[0]) ? 62 NEXTHDR_HOP : NEXTHDR_DEST, NULL, NULL); 63 if (err < 0) { 64 if (err != -ENOENT) 65 par->hotdrop = true; 66 return false; 67 } 68 69 oh = skb_header_pointer(skb, ptr, sizeof(_optsh), &_optsh); 70 if (oh == NULL) { 71 par->hotdrop = true; 72 return false; 73 } 74 75 hdrlen = ipv6_optlen(oh); 76 if (skb->len - ptr < hdrlen) { 77 /* Packet smaller than it's length field */ 78 return false; 79 } 80 81 pr_debug("IPv6 OPTS LEN %u %u ", hdrlen, oh->hdrlen); 82 83 pr_debug("len %02X %04X %02X ", 84 optinfo->hdrlen, hdrlen, 85 (!(optinfo->flags & IP6T_OPTS_LEN) || 86 ((optinfo->hdrlen == hdrlen) ^ 87 !!(optinfo->invflags & IP6T_OPTS_INV_LEN)))); 88 89 ret = (!(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 163static int 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("unknown flags %X\n", optsinfo->invflags); 169 return -EINVAL; 170 } 171 172 if (optsinfo->flags & IP6T_OPTS_NSTRICT) { 173 pr_debug("Not strict - not implemented"); 174 return -EINVAL; 175 } 176 177 return 0; 178} 179 180static struct xt_match hbh_mt6_reg[] __read_mostly = { 181 { 182 /* Note, hbh_mt6 relies on the order of hbh_mt6_reg */ 183 .name = "hbh", 184 .family = NFPROTO_IPV6, 185 .match = hbh_mt6, 186 .matchsize = sizeof(struct ip6t_opts), 187 .checkentry = hbh_mt6_check, 188 .me = THIS_MODULE, 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 }, 198}; 199 200static int __init hbh_mt6_init(void) 201{ 202 return xt_register_matches(hbh_mt6_reg, ARRAY_SIZE(hbh_mt6_reg)); 203} 204 205static void __exit hbh_mt6_exit(void) 206{ 207 xt_unregister_matches(hbh_mt6_reg, ARRAY_SIZE(hbh_mt6_reg)); 208} 209 210module_init(hbh_mt6_init); 211module_exit(hbh_mt6_exit);