blob: d7b68baa33b3525d7f53a8e641d3da19ed22d04a [file] [log] [blame]
/*
* ==[ 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;
}