1/* 2 * ebt_among 3 * 4 * Authors: 5 * Grzegorz Borowiak <grzes@gnu.univ.gda.pl> 6 * 7 * August, 2003 8 * 9 */ 10 11#include <linux/netfilter_bridge/ebtables.h> 12#include <linux/netfilter_bridge/ebt_among.h> 13#include <linux/ip.h> 14#include <linux/if_arp.h> 15#include <linux/module.h> 16 17static int ebt_mac_wormhash_contains(const struct ebt_mac_wormhash *wh, 18 const char *mac, __be32 ip) 19{ 20 /* You may be puzzled as to how this code works. 21 * Some tricks were used, refer to 22 * include/linux/netfilter_bridge/ebt_among.h 23 * as there you can find a solution of this mystery. 24 */ 25 const struct ebt_mac_wormhash_tuple *p; 26 int start, limit, i; 27 uint32_t cmp[2] = { 0, 0 }; 28 int key = (const unsigned char) mac[5]; 29 30 memcpy(((char *) cmp) + 2, mac, 6); 31 start = wh->table[key]; 32 limit = wh->table[key + 1]; 33 if (ip) { 34 for (i = start; i < limit; i++) { 35 p = &wh->pool[i]; 36 if (cmp[1] == p->cmp[1] && cmp[0] == p->cmp[0]) { 37 if (p->ip == 0 || p->ip == ip) { 38 return 1; 39 } 40 } 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 1; 48 } 49 } 50 } 51 } 52 return 0; 53} 54 55static int ebt_mac_wormhash_check_integrity(const struct ebt_mac_wormhash 56 *wh) 57{ 58 int i; 59 60 for (i = 0; i < 256; i++) { 61 if (wh->table[i] > wh->table[i + 1]) 62 return -0x100 - i; 63 if (wh->table[i] < 0) 64 return -0x200 - i; 65 if (wh->table[i] > wh->poolsize) 66 return -0x300 - i; 67 } 68 if (wh->table[256] > wh->poolsize) 69 return -0xc00; 70 return 0; 71} 72 73static int get_ip_dst(const struct sk_buff *skb, __be32 *addr) 74{ 75 if (eth_hdr(skb)->h_proto == htons(ETH_P_IP)) { 76 struct iphdr _iph, *ih; 77 78 ih = skb_header_pointer(skb, 0, sizeof(_iph), &_iph); 79 if (ih == NULL) 80 return -1; 81 *addr = ih->daddr; 82 } else if (eth_hdr(skb)->h_proto == htons(ETH_P_ARP)) { 83 struct arphdr _arph, *ah; 84 __be32 buf, *bp; 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 struct iphdr _iph, *ih; 105 106 ih = skb_header_pointer(skb, 0, sizeof(_iph), &_iph); 107 if (ih == NULL) 108 return -1; 109 *addr = ih->saddr; 110 } else if (eth_hdr(skb)->h_proto == htons(ETH_P_ARP)) { 111 struct arphdr _arph, *ah; 112 __be32 buf, *bp; 113 114 ah = skb_header_pointer(skb, 0, sizeof(_arph), &_arph); 115 if (ah == NULL || 116 ah->ar_pln != sizeof(__be32) || 117 ah->ar_hln != ETH_ALEN) 118 return -1; 119 bp = skb_header_pointer(skb, sizeof(struct arphdr) + 120 ETH_ALEN, sizeof(__be32), &buf); 121 if (bp == NULL) 122 return -1; 123 *addr = *bp; 124 } 125 return 0; 126} 127 128static int ebt_filter_among(const struct sk_buff *skb, 129 const struct net_device *in, 130 const struct net_device *out, const void *data, 131 unsigned int datalen) 132{ 133 struct ebt_among_info *info = (struct ebt_among_info *) data; 134 const char *dmac, *smac; 135 const struct ebt_mac_wormhash *wh_dst, *wh_src; 136 __be32 dip = 0, sip = 0; 137 138 wh_dst = ebt_among_wh_dst(info); 139 wh_src = ebt_among_wh_src(info); 140 141 if (wh_src) { 142 smac = eth_hdr(skb)->h_source; 143 if (get_ip_src(skb, &sip)) 144 return EBT_NOMATCH; 145 if (!(info->bitmask & EBT_AMONG_SRC_NEG)) { 146 /* we match only if it contains */ 147 if (!ebt_mac_wormhash_contains(wh_src, smac, sip)) 148 return EBT_NOMATCH; 149 } else { 150 /* we match only if it DOES NOT contain */ 151 if (ebt_mac_wormhash_contains(wh_src, smac, sip)) 152 return EBT_NOMATCH; 153 } 154 } 155 156 if (wh_dst) { 157 dmac = eth_hdr(skb)->h_dest; 158 if (get_ip_dst(skb, &dip)) 159 return EBT_NOMATCH; 160 if (!(info->bitmask & EBT_AMONG_DST_NEG)) { 161 /* we match only if it contains */ 162 if (!ebt_mac_wormhash_contains(wh_dst, dmac, dip)) 163 return EBT_NOMATCH; 164 } else { 165 /* we match only if it DOES NOT contain */ 166 if (ebt_mac_wormhash_contains(wh_dst, dmac, dip)) 167 return EBT_NOMATCH; 168 } 169 } 170 171 return EBT_MATCH; 172} 173 174static int ebt_among_check(const char *tablename, unsigned int hookmask, 175 const struct ebt_entry *e, void *data, 176 unsigned int datalen) 177{ 178 struct ebt_among_info *info = (struct ebt_among_info *) data; 179 int expected_length = sizeof(struct ebt_among_info); 180 const struct ebt_mac_wormhash *wh_dst, *wh_src; 181 int err; 182 183 wh_dst = ebt_among_wh_dst(info); 184 wh_src = ebt_among_wh_src(info); 185 expected_length += ebt_mac_wormhash_size(wh_dst); 186 expected_length += ebt_mac_wormhash_size(wh_src); 187 188 if (datalen != EBT_ALIGN(expected_length)) { 189 printk(KERN_WARNING 190 "ebtables: among: wrong size: %d" 191 "against expected %d, rounded to %Zd\n", 192 datalen, expected_length, 193 EBT_ALIGN(expected_length)); 194 return -EINVAL; 195 } 196 if (wh_dst && (err = ebt_mac_wormhash_check_integrity(wh_dst))) { 197 printk(KERN_WARNING 198 "ebtables: among: dst integrity fail: %x\n", -err); 199 return -EINVAL; 200 } 201 if (wh_src && (err = ebt_mac_wormhash_check_integrity(wh_src))) { 202 printk(KERN_WARNING 203 "ebtables: among: src integrity fail: %x\n", -err); 204 return -EINVAL; 205 } 206 return 0; 207} 208 209static struct ebt_match filter_among = { 210 .name = EBT_AMONG_MATCH, 211 .match = ebt_filter_among, 212 .check = ebt_among_check, 213 .me = THIS_MODULE, 214}; 215 216static int __init ebt_among_init(void) 217{ 218 return ebt_register_match(&filter_among); 219} 220 221static void __exit ebt_among_fini(void) 222{ 223 ebt_unregister_match(&filter_among); 224} 225 226module_init(ebt_among_init); 227module_exit(ebt_among_fini); 228MODULE_LICENSE("GPL"); 229