root/net/bridge/netfilter/ebt_among.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ebt_mac_wormhash_contains
  2. ebt_mac_wormhash_check_integrity
  3. get_ip_dst
  4. get_ip_src
  5. ebt_among_mt
  6. poolsize_invalid
  7. wormhash_offset_invalid
  8. wormhash_sizes_valid
  9. ebt_among_mt_check
  10. ebt_among_init
  11. ebt_among_fini

   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 
  19 static 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 
  53 static 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 
  71 static 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 
 102 static 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 
 132 static bool
 133 ebt_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 
 176 static 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 
 181 static 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 
 195 static 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 
 203 static 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 
 258 static 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 
 268 static int __init ebt_among_init(void)
 269 {
 270         return xt_register_match(&ebt_among_mt_reg);
 271 }
 272 
 273 static void __exit ebt_among_fini(void)
 274 {
 275         xt_unregister_match(&ebt_among_mt_reg);
 276 }
 277 
 278 module_init(ebt_among_init);
 279 module_exit(ebt_among_fini);
 280 MODULE_DESCRIPTION("Ebtables: Combined MAC/IP address list matching");
 281 MODULE_LICENSE("GPL");

/* [<][>][^][v][top][bottom][index][help] */