0001
0002
0003
0004
0005
0006
0007
0008
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
0023
0024
0025
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
0149 if (!ebt_mac_wormhash_contains(wh_src, smac, sip))
0150 return false;
0151 } else {
0152
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
0164 if (!ebt_mac_wormhash_contains(wh_dst, dmac, dip))
0165 return false;
0166 } else {
0167
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)
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,
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");