netfilter: ebtables: add IPv6 support
authorKuo-lang Tseng <kuo-lang.tseng@intel.com>
Mon, 9 Jun 2008 22:55:45 +0000 (15:55 -0700)
committerDavid S. Miller <davem@davemloft.net>
Mon, 9 Jun 2008 22:55:45 +0000 (15:55 -0700)
It implements matching functions for IPv6 address & traffic class
(merged from the patch sent by Jan Engelhardt [jengelh@computergmbh.de]
http://marc.info/?l=netfilter-devel&m=120182168424052&w=2), protocol,
and layer-4 port id. Corresponding watcher logging function is also
added for IPv6.

Signed-off-by: Kuo-lang Tseng <kuo-lang.tseng@intel.com>
Signed-off-by: Patrick McHardy <kaber@trash.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/linux/netfilter_bridge/ebt_ip6.h [new file with mode: 0644]
include/linux/netfilter_bridge/ebt_log.h
net/bridge/netfilter/Kconfig
net/bridge/netfilter/Makefile
net/bridge/netfilter/ebt_ip6.c [new file with mode: 0644]
net/bridge/netfilter/ebt_log.c

diff --git a/include/linux/netfilter_bridge/ebt_ip6.h b/include/linux/netfilter_bridge/ebt_ip6.h
new file mode 100644 (file)
index 0000000..2273c3a
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ *  ebt_ip6
+ *
+ *     Authors:
+ * Kuo-Lang Tseng <kuo-lang.tseng@intel.com>
+ * Manohar Castelino <manohar.r.castelino@intel.com>
+ *
+ *  Jan 11, 2008
+ *
+ */
+
+#ifndef __LINUX_BRIDGE_EBT_IP6_H
+#define __LINUX_BRIDGE_EBT_IP6_H
+
+#define EBT_IP6_SOURCE 0x01
+#define EBT_IP6_DEST 0x02
+#define EBT_IP6_TCLASS 0x04
+#define EBT_IP6_PROTO 0x08
+#define EBT_IP6_SPORT 0x10
+#define EBT_IP6_DPORT 0x20
+#define EBT_IP6_MASK (EBT_IP6_SOURCE | EBT_IP6_DEST | EBT_IP6_TCLASS |\
+                     EBT_IP6_PROTO | EBT_IP6_SPORT | EBT_IP6_DPORT)
+#define EBT_IP6_MATCH "ip6"
+
+/* the same values are used for the invflags */
+struct ebt_ip6_info
+{
+       struct in6_addr saddr;
+       struct in6_addr daddr;
+       struct in6_addr smsk;
+       struct in6_addr dmsk;
+       uint8_t  tclass;
+       uint8_t  protocol;
+       uint8_t  bitmask;
+       uint8_t  invflags;
+       uint16_t sport[2];
+       uint16_t dport[2];
+};
+
+#endif
index 96e231ae75548a3188b77b5708567cb99c75e0e4..b76e653157e56db9d3d2329b2294465736d9de68 100644 (file)
@@ -4,7 +4,8 @@
 #define EBT_LOG_IP 0x01 /* if the frame is made by ip, log the ip information */
 #define EBT_LOG_ARP 0x02
 #define EBT_LOG_NFLOG 0x04
-#define EBT_LOG_MASK (EBT_LOG_IP | EBT_LOG_ARP)
+#define EBT_LOG_IP6 0x08
+#define EBT_LOG_MASK (EBT_LOG_IP | EBT_LOG_ARP | EBT_LOG_IP6)
 #define EBT_LOG_PREFIX_SIZE 30
 #define EBT_LOG_WATCHER "log"
 
index 7beeefa0f9c05ffabef8dcbd3257330ed7ad649c..fb684c2ff8b60ab24f3d7c34c9777fdceb98d2f2 100644 (file)
@@ -83,6 +83,15 @@ config BRIDGE_EBT_IP
 
          To compile it as a module, choose M here.  If unsure, say N.
 
