]> git.hungrycats.org Git - linux/commitdiff
[NETFILTER]: Add new ip6tables matches.
authorHarald Welte <laforge@netfilter.org>
Sun, 2 Mar 2003 17:40:56 +0000 (09:40 -0800)
committerDavid S. Miller <davem@nuts.ninka.net>
Sun, 2 Mar 2003 17:40:56 +0000 (09:40 -0800)
17 files changed:
include/linux/netfilter_ipv6/ip6t_ah.h [new file with mode: 0644]
include/linux/netfilter_ipv6/ip6t_esp.h [new file with mode: 0644]
include/linux/netfilter_ipv6/ip6t_frag.h [new file with mode: 0644]
include/linux/netfilter_ipv6/ip6t_hl.h [new file with mode: 0644]
include/linux/netfilter_ipv6/ip6t_ipv6header.h [new file with mode: 0644]
include/linux/netfilter_ipv6/ip6t_opts.h [new file with mode: 0644]
include/linux/netfilter_ipv6/ip6t_rt.h [new file with mode: 0644]
net/ipv6/netfilter/Kconfig
net/ipv6/netfilter/Makefile
net/ipv6/netfilter/ip6t_ah.c [new file with mode: 0644]
net/ipv6/netfilter/ip6t_dst.c [new file with mode: 0644]
net/ipv6/netfilter/ip6t_esp.c [new file with mode: 0644]
net/ipv6/netfilter/ip6t_frag.c [new file with mode: 0644]
net/ipv6/netfilter/ip6t_hbh.c [new file with mode: 0644]
net/ipv6/netfilter/ip6t_hl.c [new file with mode: 0644]
net/ipv6/netfilter/ip6t_ipv6header.c [new file with mode: 0644]
net/ipv6/netfilter/ip6t_rt.c [new file with mode: 0644]

diff --git a/include/linux/netfilter_ipv6/ip6t_ah.h b/include/linux/netfilter_ipv6/ip6t_ah.h
new file mode 100644 (file)
index 0000000..c4f0793
--- /dev/null
@@ -0,0 +1,30 @@
+#ifndef _IP6T_AH_H
+#define _IP6T_AH_H
+
+struct ip6t_ah
+{
+       u_int32_t spis[2];                      /* Security Parameter Index */
+       u_int32_t hdrlen;                       /* Header Length */
+       u_int8_t  hdrres;                       /* Test of the Reserved Filed */
+       u_int8_t  invflags;                     /* Inverse flags */
+};
+
+#define IP6T_AH_SPI 0x01
+#define IP6T_AH_LEN 0x02
+#define IP6T_AH_RES 0x04
+
+/* Values for "invflags" field in struct ip6t_ah. */
+#define IP6T_AH_INV_SPI                0x01    /* Invert the sense of spi. */
+#define IP6T_AH_INV_LEN                0x02    /* Invert the sense of length. */
+#define IP6T_AH_INV_MASK       0x03    /* All possible flags. */
+
+#define MASK_HOPOPTS    128
+#define MASK_DSTOPTS    64
+#define MASK_ROUTING    32
+#define MASK_FRAGMENT   16
+#define MASK_AH         8
+#define MASK_ESP        4
+#define MASK_NONE       2
+#define MASK_PROTO      1
+
+#endif /*_IP6T_AH_H*/
diff --git a/include/linux/netfilter_ipv6/ip6t_esp.h b/include/linux/netfilter_ipv6/ip6t_esp.h
new file mode 100644 (file)
index 0000000..01142b9
--- /dev/null
@@ -0,0 +1,23 @@
+#ifndef _IP6T_ESP_H
+#define _IP6T_ESP_H
+
+struct ip6t_esp
+{
+       u_int32_t spis[2];                      /* Security Parameter Index */
+       u_int8_t  invflags;                     /* Inverse flags */
+};
+
+#define MASK_HOPOPTS    128
+#define MASK_DSTOPTS    64
+#define MASK_ROUTING    32
+#define MASK_FRAGMENT   16
+#define MASK_AH         8
+#define MASK_ESP        4
+#define MASK_NONE       2
+#define MASK_PROTO      1
+
+/* Values for "invflags" field in struct ip6t_esp. */
+#define IP6T_ESP_INV_SPI               0x01    /* Invert the sense of spi. */
+#define IP6T_ESP_INV_MASK      0x01    /* All possible flags. */
+
+#endif /*_IP6T_ESP_H*/
diff --git a/include/linux/netfilter_ipv6/ip6t_frag.h b/include/linux/netfilter_ipv6/ip6t_frag.h
new file mode 100644 (file)
index 0000000..449a57e
--- /dev/null
@@ -0,0 +1,33 @@
+#ifndef _IP6T_FRAG_H
+#define _IP6T_FRAG_H
+
+struct ip6t_frag
+{
+       u_int32_t ids[2];                       /* Security Parameter Index */
+       u_int32_t hdrlen;                       /* Header Length */
+       u_int8_t  flags;                        /*  */
+       u_int8_t  invflags;                     /* Inverse flags */
+};
+
+#define IP6T_FRAG_IDS          0x01
+#define IP6T_FRAG_LEN          0x02
+#define IP6T_FRAG_RES          0x04
+#define IP6T_FRAG_FST          0x08
+#define IP6T_FRAG_MF           0x10
+#define IP6T_FRAG_NMF                  0x20
+
+/* Values for "invflags" field in struct ip6t_frag. */
+#define IP6T_FRAG_INV_IDS      0x01    /* Invert the sense of ids. */
+#define IP6T_FRAG_INV_LEN      0x02    /* Invert the sense of length. */
+#define IP6T_FRAG_INV_MASK     0x03    /* All possible flags. */
+
+#define MASK_HOPOPTS    128
+#define MASK_DSTOPTS    64
+#define MASK_ROUTING    32
+#define MASK_FRAGMENT   16
+#define MASK_AH         8
+#define MASK_ESP        4
+#define MASK_NONE       2
+#define MASK_PROTO      1
+
+#endif /*_IP6T_FRAG_H*/
diff --git a/include/linux/netfilter_ipv6/ip6t_hl.h b/include/linux/netfilter_ipv6/ip6t_hl.h
new file mode 100644 (file)
index 0000000..5ef91b8
--- /dev/null
@@ -0,0 +1,22 @@
+/* ip6tables module for matching the Hop Limit value
+ * Maciej Soltysiak <solt@dns.toxicfilms.tv>
+ * Based on HW's ttl module */
+
+#ifndef _IP6T_HL_H
+#define _IP6T_HL_H
+
+enum {
+       IP6T_HL_EQ = 0,         /* equals */
+       IP6T_HL_NE,             /* not equals */
+       IP6T_HL_LT,             /* less than */
+       IP6T_HL_GT,             /* greater than */
+};
+
+
+struct ip6t_hl_info {
+       u_int8_t        mode;
+       u_int8_t        hop_limit;
+};
+
+
+#endif
diff --git a/include/linux/netfilter_ipv6/ip6t_ipv6header.h b/include/linux/netfilter_ipv6/ip6t_ipv6header.h
new file mode 100644 (file)
index 0000000..51c53fc
--- /dev/null
@@ -0,0 +1,27 @@
+/* ipv6header match - matches IPv6 packets based
+on whether they contain certain headers */
+
+/* Original idea: Brad Chapman 
+ * Rewritten by: Andras Kis-Szabo <kisza@sch.bme.hu> */
+
+
+#ifndef __IPV6HEADER_H
+#define __IPV6HEADER_H
+
+struct ip6t_ipv6header_info
+{
+       u_int8_t matchflags;
+       u_int8_t invflags;
+       u_int8_t modeflag;
+};
+
+#define MASK_HOPOPTS    128
+#define MASK_DSTOPTS    64
+#define MASK_ROUTING    32
+#define MASK_FRAGMENT   16
+#define MASK_AH         8
+#define MASK_ESP        4
+#define MASK_NONE       2
+#define MASK_PROTO      1
+
+#endif /* __IPV6HEADER_H */
diff --git a/include/linux/netfilter_ipv6/ip6t_opts.h b/include/linux/netfilter_ipv6/ip6t_opts.h
new file mode 100644 (file)
index 0000000..e259b62
--- /dev/null
@@ -0,0 +1,32 @@
+#ifndef _IP6T_OPTS_H
+#define _IP6T_OPTS_H
+
+#define IP6T_OPTS_OPTSNR 16
+
+struct ip6t_opts
+{
+       u_int32_t hdrlen;                       /* Header Length */
+       u_int8_t flags;                         /*  */
+       u_int8_t invflags;                      /* Inverse flags */
+       u_int16_t opts[IP6T_OPTS_OPTSNR];       /* opts */
+       u_int8_t optsnr;                        /* Nr of OPts */
+};
+
+#define IP6T_OPTS_LEN          0x01
+#define IP6T_OPTS_OPTS                 0x02
+#define IP6T_OPTS_NSTRICT      0x04
+
+/* Values for "invflags" field in struct ip6t_rt. */
+#define IP6T_OPTS_INV_LEN      0x01    /* Invert the sense of length. */
+#define IP6T_OPTS_INV_MASK     0x01    /* All possible flags. */
+
+#define MASK_HOPOPTS    128
+#define MASK_DSTOPTS    64
+#define MASK_ROUTING    32
+#define MASK_FRAGMENT   16
+#define MASK_AH         8
+#define MASK_ESP        4
+#define MASK_NONE       2
+#define MASK_PROTO      1
+
+#endif /*_IP6T_OPTS_H*/
diff --git a/include/linux/netfilter_ipv6/ip6t_rt.h b/include/linux/netfilter_ipv6/ip6t_rt.h
new file mode 100644 (file)
index 0000000..f1070fb
--- /dev/null
@@ -0,0 +1,42 @@
+#ifndef _IP6T_RT_H
+#define _IP6T_RT_H
+
+/*#include <linux/in6.h>*/
+
+#define IP6T_RT_HOPS 16
+
+struct ip6t_rt
+{
+       u_int32_t rt_type;                      /* Routing Type */
+       u_int32_t segsleft[2];                  /* Segments Left */
+       u_int32_t hdrlen;                       /* Header Length */
+       u_int8_t  flags;                        /*  */
+       u_int8_t  invflags;                     /* Inverse flags */
+       struct in6_addr addrs[IP6T_RT_HOPS];    /* Hops */
+       u_int8_t addrnr;                        /* Nr of Addresses */
+};
+
+#define IP6T_RT_TYP            0x01
+#define IP6T_RT_SGS            0x02
+#define IP6T_RT_LEN            0x04
+#define IP6T_RT_RES            0x08
+#define IP6T_RT_FST_MASK       0x30
+#define IP6T_RT_FST            0x10
+#define IP6T_RT_FST_NSTRICT    0x20
+
+/* Values for "invflags" field in struct ip6t_rt. */
+#define IP6T_RT_INV_TYP                0x01    /* Invert the sense of type. */
+#define IP6T_RT_INV_SGS                0x02    /* Invert the sense of Segments. */
+#define IP6T_RT_INV_LEN                0x04    /* Invert the sense of length. */
+#define IP6T_RT_INV_MASK       0x07    /* All possible flags. */
+
+#define MASK_HOPOPTS    128
+#define MASK_DSTOPTS    64
+#define MASK_ROUTING    32
+#define MASK_FRAGMENT   16
+#define MASK_AH         8
+#define MASK_ESP        4
+#define MASK_NONE       2
+#define MASK_PROTO      1
+
+#endif /*_IP6T_RT_H*/
index 27336bb287335ccd25b35b9b62e8321bffd581fd..6b99d508745703c8529a6d5193aa046b66aa4372 100644 (file)
@@ -60,6 +60,46 @@ config IP6_NF_MATCH_MAC
          If you want to compile it as a module, say M here and read
          <file:Documentation/modules.txt>.  If unsure, say `N'.
 
+config IP6_NF_MATCH_RT
+       tristate "Routing header match support"
+       depends on IP6_NF_IPTABLES && EXPERIMENTAL
+       help
+         rt matching allows you to match packets based on the routing
+         header of the packet.
+
+         If you want to compile it as a module, say M here and read
+         <file:Documentation/modules.txt>.  If unsure, say `N'.
+
+config IP6_NF_MATCH_OPTS
+       tristate "Hop-by-hop and Dst opts header match support"
+       depends on IP6_NF_IPTABLES && EXPERIMENTAL
+       help
+         This allows one to match packets based on the hop-by-hop
+         and destination options headers of a packet.
+
+         If you want to compile it as a module, say M here and read
+         <file:Documentation/modules.txt>.  If unsure, say `N'.
+
+config IP6_NF_MATCH_FRAG
+       tristate "Fragmentation header match support"
+       depends on IP6_NF_IPTABLES && EXPERIMENTAL
+       help
+         frag matching allows you to match packets based on the fragmentation
+         header of the packet.
+
+         If you want to compile it as a module, say M here and read
+         <file:Documentation/modules.txt>.  If unsure, say `N'.
+
+config IP6_NF_MATCH_HL
+       tristate "HL match support"
+       depends on IP6_NF_IPTABLES
+       help
+         HL matching allows you to match packets based on the hop
+         limit of the packet.
+
+         If you want to compile it as a module, say M here and read
+         <file:Documentation/modules.txt>.  If unsure, say `N'.
+
 config IP6_NF_MATCH_MULTIPORT
        tristate "Multiple port match support"
        depends on IP6_NF_IPTABLES
@@ -93,6 +133,25 @@ config IP6_NF_MATCH_MARK
          If you want to compile it as a module, say M here and read
          <file:Documentation/modules.txt>.  If unsure, say `N'.
 
