[netfilter-cvslog] r3611 - in trunk/patch-o-matic-ng: . geoip geoip/iptables geoip/iptables/extensions geoip/linux geoip/linux/Documentation geoip/linux/include geoip/linux/include/linux geoip/linux/include/linux/netfilter_ipv4 geoip/linux/net geoip/linux/net/ipv4 geoip/linux/net/ipv4/netfilter geoip/linux-2.6 geoip/linux-2.6/include geoip/linux-2.6/include/linux geoip/linux-2.6/include/linux/netfilter_ipv4 geoip/linux-2.6/net geoip/linux-2.6/net/ipv4 geoip/linux-2.6/net/ipv4/netfilter

gandalf at netfilter.org gandalf at netfilter.org
Sun Jan 23 16:53:15 CET 2005


Author: gandalf at netfilter.org
Date: 2005-01-23 16:53:14 +0100 (Sun, 23 Jan 2005)
New Revision: 3611

Added:
   trunk/patch-o-matic-ng/geoip/
   trunk/patch-o-matic-ng/geoip/help
   trunk/patch-o-matic-ng/geoip/info
   trunk/patch-o-matic-ng/geoip/iptables/
   trunk/patch-o-matic-ng/geoip/iptables/extensions/
   trunk/patch-o-matic-ng/geoip/iptables/extensions/.geoip-test
   trunk/patch-o-matic-ng/geoip/iptables/extensions/libipt_geoip.c
   trunk/patch-o-matic-ng/geoip/linux-2.6/
   trunk/patch-o-matic-ng/geoip/linux-2.6/include/
   trunk/patch-o-matic-ng/geoip/linux-2.6/include/linux/
   trunk/patch-o-matic-ng/geoip/linux-2.6/include/linux/netfilter_ipv4/
   trunk/patch-o-matic-ng/geoip/linux-2.6/include/linux/netfilter_ipv4/ipt_geoip.h
   trunk/patch-o-matic-ng/geoip/linux-2.6/net/
   trunk/patch-o-matic-ng/geoip/linux-2.6/net/ipv4/
   trunk/patch-o-matic-ng/geoip/linux-2.6/net/ipv4/netfilter/
   trunk/patch-o-matic-ng/geoip/linux-2.6/net/ipv4/netfilter/Kconfig.ladd
   trunk/patch-o-matic-ng/geoip/linux-2.6/net/ipv4/netfilter/Makefile.ladd
   trunk/patch-o-matic-ng/geoip/linux-2.6/net/ipv4/netfilter/ipt_geoip.c
   trunk/patch-o-matic-ng/geoip/linux/
   trunk/patch-o-matic-ng/geoip/linux/Documentation/
   trunk/patch-o-matic-ng/geoip/linux/Documentation/Configure.help.ladd
   trunk/patch-o-matic-ng/geoip/linux/include/
   trunk/patch-o-matic-ng/geoip/linux/include/linux/
   trunk/patch-o-matic-ng/geoip/linux/include/linux/netfilter_ipv4/
   trunk/patch-o-matic-ng/geoip/linux/include/linux/netfilter_ipv4/ipt_geoip.h
   trunk/patch-o-matic-ng/geoip/linux/net/
   trunk/patch-o-matic-ng/geoip/linux/net/ipv4/
   trunk/patch-o-matic-ng/geoip/linux/net/ipv4/netfilter/
   trunk/patch-o-matic-ng/geoip/linux/net/ipv4/netfilter/Config.in.ladd
   trunk/patch-o-matic-ng/geoip/linux/net/ipv4/netfilter/Makefile.ladd
   trunk/patch-o-matic-ng/geoip/linux/net/ipv4/netfilter/ipt_geoip.c
Log:
Add the geoip match (Samuel Jean, Nicolas Bouliane)


Added: trunk/patch-o-matic-ng/geoip/help
===================================================================
--- trunk/patch-o-matic-ng/geoip/help	2005-01-23 01:08:44 UTC (rev 3610)
+++ trunk/patch-o-matic-ng/geoip/help	2005-01-23 15:53:14 UTC (rev 3611)
@@ -0,0 +1,18 @@
+This patch makes possible to match a packet
+by its source or destination country.
+
+GeoIP options:
+        [!]   --src-cc, --source-country country[,country,country,...]
+                                                     Match packet coming from (one of)
+                                                     the specified country(ies)
+
+        [!]   --dst-cc, --destination-country country[,country,country,...]
+                                                     Match packet going to (one of)
+                                                     the specified country(ies)
+
+           NOTE: The country is inputed by its ISO3166 code.
+
+The only extra files you need is a binary db (geoipdb.bin) & its index file (geoipdb.idx).
+Both files are generated from a countries & subnets database with the csv2bin tool,
+available at www.cookinglinux.org/geoip/. Both files MUST also be moved in /var/geoip/
+as the shared library is statically looking for that pathname (ex.: /var/geoip/geoipdb.bin).

Added: trunk/patch-o-matic-ng/geoip/info
===================================================================
--- trunk/patch-o-matic-ng/geoip/info	2005-01-23 01:08:44 UTC (rev 3610)
+++ trunk/patch-o-matic-ng/geoip/info	2005-01-23 15:53:14 UTC (rev 3611)
@@ -0,0 +1,5 @@
+Title:      geoip match
+Author:     Samuel Jean <sjean at cookinglinux.org>; Nicolas Bouliane <nib at cookinglinux.org>
+Status:     Testing
+Repository: extra
+Recompile:  netfilter, iptables

