Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  *  ebt_among
0004  *
0005  *  Authors:
0006  *  Grzegorz Borowiak <grzes@gnu.univ.gda.pl>
0007  *
0008  *  August, 2003
0009  *
0010  */
0011 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0012 #include <linux/ip.h>
0013 #include <linux/if_arp.h>
0014 #include <linux/module.h>
0015 #include <linux/netfilter/x_tables.h>
0016 #include <linux/netfilter_bridge/ebtables.h>
0017 #include <linux/netfilter_bridge/ebt_among.h>
0018 
0019 static bool ebt_mac_wormhash_contains(const struct ebt_mac_wormhash *wh,
0020                       const char *mac, __be32 ip)
0021 {
0022     /* You may be puzzled as to how this code works.
0023      * Some tricks were used, refer to
0024      *  include/linux/netfilter_bridge/ebt_among.h
0025      * as there you can find a solution of this mystery.
0026      */
0027     const struct ebt_mac_wormhash_tuple *p;
0028     int start, limit, i;
0029     uint32_t cmp[2] = { 0, 0 };
0030     int key = ((const unsigned char *)mac)[5];
0031 
0032     ether_addr_copy(((char *) cmp) + 2, mac);
0033     start = wh->table[key];
0034     limit = wh->table[key + 1];
0035     if (ip) {
0036         for (i = start; i < limit; i++) {
0037             p = &wh->pool[i];
0038             if (cmp[1] == p->cmp[1] && cmp[0] == p->cmp[0])
0039                 if (p->ip == 0 || p->ip == ip)
0040                     return true;
0041         }
0042     } else {
0043         for (i = start; i < limit; i++) {
0044             p = &wh->pool[i];
0045             if (cmp[1] == p->cmp[1] && cmp[0] == p->cmp[0])
0046                 if (p->ip == 0)
0047                     return true;
0048         }
0049     }
0050     return false;
0051 }
0052 
0053 static int ebt_mac_wormhash_check_integrity(const struct ebt_mac_wormhash
0054                         *wh)
0055 {
0056     int i;
0057 
0058     for (i = 0; i < 256; i++) {
0059         if (wh->table[i] > wh->table[i + 1])
0060             return -0x100 - i;
0061         if (wh->table[i] < 0)
0062             return -0x200 - i;
0063         if (wh->table[i] > wh->poolsize)
0064             return -0x300 - i;
0065     }
0066     if (wh->table[256] > wh->poolsize)
0067         return -0xc00;
0068     return 0;
0069 }
0070 
0071 static int get_ip_dst(const struct sk_buff *skb, __be32 *addr)
0072 {
0073     if (eth_hdr(skb)->h_proto == htons(ETH_P_IP)) {
0074         const struct iphdr *ih;
0075         struct iphdr _iph;
0076 
0077         ih = skb_header_pointer(skb, 0, sizeof(_iph), &_iph);
0078         if (ih == NULL)
0079             return -1;
0080         *addr = ih->daddr;
0081     } else if (eth_hdr(skb)->h_proto == htons(ETH_P_ARP)) {
0082         const struct arphdr *ah;
0083         struct arphdr _arph;
0084         const __be32 *bp;
0085         __be32 buf;
0086 
0087         ah = skb_header_pointer(skb, 0, sizeof(_arph), &_arph);
0088         if (ah == NULL ||
0089             ah->ar_pln != sizeof(__be32) ||
0090             ah->ar_hln != ETH_ALEN)
0091             return -1;
0092         bp = skb_header_pointer(skb, sizeof(struct arphdr) +
0093                     2 * ETH_ALEN + sizeof(__be32),
0094                     sizeof(__be32), &buf);
0095         if (bp == NULL)
0096             return -1;
0097         *addr = *bp;
0098     }
0099     return 0;
0100 }
0101 
0102 static int get_ip_src(const struct sk_buff *skb, __be32 *addr)
0103 {
0104     if (eth_hdr(skb)->h_proto == htons(ETH_P_IP)) {
0105         const struct iphdr *ih;
0106         struct iphdr _iph;
0107 
0108         ih = skb_header_pointer(skb, 0, sizeof(_iph), &_iph);
0109         if (ih == NULL)
0110             return -1;
0111         *addr = ih->saddr;
0112     } else if (eth_hdr(skb)->h_proto == htons(ETH_P_ARP)) {
0113         const struct arphdr *ah;
0114         struct arphdr _arph;
0115         const __be32 *bp;
0116         __be32 buf;
0117 
0118         ah = skb_header_pointer(skb, 0, sizeof(_arph), &_arph);
0119         if (ah == NULL ||
0120             ah->ar_pln != sizeof(__be32) ||
0121             ah->ar_hln != ETH_ALEN)
0122             return -1;
0123         bp = skb_header_pointer(skb, sizeof(struct arphdr) +
0124                     ETH_ALEN, sizeof(__be32), &buf);
0125         if (bp == NULL)
0126             return -1;
0127         *addr = *bp;
0128     }
0129     return 0;
0130 }
0131 
0132 static bool
0133 ebt_among_mt(const struct sk_buff *skb, struct xt_action_param *par)
0134 {
0135     const struct ebt_among_info *info = par->matchinfo;
0136     const char *dmac, *smac;
0137     const struct ebt_mac_wormhash *wh_dst, *wh_src;
0138     __be32 dip = 0, sip = 0;
0139 
0140     wh_dst = ebt_among_wh_dst(info);
0141     wh_src = ebt_among_wh_src(info);
0142 
0143     if (wh_src) {
0144         smac = eth_hdr(skb)->h_source;
0145         if (get_ip_src(skb, &sip))
0146             return false;
0147         if (!(info->bitmask & EBT_AMONG_SRC_NEG)) {
0148             /* we match only if it contains */
0149             if (!ebt_mac_wormhash_contains(wh_src, smac, sip))
0150                 return false;
0151         } else {
0152             /* we match only if it DOES NOT contain */
0153             if (ebt_mac_wormhash_contains(wh_src, smac, sip))
0154                 return false;
0155         }
0156     }
0157 
0158     if (wh_dst) {
0159         dmac = eth_hdr(skb)->h_dest;
0160         if (get_ip_dst(skb, &dip))
0161             return false;
0162         if (!(info->bitmask & EBT_AMONG_DST_NEG)) {
0163             /* we match only if it contains */
0164             if (!ebt_mac_wormhash_contains(wh_dst, dmac, dip))
0165                 return false;
0166         } else {
0167             /* we match only if it DOES NOT contain */
0168             if (ebt_mac_wormhash_contains(wh_dst, dmac, dip))
0169                 return false;
0170         }
0171     }
0172 
0173     return true;
0174 }
0175 
0176 static bool poolsize_invalid(const struct ebt_mac_wormhash *w)
0177 {
0178     return w && w->poolsize >= (INT_MAX / sizeof(struct ebt_mac_wormhash_tuple));
0179 }
0180 
0181 static bool wormhash_offset_invalid(int off, unsigned int len)
0182 {
0183     if (off == 0) /* not present */
0184         return false;
0185 
0186     if (off < (int)sizeof(struct ebt_among_info) ||
0187         off % __alignof__(struct ebt_mac_wormhash))
0188         return true;
0189 
0190     off += sizeof(struct ebt_mac_wormhash);
0191 
0192     return off > len;
0193 }
0194 
0195 static bool wormhash_sizes_valid(const struct ebt_mac_wormhash *wh, int a, int b)
0196 {
0197     if (a == 0)
0198         a = sizeof(struct ebt_among_info);
0199 
0200     return ebt_mac_wormhash_size(wh) + a == b;
0201 }
0202 
0203 static int ebt_among_mt_check(const struct xt_mtchk_param *par)
0204 {
0205     const struct ebt_among_info *info = par->matchinfo;
0206     const struct ebt_entry_match *em =
0207         container_of(par->matchinfo, const struct ebt_entry_match, data);
0208     unsigned int expected_length = sizeof(struct ebt_among_info);
0209     const struct ebt_mac_wormhash *wh_dst, *wh_src;
0210     int err;
0211 
0212     if (expected_length > em->match_size)
0213         return -EINVAL;
0214 
0215     if (wormhash_offset_invalid(info->wh_dst_ofs, em->match_size) ||
0216         wormhash_offset_invalid(info->wh_src_ofs, em->match_size))
0217         return -EINVAL;
0218 
0219     wh_dst = ebt_among_wh_dst(info);
0220     if (poolsize_invalid(wh_dst))
0221         return -EINVAL;
0222 
0223     expected_length += ebt_mac_wormhash_size(wh_dst);
0224     if (expected_length > em->match_size)
0225         return -EINVAL;
0226 
0227     wh_src = ebt_among_wh_src(info);
0228     if (poolsize_invalid(wh_src))
0229         return -EINVAL;
0230 
0231     if (info->wh_src_ofs < info->wh_dst_ofs) {
0232         if (!wormhash_sizes_valid(wh_src, info->wh_src_ofs, info->wh_dst_ofs))
0233             return -EINVAL;
0234     } else {
0235         if (!wormhash_sizes_valid(wh_dst, info->wh_dst_ofs, info->wh_src_ofs))
0236             return -EINVAL;
0237     }
0238 
0239     expected_length += ebt_mac_wormhash_size(wh_src);
0240 
0241     if (em->match_size != EBT_ALIGN(expected_length)) {
0242         pr_err_ratelimited("wrong size: %d against expected %d, rounded to %zd\n",
0243                    em->match_size, expected_length,
0244                    EBT_ALIGN(expected_length));
0245         return -EINVAL;
0246     }
0247     if (wh_dst && (err = ebt_mac_wormhash_check_integrity(wh_dst))) {
0248         pr_err_ratelimited("dst integrity fail: %x\n", -err);
0249         return -EINVAL;
0250     }
0251     if (wh_src && (err = ebt_mac_wormhash_check_integrity(wh_src))) {
0252         pr_err_ratelimited("src integrity fail: %x\n", -err);
0253         return -EINVAL;
0254     }
0255     return 0;
0256 }
0257 
0258 static struct xt_match ebt_among_mt_reg __read_mostly = {
0259     .name       = "among",
0260     .revision   = 0,
0261     .family     = NFPROTO_BRIDGE,
0262     .match      = ebt_among_mt,
0263     .checkentry = ebt_among_mt_check,
0264     .matchsize  = -1, /* special case */
0265     .me     = THIS_MODULE,
0266 };
0267 
0268 static int __init ebt_among_init(void)
0269 {
0270     return xt_register_match(&ebt_among_mt_reg);
0271 }
0272 
0273 static void __exit ebt_among_fini(void)
0274 {
0275     xt_unregister_match(&ebt_among_mt_reg);
0276 }
0277 
0278 module_init(ebt_among_init);
0279 module_exit(ebt_among_fini);
0280 MODULE_DESCRIPTION("Ebtables: Combined MAC/IP address list matching");
0281 MODULE_LICENSE("GPL");