+config IP6_NF_MATCH_IPV6HEADER
+       tristate "IPv6 Extension Headers Match (EXPERIMENTAL)"
+       depends on IP6_NF_IPTABLES && EXPERIMENTAL
+       help
+         This module allows one to match packets based upon
+         the ipv6 extension headers.
+
+         If you want to compile it as a module, say M here and read
+         <file:Documentation/modules.txt>.  If unsure, say `N'.
+
+config IP6_NF_MATCH_AHESP
+       tristate "AH/ESP match support (EXPERIMENTAL)"
+       depends on IP6_NF_IPTABLES && EXPERIMENTAL
+       help
+         This module allows one to match AH and ESP packets.
+
+         If you want to compile it as a module, say M here and read
+         <file:Documentation/modules.txt>.  If unsure, say `N'.
+
 config IP6_NF_MATCH_LENGTH
        tristate "Packet Length match support"
        depends on IP6_NF_IPTABLES
index 56ffe51f76fd01a19d180d58fc859af128826879..5ac6efcdf7d1663d8c3aac4100b0fc735d1e5d7f 100644 (file)
@@ -8,6 +8,11 @@ obj-$(CONFIG_IP6_NF_MATCH_LIMIT) += ip6t_limit.o
 obj-$(CONFIG_IP6_NF_MATCH_MARK) += ip6t_mark.o
 obj-$(CONFIG_IP6_NF_MATCH_LENGTH) += ip6t_length.o
 obj-$(CONFIG_IP6_NF_MATCH_MAC) += ip6t_mac.o
+obj-$(CONFIG_IP6_NF_MATCH_RT) += ip6t_rt.o
+obj-$(CONFIG_IP6_NF_MATCH_OPTS) += ip6t_hbh.o ip6t_dst.o
+obj-$(CONFIG_IP6_NF_MATCH_IPV6HEADER) += ip6t_ipv6header.o
+obj-$(CONFIG_IP6_NF_MATCH_FRAG) += ip6t_frag.o
+obj-$(CONFIG_IP6_NF_MATCH_AHESP) += ip6t_esp.o ip6t_ah.o
 obj-$(CONFIG_IP6_NF_MATCH_EUI64) += ip6t_eui64.o
 obj-$(CONFIG_IP6_NF_MATCH_MULTIPORT) += ip6t_multiport.o
 obj-$(CONFIG_IP6_NF_MATCH_OWNER) += ip6t_owner.o
@@ -16,3 +21,4 @@ obj-$(CONFIG_IP6_NF_MANGLE) += ip6table_mangle.o
 obj-$(CONFIG_IP6_NF_TARGET_MARK) += ip6t_MARK.o
 obj-$(CONFIG_IP6_NF_QUEUE) += ip6_queue.o
 obj-$(CONFIG_IP6_NF_TARGET_LOG) += ip6t_LOG.o
