Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * ebt_among | |
3 | * | |
4 | * Authors: | |
5 | * Grzegorz Borowiak <grzes@gnu.univ.gda.pl> | |
6 | * | |
7 | * August, 2003 | |
8 | * | |
9 | */ | |
1da177e4 LT |
10 | #include <linux/ip.h> |
11 | #include <linux/if_arp.h> | |
12 | #include <linux/module.h> | |
043ef46c JE |
13 | #include <linux/netfilter/x_tables.h> |
14 | #include <linux/netfilter_bridge/ebtables.h> | |
15 | #include <linux/netfilter_bridge/ebt_among.h> | |
1da177e4 | 16 | |
8cc784ee JE |
17 | static bool ebt_mac_wormhash_contains(const struct ebt_mac_wormhash *wh, |
18 | const char *mac, __be32 ip) | |
1da177e4 LT |
19 | { |
20 | /* You may be puzzled as to how this code works. | |
9d6f229f | 21 | * Some tricks were used, refer to |
1da177e4 LT |
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 }; | |
abfdf1c4 | 28 | int key = ((const unsigned char *)mac)[5]; |
1da177e4 LT |
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]; | |
8cc784ee JE |
36 | if (cmp[1] == p->cmp[1] && cmp[0] == p->cmp[0]) |
37 | if (p->ip == 0 || p->ip == ip) | |
38 | return true; | |
1da177e4 LT |
39 | } |
40 | } else { | |
41 | for (i = start; i < limit; i++) { | |
42 | p = &wh->pool[i]; | |
8cc784ee JE |
43 | if (cmp[1] == p->cmp[1] && cmp[0] == p->cmp[0]) |
44 | if (p->ip == 0) | |
45 | return true; | |
1da177e4 LT |
46 | } |
47 | } | |
8cc784ee | 48 | return false; |
1da177e4 LT |
49 | } |
50 | ||
51 | static int ebt_mac_wormhash_check_integrity(const struct ebt_mac_wormhash | |
52 | *wh) | |
53 | { | |
54 | int i; | |
55 | ||
56 | for (i = 0; i < 256; i++) { | |
57 | if (wh->table[i] > wh->table[i + 1]) | |
58 | return -0x100 - i; | |
59 | if (wh->table[i] < 0) | |
60 | return -0x200 - i; | |
61 | if (wh->table[i] > wh->poolsize) | |
62 | return -0x300 - i; | |
63 | } | |
64 | if (wh->table[256] > wh->poolsize) | |
65 | return -0xc00; | |
66 | return 0; | |
67 | } | |
68 | ||
47c183fa | 69 | static int get_ip_dst(const struct sk_buff *skb, __be32 *addr) |
1da177e4 LT |
70 | { |
71 | if (eth_hdr(skb)->h_proto == htons(ETH_P_IP)) { | |
abfdf1c4 JE |
72 | const struct iphdr *ih; |
73 | struct iphdr _iph; | |
1da177e4 LT |
74 | |
75 | ih = skb_header_pointer(skb, 0, sizeof(_iph), &_iph); | |
76 | if (ih == NULL) | |
77 | return -1; | |
78 | *addr = ih->daddr; | |
79 | } else if (eth_hdr(skb)->h_proto == htons(ETH_P_ARP)) { | |
abfdf1c4 JE |
80 | const struct arphdr *ah; |
81 | struct arphdr _arph; | |
82 | const __be32 *bp; | |
83 | __be32 buf; | |
1da177e4 LT |
84 | |
85 | ah = skb_header_pointer(skb, 0, sizeof(_arph), &_arph); | |
86 | if (ah == NULL || | |
47c183fa | 87 | ah->ar_pln != sizeof(__be32) || |
1da177e4 LT |
88 | ah->ar_hln != ETH_ALEN) |
89 | return -1; | |
90 | bp = skb_header_pointer(skb, sizeof(struct arphdr) + | |
47c183fa AV |
91 | 2 * ETH_ALEN + sizeof(__be32), |
92 | sizeof(__be32), &buf); | |
1da177e4 LT |
93 | if (bp == NULL) |
94 | return -1; | |
95 | *addr = *bp; | |
96 | } | |
97 | return 0; | |
98 | } | |
99 | ||
47c183fa | 100 | static int get_ip_src(const struct sk_buff *skb, __be32 *addr) |
1da177e4 LT |
101 | { |
102 | if (eth_hdr(skb)->h_proto == htons(ETH_P_IP)) { | |
abfdf1c4 JE |
103 | const struct iphdr *ih; |
104 | struct iphdr _iph; | |
1da177e4 LT |
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)) { | |
abfdf1c4 JE |
111 | const struct arphdr *ah; |
112 | struct arphdr _arph; | |
113 | const __be32 *bp; | |
114 | __be32 buf; | |
1da177e4 LT |
115 | |
116 | ah = skb_header_pointer(skb, 0, sizeof(_arph), &_arph); | |
117 | if (ah == NULL || | |
47c183fa | 118 | ah->ar_pln != sizeof(__be32) || |
1da177e4 LT |
119 | ah->ar_hln != ETH_ALEN) |
120 | return -1; | |
121 | bp = skb_header_pointer(skb, sizeof(struct arphdr) + | |
47c183fa | 122 | ETH_ALEN, sizeof(__be32), &buf); |
1da177e4 LT |
123 | if (bp == NULL) |
124 | return -1; | |
125 | *addr = *bp; | |
126 | } | |
127 | return 0; | |
128 | } | |
129 | ||
2d06d4a5 JE |
130 | static bool |
131 | ebt_among_mt(const struct sk_buff *skb, const struct net_device *in, | |
132 | const struct net_device *out, const struct xt_match *match, | |
133 | const void *data, int offset, unsigned int protoff, bool *hotdrop) | |
1da177e4 | 134 | { |
abfdf1c4 | 135 | const struct ebt_among_info *info = data; |
1da177e4 LT |
136 | const char *dmac, *smac; |
137 | const struct ebt_mac_wormhash *wh_dst, *wh_src; | |
47c183fa | 138 | __be32 dip = 0, sip = 0; |
1da177e4 LT |
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)) | |
8cc784ee | 146 | return false; |
1da177e4 LT |
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)) | |
8cc784ee | 150 | return false; |
1da177e4 LT |
151 | } else { |
152 | /* we match only if it DOES NOT contain */ | |
153 | if (ebt_mac_wormhash_contains(wh_src, smac, sip)) | |
8cc784ee | 154 | return false; |
1da177e4 LT |
155 | } |
156 | } | |
157 | ||
158 | if (wh_dst) { | |
159 | dmac = eth_hdr(skb)->h_dest; | |
160 | if (get_ip_dst(skb, &dip)) | |
8cc784ee | 161 | return false; |
1da177e4 LT |
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)) | |
8cc784ee | 165 | return false; |
1da177e4 LT |
166 | } else { |
167 | /* we match only if it DOES NOT contain */ | |
168 | if (ebt_mac_wormhash_contains(wh_dst, dmac, dip)) | |
8cc784ee | 169 | return false; |
1da177e4 LT |
170 | } |
171 | } | |
172 | ||
8cc784ee | 173 | return true; |
1da177e4 LT |
174 | } |
175 | ||
19eda879 | 176 | static bool |
2d06d4a5 JE |
177 | ebt_among_mt_check(const char *table, const void *entry, |
178 | const struct xt_match *match, void *data, | |
179 | unsigned int hook_mask) | |
1da177e4 | 180 | { |
815377fe JE |
181 | const struct ebt_entry_match *em = |
182 | container_of(data, const struct ebt_entry_match, data); | |
abfdf1c4 | 183 | const struct ebt_among_info *info = data; |
1da177e4 LT |
184 | int expected_length = sizeof(struct ebt_among_info); |
185 | const struct ebt_mac_wormhash *wh_dst, *wh_src; | |
186 | int err; | |
187 | ||
188 | wh_dst = ebt_among_wh_dst(info); | |
189 | wh_src = ebt_among_wh_src(info); | |
190 | expected_length += ebt_mac_wormhash_size(wh_dst); | |
191 | expected_length += ebt_mac_wormhash_size(wh_src); | |
192 | ||
815377fe | 193 | if (em->match_size != EBT_ALIGN(expected_length)) { |
1da177e4 | 194 | printk(KERN_WARNING |
3a47a68b | 195 | "ebtables: among: wrong size: %d " |
1da177e4 | 196 | "against expected %d, rounded to %Zd\n", |
815377fe | 197 | em->match_size, expected_length, |
1da177e4 | 198 | EBT_ALIGN(expected_length)); |
19eda879 | 199 | return false; |
1da177e4 LT |
200 | } |
201 | if (wh_dst && (err = ebt_mac_wormhash_check_integrity(wh_dst))) { | |
202 | printk(KERN_WARNING | |
203 | "ebtables: among: dst integrity fail: %x\n", -err); | |
19eda879 | 204 | return false; |
1da177e4 LT |
205 | } |
206 | if (wh_src && (err = ebt_mac_wormhash_check_integrity(wh_src))) { | |
207 | printk(KERN_WARNING | |
208 | "ebtables: among: src integrity fail: %x\n", -err); | |
19eda879 | 209 | return false; |
1da177e4 | 210 | } |
19eda879 | 211 | return true; |
1da177e4 LT |
212 | } |
213 | ||
043ef46c JE |
214 | static struct xt_match ebt_among_mt_reg __read_mostly = { |
215 | .name = "among", | |
001a18d3 JE |
216 | .revision = 0, |
217 | .family = NFPROTO_BRIDGE, | |
2d06d4a5 JE |
218 | .match = ebt_among_mt, |
219 | .checkentry = ebt_among_mt_check, | |
18219d3f | 220 | .matchsize = -1, /* special case */ |
1da177e4 LT |
221 | .me = THIS_MODULE, |
222 | }; | |
223 | ||
65b4b4e8 | 224 | static int __init ebt_among_init(void) |
1da177e4 | 225 | { |
043ef46c | 226 | return xt_register_match(&ebt_among_mt_reg); |
1da177e4 LT |
227 | } |
228 | ||
65b4b4e8 | 229 | static void __exit ebt_among_fini(void) |
1da177e4 | 230 | { |
043ef46c | 231 | xt_unregister_match(&ebt_among_mt_reg); |
1da177e4 LT |
232 | } |
233 | ||
65b4b4e8 AM |
234 | module_init(ebt_among_init); |
235 | module_exit(ebt_among_fini); | |
f776c4cd | 236 | MODULE_DESCRIPTION("Ebtables: Combined MAC/IP address list matching"); |
1da177e4 | 237 | MODULE_LICENSE("GPL"); |