Added: trunk/patch-o-matic-ng/geoip/iptables/extensions/.geoip-test
===================================================================
--- trunk/patch-o-matic-ng/geoip/iptables/extensions/.geoip-test	2005-01-23 01:08:44 UTC (rev 3610)
+++ trunk/patch-o-matic-ng/geoip/iptables/extensions/.geoip-test	2005-01-23 15:53:14 UTC (rev 3611)
@@ -0,0 +1,3 @@
+#!/bin/sh
+# True if geoip is applied in given kernel tree.
+[ -f $KERNEL_DIR/include/linux/netfilter_ipv4/ipt_geoip.h ] && echo geoip


Property changes on: trunk/patch-o-matic-ng/geoip/iptables/extensions/.geoip-test
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/patch-o-matic-ng/geoip/iptables/extensions/libipt_geoip.c
===================================================================
--- trunk/patch-o-matic-ng/geoip/iptables/extensions/libipt_geoip.c	2005-01-23 01:08:44 UTC (rev 3610)
+++ trunk/patch-o-matic-ng/geoip/iptables/extensions/libipt_geoip.c	2005-01-23 15:53:14 UTC (rev 3611)
@@ -0,0 +1,338 @@
+/* Shared library add-on to iptables to add geoip match support.
+ 
+ * 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.
+ *
+ * Copyright (c) 2004 Cookinglinux
+ 
+ * For comments, bugs or suggestions, please contact
+ * Samuel Jean       <sjean at cookinglinux.org>
+ * Nicolas Bouliane  <nib at cookinglinux.org>
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <ctype.h>
+#include <stddef.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ipt_geoip.h>
+
+static void help(void)
+{
+   printf (
+            "GeoIP v%s options:\n"
+            "        [!]   --src-cc, --source-country country[,country,country,...]\n"
+            "                                                     Match packet coming from (one of)\n"
+            "                                                     the specified country(ies)\n"
+            "\n"
+            "        [!]   --dst-cc, --destination-country country[,country,country,...]\n"
+            "                                                     Match packet going to (one of)\n"
+            "                                                     the specified country(ies)\n"
+            "\n"
+            "           NOTE: The country is inputed by its ISO3166 code.\n"
+            "\n"
+            "\n", IPTABLES_VERSION
+         );
+}
+
+static struct option opts[] = {
+   {  "dst-cc",  1, 0, '2'  }, /* Alias for --destination-country */
+   {  "destination-country",   1, 0, '2'  },
+   {  "src-cc",  1, 0, '1'  }, /* Alias for --source-country */
+   {  "source-country",  1, 0, '1'  },
+   {  0  }
+};
+
+static void 
+init(struct ipt_entry_match *m, unsigned int *nfcache)
+{
+}
+
+/* NOT IMPLEMENTED YET
+static void geoip_free(struct geoip_info *oldmem)
+{
+}
+*/
+
+struct geoip_index {
+   u_int16_t cc;
+   u_int32_t offset;
+} __attribute__ ((packed));
+
+struct geoip_subnet *
+get_country_subnets(u_int16_t cc, u_int32_t *count)
+{
+   FILE *ixfd, *dbfd;
+   struct geoip_subnet *subnets;
+   struct geoip_index *index;
+   struct stat buf;
+  
+   size_t idxsz;
+   u_int16_t i;
+   
+   u_int16_t db_cc = 0;
+   u_int16_t db_nsubnets = 0;
+
+   if ((ixfd = fopen("/var/geoip/geoipdb.idx", "r")) == NULL) {
+         perror("/var/geoip/geoipdb.idx");
+         exit_error(OTHER_PROBLEM,
+               "geoip match: cannot open geoip's database index file");               
+   }
+   
+   stat("/var/geoip/geoipdb.idx", &buf);
+   idxsz = buf.st_size/sizeof(struct geoip_index);
+   index = (struct geoip_index *)malloc(buf.st_size);
+
+   fread(index, buf.st_size, 1, ixfd);
+
+   for (i = 0; i < idxsz; i++)
+      if (cc == index[i].cc)
+         break;
+   
+   if (cc != index[i].cc)
+      exit_error(OTHER_PROBLEM,
+            "geoip match: sorry, '%c%c' isn't in the database\n", COUNTRY(cc));
+
+   fclose(ixfd);
+
+   if ((dbfd = fopen("/var/geoip/geoipdb.bin", "r")) == NULL) {
+      perror("/var/geoip/geoipdb.bin");
+      exit_error(OTHER_PROBLEM,
+            "geoip match: cannot open geoip's database file");
+   }
+
+   fseek(dbfd, index[i].offset, SEEK_SET);
+   fread(&db_cc, sizeof(u_int16_t), 1, dbfd);
+
+   if (db_cc != cc)
+      exit_error(OTHER_PROBLEM,
+            "geoip match: this shouldn't happened, the database might be corrupted, or there's a bug.\n"
+            "you should contact maintainers");
+            
+   fread(&db_nsubnets, sizeof(u_int16_t), 1, dbfd);
+
+   subnets = (struct geoip_subnet*)malloc(db_nsubnets * sizeof(struct geoip_subnet));
+
+   if (!subnets)
+      exit_error(OTHER_PROBLEM,
+            "geoip match: insufficient memory available");
+   
+   fread(subnets, db_nsubnets * sizeof(struct geoip_subnet), 1, dbfd);
+   
+   fclose(dbfd);
+   free(index);
+   *count = db_nsubnets;
+   return subnets;
+}
+ 
+static struct geoip_info *
+load_geoip_cc(u_int16_t cc)
+{
+   static struct geoip_info *ginfo;
+   ginfo = malloc(sizeof(struct geoip_info));
+
+   if (!ginfo)
+      return NULL;
+   
+   ginfo->subnets = get_country_subnets(cc, &ginfo->count);
+   ginfo->cc = cc;
+   
+   return ginfo;
+}
+
+static u_int16_t
+check_geoip_cc(char *cc, u_int16_t cc_used[], u_int8_t count)
+{
+   u_int8_t i;
+   u_int16_t cc_int16;
+
+   if (strlen(cc) != 2) /* Country must be 2 chars long according
+                                        to the ISO3166 standard */
+    exit_error(PARAMETER_PROBLEM,
+         "geoip match: invalid country code '%s'", cc);
+
+   // Verification will fail if chars aren't uppercased.
+   // Make sure they are..
+   for (i = 0; i < 2; i++)
+      if (isalnum(cc[i]) != 0)
+         cc[i] = toupper(cc[i]);
+      else
+         exit_error(PARAMETER_PROBLEM,
+               "geoip match:  invalid country code '%s'", cc);
+
+   /* Convert chars into a single 16 bit integer.
+    * FIXME:   This assumes that a country code is
+    *          exactly 2 chars long. If this is
+    *          going to change someday, this whole
+    *          match will need to be rewritten, anyway.
+    *                                  - SJ  */
+   cc_int16 = (cc[0]<<8) + cc[1];
+
+   // Check for presence of value in cc_used
+   for (i = 0; i < count; i++)
+      if (cc_int16 == cc_used[i])
+         return 0; // Present, skip it!
+   
+   return cc_int16;
+}
+
+/* Based on libipt_multiport.c parsing code. */ 
+static u_int8_t
+parse_geoip_cc(const char *ccstr, u_int16_t *cc, struct geoip_info **mem)
+{
+   char *buffer, *cp, *next;
+   u_int8_t i, count = 0;
+   u_int16_t cctmp;
+
+   buffer = strdup(ccstr);
+   if (!buffer) exit_error(OTHER_PROBLEM,
+         "geoip match: insufficient memory available");
+
+   for (cp = buffer, i = 0; cp && i < IPT_GEOIP_MAX; cp = next, i++)
+   {
+      next = strchr(cp, ',');
+      if (next) *next++ = '\0';
+      
+      if ((cctmp = check_geoip_cc(cp, cc, count)) != 0) {
+         if ((mem[count++] = load_geoip_cc(cctmp)) == NULL)
+            exit_error(OTHER_PROBLEM,
+                  "geoip match: insufficient memory available");
+         cc[count-1] = cctmp;
+         }
+   }
+   
+   if (cp) exit_error(PARAMETER_PROBLEM,
+         "geoip match: too many countries specified");
+   free(buffer);
+
+   if (count == 0) exit_error(PARAMETER_PROBLEM,
+         "geoip match: don't know what happened");
+   
+   return count;
+}
+
+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 ipt_geoip_info *info
+      = (struct ipt_geoip_info *)(*match)->data;
+  
+    switch(c) {
+      case '1':
+         // Ensure that IPT_GEOIP_SRC *OR* IPT_GEOIP_DST haven't been used yet.
+         if (*flags & (IPT_GEOIP_SRC | IPT_GEOIP_DST))
+            exit_error(PARAMETER_PROBLEM,
+                  "geoip match: only use --source-country *OR* --destination-country once!");
+ 
+         *flags |= IPT_GEOIP_SRC;
+         *nfcache |= NFC_IP_SRC;
+         break;
+         
+      case '2':
+         // Ensure that IPT_GEOIP_SRC *OR* IPT_GEOIP_DST haven't been used yet.
+         if (*flags & (IPT_GEOIP_SRC | IPT_GEOIP_DST))
+            exit_error(PARAMETER_PROBLEM,
+                  "geoip match: only use --source-country *OR* --destination-country once!");
+ 
+         *flags |= IPT_GEOIP_DST;
+         *nfcache |= NFC_IP_DST;
+         break;
+      
+      default:
+         return 0;
+    }
+    
+    if (invert)
+       *flags |= IPT_GEOIP_INV;
+   
+    info->count = parse_geoip_cc(argv[optind-1], info->cc, info->mem);
+    info->flags = *flags;
+    info->refcount = NULL;
+    //info->fini = &geoip_free;
+
+    return 1;
+}
+
+static void 
+final_check(unsigned int flags)
+{
+   if (!flags)
+      exit_error(PARAMETER_PROBLEM,
+            "geoip match: missing arguments");
+}
+
+static void 
+print(const struct ipt_ip *ip,
+                  const struct ipt_entry_match *match,
+                  int numeric)
+{
+   const struct ipt_geoip_info *info
+      = (const struct ipt_geoip_info *)match->data;
+   
+   u_int8_t i;
+   
+   if (info->flags & IPT_GEOIP_SRC)
+      printf("Source ");
+   else printf("Destination ");
+   
+   if (info->count > 1)
+      printf("countries: ");
+   else printf("country: ");
+   
+   if (info->flags & IPT_GEOIP_INV)
+      printf("! ");
+      
+   for (i = 0; i < info->count; i++)
+       printf("%s%c%c", i ? "," : "", COUNTRY(info->cc[i]));
+   printf(" ");
+}
+
+static void 
+save(const struct ipt_ip *ip,
+                 const struct ipt_entry_match *match)
+{
+   const struct ipt_geoip_info *info
+      = (const struct ipt_geoip_info *)match->data;
+   u_int8_t i;
+
+   if (info->flags & IPT_GEOIP_INV)
+      printf("! ");
+ 
+   if (info->flags & IPT_GEOIP_SRC)
+      printf("--source-country ");
+   else printf("--destination-country ");
+      
+   for (i = 0; i < info->count; i++)
+      printf("%s%c%c", i ? "," : "", COUNTRY(info->cc[i]));
+   printf(" ");
+}
+
+static struct iptables_match geoip = {
+    .name            = "geoip",
+    .version         = IPTABLES_VERSION,
+    .size            = IPT_ALIGN(sizeof(struct ipt_geoip_info)),
+    .userspacesize   = offsetof(struct ipt_geoip_info, mem),
+    .help            = &help,
+    .init            = &init,
+    .parse           = &parse,
+    .final_check     = &final_check,
+    .print           = &print,
+    .save            = &save,
+    .extra_opts      = opts
+};
+
+void _init(void)
+{
+   register_match(&geoip);
+}

