mirror of
git://git.code.sf.net/p/xtables-addons/xtables-addons
synced 2025-09-07 05:05:12 +02:00
Merge branch 'ACCOUNT'
This commit is contained in:
@@ -104,5 +104,6 @@ AC_SUBST([kbuilddir])
|
||||
AC_SUBST([ksourcedir])
|
||||
AC_SUBST([xtlibdir])
|
||||
AC_CONFIG_FILES([Makefile Makefile.iptrules Makefile.mans
|
||||
extensions/Makefile extensions/ipset/Makefile])
|
||||
extensions/Makefile extensions/ACCOUNT/Makefile
|
||||
extensions/ipset/Makefile])
|
||||
AC_OUTPUT
|
||||
|
5
extensions/ACCOUNT/Kbuild
Normal file
5
extensions/ACCOUNT/Kbuild
Normal file
@@ -0,0 +1,5 @@
|
||||
# -*- Makefile -*-
|
||||
|
||||
EXTRA_CFLAGS = -I${src}/..
|
||||
|
||||
obj-m += xt_ACCOUNT.o
|
8
extensions/ACCOUNT/Makefile.am
Normal file
8
extensions/ACCOUNT/Makefile.am
Normal file
@@ -0,0 +1,8 @@
|
||||
# -*- Makefile -*-
|
||||
|
||||
include ../../Makefile.extra
|
||||
|
||||
sbin_PROGRAMS = iptaccount
|
||||
iptaccount_LDADD = libxt_ACCOUNT_cl.la
|
||||
|
||||
lib_LTLIBRARIES = libxt_ACCOUNT_cl.la
|
3
extensions/ACCOUNT/Mbuild
Normal file
3
extensions/ACCOUNT/Mbuild
Normal file
@@ -0,0 +1,3 @@
|
||||
# -*- Makefile -*-
|
||||
|
||||
obj-${build_ACCOUNT} += libxt_ACCOUNT.so
|
223
extensions/ACCOUNT/iptaccount.c
Normal file
223
extensions/ACCOUNT/iptaccount.c
Normal file
@@ -0,0 +1,223 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2004-2006 by Intra2net AG *
|
||||
* opensource@intra2net.com *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License *
|
||||
* version 2.1 as published by the Free Software Foundation; *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <getopt.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include <libxt_ACCOUNT_cl.h>
|
||||
|
||||
bool exit_now;
|
||||
static void sig_term(int signr)
|
||||
{
|
||||
signal(SIGINT, SIG_IGN);
|
||||
signal(SIGQUIT, SIG_IGN);
|
||||
signal(SIGTERM, SIG_IGN);
|
||||
|
||||
exit_now = true;
|
||||
}
|
||||
|
||||
char *addr_to_dotted(unsigned int);
|
||||
char *addr_to_dotted(unsigned int addr)
|
||||
{
|
||||
static char buf[17];
|
||||
const unsigned char *bytep;
|
||||
|
||||
bytep = (const unsigned char *)&addr;
|
||||
snprintf(buf, 16, "%u.%u.%u.%u", bytep[0], bytep[1], bytep[2], bytep[3]);
|
||||
buf[16] = 0;
|
||||
return buf;
|
||||
}
|
||||
|
||||
static void show_usage(void)
|
||||
{
|
||||
printf("Unknown command line option. Try: [-u] [-h] [-a] [-f] [-c] [-s] [-l name]\n");
|
||||
printf("[-u] show kernel handle usage\n");
|
||||
printf("[-h] free all kernel handles (experts only!)\n\n");
|
||||
printf("[-a] list all table names\n");
|
||||
printf("[-l name] show data in table <name>\n");
|
||||
printf("[-f] flush data after showing\n");
|
||||
printf("[-c] loop every second (abort with CTRL+C)\n");
|
||||
printf("[-s] CSV output (for spreadsheet import)\n");
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct ipt_ACCOUNT_context ctx;
|
||||
struct ipt_acc_handle_ip *entry;
|
||||
int i;
|
||||
char optchar;
|
||||
bool doHandleUsage = false, doHandleFree = false, doTableNames = false;
|
||||
bool doFlush = false, doContinue = false, doCSV = false;
|
||||
|
||||
char *table_name = NULL;
|
||||
const char *name;
|
||||
|
||||
printf("\nlibxt_ACCOUNT_cl userspace accounting tool v%s\n\n",
|
||||
LIBXT_ACCOUNT_VERSION);
|
||||
|
||||
if (argc == 1)
|
||||
{
|
||||
show_usage();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
while ((optchar = getopt(argc, argv, "uhacfsl:")) != -1)
|
||||
{
|
||||
switch (optchar)
|
||||
{
|
||||
case 'u':
|
||||
doHandleUsage = true;
|
||||
break;
|
||||
case 'h':
|
||||
doHandleFree = true;
|
||||
break;
|
||||
case 'a':
|
||||
doTableNames = true;
|
||||
break;
|
||||
case 'f':
|
||||
doFlush = true;
|
||||
break;
|
||||
case 'c':
|
||||
doContinue = true;
|
||||
break;
|
||||
case 's':
|
||||
doCSV = true;
|
||||
break;
|
||||
case 'l':
|
||||
table_name = strdup(optarg);
|
||||
break;
|
||||
case '?':
|
||||
default:
|
||||
show_usage();
|
||||
exit(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// install exit handler
|
||||
if (signal(SIGTERM, sig_term) == SIG_ERR)
|
||||
{
|
||||
printf("can't install signal handler for SIGTERM\n");
|
||||
exit(-1);
|
||||
}
|
||||
if (signal(SIGINT, sig_term) == SIG_ERR)
|
||||
{
|
||||
printf("can't install signal handler for SIGINT\n");
|
||||
exit(-1);
|
||||
}
|
||||
if (signal(SIGQUIT, sig_term) == SIG_ERR)
|
||||
{
|
||||
printf("can't install signal handler for SIGQUIT\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
if (ipt_ACCOUNT_init(&ctx))
|
||||
{
|
||||
printf("Init failed: %s\n", ctx.error_str);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
// Get handle usage?
|
||||
if (doHandleUsage)
|
||||
{
|
||||
int rtn = ipt_ACCOUNT_get_handle_usage(&ctx);
|
||||
if (rtn < 0)
|
||||
{
|
||||
printf("get_handle_usage failed: %s\n", ctx.error_str);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
printf("Current kernel handle usage: %d\n", ctx.handle.itemcount);
|
||||
}
|
||||
|
||||
if (doHandleFree)
|
||||
{
|
||||
int rtn = ipt_ACCOUNT_free_all_handles(&ctx);
|
||||
if (rtn < 0)
|
||||
{
|
||||
printf("handle_free_all failed: %s\n", ctx.error_str);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
printf("Freed all handles in kernel space\n");
|
||||
}
|
||||
|
||||
if (doTableNames)
|
||||
{
|
||||
int rtn = ipt_ACCOUNT_get_table_names(&ctx);
|
||||
if (rtn < 0)
|
||||
{
|
||||
printf("get_table_names failed: %s\n", ctx.error_str);
|
||||
exit(-1);
|
||||
}
|
||||
while ((name = ipt_ACCOUNT_get_next_name(&ctx)) != 0)
|
||||
printf("Found table: %s\n", name);
|
||||
}
|
||||
|
||||
if (table_name)
|
||||
{
|
||||
// Read out data
|
||||
if (doCSV)
|
||||
printf("IP;SRC packets;SRC bytes;DST packets;DST bytes\n");
|
||||
else
|
||||
printf("Showing table: %s\n", table_name);
|
||||
|
||||
i = 0;
|
||||
while (!exit_now)
|
||||
{
|
||||
// Get entries from table test
|
||||
if (ipt_ACCOUNT_read_entries(&ctx, table_name, !doFlush))
|
||||
{
|
||||
printf("Read failed: %s\n", ctx.error_str);
|
||||
ipt_ACCOUNT_deinit(&ctx);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
if (!doCSV)
|
||||
printf("Run #%d - %u %s found\n", i, ctx.handle.itemcount,
|
||||
ctx.handle.itemcount == 1 ? "item" : "items");
|
||||
|
||||
// Output and free entries
|
||||
while ((entry = ipt_ACCOUNT_get_next_entry(&ctx)) != NULL)
|
||||
{
|
||||
if (doCSV)
|
||||
printf("%s;%u;%u;%u;%u\n",
|
||||
addr_to_dotted(entry->ip), entry->src_packets, entry->src_bytes,
|
||||
entry->dst_packets, entry->dst_bytes);
|
||||
else
|
||||
printf("IP: %s SRC packets: %u bytes: %u DST packets: %u bytes: %u\n",
|
||||
addr_to_dotted(entry->ip), entry->src_packets, entry->src_bytes,
|
||||
entry->dst_packets, entry->dst_bytes);
|
||||
}
|
||||
|
||||
if (doContinue)
|
||||
{
|
||||
sleep(1);
|
||||
i++;
|
||||
} else
|
||||
exit_now = true;
|
||||
}
|
||||
}
|
||||
|
||||
printf("Finished.\n");
|
||||
ipt_ACCOUNT_deinit(&ctx);
|
||||
exit(0);
|
||||
}
|
199
extensions/ACCOUNT/libxt_ACCOUNT_cl.c
Normal file
199
extensions/ACCOUNT/libxt_ACCOUNT_cl.c
Normal file
@@ -0,0 +1,199 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2004 by Intra2net AG *
|
||||
* opensource@intra2net.com *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License *
|
||||
* version 2.1 as published by the Free Software Foundation; *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <linux/if.h>
|
||||
|
||||
#include <libxt_ACCOUNT_cl.h>
|
||||
|
||||
int ipt_ACCOUNT_init(struct ipt_ACCOUNT_context *ctx)
|
||||
{
|
||||
memset(ctx, 0, sizeof(struct ipt_ACCOUNT_context));
|
||||
ctx->handle.handle_nr = -1;
|
||||
|
||||
ctx->sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
|
||||
if (ctx->sockfd < 0) {
|
||||
ctx->sockfd = -1;
|
||||
ctx->error_str = "Can't open socket to kernel. "
|
||||
"Permission denied or ipt_ACCOUNT module not loaded";
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 4096 bytes default buffer should save us from reallocations
|
||||
// as it fits 200 concurrent active clients
|
||||
if ((ctx->data = malloc(IPT_ACCOUNT_MIN_BUFSIZE)) == NULL) {
|
||||
close(ctx->sockfd);
|
||||
ctx->sockfd = -1;
|
||||
ctx->error_str = "Out of memory for data buffer";
|
||||
return -1;
|
||||
}
|
||||
ctx->data_size = IPT_ACCOUNT_MIN_BUFSIZE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ipt_ACCOUNT_free_entries(struct ipt_ACCOUNT_context *ctx)
|
||||
{
|
||||
if (ctx->handle.handle_nr != -1) {
|
||||
setsockopt(ctx->sockfd, IPPROTO_IP, IPT_SO_SET_ACCOUNT_HANDLE_FREE,
|
||||
&ctx->handle, sizeof(struct ipt_acc_handle_sockopt));
|
||||
ctx->handle.handle_nr = -1;
|
||||
}
|
||||
|
||||
ctx->handle.itemcount = 0;
|
||||
ctx->pos = 0;
|
||||
}
|
||||
|
||||
void ipt_ACCOUNT_deinit(struct ipt_ACCOUNT_context *ctx)
|
||||
{
|
||||
free(ctx->data);
|
||||
ctx->data = NULL;
|
||||
|
||||
ipt_ACCOUNT_free_entries(ctx);
|
||||
|
||||
close(ctx->sockfd);
|
||||
ctx->sockfd = -1;
|
||||
}
|
||||
|
||||
int ipt_ACCOUNT_read_entries(struct ipt_ACCOUNT_context *ctx,
|
||||
const char *table, char dont_flush)
|
||||
{
|
||||
unsigned int s = sizeof(struct ipt_acc_handle_sockopt);
|
||||
unsigned int new_size;
|
||||
int rtn;
|
||||
|
||||
strncpy(ctx->handle.name, table, ACCOUNT_TABLE_NAME_LEN-1);
|
||||
|
||||
// Get table information
|
||||
if (!dont_flush)
|
||||
rtn = getsockopt(ctx->sockfd, IPPROTO_IP,
|
||||
IPT_SO_GET_ACCOUNT_PREPARE_READ_FLUSH, &ctx->handle, &s);
|
||||
else
|
||||
rtn = getsockopt(ctx->sockfd, IPPROTO_IP, IPT_SO_GET_ACCOUNT_PREPARE_READ,
|
||||
&ctx->handle, &s);
|
||||
|
||||
if (rtn < 0) {
|
||||
ctx->error_str = "Can't get table information from kernel. "
|
||||
"Does it exist?";
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Check data buffer size
|
||||
ctx->pos = 0;
|
||||
new_size = ctx->handle.itemcount * sizeof(struct ipt_acc_handle_ip);
|
||||
// We want to prevent reallocations all the time
|
||||
if (new_size < IPT_ACCOUNT_MIN_BUFSIZE)
|
||||
new_size = IPT_ACCOUNT_MIN_BUFSIZE;
|
||||
|
||||
// Reallocate if it's too small or twice as big
|
||||
if (ctx->data_size < new_size || ctx->data_size > new_size * 2) {
|
||||
// Free old buffer
|
||||
free(ctx->data);
|
||||
ctx->data_size = 0;
|
||||
|
||||
if ((ctx->data = malloc(new_size)) == NULL) {
|
||||
ctx->error_str = "Out of memory for data buffer";
|
||||
ipt_ACCOUNT_free_entries(ctx);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ctx->data_size = new_size;
|
||||
}
|
||||
|
||||
// Copy data from kernel
|
||||
memcpy(ctx->data, &ctx->handle, sizeof(struct ipt_acc_handle_sockopt));
|
||||
rtn = getsockopt(ctx->sockfd, IPPROTO_IP, IPT_SO_GET_ACCOUNT_GET_DATA,
|
||||
ctx->data, &ctx->data_size);
|
||||
if (rtn < 0) {
|
||||
ctx->error_str = "Can't get data from kernel. "
|
||||
"Check /var/log/messages for details.";
|
||||
ipt_ACCOUNT_free_entries(ctx);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Free kernel handle but don't reset pos/itemcount
|
||||
setsockopt(ctx->sockfd, IPPROTO_IP, IPT_SO_SET_ACCOUNT_HANDLE_FREE,
|
||||
&ctx->handle, sizeof(struct ipt_acc_handle_sockopt));
|
||||
ctx->handle.handle_nr = -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct ipt_acc_handle_ip *ipt_ACCOUNT_get_next_entry(struct ipt_ACCOUNT_context *ctx)
|
||||
{
|
||||
struct ipt_acc_handle_ip *rtn;
|
||||
|
||||
// Empty or no more items left to return?
|
||||
if (!ctx->handle.itemcount || ctx->pos >= ctx->handle.itemcount)
|
||||
return NULL;
|
||||
|
||||
// Get next entry
|
||||
rtn = (struct ipt_acc_handle_ip *)(ctx->data + ctx->pos
|
||||
* sizeof(struct ipt_acc_handle_ip));
|
||||
ctx->pos++;
|
||||
|
||||
return rtn;
|
||||
}
|
||||
|
||||
int ipt_ACCOUNT_get_handle_usage(struct ipt_ACCOUNT_context *ctx)
|
||||
{
|
||||
unsigned int s = sizeof(struct ipt_acc_handle_sockopt);
|
||||
if (getsockopt(ctx->sockfd, IPPROTO_IP,
|
||||
IPT_SO_GET_ACCOUNT_GET_HANDLE_USAGE, &ctx->handle, &s) < 0) {
|
||||
ctx->error_str = "Can't get handle usage information from kernel";
|
||||
return -1;
|
||||
}
|
||||
ctx->handle.handle_nr = -1;
|
||||
|
||||
return ctx->handle.itemcount;
|
||||
}
|
||||
|
||||
int ipt_ACCOUNT_free_all_handles(struct ipt_ACCOUNT_context *ctx)
|
||||
{
|
||||
if (setsockopt(ctx->sockfd, IPPROTO_IP,
|
||||
IPT_SO_SET_ACCOUNT_HANDLE_FREE_ALL, NULL, 0) < 0) {
|
||||
ctx->error_str = "Can't free all kernel handles";
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ipt_ACCOUNT_get_table_names(struct ipt_ACCOUNT_context *ctx)
|
||||
{
|
||||
int rtn = getsockopt(ctx->sockfd, IPPROTO_IP,
|
||||
IPT_SO_GET_ACCOUNT_GET_TABLE_NAMES,
|
||||
ctx->data, &ctx->data_size);
|
||||
if (rtn < 0) {
|
||||
ctx->error_str = "Can't get table names from kernel. Out of memory, "
|
||||
"MINBUFISZE too small?";
|
||||
return -1;
|
||||
}
|
||||
ctx->pos = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *ipt_ACCOUNT_get_next_name(struct ipt_ACCOUNT_context *ctx)
|
||||
{
|
||||
const char *rtn;
|
||||
if (((char *)ctx->data)[ctx->pos] == 0)
|
||||
return 0;
|
||||
|
||||
rtn = ctx->data + ctx->pos;
|
||||
ctx->pos += strlen(ctx->data + ctx->pos) + 1;
|
||||
|
||||
return rtn;
|
||||
}
|
60
extensions/ACCOUNT/libxt_ACCOUNT_cl.h
Normal file
60
extensions/ACCOUNT/libxt_ACCOUNT_cl.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2004 by Intra2net AG *
|
||||
* opensource@intra2net.com *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License *
|
||||
* version 2.1 as published by the Free Software Foundation; *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef _xt_ACCOUNT_cl_H
|
||||
#define _xt_ACCOUNT_cl_H
|
||||
|
||||
#include <xt_ACCOUNT.h>
|
||||
|
||||
#define LIBXT_ACCOUNT_VERSION "1.3"
|
||||
|
||||
/* Don't set this below the size of struct ipt_account_handle_sockopt */
|
||||
#define IPT_ACCOUNT_MIN_BUFSIZE 4096
|
||||
|
||||
struct ipt_ACCOUNT_context
|
||||
{
|
||||
int sockfd;
|
||||
struct ipt_acc_handle_sockopt handle;
|
||||
|
||||
unsigned int data_size;
|
||||
void *data;
|
||||
unsigned int pos;
|
||||
|
||||
char *error_str;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int ipt_ACCOUNT_init(struct ipt_ACCOUNT_context *ctx);
|
||||
void ipt_ACCOUNT_deinit(struct ipt_ACCOUNT_context *ctx);
|
||||
|
||||
void ipt_ACCOUNT_free_entries(struct ipt_ACCOUNT_context *ctx);
|
||||
int ipt_ACCOUNT_read_entries(struct ipt_ACCOUNT_context *ctx,
|
||||
const char *table, char dont_flush);
|
||||
struct ipt_acc_handle_ip *ipt_ACCOUNT_get_next_entry(
|
||||
struct ipt_ACCOUNT_context *ctx);
|
||||
|
||||
/* ipt_ACCOUNT_free_entries is for internal use only function as this library
|
||||
is constructed to be used in a loop -> Don't allocate memory all the time.
|
||||
The data buffer is freed on deinit() */
|
||||
|
||||
int ipt_ACCOUNT_get_handle_usage(struct ipt_ACCOUNT_context *ctx);
|
||||
int ipt_ACCOUNT_free_all_handles(struct ipt_ACCOUNT_context *ctx);
|
||||
int ipt_ACCOUNT_get_table_names(struct ipt_ACCOUNT_context *ctx);
|
||||
const char *ipt_ACCOUNT_get_next_name(struct ipt_ACCOUNT_context *ctx);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
@@ -1037,7 +1037,7 @@ static struct nf_sockopt_ops ipt_acc_sockopts = {
|
||||
|
||||
static int __init account_tg_init(void)
|
||||
{
|
||||
init_MUTEX(&ipt_acc_userspace_mutex);
|
||||
sema_init(&ipt_acc_userspace_mutex, 1);
|
||||
|
||||
if ((ipt_acc_tables =
|
||||
kmalloc(ACCOUNT_MAX_TABLES *
|
@@ -5,7 +5,7 @@ include ${XA_ABSTOPSRCDIR}/mconfig
|
||||
|
||||
obj-m += compat_xtables.o
|
||||
|
||||
obj-${build_ACCOUNT} += xt_ACCOUNT.o
|
||||
obj-${build_ACCOUNT} += ACCOUNT/
|
||||
obj-${build_CHAOS} += xt_CHAOS.o
|
||||
obj-${build_DELUDE} += xt_DELUDE.o
|
||||
obj-${build_DHCPMAC} += xt_DHCPMAC.o
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# -*- Makefile -*-
|
||||
|
||||
obj-${build_ACCOUNT} += libxt_ACCOUNT.so
|
||||
obj-${build_ACCOUNT} += ACCOUNT/
|
||||
obj-${build_CHAOS} += libxt_CHAOS.so
|
||||
obj-${build_DELUDE} += libxt_DELUDE.so
|
||||
obj-${build_DHCPMAC} += libxt_DHCPMAC.so libxt_dhcpmac.so
|
||||
|
13
extensions/xt_ACCOUNT.Kconfig
Normal file
13
extensions/xt_ACCOUNT.Kconfig
Normal file
@@ -0,0 +1,13 @@
|
||||
config NETFILTER_XT_TARGET_ACCOUNT
|
||||
tristate "ACCOUNT target support"
|
||||
depends on NETFILTER_XTABLES
|
||||
---help---
|
||||
This module implements an ACCOUNT target
|
||||
|
||||
The ACCOUNT target is a high performance accounting system for large
|
||||
local networks. It allows per-IP accounting in whole prefixes of IPv4
|
||||
addresses with size of up to /8 without the need to add individual
|
||||
accouting rule for each IP address.
|
||||
|
||||
For more information go to:
|
||||
http://www.intra2net.com/de/produkte/opensource/ipt_account/
|
Reference in New Issue
Block a user