ebt_among.c (7078B)
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");