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 18static 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 52static 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 70static 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 101static 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 131static bool 132ebt_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 175static int ebt_among_mt_check(const struct xt_mtchk_param *par) 176{ 177 const struct ebt_among_info *info = par->matchinfo; 178 const struct ebt_entry_match *em = 179 container_of(par->matchinfo, const struct ebt_entry_match, data); 180 int expected_length = sizeof(struct ebt_among_info); 181 const struct ebt_mac_wormhash *wh_dst, *wh_src; 182 int err; 183 184 wh_dst = ebt_among_wh_dst(info); 185 wh_src = ebt_among_wh_src(info); 186 expected_length += ebt_mac_wormhash_size(wh_dst); 187 expected_length += ebt_mac_wormhash_size(wh_src); 188 189 if (em->match_size != EBT_ALIGN(expected_length)) { 190 pr_info("wrong size: %d against expected %d, rounded to %Zd\n", 191 em->match_size, expected_length, 192 EBT_ALIGN(expected_length)); 193 return -EINVAL; 194 } 195 if (wh_dst && (err = ebt_mac_wormhash_check_integrity(wh_dst))) { 196 pr_info("dst integrity fail: %x\n", -err); 197 return -EINVAL; 198 } 199 if (wh_src && (err = ebt_mac_wormhash_check_integrity(wh_src))) { 200 pr_info("src integrity fail: %x\n", -err); 201 return -EINVAL; 202 } 203 return 0; 204} 205 206static struct xt_match ebt_among_mt_reg __read_mostly = { 207 .name = "among", 208 .revision = 0, 209 .family = NFPROTO_BRIDGE, 210 .match = ebt_among_mt, 211 .checkentry = ebt_among_mt_check, 212 .matchsize = -1, /* special case */ 213 .me = THIS_MODULE, 214}; 215 216static int __init ebt_among_init(void) 217{ 218 return xt_register_match(&ebt_among_mt_reg); 219} 220 221static void __exit ebt_among_fini(void) 222{ 223 xt_unregister_match(&ebt_among_mt_reg); 224} 225 226module_init(ebt_among_init); 227module_exit(ebt_among_fini); 228MODULE_DESCRIPTION("Ebtables: Combined MAC/IP address list matching"); 229MODULE_LICENSE("GPL"); 230