linux/net/ipv6/netfilter/ip6t_hbh.c
<<
>>
Prefs
   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/*
  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
  44static struct xt_match hbh_mt6_reg[] __read_mostly;
  45
  46static bool
  47hbh_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 = 0;
  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, 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
 167static 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
 184static 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
 204static int __init hbh_mt6_init(void)
 205{
 206        return xt_register_matches(hbh_mt6_reg, ARRAY_SIZE(hbh_mt6_reg));
 207}
 208
 209static void __exit hbh_mt6_exit(void)
 210{
 211        xt_unregister_matches(hbh_mt6_reg, ARRAY_SIZE(hbh_mt6_reg));
 212}
 213
 214module_init(hbh_mt6_init);
 215module_exit(hbh_mt6_exit);
 216