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 17 static 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 55 static 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 73 static int get_ip_dst(const struct sk_buff *skb, __be32 *addr) 74 { 75 if (eth_hdr(skb)->h_proto == htons(ETH_P_IP)) { 76 const struct iphdr *ih; 77 struct iphdr _iph; 78 79 ih = skb_header_pointer(skb, 0, sizeof(_iph), &_iph); 80 if (ih == NULL) 81 return -1; 82 *addr = ih->daddr; 83 } else if (eth_hdr(skb)->h_proto == htons(ETH_P_ARP)) { 84 const struct arphdr *ah; 85 struct arphdr _arph; 86 const __be32 *bp; 87 __be32 buf; 88 89 ah = skb_header_pointer(skb, 0, sizeof(_arph), &_arph); 90 if (ah == NULL || 91 ah->ar_pln != sizeof(__be32) || 92 ah->ar_hln != ETH_ALEN) 93 return -1; 94 bp = skb_header_pointer(skb, sizeof(struct arphdr) + 95 2 * ETH_ALEN + sizeof(__be32), 96 sizeof(__be32), &buf); 97 if (bp == NULL) 98 return -1; 99 *addr = *bp; 100 } 101 return 0; 102 } 103 104 static int get_ip_src(const struct sk_buff *skb, __be32 *addr) 105 { 106 if (eth_hdr(skb)->h_proto == htons(ETH_P_IP)) { 107 const struct iphdr *ih; 108 struct iphdr _iph; 109 110 ih = skb_header_pointer(skb, 0, sizeof(_iph), &_iph); 111 if (ih == NULL) 112 return -1; 113 *addr = ih->saddr; 114 } else if (eth_hdr(skb)->h_proto == htons(ETH_P_ARP)) { 115 const struct arphdr *ah; 116 struct arphdr _arph; 117 const __be32 *bp; 118 __be32 buf; 119 120 ah = skb_header_pointer(skb, 0, sizeof(_arph), &_arph); 121 if (ah == NULL || 122 ah->ar_pln != sizeof(__be32) || 123 ah->ar_hln != ETH_ALEN) 124 return -1; 125 bp = skb_header_pointer(skb, sizeof(struct arphdr) + 126 ETH_ALEN, sizeof(__be32), &buf); 127 if (bp == NULL) 128 return -1; 129 *addr = *bp; 130 } 131 return 0; 132 } 133 134 static int ebt_filter_among(const struct sk_buff *skb, 135 const struct net_device *in, 136 const struct net_device *out, const void *data, 137 unsigned int datalen) 138 { 139 const struct ebt_among_info *info = data; 140 const char *dmac, *smac; 141 const struct ebt_mac_wormhash *wh_dst, *wh_src; 142 __be32 dip = 0, sip = 0; 143 144 wh_dst = ebt_among_wh_dst(info); 145 wh_src = ebt_among_wh_src(info); 146 147 if (wh_src) { 148 smac = eth_hdr(skb)->h_source; 149 if (get_ip_src(skb, &sip)) 150 return EBT_NOMATCH; 151 if (!(info->bitmask & EBT_AMONG_SRC_NEG)) { 152 /* we match only if it contains */ 153 if (!ebt_mac_wormhash_contains(wh_src, smac, sip)) 154 return EBT_NOMATCH; 155 } else { 156 /* we match only if it DOES NOT contain */ 157 if (ebt_mac_wormhash_contains(wh_src, smac, sip)) 158 return EBT_NOMATCH; 159 } 160 } 161 162 if (wh_dst) { 163 dmac = eth_hdr(skb)->h_dest; 164 if (get_ip_dst(skb, &dip)) 165 return EBT_NOMATCH; 166 if (!(info->bitmask & EBT_AMONG_DST_NEG)) { 167 /* we match only if it contains */ 168 if (!ebt_mac_wormhash_contains(wh_dst, dmac, dip)) 169 return EBT_NOMATCH; 170 } else { 171 /* we match only if it DOES NOT contain */ 172 if (ebt_mac_wormhash_contains(wh_dst, dmac, dip)) 173 return EBT_NOMATCH; 174 } 175 } 176 177 return EBT_MATCH; 178 } 179 180 static int ebt_among_check(const char *tablename, unsigned int hookmask, 181 const struct ebt_entry *e, void *data, 182 unsigned int datalen) 183 { 184 const struct ebt_among_info *info = data; 185 int expected_length = sizeof(struct ebt_among_info); 186 const struct ebt_mac_wormhash *wh_dst, *wh_src; 187 int err; 188 189 wh_dst = ebt_among_wh_dst(info); 190 wh_src = ebt_among_wh_src(info); 191 expected_length += ebt_mac_wormhash_size(wh_dst); 192 expected_length += ebt_mac_wormhash_size(wh_src); 193 194 if (datalen != EBT_ALIGN(expected_length)) { 195 printk(KERN_WARNING 196 "ebtables: among: wrong size: %d " 197 "against expected %d, rounded to %Zd\n", 198 datalen, expected_length, 199 EBT_ALIGN(expected_length)); 200 return -EINVAL; 201 } 202 if (wh_dst && (err = ebt_mac_wormhash_check_integrity(wh_dst))) { 203 printk(KERN_WARNING 204 "ebtables: among: dst integrity fail: %x\n", -err); 205 return -EINVAL; 206 } 207 if (wh_src && (err = ebt_mac_wormhash_check_integrity(wh_src))) { 208 printk(KERN_WARNING 209 "ebtables: among: src integrity fail: %x\n", -err); 210 return -EINVAL; 211 } 212 return 0; 213 } 214 215 static struct ebt_match filter_among = { 216 .name = EBT_AMONG_MATCH, 217 .match = ebt_filter_among, 218 .check = ebt_among_check, 219 .me = THIS_MODULE, 220 }; 221 222 static int __init ebt_among_init(void) 223 { 224 return ebt_register_match(&filter_among); 225 } 226 227 static void __exit ebt_among_fini(void) 228 { 229 ebt_unregister_match(&filter_among); 230 } 231 232 module_init(ebt_among_init); 233 module_exit(ebt_among_fini); 234 MODULE_LICENSE("GPL"); 235