Added: trunk/patch-o-matic-ng/geoip/linux/Documentation/Configure.help.ladd
===================================================================
--- trunk/patch-o-matic-ng/geoip/linux/Documentation/Configure.help.ladd	2005-01-23 01:08:44 UTC (rev 3610)
+++ trunk/patch-o-matic-ng/geoip/linux/Documentation/Configure.help.ladd	2005-01-23 15:53:14 UTC (rev 3611)
@@ -0,0 +1,14 @@
+CONFIG_IP_NF_MATCH_TOS
+geoip match support
+CONFIG_IP_NF_MATCH_GEOIP
+  This option allows you to match a packet by its source or
+  destination country.  Basically, you need a country's
+  database containing all subnets and associated countries.
+
+  For the complete procedure and understanding, read :
+  http://www.cookinglinux.org/geoip/howto/geoip-HOWTO.html
+  
+  
+  If you want to compile it as a module, say M here and read
+  <file:Documentation/modules.txt>.  If unsure, say `N'.
+

Added: trunk/patch-o-matic-ng/geoip/linux/include/linux/netfilter_ipv4/ipt_geoip.h
===================================================================
--- trunk/patch-o-matic-ng/geoip/linux/include/linux/netfilter_ipv4/ipt_geoip.h	2005-01-23 01:08:44 UTC (rev 3610)
+++ trunk/patch-o-matic-ng/geoip/linux/include/linux/netfilter_ipv4/ipt_geoip.h	2005-01-23 15:53:14 UTC (rev 3611)
@@ -0,0 +1,51 @@
+/* ipt_geoip.h header file for libipt_geoip.c and ipt_geoip.c
+ * 
+ * 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.
+ *
+ * Copyright (c) 2004 Cookinglinux
+ */
+#ifndef _IPT_GEOIP_H
+#define _IPT_GEOIP_H
+
+#define IPT_GEOIP_SRC         0x01     /* Perform check on Source IP */
+#define IPT_GEOIP_DST         0x02     /* Perform check on Destination IP */
+#define IPT_GEOIP_INV         0x04     /* Negate the condition */
+
+#define IPT_GEOIP_MAX         15       /* Maximum of countries */
+
+struct geoip_subnet {
+   u_int32_t begin;
+   u_int32_t end;
+};
+
+struct geoip_info {
+   struct geoip_subnet *subnets;
+   u_int32_t count;
+   u_int32_t ref;
+   u_int16_t cc;
+   struct geoip_info *next;
+   struct geoip_info *prev;
+};
+
+struct ipt_geoip_info {
+   u_int8_t flags;
+   u_int8_t count;
+   u_int16_t cc[IPT_GEOIP_MAX];
+
+   /* Used internally by the kernel */
+   struct geoip_info *mem[IPT_GEOIP_MAX];
+   u_int8_t *refcount;
+
+   /* not implemented yet:
+   void *fini;
+   */
+};
+
+#define COUNTRY(cc) (cc >> 8), (cc & 0x00FF)
+
+#endif
+
+/* End of ipt_geoip.h */

Added: trunk/patch-o-matic-ng/geoip/linux/net/ipv4/netfilter/Config.in.ladd
===================================================================
--- trunk/patch-o-matic-ng/geoip/linux/net/ipv4/netfilter/Config.in.ladd	2005-01-23 01:08:44 UTC (rev 3610)
+++ trunk/patch-o-matic-ng/geoip/linux/net/ipv4/netfilter/Config.in.ladd	2005-01-23 15:53:14 UTC (rev 3611)
@@ -0,0 +1,2 @@
+  dep_tristate '  limit match support' CONFIG_IP_NF_MATCH_LIMIT $CONFIG_IP_NF_IPTABLES
+  dep_tristate '  geoip match support' CONFIG_IP_NF_MATCH_GEOIP $CONFIG_IP_NF_IPTABLES

Added: trunk/patch-o-matic-ng/geoip/linux/net/ipv4/netfilter/Makefile.ladd
===================================================================
--- trunk/patch-o-matic-ng/geoip/linux/net/ipv4/netfilter/Makefile.ladd	2005-01-23 01:08:44 UTC (rev 3610)
+++ trunk/patch-o-matic-ng/geoip/linux/net/ipv4/netfilter/Makefile.ladd	2005-01-23 15:53:14 UTC (rev 3611)
@@ -0,0 +1,2 @@
+obj-$(CONFIG_IP_NF_MATCH_LIMIT) += ipt_limit.o
+obj-$(CONFIG_IP_NF_MATCH_GEOIP) += ipt_geoip.o

Added: trunk/patch-o-matic-ng/geoip/linux/net/ipv4/netfilter/ipt_geoip.c
===================================================================
--- trunk/patch-o-matic-ng/geoip/linux/net/ipv4/netfilter/ipt_geoip.c	2005-01-23 01:08:44 UTC (rev 3610)
+++ trunk/patch-o-matic-ng/geoip/linux/net/ipv4/netfilter/ipt_geoip.c	2005-01-23 15:53:14 UTC (rev 3611)
@@ -0,0 +1,272 @@
+/* netfilter's kernel module for the geoip match
+ * 
+ * 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.
+ *
+ * Copyright (c) 2004 Cookinglinux
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <asm/uaccess.h>
+#include <asm/atomic.h>
+
+#include <linux/netfilter_ipv4/ipt_geoip.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Samuel Jean, Nicolas Bouliane");
+MODULE_DESCRIPTION("iptables/netfilter's geoip match");
+
+struct geoip_info *head = NULL;
+static spinlock_t geoip_lock = SPIN_LOCK_UNLOCKED;
+
+static struct geoip_info *add_node(struct geoip_info *memcpy)
+{
+   struct geoip_info *p =
+      (struct geoip_info *)kmalloc(sizeof(struct geoip_info), GFP_KERNEL);
+
+   struct geoip_subnet *s;
+
+   if ((p == NULL) || (copy_from_user(p, memcpy, sizeof(struct geoip_info)) != 0))
+      return NULL;
+
+   s = (struct geoip_subnet *)kmalloc(p->count * sizeof(struct geoip_subnet), GFP_KERNEL);
+   if ((s == NULL) || (copy_from_user(s, p->subnets, p->count * sizeof(struct geoip_subnet)) != 0))
+      return NULL;
+  
+   spin_lock_bh(&geoip_lock);
+
+   p->subnets = s;
+   p->ref = 1;
+   p->next = head;
+   p->prev = NULL;
+   if (p->next) p->next->prev = p;
+   head = p;
+
+   spin_unlock_bh(&geoip_lock);
+   return p;
+}
+
+static void remove_node(struct geoip_info *p)
+ {
+   spin_lock_bh(&geoip_lock);
+   
+   if (p->next) { /* Am I following a node ? */
+      p->next->prev = p->prev;
+      if (p->prev) p->prev->next = p->next; /* Is there a node behind me ? */
+      else head = p->next; /* No? Then I was the head */
+   }
+   
+   else 
+      if (p->prev) /* Is there a node behind me ? */
+         p->prev->next = NULL;
+      else
+         head = NULL; /* No, we're alone */
+
+   /* So now am unlinked or the only one alive, right ?
+    * What are you waiting ? Free up some memory!
+    */
+
+   kfree(p->subnets);
+   kfree(p);
+   
+   spin_unlock_bh(&geoip_lock);   
+   return;
+}
+
+static struct geoip_info *find_node(u_int16_t cc)
+{
+   struct geoip_info *p = head;
+   spin_lock_bh(&geoip_lock);
+   
+   while (p) {
+      if (p->cc == cc) {
+         spin_unlock_bh(&geoip_lock);         
+         return p;
+      }
+      p = p->next;
+   }
+   spin_unlock_bh(&geoip_lock);
+   return NULL;
+}
+
+static int match(const struct sk_buff *skb,
+                 const struct net_device *in,
+                 const struct net_device *out,
+                 const void *matchinfo,
+                 int offset,
+                 const void *hdr,
+                 u_int16_t datalen,
+                 int *hotdrop)
+{
+   const struct ipt_geoip_info *info = matchinfo;
+   const struct geoip_info *node; /* This keeps the code sexy */
+   const struct iphdr *iph = skb->nh.iph;
+   u_int32_t ip, j;
+   u_int8_t i;
+
+   if (info->flags & IPT_GEOIP_SRC)
+      ip = ntohl(iph->saddr);
+   else
+      ip = ntohl(iph->daddr);
+
+   spin_lock_bh(&geoip_lock);
+   for (i = 0; i < info->count; i++) {
+      if ((node = info->mem[i]) == NULL) {
+         printk(KERN_ERR "ipt_geoip: what the hell ?? '%c%c' isn't loaded into memory... skip it!\n",
+               COUNTRY(info->cc[i]));
+         
+         continue;
+      }
+
+      for (j = 0; j < node->count; j++)
+         if ((ip > node->subnets[j].begin) && (ip < node->subnets[j].end)) {
+            spin_unlock_bh(&geoip_lock);
+            return (info->flags & IPT_GEOIP_INV) ? 0 : 1;
+         }
+   }
+   
+   spin_unlock_bh(&geoip_lock);
+   return (info->flags & IPT_GEOIP_INV) ? 1 : 0;
+}
+
+static int geoip_checkentry(const char *tablename,
+                             const struct ipt_ip *ip,
+                             void *matchinfo,
+                             unsigned int matchsize,
+                             unsigned int hook_mask)
+{
+   struct ipt_geoip_info *info = matchinfo;
+   struct geoip_info *node;
+   u_int8_t i;
+   
+   /* FIXME:   Call a function to free userspace allocated memory.
+    *          As Martin J. said; this match might eat lot of memory
+    *          if commited with iptables-restore --noflush
+   void (*gfree)(struct geoip_info *oldmem);
+   gfree = info->fini;
+   */
+
+   if (matchsize != IPT_ALIGN(sizeof(struct ipt_geoip_info))) {
+      printk(KERN_ERR "ipt_geoip: matchsize differ, you may have forgotten to recompile me\n");
+      return 0;
+   }
+
+   /* If info->refcount isn't NULL, then
+    * it means that checkentry() already
+    * initialized this entry. Increase a
+    * refcount to prevent destroy() of
+    * this entry. */
+   if (info->refcount != NULL) {
+      atomic_inc((atomic_t *)info->refcount);
+      return 1;
+   }
+   
+  
+   for (i = 0; i < info->count; i++) {
+     
+      if ((node = find_node(info->cc[i])) != NULL)
+            atomic_inc((atomic_t *)&node->ref);   //increase the reference
+      else
+         if ((node = add_node(info->mem[i])) == NULL) {
+            printk(KERN_ERR
+                  "ipt_geoip: unable to load '%c%c' into memory\n",
+                  COUNTRY(info->cc[i]));
+            return 0;
+         }
+
+      /* Free userspace allocated memory for that country.
+       * FIXME:   It's a bit odd to call this function everytime
+       *          we process a country.  Would be nice to call
+       *          it once after all countries've been processed.
+       *          - SJ
+       * *not implemented for now*
+      gfree(info->mem[i]);
+      */
+
+      /* Overwrite the now-useless pointer info->mem[i] with
+       * a pointer to the node's kernelspace structure.
+       * This avoids searching for a node in the match() and
+       * destroy() functions.
+       */
+      info->mem[i] = node;
+   }
+
+   /* We allocate some memory and give info->refcount a pointer
+    * to this memory.  This prevents checkentry() from increasing a refcount
+    * different from the one used by destroy().
+    * For explanation, see http://www.mail-archive.com/netfilter-devel@lists.samba.org/msg00625.html
+    */
+   info->refcount = kmalloc(sizeof(u_int8_t), GFP_KERNEL);
+   if (info->refcount == NULL) {
+      printk(KERN_ERR "ipt_geoip: failed to allocate `refcount' memory\n");
+      return 0;
+   }
+   *(info->refcount) = 1;
+   
+   return 1;
+}
+
+static void geoip_destroy(void *matchinfo, unsigned int matchsize)
+{
+   struct ipt_geoip_info *info = matchinfo;
+   struct geoip_info *node; /* this keeps the code sexy */
+   u_int8_t i;
+   
+   /* Decrease the previously increased refcount in checkentry()
+    * If it's equal to 1, we know this entry is just moving
+    * but not removed. We simply return to avoid useless destroy()
+    * processing.
+    */
+   atomic_dec((atomic_t *)info->refcount);
+   if (*info->refcount)
+      return;
+
+   /* Don't leak my memory, you idiot.
+    * Bug found with nfsim.. the netfilter's best
+    * friend. --peejix */
+   kfree(info->refcount);
+   
+   /* This entry has been removed from the table so
+    * decrease the refcount of all countries it is
+    * using.
+    */
+  
+   for (i = 0; i < info->count; i++)
+      if ((node = info->mem[i]) != NULL) {
+         atomic_dec((atomic_t *)&node->ref);
+
+         /* Free up some memory if that node isn't used
+          * anymore. */
+         if (node->ref < 1)
+            remove_node(node);
+      }
+      else
+         /* Something strange happened. There's no memory allocated for this
+          * country.  Please send this bug to the mailing list. */
+         printk(KERN_ERR
+               "ipt_geoip: What happened peejix ? What happened acidmen ?\n"
+               "ipt_geoip: please report this bug to the maintainers\n");
+   return;
+}
+
+static struct ipt_match geoip_match
+= { { NULL, NULL }, "geoip", &match, &geoip_checkentry, &geoip_destroy, THIS_MODULE };
+
+static int __init init(void)
+{
+   return ipt_register_match(&geoip_match);
+}
+
+static void __exit fini(void)
+{
+  ipt_unregister_match(&geoip_match);
+  return;
+}
+
+module_init(init);
+module_exit(fini);

