From 22bcbb5aaaadb6605abfe02c5a6f052fe64eae27 Mon Sep 17 00:00:00 2001 From: Bart De Schuymer Date: Wed, 14 Jan 2004 20:06:44 +0000 Subject: ebtables library functions --- userspace/ebtables2/libebtc.c | 1499 ++++++++++++++++++++++++++++++++ userspace/ebtables2/useful_functions.c | 261 ++++++ 2 files changed, 1760 insertions(+) create mode 100644 userspace/ebtables2/libebtc.c create mode 100644 userspace/ebtables2/useful_functions.c diff --git a/userspace/ebtables2/libebtc.c b/userspace/ebtables2/libebtc.c new file mode 100644 index 0000000..843a6a5 --- /dev/null +++ b/userspace/ebtables2/libebtc.c @@ -0,0 +1,1499 @@ + +/* + * libebtc.c, January 2004 + * + * Contains the functions with which to make a table in userspace. + * + * Author: Bart De Schuymer + * + * This code is stongly inspired on the iptables code which is + * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include "include/ebtables_u.h" +#include "include/ethernetdb.h" +#include +#include +#include + +static void decrease_chain_jumps(struct ebt_u_replace *replace); +static void remove_udc(struct ebt_u_replace *replace); +static int iterate_entries(struct ebt_u_replace *replace, int type); + +/* + * The standard names + */ +const char *ebt_hooknames[NF_BR_NUMHOOKS] = +{ + [NF_BR_PRE_ROUTING]"PREROUTING", + [NF_BR_LOCAL_IN]"INPUT", + [NF_BR_FORWARD]"FORWARD", + [NF_BR_LOCAL_OUT]"OUTPUT", + [NF_BR_POST_ROUTING]"POSTROUTING", + [NF_BR_BROUTING]"BROUTING" +}; + +/* + * The four target names + */ +const char* ebt_standard_targets[NUM_STANDARD_TARGETS] = +{ + "ACCEPT", + "DROP", + "CONTINUE", + "RETURN", +}; + +/* + * The lists of supported tables, matches, watchers and targets + */ +struct ebt_u_table *ebt_tables; +struct ebt_u_match *ebt_matches; +struct ebt_u_watcher *ebt_watchers; +struct ebt_u_target *ebt_targets; + +/* + * Find the right structure belonging to a name + */ +struct ebt_u_target *ebt_find_target(const char *name) +{ + struct ebt_u_target *t = ebt_targets; + + while(t && strcmp(t->name, name)) + t = t->next; + return t; +} + +struct ebt_u_match *ebt_find_match(const char *name) +{ + struct ebt_u_match *m = ebt_matches; + + while(m && strcmp(m->name, name)) + m = m->next; + return m; +} + +struct ebt_u_watcher *ebt_find_watcher(const char *name) +{ + struct ebt_u_watcher *w = ebt_watchers; + + while(w && strcmp(w->name, name)) + w = w->next; + return w; +} + +struct ebt_u_table *ebt_find_table(const char *name) +{ + struct ebt_u_table *t = ebt_tables; + + while (t && strcmp(t->name, name)) + t = t->next; + return t; +} + +/* + * Prints all registered extensions + */ +void ebt_list_extensions() +{ + struct ebt_u_table *tbl = ebt_tables; + struct ebt_u_target *t = ebt_targets; + struct ebt_u_match *m = ebt_matches; + struct ebt_u_watcher *w = ebt_watchers; + + PRINT_VERSION; + printf("Supported userspace extensions:\n\nSupported tables:\n"); + while(tbl) { + printf("%s\n", tbl->name); + tbl = tbl->next; + } + printf("\nSupported targets:\n"); + while(t) { + printf("%s\n", t->name); + t = t->next; + } + printf("\nSupported matches:\n"); + while(m) { + printf("%s\n", m->name); + m = m->next; + } + printf("\nSupported watchers:\n"); + while(w) { + printf("%s\n", w->name); + w = w->next; + } + exit(0); +} + +/* + * Get the table from the kernel or from a binary file + */ +void ebt_get_kernel_table(struct ebt_u_replace *replace, + struct ebt_u_table *table) +{ + if ( !(table = ebt_find_table(replace->name)) ) + print_error("Bad table name"); + /* + * get the kernel's information + */ + if (ebt_get_table(replace)) { + ebtables_insmod("ebtables"); + if (ebt_get_table(replace)) + print_error("The kernel doesn't support the ebtables " + "%s table", replace->name); + } + /* + * when listing a table contained in a file, we don't demand that + * the user knows the table's name + */ + if ( !(table = ebt_find_table(replace->name)) ) + print_error("Bad table name"); +} + +/* + * Put sane values into a new entry + */ +void ebt_initialize_entry(struct ebt_u_entry *e) +{ + e->bitmask = EBT_NOPROTO; + e->invflags = 0; + e->ethproto = 0; + strcpy(e->in, ""); + strcpy(e->out, ""); + strcpy(e->logical_in, ""); + strcpy(e->logical_out, ""); + e->m_list = NULL; + e->w_list = NULL; + e->t = (struct ebt_entry_target *)ebt_find_target(EBT_STANDARD_TARGET); + if (!e->t) + print_bug("Couldn't load standard target"); + ((struct ebt_standard_target *)e->t)->verdict = EBT_CONTINUE; +} + +/* + * replace is reborn, i.e. rebirth of replace + */ +void ebt_cleanup_replace(struct ebt_u_replace *replace) +{ + int i; + struct ebt_u_entries *entries; + struct ebt_u_chain_list *udc1, *udc2; + struct ebt_cntchanges *cc1, *cc2; + struct ebt_u_entry *u_e1, *u_e2; + + replace->name[0] = '\0'; + replace->valid_hooks = 0; + replace->nentries = 0; + replace->num_counters = 0; + replace->flags = 0; + replace->command = 0; + replace->selected_chain = -1; + if (replace->filename) { + free(replace->filename); + replace->filename = NULL; + } + if (replace->counters) { + free(replace->counters); + replace->counters = NULL; + } + + i = -1; + while (1) { + i++; + entries = ebt_nr_to_chain(replace, i); + if (!entries) { + if (i < NF_BR_NUMHOOKS) + continue; + else + break; + } + entries->nentries = 0; + entries->counter_offset = 0; + u_e1 = entries->entries; + entries->entries = NULL; + while (u_e1) { + ebt_free_u_entry(u_e1); + u_e2 = u_e1->next; + free(u_e1); + u_e1 = u_e2; + } + } + udc1 = replace->udc; + while (udc1) { + udc2 = udc1->next; + free(udc1); + udc1 = udc2; + } + replace->udc = NULL; + cc1 = replace->counterchanges; + while (cc1) { + cc2 = cc1->next; + free(cc2); + cc1 = cc2; + } + replace->counterchanges = NULL; +} + +/* + * Should be called between 2 rule adds, f.e. + */ +void ebt_reinit_extensions(struct ebt_u_replace *replace) +{ + struct ebt_u_match *m; + struct ebt_u_watcher *w; + struct ebt_u_target *t; + + /* The init functions should determine by themselves whether they are + * called for the first time or not (when necessary). */ + for (m = ebt_matches; m; m = m->next) { + m->used = 0; + m->flags = 0; + m->init(m->m); + } + for (w = ebt_watchers; w; w = w->next) { + w->used = 0; + w->flags = 0; + w->init(w->w); + } + for (t = ebt_targets; m; t = t->next) { + t->used = 0; + t->flags = 0; + t->init(t->t); + } +} + +/* + * This doesn't free e, becoz the calling function might need e->next + */ +void ebt_free_u_entry(struct ebt_u_entry *e) +{ + struct ebt_u_match_list *m_l, *m_l2; + struct ebt_u_watcher_list *w_l, *w_l2; + + m_l = e->m_list; + while (m_l) { + m_l2 = m_l->next; + free(m_l->m); + free(m_l); + m_l = m_l2; + } + w_l = e->w_list; + while (w_l) { + w_l2 = w_l->next; + free(w_l->w); + free(w_l); + w_l = w_l2; + } + free(e->t); +} + +/* + * Blatently stolen (again) from iptables.c userspace program + * find out where the modprobe utility is located + */ +static char *get_modprobe(void) +{ + int procfile; + char *ret; + + procfile = open(PROC_SYS_MODPROBE, O_RDONLY); + if (procfile < 0) + return NULL; + + ret = malloc(1024); + if (ret) { + switch (read(procfile, ret, 1024)) { + case -1: goto fail; + case 1024: goto fail; /* Partial read. Wierd */ + } + if (ret[strlen(ret)-1]=='\n') + ret[strlen(ret)-1]=0; + close(procfile); + return ret; + } + fail: + free(ret); + close(procfile); + return NULL; +} + +char *ebt_modprobe; +/* + * Try to load the kernel module + */ +int ebtables_insmod(const char *modname) +{ + char *buf = NULL; + char *argv[3]; + + /* If they don't explicitly set it, read out of kernel */ + if (!ebt_modprobe) { + buf = get_modprobe(); + if (!buf) + return -1; + ebt_modprobe = buf; + } + + switch (fork()) { + case 0: + argv[0] = (char *)ebt_modprobe; + argv[1] = (char *)modname; + argv[2] = NULL; + execv(argv[0], argv); + + /* not usually reached */ + exit(0); + case -1: + return -1; + + default: /* parent */ + wait(NULL); + } + + free(buf); + return 0; +} + +/* + * Gives back a pointer to the chain base, based on nr. + * If nr >= NF_BR_NUMHOOKS you'll get back a user-defined chain. + * Returns NULL on failure. + */ +struct ebt_u_entries *ebt_nr_to_chain(const struct ebt_u_replace *replace, + int nr) +{ + if (nr == -1) + return NULL; + if (nr < NF_BR_NUMHOOKS) + return replace->hook_entry[nr]; + else { + int i; + struct ebt_u_chain_list *cl = replace->udc; + + i = nr - NF_BR_NUMHOOKS; + while (i > 0 && cl) { + cl = cl->next; + i--; + } + if (cl) + return cl->udc; + else + return NULL; + } +} + +/* + * Gives back a pointer to the chain base of selected_chain + */ +struct ebt_u_entries *ebt_to_chain(const struct ebt_u_replace *replace) +{ + return ebt_nr_to_chain(replace, replace->selected_chain); +} + +/* + * Parse the chain name and return a pointer to the chain base. + * Returns NULL on failure. + */ +struct ebt_u_entries *ebt_name_to_chain(const struct ebt_u_replace *replace, + const char* arg) +{ + int i; + struct ebt_u_chain_list *cl = replace->udc; + + for (i = 0; i < NF_BR_NUMHOOKS; i++) { + if (!(replace->valid_hooks & (1 << i))) + continue; + if (!strcmp(arg, replace->hook_entry[i]->name)) + return replace->hook_entry[i]; + } + while(cl) { + if (!strcmp(arg, cl->udc->name)) + return cl->udc; + cl = cl->next; + } + return NULL; +} + +/* + * Parse the chain name and return the corresponding chain nr + * returns -1 on failure + */ +int ebt_get_chainnr(const struct ebt_u_replace *replace, const char* arg) +{ + int i; + struct ebt_u_chain_list *cl = replace->udc; + + for (i = 0; i < NF_BR_NUMHOOKS; i++) { + if (!(replace->valid_hooks & (1 << i))) + continue; + if (!strcmp(arg, replace->hook_entry[i]->name)) + return i; + } + while(cl) { + if (!strcmp(arg, cl->udc->name)) + return i; + i++; + cl = cl->next; + } + return -1; +} + + /* +************ +************ +**COMMANDS** +************ +************ + */ + +/* + * Chainge the policy of selected_chain. + * No sanity checks are done. + */ +void ebt_change_policy(struct ebt_u_replace *replace, int policy) +{ + struct ebt_u_entries *entries = ebt_to_chain(replace); + + if (policy < -NUM_STANDARD_TARGETS || policy == EBT_CONTINUE) + print_bug("wrong policy: %d", policy); + entries->policy = policy; +} + +/* + * Flush one chain or the complete table + * If selected_chain == -1: flush the complete table + */ +void ebt_flush_chains(struct ebt_u_replace *replace) +{ + int i, j, numdel; + struct ebt_u_entry *u_e, *tmp; + struct ebt_u_entries *entries = ebt_to_chain(replace); + struct ebt_cntchanges *cc = replace->counterchanges; + struct ebt_cntchanges **prev_cc = &(replace->counterchanges); + + /* + * flush whole table + */ + if (!entries) { + if (replace->nentries == 0) + return; + replace->nentries = 0; + + /* + * free everything and zero (n)entries + */ + i = -1; + while (1) { + i++; + entries = ebt_nr_to_chain(replace, i); + if (!entries) { + if (i < NF_BR_NUMHOOKS) + continue; + else + break; + } + entries->nentries = 0; + entries->counter_offset = 0; + u_e = entries->entries; + entries->entries = NULL; + while (u_e) { + ebt_free_u_entry(u_e); + tmp = u_e->next; + free(u_e); + u_e = tmp; + } + } + /* + * update the counters + */ + while (cc) { + if (cc->type == CNT_ADD) { + *prev_cc = cc->next; + free(cc); + cc = *prev_cc; + continue; + } + cc->type = CNT_DEL; + prev_cc = &(cc->next); + cc = cc->next; + } + return; + } + + if (entries->nentries == 0) + return; + replace->nentries -= entries->nentries; + numdel = entries->nentries; + + /* + * delete the counters belonging to the specified chain, + * update counter_offset + */ + i = -1; + while (1) { + i++; + entries = ebt_nr_to_chain(replace, i); + if (!entries) { + if (i < NF_BR_NUMHOOKS) + continue; + else + break; + } + if (i > replace->selected_chain) { + entries->counter_offset -= numdel; + continue; + } + j = entries->nentries; + while (j) { + /* don't count deleted entries */ + if (cc->type == CNT_DEL) + goto letscontinue; + if (i == replace->selected_chain) { + if (cc->type == CNT_ADD) { + *prev_cc = cc->next; + free(cc); + cc = *prev_cc; + j--; + continue; + } + cc->type = CNT_DEL; + } + j--; +letscontinue: + prev_cc = &(cc->next); + cc = cc->next; + } + } + + entries = ebt_to_chain(replace); + entries->nentries = 0; + u_e = entries->entries; + while (u_e) { + ebt_free_u_entry(u_e); + tmp = u_e->next; + free(u_e); + u_e = tmp; + } + entries->entries = NULL; + return; +} + +/* + * returns the rule number on success (starting from 0), -1 on failure + * + * This function expects the ebt_{match,watcher,target} members of new_entry + * to contain pointers to ebt_u_{match,watcher,target}. + */ +int ebt_check_rule_exists(struct ebt_u_replace *replace, + struct ebt_u_entry *new_entry) +{ + struct ebt_u_entry *u_e; + struct ebt_u_match_list *m_l, *m_l2; + struct ebt_u_match *m; + struct ebt_u_watcher_list *w_l, *w_l2; + struct ebt_u_watcher *w; + struct ebt_u_target *t = (struct ebt_u_target *)new_entry->t; + struct ebt_u_entries *entries = ebt_to_chain(replace); + int i, j, k; + + u_e = entries->entries; + /* + * check for an existing rule (if there are duplicate rules, + * take the first occurance) + */ + for (i = 0; i < entries->nentries; i++, u_e = u_e->next) { + if (!u_e) + print_bug("Hmm, trouble"); + if (u_e->ethproto != new_entry->ethproto) + continue; + if (strcmp(u_e->in, new_entry->in)) + continue; + if (strcmp(u_e->out, new_entry->out)) + continue; + if (strcmp(u_e->logical_in, new_entry->logical_in)) + continue; + if (strcmp(u_e->logical_out, new_entry->logical_out)) + continue; + if (new_entry->bitmask & EBT_SOURCEMAC && + memcmp(u_e->sourcemac, new_entry->sourcemac, ETH_ALEN)) + continue; + if (new_entry->bitmask & EBT_DESTMAC && + memcmp(u_e->destmac, new_entry->destmac, ETH_ALEN)) + continue; + if (new_entry->bitmask != u_e->bitmask || + new_entry->invflags != u_e->invflags) + continue; + /* + * compare all matches + */ + m_l = new_entry->m_list; + j = 0; + while (m_l) { + m = (struct ebt_u_match *)(m_l->m); + m_l2 = u_e->m_list; + while (m_l2 && strcmp(m_l2->m->u.name, m->m->u.name)) + m_l2 = m_l2->next; + if (!m_l2 || !m->compare(m->m, m_l2->m)) + goto letscontinue; + j++; + m_l = m_l->next; + } + /* + * now be sure they have the same nr of matches + */ + k = 0; + m_l = u_e->m_list; + while (m_l) { + k++; + m_l = m_l->next; + } + if (j != k) + continue; + + /* + * compare all watchers + */ + w_l = new_entry->w_list; + j = 0; + while (w_l) { + w = (struct ebt_u_watcher *)(w_l->w); + w_l2 = u_e->w_list; + while (w_l2 && strcmp(w_l2->w->u.name, w->w->u.name)) + w_l2 = w_l2->next; + if (!w_l2 || !w->compare(w->w, w_l2->w)) + goto letscontinue; + j++; + w_l = w_l->next; + } + k = 0; + w_l = u_e->w_list; + while (w_l) { + k++; + w_l = w_l->next; + } + if (j != k) + continue; + if (strcmp(t->t->u.name, u_e->t->u.name)) + continue; + if (!t->compare(t->t, u_e->t)) + continue; + return i; +letscontinue: + } + return -1; +} + +/* Add a rule, rule_nr is the rule to update + * rule_nr specifies where the rule should be inserted + * rule_nr > 0 : insert the rule right before the rule_nr'th rule + * (the first rule is rule 1) + * rule_nr < 0 : insert the rule right before the (n+rule_nr+1)'th rule, + * where n denotes the number of rule in the chain + * rule_nr == 0: add a new rule at the end of the chain + * + * This function expects the ebt_{match,watcher,target} members of new_entry + * to contain pointers to ebt_u_{match,watcher,target} and updates these + * pointers before adding the rule to the chain. + */ +void ebt_add_rule(struct ebt_u_replace *replace, struct ebt_u_entry *new_entry, + int rule_nr) +{ + int i, j; + struct ebt_u_entry **u_e; + struct ebt_u_match_list *m_l; + struct ebt_u_watcher_list *w_l; + struct ebt_u_entries *entries = ebt_to_chain(replace); + struct ebt_cntchanges *cc = replace->counterchanges, *new_cc; + struct ebt_cntchanges **prev_cc = &(replace->counterchanges); + + if (rule_nr <= 0) + rule_nr += entries->nentries; + else + rule_nr--; + if (rule_nr > entries->nentries || rule_nr < 0) + print_error("The specified rule number is incorrect"); + /* + * we're adding one rule + */ + replace->nentries++; + entries->nentries++; + + /* + * handle counter stuff + */ + for (i = 0; i < replace->selected_chain; i++) { + if (i < NF_BR_NUMHOOKS && !(replace->valid_hooks & (1 << i))) + continue; + j = ebt_nr_to_chain(replace, i)->nentries; + while (j) { + if (cc->type != CNT_DEL) + j--; + prev_cc = &(cc->next); + cc = cc->next; + } + } + j = rule_nr; + while (j) { + if (cc->type != CNT_DEL) + j--; + prev_cc = &(cc->next); + cc = cc->next; + } + if (cc && cc->type == CNT_DEL) /* The add is victorious and conquers + * the delete */ + cc->type = CNT_OWRITE; + else { + new_cc = (struct ebt_cntchanges *)malloc(sizeof(struct ebt_cntchanges)); + new_cc->type = CNT_ADD; + new_cc->next = cc; + *prev_cc = new_cc; + } + + /* + * go to the right position in the chain + */ + u_e = &entries->entries; + for (i = 0; i < rule_nr; i++) + u_e = &(*u_e)->next; + /* + * insert the rule + */ + new_entry->next = *u_e; + *u_e = new_entry; + + /* + * put the ebt_[match, watcher, target] pointers in place + */ + m_l = new_entry->m_list; + while (m_l) { + m_l->m = ((struct ebt_u_match *)m_l->m)->m; + m_l = m_l->next; + } + w_l = new_entry->w_list; + while (w_l) { + w_l->w = ((struct ebt_u_watcher *)w_l->w)->w; + w_l = w_l->next; + } + new_entry->t = ((struct ebt_u_target *)new_entry->t)->t; + + /* + * update the counter_offset of chains behind this one + */ + i = replace->selected_chain; + while (1) { + i++; + entries = ebt_nr_to_chain(replace, i); + if (!entries) { + if (i < NF_BR_NUMHOOKS) + continue; + else + break; + } else + entries->counter_offset++; + } +} + +/* + * Delete a rule or rules + * begin == end == 0: delete the rule corresponding to new_entry + * + * the first rule has rule nr 1, the last rule has rule nr -1, etc. + * This function expects the ebt_{match,watcher,target} members of new_entry + * to contain pointers to ebt_u_{match,watcher,target}. + */ +void ebt_delete_rule(struct ebt_u_replace *replace, + struct ebt_u_entry *new_entry, int begin, int end) +{ + int i, j, nr_deletes; + struct ebt_u_entry **u_e, *u_e2; + struct ebt_u_entries *entries = ebt_to_chain(replace); + struct ebt_cntchanges *cc = replace->counterchanges; + struct ebt_cntchanges **prev_cc = &(replace->counterchanges); + + if (begin < 0) + begin += entries->nentries + 1; + if (end < 0) + end += entries->nentries + 1; + + if (begin < 0 || begin > end || end > entries->nentries) + print_error("Sorry, wrong rule numbers"); + + if ((begin * end == 0) && (begin + end != 0)) + print_bug("begin and end should be either both zero, either" + " both non-zero"); + if (begin != 0 && end != 0) { + begin--; + end--; + } else { + begin = ebt_check_rule_exists(replace, new_entry); + end = begin; + if (begin == -1) + print_error("Sorry, rule does not exist"); + } + + /* + * we're deleting rules + */ + nr_deletes = end - begin + 1; + replace->nentries -= nr_deletes; + entries->nentries -= nr_deletes; + + /* + * handle counter stuff + */ + for (i = 0; i < replace->selected_chain; i++) { + if (i < NF_BR_NUMHOOKS && !(replace->valid_hooks & (1 << i))) + continue; + j = ebt_nr_to_chain(replace, i)->nentries; + while (j) { + if (cc->type != CNT_DEL) + j--; + prev_cc = &(cc->next); + cc = cc->next; + } + } + j = begin; + while (j) { + if (cc->type != CNT_DEL) + j--; + prev_cc = &(cc->next); + cc = cc->next; + } + j = nr_deletes; + while (j) { + if (cc->type != CNT_DEL) { + j--; + if (cc->type == CNT_ADD) { + *prev_cc = cc->next; + free(cc); + cc = *prev_cc; + continue; + } + cc->type = CNT_DEL; + } + prev_cc = &(cc->next); + cc = cc->next; + } + + /* + * go to the right position in the chain + */ + u_e = &entries->entries; + for (j = 0; j < begin; j++) + u_e = &(*u_e)->next; + /* + * remove the rules + */ + j = nr_deletes; + while(j--) { + u_e2 = *u_e; + *u_e = (*u_e)->next; + /* free everything */ + ebt_free_u_entry(u_e2); + free(u_e2); + } + + /* + * update the counter_offset of chains behind this one + */ + j = replace->selected_chain; + while (1) { + j++; + entries = ebt_nr_to_chain(replace, j); + if (!entries) { + if (j < NF_BR_NUMHOOKS) + continue; + else + break; + } else + entries->counter_offset -= nr_deletes; + } +} + +/* + * Selected_chain == -1 : zero all counters + * else, zero the counters of selected_chain + */ +void ebt_zero_counters(struct ebt_u_replace *replace) +{ + struct ebt_u_entries *entries = ebt_to_chain(replace); + struct ebt_cntchanges *cc = replace->counterchanges; + int i, j; + + if (!entries) { + while (cc) { + if (cc->type == CNT_NORM) + cc->type = CNT_ZERO; + cc = cc->next; + } + } else { + if (entries->nentries == 0) + return; + + for (i = 0; i < replace->selected_chain; i++) { + if (i < NF_BR_NUMHOOKS && + !(replace->valid_hooks & (1 << i))) + continue; + j = ebt_nr_to_chain(replace, i)->nentries; + while (j) { + if (cc->type != CNT_DEL) + j--; + cc = cc->next; + } + } + j = entries->nentries; + while (j) { + if (cc->type != CNT_DEL) { + j--; + if (cc->type == CNT_NORM) + cc->type = CNT_ZERO; + } + cc = cc->next; + } + } +} + +/* + * Add a new chain and specify its policy + */ +void ebt_new_chain(struct ebt_u_replace *replace, const char *name, int policy) +{ + struct ebt_u_chain_list *cl, **cl2; + + if (ebt_get_chainnr(replace, name) != -1) + print_error("Chain %s already exists", optarg); + if (ebt_find_target(name)) + print_error("Target with name %s exists", optarg); + if (strlen(optarg) >= EBT_CHAIN_MAXNAMELEN) + print_error("Chain name length can't exceed %d", + EBT_CHAIN_MAXNAMELEN - 1); + cl = (struct ebt_u_chain_list *) + malloc(sizeof(struct ebt_u_chain_list)); + if (!cl) + print_memory(); + cl->next = NULL; + cl->udc = (struct ebt_u_entries *) + malloc(sizeof(struct ebt_u_entries)); + if (!cl->udc) + print_memory(); + cl->udc->nentries = 0; + cl->udc->policy = policy; + cl->udc->counter_offset = replace->nentries; + cl->udc->hook_mask = 0; + strcpy(cl->udc->name, name); + cl->udc->entries = NULL; + cl->kernel_start = NULL; + /* + * put the new chain at the end + */ + cl2 = &(replace->udc); + while (*cl2) + cl2 = &((*cl2)->next); + *cl2 = cl; +} + +/* + * Selected_chain == -1: delete all non-referenced udc + * selected_chain < NF_BR_NUMHOOKS is illegal + */ +void ebt_delete_chain(struct ebt_u_replace *replace) +{ + int chain_nr = replace->selected_chain; + + if (chain_nr != -1 && chain_nr < NF_BR_NUMHOOKS) + print_bug("You can't remove a standard chain"); + if (chain_nr == -1) + replace->selected_chain = NF_BR_NUMHOOKS; + do { + if (ebt_to_chain(replace) == NULL) { + if (chain_nr == -1) + break; + print_bug("udc nr %d doesn't exist", chain_nr); + } + /* + * if the chain is referenced, don't delete it, + * also decrement jumps to a chain behind the + * one we're deleting + */ + if (ebt_check_for_references(replace)) { + replace->selected_chain++; + continue; + } + decrease_chain_jumps(replace); + ebt_flush_chains(replace); + remove_udc(replace); + } while (chain_nr != -1); + replace->selected_chain = chain_nr; +} + +/* + * Rename an existing chain. + */ +void ebt_rename_chain(struct ebt_u_replace *replace, const char *name) +{ + struct ebt_u_entries *entries = ebt_to_chain(replace); + + if (!entries) + print_bug("ebt_rename_chain: entries == NULL"); + strcpy(entries->name, name); +} + + + /* +************************* +************************* +**SPECIALIZED*FUNCTIONS** +************************* +************************* + */ + + +/* + * executes the final_check() function for all extensions used by the rule + * ebt_check_for_loops should have been executed earlier, to make sure the + * hook_mask is correct. + */ +void ebt_do_final_checks(struct ebt_u_replace *replace, struct ebt_u_entry *e, + struct ebt_u_entries *entries) +{ + struct ebt_u_match_list *m_l; + struct ebt_u_watcher_list *w_l; + struct ebt_u_target *t; + struct ebt_u_match *m; + struct ebt_u_watcher *w; + + m_l = e->m_list; + w_l = e->w_list; + while (m_l) { + m = ebt_find_match(m_l->m->u.name); + m->final_check(e, m_l->m, replace->name, + entries->hook_mask, 1); + m_l = m_l->next; + } + while (w_l) { + w = ebt_find_watcher(w_l->w->u.name); + w->final_check(e, w_l->w, replace->name, + entries->hook_mask, 1); + w_l = w_l->next; + } + t = ebt_find_target(e->t->u.name); + t->final_check(e, e->t, replace->name, + entries->hook_mask, 1); +} + +/* + * returns 1 when the chain is referenced, + * 0 when it isn't. + */ +int ebt_check_for_references(struct ebt_u_replace *replace) +{ + return iterate_entries(replace, 1); +} + +/* + * chain_nr: nr of the udc (>= NF_BR_NUMHOOKS) + * returns 1 when the chain is referenced, + * 0 when it isn't. + */ +int ebt_check_for_references2(struct ebt_u_replace *replace, int chain_nr) +{ + int tmp = replace->selected_chain, ret; + + replace->selected_chain = chain_nr; + ret = iterate_entries(replace, 1); + replace->selected_chain = tmp; + return ret; +} + +struct ebt_u_stack +{ + int chain_nr; + int n; + struct ebt_u_entry *e; + struct ebt_u_entries *entries; +}; + +/* + * Checks for loops + * As a by-product, the hook_mask member of each chain is filled in + * correctly. The check functions of the extensions need this hook_mask + * to know from which standard chains they can be called. + */ +void ebt_check_for_loops(struct ebt_u_replace *replace) +{ + int chain_nr , i, j , k, sp = 0, verdict; + struct ebt_u_entries *entries, *entries2; + struct ebt_u_stack *stack = NULL; + struct ebt_u_entry *e; + + i = -1; + /* + * initialize hook_mask to 0 + */ + while (1) { + i++; + if (i < NF_BR_NUMHOOKS && !(replace->valid_hooks & (1 << i))) + continue; + entries = ebt_nr_to_chain(replace, i); + if (!entries) + break; + entries->hook_mask = 0; + } + if (i > NF_BR_NUMHOOKS) { + stack = (struct ebt_u_stack *)malloc((i - NF_BR_NUMHOOKS) * + sizeof(struct ebt_u_stack)); + if (!stack) + print_memory(); + } + + /* + * check for loops, starting from every base chain + */ + for (i = 0; i < NF_BR_NUMHOOKS; i++) { + if (!(replace->valid_hooks & (1 << i))) + continue; + entries = ebt_nr_to_chain(replace, i); + /* + * (1 << NF_BR_NUMHOOKS) implies it's a standard chain + * (usefull in the final_check() funtions) + */ + entries->hook_mask = (1 << i) | (1 << NF_BR_NUMHOOKS); + chain_nr = i; + + e = entries->entries; + for (j = 0; j < entries->nentries; j++) { + if (strcmp(e->t->u.name, EBT_STANDARD_TARGET)) + goto letscontinue; + verdict = ((struct ebt_standard_target *)(e->t))->verdict; + if (verdict < 0) + goto letscontinue; + entries2 = ebt_nr_to_chain(replace, + verdict + NF_BR_NUMHOOKS); + entries2->hook_mask |= entries->hook_mask; + /* + * now see if we've been here before + */ + for (k = 0; k < sp; k++) + if (stack[k].chain_nr == verdict + NF_BR_NUMHOOKS) + print_error("Loop from chain %s to chain %s", + ebt_nr_to_chain(replace, chain_nr)->name, + ebt_nr_to_chain(replace, stack[k].chain_nr)->name); + /* + * jump to the chain, make sure we know how to get back + */ + stack[sp].chain_nr = chain_nr; + stack[sp].n = j; + stack[sp].entries = entries; + stack[sp].e = e; + sp++; + j = -1; + e = entries2->entries; + chain_nr = verdict + NF_BR_NUMHOOKS; + entries = entries2; + continue; +letscontinue: + e = e->next; + } + /* + * we are at the end of a standard chain + */ + if (sp == 0) + continue; + /* + * go back to the chain one level higher + */ + sp--; + j = stack[sp].n; + chain_nr = stack[sp].chain_nr; + e = stack[sp].e; + entries = stack[sp].entries; + goto letscontinue; + } + free(stack); + return; +} + +/* + * the user will use the match, so put it in new_entry + */ +void ebt_add_match(struct ebt_u_entry *new_entry, struct ebt_u_match *m) +{ + struct ebt_u_match_list **m_list, *new; + + for (m_list = &new_entry->m_list; *m_list; m_list = &(*m_list)->next); + new = (struct ebt_u_match_list *) + malloc(sizeof(struct ebt_u_match_list)); + if (!new) + print_memory(); + *m_list = new; + new->next = NULL; + new->m = (struct ebt_entry_match *)m; +} + +void ebt_add_watcher(struct ebt_u_entry *new_entry, struct ebt_u_watcher *w) +{ + struct ebt_u_watcher_list **w_list; + struct ebt_u_watcher_list *new; + + for (w_list = &new_entry->w_list; *w_list; w_list = &(*w_list)->next); + new = (struct ebt_u_watcher_list *) + malloc(sizeof(struct ebt_u_watcher_list)); + if (!new) + print_memory(); + *w_list = new; + new->next = NULL; + new->w = (struct ebt_entry_watcher *)w; +} + + + /* +******************* +******************* +**OTHER*FUNCTIONS** +******************* +******************* + */ + + +/* + * type = 0 => update chain jumps + * type = 1 => check for reference + * + * returns 1 when type == 1 and the chain is referenced + * returns 0 otherwise + */ +static int iterate_entries(struct ebt_u_replace *replace, int type) +{ + int i = -1, j, chain_nr = replace->selected_chain - NF_BR_NUMHOOKS; + struct ebt_u_entries *entries; + struct ebt_u_entry *e; + + if (chain_nr < 0) + print_bug("iterate_entries: udc = %d < 0", chain_nr); + while (1) { + i++; + entries = ebt_nr_to_chain(replace, i); + if (!entries) { + if (i < NF_BR_NUMHOOKS) + continue; + else + break; + } + e = entries->entries; + j = 0; + while (e) { + int chain_jmp; + + j++; + if (strcmp(e->t->u.name, EBT_STANDARD_TARGET)) { + e = e->next; + continue; + } + chain_jmp = ((struct ebt_standard_target *)e->t)->verdict; + switch (type) { + case 1: + if (chain_jmp == chain_nr) { + print_error("Can't delete the chain, it's " + "referenced in chain %s, rule %d", + entries->name, j); + return 1; + } + break; + case 0: + /* adjust the chain jumps when necessary */ + if (chain_jmp > chain_nr) + ((struct ebt_standard_target *)e->t)->verdict--; + break; + } /* end switch */ + e = e->next; + } + } + return 0; +} + +static void decrease_chain_jumps(struct ebt_u_replace *replace) +{ + iterate_entries(replace, 0); +} + +/* + * selected_chain >= NF_BR_NUMHOOKS + */ +static void remove_udc(struct ebt_u_replace *replace) +{ + struct ebt_u_chain_list *cl, **cl2; + struct ebt_u_entries *entries; + struct ebt_u_entry *u_e, *tmp; + int chain_nr = replace->selected_chain; + + if (chain_nr < NF_BR_NUMHOOKS) + print_bug("remove_udc: chain_nr = %d < %d", chain_nr, + NF_BR_NUMHOOKS); + /* first free the rules */ + entries = ebt_nr_to_chain(replace, chain_nr); + u_e = entries->entries; + while (u_e) { + ebt_free_u_entry(u_e); + tmp = u_e->next; + free(u_e); + u_e = tmp; + } + + /* next, remove the chain */ + cl2 = &(replace->udc); + while ((*cl2)->udc != entries) + cl2 = &((*cl2)->next); + cl = (*cl2); + (*cl2) = (*cl2)->next; + free(cl->udc); + free(cl); +} + + +/* + * used in initialization code of modules + */ +void ebt_register_match(struct ebt_u_match *m) +{ + int size = EBT_ALIGN(m->size) + sizeof(struct ebt_entry_match); + struct ebt_u_match **i; + + m->m = (struct ebt_entry_match *)malloc(size); + if (!m->m) + print_memory(); + strcpy(m->m->u.name, m->name); + m->m->match_size = EBT_ALIGN(m->size); + m->init(m->m); + + for (i = &ebt_matches; *i; i = &((*i)->next)); + m->next = NULL; + *i = m; +} + +void ebt_register_watcher(struct ebt_u_watcher *w) +{ + int size = EBT_ALIGN(w->size) + sizeof(struct ebt_entry_watcher); + struct ebt_u_watcher **i; + + w->w = (struct ebt_entry_watcher *)malloc(size); + if (!w->w) + print_memory(); + strcpy(w->w->u.name, w->name); + w->w->watcher_size = EBT_ALIGN(w->size); + w->init(w->w); + + for (i = &ebt_watchers; *i; i = &((*i)->next)); + w->next = NULL; + *i = w; +} + +void ebt_register_target(struct ebt_u_target *t) +{ + int size = EBT_ALIGN(t->size) + sizeof(struct ebt_entry_target); + struct ebt_u_target **i; + + t->t = (struct ebt_entry_target *)malloc(size); + if (!t->t) + print_memory(); + strcpy(t->t->u.name, t->name); + t->t->target_size = EBT_ALIGN(t->size); + t->init(t->t); + + for (i = &ebt_targets; *i; i = &((*i)->next)); + t->next = NULL; + *i = t; +} + +void ebt_register_table(struct ebt_u_table *t) +{ + t->next = ebt_tables; + ebt_tables = t; +} + +void ebt_iterate_matches(void (*f)(struct ebt_u_match *)) +{ + struct ebt_u_match *i; + + for (i = ebt_matches; i; i = i->next) + f(i); +} + +void ebt_iterate_watchers(void (*f)(struct ebt_u_watcher *)) +{ + struct ebt_u_watcher *i; + + for (i = ebt_watchers; i; i = i->next) + f(i); +} + +void ebt_iterate_targets(void (*f)(struct ebt_u_target *)) +{ + struct ebt_u_target *i; + + for (i = ebt_targets; i; i = i->next) + f(i); +} + +/* + * Don't use this function, use print_bug() + */ +void __print_bug(char *file, int line, char *format, ...) +{ + va_list l; + + va_start(l, format); + printf(PROGNAME" v"PROGVERSION":%s:%d:--BUG--: \n", file, line); + vprintf(format, l); + printf("\n"); + va_end(l); + exit (-1); +} + +/* + * The error messages are put in here when ebt_silent == 1 + * ebt_errormsg[0] == '\0' implies there was no error + */ +char ebt_errormsg[ERRORMSG_MAXLEN]; +/* + * When error messages should not be printed on the screen, after which + * the program exit()s, set ebt_silent to 1. + */ +int ebt_silent; +/* + * Don't use this function, use print_error() + */ +void __print_error(char *format, ...) +{ + va_list l; + + va_start(l, format); + if (ebt_silent) { + snprintf(ebt_errormsg, ERRORMSG_MAXLEN, format, l); + va_end(l); + } else { + vprintf(format, l); + printf("\n"); + va_end(l); + exit (-1); + } +} diff --git a/userspace/ebtables2/useful_functions.c b/userspace/ebtables2/useful_functions.c new file mode 100644 index 0000000..cec0cc4 --- /dev/null +++ b/userspace/ebtables2/useful_functions.c @@ -0,0 +1,261 @@ +/* + * useful_functions.c, January 2004 + * + * Random collection of functions that can be used by extensions. + * + * Author: Bart De Schuymer + * + * This code is stongly inspired on the iptables code which is + * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include "include/ebtables_u.h" +#include "include/ethernetdb.h" +#include +#include +#include +#include +#include + +const unsigned char mac_type_unicast[ETH_ALEN] = {0,0,0,0,0,0}; +const unsigned char msk_type_unicast[ETH_ALEN] = {1,0,0,0,0,0}; +const unsigned char mac_type_multicast[ETH_ALEN] = {1,0,0,0,0,0}; +const unsigned char msk_type_multicast[ETH_ALEN] = {1,0,0,0,0,0}; +const unsigned char mac_type_broadcast[ETH_ALEN] = {255,255,255,255,255,255}; +const unsigned char msk_type_broadcast[ETH_ALEN] = {255,255,255,255,255,255}; +const unsigned char mac_type_bridge_group[ETH_ALEN] = {0x01,0x80,0xc2,0,0,0}; +const unsigned char msk_type_bridge_group[ETH_ALEN] = {255,255,255,255,255,255}; + +int ebt_printstyle_mac; +/* + * flag = 2: byte always printed with 2 digits + */ +void ebt_print_mac(const char *mac) +{ + if (ebt_printstyle_mac == 2) { + int j; + for (j = 0; j < ETH_ALEN; j++) + printf("%02x%s", (unsigned char)mac[j], + (j==ETH_ALEN-1) ? "" : ":"); + } else + printf("%s", ether_ntoa((struct ether_addr *) mac)); +} + +void ebt_print_mac_and_mask(const char *mac, const char *mask) +{ + char hlpmsk[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + + if (!memcmp(mac, mac_type_unicast, 6) && + !memcmp(mask, msk_type_unicast, 6)) + printf("Unicast"); + else if (!memcmp(mac, mac_type_multicast, 6) && + !memcmp(mask, msk_type_multicast, 6)) + printf("Multicast"); + else if (!memcmp(mac, mac_type_broadcast, 6) && + !memcmp(mask, msk_type_broadcast, 6)) + printf("Broadcast"); + else if (!memcmp(mac, mac_type_bridge_group, 6) && + !memcmp(mask, msk_type_bridge_group, 6)) + printf("BGA"); + else { + ebt_print_mac(mac); + if (memcmp(mask, hlpmsk, 6)) { + printf("/"); + ebt_print_mac(mask); + } + } +} + +/* + * Checks the type for validity and calls getethertypebynumber() + */ +struct ethertypeent *parseethertypebynumber(int type) +{ + if (type < 1536) + print_error("Ethernet protocols have values >= 0x0600"); + if (type > 0xffff) + print_error("Ethernet protocols have values <= 0xffff"); + return getethertypebynumber(type); +} + +/* + * put the mac address into 6 (ETH_ALEN) bytes + * returns 0 on success + */ +int ebt_get_mac_and_mask(char *from, char *to, char *mask) +{ + char *p; + int i; + struct ether_addr *addr; + + if (strcasecmp(from, "Unicast") == 0) { + memcpy(to, mac_type_unicast, ETH_ALEN); + memcpy(mask, msk_type_unicast, ETH_ALEN); + return 0; + } + if (strcasecmp(from, "Multicast") == 0) { + memcpy(to, mac_type_multicast, ETH_ALEN); + memcpy(mask, msk_type_multicast, ETH_ALEN); + return 0; + } + if (strcasecmp(from, "Broadcast") == 0) { + memcpy(to, mac_type_broadcast, ETH_ALEN); + memcpy(mask, msk_type_broadcast, ETH_ALEN); + return 0; + } + if (strcasecmp(from, "BGA") == 0) { + memcpy(to, mac_type_bridge_group, ETH_ALEN); + memcpy(mask, msk_type_bridge_group, ETH_ALEN); + return 0; + } + if ( (p = strrchr(from, '/')) != NULL) { + *p = '\0'; + if (!(addr = ether_aton(p + 1))) + return -1; + memcpy(mask, addr, ETH_ALEN); + } else + memset(mask, 0xff, ETH_ALEN); + if (!(addr = ether_aton(from))) + return -1; + memcpy(to, addr, ETH_ALEN); + for (i = 0; i < ETH_ALEN; i++) + to[i] &= mask[i]; + return 0; +} + +int ebt_invert = 0; +int ebt_check_inverse(const char option[]) +{ + if (strcmp(option, "!") == 0) { + if (ebt_invert == 1) + print_error("double use of '!' not allowed"); + optind++; + ebt_invert = 1; + return 1; + } + return ebt_invert; +} + +void ebt_check_option(unsigned int *flags, unsigned int mask) +{ + if (*flags & mask) + print_error("Multiple use of same option not allowed"); + *flags |= mask; +} + +/* put the ip string into 4 bytes */ +static int undot_ip(char *ip, unsigned char *ip2) +{ + char *p, *q, *end; + long int onebyte; + int i; + char buf[20]; + + strncpy(buf, ip, sizeof(buf) - 1); + + p = buf; + for (i = 0; i < 3; i++) { + if ((q = strchr(p, '.')) == NULL) + return -1; + *q = '\0'; + onebyte = strtol(p, &end, 10); + if (*end != '\0' || onebyte > 255 || onebyte < 0) + return -1; + ip2[i] = (unsigned char)onebyte; + p = q + 1; + } + + onebyte = strtol(p, &end, 10); + if (*end != '\0' || onebyte > 255 || onebyte < 0) + return -1; + ip2[3] = (unsigned char)onebyte; + + return 0; +} + +/* put the mask into 4 bytes */ +static int ip_mask(char *mask, unsigned char *mask2) +{ + char *end; + long int bits; + uint32_t mask22; + + if (undot_ip(mask, mask2)) { + /* not the /a.b.c.e format, maybe the /x format */ + bits = strtol(mask, &end, 10); + if (*end != '\0' || bits > 32 || bits < 0) + return -1; + if (bits != 0) { + mask22 = htonl(0xFFFFFFFF << (32 - bits)); + memcpy(mask2, &mask22, 4); + } else { + mask22 = 0xFFFFFFFF; + memcpy(mask2, &mask22, 4); + } + } + return 0; +} + +/* set the ip mask and ip address */ +void ebt_parse_ip_address(char *address, uint32_t *addr, uint32_t *msk) +{ + char *p; + + /* first the mask */ + if ((p = strrchr(address, '/')) != NULL) { + *p = '\0'; + if (ip_mask(p + 1, (unsigned char *)msk)) + print_error("Problem with the IP mask"); + } else + *msk = 0xFFFFFFFF; + + if (undot_ip(address, (unsigned char *)addr)) + print_error("Problem with the IP address"); + *addr = *addr & *msk; +} + +/* transform the ip mask into a string ready for output */ +char *ebt_mask_to_dotted(uint32_t mask) +{ + int i; + static char buf[20]; + uint32_t maskaddr, bits; + + maskaddr = ntohl(mask); + + /* don't print /32 */ + if (mask == 0xFFFFFFFFL) { + *buf = '\0'; + return buf; + } + + i = 32; + bits = 0xFFFFFFFEL; /* case 0xFFFFFFFF has just been dealt with */ + while (--i >= 0 && maskaddr != bits) + bits <<= 1; + + if (i > 0) + sprintf(buf, "/%d", i); + else if (!i) + *buf = '\0'; + else + /* mask was not a decent combination of 1's and 0's */ + sprintf(buf, "/%d.%d.%d.%d", ((unsigned char *)&mask)[0], + ((unsigned char *)&mask)[1], ((unsigned char *)&mask)[2], + ((unsigned char *)&mask)[3]); + + return buf; +} -- cgit v1.2.3