netfilter: ebtables: add IPv6 support

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>
This commit is contained in:
Kuo-lang Tseng 2008-06-09 15:55:45 -07:00 committed by David S. Miller
parent 469689a4dd
commit 93f6515872
6 changed files with 243 additions and 16 deletions

View File

@ -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

View 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"

View 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

View 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

View File

@ -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");

View 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;
print_ports(skb, ih->protocol, ih->ihl*4);
goto out;
}
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));
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;
}