Added: trunk/patch-o-matic-ng/geoip/linux-2.6/include/linux/netfilter_ipv4/ipt_geoip.h
===================================================================
--- trunk/patch-o-matic-ng/geoip/linux-2.6/include/linux/netfilter_ipv4/ipt_geoip.h	2005-01-23 01:08:44 UTC (rev 3610)
+++ trunk/patch-o-matic-ng/geoip/linux-2.6/include/linux/netfilter_ipv4/ipt_geoip.h	2005-01-23 15:53:14 UTC (rev 3611)
@@ -0,0 +1,50 @@
+/* ipt_geoip.h header file for libipt_geoip.c and ipt_geoip.c
+ * 
+ * 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.
+ *
+ * Copyright (c) 2004 Cookinglinux
+ */
+#ifndef _IPT_GEOIP_H
+#define _IPT_GEOIP_H
+
+#define IPT_GEOIP_SRC         0x01     /* Perform check on Source IP */
+#define IPT_GEOIP_DST         0x02     /* Perform check on Destination IP */
+#define IPT_GEOIP_INV         0x04     /* Negate the condition */
+
+#define IPT_GEOIP_MAX         15       /* Maximum of countries */
+
+struct geoip_subnet {
+   u_int32_t begin;
+   u_int32_t end;
+};
+
+struct geoip_info {
+   struct geoip_subnet *subnets;
+   u_int32_t count;
+   u_int32_t ref;
+   u_int16_t cc;
+   struct geoip_info *next;
+   struct geoip_info *prev;
+};
+
+struct ipt_geoip_info {
+   u_int8_t flags;
+   u_int8_t count;
+   u_int16_t cc[IPT_GEOIP_MAX];
+
+   /* Used internally by the kernel */
+   struct geoip_info *mem[IPT_GEOIP_MAX];
+   u_int8_t *refcount;
+
+   /* not implemented yet:
+   void *fini;
+   */
+};
+
+#define COUNTRY(cc) (cc >> 8), (cc & 0x00FF)
+
+#endif
+

