From 1abc55d3114378b4e73ec315eac6b122e55148c4 Mon Sep 17 00:00:00 2001 From: Bart De Schuymer Date: Sat, 1 Jun 2002 19:23:47 +0000 Subject: Initial revision --- communication.c | 454 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 454 insertions(+) create mode 100644 communication.c (limited to 'communication.c') diff --git a/communication.c b/communication.c new file mode 100644 index 0000000..02aff3f --- /dev/null +++ b/communication.c @@ -0,0 +1,454 @@ +/* + * communication.c, v2.0 April 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, lots of linked lists. +// So, the translation is done here. + +#include +#include +#include +#include +#include +#include +#include +#include // the database +#include // IPPROTO_IP +#include +#include "include/ebtables_u.h" + +extern char* hooknames[NF_BR_NUMHOOKS]; + +int sockfd = -1; + +void get_sockfd() +{ + if (sockfd == -1) { + sockfd = socket(AF_INET, SOCK_RAW, PF_INET); + if (sockfd < 0) + print_error("Problem getting a socket"); + } +} + +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; + char *p, *base; + int i, j; + unsigned int entries_size = 0; + + new = (struct ebt_replace *)malloc(sizeof(struct ebt_replace)); + if (!new) + print_memory(); + new->valid_hooks = u_repl->valid_hooks; + memcpy(new->name, u_repl->name, sizeof(new->name)); + new->nentries = u_repl->nentries; + new->num_counters = u_repl->num_counters; + new->counters = u_repl->counters; + memcpy(new->counter_entry, u_repl->counter_entry, + sizeof(new->counter_entry)); + // determine size + for (i = 0; i < NF_BR_NUMHOOKS; i++) { + if (!(new->valid_hooks & (1 << i))) + continue; + entries_size += sizeof(struct ebt_entries); + j = 0; + e = u_repl->hook_entry[i]->entries; + while (e) { + 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 != u_repl->hook_entry[i]->nentries) + print_bug("Wrong nentries: %d != %d, hook = %s", j, + u_repl->hook_entry[i]->nentries, hooknames[i]); + } + + new->entries_size = entries_size; + new->entries = (char *)malloc(entries_size); + if (!new->entries) + print_memory(); + + // put everything in one block + p = new->entries; + for (i = 0; i < NF_BR_NUMHOOKS; i++) { + struct ebt_entries *hlp; + + if (!(new->valid_hooks & (1 << i))) + continue; + hlp = (struct ebt_entries *)p; + new->hook_entry[i] = hlp; + hlp->nentries = u_repl->hook_entry[i]->nentries; + hlp->policy = u_repl->hook_entry[i]->policy; + hlp->distinguisher = 0; // make the kernel see the light + p += sizeof(struct ebt_entries); + e = u_repl->hook_entry[i]->entries; + while (e) { + struct ebt_entry *tmp = (struct ebt_entry *)p; + + tmp->bitmask = e->bitmask | EBT_ENTRY_OR_ENTRIES; + tmp->invflags = e->invflags; + tmp->ethproto = e->ethproto; + memcpy(tmp->in, e->in, sizeof(tmp->in)); + memcpy(tmp->out, e->out, sizeof(tmp->out)); + memcpy(tmp->logical_in, e->logical_in, + sizeof(tmp->logical_in)); + memcpy(tmp->logical_out, e->logical_out, + sizeof(tmp->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)); + p += e->t->target_size + + sizeof(struct ebt_entry_target); + tmp->next_offset = p - base; + e = e->next; + } + } + + // sanity check + if (p - new->entries != new->entries_size) + print_bug("Entries_size bug"); + return new; +} + +void 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); + get_sockfd(); + // give the data to the kernel + optlen = sizeof(struct ebt_replace) + repl->entries_size; + if (setsockopt(sockfd, IPPROTO_IP, EBT_SO_SET_ENTRIES, repl, optlen)) + print_error("Couldn't update kernel chains, you probably need " + "to insmod an extension"); +} + +// gets executed after deliver_table +void +deliver_counters(struct ebt_u_replace *u_repl, unsigned short *counterchanges) +{ + unsigned short *point; + struct ebt_counter *old, *new, *newcounters; + socklen_t optlen; + struct ebt_replace repl; + + if (u_repl->nentries == 0) + return; + + newcounters = (struct ebt_counter *) + malloc(u_repl->nentries * sizeof(struct ebt_counter)); + if (!newcounters) + print_memory(); + memset(newcounters, 0, u_repl->nentries * sizeof(struct ebt_counter)); + old = u_repl->counters; + new = newcounters; + point = counterchanges; + while (*point != CNT_END) { + if (*point == CNT_NORM) { + // 'normal' rule, meaning we didn't do anything to it + // So, we just copy + new->pcnt = old->pcnt; + // we've used an old counter + old++; + // we've set a new counter + new++; + } else + if (*point == CNT_DEL) { + // don't use this old counter + old++; + } else if (*point == CNT_ADD) { + // new counter, let it stay 0 + new++; + } else { + // zero it + new->pcnt = 0; + old++; + new++; + } + point++; + } + + free(u_repl->counters); + u_repl->counters = newcounters; + u_repl->num_counters = u_repl->nentries; + 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 = u_repl->counters; + repl.num_counters = u_repl->num_counters; + memcpy(repl.name, u_repl->name, sizeof(repl.name)); + + get_sockfd(); + if (setsockopt(sockfd, IPPROTO_IP, EBT_SO_SET_COUNTERS, &repl, optlen)) + 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; + + new = (struct ebt_u_match_list *) + malloc(sizeof(struct ebt_u_match_list)); + if (!new) + print_memory(); + new->m = (struct ebt_entry_match *) + malloc(m->match_size + sizeof(struct ebt_entry_match)); + if (!new->m) + print_memory(); + memcpy(new->m, m, m->match_size + sizeof(struct ebt_entry_match)); + new->next = NULL; + **l = new; + *l = &new->next; + if (find_match(new->m->u.name) == NULL) + print_error("Kernel match %s unsupported by userspace tool", + new->m->u.name); + return 0; +} + +static int +ebt_translate_watcher(struct ebt_entry_watcher *w, + struct ebt_u_watcher_list ***l) +{ + struct ebt_u_watcher_list *new; + + new = (struct ebt_u_watcher_list *) + malloc(sizeof(struct ebt_u_watcher_list)); + if (!new) + print_memory(); + new->w = (struct ebt_entry_watcher *) + malloc(w->watcher_size + sizeof(struct ebt_entry_watcher)); + if (!new->w) + print_memory(); + memcpy(new->w, w, w->watcher_size + sizeof(struct ebt_entry_watcher)); + new->next = NULL; + **l = new; + *l = &new->next; + if (find_watcher(new->w->u.name) == NULL) + print_error("Kernel watcher %s unsupported by userspace tool", + new->w->u.name); + return 0; +} + +static int +ebt_translate_entry(struct ebt_entry *e, unsigned int *hook, int *n, int *cnt, + int *totalcnt, struct ebt_u_entry ***u_e, struct ebt_u_replace *u_repl, + unsigned int valid_hooks) +{ + // 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) + 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; + memcpy(new->in, e->in, sizeof(new->in)); + memcpy(new->out, e->out, sizeof(new->out)); + memcpy(new->logical_in, e->logical_in, + sizeof(new->logical_in)); + memcpy(new->logical_out, e->logical_out, + sizeof(new->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)); + new->m_list = NULL; + new->w_list = NULL; + new->next = NULL; + 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) + print_memory(); + if (find_target(t->u.name) == NULL) + print_error("Kernel target %s unsupported by " + "userspace tool", t->u.name); + memcpy(new->t, t, t->target_size + + sizeof(struct ebt_entry_target)); + + // I love pointers + **u_e = new; + *u_e = &new->next; + (*cnt)++; + (*totalcnt)++; + return 0; + } else { // a new chain + int i; + struct ebt_entries *entries = (struct ebt_entries *)e; + struct ebt_u_entries *new; + + for (i = *hook + 1; i < NF_BR_NUMHOOKS; i++) + if (valid_hooks & (1 << i)) + break; + if (i >= NF_BR_NUMHOOKS) + print_bug("Not enough valid hooks"); + *hook = i; + if (*n != *cnt) + print_bug("Nr of entries in the chain is wrong"); + *n = entries->nentries; + *cnt = 0; + new = (struct ebt_u_entries *) + malloc(sizeof(struct ebt_u_entries)); + if (!new) + print_memory(); + new->nentries = entries->nentries; + new->policy = entries->policy; + new->entries = NULL; + u_repl->hook_entry[*hook] = new; + *u_e = &new->entries; + return 0; + } +} + +// talk with kernel to receive the kernel's table +void get_table(struct ebt_u_replace *u_repl) +{ + int i, j, k, hook; + socklen_t optlen; + struct ebt_replace repl; + struct ebt_u_entry **u_e; + + get_sockfd(); + + optlen = sizeof(struct ebt_replace); + strcpy(repl.name, u_repl->name); + if (getsockopt(sockfd, IPPROTO_IP, EBT_SO_GET_INFO, &repl, &optlen)) + print_error("A kernel module needed by your command is probably" + " not loaded. Try insmod ebtables or" + " insmod ebtable_%s", repl.name); + + 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))) ) + print_memory(); + } + else + repl.counters = NULL; + + // we want to receive the 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)) + print_bug("hmm, what is wrong??? bug#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; + u_repl->nentries = repl.nentries; + u_repl->num_counters = repl.num_counters; + u_repl->counters = repl.counters; + memcpy(u_repl->counter_entry, repl.counter_entry, + sizeof(repl.counter_entry)); + hook = -1; + 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 + EBT_ENTRY_ITERATE(repl.entries, repl.entries_size, ebt_translate_entry, + &hook, &i, &j, &k, &u_e, u_repl, u_repl->valid_hooks); + if (k != u_repl->nentries) + print_bug("Wrong total nentries"); +} + +void get_dbinfo(struct brdb_dbinfo *nr) +{ + socklen_t optlen = sizeof(struct brdb_dbinfo); + + get_sockfd(); + + if (getsockopt(sockfd, IPPROTO_IP, BRDB_SO_GET_DBINFO, nr, &optlen)) + print_error("Sorry, br_db code probably not in kernel, " + "try insmod br_db"); +} + +void get_db(int len, struct brdb_dbentry *db) +{ + socklen_t optlen = len; + + get_sockfd(); + + if ( getsockopt(sockfd, IPPROTO_IP, BRDB_SO_GET_DB, db, &optlen) ) { + print_bug("hmm, what is wrong??? bug#2"); + } +} + +void deliver_allowdb(__u16 *decision) +{ + socklen_t optlen = sizeof(__u16); + + get_sockfd(); + + if (setsockopt(sockfd, IPPROTO_IP, BRDB_SO_SET_ALLOWDB, + decision, optlen)) + print_error("Sorry, br_db code probably not in kernel, " + "try insmod br_db"); +} -- cgit v1.2.3