iptables-1.3.2-20050720
[iptables.git] / extensions / libipt_account.c
diff --git a/extensions/libipt_account.c b/extensions/libipt_account.c
new file mode 100644 (file)
index 0000000..d049a03
--- /dev/null
@@ -0,0 +1,277 @@
+/* 
+ * 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);
+}
+