summaryrefslogtreecommitdiffstats
path: root/userspace/libebtc/src/ebtc.c
diff options
context:
space:
mode:
Diffstat (limited to 'userspace/libebtc/src/ebtc.c')
-rw-r--r--userspace/libebtc/src/ebtc.c2157
1 files changed, 2157 insertions, 0 deletions
diff --git a/userspace/libebtc/src/ebtc.c b/userspace/libebtc/src/ebtc.c
new file mode 100644
index 0000000..d7b68ba
--- /dev/null
+++ b/userspace/libebtc/src/ebtc.c
@@ -0,0 +1,2157 @@
+/*
+ * ==[ FILENAME: ebtc.c ]=======================================================
+ *
+ * Project
+ *
+ * Library for ethernet bridge tables.
+ *
+ *
+ * Description
+ *
+ * See project
+ *
+ *
+ * Copyright
+ *
+ * Copyright 2005 by Jens Götze
+ * All rights reserved.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
+ * USA.
+ *
+ *
+ * =============================================================================
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <linux/netfilter_bridge/ebtables.h>
+#include <libebtc.h>
+
+
+/* Macros */
+
+#define SUCCESS 0
+#define ERR_CHAINNOTFOUND 1
+#define ERR_CHAINEXIST 2
+#define ERR_POLICYNOTFOUND 3
+#define ERR_ALLOCATEMEM 4
+#define ERR_RULENUM 5
+#define ERR_BUILTINCHAIN 6
+#define ERR_RAWSOCKET 7
+#define ERR_GETINFO 8
+#define ERR_GETENTRIES 9
+#define ERR_SETENTRIES 10
+#define ERR_SETCOUNTERS 11
+#define ERR_BADENTRY 12
+#define ERR_STDCHAINNOTALLOW 13
+#define ERR_ENTRYTARGETINVALID 14
+
+
+/* Types */
+
+typedef struct rule_list_st rule_list_t;
+
+typedef struct chain_list_st chain_list_t;
+
+typedef struct chain2id_st chain2id_t;
+
+
+/* Structures */
+
+struct rule_list_st {
+
+ rule_list_t *next;
+
+ /* Payload */
+
+ struct ebt_entry *entry;
+
+ struct {
+
+ int changed;
+
+ int offset;
+
+ struct ebt_counter counter;
+
+ } counter;
+
+};
+
+struct chain_list_st {
+
+ chain_list_t *next;
+
+ int id; /* used for chain jump */
+
+ int offset;
+
+ /* Payload */
+
+ int hookid;
+
+ struct ebt_entries entries;
+
+ struct {
+
+ int count;
+
+ unsigned int size;
+
+ rule_list_t *first;
+
+ rule_list_t *cur;
+
+ rule_list_t *last;
+
+ } rules;
+
+};
+
+struct chain2id_st {
+
+ const char name[EBT_CHAIN_MAXNAMELEN];
+
+ int id;
+
+};
+
+struct ebtc_handle_st {
+
+ /* Communication backend to kernel */
+
+ int fd;
+
+ struct ebt_replace replace;
+
+ /* Errorhandling */
+
+ unsigned int error_no;
+
+ /* Cache */
+
+ struct {
+
+ unsigned int num_counters;
+
+ } cache;
+
+ /* Payload */
+
+ int changed;
+
+ struct {
+
+ int count;
+
+ int last_id;
+
+ chain_list_t *first;
+
+ chain_list_t *cur;
+
+ chain_list_t *last;
+
+ } chains;
+
+};
+
+
+/* Global variables */
+
+static chain2id_t targets[] = {
+ { "ACCEPT", EBT_ACCEPT },
+ { "DROP", EBT_DROP },
+ { "CONTINUE", EBT_CONTINUE },
+ { "RETURN", EBT_RETURN },
+ { "", 0 }
+};
+
+static chain2id_t builtinchains[] = {
+ { "PREROUTING", NF_BR_PRE_ROUTING, },
+ { "INPUT", NF_BR_LOCAL_IN },
+ { "FORWARD", NF_BR_FORWARD },
+ { "OUTPUT", NF_BR_LOCAL_OUT },
+ { "POSTROUTING", NF_BR_POST_ROUTING, },
+ { "BROUTING", NF_BR_BROUTING, },
+ { "", 0 }
+};
+
+static struct {
+
+ unsigned int id;
+
+ char *msg;
+
+} error_msg[] = {
+ { SUCCESS, "success" },
+ { ERR_CHAINNOTFOUND, "chain not found" },
+ { ERR_CHAINEXIST, "chain already exist" },
+ { ERR_POLICYNOTFOUND, "policy not exist" },
+ { ERR_ALLOCATEMEM, "can't allocate memory" },
+ { ERR_RULENUM, "rule number is out of range" },
+ { ERR_BUILTINCHAIN, "delete denied this chain is a builtin chain" },
+ { ERR_RAWSOCKET, "can't open raw socket" },
+ { ERR_GETINFO, "can't get informations" },
+ { ERR_GETENTRIES, "can't get entries" },
+ { ERR_SETENTRIES, "can't set entries" },
+ { ERR_SETCOUNTERS, "can't set counters" },
+ { ERR_BADENTRY, "entry is not valid" },
+ { ERR_STDCHAINNOTALLOW, "standard chain is not allowed" },
+ { ERR_ENTRYTARGETINVALID, "target of entry is invalid" }
+};
+
+static struct {
+
+ /* Errorhandling */
+
+ unsigned int error_no;
+
+} self_private;
+
+
+static chain_list_t *find_chain (const char *chainname,
+ const ebtc_handle_t handle)
+{
+
+/* ---- VAR ---- */
+
+ chain_list_t *chain = handle->chains.first;
+
+
+
+/* ---- CODE ---- */
+
+ for (; chain; chain = chain->next) {
+
+ if (!strcmp(chain->entries.name, chainname))
+ return chain;
+
+ }
+
+ return NULL;
+
+}
+
+
+static int check_entry (const ebt_entry_t *entry)
+{
+
+/* ---- VAR ---- */
+
+ ebt_standard_target_t *std_target;
+
+ ebt_entry_target_t *target;
+
+
+
+/* ---- CODE ---- */
+
+ if (!(entry->bitmask & EBT_ENTRY_OR_ENTRIES))
+ return -1;
+
+ /* Check offsets */
+
+ if (!entry->watchers_offset)
+ return -1;
+
+ if (!entry->target_offset)
+ return -1;
+
+ if (!entry->next_offset)
+ return -1;
+
+ if (entry->watchers_offset > entry->target_offset)
+ return -1;
+
+ if (entry->target_offset > entry->next_offset)
+ return -1;
+
+ return 0;
+
+}
+
+
+int ebtc_is_chain (const char *chainname, const ebtc_handle_t handle)
+{
+
+/* ---- CODE ---- */
+
+ return find_chain(chainname, handle) ? 0 : -1;
+
+}
+
+
+const char *ebtc_first_chain (ebtc_handle_t *handle)
+{
+
+/* ---- VAR ---- */
+
+ chain_list_t *chain;
+
+
+
+/* ---- CODE ---- */
+
+ (*handle)->chains.cur = chain = (*handle)->chains.first;
+
+ return chain->entries.name;
+
+}
+
+
+const char *ebtc_next_chain (ebtc_handle_t *handle)
+{
+
+/* ---- VAR ---- */
+
+ chain_list_t *chain;
+
+
+
+/* ---- CODE ---- */
+
+ chain = (*handle)->chains.cur;
+
+ if (!chain)
+ return NULL;
+
+ (*handle)->chains.cur = chain = chain->next;
+
+ return chain ? chain->entries.name : NULL;
+
+}
+
+
+const struct ebt_entry *ebtc_first_rule (const char *chainname,
+ ebtc_handle_t *handle)
+{
+
+/* ---- VAR ---- */
+
+ chain_list_t *chain;
+
+ rule_list_t *rule;
+
+
+
+/* ---- CODE ---- */
+
+ chain = find_chain(chainname, *handle);
+
+ if (!chain) {
+
+ (*handle)->error_no = ERR_CHAINNOTFOUND;
+
+ return NULL;
+
+ }
+
+ rule = chain->rules.cur = chain->rules.first;
+
+ return rule ? rule->entry : NULL;
+
+}
+
+
+const struct ebt_entry *ebtc_next_rule (const char *chainname,
+ ebtc_handle_t *handle)
+{
+
+/* ---- VAR ---- */
+
+ chain_list_t *chain;
+
+ rule_list_t *rule;
+
+
+
+/* ---- CODE ---- */
+
+ chain = find_chain(chainname, *handle);
+
+ if (!chain) {
+
+ (*handle)->error_no = ERR_CHAINNOTFOUND;
+
+ return NULL;
+
+ }
+
+ if (!chain->rules.cur)
+ return NULL;
+
+ rule = chain->rules.cur = chain->rules.cur->next;
+
+ (*handle)->error_no = SUCCESS;
+
+ return rule ? rule->entry : NULL;
+
+}
+
+
+const char *ebtc_get_target (const struct ebt_entry *entry,
+ const ebtc_handle_t *handle)
+{
+
+/* ---- VAR ---- */
+
+ char *ptr;
+
+ ebt_entry_target_t *target;
+
+ ebt_standard_target_t *std_target;
+
+ chain2id_t *chain2id;
+
+
+
+/* ---- CODE ---- */
+
+ ptr = (char *)entry;
+ ptr += entry->target_offset;
+
+ target = (struct ebt_entry_target *)ptr;
+
+ if (strcmp(target->u.name, "standard"))
+ return target->u.name;
+
+ /* Convert id to ascii */
+
+ std_target = (ebt_standard_target_t *)target;
+
+ for (chain2id = targets; *chain2id->name; chain2id++) {
+
+ if (std_target->verdict == chain2id->id)
+ return chain2id->name;
+
+ }
+
+ return NULL;
+
+}
+
+
+int ebtc_is_builtin (const char *chainname, const ebtc_handle_t *handle)
+{
+
+/* ---- VAR ---- */
+
+ chain_list_t *chain;
+
+
+
+/* ---- CODE ---- */
+
+ chain = find_chain(chainname, *handle);
+
+ if (!chain)
+ return -1;
+
+ return chain->hookid == NF_BR_NUMHOOKS ? -1 : 0;
+
+}
+
+
+int ebtc_set_policy (const char *chainname, const char *policy,
+ ebtc_handle_t *handle)
+{
+
+/* ---- VAR ---- */
+
+ int i;
+
+ int id = 0;
+
+ chain_list_t *chain = (*handle)->chains.first;
+
+
+
+/* ---- CODE ---- */
+
+ /* Transform policy to id */
+
+ for (i = 0; ; i++) {
+
+ if (!*targets[i].name) {
+
+ (*handle)->error_no = ERR_POLICYNOTFOUND;
+
+ return -1;
+
+ }
+
+ if (!strcmp(policy, targets[i].name)) {
+
+ id = targets[i].id;
+ break;
+
+ }
+
+ }
+
+ /* Search chain and set policy */
+
+ chain = find_chain(chainname, *handle);
+
+ if (!chain) {
+
+ (*handle)->error_no = ERR_CHAINNOTFOUND;
+
+ return -1;
+
+ }
+
+ if (chain->entries.policy != id) {
+
+ chain->entries.policy = id;
+ (*handle)->changed = EBTC_TRUE;
+
+ }
+
+ (*handle)->error_no = SUCCESS;
+
+ return 0;
+
+}
+
+
+const char *ebtc_get_policy (const char *chainname, const ebtc_handle_t *handle)
+{
+
+/* ---- VAR ---- */
+
+ int i;
+
+ chain_list_t *chain;
+
+ chain2id_t *chain2id;
+
+
+
+/* ---- CODE ---- */
+
+ /* Find chain */
+
+ chain = find_chain(chainname, *handle);
+
+ if (!chain) {
+
+ (*handle)->error_no = ERR_CHAINNOTFOUND;
+
+ return NULL;
+
+ }
+
+ /* Convert id to ascii */
+
+ for (chain2id = targets; *chain2id->name; chain2id++) {
+
+ if (chain->entries.policy == chain2id->id) {
+
+ (*handle)->error_no = SUCCESS;
+
+ return chain2id->name;
+
+ }
+
+ }
+
+ return NULL;
+
+}
+
+
+int ebtc_insert_entry (const char *chainname, const struct ebt_entry *entry,
+ unsigned int rulenum, ebtc_handle_t *handle)
+{
+
+/* ---- VAR ---- */
+
+ chain_list_t *chain;
+
+ rule_list_t *rule_last = NULL;
+
+ rule_list_t *rule_cur;
+
+ rule_list_t *rule;
+
+ unsigned int size;
+
+
+
+/* ---- CODE ---- */
+
+ if (check_entry(entry)) {
+
+ (*handle)->error_no = ERR_BADENTRY;
+
+ return -1;
+
+ }
+
+ /* Search for chain in chain list */
+
+ chain = find_chain(chainname, *handle);
+
+ if (!chain) {
+
+ (*handle)->error_no = ERR_CHAINNOTFOUND;
+
+ return -1;
+
+ }
+
+ /* Copy entry and append to chain */
+
+ size = sizeof(rule_list_t) + entry->next_offset;
+ rule = (rule_list_t *)malloc(size);
+
+ if (!rule) {
+
+ (*handle)->error_no = ERR_ALLOCATEMEM;
+
+ return -1;
+
+ }
+
+ memset(rule, 0, size);
+
+ rule->entry = (ebt_entry_t *)(rule + 1);
+ memcpy(rule->entry, entry, entry->next_offset);
+
+ rule_cur = chain->rules.first;
+
+ if (rule_cur) {
+
+ for (; rulenum > 0 && rule_cur; rulenum--) {
+
+ rule_last = rule_cur;
+ rule_cur = rule_cur->next;
+
+ }
+
+ if (rulenum || !rule_cur) {
+
+ (*handle)->error_no = ERR_RULENUM;
+
+ return -1;
+
+ }
+
+ if (rule_last)
+ rule_last->next = rule;
+ else
+ chain->rules.first = rule;
+
+ rule->next = rule_cur;
+
+ } else {
+
+ if (rulenum != 0) {
+
+ (*handle)->error_no = ERR_RULENUM;
+
+ return -1;
+
+ }
+
+ chain->rules.first = chain->rules.last = rule;
+
+ }
+
+ chain->rules.count++;
+ chain->rules.size += entry->next_offset;
+
+ (*handle)->changed = EBTC_TRUE;
+ (*handle)->error_no = SUCCESS;
+
+ return 0;
+
+}
+
+
+int ebtc_replace_entry (const char *chainname, const struct ebt_entry *entry,
+ unsigned int rulenum, ebtc_handle_t *handle)
+{
+
+/* ---- VAR ---- */
+
+ chain_list_t *chain;
+
+ rule_list_t *rule_last = NULL;
+
+ rule_list_t *rule_old;
+
+ rule_list_t *rule_new;
+
+ unsigned int size;
+
+
+
+/* ---- CODE ---- */
+
+ if (check_entry(entry)) {
+
+ (*handle)->error_no = ERR_BADENTRY;
+
+ return -1;
+
+ }
+
+ /* Search for chain in chain list */
+
+ chain = find_chain(chainname, *handle);
+
+ if (!chain) {
+
+ (*handle)->error_no = ERR_CHAINNOTFOUND;
+
+ return -1;
+
+ }
+
+ /* Copy entry and replace in chain */
+
+ size = sizeof(rule_list_t) + entry->next_offset;
+ rule_new = (rule_list_t *)malloc(size);
+
+ if (!rule_new) {
+
+ (*handle)->error_no = ERR_ALLOCATEMEM;
+
+ return -1;
+
+ }
+
+ memset(rule_new, 0, size);
+
+ rule_new->entry = (ebt_entry_t *)(rule_new + 1);
+ memcpy(rule_new->entry, entry, entry->next_offset);
+
+ rule_old = chain->rules.first;
+
+ if (!rule_old) {
+
+ (*handle)->error_no = ERR_RULENUM;
+
+ return -1;
+
+ }
+
+ for (; rulenum > 0 && rule_old; rulenum--) {
+
+ rule_last = rule_old;
+ rule_old = rule_old->next;
+
+ }
+
+ if (rulenum || !rule_old) {
+
+ (*handle)->error_no = ERR_RULENUM;
+
+ return -1;
+
+ }
+
+ if (rule_last)
+ rule_last->next = rule_new;
+ else
+ chain->rules.first = rule_new;
+
+ rule_new->next = rule_old->next;
+
+ if (!rule_new->next)
+ chain->rules.last = rule_new;
+
+ chain->rules.size -= rule_old->entry->next_offset;
+ chain->rules.size += entry->next_offset;
+
+ (*handle)->changed = EBTC_TRUE;
+
+ /* Clean up */
+
+ free(rule_old);
+
+ (*handle)->error_no = SUCCESS;
+
+ return 0;
+
+}
+
+
+int ebtc_append_entry (const char *chainname, const struct ebt_entry *entry,
+ ebtc_handle_t *handle)
+{
+
+/* ---- VAR ---- */
+
+ chain_list_t *chain = (*handle)->chains.first;
+
+ rule_list_t *rule;
+
+ unsigned int size;
+
+
+
+/* ---- CODE ---- */
+
+ if (check_entry(entry)) {
+
+ (*handle)->error_no = ERR_BADENTRY;
+
+ return -1;
+
+ }
+
+ /* Search for chain in chain list */
+
+ while (chain) {
+
+ if (!strcmp(chain->entries.name, chainname))
+ break;
+
+ chain = chain->next;
+
+ }
+
+ if (!chain) {
+
+ (*handle)->error_no = ERR_CHAINNOTFOUND;
+
+ return -1;
+
+ }
+
+ /* Copy entry and insert into chain */
+
+ size = sizeof(rule_list_t) + entry->next_offset;
+ rule = (rule_list_t *)malloc(size);
+
+ if (!rule) {
+
+ (*handle)->error_no = ERR_ALLOCATEMEM;
+
+ return -1;
+
+ }
+
+ memset(rule, 0, size);
+
+ rule->entry = (ebt_entry_t *)(rule + 1);
+ memcpy(rule->entry, entry, entry->next_offset);
+
+ if (chain->rules.last) {
+
+ chain->rules.last->next = rule;
+ chain->rules.last = rule;
+
+ } else
+ chain->rules.first = chain->rules.last = rule;
+
+ chain->rules.count++;
+ chain->rules.size += entry->next_offset;
+
+ (*handle)->changed = EBTC_TRUE;
+ (*handle)->error_no = SUCCESS;
+
+ return 0;
+
+}
+
+
+int ebtc_delete_entry (const char *chainname, unsigned int rulenum,
+ ebtc_handle_t *handle)
+{
+
+/* ---- VAR ---- */
+
+ chain_list_t *chain;
+
+ rule_list_t *rule_last = NULL;
+
+ rule_list_t *rule;
+
+
+
+/* ---- CODE ---- */
+
+ /* Find chain */
+
+ chain = find_chain(chainname, *handle);
+
+ if (!chain) {
+
+ (*handle)->error_no = ERR_CHAINNOTFOUND;
+
+ return -1;
+
+ }
+
+ /* Find and delete rule */
+
+ rule = chain->rules.first;
+
+ if (!rule) {
+
+ (*handle)->error_no = ERR_RULENUM;
+
+ return -1;
+
+ }
+
+ for (; rulenum > 0 && rule; rulenum--) {
+
+ rule_last = rule;
+ rule = rule->next;
+
+ }
+
+ if (rulenum || !rule) {
+
+ (*handle)->error_no = ERR_RULENUM;
+
+ return -1;
+
+ }
+
+ if (rule_last)
+ rule_last->next = rule->next;
+ else
+ chain->rules.first = rule->next;
+
+ if (!rule->next)
+ chain->rules.last = rule_last;
+
+ chain->rules.count--;
+ chain->rules.size -= rule->entry->next_offset;
+
+ (*handle)->changed = EBTC_TRUE;
+
+ /* Clean up */
+
+ free(rule);
+
+ (*handle)->error_no = SUCCESS;
+
+ return 0;
+
+}
+
+
+int ebtc_flush_entries (const char *chainname, ebtc_handle_t *handle)
+{
+
+/* ---- VAR ---- */
+
+ chain_list_t *chain;
+
+ rule_list_t *rule;
+
+ rule_list_t *rule_next;
+
+
+
+/* ---- CODE ---- */
+
+ /* Find chain */
+
+ chain = find_chain(chainname, *handle);
+
+ if (!chain) {
+
+ (*handle)->error_no = ERR_CHAINNOTFOUND;
+
+ return -1;
+
+ }
+
+ /* Free all rules */
+
+ rule = chain->rules.first;
+
+ while (rule) {
+
+ rule_next = rule->next;
+
+ free(rule);
+
+ rule = rule_next;
+
+ }
+
+ memset(&chain->rules, 0, sizeof(chain->rules));
+
+ (*handle)->changed = EBTC_TRUE;
+ (*handle)->error_no = SUCCESS;
+
+ return 0;
+
+}
+
+
+int ebtc_zero_entries (const char *chainname, ebtc_handle_t *handle)
+{
+
+/* ---- VAR ---- */
+
+ chain_list_t *chain;
+
+ rule_list_t *rule;
+
+
+
+/* ---- CODE ---- */
+
+ /* Find chain */
+
+ chain = find_chain(chainname, *handle);
+
+ if (!chain) {
+
+ (*handle)->error_no = ERR_CHAINNOTFOUND;
+
+ return -1;
+
+ }
+
+ /* Iterate set zero in rule list */
+
+ rule = chain->rules.first;
+
+ while (rule) {
+
+ memset(&rule->counter.counter, 0, sizeof(ebt_counter_t));
+ rule->counter.changed = EBTC_TRUE;
+
+ rule = rule->next;
+
+ }
+
+ (*handle)->changed = EBTC_TRUE;
+ (*handle)->error_no = SUCCESS;
+
+ return 0;
+
+}
+
+
+int ebtc_rename_chain (const char *chainname_old, const char *chainname_new,
+ ebtc_handle_t *handle)
+{
+
+/* ---- VAR ---- */
+
+ chain_list_t *chain;
+
+
+
+/* ---- CODE ---- */
+
+ /* Find chain */
+
+ chain = find_chain(chainname_old, *handle);
+
+ if (!chain) {
+
+ (*handle)->error_no = ERR_CHAINNOTFOUND;
+
+ return -1;
+
+ }
+
+ if (chain->hookid != NF_BR_NUMHOOKS) {
+
+ (*handle)->error_no = ERR_BUILTINCHAIN;
+
+ return -1;
+
+ }
+
+ /* Set new chainname */
+
+ snprintf(chain->entries.name, EBT_CHAIN_MAXNAMELEN, "%s", chainname_new);
+
+ (*handle)->changed = EBTC_TRUE;
+
+ return 0;
+
+}
+
+
+int ebtc_create_chain (const char *chainname, ebtc_handle_t *handle)
+{
+
+/* ---- VAR ---- */
+
+ chain_list_t *chain;
+
+
+
+/* ---- CODE ---- */
+
+ /* Exist chain */
+
+ chain = find_chain(chainname, *handle);
+
+ if (chain) {
+
+ (*handle)->error_no = ERR_CHAINEXIST;
+
+ return -1;
+
+ }
+
+ /* Allocate new chain */
+
+ chain = (chain_list_t *)malloc(sizeof(chain_list_t));
+
+ if (!chain) {
+
+ (*handle)->error_no = ERR_ALLOCATEMEM;
+
+ return -1;
+
+ }
+
+ memset(chain, 0, sizeof(chain_list_t));
+
+ chain->hookid = NF_BR_NUMHOOKS;
+ chain->id = (*handle)->chains.last_id++;
+
+ snprintf(chain->entries.name, EBT_CHAIN_MAXNAMELEN, "%s", chainname);
+ chain->entries.policy = EBT_ACCEPT;
+
+ if ((*handle)->chains.last) {
+
+ (*handle)->chains.last->next = chain;
+ (*handle)->chains.last = chain;
+
+ } else
+ (*handle)->chains.last = (*handle)->chains.first = chain;
+
+ (*handle)->changed = EBTC_TRUE;
+ (*handle)->error_no = SUCCESS;
+
+ return 0;
+
+}
+
+
+int ebtc_delete_chain (const char *chainname, ebtc_handle_t *handle)
+{
+
+/* ---- VAR ---- */
+
+ chain_list_t *chain = (*handle)->chains.first;
+
+ chain_list_t *chain_last = NULL;
+
+ rule_list_t *rule;
+
+ rule_list_t *rule_next;
+
+
+
+/* ---- CODE ---- */
+
+ /* Find chain */
+
+ for (; chain; chain_last = chain, chain = chain->next) {
+
+ if (!strcmp(chain->entries.name, chainname))
+ break;
+
+ }
+
+ if (!chain) {
+
+ (*handle)->error_no = ERR_CHAINNOTFOUND;
+
+ return -1;
+
+ }
+
+ if (chain->hookid != NF_BR_NUMHOOKS) {
+
+ (*handle)->error_no = ERR_BUILTINCHAIN;
+
+ return -1;
+
+ }
+
+ /* Free all rules */
+
+ rule = chain->rules.first;
+
+ while (rule) {
+
+ rule_next = rule->next;
+
+ free(rule);
+
+ rule = rule_next;
+
+ }
+
+ /* Remove chain from list */
+
+ if (chain_last)
+ chain_last->next = chain->next;
+ else
+ (*handle)->chains.first = chain->next;
+
+ if (!chain->next)
+ (*handle)->chains.last = chain_last;
+
+ (*handle)->changed = EBTC_TRUE;
+ (*handle)->error_no = SUCCESS;
+
+ /* Clean up */
+
+ free(chain);
+
+ return 0;
+
+}
+
+
+const struct ebt_counter *ebtc_read_counter (const char *chainname,
+ unsigned int rulenum,
+ ebtc_handle_t *handle)
+{
+
+/* ---- VAR ---- */
+
+ chain_list_t *chain;
+
+ rule_list_t *rule;
+
+
+
+/* ---- CODE ---- */
+
+ /* Find chain */
+
+ chain = find_chain(chainname, *handle);
+
+ if (!chain) {
+
+ (*handle)->error_no = ERR_CHAINNOTFOUND;
+
+ return NULL;
+
+ }
+
+ /* Find rule */
+
+ rule = chain->rules.first;
+
+ for (; rulenum > 0 && rule; rulenum--)
+ rule = rule->next;
+
+ if (rulenum || !rule) {
+
+ (*handle)->error_no = ERR_RULENUM;
+
+ return NULL;
+
+ }
+
+ (*handle)->error_no = SUCCESS;
+
+ return &rule->counter.counter;
+
+}
+
+
+int ebtc_zero_counter (const char *chainname, unsigned int rulenum,
+ ebtc_handle_t *handle)
+{
+
+/* ---- VAR ---- */
+
+ chain_list_t *chain;
+
+ rule_list_t *rule;
+
+
+
+/* ---- CODE ---- */
+
+ /* Find chain */
+
+ chain = find_chain(chainname, *handle);
+
+ if (!chain) {
+
+ (*handle)->error_no = ERR_CHAINNOTFOUND;
+
+ return -1;
+
+ }
+
+ /* Find rule */
+
+ rule = chain->rules.first;
+
+ for (; rulenum > 0 && rule; rulenum--)
+ rule = rule->next;
+
+ if (rulenum || !rule) {
+
+ (*handle)->error_no = ERR_RULENUM;
+
+ return -1;
+
+ }
+
+ memset(&rule->counter.counter, 0, sizeof(ebt_counter_t));
+ rule->counter.changed = EBTC_TRUE;
+
+ (*handle)->changed = EBTC_TRUE;
+ (*handle)->error_no = SUCCESS;
+
+ return 0;
+
+}
+
+
+int ebtc_set_counter (const char *chainname, unsigned int rulenum,
+ const struct ebt_counter *counter, ebtc_handle_t *handle)
+{
+
+/* ---- VAR ---- */
+
+ chain_list_t *chain;
+
+ rule_list_t *rule;
+
+
+
+/* ---- CODE ---- */
+
+ /* Find chain */
+
+ chain = find_chain(chainname, *handle);
+
+ if (!chain) {
+
+ (*handle)->error_no = ERR_CHAINNOTFOUND;
+
+ return -1;
+
+ }
+
+ /* Find rule */
+
+ rule = chain->rules.first;
+
+ for (; rulenum > 0 && rule; rulenum--)
+ rule = rule->next;
+
+ if (rulenum || !rule) {
+
+ (*handle)->error_no = ERR_RULENUM;
+
+ return -1;
+
+ }
+
+ memcpy(&rule->counter.counter, counter, sizeof(ebt_counter_t));
+ rule->counter.changed = EBTC_TRUE;
+
+ (*handle)->changed = EBTC_TRUE;
+ (*handle)->error_no = SUCCESS;
+
+ return 0;
+
+}
+
+
+int ebtc_target_jumptochain (ebt_standard_target_t *target, char *chainname,
+ ebtc_handle_t *handle)
+{
+
+/* ---- VAR ---- */
+
+ int i;
+
+ chain_list_t *chain;
+
+
+
+/* ---- CODE ---- */
+
+ /* Prepare basics of target */
+
+ target->target.target_size = EBTC_SIZEOF(ebt_standard_target_t);
+ target->target.target_size -= EBTC_SIZEOF(ebt_entry_target_t);
+
+ snprintf(target->target.u.name, EBT_FUNCTION_MAXNAMELEN, "standard");
+
+ /* Check for standard targets */
+
+ for (i = 0; *targets[i].name; i++) {
+
+ if (!strcmp(chainname, targets[i].name)) {
+
+ target->verdict = targets[i].id;
+
+ return 0;
+
+ }
+
+ }
+
+ /* Check for standard targets */
+
+ for (i = 0; *targets[i].name; i++) {
+
+ if (!strcmp(chainname, targets[i].name)) {
+
+ target->verdict = targets[i].id;
+
+ return 0;
+
+ }
+
+ }
+
+ /* Find chain */
+
+ chain = find_chain(chainname, *handle);
+
+ if (!chain) {
+
+ (*handle)->error_no = ERR_CHAINNOTFOUND;
+
+ return -1;
+
+ }
+
+ if (chain->hookid != NF_BR_NUMHOOKS) {
+
+ (*handle)->error_no = ERR_STDCHAINNOTALLOW;
+
+ return -1;
+
+ }
+
+ target->verdict = chain->id;
+
+ return 0;
+
+}
+
+
+ebtc_handle_t ebtc_init (const char *tablename, int options)
+{
+
+/* ---- VAR ---- */
+
+ int i;
+
+ int optlen;
+
+ int optname;
+
+ int counter;
+
+ unsigned int size;
+
+ unsigned int entry_count;
+
+ unsigned int rule_size;
+
+ ebt_replace_t *replace;
+
+ ebt_entries_t *entries;
+
+ ebt_entry_t *entry;
+
+ chain_list_t *chain;
+
+ rule_list_t *rule;
+
+ ebtc_handle_t handle;
+
+
+
+/* ---- CODE ---- */
+
+ /* Allocate memory for handle and initialize this object */
+
+ handle = (ebtc_handle_t)malloc(sizeof(struct ebtc_handle_st));
+
+ if (!handle) {
+
+ self_private.error_no = ERR_ALLOCATEMEM;
+
+ return NULL;
+
+ }
+
+ memset(handle, 0, sizeof(struct ebtc_handle_st));
+
+ handle->changed = options & EBTC_INIT_WITHFLUSH ? EBTC_TRUE : EBTC_FALSE;
+ replace = &handle->replace;
+
+ /* Open raw socket */
+
+ handle->fd = socket(AF_INET, SOCK_RAW, PF_INET);
+
+ if (handle->fd == -1) {
+
+ free(handle);
+
+ self_private.error_no = ERR_RAWSOCKET;
+
+ return NULL;
+
+ }
+
+ /* Get infomations */
+
+ memset(replace, 0, sizeof(ebt_replace_t));
+ snprintf(replace->name, EBT_TABLE_MAXNAMELEN, "%s", tablename);
+
+ optlen = sizeof(ebt_replace_t);
+ optname = options & EBTC_INIT_WITHFLUSH ? EBT_SO_GET_INIT_INFO :
+ EBT_SO_GET_INFO;
+
+ if (getsockopt(handle->fd, IPPROTO_IP, optname, replace, &optlen)) {
+
+ free(handle);
+
+ self_private.error_no = ERR_GETINFO;
+
+ return NULL;
+
+ }
+
+ /* Get entries */
+
+ if (replace->nentries) {
+
+ size = replace->nentries * sizeof(ebt_counter_t);
+ replace->counters = (ebt_counter_t *)malloc(size);
+
+ if (!replace->counters) {
+
+ close(handle->fd);
+ free(handle);
+
+ self_private.error_no = ERR_ALLOCATEMEM;
+
+ return NULL;
+
+ }
+
+ } else {
+
+ size = 0;
+ replace->counters = NULL;
+
+ }
+
+ handle->cache.num_counters = replace->num_counters = replace->nentries;
+ replace->entries = malloc(replace->entries_size);
+
+ if (!replace->entries) {
+
+ if (replace->counters)
+ free(replace->counters);
+
+ close(handle->fd);
+ free(handle);
+
+ self_private.error_no = ERR_ALLOCATEMEM;
+
+ return NULL;
+
+ }
+
+ optlen += replace->entries_size;
+ optlen += replace->num_counters * sizeof(ebt_counter_t);
+ optname = options & EBTC_INIT_WITHFLUSH ? EBT_SO_GET_INIT_ENTRIES :
+ EBT_SO_GET_ENTRIES;
+
+ if (getsockopt(handle->fd, IPPROTO_IP, optname, replace, &optlen)) {
+
+ close(handle->fd);
+
+ if (replace->counters)
+ free(replace->counters);
+
+ free(replace->entries);
+ free(handle);
+
+ self_private.error_no = ERR_GETENTRIES;
+
+ return NULL;
+
+ }
+
+ /* Split one block from kernel into lists */
+
+ size = replace->entries_size;
+ entries = (ebt_entries_t *)replace->entries;
+
+ for (; size > 0; size -= sizeof(ebt_entries_t)) {
+
+ /* Allocate and fill a container for chain */
+
+ chain = (chain_list_t *)malloc(sizeof(chain_list_t));
+
+ if (!chain) {
+
+ ebtc_free(&handle);
+
+ self_private.error_no = ERR_ALLOCATEMEM;
+
+ return NULL;
+
+ }
+
+ memset(chain, 0, sizeof(chain_list_t));
+ memcpy(&chain->entries, entries, sizeof(ebt_entries_t));
+
+ chain->hookid = NF_BR_NUMHOOKS;
+ chain->id = handle->chains.last_id++;
+
+ for (i = 0; i < NF_BR_NUMHOOKS; i++) {
+
+ if (!strcmp(entries->name, builtinchains[i].name)) {
+
+ chain->hookid = builtinchains[i].id;
+ break;
+
+ }
+
+ }
+
+ if (handle->chains.last) {
+
+ handle->chains.last->next = chain;
+ handle->chains.last = chain;
+
+ } else
+ handle->chains.last = handle->chains.first = chain;
+
+ handle->chains.count++;
+
+ /* Put entries into rule list of current chain */
+
+ entry_count = entries->nentries;
+ counter = entries->counter_offset;
+
+ entries++;
+
+ if (entry_count) {
+
+ entry = (ebt_entry_t *)entries;
+ counter = chain->entries.counter_offset;
+
+ for (; entry_count > 0; entry_count--) {
+
+ /* Allocate memory for rule */
+
+ rule_size = sizeof(rule_list_t) + entry->next_offset;
+ rule = (rule_list_t *)malloc(rule_size);
+
+ if (!rule) {
+
+ ebtc_free(&handle);
+
+ self_private.error_no = ERR_ALLOCATEMEM;
+
+ return NULL;
+
+ }
+
+ /* Initialize rule */
+
+ memset(rule, 0, sizeof(rule_list_t));
+
+ rule->counter.changed = EBTC_FALSE;
+ rule->counter.offset = counter;
+
+ rule->entry = (ebt_entry_t *)(rule + 1);
+ memcpy(rule->entry, entry, entry->next_offset);
+
+ chain->rules.size += entry->next_offset;
+
+ memcpy(&rule->counter.counter, &replace->counters[counter++],
+ sizeof(ebt_counter_t));
+
+ if (chain->rules.last) {
+
+ chain->rules.last->next = rule;
+ chain->rules.last = rule;
+
+ } else
+ chain->rules.last = chain->rules.first = rule;
+
+ chain->rules.count++;
+
+ /* Jump to next entry */
+
+ size -= entry->next_offset;
+ entry = (ebt_entry_t *)((char *)entry +
+ entry->next_offset);
+
+ }
+
+ entries = (ebt_entries_t *)entry;
+
+ }
+
+ }
+
+ /* Clean up */
+
+ if (replace->counters) {
+
+ free(replace->counters);
+ replace->counters = NULL;
+
+ }
+
+ free(replace->entries);
+ replace->entries = NULL;
+
+ handle->error_no = SUCCESS;
+
+ return handle;
+
+}
+
+
+static int precommit_standard_target (chain_list_t *chain, ebt_entry_t *entry)
+{
+
+/* ---- VAR ---- */
+
+ ebt_standard_target_t *target;
+
+
+
+/* ---- CODE ---- */
+
+ /* Check for standard target */
+
+ target = (ebt_standard_target_t *)EBTC_ADDOFFSET(entry,
+ entry->target_offset);
+
+ if (strncmp(target->target.u.name, "standard", EBT_FUNCTION_MAXNAMELEN))
+ return 0;
+
+ for (; chain; chain = chain->next) {
+
+ if (chain->id == target->verdict) {
+
+ target->verdict = chain->offset;
+
+ return 0;
+
+ }
+
+ }
+
+ return 0;
+
+}
+
+
+int ebtc_commit (ebtc_handle_t *handle)
+{
+
+/* ---- VAR ---- */
+
+ socklen_t optlen;
+
+ int result;
+
+ int fd;
+
+ int i;
+
+ unsigned int size;
+
+ unsigned int size_entry;
+
+ unsigned int size_entries = 0;
+
+ unsigned int size_counters = 0;
+
+ unsigned int counter_offset = 0;
+
+ unsigned int count_entries = 0;
+
+ chain_list_t *chain;
+
+ rule_list_t *rule;
+
+ ebt_replace_t *replace;
+
+ ebt_entries_t *entries;
+
+ ebt_entry_t *entry;
+
+ ebt_counter_t *counters;
+
+ ebt_counter_t *counters_back;
+
+
+
+/* ---- CODE ---- */
+
+ /* Check of all conditions */
+
+ if ((*handle)->changed == EBTC_FALSE) {
+
+ ebtc_free(handle);
+
+ self_private.error_no = SUCCESS;
+
+ return 0;
+
+ }
+
+ /* Collect all size informations for entries */
+
+ for (chain = (*handle)->chains.first; chain; chain = chain->next) {
+
+ chain->offset = size_entries;
+
+ size_entries += sizeof(ebt_entries_t);
+ size_entries += chain->rules.size;
+
+ size_counters += sizeof(ebt_counter_t) * chain->rules.count;
+ count_entries += chain->rules.count;
+
+ }
+
+ /* Allocate */
+
+ replace = (ebt_replace_t *)malloc(sizeof(ebt_replace_t));
+
+ if (!replace) {
+
+ ebtc_free(handle);
+
+ self_private.error_no = ERR_ALLOCATEMEM;
+
+ return -1;
+
+ }
+
+ entries = (ebt_entries_t *)malloc(size_entries);
+
+ if (!entries) {
+
+ free(replace);
+ ebtc_free(handle);
+
+ self_private.error_no = ERR_ALLOCATEMEM;
+
+ return -1;
+
+ }
+
+ /* Pack all entries in a big block */
+
+ memcpy(replace, &(*handle)->replace, sizeof(ebt_replace_t));
+
+ replace->entries_size = size_entries;
+ replace->nentries = count_entries;
+ replace->entries = (char *)entries;
+ replace->counters = counters;
+ replace->num_counters = 0;
+
+ for (chain = (*handle)->chains.first; chain; chain = chain->next) {
+
+ /* Copy entries */
+
+ memcpy(entries, &chain->entries, sizeof(ebt_entries_t));
+
+ entries->distinguisher = 0;
+ entries->counter_offset = counter_offset;
+ entries->nentries = chain->rules.count;
+
+ /* Is chain a hook chain? */
+
+ if (chain->hookid != NF_BR_NUMHOOKS)
+ replace->hook_entry[chain->hookid] = entries;
+
+ /* Copy list of entries */
+
+ entries++;
+ entry = (ebt_entry_t *)entries;
+
+ for (rule = chain->rules.first; rule; rule = rule->next) {
+
+ size_entry = rule->entry->next_offset;
+
+ result = precommit_standard_target((*handle)->chains.first,
+ rule->entry);
+
+ if (result) {
+
+ free(entries);
+ free(replace);
+ ebtc_free(handle);
+
+ self_private.error_no = ERR_ENTRYTARGETINVALID;
+
+ return -1;
+
+ }
+
+ memcpy(entry, rule->entry, size_entry);
+ entry = (ebt_entry_t *)((char *)entry + size_entry);
+
+ }
+
+ /* Setup for next entries */
+
+ counter_offset += chain->rules.count;
+ entries = (ebt_entries_t *)entry;
+
+ }
+
+ entries = (ebt_entries_t *)replace->entries;
+
+ /* Create container for counter refresh */
+
+ if (size_counters > 0 && (*handle)->cache.num_counters > 0) {
+
+ size = (*handle)->cache.num_counters * sizeof(ebt_counter_t);
+ replace->num_counters = (*handle)->cache.num_counters;
+ counters_back = replace->counters = (ebt_counter_t *)malloc(size);
+
+ if (!counters_back) {
+
+ free(entries);
+ free(replace);
+ ebtc_free(handle);
+
+ self_private.error_no = ERR_ALLOCATEMEM;
+
+ return -1;
+
+ }
+
+ } else {
+
+ counters_back = replace->counters = NULL;
+ replace->num_counters = 0;
+
+ }
+
+ /* Submit entries to kernel */
+
+ fd = (*handle)->fd;
+
+ optlen = sizeof(ebt_replace_t);
+ optlen += size_entries;
+
+ if (setsockopt(fd, IPPROTO_IP, EBT_SO_SET_ENTRIES, replace, optlen)) {
+
+ if (counters_back)
+ free(counters_back);
+
+ free(entries);
+ free(replace);
+ ebtc_free(handle);
+
+ self_private.error_no = ERR_SETENTRIES;
+
+ return -1;
+
+ }
+
+ if (size_counters > 0) {
+
+ /* Allocate memory for counters */
+
+ replace->counters = counters = (ebt_counter_t *)malloc(size_counters);
+
+ if (!counters) {
+
+ if (counters_back)
+ free(counters_back);
+
+ free(replace);
+ ebtc_free(handle);
+
+ self_private.error_no = ERR_ALLOCATEMEM;
+
+ return -1;
+
+ }
+
+ /* Fill counters */
+
+ for (chain = (*handle)->chains.first; chain; chain = chain->next) {
+
+ for (rule = chain->rules.first; rule; rule = rule->next) {
+
+ if (rule->counter.changed == EBTC_FALSE) {
+
+ memcpy(counters, &counters_back[rule->counter.offset],
+ sizeof(ebt_counter_t));
+
+ } else {
+
+ memcpy(counters, &rule->counter.counter,
+ sizeof(ebt_counter_t));
+
+ }
+
+ counters++;
+
+ }
+
+ }
+
+ counters = replace->counters;
+
+ if (counters_back)
+ free(counters_back);
+
+ /* Submit counters to kernel */
+
+ replace->num_counters = count_entries;
+ replace->entries = NULL;
+
+ optlen = sizeof(ebt_replace_t);
+ optlen += sizeof(ebt_counter_t) * count_entries;
+
+ if (setsockopt(fd, IPPROTO_IP, EBT_SO_SET_COUNTERS, replace, optlen)) {
+
+ free(counters);
+ free(entries);
+ free(replace);
+ ebtc_free(handle);
+
+ self_private.error_no = ERR_SETCOUNTERS;
+
+ return -1;
+
+ }
+
+ free(counters);
+
+ }
+
+ /* Clean up */
+
+ free(entries);
+ free(replace);
+ ebtc_free(handle);
+
+ self_private.error_no = SUCCESS;
+
+ return 0;
+
+}
+
+
+void ebtc_free (ebtc_handle_t *handle)
+{
+
+/* ---- VAR ---- */
+
+ ebt_replace_t *replace = &(*handle)->replace;
+
+ chain_list_t *chain = (*handle)->chains.first;
+
+ chain_list_t *chain_next;
+
+ rule_list_t *rule;
+
+ rule_list_t *rule_next;
+
+
+
+/* ---- CODE ---- */
+
+ /* Free chain list with all rules */
+
+ for (; chain; chain = chain_next) {
+
+ for (rule = chain->rules.first; rule; rule = rule_next) {
+
+ rule_next = rule->next;
+ free(rule);
+
+ }
+
+ chain_next = chain->next;
+ free(chain);
+
+ }
+
+ /* Close gateway into the kernel */
+
+ close((*handle)->fd);
+
+ /* Clean up*/
+
+ if (replace->counters)
+ free(replace->counters);
+
+ if (replace->entries)
+ free(replace->entries);
+
+ free(*handle);
+
+ (*handle)->error_no = SUCCESS;
+
+ return;
+
+}
+
+
+const char *ebtc_strerror (const ebtc_handle_t *handle)
+{
+
+/* ---- VAR ---- */
+
+ int i;
+
+
+
+/* ---- CODE ---- */
+
+ if (handle)
+ i = (*handle)->error_no;
+ else
+ i = self_private.error_no;
+
+ if (i > sizeof(error_msg) / sizeof(error_msg[0]))
+ return NULL;
+
+ return error_msg[i].msg;
+
+}
+
+