/* * ==[ 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 #include #include #include #include #include /* 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; }