+config BRIDGE_EBT_IP6
+       tristate "ebt: IP6 filter support"
+       depends on BRIDGE_NF_EBTABLES
+       help
+         This option adds the IP6 match, which allows basic IPV6 header field
+         filtering.
+
+         To compile it as a module, choose M here.  If unsure, say N.
+
 config BRIDGE_EBT_LIMIT
        tristate "ebt: limit match support"
        depends on BRIDGE_NF_EBTABLES
index 83715d73a50352c33eb922e81974e769b84bbd1a..dd960645b4139ca1c8e0545ae75bf1a81fad7d8c 100644 (file)
@@ -14,6 +14,7 @@ obj-$(CONFIG_BRIDGE_EBT_802_3) += ebt_802_3.o
 obj-$(CONFIG_BRIDGE_EBT_AMONG) += ebt_among.o
 obj-$(CONFIG_BRIDGE_EBT_ARP) += ebt_arp.o
 obj-$(CONFIG_BRIDGE_EBT_IP) += ebt_ip.o
+obj-$(CONFIG_BRIDGE_EBT_IP) += ebt_ip6.o
 obj-$(CONFIG_BRIDGE_EBT_LIMIT) += ebt_limit.o
 obj-$(CONFIG_BRIDGE_EBT_MARK) += ebt_mark_m.o
 obj-$(CONFIG_BRIDGE_EBT_PKTTYPE) += ebt_pkttype.o
