linux/net/ipv6/netfilter/ip6t_hbh.c
<<
>>
Prefs
   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 = (oh != NULL) &&
  90              (!(optinfo->flags & IP6T_OPTS_LEN) ||
  91               ((optinfo->hdrlen == hdrlen) ^
  92                !!(optinfo->invflags & IP6T_OPTS_INV_LEN)));
  93
  94        ptr += 2;
  95        hdrlen -= 2;
  96        if (!(optinfo->flags & IP6T_OPTS_OPTS)) {
  97                return ret;
  98        } else {
  99                pr_debug("Strict ");
 100                pr_debug("#%d ", optinfo->optsnr);
 101                for (temp = 0; temp < optinfo->optsnr; temp++) {
 102                        /* type field exists ? */
 103                        if (hdrlen < 1)
 104                                break;
 105                        tp = skb_header_pointer(skb, ptr, sizeof(_opttype),
 106                                                &_opttype);
 107                        if (tp == NULL)
 108                                break;
 109
 110                        /* Type check */
 111                        if (*tp != (optinfo->opts[temp] & 0xFF00) >> 8) {
 112                                pr_debug("Tbad %02X %02X\n", *tp,
 113                                         (optinfo->opts[temp] & 0xFF00) >> 8);
 114                                return false;
 115                        } else {
 116                                pr_debug("Tok ");
 117                        }
 118                        /* Length check */
 119                        if (*tp) {
 120                                u16 spec_len;
 121
 122                                /* length field exists ? */
 123                                if (hdrlen < 2)
 124                                        break;
 125                                lp = skb_header_pointer(skb, ptr + 1,
 126                                                        sizeof(_optlen),
 127                                                        &_optlen);
 128                                if (lp == NULL)
 129                                        break;
 130                                spec_len = optinfo->opts[temp] & 0x00FF;
 131
 132                                if (spec_len != 0x00FF && spec_len != *lp) {
 133                                        pr_debug("Lbad %02X %04X\n", *lp,
 134                                                 spec_len);
 135                                        return false;
 136                                }
 137                                pr_debug("Lok ");
 138                                optlen = *lp + 2;
 139                        } else {
 140                                pr_debug("Pad1\n");
 141                                optlen = 1;
 142                        }
 143
 144                        /* Step to the next */
 145                        pr_debug("len%04X\n", optlen);
 146
 147                        if ((ptr > skb->len - optlen || hdrlen < optlen) &&
 148                            temp < optinfo->optsnr - 1) {
 149                                pr_debug("new pointer is too large!\n");
 150                                break;
 151                        }
 152                        ptr += optlen;
 153                        hdrlen -= optlen;
 154                }
 155                if (temp == optinfo->optsnr)
 156                        return ret;
 157                else
 158                        return false;
 159        }
 160
 161        return false;
 162}
 163
 164static int hbh_mt6_check(const struct xt_mtchk_param *par)
 165{
 166        const struct ip6t_opts *optsinfo = par->matchinfo;
 167
 168        if (optsinfo->invflags & ~IP6T_OPTS_INV_MASK) {
 169                pr_debug("unknown flags %X\n", optsinfo->invflags);
 170                return -EINVAL;
 171        }
 172
 173        if (optsinfo->flags & IP6T_OPTS_NSTRICT) {
 174                pr_debug("Not strict - not implemented");
 175                return -EINVAL;
 176        }
 177
 178        return 0;
 179}
 180
 181static struct xt_match hbh_mt6_reg[] __read_mostly = {
 182        {
 183                /* Note, hbh_mt6 relies on the order of hbh_mt6_reg */
 184                .name           = "hbh",
 185                .family         = NFPROTO_IPV6,
 186                .match          = hbh_mt6,
 187                .matchsize      = sizeof(struct ip6t_opts),
 188                .checkentry     = hbh_mt6_check,
 189                .me             = THIS_MODULE,
 190        },
 191        {
 192                .name           = "dst",
 193                .family         = NFPROTO_IPV6,
 194                .match          = hbh_mt6,
 195                .matchsize      = sizeof(struct ip6t_opts),
 196                .checkentry     = hbh_mt6_check,
 197                .me             = THIS_MODULE,
 198        },
 199};
 200
 201static int __init hbh_mt6_init(void)
 202{
 203        return xt_register_matches(hbh_mt6_reg, ARRAY_SIZE(hbh_mt6_reg));
 204}
 205
 206static void __exit hbh_mt6_exit(void)
 207{
 208        xt_unregister_matches(hbh_mt6_reg, ARRAY_SIZE(hbh_mt6_reg));
 209}
 210
 211module_init(hbh_mt6_init);
 212module_exit(hbh_mt6_exit);
 213