blob: d049a03df7ffe775b147f6ea0e844eededfc35f5 [file] [log] [blame]
/*
* accounting match helper (libipt_account.c)
* (C) 2003,2004 by Piotr Gasid³o (quaker@barbara.eu.org)
*
* Version: 0.1.6
*
* This software is distributed under the terms of GNU GPL
*/
#include <stdio.h>
#include <stdlib.h>
#include <iptables.h>
#include <string.h>
#include <getopt.h>
#include <linux/netfilter_ipv4/ipt_account.h>
#ifndef HIPQUAD
#define HIPQUAD(addr) \
((unsigned char *)&addr)[3], \
((unsigned char *)&addr)[2], \
((unsigned char *)&addr)[1], \
((unsigned char *)&addr)[0]
#endif
static void help(void) {
printf(
"account v%s options:\n"
"--aaddr network/netmask\n"
" defines network/netmask for which make statistics.\n"
"--aname name\n"
" defines name of list where statistics will be kept. If no is\n"
" specified DEFAULT will be used.\n"
"--ashort\n"
" table will colect only short statistics (only total counters\n"
" without splitting it into protocols.\n"
,
IPTABLES_VERSION);
};
static struct option opts[] = {
{ .name = "aaddr", .has_arg = 1, .flag = NULL, .val = 201 },
{ .name = "aname", .has_arg = 1, .flag = NULL, .val = 202 },
{ .name = "ashort", .has_arg = 0, .flag = NULL, .val = 203 },
{ .name = 0, .has_arg = 0, .flag = 0, .val = 0 }
};
/* Helper functions for parse_network */
int parseip(const char *parameter, u_int32_t *ip) {
char buffer[16], *bufferptr, *dot;
unsigned int i, shift, part;
if (strlen(parameter) > 15)
return 0;
strncpy(buffer, parameter, 15);
buffer[15] = 0;
bufferptr = buffer;
for (i = 0, shift = 24, *ip = 0; i < 3; i++, shift -= 8) {
/* no dot */
if ((dot = strchr(bufferptr, '.')) == NULL)
return 0;
/* not a number */
if ((part = strtol(bufferptr, (char**)NULL, 10)) < 0)
return 0;
/* to big number */
if (part > 255)
return 0;
*ip |= part << shift;
bufferptr = dot + 1;
}
/* not a number */
if ((part = strtol(bufferptr, (char**)NULL, 10)) < 0)
return 0;
/* to big number */
if (part > 255)
return 0;
*ip |= part;
return 1;
}
static void parsenetwork(const char *parameter, u_int32_t *network) {
if (!parseip(parameter, network))
exit_error(PARAMETER_PROBLEM, "account: wrong ip in network");
}
static void parsenetmaskasbits(const char *parameter, u_int32_t *netmask) {
u_int32_t bits;
if ((bits = strtol(parameter, (char **)NULL, 10)) < 0 || bits > 32)
exit_error(PARAMETER_PROBLEM, "account: wrong netmask");
*netmask = 0xffffffff << (32 - bits);
}
static void parsenetmaskasip(const char *parameter, u_int32_t *netmask) {
if (!parseip(parameter, netmask))
exit_error(PARAMETER_PROBLEM, "account: wrong ip in netmask");
}
static void parsenetmask(const char *parameter, u_int32_t *netmask)
{
if (strchr(parameter, '.') != NULL)
parsenetmaskasip(parameter, netmask);
else
parsenetmaskasbits(parameter, netmask);
}
static void parsenetworkandnetmask(const char *parameter, u_int32_t *network, u_int32_t *netmask)
{
char buffer[32], *slash;
if (strlen(parameter) > 31)
/* text is to long, even for 255.255.255.255/255.255.255.255 */
exit_error(PARAMETER_PROBLEM, "account: wrong network/netmask");
strncpy(buffer, parameter, 31);
buffer[31] = 0;
/* check whether netmask is given */
if ((slash = strchr(buffer, '/')) != NULL) {
parsenetmask(slash + 1, netmask);
*slash = 0;
} else
*netmask = 0xffffffff;
parsenetwork(buffer, network);
if ((*network & *netmask) != *network)
exit_error(PARAMETER_PROBLEM, "account: wrong network/netmask");
}
/* Function gets network & netmask from argument after --aaddr */
static void parse_network(const char *parameter, struct t_ipt_account_info *info) {
parsenetworkandnetmask(parameter, &info->network, &info->netmask);
}
/* validate netmask */
inline int valid_netmask(u_int32_t netmask) {
while (netmask & 0x80000000)
netmask <<= 1;
if (netmask != 0)
return 0;
return 1;
}
/* validate network/netmask pair */
inline int valid_network_and_netmask(struct t_ipt_account_info *info) {
if (!valid_netmask(info->netmask))
return 0;
if ((info->network & info->netmask) != info->network)
return 0;
return 1;
}
/* Function initializes match */
static void init(struct ipt_entry_match *match,
unsigned int *nfcache) {
struct t_ipt_account_info *info = (struct t_ipt_account_info *)(match)->data;
/* set default table name to DEFAULT */
strncpy(info->name, "DEFAULT", IPT_ACCOUNT_NAME_LEN);
info->shortlisting = 0;
}
/* Function parses match's arguments */
static int parse(int c, char **argv,
int invert,
unsigned int *flags,
const struct ipt_entry *entry,
unsigned int *nfcache,
struct ipt_entry_match **match) {
struct t_ipt_account_info *info = (struct t_ipt_account_info *)(*match)->data;
switch (c) {
/* --aaddr */
case 201:
parse_network(optarg, info);
if (!valid_network_and_netmask(info))
exit_error(PARAMETER_PROBLEM, "account: wrong network/netmask");
*flags = 1;
break;
/* --aname */
case 202:
if (strlen(optarg) < IPT_ACCOUNT_NAME_LEN)
strncpy(info->name, optarg, IPT_ACCOUNT_NAME_LEN);
else
exit_error(PARAMETER_PROBLEM, "account: Too long table name");
break;
/* --ashort */
case 203:
info->shortlisting = 1;
break;
default:
return 0;
}
return 1;
}
/* Final check whether network/netmask was specified */
static void final_check(unsigned int flags) {
if (!flags)
exit_error(PARAMETER_PROBLEM, "account: You need specify '--aaddr' parameter");
}
/* Function used for printing rule with account match for iptables -L */
static void print(const struct ipt_ip *ip,
const struct ipt_entry_match *match,
int numeric) {
struct t_ipt_account_info *info = (struct t_ipt_account_info *)match->data;
printf("account: ");
printf("network/netmask: ");
printf("%u.%u.%u.%u/%u.%u.%u.%u ",
HIPQUAD(info->network),
HIPQUAD(info->netmask)
);
printf("name: %s ", info->name);
if (info->shortlisting)
printf("short-listing ");
}
/* Function used for saving rule containing account match */
static void save(const struct ipt_ip *ip,
const struct ipt_entry_match *match) {
struct t_ipt_account_info *info = (struct t_ipt_account_info *)match->data;
printf("--aaddr ");
printf("%u.%u.%u.%u/%u.%u.%u.%u ",
HIPQUAD(info->network),
HIPQUAD(info->netmask)
);
printf("--aname %s ", info->name);
if (info->shortlisting)
printf("--ashort ");
}
static struct iptables_match account = {
.next = NULL,
.name = "account",
.version = IPTABLES_VERSION,
.size = IPT_ALIGN(sizeof(struct t_ipt_account_info)),
.userspacesize = IPT_ALIGN(sizeof(struct t_ipt_account_info)),
.help = &help,
.init = &init,
.parse = &parse,
.final_check = &final_check,
.print = &print,
.save = &save,
.extra_opts = opts
};
/* Function which registers match */
void _init(void)
{
register_match(&account);
}