diff --git a/net/bridge/netfilter/ebt_ip6.c b/net/bridge/netfilter/ebt_ip6.c
new file mode 100644 (file)
index 0000000..36efb3a
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ *  ebt_ip6
+ *
+ *     Authors:
+ *     Manohar Castelino <manohar.r.castelino@intel.com>
+ *     Kuo-Lang Tseng <kuo-lang.tseng@intel.com>
+ *     Jan Engelhardt <jengelh@computergmbh.de>
+ *
+ * Summary:
+ * This is just a modification of the IPv4 code written by
+ * Bart De Schuymer <bdschuym@pandora.be>
+ * with the changes required to support IPv6
+ *
+ *  Jan, 2008
+ */
+
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/netfilter_bridge/ebt_ip6.h>
+#include <linux/ipv6.h>
+#include <net/ipv6.h>
+#include <linux/in.h>
+#include <linux/module.h>
+#include <net/dsfield.h>
+
+struct tcpudphdr {
+       __be16 src;
+       __be16 dst;
+};
+
+static int ebt_filter_ip6(const struct sk_buff *skb,
+   const struct net_device *in,
+   const struct net_device *out, const void *data,
+   unsigned int datalen)
+{
+       const struct ebt_ip6_info *info = (struct ebt_ip6_info *)data;
+       const struct ipv6hdr *ih6;
+       struct ipv6hdr _ip6h;
+       const struct tcpudphdr *pptr;
+       struct tcpudphdr _ports;
+       struct in6_addr tmp_addr;
+       int i;
+
+       ih6 = skb_header_pointer(skb, 0, sizeof(_ip6h), &_ip6h);
+       if (ih6 == NULL)
+               return EBT_NOMATCH;
+       if (info->bitmask & EBT_IP6_TCLASS &&
+          FWINV(info->tclass != ipv6_get_dsfield(ih6), EBT_IP6_TCLASS))
+               return EBT_NOMATCH;
+       for (i = 0; i < 4; i++)
+               tmp_addr.in6_u.u6_addr32[i] = ih6->saddr.in6_u.u6_addr32[i] &
+                       info->smsk.in6_u.u6_addr32[i];
+       if (info->bitmask & EBT_IP6_SOURCE &&
+               FWINV((ipv6_addr_cmp(&tmp_addr, &info->saddr) != 0),
+                       EBT_IP6_SOURCE))
+               return EBT_NOMATCH;
+       for (i = 0; i < 4; i++)
+               tmp_addr.in6_u.u6_addr32[i] = ih6->daddr.in6_u.u6_addr32[i] &
+                       info->dmsk.in6_u.u6_addr32[i];
+       if (info->bitmask & EBT_IP6_DEST &&
+          FWINV((ipv6_addr_cmp(&tmp_addr, &info->daddr) != 0), EBT_IP6_DEST))
+               return EBT_NOMATCH;
+       if (info->bitmask & EBT_IP6_PROTO) {
+               uint8_t nexthdr = ih6->nexthdr;
+               int offset_ph;
+
+               offset_ph = ipv6_skip_exthdr(skb, sizeof(_ip6h), &nexthdr);
+               if (offset_ph == -1)
+                       return EBT_NOMATCH;
+               if (FWINV(info->protocol != nexthdr, EBT_IP6_PROTO))
+                       return EBT_NOMATCH;
+               if (!(info->bitmask & EBT_IP6_DPORT) &&
+                   !(info->bitmask & EBT_IP6_SPORT))
+                       return EBT_MATCH;
+               pptr = skb_header_pointer(skb, offset_ph, sizeof(_ports),
+                                         &_ports);
+               if (pptr == NULL)
+                       return EBT_NOMATCH;
+               if (info->bitmask & EBT_IP6_DPORT) {
+                       u32 dst = ntohs(pptr->dst);
+                       if (FWINV(dst < info->dport[0] ||
+                                 dst > info->dport[1], EBT_IP6_DPORT))
+                               return EBT_NOMATCH;
+               }
+               if (info->bitmask & EBT_IP6_SPORT) {
+                       u32 src = ntohs(pptr->src);
+                       if (FWINV(src < info->sport[0] ||
+                                 src > info->sport[1], EBT_IP6_SPORT))
+                       return EBT_NOMATCH;
+               }
+               return EBT_MATCH;
+       }
+       return EBT_MATCH;
+}
+
+static int ebt_ip6_check(const char *tablename, unsigned int hookmask,
+   const struct ebt_entry *e, void *data, unsigned int datalen)
+{
+       struct ebt_ip6_info *info = (struct ebt_ip6_info *)data;
+
+       if (datalen != EBT_ALIGN(sizeof(struct ebt_ip6_info)))
+               return -EINVAL;
+       if (e->ethproto != htons(ETH_P_IPV6) || e->invflags & EBT_IPROTO)
+               return -EINVAL;
+       if (info->bitmask & ~EBT_IP6_MASK || info->invflags & ~EBT_IP6_MASK)
+               return -EINVAL;
+       if (info->bitmask & (EBT_IP6_DPORT | EBT_IP6_SPORT)) {
+               if (info->invflags & EBT_IP6_PROTO)
+                       return -EINVAL;
+               if (info->protocol != IPPROTO_TCP &&
+                   info->protocol != IPPROTO_UDP &&
+                   info->protocol != IPPROTO_UDPLITE &&
+                   info->protocol != IPPROTO_SCTP &&
+                   info->protocol != IPPROTO_DCCP)
+                        return -EINVAL;
+       }
+       if (info->bitmask & EBT_IP6_DPORT && info->dport[0] > info->dport[1])
+               return -EINVAL;
+       if (info->bitmask & EBT_IP6_SPORT && info->sport[0] > info->sport[1])
+               return -EINVAL;
+       return 0;
+}
+
+static struct ebt_match filter_ip6 =
+{
+       .name           = EBT_IP6_MATCH,
+       .match          = ebt_filter_ip6,
+       .check          = ebt_ip6_check,
+       .me             = THIS_MODULE,
+};
+
+static int __init ebt_ip6_init(void)
+{
+       return ebt_register_match(&filter_ip6);
+}
+
+static void __exit ebt_ip6_fini(void)
+{
+       ebt_unregister_match(&filter_ip6);
+}
+
+module_init(ebt_ip6_init);
+module_exit(ebt_ip6_fini);
+MODULE_DESCRIPTION("Ebtables: IPv6 protocol packet match");
+MODULE_LICENSE("GPL");
index 0b209e4aad0a0a76e53c9cbc7c89d950eda92af7..c883ec8a28b46c77673b1ada6316fd10ae983353 100644 (file)
@@ -18,6 +18,9 @@
 #include <linux/if_arp.h>
 #include <linux/spinlock.h>
 #include <net/netfilter/nf_log.h>
