diff options
Diffstat (limited to 'tools/remus/imqebt/communication.c')
-rw-r--r-- | tools/remus/imqebt/communication.c | 762 |
1 files changed, 762 insertions, 0 deletions
diff --git a/tools/remus/imqebt/communication.c b/tools/remus/imqebt/communication.c new file mode 100644 index 0000000000..0c58c8fbe4 --- /dev/null +++ b/tools/remus/imqebt/communication.c @@ -0,0 +1,762 @@ +/* + * communication.c, v2.0 July 2002 + * + * Author: Bart De Schuymer + * + */ + +/* + * All the userspace/kernel communication is in this file. + * The other code should not have to know anything about the way the + * kernel likes the structure of the table data. + * The other code works with linked lists. So, the translation is done here. + */ + +#include <getopt.h> +#include <string.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <fcntl.h> +#include <unistd.h> +#include <sys/socket.h> +#include "include/ebtables_u.h" + +extern char* hooknames[NF_BR_NUMHOOKS]; + +#ifdef KERNEL_64_USERSPACE_32 +#define sparc_cast (uint64_t) +#else +#define sparc_cast +#endif + +int sockfd = -1; + +static int get_sockfd(void) +{ + int ret = 0; + if (sockfd == -1) { + sockfd = socket(AF_INET, SOCK_RAW, PF_INET); + if (sockfd < 0) { + ebt_print_error("Problem getting a socket, " + "you probably don't have the right " + "permissions"); + ret = -1; + } + } + return ret; +} + +static struct ebt_replace *translate_user2kernel(struct ebt_u_replace *u_repl) +{ + struct ebt_replace *new; + struct ebt_u_entry *e; + struct ebt_u_match_list *m_l; + struct ebt_u_watcher_list *w_l; + struct ebt_u_entries *entries; + char *p, *base; + int i, j; + unsigned int entries_size = 0, *chain_offsets; + + new = (struct ebt_replace *)malloc(sizeof(struct ebt_replace)); + if (!new) + ebt_print_memory(); + new->valid_hooks = u_repl->valid_hooks; + strcpy(new->name, u_repl->name); + new->nentries = u_repl->nentries; + new->num_counters = u_repl->num_counters; + new->counters = sparc_cast u_repl->counters; + chain_offsets = (unsigned int *)malloc(u_repl->num_chains * sizeof(unsigned int)); + /* Determine size */ + for (i = 0; i < u_repl->num_chains; i++) { + if (!(entries = u_repl->chains[i])) + continue; + chain_offsets[i] = entries_size; + entries_size += sizeof(struct ebt_entries); + j = 0; + e = entries->entries->next; + while (e != entries->entries) { + j++; + entries_size += sizeof(struct ebt_entry); + m_l = e->m_list; + while (m_l) { + entries_size += m_l->m->match_size + + sizeof(struct ebt_entry_match); + m_l = m_l->next; + } + w_l = e->w_list; + while (w_l) { + entries_size += w_l->w->watcher_size + + sizeof(struct ebt_entry_watcher); + w_l = w_l->next; + } + entries_size += e->t->target_size + + sizeof(struct ebt_entry_target); + e = e->next; + } + /* A little sanity check */ + if (j != entries->nentries) + ebt_print_bug("Wrong nentries: %d != %d, hook = %s", j, + entries->nentries, entries->name); + } + + new->entries_size = entries_size; + p = (char *)malloc(entries_size); + if (!p) + ebt_print_memory(); + + /* Put everything in one block */ + new->entries = sparc_cast p; + for (i = 0; i < u_repl->num_chains; i++) { + struct ebt_entries *hlp; + + hlp = (struct ebt_entries *)p; + if (!(entries = u_repl->chains[i])) + continue; + if (i < NF_BR_NUMHOOKS) + new->hook_entry[i] = sparc_cast hlp; + hlp->nentries = entries->nentries; + hlp->policy = entries->policy; + strcpy(hlp->name, entries->name); + hlp->counter_offset = entries->counter_offset; + hlp->distinguisher = 0; /* Make the kernel see the light */ + p += sizeof(struct ebt_entries); + e = entries->entries->next; + while (e != entries->entries) { + struct ebt_entry *tmp = (struct ebt_entry *)p; + + tmp->bitmask = e->bitmask | EBT_ENTRY_OR_ENTRIES; + tmp->invflags = e->invflags; + tmp->ethproto = e->ethproto; + strcpy(tmp->in, e->in); + strcpy(tmp->out, e->out); + strcpy(tmp->logical_in, e->logical_in); + strcpy(tmp->logical_out, e->logical_out); + memcpy(tmp->sourcemac, e->sourcemac, + sizeof(tmp->sourcemac)); + memcpy(tmp->sourcemsk, e->sourcemsk, + sizeof(tmp->sourcemsk)); + memcpy(tmp->destmac, e->destmac, sizeof(tmp->destmac)); + memcpy(tmp->destmsk, e->destmsk, sizeof(tmp->destmsk)); + + base = p; + p += sizeof(struct ebt_entry); + m_l = e->m_list; + while (m_l) { + memcpy(p, m_l->m, m_l->m->match_size + + sizeof(struct ebt_entry_match)); + p += m_l->m->match_size + + sizeof(struct ebt_entry_match); + m_l = m_l->next; + } + tmp->watchers_offset = p - base; + w_l = e->w_list; + while (w_l) { + memcpy(p, w_l->w, w_l->w->watcher_size + + sizeof(struct ebt_entry_watcher)); + p += w_l->w->watcher_size + + sizeof(struct ebt_entry_watcher); + w_l = w_l->next; + } + tmp->target_offset = p - base; + memcpy(p, e->t, e->t->target_size + + sizeof(struct ebt_entry_target)); + if (!strcmp(e->t->u.name, EBT_STANDARD_TARGET)) { + struct ebt_standard_target *st = + (struct ebt_standard_target *)p; + /* Translate the jump to a udc */ + if (st->verdict >= 0) + st->verdict = chain_offsets + [st->verdict + NF_BR_NUMHOOKS]; + } + p += e->t->target_size + + sizeof(struct ebt_entry_target); + tmp->next_offset = p - base; + e = e->next; + } + } + + /* Sanity check */ + if (p - (char *)new->entries != new->entries_size) + ebt_print_bug("Entries_size bug"); + free(chain_offsets); + return new; +} + +static void store_table_in_file(char *filename, struct ebt_replace *repl) +{ + char *data; + int size; + int fd; + + /* Start from an empty file with right priviliges */ + if (!(fd = creat(filename, 0600))) { + ebt_print_error("Couldn't create file %s", filename); + return; + } + + size = sizeof(struct ebt_replace) + repl->entries_size + + repl->nentries * sizeof(struct ebt_counter); + data = (char *)malloc(size); + if (!data) + ebt_print_memory(); + memcpy(data, repl, sizeof(struct ebt_replace)); + memcpy(data + sizeof(struct ebt_replace), (char *)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 (write(fd, data, size) != size) + ebt_print_error("Couldn't write everything to file %s", + filename); + close(fd); + free(data); +} + +void ebt_deliver_table(struct ebt_u_replace *u_repl) +{ + socklen_t optlen; + struct ebt_replace *repl; + + /* Translate the struct ebt_u_replace to a struct ebt_replace */ + repl = translate_user2kernel(u_repl); + if (u_repl->filename != NULL) { + store_table_in_file(u_repl->filename, repl); + goto free_repl; + } + /* Give the data to the kernel */ + optlen = sizeof(struct ebt_replace) + repl->entries_size; + if (get_sockfd()) + goto free_repl; + if (!setsockopt(sockfd, IPPROTO_IP, EBT_SO_SET_ENTRIES, repl, optlen)) + goto free_repl; + if (u_repl->command == 8) { /* The ebtables module may not + * yet be loaded with --atomic-commit */ + ebtables_insmod("ebtables"); + if (!setsockopt(sockfd, IPPROTO_IP, EBT_SO_SET_ENTRIES, + repl, optlen)) + goto free_repl; + } + + ebt_print_error("The kernel doesn't support a certain ebtables" + " extension, consider recompiling your kernel or insmod" + " the extension"); +free_repl: + if (repl) { + free(repl->entries); + free(repl); + } +} + +static int store_counters_in_file(char *filename, struct ebt_u_replace *repl) +{ + int size = repl->nentries * sizeof(struct ebt_counter), ret = 0; + unsigned int entries_size; + struct ebt_replace hlp; + FILE *file; + + if (!(file = fopen(filename, "r+b"))) { + ebt_print_error("Could not open file %s", filename); + return -1; + } + /* 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)) { + ebt_print_error("File %s is corrupt", filename); + ret = -1; + goto close_file; + } + if (fwrite(repl->counters, sizeof(char), size, file) != size) { + ebt_print_error("Could not write everything to file %s", + filename); + ret = -1; + } +close_file: + fclose(file); + return 0; +} + +/* Gets executed after ebt_deliver_table. Delivers the counters to the kernel + * and resets the counterchanges to CNT_NORM */ +void ebt_deliver_counters(struct ebt_u_replace *u_repl) +{ + struct ebt_counter *old, *new, *newcounters; + socklen_t optlen; + struct ebt_replace repl; + struct ebt_cntchanges *cc = u_repl->cc->next, *cc2; + struct ebt_u_entries *entries = NULL; + struct ebt_u_entry *next = NULL; + int i, chainnr = 0; + + if (u_repl->nentries == 0) + return; + + newcounters = (struct ebt_counter *) + malloc(u_repl->nentries * sizeof(struct ebt_counter)); + if (!newcounters) + ebt_print_memory(); + memset(newcounters, 0, u_repl->nentries * sizeof(struct ebt_counter)); + old = u_repl->counters; + new = newcounters; + while (cc != u_repl->cc) { + if (!next || next == entries->entries) { + while (chainnr < u_repl->num_chains && (!(entries = u_repl->chains[chainnr++]) || + (next = entries->entries->next) == entries->entries)); + if (chainnr == u_repl->num_chains) + break; + } + if (cc->type == CNT_NORM) { + /* 'Normal' rule, meaning we didn't do anything to it + * So, we just copy */ + *new = *old; + next->cnt = *new; + next->cnt_surplus.pcnt = next->cnt_surplus.bcnt = 0; + old++; /* We've used an old counter */ + new++; /* We've set a new counter */ + next = next->next; + } else if (cc->type == CNT_DEL) { + old++; /* Don't use this old counter */ + } else { + if (cc->type == CNT_CHANGE) { + if (cc->change % 3 == 1) + new->pcnt = old->pcnt + next->cnt_surplus.pcnt; + else if (cc->change % 3 == 2) + new->pcnt = old->pcnt - next->cnt_surplus.pcnt; + else + new->pcnt = next->cnt.pcnt; + if (cc->change / 3 == 1) + new->bcnt = old->bcnt + next->cnt_surplus.bcnt; + else if (cc->change / 3 == 2) + new->bcnt = old->bcnt - next->cnt_surplus.bcnt; + else + new->bcnt = next->cnt.bcnt; + } else + *new = next->cnt; + next->cnt = *new; + next->cnt_surplus.pcnt = next->cnt_surplus.bcnt = 0; + if (cc->type == CNT_ADD) + new++; + else { + old++; + new++; + } + next = next->next; + } + cc = cc->next; + } + + free(u_repl->counters); + u_repl->counters = newcounters; + u_repl->num_counters = u_repl->nentries; + /* Reset the counterchanges to CNT_NORM and delete the unused cc */ + i = 0; + cc = u_repl->cc->next; + while (cc != u_repl->cc) { + if (cc->type == CNT_DEL) { + cc->prev->next = cc->next; + cc->next->prev = cc->prev; + cc2 = cc->next; + free(cc); + cc = cc2; + } else { + cc->type = CNT_NORM; + cc->change = 0; + i++; + cc = cc->next; + } + } + if (i != u_repl->nentries) + ebt_print_bug("i != 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 */ + repl.counters = sparc_cast u_repl->counters; + repl.num_counters = u_repl->num_counters; + memcpy(repl.name, u_repl->name, sizeof(repl.name)); + + if (get_sockfd()) + return; + if (setsockopt(sockfd, IPPROTO_IP, EBT_SO_SET_COUNTERS, &repl, optlen)) + ebt_print_bug("Couldn't update kernel counters"); +} + +static int +ebt_translate_match(struct ebt_entry_match *m, struct ebt_u_match_list ***l) +{ + struct ebt_u_match_list *new; + int ret = 0; + + new = (struct ebt_u_match_list *) + malloc(sizeof(struct ebt_u_match_list)); + if (!new) + ebt_print_memory(); + new->m = (struct ebt_entry_match *) + malloc(m->match_size + sizeof(struct ebt_entry_match)); + if (!new->m) + ebt_print_memory(); + memcpy(new->m, m, m->match_size + sizeof(struct ebt_entry_match)); + new->next = NULL; + **l = new; + *l = &new->next; + if (ebt_find_match(new->m->u.name) == NULL) { + ebt_print_error("Kernel match %s unsupported by userspace tool", + new->m->u.name); + ret = -1; + } + return ret; +} + +static int +ebt_translate_watcher(struct ebt_entry_watcher *w, + struct ebt_u_watcher_list ***l) +{ + struct ebt_u_watcher_list *new; + int ret = 0; + + new = (struct ebt_u_watcher_list *) + malloc(sizeof(struct ebt_u_watcher_list)); + if (!new) + ebt_print_memory(); + new->w = (struct ebt_entry_watcher *) + malloc(w->watcher_size + sizeof(struct ebt_entry_watcher)); + if (!new->w) + ebt_print_memory(); + memcpy(new->w, w, w->watcher_size + sizeof(struct ebt_entry_watcher)); + new->next = NULL; + **l = new; + *l = &new->next; + if (ebt_find_watcher(new->w->u.name) == NULL) { + ebt_print_error("Kernel watcher %s unsupported by userspace " + "tool", new->w->u.name); + ret = -1; + } + return ret; +} + +static int +ebt_translate_entry(struct ebt_entry *e, int *hook, int *n, int *cnt, + int *totalcnt, struct ebt_u_entry **u_e, struct ebt_u_replace *u_repl, + unsigned int valid_hooks, char *base, struct ebt_cntchanges **cc) +{ + /* An entry */ + if (e->bitmask & EBT_ENTRY_OR_ENTRIES) { + struct ebt_u_entry *new; + struct ebt_u_match_list **m_l; + struct ebt_u_watcher_list **w_l; + struct ebt_entry_target *t; + + new = (struct ebt_u_entry *)malloc(sizeof(struct ebt_u_entry)); + if (!new) + ebt_print_memory(); + new->bitmask = e->bitmask; + /* + * Plain userspace code doesn't know about + * EBT_ENTRY_OR_ENTRIES + */ + new->bitmask &= ~EBT_ENTRY_OR_ENTRIES; + new->invflags = e->invflags; + new->ethproto = e->ethproto; + strcpy(new->in, e->in); + strcpy(new->out, e->out); + strcpy(new->logical_in, e->logical_in); + strcpy(new->logical_out, e->logical_out); + memcpy(new->sourcemac, e->sourcemac, sizeof(new->sourcemac)); + memcpy(new->sourcemsk, e->sourcemsk, sizeof(new->sourcemsk)); + memcpy(new->destmac, e->destmac, sizeof(new->destmac)); + memcpy(new->destmsk, e->destmsk, sizeof(new->destmsk)); + if (*totalcnt >= u_repl->nentries) + ebt_print_bug("*totalcnt >= u_repl->nentries"); + new->cnt = u_repl->counters[*totalcnt]; + new->cnt_surplus.pcnt = new->cnt_surplus.bcnt = 0; + new->cc = *cc; + *cc = (*cc)->next; + new->m_list = NULL; + new->w_list = NULL; + new->next = (*u_e)->next; + new->next->prev = new; + (*u_e)->next = new; + new->prev = *u_e; + *u_e = new; + m_l = &new->m_list; + EBT_MATCH_ITERATE(e, ebt_translate_match, &m_l); + w_l = &new->w_list; + EBT_WATCHER_ITERATE(e, ebt_translate_watcher, &w_l); + + t = (struct ebt_entry_target *)(((char *)e) + e->target_offset); + new->t = (struct ebt_entry_target *) + malloc(t->target_size + sizeof(struct ebt_entry_target)); + if (!new->t) + ebt_print_memory(); + if (ebt_find_target(t->u.name) == NULL) { + ebt_print_error("Kernel target %s unsupported by " + "userspace tool", t->u.name); + return -1; + } + memcpy(new->t, t, t->target_size + + sizeof(struct ebt_entry_target)); + /* Deal with jumps to udc */ + if (!strcmp(t->u.name, EBT_STANDARD_TARGET)) { + char *tmp = base; + int verdict = ((struct ebt_standard_target *)t)->verdict; + int i; + + if (verdict >= 0) { + tmp += verdict; + for (i = NF_BR_NUMHOOKS; i < u_repl->num_chains; i++) + if (u_repl->chains[i]->kernel_start == tmp) + break; + if (i == u_repl->num_chains) + ebt_print_bug("Can't find udc for jump"); + ((struct ebt_standard_target *)new->t)->verdict = i-NF_BR_NUMHOOKS; + } + } + + (*cnt)++; + (*totalcnt)++; + return 0; + } else { /* A new chain */ + int i; + struct ebt_entries *entries = (struct ebt_entries *)e; + + if (*n != *cnt) + ebt_print_bug("Nr of entries in the chain is wrong"); + *n = entries->nentries; + *cnt = 0; + for (i = *hook + 1; i < NF_BR_NUMHOOKS; i++) + if (valid_hooks & (1 << i)) + break; + *hook = i; + *u_e = u_repl->chains[*hook]->entries; + return 0; + } +} + +/* Initialize all chain headers */ +static int +ebt_translate_chains(struct ebt_entry *e, int *hook, + struct ebt_u_replace *u_repl, unsigned int valid_hooks) +{ + int i; + struct ebt_entries *entries = (struct ebt_entries *)e; + struct ebt_u_entries *new; + + if (!(e->bitmask & EBT_ENTRY_OR_ENTRIES)) { + for (i = *hook + 1; i < NF_BR_NUMHOOKS; i++) + if (valid_hooks & (1 << i)) + break; + new = (struct ebt_u_entries *)malloc(sizeof(struct ebt_u_entries)); + if (!new) + ebt_print_memory(); + if (i == u_repl->max_chains) + ebt_double_chains(u_repl); + u_repl->chains[i] = new; + if (i >= NF_BR_NUMHOOKS) + new->kernel_start = (char *)e; + *hook = i; + new->nentries = entries->nentries; + new->policy = entries->policy; + 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->counter_offset = entries->counter_offset; + strcpy(new->name, entries->name); + } + return 0; +} + +static int retrieve_from_file(char *filename, struct ebt_replace *repl, + char command) +{ + FILE *file; + char *hlp = NULL, *entries; + struct ebt_counter *counters; + int size, ret = 0; + + if (!(file = fopen(filename, "r+b"))) { + ebt_print_error("Could not open file %s", filename); + return -1; + } + /* 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) + 1); + if (!hlp) + ebt_print_memory(); + strcpy(hlp, repl->name); + } + if (fread(repl, sizeof(char), sizeof(struct ebt_replace), file) + != sizeof(struct ebt_replace)) { + ebt_print_error("File %s is corrupt", filename); + ret = -1; + goto close_file; + } + if (command != 'L' && command != 8 && strcmp(hlp, repl->name)) { + ebt_print_error("File %s contains wrong table name or is " + "corrupt", filename); + ret = -1; + goto close_file; + } else if (!ebt_find_table(repl->name)) { + ebt_print_error("File %s contains invalid table name", + filename); + ret = -1; + goto close_file; + } + + size = sizeof(struct ebt_replace) + + repl->nentries * sizeof(struct ebt_counter) + repl->entries_size; + fseek(file, 0, SEEK_END); + if (size != ftell(file)) { + ebt_print_error("File %s has wrong size", filename); + ret = -1; + goto close_file; + } + entries = (char *)malloc(repl->entries_size); + if (!entries) + ebt_print_memory(); + repl->entries = sparc_cast entries; + if (repl->nentries) { + counters = (struct ebt_counter *) + malloc(repl->nentries * sizeof(struct ebt_counter)); + repl->counters = sparc_cast counters; + if (!repl->counters) + ebt_print_memory(); + } else + repl->counters = sparc_cast NULL; + /* Copy entries and counters */ + if (fseek(file, sizeof(struct ebt_replace), SEEK_SET) || + fread((char *)repl->entries, sizeof(char), repl->entries_size, file) + != repl->entries_size || + fseek(file, sizeof(struct ebt_replace) + repl->entries_size, + SEEK_SET) + || fread((char *)repl->counters, sizeof(char), + repl->nentries * sizeof(struct ebt_counter), file) + != repl->nentries * sizeof(struct ebt_counter)) { + ebt_print_error("File %s is corrupt", filename); + free(entries); + repl->entries = NULL; + ret = -1; + } +close_file: + fclose(file); + free(hlp); + return ret; +} + +static int retrieve_from_kernel(struct ebt_replace *repl, char command, + int init) +{ + socklen_t optlen; + int optname; + char *entries; + + optlen = sizeof(struct ebt_replace); + if (get_sockfd()) + return -1; + /* --atomic-init || --init-table */ + if (init) + optname = EBT_SO_GET_INIT_INFO; + else + optname = EBT_SO_GET_INFO; + if (getsockopt(sockfd, IPPROTO_IP, optname, repl, &optlen)) + return -1; + + if ( !(entries = (char *)malloc(repl->entries_size)) ) + ebt_print_memory(); + repl->entries = sparc_cast entries; + if (repl->nentries) { + struct ebt_counter *counters; + + if (!(counters = (struct ebt_counter *) + malloc(repl->nentries * sizeof(struct ebt_counter))) ) + ebt_print_memory(); + repl->counters = sparc_cast counters; + } + else + repl->counters = sparc_cast NULL; + + /* We want to receive the counters */ + repl->num_counters = repl->nentries; + optlen += repl->entries_size + repl->num_counters * + sizeof(struct ebt_counter); + if (init) + optname = EBT_SO_GET_INIT_ENTRIES; + else + optname = EBT_SO_GET_ENTRIES; + if (getsockopt(sockfd, IPPROTO_IP, optname, repl, &optlen)) + ebt_print_bug("Hmm, what is wrong??? bug#1"); + + return 0; +} + +int ebt_get_table(struct ebt_u_replace *u_repl, int init) +{ + int i, j, k, hook; + struct ebt_replace repl; + struct ebt_u_entry *u_e = NULL; + struct ebt_cntchanges *new_cc, *cc; + + strcpy(repl.name, u_repl->name); + if (u_repl->filename != NULL) { + if (init) + ebt_print_bug("Getting initial table data from a file is impossible"); + if (retrieve_from_file(u_repl->filename, &repl, u_repl->command)) + return -1; + /* -L with a wrong table name should be dealt with silently */ + strcpy(u_repl->name, repl.name); + } else if (retrieve_from_kernel(&repl, u_repl->command, init)) + return -1; + + /* Translate the struct ebt_replace to a struct ebt_u_replace */ + u_repl->valid_hooks = repl.valid_hooks; + u_repl->nentries = repl.nentries; + u_repl->num_counters = repl.num_counters; + u_repl->counters = repl.counters; + u_repl->cc = (struct ebt_cntchanges *)malloc(sizeof(struct ebt_cntchanges)); + if (!u_repl->cc) + ebt_print_memory(); + u_repl->cc->next = u_repl->cc->prev = u_repl->cc; + cc = u_repl->cc; + for (i = 0; i < repl.nentries; i++) { + new_cc = (struct ebt_cntchanges *)malloc(sizeof(struct ebt_cntchanges)); + if (!new_cc) + ebt_print_memory(); + new_cc->type = CNT_NORM; + new_cc->change = 0; + new_cc->prev = cc; + cc->next = new_cc; + cc = new_cc; + } + if (repl.nentries) { + new_cc->next = u_repl->cc; + u_repl->cc->prev = new_cc; + } + u_repl->chains = (struct ebt_u_entries **)calloc(EBT_ORI_MAX_CHAINS, sizeof(void *)); + u_repl->max_chains = EBT_ORI_MAX_CHAINS; + hook = -1; + /* FIXME: Clean up when an error is encountered */ + EBT_ENTRY_ITERATE(repl.entries, repl.entries_size, ebt_translate_chains, + &hook, u_repl, u_repl->valid_hooks); + if (hook >= NF_BR_NUMHOOKS) + u_repl->num_chains = hook + 1; + else + u_repl->num_chains = NF_BR_NUMHOOKS; + i = 0; /* Holds the expected nr. of entries for the chain */ + j = 0; /* Holds the up to now counted entries for the chain */ + k = 0; /* Holds the total nr. of entries, should equal u_repl->nentries afterwards */ + cc = u_repl->cc->next; + hook = -1; + EBT_ENTRY_ITERATE((char *)repl.entries, repl.entries_size, + ebt_translate_entry, &hook, &i, &j, &k, &u_e, u_repl, + u_repl->valid_hooks, (char *)repl.entries, &cc); + if (k != u_repl->nentries) + ebt_print_bug("Wrong total nentries"); + free(repl.entries); + return 0; +} |