From 624237440835da0dbbba05a97fd9c12a6e84f190 Mon Sep 17 00:00:00 2001 From: Bart De Schuymer Date: Sun, 14 Jul 2002 19:06:20 +0000 Subject: add --atomic options --- communication.c | 183 ++++++++++++++++++++++++++++++++++++++++++++++++++------ ebtables.c | 72 +++++++++++++++++----- 2 files changed, 222 insertions(+), 33 deletions(-) diff --git a/communication.c b/communication.c index 8599d48..3dee991 100644 --- a/communication.c +++ b/communication.c @@ -27,7 +27,7 @@ extern char* hooknames[NF_BR_NUMHOOKS]; int sockfd = -1; -void get_sockfd() +static void get_sockfd() { if (sockfd == -1) { sockfd = socket(AF_INET, SOCK_RAW, PF_INET); @@ -209,6 +209,47 @@ static struct ebt_replace * translate_user2kernel(struct ebt_u_replace *u_repl) return new; } +static void store_table_in_file(char *filename, struct ebt_replace *repl) +{ + char *command, *data; + int size; + FILE *file; + + // start from an empty file with right priviliges + command = (char *)malloc(strlen(filename) + 15); + if (!command) + print_memory(); + strcpy(command, "cat /dev/null>"); + strcpy(command + 14, filename); + if (system(command)) + print_error("Couldn't create file %s", filename); + strcpy(command, "chmod 600 "); + strcpy(command + 10, filename); + if (system(command)) + print_error("Couldn't chmod file %s", filename); + free(command); + + size = sizeof(struct ebt_replace) + repl->entries_size + + repl->nentries * sizeof(struct ebt_counter); + data = (char *)malloc(size); + if (!data) + print_memory(); + memcpy(data, repl, sizeof(struct ebt_replace)); + memcpy(data + sizeof(struct ebt_replace), repl->entries, + repl->entries_size); + // initialize counters to zero, deliver_counters() can update them + memset(data + sizeof(struct ebt_replace) + repl->entries_size, + 0, repl->nentries * sizeof(struct ebt_counter)); + if (!(file = fopen(filename, "wb"))) + print_error("Could not open file %s", filename); + if (fwrite(data, sizeof(char), size, file) != size) { + fclose(file); + print_error("Could not write everything to file %s", filename); + } + fclose(file); + free(data); +} + void deliver_table(struct ebt_u_replace *u_repl) { socklen_t optlen; @@ -216,15 +257,43 @@ void deliver_table(struct ebt_u_replace *u_repl) // translate the struct ebt_u_replace to a struct ebt_replace repl = translate_user2kernel(u_repl); - get_sockfd(); // give the data to the kernel optlen = sizeof(struct ebt_replace) + repl->entries_size; + if (u_repl->filename != NULL) { + store_table_in_file(u_repl->filename, repl); + return; + } + get_sockfd(); if (setsockopt(sockfd, IPPROTO_IP, EBT_SO_SET_ENTRIES, repl, optlen)) print_error("The kernel doesn't support a certain ebtables" " extension, consider recompiling your kernel or insmod" " the extension"); } +static void store_counters_in_file(char *filename, struct ebt_u_replace *repl) +{ + int size = repl->nentries * sizeof(struct ebt_counter); + int entries_size; + struct ebt_replace hlp; + FILE *file; + + if (!(file = fopen(filename, "r+b"))) + print_error("Could not open file %s", filename); + // find out entries_size and then set the file pointer to the counters + if (fseek(file, (char *)(&hlp.entries_size) - (char *)(&hlp), SEEK_SET) + || fread(&entries_size, sizeof(char), sizeof(unsigned int), file) != + sizeof(unsigned int) || + fseek(file, entries_size + sizeof(struct ebt_replace), SEEK_SET)) { + fclose(file); + print_error("File %s is corrupt", filename); + } + if (fwrite(repl->counters, sizeof(char), size, file) != size) { + fclose(file); + print_error("Could not write everything to file %s", filename); + } + fclose(file); +} + // gets executed after deliver_table void deliver_counters(struct ebt_u_replace *u_repl, unsigned short *counterchanges) @@ -273,6 +342,10 @@ deliver_counters(struct ebt_u_replace *u_repl, unsigned short *counterchanges) free(u_repl->counters); u_repl->counters = newcounters; u_repl->num_counters = u_repl->nentries; + if (u_repl->filename != NULL) { + store_counters_in_file(u_repl->filename, u_repl); + return; + } optlen = u_repl->nentries * sizeof(struct ebt_counter) + sizeof(struct ebt_replace); // now put the stuff in the kernel's struct ebt_replace @@ -484,38 +557,110 @@ ebt_translate_chains(struct ebt_entry *e, unsigned int *hook, return 0; } -// talk with kernel to receive the kernel's table -int get_table(struct ebt_u_replace *u_repl) +static void retrieve_from_file(char *filename, struct ebt_replace *repl, + char command) { - int i, j, k, hook; - socklen_t optlen; - struct ebt_replace repl; - struct ebt_u_entry **u_e; + FILE *file; + char *hlp; + int size; + + if (!(file = fopen(filename, "r+b"))) + print_error("Could not open file %s", filename); + // make sure table name is right if command isn't -L or --atomic-commit + if (command != 'L' && command != 8) { + hlp = (char *)malloc(strlen(repl->name)); + if (!hlp) + print_memory(); + strcpy(hlp, repl->name); + } else + if (!find_table(repl->name)) + print_error("File %s contains invalid table name", + filename); + if (fread(repl, sizeof(char), sizeof(struct ebt_replace), file) + != sizeof(struct ebt_replace)) + print_error("File %s is corrupt", filename); + if (command != 'L' && command != 8 && strcmp(hlp, repl->name)) + print_error("File %s contains wrong table name or is corrupt", + filename); + size = sizeof(struct ebt_replace) + + repl->nentries * sizeof(struct ebt_counter) + repl->entries_size; + fseek(file, 0, SEEK_END); + if (size != ftell(file)) + print_error("File %s has wrong size", filename); + repl->entries = (char *)malloc(repl->entries_size); + if (!repl->entries) + print_memory(); + if (repl->nentries) { + repl->counters = (struct ebt_counter *) + malloc(repl->nentries * sizeof(struct ebt_counter)); + if (!repl->counters) + print_memory(); + } else + repl->counters = NULL; + // copy entries and counters + if (fseek(file, sizeof(struct ebt_replace), SEEK_SET) || + fread(repl->entries, sizeof(char), repl->entries_size, file) + != repl->entries_size || + fseek(file, sizeof(struct ebt_replace) + repl->entries_size, SEEK_SET) + || fread(repl->counters, sizeof(char), + repl->nentries * sizeof(struct ebt_counter), file) + != repl->nentries * sizeof(struct ebt_counter)) + print_error("File %s is corrupt", filename); + fclose(file); +} - get_sockfd(); +static int retrieve_from_kernel(struct ebt_replace *repl, char command) +{ + socklen_t optlen; + int optname; optlen = sizeof(struct ebt_replace); - strcpy(repl.name, u_repl->name); - if (getsockopt(sockfd, IPPROTO_IP, EBT_SO_GET_INFO, &repl, &optlen)) + get_sockfd(); + if (command == 7) + optname = EBT_SO_GET_INIT_INFO; + else + optname = EBT_SO_GET_INFO; + if (getsockopt(sockfd, IPPROTO_IP, optname, repl, &optlen)) return -1; - if ( !(repl.entries = (char *) malloc(repl.entries_size)) ) + if ( !(repl->entries = (char *) malloc(repl->entries_size)) ) print_memory(); - if (repl.nentries) { - if (!(repl.counters = (struct ebt_counter *) - malloc(repl.nentries * sizeof(struct ebt_counter))) ) + if (repl->nentries) { + if (!(repl->counters = (struct ebt_counter *) + malloc(repl->nentries * sizeof(struct ebt_counter))) ) print_memory(); } else - repl.counters = NULL; + repl->counters = NULL; // we want to receive the counters - repl.num_counters = repl.nentries; - optlen += repl.entries_size + repl.num_counters * + repl->num_counters = repl->nentries; + optlen += repl->entries_size + repl->num_counters * sizeof(struct ebt_counter); - if (getsockopt(sockfd, IPPROTO_IP, EBT_SO_GET_ENTRIES, &repl, &optlen)) + if (command == 7) + optname = EBT_SO_GET_INIT_ENTRIES; + else + optname = EBT_SO_GET_ENTRIES; + if (getsockopt(sockfd, IPPROTO_IP, optname, repl, &optlen)) print_bug("hmm, what is wrong??? bug#1"); + return 0; +} + +// talk with kernel to receive the kernel's table +int get_table(struct ebt_u_replace *u_repl) +{ + int i, j, k, hook; + struct ebt_replace repl; + struct ebt_u_entry **u_e; + + strcpy(repl.name, u_repl->name); + if (u_repl->filename != NULL) + retrieve_from_file(u_repl->filename, &repl, u_repl->command); + else + if (retrieve_from_kernel(&repl, u_repl->command) == -1) + return -1; + // translate the struct ebt_replace to a struct ebt_u_replace memcpy(u_repl->name, repl.name, sizeof(u_repl->name)); u_repl->valid_hooks = repl.valid_hooks; diff --git a/ebtables.c b/ebtables.c index b9d20a4..a8e564a 100644 --- a/ebtables.c +++ b/ebtables.c @@ -62,7 +62,8 @@ char *hooknames[NF_BR_NUMHOOKS] = }; // default command line options -static struct option ebt_original_options[] = { +static struct option ebt_original_options[] = +{ { "append" , required_argument, 0, 'A' }, { "insert" , required_argument, 0, 'I' }, { "delete" , required_argument, 0, 'D' }, @@ -94,13 +95,19 @@ static struct option ebt_original_options[] = { { "new-chain" , required_argument, 0, 'N' }, { "rename-chain" , required_argument, 0, 'E' }, { "delete-chain" , required_argument, 0, 'X' }, + { "atomic-init" , required_argument, 0, 7 }, + // communication.c uses fact that atomic-commit equals 8 + { "atomic-commit" , required_argument, 0, 8 }, + { "atomic" , required_argument, 0, 9 }, + { "atomic-save" , required_argument, 0, 10 }, { 0 } }; static struct option *ebt_options = ebt_original_options; // yup, all the possible target names -char* standard_targets[NUM_STANDARD_TARGETS] = { +char* standard_targets[NUM_STANDARD_TARGETS] = +{ "ACCEPT", "DROP", "CONTINUE", @@ -172,7 +179,7 @@ struct ebt_u_table *find_table(char *name) // Same holds for the struct ebt_match and struct ebt_watcher pointers struct ebt_u_entry *new_entry; -void initialize_entry(struct ebt_u_entry *e) +static void initialize_entry(struct ebt_u_entry *e) { e->bitmask = EBT_NOPROTO; e->invflags = 0; @@ -191,7 +198,7 @@ void initialize_entry(struct ebt_u_entry *e) } // this doesn't free e, becoz the calling function might need e->next -void free_u_entry(struct ebt_u_entry *e) +static void 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; @@ -406,7 +413,7 @@ int ebtables_insmod(const char *modname, const char *modprobe) // used to parse /etc/ethertypes -int disregard_whitespace(char *buffer, FILE *ifp) +static int disregard_whitespace(char *buffer, FILE *ifp) { int hlp; @@ -419,7 +426,7 @@ int disregard_whitespace(char *buffer, FILE *ifp) } // used to parse /etc/ethertypes -int disregard_tabspace(char *buffer, FILE *ifp) +static int disregard_tabspace(char *buffer, FILE *ifp) { int hlp; @@ -432,7 +439,7 @@ int disregard_tabspace(char *buffer, FILE *ifp) } // helper function: processes a line of data from the file /etc/ethertypes -int get_a_line(char *buffer, char *value, FILE *ifp) +static int get_a_line(char *buffer, char *value, FILE *ifp) { int i, hlp; char anotherhlp; @@ -729,7 +736,7 @@ struct ebt_u_stack struct ebt_u_entries *entries; }; -void check_for_loops() +static void check_for_loops() { int chain_nr , i, j , k, sp = 0, verdict; struct ebt_u_entries *entries, *entries2; @@ -832,7 +839,7 @@ int get_hooknr(char* arg) } // yup, print out help -void print_help() +static void print_help() { struct ebt_u_match_list *m_l; struct ebt_u_watcher_list *w_l; @@ -857,6 +864,10 @@ void print_help() "--new-chain -N chain : Create a user defined chain\n" "--rename-chain -E old new : Rename a chain\n" "--delete-chain -X chain : Delete a user defined chain\n" +"--atomic-commit file : update the kernel w/ the table contained in file\n" +"--atomic-init file : put the initial kernel table into file\n" +"--atomic-save file : put the current kernel table into file\n" +"--atomic file : write changes to file instead of kernel\n" "Options:\n" "--proto -p [!] proto : protocol hexadecimal, by name or LENGTH\n" "--src -s [!] address[/mask]: source mac address\n" @@ -1327,7 +1338,7 @@ static void delete_rule(int rule_nr) } // execute command Z -void zero_counters(int zerochain) +static void zero_counters(int zerochain) { if (zerochain == -1) { @@ -1498,7 +1509,7 @@ int getmac_and_mask(char *from, char *to, char *mask) } // executes the final_check() function for all extensions used by the rule -void do_final_checks(struct ebt_u_entry *e, struct ebt_u_entries *entries) +static void do_final_checks(struct ebt_u_entry *e, struct ebt_u_entries *entries) { struct ebt_u_match_list *m_l; struct ebt_u_watcher_list *w_l; @@ -1526,7 +1537,7 @@ void do_final_checks(struct ebt_u_entry *e, struct ebt_u_entries *entries) } // used for the -X command -void check_for_references(int chain_nr) +static void check_for_references(int chain_nr) { int i = -1, j; struct ebt_u_entries *entries; @@ -1606,6 +1617,7 @@ int main(int argc, char *argv[]) replace.flags = 0; replace.selected_hook = -1; replace.command = 'h'; + replace.filename = NULL; new_entry = (struct ebt_u_entry *)malloc(sizeof(struct ebt_u_entry)); if (!new_entry) @@ -1614,7 +1626,8 @@ int main(int argc, char *argv[]) initialize_entry(new_entry); // The scenario induced by this loop makes that: - // '-t' and '-M' (if specified) have to come before '-A' and the like + // '-t' ,'-M' and --atomic (if specified) have to come + // before '-A' and the like // getopt saves the day while ((c = getopt_long(argc, argv, @@ -2083,6 +2096,37 @@ int main(int argc, char *argv[]) print_error("--Lx not compatible with --Ln"); replace.flags |= LIST_X; break; + case 8 : // atomic-commit + replace.command = c; + if (replace.flags & OPT_COMMAND) + print_error("Multiple commands not allowed"); + replace.flags |= OPT_COMMAND; + replace.filename = (char *)malloc(strlen(optarg) + 1); + strcpy(replace.filename, optarg); + // get the information from the file + get_table(&replace); + replace.num_counters = 0; + free(replace.filename); + replace.filename = NULL; + break; + case 7 : // atomic-init + case 10: // atomic-save + replace.command = c; + if (replace.flags & OPT_COMMAND) + print_error("Multiple commands not allowed"); + replace.flags |= OPT_COMMAND; + if ( !(table = find_table(replace.name)) ) + print_error("Bad table name"); + if (get_table(&replace)) { + ebtables_insmod("ebtables", modprobe); + if (get_table(&replace)) + print_error("can't initialize ebtables " + "table %s", replace.name); + } + case 9 : // atomic + replace.filename = (char *)malloc(strlen(optarg) + 1); + strcpy(replace.filename, optarg); + break; default: // is it a target option? @@ -2212,7 +2256,7 @@ check_extension: } } else if (replace.command == 'D') delete_rule(rule_nr); - // commands -N, -E, -X fall through + // commands -N, -E, -X, --atomic-commit fall through if (table->check) table->check(&replace); -- cgit v1.2.3