/*-
* Copyright (c) 2003 Andrey Simonenko
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "compat.h"
#ifndef lint
static const char rcsid[] ATTR_UNUSED =
"@(#)$Id: ipa_ip6fw.c,v 1.5 2006/04/26 21:08:22 simon Exp $";
#endif /* !lint */
#include <errno.h>
#include <limits.h>
#include <stdarg.h>
#include <stdlib.h>
#include <sys/queue.h>
#include <sys/types.h>
#include <regex.h>
#include <ipa_mod.h>
#include "ipa_ip6fw.h"
#include "kip6fw.h"
#include "version.h"
#if IPA_AC_MOD_API_VERSION != 1
# error This module supports only ipa_ac_mod API version 1
#endif
#define AC_MOD_API_VERSION 1 /* Supported ipa_ac_mod API. */
#if IPA_MEMFUNC_API_VERSION != 1
# error This module supports only ipa_memfunc API version 1
#endif
#define MEMFUNC_API_VERSION 1 /* Supported memfunc API. */
#ifdef WITH_PTHREAD
# define MOD_FLAGS IPA_MOD_FLAG_PTHREAD_SAFE
#else
# define MOD_FLAGS 0
#endif
#define MEMFLAG_OPTIMIZE IPA_MEMFUNC_FLAG_OPTIMIZE
#define MOD_NAME "ipa_ip6fw"
#define MOD_AC_NAME "ip6fw"
#define MOD_CONF_PREFIX "ip6fw"
#define MARRAY_NAME(x) MOD_NAME ":" #x
#define MZONE_NAME(x) MOD_NAME ":" #x
#define MTYPE_NAME(x) MOD_NAME ":m_" #x
extern struct ipa_ac_mod IPA_AC_MOD_ENTRY(ipa_ip6fw); /* Is defined at the end of this file. */
static const ipa_suppfunc *suppfunc; /* Exported support functions. */
const ipa_memfunc *memfunc; /* Exported memory manipulation functions. */
ipa_mem_type *m_anon; /* Anonymous memory allocations. */
ipa_mem_type *m_buf; /* Memory for buffers. */
void *(*mem_realloc)(void *, size_t, ipa_mem_type *);
void (*mem_free)(void *, ipa_mem_type *);
static ipa_marray *(*marray_init)(const char *, const char *, u_int, void **, size_t, u_int, u_int);
static void (*marray_deinit)(ipa_marray *);
static int (*marray_alloc)(ipa_marray *, u_int *, int);
static void (*marray_minimize)(ipa_marray *);
static int (*marray_check_index)(ipa_marray *, u_int);
static ipa_mzone *(*mzone_init)(const char *, const char *, u_int, size_t, u_int, u_int);
static void (*mzone_deinit)(ipa_mzone *);
static void *(*mzone_alloc)(ipa_mzone *);
static uint64_t global_maxchunk; /* global { ip6fw:maxchunk } */
static int global_quiet; /* global { ip6fw:quiet } */
/*
* Struct ip6_fw{} has only a rule number for each rule.
* An extra subnumber is introduced: if there are some rules
* with the same number we will distinguish such rules
* by subnumbers, first rule in the set has subnumber equal to 0.
*/
struct ip6fwac {
struct kip6fw_rule *kip6fw_rule;/* Pointer to kip6fw_rule. */
int seen; /* 1, if bcnt_old is known. */
int addition; /* Non-zero if add. */
u_short number; /* IP6FW rule number. */
u_int subnumber; /* IP6FW rule subnumber. */
uint64_t bcnt_old; /* Old value of counter. */
};
/*
* Module's data for each rule.
*/
struct rule {
const char *rule_name; /* Name of this rule. */
uint64_t maxchunk; /* rule { ip6fw:maxchunk } */
int quiet; /* rule { ip6fw:quiet } */
struct ip6fwac *ip6fwac; /* rule { ip6fw:rules } */
u_int nip6fwac; /* Number of accounting rules. */
int inited; /* Nonzero, if rule was inited. */
STAILQ_ENTRY(rule) link; /* For list of static rules. */
};
#define LOG_IP6FW_OVERFLOW(r) (!((r)->quiet & 0x1))
#define LOG_IP6FW_EVENTS(r) (!((r)->quiet & 0x2))
static struct rule **rules_ptr; /* Array of pointers to rules. */
static u_int nrules; /* Number of rules. */
static u_int nalloced; /* Number of allocated rules. */
static u_int ninited; /* Number of inited rules. */
static ipa_mzone *rule_mzone; /* Mzone for all rules. */
static ipa_marray *rules_ptr_marray; /* Marray for array of pointers to rules. */
#define RULE(x) rules_ptr[x] /* Pointer to rule by number. */
static struct rule *currule; /* Current rule. */
static const char *currulename; /* Name of current rule. */
static STAILQ_HEAD(, rule) rules_list; /* List of static rules. */
#define RULE_NSIZE 30
#define RULE_NALLOC 20
static int in_rule; /* 1, if we are in rule{} section. */
static void logconferr(const char *, ...) ATTR_PRINTFLIKE(1, 2);
static void logconferrx(const char *, ...) ATTR_PRINTFLIKE(1, 2);
static void print_param_args(const char *, ...) ATTR_PRINTFLIKE(1, 2);
#define KIP6FW_RULE_NSIZE 100
#define KIP6FW_RULE_NALLOC 20
static ipa_marray *kip6fw_rules_marray; /* Marray for all kip6fw_rules. */
static struct kip6fw_rule *kip6fw_rules;/* Sorted array of all kip6fw_rules. */
static u_int nkip6fw_rules; /* Number of all kip6fw_rules. */
/*
* A wrapper for imported logmsg() function.
*/
void
logmsg(int priority, const char *format, ...)
{
va_list ap;
va_start(ap, format);
suppfunc->logmsg(MOD_NAME, priority, errno, format, ap);
va_end(ap);
}
/*
* A wrapper for imported logmsg() function,
* but error code is always zero here.
*/
void
logmsgx(int priority, const char *format, ...)
{
va_list ap;
va_start(ap, format);
suppfunc->logmsg(MOD_NAME, priority, 0, format, ap);
va_end(ap);
}
/*
* A wrapper for imported logconferr() function.
*/
static void
logconferr(const char *format, ...)
{
va_list ap;
va_start(ap, format);
suppfunc->logconferr(MOD_NAME, errno, format, ap);
va_end(ap);
}
/*
* A wrapper for imported logconferr() function,
* but error code is always zero here.
*/
static void
logconferrx(const char *format, ...)
{
va_list ap;
va_start(ap, format);
suppfunc->logconferr(MOD_NAME, 0, format, ap);
va_end(ap);
}
/*
* A wrapper for imported print_param_args() function.
*/
static void
print_param_args(const char *format, ...)
{
va_list ap;
va_start(ap, format);
suppfunc->print_param_args(format, ap);
va_end(ap);
}
/*
* A wrapper for imported print_param_name() function.
*/
static void
print_param_name(const char *name)
{
suppfunc->print_param_name(MOD_CONF_PREFIX, name);
}
/*
* A wrapper for imported print_param_end() function.
*/
static void
print_param_end(void)
{
suppfunc->print_param_end();
}
/*
* Convert string to u_int.
* Assumed that sizeof(u_long) >= sizeof(u_int).
* Since value was parsed by regular expression, it is not
* needed to check '-' in first character in a string (remember,
* that strtoul() and strtoull() work with negative values).
*/
static int
strto_u_int(u_int *result, const char *nptr, char **endptr)
{
u_long val_ul;
errno = 0;
val_ul = strtoul(nptr, endptr, 10);
if (errno != 0) {
logconferr("strtoul");
return -1;
}
if (val_ul > UINT_MAX) {
logconferrx("too big value for u_int type");
return -1;
}
*result = (u_int)val_ul;
return 0;
}
static int
conf_init(void)
{
suppfunc = IPA_AC_MOD_ENTRY(ipa_ip6fw).suppfunc;
memfunc = IPA_AC_MOD_ENTRY(ipa_ip6fw).memfunc;
if (memfunc->api_ver != MEMFUNC_API_VERSION) {
logconferrx("conf_init: module understands memfunc API version %u, exported memfunc API version is %u",
MEMFUNC_API_VERSION, memfunc->api_ver);
return -1;
}
if ((m_anon = memfunc->mem_type_new(MTYPE_NAME(anon), "Anonymous memory", 0)) == NULL ||
(m_buf = memfunc->mem_type_new(MTYPE_NAME(buf), "Statistics from kernel", 0)) == NULL) {
logconferrx("conf_init: mem_type_new failed");
return -1;
}
mem_realloc = memfunc->mem_realloc;
mem_free = memfunc->mem_free;
marray_init = memfunc->marray_init;
marray_deinit = memfunc->marray_deinit;
marray_alloc = memfunc->marray_alloc;
marray_minimize = memfunc->marray_minimize;
marray_check_index = memfunc->marray_check_index;
mzone_init = memfunc->mzone_init;
mzone_deinit = memfunc->mzone_deinit;
mzone_alloc = memfunc->mzone_alloc;
if ( (kip6fw_rules_marray = marray_init(MARRAY_NAME(kip6fw_rules), "Kernel IP6FW rules",
0, (void *)&kip6fw_rules, sizeof(struct kip6fw_rule),
KIP6FW_RULE_NSIZE, KIP6FW_RULE_NALLOC)) == NULL ||
(rules_ptr_marray = marray_init(MARRAY_NAME(rules_ptr), "Pointers to rules",
0, (void *)&rules_ptr, sizeof(struct rule *),
RULE_NSIZE, RULE_NALLOC)) == NULL) {
logconferrx("conf_init: marray_init failed");
return -1;
}
if ( (rule_mzone = mzone_init(MZONE_NAME(rule), "Rules", MEMFLAG_OPTIMIZE,
sizeof(struct rule), RULE_NSIZE, RULE_NALLOC)) == NULL) {
logconferrx("conf_init: mzone_init failed");
return -1;
}
nrules = nalloced = ninited = 0;
in_rule = 0;
nkip6fw_rules = 0;
debug_ip6fw = global_quiet = -1;
global_maxchunk = UINT64_C(0);
STAILQ_INIT(&rules_list);
return 0;
}
/*
* Compare two struct ip6fwac{}. This function is used in qsort(3).
*/
static int
cmp_ip6fwac(const void *p1, const void *p2)
{
const struct ip6fwac *q1 = p1, *q2 = p2;
if (q1->number > q2->number)
return 1;
if (q1->number < q2->number)
return -1;
return q1->subnumber > q2->subnumber ? 1 : -1;
}
/*
* Compare two struct kip6fw_rule{}. This function is used in qsort(3).
*/
static int
cmp_kip6fw_rule(const void *p1, const void *p2)
{
const struct kip6fw_rule *q1 = p1, *q2 = p2;
if (q1->number > q2->number)
return 1;
if (q1->number < q2->number)
return -1;
return q1->subnumber > q2->subnumber ? 1 : -1;
}
static int
conf_deinit(void)
{
struct ip6fwac *fwac;
struct kip6fw_rule *kip6fw_rule;
const struct rule *rule;
/* Try to minimize rules_ptr_marray. */
if (nrules != 0)
marray_minimize(rules_ptr_marray);
/* Try to minimize kip6fw_rules_marray. */
if (nkip6fw_rules != 0)
marray_minimize(kip6fw_rules_marray);
/* Sort kip6fw_rules. */
qsort(kip6fw_rules, nkip6fw_rules, sizeof *kip6fw_rules, cmp_kip6fw_rule);
/* Sort accounting rules in rule->ip6fwac. */
STAILQ_FOREACH(rule, &rules_list, link) {
if (rule->ip6fwac != NULL)
qsort(rule->ip6fwac, rule->nip6fwac, sizeof *rule->ip6fwac, cmp_ip6fwac);
for (kip6fw_rule = kip6fw_rules, fwac = rule->ip6fwac; fwac < rule->ip6fwac + rule->nip6fwac; ++fwac)
for (; kip6fw_rule < kip6fw_rules + nkip6fw_rules; ++kip6fw_rule)
if (kip6fw_rule->number == fwac->number && kip6fw_rule->subnumber == fwac->subnumber) {
fwac->kip6fw_rule = kip6fw_rule;
break;
}
}
/* Just to prevent of using incorrect link in alloc_rule(). */
currulename = NULL;
return 0;
}
static int
conf_event(u_int event, u_int no, const void *arg)
{
switch (event) {
case IPA_CONF_EVENT_RULE_BEGIN:
nrules = no + 1;
in_rule = 1;
currule = NULL;
currulename = arg;
break;
case IPA_CONF_EVENT_RULE_END:
in_rule = 0;
break;
}
return 0;
}
static void
show_maxchunk(const uint64_t *val)
{
if (*val > UINT64_C(0)) {
print_param_name("maxchunk");
suppfunc->print_bytes(val);
print_param_end();
}
}
static void
show_quiet(int val)
{
if (val >= 0) {
print_param_name("quiet");
print_param_args("%d", val);
print_param_end();
}
}
static int
conf_mimic_real(void)
{
struct rule *rule;
if (global_quiet < 0)
global_quiet = 0;
if (debug_ip6fw < 0)
debug_ip6fw = 0;
STAILQ_FOREACH(rule, &rules_list, link) {
if (rule->quiet < 0)
rule->quiet = global_quiet;
if (rule->maxchunk == UINT64_C(0))
rule->maxchunk = global_maxchunk;
}
return 0;
}
static void
conf_show(u_int sect_id, u_int no)
{
const struct rule *rule;
switch (sect_id) {
case IPA_CONF_SECT_ROOT:
if (debug_ip6fw >= 0) {
print_param_name("debug_ip6fw");
print_param_args("%d", debug_ip6fw);
print_param_end();
}
break;
case IPA_CONF_SECT_GLOBAL:
show_maxchunk(&global_maxchunk);
show_quiet(global_quiet);
break;
case IPA_CONF_SECT_RULE:
if (marray_check_index(rules_ptr_marray, no)) {
rule = RULE(no);
if (rule->ip6fwac != NULL) {
const struct ip6fwac *fwac;
print_param_name("rules");
for (fwac = rule->ip6fwac; fwac < rule->ip6fwac + rule->nip6fwac; ++fwac) {
suppfunc->print_space();
print_param_args("%s%hu", fwac->addition ? "" : "-",
fwac->number);
if (fwac->subnumber != 0)
print_param_args(".%u", fwac->subnumber);
}
print_param_end();
}
show_maxchunk(&rule->maxchunk);
show_quiet(rule->quiet);
}
break;
}
}
static struct rule *
alloc_rule(u_int ruleno)
{
struct rule *rule;
if ( (rule = mzone_alloc(rule_mzone)) == NULL) {
logconferrx("alloc_rule: mzone_alloc failed");
return NULL;
}
rule->rule_name = currulename;
rule->maxchunk = UINT64_C(0);
rule->quiet = -1;
rule->ip6fwac = NULL;
rule->nip6fwac = 0;
rule->inited = 0;
if (marray_alloc(rules_ptr_marray, &ruleno, 1) < 0) {
logconferrx("alloc_rule: marray_alloc failed");
return NULL;
}
RULE(ruleno) = rule;
++nalloced;
STAILQ_INSERT_TAIL(&rules_list, rule, link);
return rule;
}
static struct rule *
alloc_currule(void)
{
return currule = alloc_rule(nrules - 1);
}
/*
* Parse "rules" parameter.
*/
static int
parse_rules(void *arg)
{
char *ptr = *(char **)arg, *endptr;
u_int number, subnumber;
int addition;
struct ip6fwac *fwac;
struct kip6fw_rule *kip6fw_rule;
if (currule == NULL)
if (alloc_currule() == NULL)
return -1;
for (;;) {
/* Parse one argument. */
if (*ptr == '-') {
addition = 0;
++ptr;
} else
addition = 1;
if (strto_u_int(&number, ptr, &endptr) < 0)
return -1;
if (number > IP6FW_NUMBER_MAX || number < IP6FW_NUMBER_MIN) {
logconferrx("IP6FW rule number should be greater than %u and less than or equal to %u",
IP6FW_NUMBER_MIN, IP6FW_NUMBER_MAX);
return -1;
}
ptr = endptr;
subnumber = 0;
if (*ptr == '.') {
if (strto_u_int(&subnumber, ++ptr, &endptr) < 0) {
logconferrx("IP6FW rule subnumber should be less than %u",
UINT_MAX);
return -1;
}
if (number == IP6FW_NUMBER_MAX && subnumber != 0) {
logconferrx("subnumbers for IP6FW rule %u are not allowed",
IP6FW_NUMBER_MAX);
return -1;
}
ptr = endptr;
}
/* Check for dups. */
for (fwac = currule->ip6fwac; fwac < currule->ip6fwac + currule->nip6fwac; ++fwac)
if (fwac->number == number && fwac->subnumber == subnumber) {
if (fwac->addition != addition)
logconferrx("IP6FW rules %u.%u and -%u.%u have no effect",
number, subnumber, number, subnumber);
else
logconferrx("duplicated IP6FW rule %u.%u",
number, subnumber);
return -1;
}
/* Allocate new kip6fw_rule if there isn't number.subnumber. */
for (kip6fw_rule = kip6fw_rules; kip6fw_rule < kip6fw_rules + nkip6fw_rules; ++kip6fw_rule)
if (kip6fw_rule->number == number && kip6fw_rule->subnumber == subnumber)
break;
if (kip6fw_rule == kip6fw_rules + nkip6fw_rules) {
if (marray_alloc(kip6fw_rules_marray, &nkip6fw_rules, 1) < 0) {
logconferrx("marray_alloc failed");
return -1;
}
kip6fw_rule = kip6fw_rules + nkip6fw_rules;
kip6fw_rule->ref_count = 0;
kip6fw_rule->number = (u_short)number;
kip6fw_rule->subnumber = subnumber;
++nkip6fw_rules;
}
kip6fw_rule->ref_count++;
/* Add a new ip6fwac for rule. */
if ( (fwac = mem_realloc(currule->ip6fwac, (currule->nip6fwac + 1) * sizeof *fwac, m_anon)) == NULL) {
logconferrx("mem_realloc failed");
return -1;
}
currule->ip6fwac = fwac;
fwac += currule->nip6fwac;
++currule->nip6fwac;
fwac->number = (u_short)number;
fwac->subnumber = subnumber;
fwac->seen = 0;
fwac->addition = addition;
if (*ptr == '\0')
break; /* EOL */
++ptr;
}
return 0;
}
/*
* Parse "debug_ip6fw" parameter.
*/
static int
parse_debug_ip6fw(void *arg)
{
debug_ip6fw = (int)(*(uint32_t *)arg);
if (debug_ip6fw > 1) {
logconferrx("too big debug level, max level is 1");
return -1;
}
return 0;
}
/*
* Parse "maxchunk" parameter.
*/
static int
parse_maxchunk(void *arg)
{
const uint64_t maxchunk = *(uint64_t *)arg;
if (maxchunk == UINT64_C(0)) {
logconferrx("should be greater than zero");
return -1;
}
if (in_rule) {
if (currule == NULL)
if (alloc_currule() == NULL)
return -1;
currule->maxchunk = maxchunk;
} else
global_maxchunk = maxchunk;
return 0;
}
/*
* Parse "quiet" parameter.
*/
static int
parse_quiet(void *arg)
{
const u_int quiet = *(uint32_t *)arg;
if (quiet > 3) {
logconferrx("such value is not allowed, max allowed value is 3");
return -1;
}
if (in_rule) {
if (currule == NULL)
if (alloc_currule() == NULL)
return -1;
currule->quiet = (int)quiet;
} else
global_quiet = (int)quiet;
return 0;
}
static int
ac_pre_init(void)
{
struct kip6fw_rule *kip6fw_rule;
logmsgx(IPA_LOG_INFO, "initing accounting system for IPv6 Firewall, version "IPA_IP6FW_VERSION);
if (global_quiet < 0)
global_quiet = 0;
if (debug_ip6fw < 0)
debug_ip6fw = 0;
if (kip6fw_init() < 0) {
logmsgx(IPA_LOG_ERR, "ac_pre_init: kipfw_init failed");
return -1;
}
TAILQ_INIT(&kip6fw_rules_active);
for (kip6fw_rule = kip6fw_rules; kip6fw_rule < kip6fw_rules + nkip6fw_rules; ++kip6fw_rule)
TAILQ_INSERT_TAIL(&kip6fw_rules_active, kip6fw_rule, link);
return 0;
}
static int
ac_init(void)
{
const struct rule *rule;
if (nalloced != ninited)
STAILQ_FOREACH(rule, &rules_list, link)
if (rule->inited == 0)
logmsgx(IPA_LOG_WARNING, "rule %s: this rule has some module's parameter, but it does not use my accounting system",
rule->rule_name);
return 0;
}
static int
ac_init_statrule(u_int ruleno, const char *rule_name)
{
struct rule *rule;
if (!marray_check_index(rules_ptr_marray, ruleno)) {
if ( (rule = alloc_rule(ruleno)) == NULL) {
logmsgx(IPA_LOG_ERR, "ac_init_statrule: alloc_rule failed");
return -1;
}
rule->rule_name = rule_name;
rule->maxchunk = global_maxchunk;
rule->quiet = global_quiet;
} else {
rule = RULE(ruleno);
if (rule->quiet < 0)
rule->quiet = global_quiet;
if (rule->maxchunk == UINT64_C(0))
rule->maxchunk = global_maxchunk;
}
if (rule->ip6fwac == NULL)
logmsgx(IPA_LOG_WARNING, "ac_init_statrule: rule %s does not have \"rules\" parameter",
rule_name);
else if (rule->maxchunk == UINT64_C(0)) {
logmsgx(IPA_LOG_ERR, "ac_init_statrule: rule %s does not have \"maxchunk\" parameter, set it!",
rule_name);
return -1;
}
rule->inited = 1;
++ninited;
return 0;
}
/*
* Deinit a rule, simply free memory used by a rule.
*/
static int
ac_deinit_rule(u_int ruleno)
{
struct rule *rule;
if (marray_check_index(rules_ptr_marray, ruleno)) {
rule = RULE(ruleno);
mem_free(rule->ip6fwac, m_anon);
rule->ip6fwac = NULL;
--nalloced;
}
return 0;
}
/*
* Deinit accounting system.
*/
static int
ac_deinit(void)
{
struct rule *rule;
logmsgx(IPA_LOG_INFO, "deiniting IPv6 Firewall support");
if (nalloced != 0) {
/*
* Very uncommon case (if no error occurred during
* initialization), when some parameter is set for rule,
* but rule does not use this accounting module.
*/
STAILQ_FOREACH(rule, &rules_list, link)
mem_free(rule->ip6fwac, m_anon);
}
marray_deinit(kip6fw_rules_marray);
marray_deinit(rules_ptr_marray);
mzone_deinit(rule_mzone);
if (kip6fw_deinit() < 0) {
logmsgx(IPA_LOG_ERR, "ac_deinit: kip6fw_deinit failed");
return -1;
}
return 0;
}
static int
ac_get_rule_stat(u_int stat_generation, int newstat, u_int ruleno,
int *addition, uint64_t *ret_chunk)
{
static u_int cur_stat_generation = 0;
uint64_t chunk, chunk_sum, chunk_sub_sum;
struct ip6fwac *fwac;
const struct ip6fwac *fwac_end;
const struct rule *rule;
const struct ip6_fw *fw;
if (cur_stat_generation != stat_generation) {
if (kip6fw_get_stat() < 0) {
logmsgx(IPA_LOG_ERR, "ac_get_rule_stat: kip6fw_get_stat failed");
return -1;
}
cur_stat_generation = stat_generation;
}
rule = RULE(ruleno);
chunk_sum = chunk_sub_sum = UINT64_C(0);
fwac_end = rule->ip6fwac + rule->nip6fwac;
for (fwac = rule->ip6fwac; fwac < fwac_end; ++fwac) {
if (newstat)
fwac->seen = 0;
fw = fwac->kip6fw_rule->ip6_fw;
if (fw != NULL) {
/*
* At this point we know that rule with given
* number and subnumber exists and fw points to
* needed struct ip6_fw{}.
*/
if (fwac->seen == 0) {
fwac->seen = 1;
fwac->bcnt_old = IP6FWP_BCNT(fw);
if (!newstat && LOG_IP6FW_EVENTS(rule))
logmsgx(IPA_LOG_WARNING, "rule %s: ac_get_rule_stat: %hu.%u was added to IP6FW kernel table",
rule->rule_name, fwac->number, fwac->subnumber);
} else {
if (fwac->bcnt_old != IP6FWP_BCNT(fw)) {
if (fwac->bcnt_old < IP6FWP_BCNT(fw))
chunk = IP6FWP_BCNT(fw) - fwac->bcnt_old;
else {
chunk = kip6fw_bcnt_max - (fwac->bcnt_old - IP6FWP_BCNT(fw));
if (LOG_IP6FW_OVERFLOW(rule))
logmsgx(IPA_LOG_WARNING, "rule %s: ac_get_rule_stat: IP6FW counter %hu.%u was overflowed, old value = %"PRIu64" bytes, current value = %"PRIu64" bytes",
rule->rule_name, fwac->number, fwac->subnumber, fwac->bcnt_old, (uint64_t)IP6FWP_BCNT(fw));
if (chunk > rule->maxchunk) {
if (LOG_IP6FW_OVERFLOW(rule)) {
logmsgx(IPA_LOG_WARNING, "rule %s: ac_get_rule_stat: was IP6FW rule %hu.%u flushed? chunk = %"PRIu64" bytes, maxchunk = %"PRIu64" bytes",
rule->rule_name, fwac->number, fwac->subnumber,
chunk, rule->maxchunk);
logmsgx(IPA_LOG_INFO, "rule %s: ac_get_rule_stat: use absolute value %"PRIu64" bytes of counter of IP6FW %hu.%u rule",
rule->rule_name, (uint64_t)IP6FWP_BCNT(fw), fwac->number, fwac->subnumber);
}
chunk = IP6FWP_BCNT(fw);
}
}
} else
continue; /* Counter wasn't changed. */
/* At this point chunk is know. */
fwac->bcnt_old = IP6FWP_BCNT(fw);
if (fwac->addition) {
if (chunk_sub_sum >= chunk)
chunk_sub_sum -= chunk;
else {
chunk -= chunk_sub_sum;
chunk_sub_sum = UINT64_C(0);
if (chunk_sum <= UINT64_MAX - chunk)
chunk_sum += chunk;
else {
logmsgx(IPA_LOG_ERR, "rule %s: ac_get_rule_stat: internal positive counter overflowed",
rule->rule_name);
logmsgx(IPA_LOG_ERR, "rule %s: ac_get_rule_stat: this means that something is wrong in configuration",
rule->rule_name);
return -1;
}
}
} else /* fwac->addition == 0 */ {
if (chunk_sum >= chunk)
chunk_sum -= chunk;
else {
chunk -= chunk_sum;
chunk_sum = UINT64_C(0);
if (chunk_sub_sum <= UINT64_MAX - chunk)
chunk_sub_sum += chunk;
else {
logmsgx(IPA_LOG_ERR, "rule %s: ac_get_rule_stat: internal negative counter overflowed",
rule->rule_name);
logmsgx(IPA_LOG_ERR, "rule %s: ac_get_rule_stat: this means that something is wrong in configuration",
rule->rule_name);
return -1;
}
}
}
}
} else {
/* Not found in kernel IP6FW table. */
if (fwac->seen) {
fwac->seen = 0;
if (LOG_IP6FW_EVENTS(rule))
logmsgx(IPA_LOG_WARNING, "rule %s: ac_get_rule_stat: %hu.%u was deleted from IP6FW kernel table",
rule->rule_name, fwac->number, fwac->subnumber);
} else if (newstat) {
if (LOG_IP6FW_EVENTS(rule))
logmsgx(IPA_LOG_WARNING, "rule %s: ac_get_rule_stat: %hu.%u does not exist in IP6FW kernel table",
rule->rule_name, fwac->number, fwac->subnumber);
}
}
}
if (chunk_sum > UINT64_C(0)) {
*addition = 1;
*ret_chunk = chunk_sum;
} else {
*addition = 0;
*ret_chunk = chunk_sub_sum;
}
return 0;
}
/*
* This module always takes whole firewall table from the kernel
* (because of the nature of the kernel IP6FW API), but we can save
* some time in kip6fw_read_table() function by removing not needed
* rules from the kip6fw_rules_active list.
*/
static int
ac_set_rule_active(u_int ruleno, int active)
{
struct kip6fw_rule *kip6fw_rule1, *kip6fw_rule2;
const struct ip6fwac *fwac;
const struct rule *rule;
rule = RULE(ruleno);
for (fwac = rule->ip6fwac; fwac < rule->ip6fwac + rule->nip6fwac; ++fwac) {
kip6fw_rule1 = fwac->kip6fw_rule;
if (active == 0) {
/* Set inactive. */
kip6fw_rule1->ref_count--;
if (kip6fw_rule1->ref_count == 0)
TAILQ_REMOVE(&kip6fw_rules_active, kip6fw_rule1, link);
} else {
/* Set active. */
if (kip6fw_rule1->ref_count == 0) {
/*
* Have to make linear search to find previous
* active rule in list, can use kip6fw_rules_active,
* but I think that following code will be faster.
*/
if (kip6fw_rule1 != kip6fw_rules) {
/* Not the first rule in array of kip6fw_rules. */
kip6fw_rule2 = kip6fw_rule1;
do {
kip6fw_rule2--;
if (kip6fw_rule2->ref_count != 0) {
/* There is previous active rule. */
TAILQ_INSERT_AFTER(&kip6fw_rules_active, kip6fw_rule2, kip6fw_rule1, link);
goto inserted;
}
} while (kip6fw_rule2 != kip6fw_rules);
}
/*
* No previous active rule or this is the first
* rule, add it to the head of the list.
*/
TAILQ_INSERT_HEAD(&kip6fw_rules_active, kip6fw_rule1, link);
}
inserted:
kip6fw_rule1->ref_count++;
}
}
return 0;
}
static ipa_conf_sect conf_sect_tbl[] = {
{ NULL, 0, 0, NULL, NULL, IPA_CONF_TYPE_MISC, 0, NULL }
};
#define PAT_RULE "-?[[:digit:]]+(\\.[[:digit:]]+)?"
#define PAT_RULES "^"PAT_RULE"( "PAT_RULE")*$"
static regex_t reg_rules;
static const u_int sect_rule[] = { IPA_CONF_SECT_RULE, 0 };
static const u_int sect_root[] = { IPA_CONF_SECT_ROOT, 0 };
static const u_int sect_global_rule[] = { IPA_CONF_SECT_GLOBAL, IPA_CONF_SECT_RULE, 0 };
static ipa_conf_param conf_param_tbl[] = {
{ "rules", -1, PAT_RULES, ®_rules, IPA_CONF_TYPE_MISC, sect_rule, parse_rules },
{ "maxchunk", -1, NULL, NULL, IPA_CONF_TYPE_BYTES, sect_global_rule, parse_maxchunk },
{ "quiet", 1, NULL, NULL, IPA_CONF_TYPE_UINT32, sect_global_rule, parse_quiet },
{ "debug_ip6fw", 1, NULL, NULL, IPA_CONF_TYPE_UINT32, sect_root, parse_debug_ip6fw },
{ NULL, 0, NULL, NULL, IPA_CONF_TYPE_MISC, 0, NULL }
};
struct ipa_ac_mod IPA_AC_MOD_ENTRY(ipa_ip6fw) = {
AC_MOD_API_VERSION, /* api_ver */
MOD_FLAGS, /* mod_flags */
MOD_AC_NAME, /* ac_name */
NULL, /* suppfunc */
NULL, /* memfunc */
MOD_CONF_PREFIX, /* conf_prefix */
conf_sect_tbl, /* conf_sect_tbl */
conf_param_tbl, /* conf_param_tbl */
conf_init, /* conf_init */
conf_deinit, /* conf_deinit */
conf_event, /* conf_event */
conf_mimic_real, /* conf_mimic_real */
NULL, /* conf_inherit */
conf_show, /* conf_show */
ac_pre_init, /* ac_pre_init */
NULL, /* ac_init_autorule */
NULL, /* ac_init_dynrule */
ac_init_statrule, /* ac_init_statrule */
ac_init, /* ac_init */
ac_deinit_rule, /* ac_deinit_rule */
NULL, /* ac_deinit_autorule */
ac_deinit, /* ac_deinit */
NULL, /* ac_get_stat */
ac_get_rule_stat, /* ac_get_rule_stat */
NULL, /* ac_set_autorule_active */
ac_set_rule_active, /* ac_set_rule_active */
NULL, /* ac_set_limit_active */
NULL, /* ac_set_threshold_active */
NULL, /* ac_limit_event */
NULL, /* ac_threshold_event */
NULL, /* ac_create_rule */
NULL /* ac_delete_rule */
};
syntax highlighted by Code2HTML, v. 0.9.1