+obj-$(CONFIG_IP_NF_MATCH_HL) += ip6t_hl.o
diff --git a/net/ipv6/netfilter/ip6t_ah.c b/net/ipv6/netfilter/ip6t_ah.c
new file mode 100644 (file)
index 0000000..0210faa
--- /dev/null
@@ -0,0 +1,218 @@
+/* Kernel module to match AH parameters. */
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/ipv6.h>
+#include <linux/types.h>
+#include <net/checksum.h>
+#include <net/ipv6.h>
+
+#include <linux/netfilter_ipv6/ip6_tables.h>
+#include <linux/netfilter_ipv6/ip6t_ah.h>
+
+EXPORT_NO_SYMBOLS;
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("IPv6 AH match");
+MODULE_AUTHOR("Andras Kis-Szabo <kisza@sch.bme.hu>");
+
+#if 0
+#define DEBUGP printk
+#else
+#define DEBUGP(format, args...)
+#endif
+
+struct ahhdr {
+       __u8    nexthdr;
+       __u8    hdrlen;
+       __u16   reserved;
+       __u32   spi;
+};
+
+int ipv6_ext_hdr(u8 nexthdr)
+{
+        return ( (nexthdr == NEXTHDR_HOP)       ||
+                 (nexthdr == NEXTHDR_ROUTING)   ||
+                 (nexthdr == NEXTHDR_FRAGMENT)  ||
+                 (nexthdr == NEXTHDR_AUTH)      ||
+                 (nexthdr == NEXTHDR_ESP)       ||
+                 (nexthdr == NEXTHDR_NONE)      ||
+                 (nexthdr == NEXTHDR_DEST) );
+}
+
+/* Returns 1 if the spi is matched by the range, 0 otherwise */
+static inline int
+spi_match(u_int32_t min, u_int32_t max, u_int32_t spi, int invert)
+{
+       int r=0;
+       DEBUGP("ah spi_match:%c 0x%x <= 0x%x <= 0x%x",invert? '!':' ',
+              min,spi,max);
+       r=(spi >= min && spi <= max) ^ invert;
+       DEBUGP(" result %s\n",r? "PASS\n" : "FAILED\n");
+       return r;
+}
+
+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 *protohdr,
+      u_int16_t datalen,
+      int *hotdrop)
+{
+       struct ahhdr *ah = NULL;
+       const struct ip6t_ah *ahinfo = matchinfo;
+       unsigned int temp;
+       int len;
+       u8 nexthdr;
+       unsigned int ptr;
+       unsigned int hdrlen = 0;
+
+       /*DEBUGP("IPv6 AH entered\n");*/
+       /* if (opt->auth == 0) return 0;
+       * It does not filled on output */
+
+       /* type of the 1st exthdr */
+       nexthdr = skb->nh.ipv6h->nexthdr;
+       /* pointer to the 1st exthdr */
+       ptr = sizeof(struct ipv6hdr);
+       /* available length */
+       len = skb->len - ptr;
+       temp = 0;
+
+        while (ipv6_ext_hdr(nexthdr)) {
+               struct ipv6_opt_hdr *hdr;
+
+              DEBUGP("ipv6_ah header iteration \n");
+
+              /* Is there enough space for the next ext header? */
+                if (len < (int)sizeof(struct ipv6_opt_hdr))
+                        return 0;
+              /* No more exthdr -> evaluate */
+                if (nexthdr == NEXTHDR_NONE) {
+                     break;
+              }
+              /* ESP -> evaluate */
+                if (nexthdr == NEXTHDR_ESP) {
+                     break;
+              }
+
+              hdr=(struct ipv6_opt_hdr *)skb->data+ptr;
+
+              /* Calculate the header length */
+                if (nexthdr == NEXTHDR_FRAGMENT) {
+                        hdrlen = 8;
+                } else if (nexthdr == NEXTHDR_AUTH)
+                        hdrlen = (hdr->hdrlen+2)<<2;
+                else
+                        hdrlen = ipv6_optlen(hdr);
+
+              /* AH -> evaluate */
+                if (nexthdr == NEXTHDR_AUTH) {
+                     temp |= MASK_AH;
+                     break;
+              }
+
+
+              /* set the flag */
+              switch (nexthdr){
+                     case NEXTHDR_HOP:
+                     case NEXTHDR_ROUTING:
+                     case NEXTHDR_FRAGMENT:
+                     case NEXTHDR_AUTH:
+                     case NEXTHDR_DEST:
+                            break;
+                     default:
+                            DEBUGP("ipv6_ah match: unknown nextheader %u\n",nexthdr);
+                            return 0;
+                            break;
+              }
+
+                nexthdr = hdr->nexthdr;
+                len -= hdrlen;
+                ptr += hdrlen;
+               if ( ptr > skb->len ) {
+                       DEBUGP("ipv6_ah: new pointer too large! \n");
+                       break;
+               }
+        }
+
+       /* AH header not found */
+       if ( temp != MASK_AH ) return 0;
+
+       if (len < (int)sizeof(struct ahhdr)){
+              *hotdrop = 1;
+                       return 0;
+       }
+
+       ah=skb->data+ptr;
+
+       DEBUGP("IPv6 AH LEN %u %u ", hdrlen, ah->hdrlen);
+       DEBUGP("RES %04X ", ah->reserved);
+       DEBUGP("SPI %u %08X\n", ntohl(ah->spi), ntohl(ah->spi));
+
+       DEBUGP("IPv6 AH spi %02X ",
+                       (spi_match(ahinfo->spis[0], ahinfo->spis[1],
+                           ntohl(ah->spi),
+                           !!(ahinfo->invflags & IP6T_AH_INV_SPI))));
+       DEBUGP("len %02X %04X %02X ",
+                       ahinfo->hdrlen, hdrlen,
+                       (!ahinfo->hdrlen ||
+                           (ahinfo->hdrlen == hdrlen) ^
+                           !!(ahinfo->invflags & IP6T_AH_INV_LEN)));
+       DEBUGP("res %02X %04X %02X\n", 
+                       ahinfo->hdrres, ah->reserved,
+                       !(ahinfo->hdrres && ah->reserved));
+
+       return (ah != NULL)
+              &&
+              (spi_match(ahinfo->spis[0], ahinfo->spis[1],
+                           ntohl(ah->spi),
+                           !!(ahinfo->invflags & IP6T_AH_INV_SPI)))
+              &&
+              (!ahinfo->hdrlen ||
+                           (ahinfo->hdrlen == hdrlen) ^
+                           !!(ahinfo->invflags & IP6T_AH_INV_LEN))
+              &&
+              !(ahinfo->hdrres && ah->reserved);
+}
+
+/* Called when user tries to insert an entry of this type. */
+static int
+checkentry(const char *tablename,
+          const struct ip6t_ip6 *ip,
+          void *matchinfo,
+          unsigned int matchinfosize,
+          unsigned int hook_mask)
+{
+       const struct ip6t_ah *ahinfo = matchinfo;
+
+       if (matchinfosize != IP6T_ALIGN(sizeof(struct ip6t_ah))) {
+              DEBUGP("ip6t_ah: matchsize %u != %u\n",
+                      matchinfosize, IP6T_ALIGN(sizeof(struct ip6t_ah)));
+              return 0;
+       }
+       if (ahinfo->invflags & ~IP6T_AH_INV_MASK) {
+              DEBUGP("ip6t_ah: unknown flags %X\n",
+                      ahinfo->invflags);
+              return 0;
+       }
+
+       return 1;
+}
+
+static struct ip6t_match ah_match
+= { { NULL, NULL }, "ah", &match, &checkentry, NULL, THIS_MODULE };
+
+static int __init init(void)
+{
+       return ip6t_register_match(&ah_match);
+}
+
+static void __exit cleanup(void)
+{
+       ip6t_unregister_match(&ah_match);
+}
+
+module_init(init);
+module_exit(cleanup);
diff --git a/net/ipv6/netfilter/ip6t_dst.c b/net/ipv6/netfilter/ip6t_dst.c
new file mode 100644 (file)
index 0000000..0ccb4dd
--- /dev/null
@@ -0,0 +1,287 @@
+/* Kernel module to match Hop-by-Hop and Destination parameters. */
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/ipv6.h>
+#include <linux/types.h>
+#include <net/checksum.h>
+#include <net/ipv6.h>
+
+#include <asm/byteorder.h>
+
+#include <linux/netfilter_ipv6/ip6_tables.h>
+#include <linux/netfilter_ipv6/ip6t_opts.h>
+
+#define LOW(n)         (n & 0x00FF)
+
+#define HOPBYHOP       0
+
+EXPORT_NO_SYMBOLS;
+MODULE_LICENSE("GPL");
+#if HOPBYHOP
+MODULE_DESCRIPTION("IPv6 HbH match");
+#else
+MODULE_DESCRIPTION("IPv6 DST match");
+#endif
+MODULE_AUTHOR("Andras Kis-Szabo <kisza@sch.bme.hu>");
+
+#if 0
+#define DEBUGP printk
+#else
+#define DEBUGP(format, args...)
+#endif
+
+int ipv6_ext_hdr(u8 nexthdr)
+{
+        return ( (nexthdr == NEXTHDR_HOP)       ||
+                 (nexthdr == NEXTHDR_ROUTING)   ||
+                 (nexthdr == NEXTHDR_FRAGMENT)  ||
+                 (nexthdr == NEXTHDR_AUTH)      ||
+                 (nexthdr == NEXTHDR_ESP)       ||
+                 (nexthdr == NEXTHDR_NONE)      ||
+                 (nexthdr == NEXTHDR_DEST) );
+}
+
+/*
+ * (Type & 0xC0) >> 6
+ *     0       -> ignorable
+ *     1       -> must drop the packet
+ *     2       -> send ICMP PARM PROB regardless and drop packet
+ *     3       -> Send ICMP if not a multicast address and drop packet
+ *  (Type & 0x20) >> 5
+ *     0       -> invariant
+ *     1       -> can change the routing
+ *  (Type & 0x1F) Type
+ *      0      -> PAD0 (only 1 byte!)
+ *      1      -> PAD1 LENGTH info (total length = length + 2)
+ *      C0 | 2 -> JUMBO 4 x x x x ( xxxx > 64k )
+ *      5      -> RTALERT 2 x x
+ */
+
+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 *protohdr,
+      u_int16_t datalen,
+      int *hotdrop)
+{
+       struct ipv6_opt_hdr *optsh = NULL;
+       const struct ip6t_opts *optinfo = matchinfo;
+       unsigned int temp;
+       unsigned int len;
+       u8 nexthdr;
+       unsigned int ptr;
+       unsigned int hdrlen = 0;
+       unsigned int ret = 0;
+       u_int16_t *optdesc = NULL;
+       
+       /* type of the 1st exthdr */
+       nexthdr = skb->nh.ipv6h->nexthdr;
+       /* pointer to the 1st exthdr */
+       ptr = sizeof(struct ipv6hdr);
+       /* available length */
+       len = skb->len - ptr;
+       temp = 0;
+
+        while (ipv6_ext_hdr(nexthdr)) {
+               struct ipv6_opt_hdr *hdr;
+
+              DEBUGP("ipv6_opts header iteration \n");
+
+              /* Is there enough space for the next ext header? */
+                if (len < (int)sizeof(struct ipv6_opt_hdr))
+                        return 0;
+              /* No more exthdr -> evaluate */
+                if (nexthdr == NEXTHDR_NONE) {
+                     break;
+              }
+              /* ESP -> evaluate */
+                if (nexthdr == NEXTHDR_ESP) {
+                     break;
+              }
+
+              hdr=(void *)(skb->data)+ptr;
+
+              /* Calculate the header length */
+                if (nexthdr == NEXTHDR_FRAGMENT) {
+                        hdrlen = 8;
+                } else if (nexthdr == NEXTHDR_AUTH)
+                        hdrlen = (hdr->hdrlen+2)<<2;
+                else
+                        hdrlen = ipv6_optlen(hdr);
+
+              /* OPTS -> evaluate */
+#if HOPBYHOP
+                if (nexthdr == NEXTHDR_HOP) {
+                     temp |= MASK_HOPOPTS;
+#else
+                if (nexthdr == NEXTHDR_DEST) {
+                     temp |= MASK_DSTOPTS;
+#endif
+                     break;
+              }
+
+
+              /* set the flag */
+              switch (nexthdr){
+                     case NEXTHDR_HOP:
+                     case NEXTHDR_ROUTING:
+                     case NEXTHDR_FRAGMENT:
+                     case NEXTHDR_AUTH:
+                     case NEXTHDR_DEST:
+                            break;
+                     default:
+                            DEBUGP("ipv6_opts match: unknown nextheader %u\n",nexthdr);
+                            return 0;
+                            break;
+              }
+
+                nexthdr = hdr->nexthdr;
+                len -= hdrlen;
+                ptr += hdrlen;
+               if ( ptr > skb->len ) {
+                       DEBUGP("ipv6_opts: new pointer is too large! \n");
+                       break;
+               }
+        }
+
+       /* OPTIONS header not found */
+#if HOPBYHOP
+       if ( temp != MASK_HOPOPTS ) return 0;
+#else
+       if ( temp != MASK_DSTOPTS ) return 0;
+#endif
+
+       if (len < (int)sizeof(struct ipv6_opt_hdr)){
+              *hotdrop = 1;
+                       return 0;
+       }
+
+       if (len < hdrlen){
+              /* Packet smaller than it's length field */
+                       return 0;
+       }
+
+       optsh=(void *)(skb->data)+ptr;
+
+       DEBUGP("IPv6 OPTS LEN %u %u ", hdrlen, optsh->hdrlen);
+
+       DEBUGP("len %02X %04X %02X ",
+                       optinfo->hdrlen, hdrlen,
+                       (!(optinfo->flags & IP6T_OPTS_LEN) ||
+                           ((optinfo->hdrlen == hdrlen) ^
+                           !!(optinfo->invflags & IP6T_OPTS_INV_LEN))));
+
+       ret = (optsh != NULL)
+                       &&
+               (!(optinfo->flags & IP6T_OPTS_LEN) ||
+                           ((optinfo->hdrlen == hdrlen) ^
+                           !!(optinfo->invflags & IP6T_OPTS_INV_LEN)));
+
+       temp = len = 0;
+       ptr += 2;
+       hdrlen -= 2;
+       if ( !(optinfo->flags & IP6T_OPTS_OPTS) ){
+              return ret;
+       } else if (optinfo->flags & IP6T_OPTS_NSTRICT) {
+               DEBUGP("Not strict - not implemented");
+       } else {
+               DEBUGP("Strict ");
+               DEBUGP("#%d ",optinfo->optsnr);
+               for(temp=0; temp<optinfo->optsnr; temp++){
+                       optdesc = (void *)(skb->data)+ptr;
+                       /* Type check */
+                       if ( (unsigned char)*optdesc != 
+                               (optinfo->opts[temp] & 0xFF00)>>8 ){
+                               DEBUGP("Tbad %02X %02X\n",
+                                               (unsigned char)*optdesc,
+                                               (optinfo->opts[temp] &
+                                                0xFF00)>>8);
+                               return 0;
+                       } else {
+                               DEBUGP("Tok ");
+                       }
+                       /* Length check */
+                       if (((optinfo->opts[temp] & 0x00FF) != 0xFF) &&
+                               (unsigned char)*optdesc != 0){
+                               if ( ntohs((u16)*optdesc) != 
+                                               optinfo->opts[temp] ){
+                                       DEBUGP("Lbad %02X %04X %04X\n",
+                                                       (unsigned char)*optdesc,
+                                                       ntohs((u16)*optdesc),
+                                                       optinfo->opts[temp]);
+                                       return 0;
+                               } else {
+                                       DEBUGP("Lok ");
+                               }
+                       }
+                       /* Step to the next */
+                       if ((unsigned char)*optdesc == 0){
+                               DEBUGP("PAD0 \n");
+                               ptr++;
+                               hdrlen--;
+                       } else {
+                               ptr += LOW(ntohs(*optdesc));
+                               hdrlen -= LOW(ntohs(*optdesc));
+                               DEBUGP("len%04X \n", 
+                                       LOW(ntohs(*optdesc)));
+                       }
+                       if (ptr > skb->len || ( !hdrlen && 
+                               (temp != optinfo->optsnr - 1))) {
+                               DEBUGP("new pointer is too large! \n");
+                               break;
+                       }
+               }
+               if (temp == optinfo->optsnr)
+                       return ret;
+               else return 0;
+       }
+
+       return 0;
+}
+
+/* Called when user tries to insert an entry of this type. */
+static int
+checkentry(const char *tablename,
+          const struct ip6t_ip6 *ip,
+          void *matchinfo,
+          unsigned int matchinfosize,
+          unsigned int hook_mask)
+{
+       const struct ip6t_opts *optsinfo = matchinfo;
+
+       if (matchinfosize != IP6T_ALIGN(sizeof(struct ip6t_opts))) {
+              DEBUGP("ip6t_opts: matchsize %u != %u\n",
+                      matchinfosize, IP6T_ALIGN(sizeof(struct ip6t_opts)));
+              return 0;
+       }
+       if (optsinfo->invflags & ~IP6T_OPTS_INV_MASK) {
+              DEBUGP("ip6t_opts: unknown flags %X\n",
+                      optsinfo->invflags);
+              return 0;
+       }
+
+       return 1;
+}
+
+static struct ip6t_match opts_match
+#if HOPBYHOP
+= { { NULL, NULL }, "hbh", &match, &checkentry, NULL, THIS_MODULE };
+#else
+= { { NULL, NULL }, "dst", &match, &checkentry, NULL, THIS_MODULE };
+#endif
+
+static int __init init(void)
+{
+       return ip6t_register_match(&opts_match);
+}
+
+static void __exit cleanup(void)
+{
+       ip6t_unregister_match(&opts_match);
+}
+
+module_init(init);
+module_exit(cleanup);
diff --git a/net/ipv6/netfilter/ip6t_esp.c b/net/ipv6/netfilter/ip6t_esp.c
new file mode 100644 (file)
index 0000000..4777b75
--- /dev/null
@@ -0,0 +1,186 @@
+/* Kernel module to match ESP parameters. */
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/ipv6.h>
+#include <linux/types.h>
+#include <net/checksum.h>
+#include <net/ipv6.h>
+
+#include <linux/netfilter_ipv6/ip6_tables.h>
+#include <linux/netfilter_ipv6/ip6t_esp.h>
+
+EXPORT_NO_SYMBOLS;
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("IPv6 ESP match");
+MODULE_AUTHOR("Andras Kis-Szabo <kisza@sch.bme.hu>");
+
+#if 0
+#define DEBUGP printk
+#else
+#define DEBUGP(format, args...)
+#endif
+
+struct esphdr {
+       __u32   spi;
+};
+
+int ipv6_ext_hdr(u8 nexthdr)
+{
+        return ( (nexthdr == NEXTHDR_HOP)       ||
+                 (nexthdr == NEXTHDR_ROUTING)   ||
+                 (nexthdr == NEXTHDR_FRAGMENT)  ||
+                 (nexthdr == NEXTHDR_AUTH)      ||
+                 (nexthdr == NEXTHDR_ESP)       ||
+                 (nexthdr == NEXTHDR_NONE)      ||
+                 (nexthdr == NEXTHDR_DEST) );
+}
+
+/* Returns 1 if the spi is matched by the range, 0 otherwise */
+static inline int
+spi_match(u_int32_t min, u_int32_t max, u_int32_t spi, int invert)
+{
+       int r=0;
+        DEBUGP("esp spi_match:%c 0x%x <= 0x%x <= 0x%x",invert? '!':' ',
+               min,spi,max);
+       r=(spi >= min && spi <= max) ^ invert;
+       DEBUGP(" result %s\n",r? "PASS\n" : "FAILED\n");
+       return r;
+}
+
+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 *protohdr,
+      u_int16_t datalen,
+      int *hotdrop)
+{
+       struct esphdr *esp = NULL;
+       const struct ip6t_esp *espinfo = matchinfo;
+       unsigned int temp;
+       int len;
+       u8 nexthdr;
+       unsigned int ptr;
+
+       /* Make sure this isn't an evil packet */
+       /*DEBUGP("ipv6_esp entered \n");*/
+
+       /* type of the 1st exthdr */
+       nexthdr = skb->nh.ipv6h->nexthdr;
+       /* pointer to the 1st exthdr */
+       ptr = sizeof(struct ipv6hdr);
+       /* available length */
+       len = skb->len - ptr;
+       temp = 0;
+
+        while (ipv6_ext_hdr(nexthdr)) {
+               struct ipv6_opt_hdr *hdr;
+               int hdrlen;
+
+               DEBUGP("ipv6_esp header iteration \n");
+
+               /* Is there enough space for the next ext header? */
+                if (len < (int)sizeof(struct ipv6_opt_hdr))
+                        return 0;
+               /* No more exthdr -> evaluate */
+                if (nexthdr == NEXTHDR_NONE) {
+                       break;
+               }
+               /* ESP -> evaluate */
+                if (nexthdr == NEXTHDR_ESP) {
+                       temp |= MASK_ESP;
+                       break;
+               }
+
+               hdr=(struct ipv6_opt_hdr *)skb->data+ptr;
+
+               /* Calculate the header length */
+                if (nexthdr == NEXTHDR_FRAGMENT) {
+                        hdrlen = 8;
+                } else if (nexthdr == NEXTHDR_AUTH)
+                        hdrlen = (hdr->hdrlen+2)<<2;
+                else
+                        hdrlen = ipv6_optlen(hdr);
+
+               /* set the flag */
+               switch (nexthdr){
+                       case NEXTHDR_HOP:
+                       case NEXTHDR_ROUTING:
+                       case NEXTHDR_FRAGMENT:
+                       case NEXTHDR_AUTH:
+                       case NEXTHDR_DEST:
+                               break;
+                       default:
+                               DEBUGP("ipv6_esp match: unknown nextheader %u\n",nexthdr);
+                               return 0;
+                               break;
+               }
+
+                nexthdr = hdr->nexthdr;
+                len -= hdrlen;
+                ptr += hdrlen;
+               if ( ptr > skb->len ) {
+                       DEBUGP("ipv6_esp: new pointer too large! \n");
+                       break;
+               }
+        }
+
+       /* ESP header not found */
+       if ( temp != MASK_ESP ) return 0;
+
+       if (len < (int)sizeof(struct esphdr)){
+              *hotdrop = 1;
+                       return 0;
+       }
+
+       esp=skb->data+ptr;
+
+       DEBUGP("IPv6 ESP SPI %u %08X\n", ntohl(esp->spi), ntohl(esp->spi));
+
+       return (esp != NULL)
+               && spi_match(espinfo->spis[0], espinfo->spis[1],
+                             ntohl(esp->spi),
+                             !!(espinfo->invflags & IP6T_ESP_INV_SPI));
+}
+
+/* Called when user tries to insert an entry of this type. */
+static int
+checkentry(const char *tablename,
+          const struct ip6t_ip6 *ip,
+          void *matchinfo,
+          unsigned int matchinfosize,
+          unsigned int hook_mask)
+{
+       const struct ip6t_esp *espinfo = matchinfo;
+
+       if (matchinfosize != IP6T_ALIGN(sizeof(struct ip6t_esp))) {
+               DEBUGP("ip6t_esp: matchsize %u != %u\n",
+                        matchinfosize, IP6T_ALIGN(sizeof(struct ip6t_esp)));
+               return 0;
+       }
+       if (espinfo->invflags & ~IP6T_ESP_INV_MASK) {
+               DEBUGP("ip6t_esp: unknown flags %X\n",
+                        espinfo->invflags);
+               return 0;
+       }
+
+       return 1;
+}
+
+static struct ip6t_match esp_match
+= { { NULL, NULL }, "esp", &match, &checkentry, NULL, THIS_MODULE };
+
+static int __init init(void)
+{
+       return ip6t_register_match(&esp_match);
+}
+
+static void __exit cleanup(void)
+{
+       ip6t_unregister_match(&esp_match);
+}
+
+module_init(init);
+module_exit(cleanup);
diff --git a/net/ipv6/netfilter/ip6t_frag.c b/net/ipv6/netfilter/ip6t_frag.c
new file mode 100644 (file)
index 0000000..72da6b0
--- /dev/null
@@ -0,0 +1,250 @@
+/* Kernel module to match FRAG parameters. */
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/ipv6.h>
+#include <linux/types.h>
+#include <net/checksum.h>
+#include <net/ipv6.h>
+
+#include <asm/byteorder.h>
+
+#include <linux/netfilter_ipv6/ip6_tables.h>
+#include <linux/netfilter_ipv6/ip6t_frag.h>
+
+EXPORT_NO_SYMBOLS;
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("IPv6 FRAG match");
+MODULE_AUTHOR("Andras Kis-Szabo <kisza@sch.bme.hu>");
+
+#if 0
+#define DEBUGP printk
+#else
+#define DEBUGP(format, args...)
+#endif
+
+#if 0
+#if     BYTE_ORDER == BIG_ENDIAN
+#define IP6F_OFF_MASK       0xfff8  /* mask out offset from _offlg */
+#define IP6F_RESERVED_MASK  0x0006  /* reserved bits in ip6f_offlg */
+#define IP6F_MORE_FRAG      0x0001  /* more-fragments flag */
+#else   /* BYTE_ORDER == LITTLE_ENDIAN */
+#define IP6F_OFF_MASK       0xf8ff  /* mask out offset from _offlg */
+#define IP6F_RESERVED_MASK  0x0600  /* reserved bits in ip6f_offlg */
+#define IP6F_MORE_FRAG      0x0100  /* more-fragments flag */
+#endif
+#endif
+
+#define IP6F_OFF_MASK       0xf8ff  /* mask out offset from _offlg */
+#define IP6F_RESERVED_MASK  0x0600  /* reserved bits in ip6f_offlg */
+#define IP6F_MORE_FRAG      0x0100  /* more-fragments flag */
+
+struct fraghdr {
+       __u8    nexthdr;
+       __u8    hdrlen;
+       __u16   info;
+       __u32   id;
+};
+
+int ipv6_ext_hdr(u8 nexthdr)
+{
+        return ( (nexthdr == NEXTHDR_HOP)       ||
+                 (nexthdr == NEXTHDR_ROUTING)   ||
+                 (nexthdr == NEXTHDR_FRAGMENT)  ||
+                 (nexthdr == NEXTHDR_AUTH)      ||
+                 (nexthdr == NEXTHDR_ESP)       ||
+                 (nexthdr == NEXTHDR_NONE)      ||
+                 (nexthdr == NEXTHDR_DEST) );
+}
+
+/* Returns 1 if the id is matched by the range, 0 otherwise */
+static inline int
+id_match(u_int32_t min, u_int32_t max, u_int32_t id, int invert)
+{
+       int r=0;
+       DEBUGP("frag id_match:%c 0x%x <= 0x%x <= 0x%x",invert? '!':' ',
+              min,id,max);
+       r=(id >= min && id <= max) ^ invert;
+       DEBUGP(" result %s\n",r? "PASS" : "FAILED");
+       return r;
+}
+
+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 *protohdr,
+      u_int16_t datalen,
+      int *hotdrop)
+{
+       struct fraghdr *frag = NULL;
+       const struct ip6t_frag *fraginfo = matchinfo;
+       unsigned int temp;
+       int len;
+       u8 nexthdr;
+       unsigned int ptr;
+       unsigned int hdrlen = 0;
+
+       /* type of the 1st exthdr */
+       nexthdr = skb->nh.ipv6h->nexthdr;
+       /* pointer to the 1st exthdr */
+       ptr = sizeof(struct ipv6hdr);
+       /* available length */
+       len = skb->len - ptr;
+       temp = 0;
+
+        while (ipv6_ext_hdr(nexthdr)) {
+               struct ipv6_opt_hdr *hdr;
+
+              DEBUGP("ipv6_frag header iteration \n");
+
+              /* Is there enough space for the next ext header? */
+                if (len < (int)sizeof(struct ipv6_opt_hdr))
+                        return 0;
+              /* No more exthdr -> evaluate */
+                if (nexthdr == NEXTHDR_NONE) {
+                     break;
+              }
+              /* ESP -> evaluate */
+                if (nexthdr == NEXTHDR_ESP) {
+                     break;
+              }
+
+              hdr=(struct ipv6_opt_hdr *)skb->data+ptr;
+
+              /* Calculate the header length */
+                if (nexthdr == NEXTHDR_FRAGMENT) {
+                        hdrlen = 8;
+                } else if (nexthdr == NEXTHDR_AUTH)
+                        hdrlen = (hdr->hdrlen+2)<<2;
+                else
+                        hdrlen = ipv6_optlen(hdr);
+
+              /* FRAG -> evaluate */
+                if (nexthdr == NEXTHDR_FRAGMENT) {
+                     temp |= MASK_FRAGMENT;
+                     break;
+              }
+
+
+              /* set the flag */
+              switch (nexthdr){
+                     case NEXTHDR_HOP:
+                     case NEXTHDR_ROUTING:
+                     case NEXTHDR_FRAGMENT:
+                     case NEXTHDR_AUTH:
+                     case NEXTHDR_DEST:
+                            break;
+                     default:
+                            DEBUGP("ipv6_frag match: unknown nextheader %u\n",nexthdr);
+                            return 0;
+                            break;
+              }
+
+                nexthdr = hdr->nexthdr;
+                len -= hdrlen;
+                ptr += hdrlen;
+               if ( ptr > skb->len ) {
+                       DEBUGP("ipv6_frag: new pointer too large! \n");
+                       break;
+               }
+        }
+
+       /* FRAG header not found */
+       if ( temp != MASK_FRAGMENT ) return 0;
+
+       if (len < (int)sizeof(struct fraghdr)){
+              *hotdrop = 1;
+                       return 0;
+       }
+
+       frag=skb->data+ptr;
+
+       DEBUGP("IPv6 FRAG LEN %u %u ", hdrlen, frag->hdrlen);
+       DEBUGP("INFO %04X ", frag->info);
+       DEBUGP("OFFSET %04X ", frag->info & IP6F_OFF_MASK);
+       DEBUGP("RES %04X ", frag->info & IP6F_RESERVED_MASK);
+       DEBUGP("MF %04X ", frag->info & IP6F_MORE_FRAG);
+       DEBUGP("ID %u %08X\n", ntohl(frag->id), ntohl(frag->id));
+
+       DEBUGP("IPv6 FRAG id %02X ",
+                       (id_match(fraginfo->ids[0], fraginfo->ids[1],
+                           ntohl(frag->id),
+                           !!(fraginfo->invflags & IP6T_FRAG_INV_IDS))));
+       DEBUGP("len %02X %04X %02X ",
+                       fraginfo->hdrlen, hdrlen,
+                       (!fraginfo->hdrlen ||
+                           (fraginfo->hdrlen == hdrlen) ^
+                           !!(fraginfo->invflags & IP6T_FRAG_INV_LEN)));
+       DEBUGP("res %02X %02X %02X ", 
+                       (fraginfo->flags & IP6T_FRAG_RES), frag->info & IP6F_RESERVED_MASK,
+                       !((fraginfo->flags & IP6T_FRAG_RES) && (frag->info & IP6F_RESERVED_MASK)));
+       DEBUGP("first %02X %02X %02X ", 
+                       (fraginfo->flags & IP6T_FRAG_FST), frag->info & IP6F_OFF_MASK,
+                       !((fraginfo->flags & IP6T_FRAG_FST) && (frag->info & IP6F_OFF_MASK)));
+       DEBUGP("mf %02X %02X %02X ", 
+                       (fraginfo->flags & IP6T_FRAG_MF), frag->info & IP6F_MORE_FRAG,
+                       !((fraginfo->flags & IP6T_FRAG_MF) && !((frag->info & IP6F_MORE_FRAG))));
+       DEBUGP("last %02X %02X %02X\n", 
+                       (fraginfo->flags & IP6T_FRAG_NMF), frag->info & IP6F_MORE_FRAG,
+                       !((fraginfo->flags & IP6T_FRAG_NMF) && (frag->info & IP6F_MORE_FRAG)));
+
+       return (frag != NULL)
+                       &&
+                       (id_match(fraginfo->ids[0], fraginfo->ids[1],
+                           ntohl(frag->id),
+                           !!(fraginfo->invflags & IP6T_FRAG_INV_IDS)))
+               &&
+               (!fraginfo->hdrlen ||
+                           (fraginfo->hdrlen == hdrlen) ^
+                           !!(fraginfo->invflags & IP6T_FRAG_INV_LEN))
+               &&
+               !((fraginfo->flags & IP6T_FRAG_RES) && (frag->info & IP6F_RESERVED_MASK))
+               &&
+               !((fraginfo->flags & IP6T_FRAG_FST) && (frag->info & IP6F_OFF_MASK))
+               &&
+               !((fraginfo->flags & IP6T_FRAG_MF) && !((frag->info & IP6F_MORE_FRAG)))
+               &&
+               !((fraginfo->flags & IP6T_FRAG_NMF) && (frag->info & IP6F_MORE_FRAG));
+}
+
+/* Called when user tries to insert an entry of this type. */
+static int
+checkentry(const char *tablename,
+          const struct ip6t_ip6 *ip,
+          void *matchinfo,
+          unsigned int matchinfosize,
+          unsigned int hook_mask)
+{
+       const struct ip6t_frag *fraginfo = matchinfo;
+
+       if (matchinfosize != IP6T_ALIGN(sizeof(struct ip6t_frag))) {
+              DEBUGP("ip6t_frag: matchsize %u != %u\n",
+                      matchinfosize, IP6T_ALIGN(sizeof(struct ip6t_frag)));
+              return 0;
+       }
+       if (fraginfo->invflags & ~IP6T_FRAG_INV_MASK) {
+              DEBUGP("ip6t_frag: unknown flags %X\n",
+                      fraginfo->invflags);
+              return 0;
+       }
+
+       return 1;
+}
+
+static struct ip6t_match frag_match
+= { { NULL, NULL }, "frag", &match, &checkentry, NULL, THIS_MODULE };
+
+static int __init init(void)
+{
+       return ip6t_register_match(&frag_match);
+}
+
+static void __exit cleanup(void)
+{
+       ip6t_unregister_match(&frag_match);
+}
+
+module_init(init);
+module_exit(cleanup);
diff --git a/net/ipv6/netfilter/ip6t_hbh.c b/net/ipv6/netfilter/ip6t_hbh.c
new file mode 100644 (file)
index 0000000..ca0fcc6
--- /dev/null
@@ -0,0 +1,287 @@
+/* Kernel module to match Hop-by-Hop and Destination parameters. */
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/ipv6.h>
+#include <linux/types.h>
+#include <net/checksum.h>
+#include <net/ipv6.h>
+
+#include <asm/byteorder.h>
+
+#include <linux/netfilter_ipv6/ip6_tables.h>
+#include <linux/netfilter_ipv6/ip6t_opts.h>
+
+#define LOW(n)         (n & 0x00FF)
+
+#define HOPBYHOP       1
+
+EXPORT_NO_SYMBOLS;
+MODULE_LICENSE("GPL");
+#if HOPBYHOP
+MODULE_DESCRIPTION("IPv6 HbH match");
+#else
+MODULE_DESCRIPTION("IPv6 DST match");
+#endif
+MODULE_AUTHOR("Andras Kis-Szabo <kisza@sch.bme.hu>");
+
+#if 0
+#define DEBUGP printk
+#else
+#define DEBUGP(format, args...)
+#endif
+
+int ipv6_ext_hdr(u8 nexthdr)
+{
+        return ( (nexthdr == NEXTHDR_HOP)       ||
+                 (nexthdr == NEXTHDR_ROUTING)   ||
+                 (nexthdr == NEXTHDR_FRAGMENT)  ||
+                 (nexthdr == NEXTHDR_AUTH)      ||
+                 (nexthdr == NEXTHDR_ESP)       ||
+                 (nexthdr == NEXTHDR_NONE)      ||
+                 (nexthdr == NEXTHDR_DEST) );
+}
+
+/*
+ * (Type & 0xC0) >> 6
+ *     0       -> ignorable
+ *     1       -> must drop the packet
+ *     2       -> send ICMP PARM PROB regardless and drop packet
+ *     3       -> Send ICMP if not a multicast address and drop packet
+ *  (Type & 0x20) >> 5
+ *     0       -> invariant
+ *     1       -> can change the routing
+ *  (Type & 0x1F) Type
+ *      0      -> PAD0 (only 1 byte!)
+ *      1      -> PAD1 LENGTH info (total length = length + 2)
+ *      C0 | 2 -> JUMBO 4 x x x x ( xxxx > 64k )
+ *      5      -> RTALERT 2 x x
+ */
+
+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 *protohdr,
+      u_int16_t datalen,
+      int *hotdrop)
+{
+       struct ipv6_opt_hdr *optsh = NULL;
+       const struct ip6t_opts *optinfo = matchinfo;
+       unsigned int temp;
+       unsigned int len;
+       u8 nexthdr;
+       unsigned int ptr;
+       unsigned int hdrlen = 0;
+       unsigned int ret = 0;
+       u_int16_t *optdesc = NULL;
+       
+       /* type of the 1st exthdr */
+       nexthdr = skb->nh.ipv6h->nexthdr;
+       /* pointer to the 1st exthdr */
+       ptr = sizeof(struct ipv6hdr);
+       /* available length */
+       len = skb->len - ptr;
+       temp = 0;
+
+        while (ipv6_ext_hdr(nexthdr)) {
+               struct ipv6_opt_hdr *hdr;
+
+              DEBUGP("ipv6_opts header iteration \n");
+
+              /* Is there enough space for the next ext header? */
+                if (len < (int)sizeof(struct ipv6_opt_hdr))
+                        return 0;
+              /* No more exthdr -> evaluate */
+                if (nexthdr == NEXTHDR_NONE) {
+                     break;
+              }
+              /* ESP -> evaluate */
+                if (nexthdr == NEXTHDR_ESP) {
+                     break;
+              }
+
+              hdr=(void *)(skb->data)+ptr;
+
+              /* Calculate the header length */
+                if (nexthdr == NEXTHDR_FRAGMENT) {
+                        hdrlen = 8;
+                } else if (nexthdr == NEXTHDR_AUTH)
+                        hdrlen = (hdr->hdrlen+2)<<2;
+                else
+                        hdrlen = ipv6_optlen(hdr);
+
+              /* OPTS -> evaluate */
+#if HOPBYHOP
+                if (nexthdr == NEXTHDR_HOP) {
+                     temp |= MASK_HOPOPTS;
+#else
+                if (nexthdr == NEXTHDR_DEST) {
+                     temp |= MASK_DSTOPTS;
+#endif
+                     break;
+              }
+
+
+              /* set the flag */
+              switch (nexthdr){
+                     case NEXTHDR_HOP:
+                     case NEXTHDR_ROUTING:
+                     case NEXTHDR_FRAGMENT:
+                     case NEXTHDR_AUTH:
+                     case NEXTHDR_DEST:
+                            break;
+                     default:
+                            DEBUGP("ipv6_opts match: unknown nextheader %u\n",nexthdr);
+                            return 0;
+                            break;
+              }
+
+                nexthdr = hdr->nexthdr;
+                len -= hdrlen;
+                ptr += hdrlen;
+               if ( ptr > skb->len ) {
+                       DEBUGP("ipv6_opts: new pointer is too large! \n");
+                       break;
+               }
+        }
+
+       /* OPTIONS header not found */
+#if HOPBYHOP
+       if ( temp != MASK_HOPOPTS ) return 0;
+#else
+       if ( temp != MASK_DSTOPTS ) return 0;
+#endif
+
+       if (len < (int)sizeof(struct ipv6_opt_hdr)){
+              *hotdrop = 1;
+                       return 0;
+       }
+
+       if (len < hdrlen){
+              /* Packet smaller than it's length field */
+                       return 0;
+       }
+
+       optsh=(void *)(skb->data)+ptr;
+
+       DEBUGP("IPv6 OPTS LEN %u %u ", hdrlen, optsh->hdrlen);
+
+       DEBUGP("len %02X %04X %02X ",
+                       optinfo->hdrlen, hdrlen,
+                       (!(optinfo->flags & IP6T_OPTS_LEN) ||
+                           ((optinfo->hdrlen == hdrlen) ^
+                           !!(optinfo->invflags & IP6T_OPTS_INV_LEN))));
+
+       ret = (optsh != NULL)
+                       &&
+               (!(optinfo->flags & IP6T_OPTS_LEN) ||
+                           ((optinfo->hdrlen == hdrlen) ^
+                           !!(optinfo->invflags & IP6T_OPTS_INV_LEN)));
+
+       temp = len = 0;
+       ptr += 2;
+       hdrlen -= 2;
+       if ( !(optinfo->flags & IP6T_OPTS_OPTS) ){
+              return ret;
+       } else if (optinfo->flags & IP6T_OPTS_NSTRICT) {
+               DEBUGP("Not strict - not implemented");
+       } else {
+               DEBUGP("Strict ");
+               DEBUGP("#%d ",optinfo->optsnr);
+               for(temp=0; temp<optinfo->optsnr; temp++){
+                       optdesc = (void *)(skb->data)+ptr;
+                       /* Type check */
+                       if ( (unsigned char)*optdesc != 
+                               (optinfo->opts[temp] & 0xFF00)>>8 ){
+                               DEBUGP("Tbad %02X %02X\n",
+                                               (unsigned char)*optdesc,
+                                               (optinfo->opts[temp] &
+                                                0xFF00)>>8);
+                               return 0;
+                       } else {
+                               DEBUGP("Tok ");
+                       }
+                       /* Length check */
+                       if (((optinfo->opts[temp] & 0x00FF) != 0xFF) &&
+                               (unsigned char)*optdesc != 0){
+                               if ( ntohs((u16)*optdesc) != 
+                                               optinfo->opts[temp] ){
+                                       DEBUGP("Lbad %02X %04X %04X\n",
+                                                       (unsigned char)*optdesc,
+                                                       ntohs((u16)*optdesc),
+                                                       optinfo->opts[temp]);
+                                       return 0;
+                               } else {
+                                       DEBUGP("Lok ");
+                               }
+                       }
+                       /* Step to the next */
+                       if ((unsigned char)*optdesc == 0){
+                               DEBUGP("PAD0 \n");
+                               ptr++;
+                               hdrlen--;
+                       } else {
+                               ptr += LOW(ntohs(*optdesc));
+                               hdrlen -= LOW(ntohs(*optdesc));
+                               DEBUGP("len%04X \n", 
+                                       LOW(ntohs(*optdesc)));
+                       }
+                       if (ptr > skb->len || ( !hdrlen && 
+                               (temp != optinfo->optsnr - 1))) {
+                               DEBUGP("new pointer is too large! \n");
+                               break;
+                       }
+               }
+               if (temp == optinfo->optsnr)
+                       return ret;
+               else return 0;
+       }
+
+       return 0;
+}
+
+/* Called when user tries to insert an entry of this type. */
+static int
+checkentry(const char *tablename,
+          const struct ip6t_ip6 *ip,
+          void *matchinfo,
+          unsigned int matchinfosize,
+          unsigned int hook_mask)
+{
+       const struct ip6t_opts *optsinfo = matchinfo;
+
+       if (matchinfosize != IP6T_ALIGN(sizeof(struct ip6t_opts))) {
+              DEBUGP("ip6t_opts: matchsize %u != %u\n",
+                      matchinfosize, IP6T_ALIGN(sizeof(struct ip6t_opts)));
+              return 0;
+       }
+       if (optsinfo->invflags & ~IP6T_OPTS_INV_MASK) {
+              DEBUGP("ip6t_opts: unknown flags %X\n",
+                      optsinfo->invflags);
+              return 0;
+       }
+
+       return 1;
+}
+
+static struct ip6t_match opts_match
+#if HOPBYHOP
+= { { NULL, NULL }, "hbh", &match, &checkentry, NULL, THIS_MODULE };
+#else
+= { { NULL, NULL }, "dst", &match, &checkentry, NULL, THIS_MODULE };
+#endif
+
+static int __init init(void)
+{
+       return ip6t_register_match(&opts_match);
+}
+
+static void __exit cleanup(void)
+{
+       ip6t_unregister_match(&opts_match);
+}
+
+module_init(init);
+module_exit(cleanup);
diff --git a/net/ipv6/netfilter/ip6t_hl.c b/net/ipv6/netfilter/ip6t_hl.c
new file mode 100644 (file)
index 0000000..7a78025
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Hop Limit matching module
+ * Maciej Soltysiak <solt@dns.toxicfilms.tv>
+ * Based on HW's ttl module
+ *
+ * This software is distributed under the terms  GNU GPL
+ */
+
+#include <linux/module.h>
+#include <linux/skbuff.h>
+
+#include <linux/netfilter_ipv6/ip6t_hl.h>
+#include <linux/netfilter_ipv6/ip6_tables.h>
+
+MODULE_AUTHOR("Maciej Soltysiak <solt@dns.toxicfilms.tv>");
+MODULE_DESCRIPTION("IP tables Hop Limit matching module");
+MODULE_LICENSE("GPL");
+
+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 ip6t_hl_info *info = matchinfo;
+       const struct ipv6hdr *ip6h = skb->nh.ipv6h;
+
+       switch (info->mode) {
+               case IP6T_HL_EQ:
+                       return (ip6h->hop_limit == info->hop_limit);
+                       break;
+               case IP6T_HL_NE:
+                       return (!(ip6h->hop_limit == info->hop_limit));
+                       break;
+               case IP6T_HL_LT:
+                       return (ip6h->hop_limit < info->hop_limit);
+                       break;
+               case IP6T_HL_GT:
+                       return (ip6h->hop_limit > info->hop_limit);
+                       break;
+               default:
+                       printk(KERN_WARNING "ip6t_hl: unknown mode %d\n", 
+                               info->mode);
+                       return 0;
+       }
+
+       return 0;
+}
+
+static int checkentry(const char *tablename, const struct ip6t_ip6 *ip,
+                     void *matchinfo, unsigned int matchsize,
+                     unsigned int hook_mask)
+{
+       if (matchsize != IP6T_ALIGN(sizeof(struct ip6t_hl_info)))
+               return 0;
+
+       return 1;
+}
+
+static struct ip6t_match hl_match = { { NULL, NULL }, "hl", &match,
+               &checkentry, NULL, THIS_MODULE };
+
+static int __init init(void)
+{
+       return ip6t_register_match(&hl_match);
+}
+
+static void __exit fini(void)
+{
+       ip6t_unregister_match(&hl_match);
+
+}
+
+module_init(init);
+module_exit(fini);
diff --git a/net/ipv6/netfilter/ip6t_ipv6header.c b/net/ipv6/netfilter/ip6t_ipv6header.c
new file mode 100644 (file)
index 0000000..0dd7f49
--- /dev/null
@@ -0,0 +1,222 @@
+/* ipv6header match - matches IPv6 packets based
+on whether they contain certain headers */
+
+/* Original idea: Brad Chapman 
+ * Rewritten by: Andras Kis-Szabo <kisza@sch.bme.hu> */
+
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/ipv6.h>
+#include <linux/types.h>
+#include <net/checksum.h>
+#include <net/ipv6.h>
+
+#include <linux/netfilter_ipv6/ip6_tables.h>
+#include <linux/netfilter_ipv6/ip6t_ipv6header.h>
+
+EXPORT_NO_SYMBOLS;
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("IPv6 headers match");
+MODULE_AUTHOR("Andras Kis-Szabo <kisza@sch.bme.hu>");
+
+#if 0
+#define DEBUGP printk
+#else
+#define DEBUGP(format, args...)
+#endif
+
+int ipv6_ext_hdr(u8 nexthdr)
+{
+        return ( (nexthdr == NEXTHDR_HOP)       ||
+                 (nexthdr == NEXTHDR_ROUTING)   ||
+                 (nexthdr == NEXTHDR_FRAGMENT)  ||
+                 (nexthdr == NEXTHDR_AUTH)      ||
+                 (nexthdr == NEXTHDR_ESP)       ||
+                 (nexthdr == NEXTHDR_NONE)      ||
+                 (nexthdr == NEXTHDR_DEST) );
+}
+
+static int
+ipv6header_match(const struct sk_buff *skb,
+                const struct net_device *in,
+                const struct net_device *out,
+                const void *matchinfo,
+                int offset,
+                const void *protohdr,
+                u_int16_t datalen,
+                int *hotdrop)
+{
+       const struct ip6t_ipv6header_info *info = matchinfo;
+       unsigned int temp;
+       int len;
+       u8 nexthdr;
+       unsigned int ptr;
+        struct inet6_skb_parm *opt = (struct inet6_skb_parm *)skb->cb;
+
+       /* Make sure this isn't an evil packet */
+       DEBUGP("ipv6_header entered \n");
+
+       /* type of the 1st exthdr */
+       nexthdr = skb->nh.ipv6h->nexthdr;
+       /* pointer to the 1st exthdr */
+       ptr = sizeof(struct ipv6hdr);
+       /* available length */
+       len = skb->len - ptr;
+       temp = 0;
+
+       DEBUGP("ipv6_header nexthdr %02X \n",nexthdr);
+       DEBUGP("ipv6_header ptr %08X \n",ptr);
+       DEBUGP("ipv6_header skblen %04X \n",skb->len);
+       DEBUGP("ipv6_header skbdatalen %04X \n",skb->data_len);
+       DEBUGP("ipv6_header len %04X \n",len);
+#if 0
+       for (temp=0;temp<skb->len;temp++){
+               if (!(temp % 16 )) DEBUGP("\nipv6_header data ");
+               DEBUGP("%02X ",skb->data[temp]);
+       }
+#endif
+       DEBUGP("\nipv6_header h.raw %02X %02X %02X %02X \n",
+                       skb->h.raw[0],
+                       skb->h.raw[1],
+                       skb->h.raw[2],
+                       skb->h.raw[3]);
+       DEBUGP("ipv6_header nh.raw %02X %02X %02X %02X \n",
+                       skb->nh.raw[0],
+                       skb->nh.raw[1],
+                       skb->nh.raw[2],
+                       skb->nh.raw[3]);
+       DEBUGP("ipv6_header CB %02X %02X %02X %02X %02X %02X %02X \n",
+                       opt->iif,
+                       opt->ra,
+                       opt->hop,
+                       opt->auth,
+                       opt->dst0,
+                       opt->srcrt,
+                       opt->dst1);
+
+       temp = 0;
+
+        while (ipv6_ext_hdr(nexthdr)) {
+               struct ipv6_opt_hdr *hdr;
+               int hdrlen;
+
+               DEBUGP("ipv6_header header iteration \n");
+
+               /* Is there enough space for the next ext header? */
+                if (len < (int)sizeof(struct ipv6_opt_hdr))
+                        return 0;
+               /* No more exthdr -> evaluate */
+                if (nexthdr == NEXTHDR_NONE) {
+                       temp |= MASK_NONE;
+                       break;
+               }
+               /* ESP -> evaluate */
+                if (nexthdr == NEXTHDR_ESP) {
+                       temp |= MASK_ESP;
+                       break;
+               }
+
+               hdr=(struct ipv6_opt_hdr *)skb->data+ptr;
+
+               /* Calculate the header length */
+                if (nexthdr == NEXTHDR_FRAGMENT) {
+                        hdrlen = 8;
+                } else if (nexthdr == NEXTHDR_AUTH)
+                        hdrlen = (hdr->hdrlen+2)<<2;
+                else
+                        hdrlen = ipv6_optlen(hdr);
+
+               DEBUGP("ipv6_header hdrlen %04X \n",hdrlen);
+
+               /* set the flag */
+               switch (nexthdr){
+                       case NEXTHDR_HOP:
+                               temp |= MASK_HOPOPTS;
+                               break;
+                       case NEXTHDR_ROUTING:
+                               temp |= MASK_ROUTING;
+                               break;
+                       case NEXTHDR_FRAGMENT:
+                               temp |= MASK_FRAGMENT;
+                               break;
+                       case NEXTHDR_AUTH:
+                               temp |= MASK_AH;
+                               break;
+                       case NEXTHDR_DEST:
+                               temp |= MASK_DSTOPTS;
+                               break;
+                       default:
+                               DEBUGP("IPV6HEADER match: unknown nextheader %u\n",nexthdr);
+                               return 0;
+                               break;
+               }
+
+                nexthdr = hdr->nexthdr;
+                len -= hdrlen;
+                ptr += hdrlen;
+               if ( ptr > skb->len ) {
+                       DEBUGP("ipv6_header new ptr %04X \n",ptr);
+                       break;
+               }
+        }
+
+       if ( (nexthdr != NEXTHDR_NONE ) && (nexthdr != NEXTHDR_ESP) )
+               temp |= MASK_PROTO;
+
+       DEBUGP ("ipv6header: %02X %02X \n", temp, info->matchflags);
+
+       if (info->modeflag)
+               return (!( (temp & info->matchflags)
+                       ^ info->matchflags) ^ info->invflags);
+       else
+               return (!( temp ^ info->matchflags) ^ info->invflags);
+}
+
+static int
+ipv6header_checkentry(const char *tablename,
+                     const struct ip6t_ip6 *ip,
+                     void *matchinfo,
+                     unsigned int matchsize,
+                     unsigned int hook_mask)
+{
+       /* Check for obvious errors */
+       /* This match is valid in all hooks! */
+       if (matchsize != IP6T_ALIGN(sizeof(struct ip6t_ipv6header_info))) {
+               DEBUGP("ip6t_ipv6header: matchsize != %u\n",
+                        IP6T_ALIGN(sizeof(struct ip6t_ipv6header_info)));
+               return 0;
+       }
+
+       return 1;
+}
+
+static void
+ipv6header_destroy(void *matchinfo,
+                  unsigned int matchinfosize)
+{
+       return;
+}
+
+static struct ip6t_match
+ip6t_ipv6header_match = {
+       { NULL, NULL },
+       "ipv6header",
+       &ipv6header_match,
+       &ipv6header_checkentry,
+       &ipv6header_destroy,
+       THIS_MODULE
+};
+
+static int  __init ipv6header_init(void)
+{
+       return ip6t_register_match(&ip6t_ipv6header_match);
+}
+
+static void __exit ipv6header_exit(void)
+{
+       ip6t_unregister_match(&ip6t_ipv6header_match);
+}
+
+module_init(ipv6header_init);
+module_exit(ipv6header_exit);
+
diff --git a/net/ipv6/netfilter/ip6t_rt.c b/net/ipv6/netfilter/ip6t_rt.c
new file mode 100644 (file)
index 0000000..fd3bc32
--- /dev/null
@@ -0,0 +1,305 @@
+/* Kernel module to match ROUTING parameters. */
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/ipv6.h>
+#include <linux/types.h>
+#include <net/checksum.h>
+#include <net/ipv6.h>
+
+#include <asm/byteorder.h>
+
+#include <linux/netfilter_ipv6/ip6_tables.h>
+#include <linux/netfilter_ipv6/ip6t_rt.h>
+
+EXPORT_NO_SYMBOLS;
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("IPv6 RT match");
+MODULE_AUTHOR("Andras Kis-Szabo <kisza@sch.bme.hu>");
+
+#if 0
+#define DEBUGP printk
+#else
+#define DEBUGP(format, args...)
+#endif
+
+int ipv6_ext_hdr(u8 nexthdr)
+{
+        return ( (nexthdr == NEXTHDR_HOP)       ||
+                 (nexthdr == NEXTHDR_ROUTING)   ||
+                 (nexthdr == NEXTHDR_FRAGMENT)  ||
+                 (nexthdr == NEXTHDR_AUTH)      ||
+                 (nexthdr == NEXTHDR_ESP)       ||
+                 (nexthdr == NEXTHDR_NONE)      ||
+                 (nexthdr == NEXTHDR_DEST) );
+}
+
+/* Returns 1 if the id is matched by the range, 0 otherwise */
+static inline int
+segsleft_match(u_int32_t min, u_int32_t max, u_int32_t id, int invert)
+{
+       int r=0;
+       DEBUGP("rt segsleft_match:%c 0x%x <= 0x%x <= 0x%x",invert? '!':' ',
+              min,id,max);
+       r=(id >= min && id <= max) ^ invert;
+       DEBUGP(" result %s\n",r? "PASS" : "FAILED");
+       return r;
+}
+
+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 *protohdr,
+      u_int16_t datalen,
+      int *hotdrop)
+{
+       struct ipv6_rt_hdr *route = NULL;
+       const struct ip6t_rt *rtinfo = matchinfo;
+       unsigned int temp;
+       unsigned int len;
+       u8 nexthdr;
+       unsigned int ptr;
+       unsigned int hdrlen = 0;
+       unsigned int ret = 0;
+
+       /* type of the 1st exthdr */
+       nexthdr = skb->nh.ipv6h->nexthdr;
+       /* pointer to the 1st exthdr */
+       ptr = sizeof(struct ipv6hdr);
+       /* available length */
+       len = skb->len - ptr;
+       temp = 0;
+
+        while (ipv6_ext_hdr(nexthdr)) {
+               struct ipv6_opt_hdr *hdr;
+
+              DEBUGP("ipv6_rt header iteration \n");
+
+              /* Is there enough space for the next ext header? */
+                if (len < (int)sizeof(struct ipv6_opt_hdr))
+                        return 0;
+              /* No more exthdr -> evaluate */
+                if (nexthdr == NEXTHDR_NONE) {
+                     break;
+              }
+              /* ESP -> evaluate */
+                if (nexthdr == NEXTHDR_ESP) {
+                     break;
+              }
+
+              hdr=(struct ipv6_opt_hdr *)skb->data+ptr;
+
+              /* Calculate the header length */
+                if (nexthdr == NEXTHDR_FRAGMENT) {
+                        hdrlen = 8;
+                } else if (nexthdr == NEXTHDR_AUTH)
+                        hdrlen = (hdr->hdrlen+2)<<2;
+                else
+                        hdrlen = ipv6_optlen(hdr);
+
+              /* ROUTING -> evaluate */
+                if (nexthdr == NEXTHDR_ROUTING) {
+                     temp |= MASK_ROUTING;
+                     break;
+              }
+
+
+              /* set the flag */
+              switch (nexthdr){
+                     case NEXTHDR_HOP:
+                     case NEXTHDR_ROUTING:
+                     case NEXTHDR_FRAGMENT:
+                     case NEXTHDR_AUTH:
+                     case NEXTHDR_DEST:
+                            break;
+                     default:
+                            DEBUGP("ipv6_rt match: unknown nextheader %u\n",nexthdr);
+                            return 0;
+                            break;
+              }
+
+                nexthdr = hdr->nexthdr;
+                len -= hdrlen;
+                ptr += hdrlen;
+               if ( ptr > skb->len ) {
+                       DEBUGP("ipv6_rt: new pointer is too large! \n");
+                       break;
+               }
+        }
+
+       /* ROUTING header not found */
+       if ( temp != MASK_ROUTING ) return 0;
+
+       if (len < (int)sizeof(struct ipv6_rt_hdr)){
+              *hotdrop = 1;
+                       return 0;
+       }
+
+       if (len < hdrlen){
+              /* Pcket smaller than its length field */
+                       return 0;
+       }
+
+       route=skb->data+ptr;
+
+       DEBUGP("IPv6 RT LEN %u %u ", hdrlen, route->hdrlen);
+       DEBUGP("TYPE %04X ", route->type);
+       DEBUGP("SGS_LEFT %u %08X\n", ntohl(route->segments_left), ntohl(route->segments_left));
+
+       DEBUGP("IPv6 RT segsleft %02X ",
+                       (segsleft_match(rtinfo->segsleft[0], rtinfo->segsleft[1],
+                           ntohl(route->segments_left),
+                           !!(rtinfo->invflags & IP6T_RT_INV_SGS))));
+       DEBUGP("type %02X %02X %02X ",
+                       rtinfo->rt_type, route->type, 
+                       (!(rtinfo->flags & IP6T_RT_TYP) ||
+                           ((rtinfo->rt_type == route->type) ^
+                           !!(rtinfo->invflags & IP6T_RT_INV_TYP))));
+       DEBUGP("len %02X %04X %02X ",
+                       rtinfo->hdrlen, hdrlen,
+                       (!(rtinfo->flags & IP6T_RT_LEN) ||
+                           ((rtinfo->hdrlen == hdrlen) ^
+                           !!(rtinfo->invflags & IP6T_RT_INV_LEN))));
+       DEBUGP("res %02X %02X %02X ", 
+                       (rtinfo->flags & IP6T_RT_RES), ((struct rt0_hdr *)route)->bitmap,
+                       !((rtinfo->flags & IP6T_RT_RES) && (((struct rt0_hdr *)route)->bitmap)));
+
+       ret = (route != NULL)
+                       &&
+                       (segsleft_match(rtinfo->segsleft[0], rtinfo->segsleft[1],
+                           ntohl(route->segments_left),
+                           !!(rtinfo->invflags & IP6T_RT_INV_SGS)))
+               &&
+               (!(rtinfo->flags & IP6T_RT_LEN) ||
+                           ((rtinfo->hdrlen == hdrlen) ^
+                           !!(rtinfo->invflags & IP6T_RT_INV_LEN)))
+               &&
+                       (!(rtinfo->flags & IP6T_RT_TYP) ||
+                           ((rtinfo->rt_type == route->type) ^
+                           !!(rtinfo->invflags & IP6T_RT_INV_TYP)))
+               &&
+                       !((rtinfo->flags & IP6T_RT_RES) && (((struct rt0_hdr *)route)->bitmap));
+
+       DEBUGP("#%d ",rtinfo->addrnr);
+       temp = len = ptr = 0;
+       if ( !(rtinfo->flags & IP6T_RT_FST) ){
+              return ret;
+       } else if (rtinfo->flags & IP6T_RT_FST_NSTRICT) {
+               DEBUGP("Not strict ");
+               if ( rtinfo->addrnr > (unsigned int)((hdrlen-8)/16) ){
+                       DEBUGP("There isn't enough space\n");
+                       return 0;
+               } else {
+                       DEBUGP("#%d ",rtinfo->addrnr);
+                       ptr = 0;
+                       for(temp=0; temp<(unsigned int)((hdrlen-8)/16); temp++){
+                               len = 0;
+                               while ((u8)(((struct rt0_hdr *)route)->
+                                               addr[temp].s6_addr[len]) ==
+                                       (u8)(rtinfo->addrs[ptr].s6_addr[len])){
+                                       DEBUGP("%02X?%02X ",
+               (u8)(((struct rt0_hdr *)route)->addr[temp].s6_addr[len]),
+                                       (u8)(rtinfo->addrs[ptr].s6_addr[len]));
+                                       len++;
+                                       if ( len == 16 ) break;
+                               }
+                               if (len==16) {
+                                       DEBUGP("ptr=%d temp=%d;\n",ptr,temp);
+                                       ptr++;
+                               } else {
+                                       DEBUGP("%02X?%02X ",
+               (u8)(((struct rt0_hdr *)route)->addr[temp].s6_addr[len]),
+                                       (u8)(rtinfo->addrs[ptr].s6_addr[len]));
+                                       DEBUGP("!ptr=%d temp=%d;\n",ptr,temp);
+                               }
+                               if (ptr==rtinfo->addrnr) break;
+                       }
+                       DEBUGP("ptr=%d len=%d #%d\n",ptr,len, rtinfo->addrnr);
+                       if ( (len == 16) && (ptr == rtinfo->addrnr))
+                               return ret;
+                       else return 0;
+               }
+       } else {
+               DEBUGP("Strict ");
+               if ( rtinfo->addrnr > (unsigned int)((hdrlen-8)/16) ){
+                       DEBUGP("There isn't enough space\n");
+                       return 0;
+               } else {
+                       DEBUGP("#%d ",rtinfo->addrnr);
+                       for(temp=0; temp<rtinfo->addrnr; temp++){
+                               len = 0;
+                               while ((u8)(((struct rt0_hdr *)route)->
+                                               addr[temp].s6_addr[len]) ==
+                                       (u8)(rtinfo->addrs[temp].s6_addr[len])){
+                                       DEBUGP("%02X?%02X ",
+               (u8)(((struct rt0_hdr *)route)->addr[temp].s6_addr[len]),
+                                       (u8)(rtinfo->addrs[temp].s6_addr[len]));
+                                       len++;
+                                       if ( len == 16 ) break;
+                               }
+                               if (len!=16) {
+                                       DEBUGP("%02X?%02X ",
+               (u8)(((struct rt0_hdr *)route)->addr[temp].s6_addr[len]),
+                                       (u8)(rtinfo->addrs[temp].s6_addr[len]));
+                                       DEBUGP("!len=%d temp=%d;\n",len,temp);
+                                       break;
+                               }
+                       }
+                       DEBUGP("temp=%d len=%d #%d\n",temp,len,rtinfo->addrnr);
+                       if ( (len == 16) && (temp == rtinfo->addrnr) && (temp == (unsigned int)((hdrlen-8)/16)))
+                               return ret;
+                       else return 0;
+               }
+       }
+
+       return 0;
+}
+
+/* Called when user tries to insert an entry of this type. */
+static int
+checkentry(const char *tablename,
+          const struct ip6t_ip6 *ip,
+          void *matchinfo,
+          unsigned int matchinfosize,
+          unsigned int hook_mask)
+{
+       const struct ip6t_rt *rtinfo = matchinfo;
+
+       if (matchinfosize != IP6T_ALIGN(sizeof(struct ip6t_rt))) {
+              DEBUGP("ip6t_rt: matchsize %u != %u\n",
+                      matchinfosize, IP6T_ALIGN(sizeof(struct ip6t_rt)));
+              return 0;
+       }
+       if (rtinfo->invflags & ~IP6T_RT_INV_MASK) {
+              DEBUGP("ip6t_rt: unknown flags %X\n",
+                      rtinfo->invflags);
+              return 0;
+       }
+       if ( (rtinfo->flags & (IP6T_RT_RES|IP6T_RT_FST_MASK)) && 
+                      (!(rtinfo->flags & IP6T_RT_TYP) || 
+                      (rtinfo->rt_type != 0) || 
+                      (rtinfo->invflags & IP6T_RT_INV_TYP)) ) {
+             DEBUGP("`--rt-type 0' required before `--rt-0-*'");
+              return 0;
+       }
+
+       return 1;
+}
+
+static struct ip6t_match rt_match
+= { { NULL, NULL }, "rt", &match, &checkentry, NULL, THIS_MODULE };
+
+static int __init init(void)
+{
+       return ip6t_register_match(&rt_match);
+}
+
+static void __exit cleanup(void)
+{
+       ip6t_unregister_match(&rt_match);
+}
+
+module_init(init);
+module_exit(cleanup);