Added: trunk/patch-o-matic-ng/geoip/linux-2.6/net/ipv4/netfilter/Kconfig.ladd
===================================================================
--- trunk/patch-o-matic-ng/geoip/linux-2.6/net/ipv4/netfilter/Kconfig.ladd	2005-01-23 01:08:44 UTC (rev 3610)
+++ trunk/patch-o-matic-ng/geoip/linux-2.6/net/ipv4/netfilter/Kconfig.ladd	2005-01-23 15:53:14 UTC (rev 3611)
@@ -0,0 +1,15 @@
+config IP_NF_MATCH_GEOIP
+   tristate  'geoip match support'
+   depends on IP_NF_IPTABLES
+   help
+          This option allows you to match a packet by its source or
+          destination country.  Basically, you need a country's
+          database containing all subnets and associated countries.
+
+          For the complete procedure and understanding, read :
+          http://www.cookinglinux.org/geoip/howto/geoip-HOWTO.html
+
+
+          If you want to compile it as a module, say M here and read
+          <file:Documentation/modules.txt>.  If unsure, say `N'.
+

Added: trunk/patch-o-matic-ng/geoip/linux-2.6/net/ipv4/netfilter/Makefile.ladd
===================================================================
--- trunk/patch-o-matic-ng/geoip/linux-2.6/net/ipv4/netfilter/Makefile.ladd	2005-01-23 01:08:44 UTC (rev 3610)
+++ trunk/patch-o-matic-ng/geoip/linux-2.6/net/ipv4/netfilter/Makefile.ladd	2005-01-23 15:53:14 UTC (rev 3611)
@@ -0,0 +1,3 @@
+obj-$(CONFIG_IP_NF_MATCH_LIMIT) += ipt_limit.o
+obj-$(CONFIG_IP_NF_MATCH_GEOIP) += ipt_geoip.o
+

Added: trunk/patch-o-matic-ng/geoip/linux-2.6/net/ipv4/netfilter/ipt_geoip.c
===================================================================
--- trunk/patch-o-matic-ng/geoip/linux-2.6/net/ipv4/netfilter/ipt_geoip.c	2005-01-23 01:08:44 UTC (rev 3610)
+++ trunk/patch-o-matic-ng/geoip/linux-2.6/net/ipv4/netfilter/ipt_geoip.c	2005-01-23 15:53:14 UTC (rev 3611)
@@ -0,0 +1,275 @@
+/* netfilter's kernel module for the geoip match
+ * 
+ * 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.
+ *
+ * Copyright (c) 2004 Cookinglinux
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <asm/uaccess.h>
+#include <asm/atomic.h>
+
+#include <linux/netfilter_ipv4/ipt_geoip.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Samuel Jean, Nicolas Bouliane");
+MODULE_DESCRIPTION("iptables/netfilter's geoip match");
+
+struct geoip_info *head = NULL;
+static spinlock_t geoip_lock = SPIN_LOCK_UNLOCKED;
+
+static struct geoip_info *add_node(struct geoip_info *memcpy)
+{
+   struct geoip_info *p =
+      (struct geoip_info *)kmalloc(sizeof(struct geoip_info), GFP_KERNEL);
+
+   struct geoip_subnet *s;
+   
+   if ((p == NULL) || (copy_from_user(p, memcpy, sizeof(struct geoip_info)) != 0))
+      return NULL;
+
+   s = (struct geoip_subnet *)kmalloc(p->count * sizeof(struct geoip_subnet), GFP_KERNEL);
+   if ((s == NULL) || (copy_from_user(s, p->subnets, p->count * sizeof(struct geoip_subnet)) != 0))
+      return NULL;
+  
+   spin_lock_bh(&geoip_lock);
+
+   p->subnets = s;
+   p->ref = 1;
+   p->next = head;
+   p->prev = NULL;
+   if (p->next) p->next->prev = p;
+   head = p;
+
+   spin_unlock_bh(&geoip_lock);
+   return p;
+}
+
+static void remove_node(struct geoip_info *p)
+ {
+   spin_lock_bh(&geoip_lock);
+   
+   if (p->next) { /* Am I following a node ? */
+      p->next->prev = p->prev;
+      if (p->prev) p->prev->next = p->next; /* Is there a node behind me ? */
+      else head = p->next; /* No? Then I was the head */
+   }
+   
+   else 
+      if (p->prev) /* Is there a node behind me ? */
+         p->prev->next = NULL;
+      else
+         head = NULL; /* No, we're alone */
+
+   /* So now am unlinked or the only one alive, right ?
+    * What are you waiting ? Free up some memory!
+    */
+
+   kfree(p->subnets);
+   kfree(p);
+   
+   spin_unlock_bh(&geoip_lock);   
+   return;
+}
+
+static struct geoip_info *find_node(u_int16_t cc)
+{
+   struct geoip_info *p = head;
+   spin_lock_bh(&geoip_lock);
+   
+   while (p) {
+      if (p->cc == cc) {
+         spin_unlock_bh(&geoip_lock);         
+         return p;
+      }
+      p = p->next;
+   }
+   spin_unlock_bh(&geoip_lock);
+   return NULL;
+}
+
+static int match(const struct sk_buff *skb,
+                 const struct net_device *in,
+                 const struct net_device *out,
+                 const void *matchinfo,
+                 int offset,
+                 int *hotdrop)
+{
+   const struct ipt_geoip_info *info = matchinfo;
+   const struct geoip_info *node; /* This keeps the code sexy */
+   const struct iphdr *iph = skb->nh.iph;
+   u_int32_t ip, j;
+   u_int8_t i;
+
+   if (info->flags & IPT_GEOIP_SRC)
+      ip = ntohl(iph->saddr);
+   else
+      ip = ntohl(iph->daddr);
+
+   spin_lock_bh(&geoip_lock);
+   for (i = 0; i < info->count; i++) {
+      if ((node = info->mem[i]) == NULL) {
+         printk(KERN_ERR "ipt_geoip: what the hell ?? '%c%c' isn't loaded into memory... skip it!\n",
+               COUNTRY(info->cc[i]));
+         
+         continue;
+      }
+
+      for (j = 0; j < node->count; j++)
+         if ((ip > node->subnets[j].begin) && (ip < node->subnets[j].end)) {
+            spin_unlock_bh(&geoip_lock);
+            return (info->flags & IPT_GEOIP_INV) ? 0 : 1;
+         }
+   }
+   
+   spin_unlock_bh(&geoip_lock);
+   return (info->flags & IPT_GEOIP_INV) ? 1 : 0;
+}
+
+static int geoip_checkentry(const char *tablename,
+                             const struct ipt_ip *ip,
+                             void *matchinfo,
+                             unsigned int matchsize,
+                             unsigned int hook_mask)
+{
+   struct ipt_geoip_info *info = matchinfo;
+   struct geoip_info *node;
+   u_int8_t i;
+
+   /* FIXME:   Call a function to free userspace allocated memory.
+    *          As Martin J. said; this match might eat lot of memory
+    *          if commited with iptables-restore --noflush
+   void (*gfree)(struct geoip_info *oldmem);
+   gfree = info->fini;
+   */
+
+   if (matchsize != IPT_ALIGN(sizeof(struct ipt_geoip_info))) {
+      printk(KERN_ERR "ipt_geoip: matchsize differ, you may have forgotten to recompile me\n");
+      return 0;
+   }
+
+   /* If info->refcount isn't NULL, then
+    * it means that checkentry() already
+    * initialized this entry. Increase a
+    * refcount to prevent destroy() of
+    * this entry. */
+   if (info->refcount != NULL) {
+      atomic_inc((atomic_t *)info->refcount);
+      return 1;
+   }
+   
+   
+   for (i = 0; i < info->count; i++) {
+     
+      if ((node = find_node(info->cc[i])) != NULL)
+            atomic_inc((atomic_t *)&node->ref);   //increase the reference
+      else
+         if ((node = add_node(info->mem[i])) == NULL) {
+            printk(KERN_ERR
+                  "ipt_geoip: unable to load '%c%c' into memory\n",
+                  COUNTRY(info->cc[i]));
+            return 0;
+         }
+
+      /* Free userspace allocated memory for that country.
+       * FIXME:   It's a bit odd to call this function everytime
+       *          we process a country.  Would be nice to call
+       *          it once after all countries've been processed.
+       *          - SJ
+       * *not implemented for now*
+      gfree(info->mem[i]);
+      */
+
+      /* Overwrite the now-useless pointer info->mem[i] with
+       * a pointer to the node's kernelspace structure.
+       * This avoids searching for a node in the match() and
+       * destroy() functions.
+       */
+      info->mem[i] = node;
+   }
+
+   /* We allocate some memory and give info->refcount a pointer
+    * to this memory.  This prevents checkentry() from increasing a refcount
+    * different from the one used by destroy().
+    * For explanation, see http://www.mail-archive.com/netfilter-devel@lists.samba.org/msg00625.html
+    */
+   info->refcount = kmalloc(sizeof(u_int8_t), GFP_KERNEL);
+   if (info->refcount == NULL) {
+      printk(KERN_ERR "ipt_geoip: failed to allocate `refcount' memory\n");
+      return 0;
+   }
+   *(info->refcount) = 1;
+   
+   return 1;
+}
+
+static void geoip_destroy(void *matchinfo, unsigned int matchsize)
+{
+   u_int8_t i;
+   struct geoip_info *node; /* this keeps the code sexy */
+ 
+   struct ipt_geoip_info *info = matchinfo;
+   /* Decrease the previously increased refcount in checkentry()
+    * If it's equal to 1, we know this entry is just moving
+    * but not removed. We simply return to avoid useless destroy()
+    * processing.
+    */
+   atomic_dec((atomic_t *)info->refcount);
+   if (*info->refcount)
+      return;
+
+   /* Don't leak my memory, you idiot.
+    * Bug found with nfsim.. the netfilter's best
+    * friend. --peejix */
+   kfree(info->refcount);
+ 
+   /* This entry has been removed from the table so
+    * decrease the refcount of all countries it is
+    * using.
+    */
+  
+   for (i = 0; i < info->count; i++)
+      if ((node = info->mem[i]) != NULL) {
+         atomic_dec((atomic_t *)&node->ref);
+
+         /* Free up some memory if that node isn't used
+          * anymore. */
+         if (node->ref < 1)
+            remove_node(node);
+      }
+      else
+         /* Something strange happened. There's no memory allocated for this
+          * country.  Please send this bug to the mailing list. */
+         printk(KERN_ERR
+               "ipt_geoip: What happened peejix ? What happened acidmen ?\n"
+               "ipt_geoip: please report this bug to the maintainers\n");
+   return;
+}
+
+static struct ipt_match geoip_match = {
+   .name    = "geoip",
+   .match      = &match,
+   .checkentry = &geoip_checkentry,
+   .destroy    = &geoip_destroy,
+   .me      = THIS_MODULE
+};
+
+static int __init init(void)
+{
+   return ipt_register_match(&geoip_match);
+}
+
+static void __exit fini(void)
+{
+  ipt_unregister_match(&geoip_match);
+  return;
+}
+
+module_init(init);
+module_exit(fini);




More information about the netfilter-cvslog mailing list