+#include <linux/ipv6.h>
+#include <net/ipv6.h>
+#include <linux/in6.h>
 
 static DEFINE_SPINLOCK(ebt_log_lock);
 
@@ -58,6 +61,27 @@ static void print_MAC(const unsigned char *p)
                printk("%02x%c", *p, i == ETH_ALEN - 1 ? ' ':':');
 }
 
+static void
+print_ports(const struct sk_buff *skb, uint8_t protocol, int offset)
+{
+       if (protocol == IPPROTO_TCP ||
+           protocol == IPPROTO_UDP ||
+           protocol == IPPROTO_UDPLITE ||
+           protocol == IPPROTO_SCTP ||
+           protocol == IPPROTO_DCCP) {
+               const struct tcpudphdr *pptr;
+               struct tcpudphdr _ports;
+
+               pptr = skb_header_pointer(skb, offset,
+                                         sizeof(_ports), &_ports);
+               if (pptr == NULL) {
+                       printk(" INCOMPLETE TCP/UDP header");
+                       return;
+               }
+               printk(" SPT=%u DPT=%u", ntohs(pptr->src), ntohs(pptr->dst));
+       }
+}
+
 #define myNIPQUAD(a) a[0], a[1], a[2], a[3]
 static void
 ebt_log_packet(unsigned int pf, unsigned int hooknum,
@@ -95,23 +119,31 @@ ebt_log_packet(unsigned int pf, unsigned int hooknum,
                printk(" IP SRC=%u.%u.%u.%u IP DST=%u.%u.%u.%u, IP "
                       "tos=0x%02X, IP proto=%d", NIPQUAD(ih->saddr),
                       NIPQUAD(ih->daddr), ih->tos, ih->protocol);
-               if (ih->protocol == IPPROTO_TCP ||
-                   ih->protocol == IPPROTO_UDP ||
-                   ih->protocol == IPPROTO_UDPLITE ||
-                   ih->protocol == IPPROTO_SCTP ||
-                   ih->protocol == IPPROTO_DCCP) {
-                       const struct tcpudphdr *pptr;
-                       struct tcpudphdr _ports;
-
-                       pptr = skb_header_pointer(skb, ih->ihl*4,
-                                                 sizeof(_ports), &_ports);
-                       if (pptr == NULL) {
-                               printk(" INCOMPLETE TCP/UDP header");
-                               goto out;
-                       }
-                       printk(" SPT=%u DPT=%u", ntohs(pptr->src),
-                          ntohs(pptr->dst));
+               print_ports(skb, ih->protocol, ih->ihl*4);
+               goto out;
+       }
+
+       if ((bitmask & EBT_LOG_IP6) && eth_hdr(skb)->h_proto ==
+          htons(ETH_P_IPV6)) {
+               const struct ipv6hdr *ih;
+               struct ipv6hdr _iph;
+               uint8_t nexthdr;
+               int offset_ph;
+
+               ih = skb_header_pointer(skb, 0, sizeof(_iph), &_iph);
+               if (ih == NULL) {
+                       printk(" INCOMPLETE IPv6 header");
+                       goto out;
                }
+               printk(" IPv6 SRC=%x:%x:%x:%x:%x:%x:%x:%x "
+                      "IPv6 DST=%x:%x:%x:%x:%x:%x:%x:%x, IPv6 "
+                      "priority=0x%01X, Next Header=%d", NIP6(ih->saddr),
+                      NIP6(ih->daddr), ih->priority, ih->nexthdr);
+               nexthdr = ih->nexthdr;
+               offset_ph = ipv6_skip_exthdr(skb, sizeof(_iph), &nexthdr);
+               if (offset_ph == -1)
+                       goto out;
+               print_ports(skb, nexthdr, offset_ph);
                goto out;
        }