From c069d58efe57f216c06fe1b9e46a63a030f4304a Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Sun, 2 Mar 2003 09:40:56 -0800 Subject: [PATCH] [NETFILTER]: Add new ip6tables matches. --- include/linux/netfilter_ipv6/ip6t_ah.h | 30 ++ include/linux/netfilter_ipv6/ip6t_esp.h | 23 ++ include/linux/netfilter_ipv6/ip6t_frag.h | 33 ++ include/linux/netfilter_ipv6/ip6t_hl.h | 22 ++ .../linux/netfilter_ipv6/ip6t_ipv6header.h | 27 ++ include/linux/netfilter_ipv6/ip6t_opts.h | 32 ++ include/linux/netfilter_ipv6/ip6t_rt.h | 42 +++ net/ipv6/netfilter/Kconfig | 59 ++++ net/ipv6/netfilter/Makefile | 6 + net/ipv6/netfilter/ip6t_ah.c | 218 +++++++++++++ net/ipv6/netfilter/ip6t_dst.c | 287 ++++++++++++++++ net/ipv6/netfilter/ip6t_esp.c | 186 +++++++++++ net/ipv6/netfilter/ip6t_frag.c | 250 ++++++++++++++ net/ipv6/netfilter/ip6t_hbh.c | 287 ++++++++++++++++ net/ipv6/netfilter/ip6t_hl.c | 74 +++++ net/ipv6/netfilter/ip6t_ipv6header.c | 222 +++++++++++++ net/ipv6/netfilter/ip6t_rt.c | 305 ++++++++++++++++++ 17 files changed, 2103 insertions(+) create mode 100644 include/linux/netfilter_ipv6/ip6t_ah.h create mode 100644 include/linux/netfilter_ipv6/ip6t_esp.h create mode 100644 include/linux/netfilter_ipv6/ip6t_frag.h create mode 100644 include/linux/netfilter_ipv6/ip6t_hl.h create mode 100644 include/linux/netfilter_ipv6/ip6t_ipv6header.h create mode 100644 include/linux/netfilter_ipv6/ip6t_opts.h create mode 100644 include/linux/netfilter_ipv6/ip6t_rt.h create mode 100644 net/ipv6/netfilter/ip6t_ah.c create mode 100644 net/ipv6/netfilter/ip6t_dst.c create mode 100644 net/ipv6/netfilter/ip6t_esp.c create mode 100644 net/ipv6/netfilter/ip6t_frag.c create mode 100644 net/ipv6/netfilter/ip6t_hbh.c create mode 100644 net/ipv6/netfilter/ip6t_hl.c create mode 100644 net/ipv6/netfilter/ip6t_ipv6header.c create mode 100644 net/ipv6/netfilter/ip6t_rt.c diff --git a/include/linux/netfilter_ipv6/ip6t_ah.h b/include/linux/netfilter_ipv6/ip6t_ah.h new file mode 100644 index 0000000000000..c4f0793a0a98b --- /dev/null +++ b/include/linux/netfilter_ipv6/ip6t_ah.h @@ -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 index 0000000000000..01142b98a2314 --- /dev/null +++ b/include/linux/netfilter_ipv6/ip6t_esp.h @@ -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 index 0000000000000..449a57eca7dde --- /dev/null +++ b/include/linux/netfilter_ipv6/ip6t_frag.h @@ -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 index 0000000000000..5ef91b8319a8a --- /dev/null +++ b/include/linux/netfilter_ipv6/ip6t_hl.h @@ -0,0 +1,22 @@ +/* ip6tables module for matching the Hop Limit value + * Maciej Soltysiak + * 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 index 0000000000000..51c53fc9c44a5 --- /dev/null +++ b/include/linux/netfilter_ipv6/ip6t_ipv6header.h @@ -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 */ + + +#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 index 0000000000000..e259b6275bd29 --- /dev/null +++ b/include/linux/netfilter_ipv6/ip6t_opts.h @@ -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 index 0000000000000..f1070fbf27570 --- /dev/null +++ b/include/linux/netfilter_ipv6/ip6t_rt.h @@ -0,0 +1,42 @@ +#ifndef _IP6T_RT_H +#define _IP6T_RT_H + +/*#include */ + +#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*/ diff --git a/net/ipv6/netfilter/Kconfig b/net/ipv6/netfilter/Kconfig index 27336bb287335..6b99d50874570 100644 --- a/net/ipv6/netfilter/Kconfig +++ b/net/ipv6/netfilter/Kconfig @@ -60,6 +60,46 @@ config IP6_NF_MATCH_MAC If you want to compile it as a module, say M here and read . 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 + . 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 + . 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 + . 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 + . 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 . 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 + . 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 + . If unsure, say `N'. + config IP6_NF_MATCH_LENGTH tristate "Packet Length match support" depends on IP6_NF_IPTABLES diff --git a/net/ipv6/netfilter/Makefile b/net/ipv6/netfilter/Makefile index 56ffe51f76fd0..5ac6efcdf7d16 100644 --- a/net/ipv6/netfilter/Makefile +++ b/net/ipv6/netfilter/Makefile @@ -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 index 0000000000000..0210faa421ce3 --- /dev/null +++ b/net/ipv6/netfilter/ip6t_ah.c @@ -0,0 +1,218 @@ +/* Kernel module to match AH parameters. */ +#include +#include +#include +#include +#include +#include + +#include +#include + +EXPORT_NO_SYMBOLS; +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("IPv6 AH match"); +MODULE_AUTHOR("Andras Kis-Szabo "); + +#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 index 0000000000000..0ccb4dd5ea092 --- /dev/null +++ b/net/ipv6/netfilter/ip6t_dst.c @@ -0,0 +1,287 @@ +/* Kernel module to match Hop-by-Hop and Destination parameters. */ +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#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 "); + +#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; tempoptsnr; 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 index 0000000000000..4777b75bcdc8e --- /dev/null +++ b/net/ipv6/netfilter/ip6t_esp.c @@ -0,0 +1,186 @@ +/* Kernel module to match ESP parameters. */ +#include +#include +#include +#include +#include +#include + +#include +#include + +EXPORT_NO_SYMBOLS; +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("IPv6 ESP match"); +MODULE_AUTHOR("Andras Kis-Szabo "); + +#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 index 0000000000000..72da6b0b28917 --- /dev/null +++ b/net/ipv6/netfilter/ip6t_frag.c @@ -0,0 +1,250 @@ +/* Kernel module to match FRAG parameters. */ +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +EXPORT_NO_SYMBOLS; +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("IPv6 FRAG match"); +MODULE_AUTHOR("Andras Kis-Szabo "); + +#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 index 0000000000000..ca0fcc628da5f --- /dev/null +++ b/net/ipv6/netfilter/ip6t_hbh.c @@ -0,0 +1,287 @@ +/* Kernel module to match Hop-by-Hop and Destination parameters. */ +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#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 "); + +#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; tempoptsnr; 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 index 0000000000000..7a780255662fe --- /dev/null +++ b/net/ipv6/netfilter/ip6t_hl.c @@ -0,0 +1,74 @@ +/* + * Hop Limit matching module + * Maciej Soltysiak + * Based on HW's ttl module + * + * This software is distributed under the terms GNU GPL + */ + +#include +#include + +#include +#include + +MODULE_AUTHOR("Maciej Soltysiak "); +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 index 0000000000000..0dd7f498a5d79 --- /dev/null +++ b/net/ipv6/netfilter/ip6t_ipv6header.c @@ -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 */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +EXPORT_NO_SYMBOLS; +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("IPv6 headers match"); +MODULE_AUTHOR("Andras Kis-Szabo "); + +#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;templen;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 index 0000000000000..fd3bc32d79bcd --- /dev/null +++ b/net/ipv6/netfilter/ip6t_rt.c @@ -0,0 +1,305 @@ +/* Kernel module to match ROUTING parameters. */ +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +EXPORT_NO_SYMBOLS; +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("IPv6 RT match"); +MODULE_AUTHOR("Andras Kis-Szabo "); + +#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; tempaddrnr; 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); -- 2.53.0