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