blob: f451735125d8fb4c6dc68fbc7b534d698e622dcb [file] [log] [blame]
Bart De Schuymer9cc9bfa2003-09-02 22:43:25 +00001#include <stdio.h>
2#include <string.h>
3#include <stdlib.h>
4#include <getopt.h>
5#include <ctype.h>
6#include <netinet/ether.h>
7#include "../include/ebtables_u.h"
8#include "../include/ethernetdb.h"
9#include <linux/if_ether.h>
10#include <linux/netfilter_bridge/ebt_among.h>
11
gborowiak6c6d7312003-09-16 19:26:38 +000012#define NODEBUG
Bart De Schuymer9cc9bfa2003-09-02 22:43:25 +000013
14#define AMONG_DST '1'
15#define AMONG_SRC '2'
16
gborowiak6c6d7312003-09-16 19:26:38 +000017static struct option opts[] = {
18 {"among-dst", required_argument, 0, AMONG_DST},
19 {"among-src", required_argument, 0, AMONG_SRC},
20 {0}
Bart De Schuymer9cc9bfa2003-09-02 22:43:25 +000021};
22
23#ifdef DEBUG
24static void hexdump(const void *mem, int howmany)
25{
26 printf("\n");
27 const unsigned char *p = mem;
28 int i;
29 for (i = 0; i < howmany; i++) {
30 if (i % 32 == 0) {
31 printf("\n%04x: ", i);
32 }
gborowiak6c6d7312003-09-16 19:26:38 +000033 printf("%2.2x%c", p[i], ". "[i % 4 == 3]);
Bart De Schuymer9cc9bfa2003-09-02 22:43:25 +000034 }
35 printf("\n");
36}
gborowiak6c6d7312003-09-16 19:26:38 +000037#endif /* DEBUG */
Bart De Schuymer9cc9bfa2003-09-02 22:43:25 +000038
39static void print_help()
40{
41 printf(
42"`among' options:\n"
43"--among-dst list : matches if ether dst is in list\n"
44"--among-src list : matches if ether src is in list\n"
45"list has form:\n"
gborowiak6c6d7312003-09-16 19:26:38 +000046" xx:xx:xx:xx:xx:xx[=ip.ip.ip.ip],yy:yy:yy:yy:yy:yy[=ip.ip.ip.ip]"
47",...,zz:zz:zz:zz:zz:zz[=ip.ip.ip.ip][,]\n"
gborowiakc50ce6a2003-09-07 13:16:26 +000048"Things in brackets are optional.\n"
gborowiak6c6d7312003-09-16 19:26:38 +000049"If you want to allow two (or more) IP addresses to one MAC address, you \n"
50"can specify two (or more) pairs witch the same MAC, e.g.\n"
gborowiakc50ce6a2003-09-07 13:16:26 +000051" 00:00:00:fa:eb:fe=153.19.120.250,00:00:00:fa:eb:fe=192.168.0.1\n"
Bart De Schuymer9cc9bfa2003-09-02 22:43:25 +000052 );
53}
54
55static void init(struct ebt_entry_match *match)
56{
gborowiak6c6d7312003-09-16 19:26:38 +000057 struct ebt_among_info *amonginfo =
58 (struct ebt_among_info *) match->data;
Bart De Schuymer9cc9bfa2003-09-02 22:43:25 +000059
60 memset(amonginfo, 0, sizeof(struct ebt_among_info));
61}
62
gborowiakc50ce6a2003-09-07 13:16:26 +000063static struct ebt_mac_wormhash *new_wormhash(int n)
Bart De Schuymer9cc9bfa2003-09-02 22:43:25 +000064{
gborowiak6c6d7312003-09-16 19:26:38 +000065 int size =
66 sizeof(struct ebt_mac_wormhash) +
67 n * sizeof(struct ebt_mac_wormhash_tuple);
68 struct ebt_mac_wormhash *result =
69 (struct ebt_mac_wormhash *) malloc(size);
gborowiakc50ce6a2003-09-07 13:16:26 +000070 memset(result, 0, size);
71 result->poolsize = n;
72 return result;
Bart De Schuymer9cc9bfa2003-09-02 22:43:25 +000073}
74
gborowiak6c6d7312003-09-16 19:26:38 +000075static void copy_wormhash(struct ebt_mac_wormhash *d,
76 const struct ebt_mac_wormhash *s)
gborowiakc50ce6a2003-09-07 13:16:26 +000077{
78 int dpoolsize = d->poolsize;
79 int dsize, ssize, amount;
80 dsize = ebt_mac_wormhash_size(d);
81 ssize = ebt_mac_wormhash_size(s);
82 amount = dsize < ssize ? dsize : ssize;
83 memcpy(d, s, amount);
84 d->poolsize = dpoolsize;
85}
86
87/* Returns:
88 * -1 when '\0' reached
89 * -2 when `n' bytes read and no delimiter found
90 * 0 when no less than `n' bytes read and delimiter found
91 * if `destbuf' is not NULL, it is filled by read bytes and ended with '\0'
92 * *pp is set on the first byte not copied to `destbuf'
93 */
gborowiak6c6d7312003-09-16 19:26:38 +000094static int read_until(const char **pp, const char *delimiters,
95 char *destbuf, int n)
gborowiakc50ce6a2003-09-07 13:16:26 +000096{
97 int count = 0;
98 int ret = 0;
99 char c;
100 while (1) {
101 c = **pp;
102 if (!c) {
103 ret = -1;
104 break;
105 }
106 if (strchr(delimiters, c)) {
107 ret = 0;
108 break;
109 }
110 if (count == n) {
111 ret = -2;
112 break;
113 }
gborowiak6c6d7312003-09-16 19:26:38 +0000114 if (destbuf)
115 destbuf[count++] = c;
gborowiakc50ce6a2003-09-07 13:16:26 +0000116 (*pp)++;
117 }
gborowiak6c6d7312003-09-16 19:26:38 +0000118 if (destbuf)
119 destbuf[count] = 0;
gborowiakc50ce6a2003-09-07 13:16:26 +0000120 return ret;
121}
122
gborowiak6c6d7312003-09-16 19:26:38 +0000123static int fcmp(const void *va, const void *vb) {
124 const struct ebt_mac_wormhash_tuple *a = va;
125 const struct ebt_mac_wormhash_tuple *b = vb;
126 int ca = ((const unsigned char*)a->cmp)[7];
127 int cb = ((const unsigned char*)b->cmp)[7];
128 return ca - cb;
129}
130
131static void index_table(struct ebt_mac_wormhash *wh)
132{
133 int ipool, itable;
134 int c;
135 for (itable = 0; itable <= 256; itable++) {
136 wh->table[itable] = wh->poolsize;
137 }
138 ipool = 0;
139 itable = 0;
140 while (1) {
141 wh->table[itable] = ipool;
142 c = ((const unsigned char*)wh->pool[ipool].cmp)[7];
143 if (itable <= c) {
144 itable++;
145 } else {
146 ipool++;
147 }
148 if (ipool > wh->poolsize)
149 break;
150 }
151}
152
gborowiakc50ce6a2003-09-07 13:16:26 +0000153static struct ebt_mac_wormhash *create_wormhash(const char *arg)
Bart De Schuymer9cc9bfa2003-09-02 22:43:25 +0000154{
155 const char *pc = arg;
156 const char *anchor;
gborowiakc50ce6a2003-09-07 13:16:26 +0000157 char *endptr;
158 struct ebt_mac_wormhash *workcopy, *result, *h;
159 unsigned char mac[6];
160 unsigned char ip[4];
Bart De Schuymer9cc9bfa2003-09-02 22:43:25 +0000161 int nmacs = 0;
gborowiakc50ce6a2003-09-07 13:16:26 +0000162 int i;
163 char token[4];
164 if (!(workcopy = new_wormhash(1024))) {
gborowiak6c6d7312003-09-16 19:26:38 +0000165 print_memory();
gborowiakc50ce6a2003-09-07 13:16:26 +0000166 }
Bart De Schuymer9cc9bfa2003-09-02 22:43:25 +0000167 while (1) {
gborowiakc50ce6a2003-09-07 13:16:26 +0000168 /* remember current position, we'll need it on error */
Bart De Schuymer9cc9bfa2003-09-02 22:43:25 +0000169 anchor = pc;
gborowiakc50ce6a2003-09-07 13:16:26 +0000170
gborowiak6c6d7312003-09-16 19:26:38 +0000171 /* collect MAC; all its bytes are followed by ':' (colon),
172 * except for the last one which can be followed by
173 * ',' (comma), '=' or '\0' */
gborowiakc50ce6a2003-09-07 13:16:26 +0000174 for (i = 0; i < 5; i++) {
gborowiak6c6d7312003-09-16 19:26:38 +0000175 if (read_until(&pc, ":", token, 2) < 0
176 || token[0] == 0) {
177 print_error("MAC parse error: %.20s",
178 anchor);
gborowiakc50ce6a2003-09-07 13:16:26 +0000179 }
180 mac[i] = strtol(token, &endptr, 16);
181 if (*endptr) {
gborowiak6c6d7312003-09-16 19:26:38 +0000182 print_error("MAC parse error: %.20s",
183 anchor);
gborowiakc50ce6a2003-09-07 13:16:26 +0000184 }
185 pc++;
Bart De Schuymer9cc9bfa2003-09-02 22:43:25 +0000186 }
gborowiakc50ce6a2003-09-07 13:16:26 +0000187 if (read_until(&pc, "=,", token, 2) == -2 || token[0] == 0) {
188 print_error("MAC parse error: %.20s", anchor);
189 }
190 mac[i] = strtol(token, &endptr, 16);
191 if (*endptr) {
192 print_error("MAC parse error: %.20s", anchor);
193 }
194 if (*pc == '=') {
195 /* an IP follows the MAC; collect similarly to MAC */
196 pc++;
197 anchor = pc;
198 for (i = 0; i < 3; i++) {
gborowiak6c6d7312003-09-16 19:26:38 +0000199 if (read_until(&pc, ".", token, 3) < 0
200 || token[0] == 0) {
201 print_error
202 ("IP parse error: %.20s",
203 anchor);
gborowiakc50ce6a2003-09-07 13:16:26 +0000204 }
205 ip[i] = strtol(token, &endptr, 10);
206 if (*endptr) {
gborowiak6c6d7312003-09-16 19:26:38 +0000207 print_error
208 ("IP parse error: %.20s",
209 anchor);
gborowiakc50ce6a2003-09-07 13:16:26 +0000210 }
211 pc++;
212 }
gborowiak6c6d7312003-09-16 19:26:38 +0000213 if (read_until(&pc, ",", token, 3) == -2
214 || token[0] == 0) {
215 print_error("IP parse error: %.20s",
216 anchor);
gborowiakc50ce6a2003-09-07 13:16:26 +0000217 }
218 ip[3] = strtol(token, &endptr, 10);
219 if (*endptr) {
gborowiak6c6d7312003-09-16 19:26:38 +0000220 print_error("IP parse error: %.20s",
221 anchor);
gborowiakc50ce6a2003-09-07 13:16:26 +0000222 }
gborowiak6c6d7312003-09-16 19:26:38 +0000223 if (*(uint32_t*)ip == 0) {
224 print_error("Illegal IP 0.0.0.0");
225 }
226 } else {
gborowiakc50ce6a2003-09-07 13:16:26 +0000227 /* no IP, we set it to 0.0.0.0 */
228 memset(ip, 0, 4);
229 }
gborowiak6c6d7312003-09-16 19:26:38 +0000230
gborowiakc50ce6a2003-09-07 13:16:26 +0000231 /* we have collected MAC and IP, so we add an entry */
gborowiak6c6d7312003-09-16 19:26:38 +0000232 memcpy(((char *) workcopy->pool[nmacs].cmp) + 2, mac, 6);
233 workcopy->pool[nmacs].ip = *(const uint32_t *) ip;
Bart De Schuymer9cc9bfa2003-09-02 22:43:25 +0000234 nmacs++;
gborowiak6c6d7312003-09-16 19:26:38 +0000235
gborowiakc50ce6a2003-09-07 13:16:26 +0000236 /* re-allocate memory if needed */
237 if (*pc && nmacs >= workcopy->poolsize) {
238 if (!(h = new_wormhash(nmacs * 2))) {
gborowiak6c6d7312003-09-16 19:26:38 +0000239 print_memory();
gborowiakc50ce6a2003-09-07 13:16:26 +0000240 }
241 copy_wormhash(h, workcopy);
242 free(workcopy);
243 workcopy = h;
Bart De Schuymer9cc9bfa2003-09-02 22:43:25 +0000244 }
gborowiak6c6d7312003-09-16 19:26:38 +0000245
gborowiakc50ce6a2003-09-07 13:16:26 +0000246 /* check if end of string was reached */
247 if (!*pc) {
248 break;
249 }
gborowiak6c6d7312003-09-16 19:26:38 +0000250
251 /* now `pc' points to comma if we are here; */
252 /* increment this to the next char */
gborowiakc50ce6a2003-09-07 13:16:26 +0000253 /* but first assert :-> */
254 if (*pc != ',') {
255 print_error("Something went wrong; no comma...\n");
256 }
257 pc++;
gborowiak6c6d7312003-09-16 19:26:38 +0000258
259 /* again check if end of string was reached; */
260 /* we allow an ending comma */
Bart De Schuymer9cc9bfa2003-09-02 22:43:25 +0000261 if (!*pc) {
262 break;
263 }
264 }
gborowiakc50ce6a2003-09-07 13:16:26 +0000265 if (!(result = new_wormhash(nmacs))) {
gborowiak6c6d7312003-09-16 19:26:38 +0000266 print_memory();
gborowiakc50ce6a2003-09-07 13:16:26 +0000267 }
268 copy_wormhash(result, workcopy);
269 free(workcopy);
gborowiak6c6d7312003-09-16 19:26:38 +0000270 qsort(&result->pool, result->poolsize,
271 sizeof(struct ebt_mac_wormhash_tuple), fcmp);
272 index_table(result);
gborowiakc50ce6a2003-09-07 13:16:26 +0000273 return result;
Bart De Schuymer9cc9bfa2003-09-02 22:43:25 +0000274}
275
276#define OPT_DST 0x01
277#define OPT_SRC 0x02
gborowiak6c6d7312003-09-16 19:26:38 +0000278static int parse(int c, char **argv, int argc,
279 const struct ebt_u_entry *entry, unsigned int *flags,
280 struct ebt_entry_match **match)
Bart De Schuymer9cc9bfa2003-09-02 22:43:25 +0000281{
gborowiak6c6d7312003-09-16 19:26:38 +0000282 struct ebt_among_info *info =
283 (struct ebt_among_info *) (*match)->data;
Bart De Schuymer9cc9bfa2003-09-02 22:43:25 +0000284 struct ebt_mac_wormhash *wh;
gborowiakc50ce6a2003-09-07 13:16:26 +0000285 struct ebt_entry_match *h;
286 int new_size, old_size;
Bart De Schuymer9cc9bfa2003-09-02 22:43:25 +0000287
288 switch (c) {
289 case AMONG_DST:
290 case AMONG_SRC:
gborowiakc50ce6a2003-09-07 13:16:26 +0000291 if (check_inverse(optarg)) {
292 if (c == AMONG_DST)
293 info->bitmask |= EBT_AMONG_DST_NEG;
294 else
295 info->bitmask |= EBT_AMONG_SRC_NEG;
Bart De Schuymer9cc9bfa2003-09-02 22:43:25 +0000296 }
297 if (optind > argc)
298 print_error("No MAC list specified\n");
gborowiakc50ce6a2003-09-07 13:16:26 +0000299 wh = create_wormhash(argv[optind - 1]);
gborowiak6c6d7312003-09-16 19:26:38 +0000300 old_size = sizeof(struct ebt_entry_match) +
301 (**match).match_size;
302 h = malloc((new_size =
303 old_size + ebt_mac_wormhash_size(wh)));
gborowiakc50ce6a2003-09-07 13:16:26 +0000304 memcpy(h, *match, old_size);
gborowiak6c6d7312003-09-16 19:26:38 +0000305 memcpy((char *) h + old_size, wh,
306 ebt_mac_wormhash_size(wh));
gborowiakc50ce6a2003-09-07 13:16:26 +0000307 h->match_size = new_size - sizeof(struct ebt_entry_match);
gborowiak6c6d7312003-09-16 19:26:38 +0000308 info = (struct ebt_among_info *) h->data;
gborowiakc50ce6a2003-09-07 13:16:26 +0000309 if (c == AMONG_DST) {
310 check_option(flags, OPT_DST);
gborowiak6c6d7312003-09-16 19:26:38 +0000311 info->wh_dst_ofs =
312 old_size - sizeof(struct ebt_entry_match);
gborowiakc50ce6a2003-09-07 13:16:26 +0000313 } else {
314 check_option(flags, OPT_SRC);
gborowiak6c6d7312003-09-16 19:26:38 +0000315 info->wh_src_ofs =
316 old_size - sizeof(struct ebt_entry_match);
gborowiakc50ce6a2003-09-07 13:16:26 +0000317 }
318 free(*match);
319 *match = h;
320 free(wh);
Bart De Schuymer9cc9bfa2003-09-02 22:43:25 +0000321 break;
322 default:
323 return 0;
324 }
325 return 1;
326}
327
328static void final_check(const struct ebt_u_entry *entry,
gborowiak6c6d7312003-09-16 19:26:38 +0000329 const struct ebt_entry_match *match,
330 const char *name, unsigned int hookmask,
331 unsigned int time)
Bart De Schuymer9cc9bfa2003-09-02 22:43:25 +0000332{
333}
334
gborowiak6c6d7312003-09-16 19:26:38 +0000335#ifdef DEBUG
336static void wormhash_debug(const struct ebt_mac_wormhash *wh)
337{
338 int i;
339 printf("poolsize: %d\n", wh->poolsize);
340 for (i = 0; i <= 256; i++) {
341 printf("%02x ", wh->table[i]);
342 if (i % 16 == 15) {
343 printf("\n");
344 }
345 }
346 printf("\n");
347}
348#endif /* DEBUG */
349
Bart De Schuymer9cc9bfa2003-09-02 22:43:25 +0000350static void wormhash_printout(const struct ebt_mac_wormhash *wh)
351{
352 int i;
gborowiakc50ce6a2003-09-07 13:16:26 +0000353 unsigned char *ip;
gborowiak6c6d7312003-09-16 19:26:38 +0000354 for (i = 0; i < wh->poolsize; i++) {
Bart De Schuymer9cc9bfa2003-09-02 22:43:25 +0000355 const struct ebt_mac_wormhash_tuple *p;
gborowiak6c6d7312003-09-16 19:26:38 +0000356 p = (const struct ebt_mac_wormhash_tuple *)(&wh->pool[i]);
357 print_mac(((const char *) &p->cmp[0]) + 2);
358 if (p->ip) {
359 ip = (unsigned char *) &p->ip;
360 printf("=%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]);
Bart De Schuymer9cc9bfa2003-09-02 22:43:25 +0000361 }
gborowiak6c6d7312003-09-16 19:26:38 +0000362 printf(",");
363 }
Bart De Schuymer9cc9bfa2003-09-02 22:43:25 +0000364 printf(" ");
365}
366
367static void print(const struct ebt_u_entry *entry,
gborowiak6c6d7312003-09-16 19:26:38 +0000368 const struct ebt_entry_match *match)
Bart De Schuymer9cc9bfa2003-09-02 22:43:25 +0000369{
gborowiakc50ce6a2003-09-07 13:16:26 +0000370 struct ebt_among_info *info = (struct ebt_among_info *)match->data;
Bart De Schuymer9cc9bfa2003-09-02 22:43:25 +0000371
gborowiakc50ce6a2003-09-07 13:16:26 +0000372 if (info->wh_dst_ofs) {
Bart De Schuymer9cc9bfa2003-09-02 22:43:25 +0000373 printf("--among-dst ");
gborowiakc50ce6a2003-09-07 13:16:26 +0000374 if (info->bitmask && EBT_AMONG_DST_NEG) {
375 printf("! ");
376 }
377 wormhash_printout(ebt_among_wh_dst(info));
Bart De Schuymer9cc9bfa2003-09-02 22:43:25 +0000378 }
gborowiakc50ce6a2003-09-07 13:16:26 +0000379 if (info->wh_src_ofs) {
Bart De Schuymer9cc9bfa2003-09-02 22:43:25 +0000380 printf("--among-src ");
gborowiakc50ce6a2003-09-07 13:16:26 +0000381 if (info->bitmask && EBT_AMONG_SRC_NEG) {
382 printf("! ");
383 }
384 wormhash_printout(ebt_among_wh_src(info));
Bart De Schuymer9cc9bfa2003-09-02 22:43:25 +0000385 }
386}
387
gborowiak6c6d7312003-09-16 19:26:38 +0000388static int compare_wh(const struct ebt_mac_wormhash *aw,
389 const struct ebt_mac_wormhash *bw)
gborowiakc50ce6a2003-09-07 13:16:26 +0000390{
391 int as, bs;
392 as = ebt_mac_wormhash_size(aw);
393 bs = ebt_mac_wormhash_size(bw);
394 if (as != bs)
395 return 0;
396 if (as && memcmp(aw, bw, as))
397 return 0;
398 return 1;
399}
400
Bart De Schuymer9cc9bfa2003-09-02 22:43:25 +0000401static int compare(const struct ebt_entry_match *m1,
gborowiak6c6d7312003-09-16 19:26:38 +0000402 const struct ebt_entry_match *m2)
Bart De Schuymer9cc9bfa2003-09-02 22:43:25 +0000403{
gborowiak6c6d7312003-09-16 19:26:38 +0000404 struct ebt_among_info *a = (struct ebt_among_info *) m1->data;
405 struct ebt_among_info *b = (struct ebt_among_info *) m2->data;
Bart De Schuymer9cc9bfa2003-09-02 22:43:25 +0000406
gborowiak6c6d7312003-09-16 19:26:38 +0000407 if (!compare_wh(ebt_among_wh_dst(a), ebt_among_wh_dst(b)))
408 return 0;
409 if (!compare_wh(ebt_among_wh_src(a), ebt_among_wh_src(b)))
410 return 0;
411 if (a->bitmask != b->bitmask)
412 return 0;
gborowiakc50ce6a2003-09-07 13:16:26 +0000413 return 1;
Bart De Schuymer9cc9bfa2003-09-02 22:43:25 +0000414}
415
gborowiak6c6d7312003-09-16 19:26:38 +0000416static struct ebt_u_match among_match = {
417 .name = EBT_AMONG_MATCH,
418 .size = sizeof(struct ebt_among_info),
419 .help = print_help,
420 .init = init,
421 .parse = parse,
422 .final_check = final_check,
423 .print = print,
424 .compare = compare,
425 .extra_ops = opts,
Bart De Schuymer9cc9bfa2003-09-02 22:43:25 +0000426};
427
428static void _init(void) __attribute__ ((constructor));
429static void _init(void)
430{
431 register_match(&among_match);
432}