linux/net/bridge/netfilter/ebt_among.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 *  ebt_among
   4 *
   5 *      Authors:
   6 *      Grzegorz Borowiak <grzes@gnu.univ.gda.pl>
   7 *
   8 *  August, 2003
   9 *
  10 */
  11#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  12#include <linux/ip.h>
  13#include <linux/if_arp.h>
  14#include <linux/module.h>
  15#include <linux/netfilter/x_tables.h>
  16#include <linux/netfilter_bridge/ebtables.h>
  17#include <linux/netfilter_bridge/ebt_among.h>
  18
  19static bool ebt_mac_wormhash_contains(const struct ebt_mac_wormhash *wh,
  20                                      const char *mac, __be32 ip)
  21{
  22        /* You may be puzzled as to how this code works.
  23         * Some tricks were used, refer to
  24         *      include/linux/netfilter_bridge/ebt_among.h
  25         * as there you can find a solution of this mystery.
  26         */
  27        const struct ebt_mac_wormhash_tuple *p;
  28        int start, limit, i;
  29        uint32_t cmp[2] = { 0, 0 };
  30        int key = ((const unsigned char *)mac)[5];
  31
  32        ether_addr_copy(((char *) cmp) + 2, mac);
  33        start = wh->table[key];
  34        limit = wh->table[key + 1];
  35        if (ip) {
  36                for (i = start; i < limit; i++) {
  37                        p = &wh->pool[i];
  38                        if (cmp[1] == p->cmp[1] && cmp[0] == p->cmp[0])
  39                                if (p->ip == 0 || p->ip == ip)
  40                                        return true;
  41                }
  42        } else {
  43                for (i = start; i < limit; i++) {
  44                        p = &wh->pool[i];
  45                        if (cmp[1] == p->cmp[1] && cmp[0] == p->cmp[0])
  46                                if (p->ip == 0)
  47                                        return true;
  48                }
  49        }
  50        return false;
  51}
  52
  53static int ebt_mac_wormhash_check_integrity(const struct ebt_mac_wormhash
  54                                            *wh)
  55{
  56        int i;
  57
  58        for (i = 0; i < 256; i++) {
  59                if (wh->table[i] > wh->table[i + 1])
  60                        return -0x100 - i;
  61                if (wh->table[i] < 0)
  62                        return -0x200 - i;
  63                if (wh->table[i] > wh->poolsize)
  64                        return -0x300 - i;
  65        }
  66        if (wh->table[256] > wh->poolsize)
  67                return -0xc00;
  68        return 0;
  69}
  70
  71static int get_ip_dst(const struct sk_buff *skb, __be32 *addr)
  72{
  73        if (eth_hdr(skb)->h_proto == htons(ETH_P_IP)) {
  74                const struct iphdr *ih;
  75                struct iphdr _iph;
  76
  77                ih = skb_header_pointer(skb, 0, sizeof(_iph), &_iph);
  78                if (ih == NULL)
  79                        return -1;
  80                *addr = ih->daddr;
  81        } else if (eth_hdr(skb)->h_proto == htons(ETH_P_ARP)) {
  82                const struct arphdr *ah;
  83                struct arphdr _arph;
  84                const __be32 *bp;
  85                __be32 buf;
  86
  87                ah = skb_header_pointer(skb, 0, sizeof(_arph), &_arph);
  88                if (ah == NULL ||
  89                    ah->ar_pln != sizeof(__be32) ||
  90                    ah->ar_hln != ETH_ALEN)
  91                        return -1;
  92                bp = skb_header_pointer(skb, sizeof(struct arphdr) +
  93                                        2 * ETH_ALEN + sizeof(__be32),
  94                                        sizeof(__be32), &buf);
  95                if (bp == NULL)
  96                        return -1;
  97                *addr = *bp;
  98        }
  99        return 0;
 100}
 101
 102static int get_ip_src(const struct sk_buff *skb, __be32 *addr)
 103{
 104        if (eth_hdr(skb)->h_proto == htons(ETH_P_IP)) {
 105                const struct iphdr *ih;
 106                struct iphdr _iph;
 107
 108                ih = skb_header_pointer(skb, 0, sizeof(_iph), &_iph);
 109                if (ih == NULL)
 110                        return -1;
 111                *addr = ih->saddr;
 112        } else if (eth_hdr(skb)->h_proto == htons(ETH_P_ARP)) {
 113                const struct arphdr *ah;
 114                struct arphdr _arph;
 115                const __be32 *bp;
 116                __be32 buf;
 117
 118                ah = skb_header_pointer(skb, 0, sizeof(_arph), &_arph);
 119                if (ah == NULL ||
 120                    ah->ar_pln != sizeof(__be32) ||
 121                    ah->ar_hln != ETH_ALEN)
 122                        return -1;
 123                bp = skb_header_pointer(skb, sizeof(struct arphdr) +
 124                                        ETH_ALEN, sizeof(__be32), &buf);
 125                if (bp == NULL)
 126                        return -1;
 127                *addr = *bp;
 128        }
 129        return 0;
 130}
 131
 132static bool
 133ebt_among_mt(const struct sk_buff *skb, struct xt_action_param *par)
 134{
 135        const struct ebt_among_info *info = par->matchinfo;
 136        const char *dmac, *smac;
 137        const struct ebt_mac_wormhash *wh_dst, *wh_src;
 138        __be32 dip = 0, sip = 0;
 139
 140        wh_dst = ebt_among_wh_dst(info);
 141        wh_src = ebt_among_wh_src(info);
 142
 143        if (wh_src) {
 144                smac = eth_hdr(skb)->h_source;
 145                if (get_ip_src(skb, &sip))
 146                        return false;
 147                if (!(info->bitmask & EBT_AMONG_SRC_NEG)) {
 148                        /* we match only if it contains */
 149                        if (!ebt_mac_wormhash_contains(wh_src, smac, sip))
 150                                return false;
 151                } else {
 152                        /* we match only if it DOES NOT contain */
 153                        if (ebt_mac_wormhash_contains(wh_src, smac, sip))
 154                                return false;
 155                }
 156        }
 157
 158        if (wh_dst) {
 159                dmac = eth_hdr(skb)->h_dest;
 160                if (get_ip_dst(skb, &dip))
 161                        return false;
 162                if (!(info->bitmask & EBT_AMONG_DST_NEG)) {
 163                        /* we match only if it contains */
 164                        if (!ebt_mac_wormhash_contains(wh_dst, dmac, dip))
 165                                return false;
 166                } else {
 167                        /* we match only if it DOES NOT contain */
 168                        if (ebt_mac_wormhash_contains(wh_dst, dmac, dip))
 169                                return false;
 170                }
 171        }
 172
 173        return true;
 174}
 175
 176static bool poolsize_invalid(const struct ebt_mac_wormhash *w)
 177{
 178        return w && w->poolsize >= (INT_MAX / sizeof(struct ebt_mac_wormhash_tuple));
 179}
 180
 181static bool wormhash_offset_invalid(int off, unsigned int len)
 182{
 183        if (off == 0) /* not present */
 184                return false;
 185
 186        if (off < (int)sizeof(struct ebt_among_info) ||
 187            off % __alignof__(struct ebt_mac_wormhash))
 188                return true;
 189
 190        off += sizeof(struct ebt_mac_wormhash);
 191
 192        return off > len;
 193}
 194
 195static bool wormhash_sizes_valid(const struct ebt_mac_wormhash *wh, int a, int b)
 196{
 197        if (a == 0)
 198                a = sizeof(struct ebt_among_info);
 199
 200        return ebt_mac_wormhash_size(wh) + a == b;
 201}
 202
 203static int ebt_among_mt_check(const struct xt_mtchk_param *par)
 204{
 205        const struct ebt_among_info *info = par->matchinfo;
 206        const struct ebt_entry_match *em =
 207                container_of(par->matchinfo, const struct ebt_entry_match, data);
 208        unsigned int expected_length = sizeof(struct ebt_among_info);
 209        const struct ebt_mac_wormhash *wh_dst, *wh_src;
 210        int err;
 211
 212        if (expected_length > em->match_size)
 213                return -EINVAL;
 214
 215        if (wormhash_offset_invalid(info->wh_dst_ofs, em->match_size) ||
 216            wormhash_offset_invalid(info->wh_src_ofs, em->match_size))
 217                return -EINVAL;
 218
 219        wh_dst = ebt_among_wh_dst(info);
 220        if (poolsize_invalid(wh_dst))
 221                return -EINVAL;
 222
 223        expected_length += ebt_mac_wormhash_size(wh_dst);
 224        if (expected_length > em->match_size)
 225                return -EINVAL;
 226
 227        wh_src = ebt_among_wh_src(info);
 228        if (poolsize_invalid(wh_src))
 229                return -EINVAL;
 230
 231        if (info->wh_src_ofs < info->wh_dst_ofs) {
 232                if (!wormhash_sizes_valid(wh_src, info->wh_src_ofs, info->wh_dst_ofs))
 233                        return -EINVAL;
 234        } else {
 235                if (!wormhash_sizes_valid(wh_dst, info->wh_dst_ofs, info->wh_src_ofs))
 236                        return -EINVAL;
 237        }
 238
 239        expected_length += ebt_mac_wormhash_size(wh_src);
 240
 241        if (em->match_size != EBT_ALIGN(expected_length)) {
 242                pr_err_ratelimited("wrong size: %d against expected %d, rounded to %zd\n",
 243                                   em->match_size, expected_length,
 244                                   EBT_ALIGN(expected_length));
 245                return -EINVAL;
 246        }
 247        if (wh_dst && (err = ebt_mac_wormhash_check_integrity(wh_dst))) {
 248                pr_err_ratelimited("dst integrity fail: %x\n", -err);
 249                return -EINVAL;
 250        }
 251        if (wh_src && (err = ebt_mac_wormhash_check_integrity(wh_src))) {
 252                pr_err_ratelimited("src integrity fail: %x\n", -err);
 253                return -EINVAL;
 254        }
 255        return 0;
 256}
 257
 258static struct xt_match ebt_among_mt_reg __read_mostly = {
 259        .name           = "among",
 260        .revision       = 0,
 261        .family         = NFPROTO_BRIDGE,
 262        .match          = ebt_among_mt,
 263        .checkentry     = ebt_among_mt_check,
 264        .matchsize      = -1, /* special case */
 265        .me             = THIS_MODULE,
 266};
 267
 268static int __init ebt_among_init(void)
 269{
 270        return xt_register_match(&ebt_among_mt_reg);
 271}
 272
 273static void __exit ebt_among_fini(void)
 274{
 275        xt_unregister_match(&ebt_among_mt_reg);
 276}
 277
 278module_init(ebt_among_init);
 279module_exit(ebt_among_fini);
 280MODULE_DESCRIPTION("Ebtables: Combined MAC/IP address list matching");
 281MODULE_LICENSE("GPL");
 282