| /* |
| * 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 <string.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <stdarg.h> |
| #include "include/ebtables_u.h" |
| #include "include/ethernetdb.h" |
| #include <unistd.h> |
| #include <fcntl.h> |
| #include <sys/wait.h> |
| |
| static void decrease_chain_jumps(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("Loaded userspace extensions:\n\nLoaded tables:\n"); |
| while (tbl) { |
| printf("%s\n", tbl->name); |
| tbl = tbl->next; |
| } |
| printf("\nLoaded targets:\n"); |
| while (t) { |
| printf("%s\n", t->name); |
| t = t->next; |
| } |
| printf("\nLoaded matches:\n"); |
| while (m) { |
| printf("%s\n", m->name); |
| m = m->next; |
| } |
| printf("\nLoaded watchers:\n"); |
| while (w) { |
| printf("%s\n", w->name); |
| w = w->next; |
| } |
| } |
| |
| /* Get the table from the kernel or from a binary file |
| * init: 1 = ask the kernel for the initial contents of a table, i.e. the |
| * way it looks when the table is insmod'ed |
| * 0 = get the current data in the table */ |
| int ebt_get_kernel_table(struct ebt_u_replace *replace, int init) |
| { |
| if (!ebt_find_table(replace->name)) { |
| ebt_print_error("Bad table name '%s'", replace->name); |
| return -1; |
| } |
| /* Get the kernel's information */ |
| if (ebt_get_table(replace, init)) { |
| if (ebt_errormsg[0] != '\0') |
| return -1; |
| ebtables_insmod("ebtables"); |
| if (ebt_get_table(replace, init)) { |
| ebt_print_error("The kernel doesn't support the ebtables '%s' table", replace->name); |
| return -1; |
| } |
| } |
| return 0; |
| } |
| |
| /* 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); |
| ebt_find_target(EBT_STANDARD_TARGET)->used = 1; |
| e->cnt.pcnt = e->cnt.bcnt = e->cnt_surplus.pcnt = e->cnt_surplus.bcnt = 0; |
| |
| if (!e->t) |
| ebt_print_bug("Couldn't load standard target"); |
| ((struct ebt_standard_target *)((struct ebt_u_target *)e->t)->t)->verdict = EBT_CONTINUE; |
| } |
| |
| /* Free up the memory of the table held in userspace, *replace can be reused */ |
| void ebt_cleanup_replace(struct ebt_u_replace *replace) |
| { |
| int i; |
| struct ebt_u_entries *entries; |
| 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; |
| free(replace->filename); |
| replace->filename = NULL; |
| free(replace->counters); |
| replace->counters = NULL; |
| |
| for (i = 0; i < replace->num_chains; i++) { |
| if (!(entries = replace->chains[i])) |
| continue; |
| u_e1 = entries->entries->next; |
| while (u_e1 != entries->entries) { |
| ebt_free_u_entry(u_e1); |
| u_e2 = u_e1->next; |
| free(u_e1); |
| u_e1 = u_e2; |
| } |
| free(entries->entries); |
| free(entries); |
| replace->chains[i] = NULL; |
| } |
| cc1 = replace->cc->next; |
| while (cc1 != replace->cc) { |
| cc2 = cc1->next; |
| free(cc1); |
| cc1 = cc2; |
| } |
| replace->cc->next = replace->cc->prev = replace->cc; |
| } |
| |
| /* Should be called, e.g., between 2 rule adds */ |
| void ebt_reinit_extensions() |
| { |
| struct ebt_u_match *m; |
| struct ebt_u_watcher *w; |
| struct ebt_u_target *t; |
| int size; |
| |
| /* 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) { |
| if (m->used) { |
| size = EBT_ALIGN(m->size) + sizeof(struct ebt_entry_match); |
| m->m = (struct ebt_entry_match *)malloc(size); |
| if (!m->m) |
| ebt_print_memory(); |
| strcpy(m->m->u.name, m->name); |
| m->m->match_size = EBT_ALIGN(m->size); |
| m->used = 0; |
| } |
| m->flags = 0; /* An error can occur before used is set, while flags is changed. */ |
| m->init(m->m); |
| } |
| for (w = ebt_watchers; w; w = w->next) { |
| if (w->used) { |
| size = EBT_ALIGN(w->size) + sizeof(struct ebt_entry_watcher); |
| w->w = (struct ebt_entry_watcher *)malloc(size); |
| if (!w->w) |
| ebt_print_memory(); |
| strcpy(w->w->u.name, w->name); |
| w->w->watcher_size = EBT_ALIGN(w->size); |
| w->used = 0; |
| } |
| w->flags = 0; |
| w->init(w->w); |
| } |
| for (t = ebt_targets; t; t = t->next) { |
| if (t->used) { |
| size = EBT_ALIGN(t->size) + sizeof(struct ebt_entry_target); |
| t->t = (struct ebt_entry_target *)malloc(size); |
| if (!t->t) |
| ebt_print_memory(); |
| strcpy(t->t->u.name, t->name); |
| t->t->target_size = EBT_ALIGN(t->size); |
| t->used = 0; |
| } |
| t->flags = 0; |
| t->init(t->t); |
| } |
| } |
| |
| /* This doesn't free e, because 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); |
| } |
| |
| 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) { |
| if (read(procfile, ret, 1024) == -1) |
| goto fail; |
| /* The kernel adds a '\n' */ |
| ret[1023] = '\n'; |
| *strchr(ret, '\n') = '\0'; |
| close(procfile); |
| return ret; |
| } |
| fail: |
| free(ret); |
| close(procfile); |
| return NULL; |
| } |
| |
| char *ebt_modprobe; |
| /* Try to load the kernel module, analogous to ip_tables.c */ |
| int ebtables_insmod(const char *modname) |
| { |
| char *buf = NULL; |
| char *argv[3]; |
| |
| /* If they don't explicitly set it, read out of /proc */ |
| if (!ebt_modprobe) { |
| buf = get_modprobe(); |
| if (!buf) |
| return -1; |
| ebt_modprobe = buf; /* Keep the value for possible later use */ |
| } |
| |
| 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); |
| } |
| |
| return 0; |
| } |
| |
| /* 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_entries *chain; |
| |
| for (i = 0; i < replace->num_chains; i++) { |
| if (!(chain = replace->chains[i])) |
| continue; |
| if (!strcmp(arg, chain->name)) |
| return chain; |
| } |
| 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; |
| |
| for (i = 0; i < replace->num_chains; i++) { |
| if (!replace->chains[i]) |
| continue; |
| if (!strcmp(arg, replace->chains[i]->name)) |
| return i; |
| } |
| return -1; |
| } |
| |
| /* |
| ************ |
| ************ |
| **COMMANDS** |
| ************ |
| ************ |
| */ |
| |
| /* Change the policy of selected_chain. |
| * Handing a bad policy to this function is a bug. */ |
| 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) |
| ebt_print_bug("Wrong policy: %d", policy); |
| entries->policy = policy; |
| } |
| |
| void ebt_delete_cc(struct ebt_cntchanges *cc) |
| { |
| if (cc->type == CNT_ADD) { |
| cc->prev->next = cc->next; |
| cc->next->prev = cc->prev; |
| free(cc); |
| } |
| cc->type = CNT_DEL; |
| } |
| |
| void ebt_empty_chain(struct ebt_u_entries *entries) |
| { |
| struct ebt_u_entry *u_e = entries->entries->next, *tmp; |
| while (u_e != entries->entries) { |
| ebt_delete_cc(u_e->cc); |
| ebt_free_u_entry(u_e); |
| tmp = u_e->next; |
| free(u_e); |
| u_e = tmp; |
| } |
| entries->entries->next = entries->entries->prev = entries->entries; |
| entries->nentries = 0; |
| } |
| |
| /* Flush one chain or the complete table |
| * If selected_chain == -1 then flush the complete table */ |
| void ebt_flush_chains(struct ebt_u_replace *replace) |
| { |
| int i, numdel; |
| struct ebt_u_entries *entries = ebt_to_chain(replace); |
| |
| /* Flush whole table */ |
| if (!entries) { |
| if (replace->nentries == 0) |
| return; |
| replace->nentries = 0; |
| |
| /* Free everything and zero (n)entries */ |
| for (i = 0; i < replace->num_chains; i++) { |
| if (!(entries = replace->chains[i])) |
| continue; |
| entries->counter_offset = 0; |
| ebt_empty_chain(entries); |
| } |
| return; |
| } |
| |
| if (entries->nentries == 0) |
| return; |
| replace->nentries -= entries->nentries; |
| numdel = entries->nentries; |
| |
| /* Update counter_offset */ |
| for (i = replace->selected_chain+1; i < replace->num_chains; i++) { |
| if (!(entries = replace->chains[i])) |
| continue; |
| entries->counter_offset -= numdel; |
| } |
| |
| entries = ebt_to_chain(replace); |
| ebt_empty_chain(entries); |
| } |
| |
| #define OPT_COUNT 0x1000 /* This value is also defined in ebtables.c */ |
| /* 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->next; |
| /* 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->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; |
| if (replace->flags & OPT_COUNT && (new_entry->cnt.pcnt != |
| u_e->cnt.pcnt || new_entry->cnt.bcnt != u_e->cnt.bcnt)) |
| 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 rules 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 so that they point to ebt_{match,watcher,target}, before adding |
| * the rule to the chain. Don't free() the ebt_{match,watcher,target} and |
| * don't reuse the new_entry after a successful call to ebt_add_rule() */ |
| void ebt_add_rule(struct ebt_u_replace *replace, struct ebt_u_entry *new_entry, int rule_nr) |
| { |
| int i; |
| 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, *new_cc; |
| |
| if (rule_nr <= 0) |
| rule_nr += entries->nentries; |
| else |
| rule_nr--; |
| if (rule_nr > entries->nentries || rule_nr < 0) { |
| ebt_print_error("The specified rule number is incorrect"); |
| return; |
| } |
| /* Go to the right position in the chain */ |
| if (rule_nr == entries->nentries) |
| u_e = entries->entries; |
| else { |
| u_e = entries->entries->next; |
| for (i = 0; i < rule_nr; i++) |
| u_e = u_e->next; |
| } |
| /* We're adding one rule */ |
| replace->nentries++; |
| entries->nentries++; |
| /* Insert the rule */ |
| new_entry->next = u_e; |
| new_entry->prev = u_e->prev; |
| u_e->prev->next = new_entry; |
| u_e->prev = new_entry; |
| new_cc = (struct ebt_cntchanges *)malloc(sizeof(struct ebt_cntchanges)); |
| if (!new_cc) |
| ebt_print_memory(); |
| new_cc->type = CNT_ADD; |
| new_cc->change = 0; |
| if (new_entry->next == entries->entries) { |
| for (i = replace->selected_chain+1; i < replace->num_chains; i++) |
| if (!replace->chains[i] || replace->chains[i]->nentries == 0) |
| continue; |
| else |
| break; |
| if (i == replace->num_chains) |
| cc = replace->cc; |
| else |
| cc = replace->chains[i]->entries->next->cc; |
| } else |
| cc = new_entry->next->cc; |
| new_cc->next = cc; |
| new_cc->prev = cc->prev; |
| cc->prev->next = new_cc; |
| cc->prev = new_cc; |
| new_entry->cc = new_cc; |
| |
| /* 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 */ |
| for (i = replace->selected_chain+1; i < replace->num_chains; i++) { |
| entries = replace->chains[i]; |
| if (!(entries = replace->chains[i])) |
| continue; |
| entries->counter_offset++; |
| } |
| } |
| |
| /* If *begin==*end==0 then find the rule corresponding to new_entry, |
| * else make the rule numbers positive (starting from 0) and check |
| * for bad rule numbers. */ |
| static int check_and_change_rule_number(struct ebt_u_replace *replace, |
| struct ebt_u_entry *new_entry, int *begin, int *end) |
| { |
| struct ebt_u_entries *entries = ebt_to_chain(replace); |
| |
| if (*begin < 0) |
| *begin += entries->nentries + 1; |
| if (*end < 0) |
| *end += entries->nentries + 1; |
| |
| if (*begin < 0 || *begin > *end || *end > entries->nentries) { |
| ebt_print_error("Sorry, wrong rule numbers"); |
| return -1; |
| } |
| |
| if ((*begin * *end == 0) && (*begin + *end != 0)) |
| ebt_print_bug("begin and end should be either both zero, " |
| "either both non-zero"); |
| if (*begin != 0) { |
| (*begin)--; |
| (*end)--; |
| } else { |
| *begin = ebt_check_rule_exists(replace, new_entry); |
| *end = *begin; |
| if (*begin == -1) { |
| ebt_print_error("Sorry, rule does not exist"); |
| return -1; |
| } |
| } |
| return 0; |
| } |
| |
| /* 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, nr_deletes; |
| struct ebt_u_entry *u_e, *u_e2, *u_e3; |
| struct ebt_u_entries *entries = ebt_to_chain(replace); |
| |
| if (check_and_change_rule_number(replace, new_entry, &begin, &end)) |
| return; |
| /* We're deleting rules */ |
| nr_deletes = end - begin + 1; |
| replace->nentries -= nr_deletes; |
| entries->nentries -= nr_deletes; |
| /* Go to the right position in the chain */ |
| u_e = entries->entries->next; |
| for (i = 0; i < begin; i++) |
| u_e = u_e->next; |
| u_e3 = u_e->prev; |
| /* Remove the rules */ |
| for (i = 0; i < nr_deletes; i++) { |
| u_e2 = u_e; |
| ebt_delete_cc(u_e2->cc); |
| u_e = u_e->next; |
| /* Free everything */ |
| ebt_free_u_entry(u_e2); |
| free(u_e2); |
| } |
| u_e3->next = u_e; |
| u_e->prev = u_e3; |
| /* Update the counter_offset of chains behind this one */ |
| for (i = replace->selected_chain+1; i < replace->num_chains; i++) { |
| if (!(entries = replace->chains[i])) |
| continue; |
| entries->counter_offset -= nr_deletes; |
| } |
| } |
| |
| /* Change the counters of a rule or rules |
| * begin == end == 0: change counters of 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}. |
| * The mask denotes the following: |
| * pcnt: mask % 3 = 0 : change; = 1: increment; = 2: decrement |
| * bcnt: mask / 3 = 0 : change; = 1: increment = 2: increment |
| * In daemon mode, mask==0 must hold */ |
| void ebt_change_counters(struct ebt_u_replace *replace, |
| struct ebt_u_entry *new_entry, int begin, int end, |
| struct ebt_counter *cnt, int mask) |
| { |
| int i; |
| struct ebt_u_entry *u_e; |
| struct ebt_u_entries *entries = ebt_to_chain(replace); |
| |
| if (check_and_change_rule_number(replace, new_entry, &begin, &end)) |
| return; |
| u_e = entries->entries->next; |
| for (i = 0; i < begin; i++) |
| u_e = u_e->next; |
| for (i = end-begin+1; i > 0; i--) { |
| if (mask % 3 == 0) { |
| u_e->cnt.pcnt = (*cnt).pcnt; |
| u_e->cnt_surplus.pcnt = 0; |
| } else { |
| #ifdef EBT_DEBUG |
| if (u_e->cc->type != CNT_NORM) |
| ebt_print_bug("cc->type != CNT_NORM"); |
| #endif |
| u_e->cnt_surplus.pcnt = (*cnt).pcnt; |
| } |
| |
| if (mask / 3 == 0) { |
| u_e->cnt.bcnt = (*cnt).bcnt; |
| u_e->cnt_surplus.bcnt = 0; |
| } else { |
| #ifdef EBT_DEBUG |
| if (u_e->cc->type != CNT_NORM) |
| ebt_print_bug("cc->type != CNT_NORM"); |
| #endif |
| u_e->cnt_surplus.bcnt = (*cnt).bcnt; |
| } |
| if (u_e->cc->type != CNT_ADD) |
| u_e->cc->type = CNT_CHANGE; |
| u_e->cc->change = mask; |
| u_e = u_e->next; |
| } |
| } |
| |
| /* If selected_chain == -1 then zero all counters, |
| * otherwise, 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_u_entry *next; |
| int i; |
| |
| if (!entries) { |
| for (i = 0; i < replace->num_chains; i++) { |
| if (!(entries = replace->chains[i])) |
| continue; |
| next = entries->entries->next; |
| while (next != entries->entries) { |
| if (next->cc->type == CNT_NORM) |
| next->cc->type = CNT_CHANGE; |
| next->cnt.bcnt = next->cnt.pcnt = 0; |
| next->cc->change = 0; |
| next = next->next; |
| } |
| } |
| } else { |
| if (entries->nentries == 0) |
| return; |
| |
| next = entries->entries->next; |
| while (next != entries->entries) { |
| if (next->cc->type == CNT_NORM) |
| next->cc->type = CNT_CHANGE; |
| next->cnt.bcnt = next->cnt.pcnt = 0; |
| next = next->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_entries *new; |
| |
| if (replace->num_chains == replace->max_chains) |
| ebt_double_chains(replace); |
| new = (struct ebt_u_entries *)malloc(sizeof(struct ebt_u_entries)); |
| if (!new) |
| ebt_print_memory(); |
| replace->chains[replace->num_chains++] = new; |
| new->nentries = 0; |
| new->policy = policy; |
| new->counter_offset = replace->nentries; |
| new->hook_mask = 0; |
| strcpy(new->name, name); |
| new->entries = (struct ebt_u_entry *)malloc(sizeof(struct ebt_u_entry)); |
| if (!new->entries) |
| ebt_print_memory(); |
| new->entries->next = new->entries->prev = new->entries; |
| new->kernel_start = NULL; |
| } |
| |
| /* returns -1 if the chain is referenced, 0 on success */ |
| static int ebt_delete_a_chain(struct ebt_u_replace *replace, int chain, int print_err) |
| { |
| int tmp = replace->selected_chain; |
| /* If the chain is referenced, don't delete it, |
| * also decrement jumps to a chain behind the |
| * one we're deleting */ |
| replace->selected_chain = chain; |
| if (ebt_check_for_references(replace, print_err)) |
| return -1; |
| decrease_chain_jumps(replace); |
| ebt_flush_chains(replace); |
| replace->selected_chain = tmp; |
| free(replace->chains[chain]->entries); |
| free(replace->chains[chain]); |
| memmove(replace->chains+chain, replace->chains+chain+1, (replace->num_chains-chain-1)*sizeof(void *)); |
| replace->num_chains--; |
| return 0; |
| } |
| |
| /* Selected_chain == -1: delete all non-referenced udc |
| * selected_chain < NF_BR_NUMHOOKS is illegal */ |
| void ebt_delete_chain(struct ebt_u_replace *replace) |
| { |
| if (replace->selected_chain != -1 && replace->selected_chain < NF_BR_NUMHOOKS) |
| ebt_print_bug("You can't remove a standard chain"); |
| if (replace->selected_chain == -1) { |
| int i = NF_BR_NUMHOOKS; |
| |
| while (i < replace->num_chains) |
| if (ebt_delete_a_chain(replace, i, 0)) |
| i++; |
| } else |
| ebt_delete_a_chain(replace, replace->selected_chain, 1); |
| } |
| |
| /* 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) |
| ebt_print_bug("ebt_rename_chain: entries == NULL"); |
| strcpy(entries->name, name); |
| } |
| |
| |
| /* |
| ************************* |
| ************************* |
| **SPECIALIZED*FUNCTIONS** |
| ************************* |
| ************************* |
| */ |
| |
| |
| void ebt_double_chains(struct ebt_u_replace *replace) |
| { |
| struct ebt_u_entries **new; |
| |
| replace->max_chains *= 2; |
| new = (struct ebt_u_entries **)malloc(replace->max_chains*sizeof(void *)); |
| if (!new) |
| ebt_print_memory(); |
| memcpy(new, replace->chains, replace->max_chains/2*sizeof(void *)); |
| free(replace->chains); |
| replace->chains = new; |
| } |
| |
| /* 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. The time argument to final_check() is set to 1, |
| * meaning it's the second time the final_check() function is executed. */ |
| 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); |
| if (ebt_errormsg[0] != '\0') |
| return; |
| 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); |
| if (ebt_errormsg[0] != '\0') |
| return; |
| 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 (if it returns) when the chain is referenced, 0 when it isn't. |
| * print_err: 0 (resp. 1) = don't (resp. do) print error when referenced */ |
| int ebt_check_for_references(struct ebt_u_replace *replace, int print_err) |
| { |
| if (print_err) |
| return iterate_entries(replace, 1); |
| else |
| return iterate_entries(replace, 2); |
| } |
| |
| /* chain_nr: nr of the udc (>= NF_BR_NUMHOOKS) |
| * Returns 1 (if it returns) when the chain is referenced, 0 when it isn't. |
| * print_err: 0 (resp. 1) = don't (resp. do) print error when referenced */ |
| int ebt_check_for_references2(struct ebt_u_replace *replace, int chain_nr, |
| int print_err) |
| { |
| int tmp = replace->selected_chain, ret; |
| |
| replace->selected_chain = chain_nr; |
| if (print_err) |
| ret = iterate_entries(replace, 1); |
| else |
| ret = iterate_entries(replace, 2); |
| 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; |
| |
| /* Initialize hook_mask to 0 */ |
| for (i = 0; i < replace->num_chains; i++) { |
| if (!(entries = replace->chains[i])) |
| continue; |
| if (i < NF_BR_NUMHOOKS) |
| /* (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); |
| else |
| entries->hook_mask = 0; |
| } |
| if (replace->num_chains == NF_BR_NUMHOOKS) |
| return; |
| stack = (struct ebt_u_stack *)malloc((replace->num_chains - NF_BR_NUMHOOKS) * sizeof(struct ebt_u_stack)); |
| if (!stack) |
| ebt_print_memory(); |
| |
| /* Check for loops, starting from every base chain */ |
| for (i = 0; i < NF_BR_NUMHOOKS; i++) { |
| if (!(entries = replace->chains[i])) |
| continue; |
| chain_nr = i; |
| |
| e = entries->entries->next; |
| 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 = replace->chains[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) { |
| ebt_print_error("Loop from chain '%s' to chain '%s'", |
| replace->chains[chain_nr]->name, |
| replace->chains[stack[k].chain_nr]->name); |
| goto free_stack; |
| } |
| /* 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->next; |
| 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: |
| free(stack); |
| return; |
| } |
| |
| /* The user will use the match, so put it in new_entry. The ebt_u_match |
| * pointer is put in the ebt_entry_match pointer. ebt_add_rule will |
| * fill in the final value for new->m. Unless the rule is added to a chain, |
| * the pointer will keep pointing to the ebt_u_match (until the new_entry |
| * is freed). I know, I should use a union for these 2 pointer types... */ |
| 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) |
| ebt_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) |
| ebt_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, print error when referenced |
| * type = 2 => check for reference, don't print error when referenced |
| * |
| * 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, j, chain_nr = replace->selected_chain - NF_BR_NUMHOOKS; |
| struct ebt_u_entries *entries; |
| struct ebt_u_entry *e; |
| |
| if (chain_nr < 0) |
| ebt_print_bug("iterate_entries: udc = %d < 0", chain_nr); |
| for (i = 0; i < replace->num_chains; i++) { |
| if (!(entries = replace->chains[i])) |
| continue; |
| e = entries->entries->next; |
| for (j = 0; j < entries->nentries; j++) { |
| int chain_jmp; |
| |
| 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: |
| case 2: |
| if (chain_jmp == chain_nr) { |
| if (type == 2) |
| return 1; |
| ebt_print_error("Can't delete the chain '%s', it's referenced in chain '%s', rule %d", |
| replace->chains[chain_nr + NF_BR_NUMHOOKS]->name, 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); |
| } |
| |
| /* 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) |
| ebt_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) |
| ebt_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) |
| ebt_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 ebt_print_bug() */ |
| void __ebt_print_bug(char *file, int line, char *format, ...) |
| { |
| va_list l; |
| |
| va_start(l, format); |
| fprintf(stderr, PROGNAME" v"PROGVERSION":%s:%d:--BUG--: \n", file, line); |
| vfprintf(stderr, format, l); |
| fprintf(stderr, "\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 ebt_print_error() */ |
| void __ebt_print_error(char *format, ...) |
| { |
| va_list l; |
| |
| va_start(l, format); |
| if (ebt_silent && ebt_errormsg[0] == '\0') { |
| vsnprintf(ebt_errormsg, ERRORMSG_MAXLEN, format, l); |
| va_end(l); |
| } else { |
| vfprintf(stderr, format, l); |
| fprintf(stderr, ".\n"); |
| va_end(l); |
| exit (-1); |
| } |
| } |