Fazal,
I took your bridge-nf-0.0.6-against-2.4.17.diff and applied to kernel-2.4.9.
Seems to work. Thanks for your effort. However, I needed something else.
- Support for bridge filtering with IPv6 was missing, that is, to work with
ip6tables I hacked:
- include/linux/netfilter_ipv6.h: sabotage def. addition
- net/bridge/br_netfilter.c IPv6 support addition
- net/ipv6/netfilter/ip6_tables.c IPv6 support hooks addition
In the attached patch there are 3 diffs from these files just catenated.
It works in my kernel, so in case you want to patch them in, pse go ahead.
I ignored the NAT support and did not do all the details ready (cf. #if 0s).
The patch is not exactly against your patch but the actual files in linux.
BR,
-Jari
--- linux/include/linux/netfilter_ipv6.h.v4orig Sun Mar 24 01:18:05 2002
+++ linux/include/linux/netfilter_ipv6.h Sun Mar 24 01:19:27 2002
@@ -59,6 +59,7 @@
NF_IP6_PRI_CONNTRACK = -200,
NF_IP6_PRI_MANGLE = -150,
NF_IP6_PRI_NAT_DST = -100,
+ NF_IP6_PRI_BRIDGE_SABOTAGE = -50,
NF_IP6_PRI_FILTER = 0,
NF_IP6_PRI_NAT_SRC = 100,
NF_IP6_PRI_LAST = INT_MAX,
--- linux/net/bridge/br_netfilter.c.v4orig Sat Mar 23 00:14:16 2002
+++ linux/net/bridge/br_netfilter.c Sun Mar 24 01:09:57 2002
@@ -5,6 +5,7 @@
* Authors:
* Lennert Buytenhek <[EMAIL PROTECTED]>
* Bart De Schuymer <[EMAIL PROTECTED]>
+ * Jari T. Malinen (IPv6 support) <[EMAIL PROTECTED]>
*
* $Id: bridge-nf-0.0.6-against-2.4.18.diff,v 1.2 2002/02/26 04:11:56 buytenh Exp
$
*
@@ -17,19 +18,23 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/ip.h>
+#include <linux/ipv6.h>
#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include <linux/if_ether.h>
#include <linux/netfilter_bridge.h>
#include <linux/netfilter_ipv4.h>
+#include <linux/netfilter_ipv6.h>
#include <linux/in_route.h>
+#include <linux/ipv6_route.h>
#include <net/ip.h>
+#include <net/ipv6.h>
#include <net/tcp.h>
#include <asm/uaccess.h>
#include <asm/checksum.h>
#include "br_private.h"
-
+#define WE_REALLY_INSIST_ON_NOT_HAVING_NAT_SUPPORT
#ifndef WE_REALLY_INSIST_ON_NOT_HAVING_NAT_SUPPORT
/* As the original source/destination addresses are variables private to this
@@ -46,11 +51,22 @@
#define store_orig_srcaddr(skb) (skb_origaddr(skb) =
(skb)->nh.iph->saddr)
#define dnat_took_place(skb) (skb_origaddr(skb) != (skb)->nh.iph->daddr)
#define snat_took_place(skb) (skb_origaddr(skb) != (skb)->nh.iph->saddr)
+#define skb_origaddr6(skb) (*((struct in6_addr*)((skb)->cb +
+sizeof((skb)->cb)))) /* - 16 XXX doesn't fit */
+#define store_orig_dstaddr6(skb) (skb_origaddr6(skb) = (skb)->nh.ipv6h->daddr)
+#define store_orig_srcaddr6(skb) (skb_origaddr6(skb) = (skb)->nh.ipv6h->saddr)
+#define dnat6_took_place(skb) (skb_origaddr6(skb) != (skb)->nh.ipv6h->daddr)
+#define snat6_took_place(skb) (skb_origaddr6(skb) != (skb)->nh.ipv6h->saddr)
+
#else
#define store_orig_dstaddr(skb)
#define store_orig_srcaddr(skb)
#define dnat_took_place(skb) (0)
#define snat_took_place(skb) (0)
+#define store_orig_dstaddr6(skb)
+#define store_orig_srcaddr6(skb)
+#define dnat6_took_place(skb) (0)
+#define snat6_took_place(skb) (0)
+
#endif
@@ -58,6 +74,7 @@
#define bridge_parent(device) (&((device)->br_port->br->dev))
+/* IPv4 */
static inline void __maybe_fixup_src_address(struct sk_buff *skb)
{
if (snat_took_place(skb) &&
@@ -68,6 +85,19 @@
}
}
+/* IPv6 XXX no NAT */
+static inline void __maybe_fixup_src6_address(struct sk_buff *skb)
+{
+#if 0
+ if (snat_took_place(skb) &&
+ inet6_addr_type(skb->nh.ipiv6h->saddr) == RTF_LOCAL) { /* XXX */
+ memcpy(skb->mac.ethernet->h_source,
+ bridge_parent(skb->dev)->dev_addr,
+ ETH_ALEN);
+ }
+#endif
+}
+
static struct net_device __fake_net_device = {
hard_header_len: ETH_HLEN
};
@@ -84,7 +114,9 @@
rt_flags: 0
};
-
+/*
+ * ########## IPv4 ###########>
+ */
/* PF_BRIDGE/PRE_ROUTING *********************************************/
static int br_nf_pre_routing_finish(struct sk_buff *skb)
{
@@ -301,6 +333,231 @@
}
+/*
+ * ########## IPv6 ###########>
+ */
+/* PF_BRIDGE/PRE_ROUTING *********************************************/
+static int br_nf6_pre_routing_finish(struct sk_buff *skb)
+{
+#if 0
+/* We dont't care about NAT.. */
+ struct net_device *dev = skb->dev;
+ struct ipv6hdr *iph = skb->nh.ipv6h;
+
+ if (dnat_took_place(skb)) {
+ if (ip6_route_input(skb)) {
+ struct rtable *rt;
+
+ if (ip6_route_output(foo,bar)) { /* XXX */
+ /* This test is racy. */
+ if (!ip6_route_output(foo,bar))
+ BUG();
+
+ kfree_skb(skb);
+ return 0;
+ }
+
+ skb->dst = (struct dst_entry *)rt;
+
+ /* This changes source ethernet address. */
+ skb->dst->output(skb);
+ return 0;
+ } else {
+ memcpy(skb->mac.ethernet->h_dest, dev->dev_addr, ETH_ALEN);
+ }
+ } else {
+#endif
+ skb->dst = (struct dst_entry *)&__fake_rtable;
+ dst_hold(skb->dst);
+#if 0
+ }
+#endif
+ skb->dev = skb->physindev;
+ NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
+ br_handle_frame_finish, 1);
+
+ return 0;
+}
+
+static unsigned int br_nf6_pre_routing(unsigned int hook, struct sk_buff **pskb,
+const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff
+*))
+{
+ struct ipv6hdr *iph;
+ __u32 len;
+ struct sk_buff *skb;
+
+ if ((*pskb)->mac.ethernet->h_proto != __constant_htons(ETH_P_IPV6))
+ return NF_ACCEPT;
+
+ if ((skb = skb_share_check(*pskb, GFP_ATOMIC)) == NULL)
+ goto out;
+
+ if (!pskb_may_pull(skb, sizeof(struct ipv6hdr)))
+ goto inhdr_error;
+
+ iph = skb->nh.ipv6h;
+ len = skb->len = ntohs(iph->payload_len) + 40;
+
+ if (iph->priority < 0 || iph->version != 6)
+ goto inhdr_error;
+
+ if (!pskb_may_pull(skb, 4*iph->priority))
+ goto inhdr_error;
+
+ iph = skb->nh.ipv6h;
+#if 0
+ if (ipv6_fast_csum((__u8 *)iph, iph->priority) != 0)
+ goto inhdr_error;
+#endif
+ if (skb->len < len || len < 4*iph->priority)
+ goto inhdr_error;
+
+ if (skb->len > len) {
+ __pskb_trim(skb, len);
+ if (skb->ip_summed == CHECKSUM_HW)
+ skb->ip_summed = CHECKSUM_NONE;
+ }
+
+ skb->physindev = skb->dev;
+ skb->dev = bridge_parent(skb->dev);
+ if (skb->pkt_type == PACKET_OTHERHOST)
+ skb->pkt_type = PACKET_HOST;
+ store_orig_dstaddr(skb);
+ NF_HOOK(PF_INET6, NF_IP6_PRE_ROUTING, skb, skb->dev, NULL,
+ br_nf6_pre_routing_finish);
+
+ return NF_STOLEN;
+
+inhdr_error:
+// IP_INC_STATS_BH(IpInHdrErrors);
+out:
+ return NF_DROP;
+}
+
+
+/* PF_BRIDGE/LOCAL_IN ************************************************/
+static unsigned int br_nf6_local_in(unsigned int hook, struct sk_buff **pskb, const
+struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *))
+{
+ struct sk_buff *skb = *pskb;
+
+ if (skb->mac.ethernet->h_proto != __constant_htons(ETH_P_IPV6))
+ return NF_ACCEPT;
+
+ if (skb->dst == (struct dst_entry *)&__fake_rtable) {
+ dst_release(skb->dst);
+ skb->dst = NULL;
+ }
+
+ return NF_ACCEPT;
+}
+
+
+/* PF_BRIDGE/FORWARD *************************************************/
+static int br_nf6_forward_finish(struct sk_buff *skb)
+{
+ NF_HOOK_THRESH(PF_BRIDGE, NF_BR_FORWARD, skb, skb->physindev,
+ skb->dev, br_forward_finish, 1);
+
+ return 0;
+}
+
+static unsigned int br_nf6_forward(unsigned int hook, struct sk_buff **pskb, const
+struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *))
+{
+ struct sk_buff *skb = *pskb;
+
+ if (skb->mac.ethernet->h_proto != __constant_htons(ETH_P_IPV6))
+ return NF_ACCEPT;
+
+ skb->physoutdev = skb->dev;
+ NF_HOOK(PF_INET6, NF_IP6_FORWARD, skb, bridge_parent(skb->physindev),
+ bridge_parent(skb->dev), br_nf6_forward_finish);
+
+ return NF_STOLEN;
+}
+
+
+/* PF_BRIDGE/LOCAL_OUT ***********************************************/
+static int br_nf6_local_out_finish_forward(struct sk_buff *skb)
+{
+ NF_HOOK_THRESH(PF_BRIDGE, NF_BR_FORWARD, skb, skb->physindev,
+ skb->dev, br_forward_finish, 1);
+
+ return 0;
+}
+
+static int br_nf6_local_out_finish(struct sk_buff *skb)
+{
+ NF_HOOK_THRESH(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
+ br_forward_finish, 1);
+
+ return 0;
+}
+
+static unsigned int br_nf6_local_out(unsigned int hook, struct sk_buff **pskb, const
+struct net_device *in, const struct net_device *out, int (*_okfn)(struct sk_buff *))
+{
+ int hookno;
+ int (*okfn)(struct sk_buff *);
+ struct net_device *realindev;
+ struct sk_buff *skb = *pskb;
+
+ if (skb->mac.ethernet->h_proto != __constant_htons(ETH_P_IPV6))
+ return NF_ACCEPT;
+
+ /* Sometimes we get packets with NULL ->dst here (for example,
+ * running a dhcp client daemon triggers this).
+ */
+ if (skb->dst == NULL)
+ return NF_ACCEPT;
+
+ skb->physoutdev = skb->dev;
+
+ hookno = NF_IP6_LOCAL_OUT;
+ okfn = br_nf6_local_out_finish;
+ if ((realindev = skb->physindev) != NULL) {
+ hookno = NF_IP6_FORWARD;
+ okfn = br_nf6_local_out_finish_forward;
+ if (has_bridge_parent(realindev))
+ realindev = bridge_parent(realindev);
+ }
+
+ NF_HOOK_THRESH(PF_INET6, hookno, skb, realindev,
+ bridge_parent(skb->dev), okfn,
+ NF_IP6_PRI_BRIDGE_SABOTAGE + 1);
+
+ return NF_STOLEN;
+}
+
+
+/* PF_BRIDGE/POST_ROUTING ********************************************/
+static int br_nf6_post_routing_finish(struct sk_buff *skb)
+{
+ __maybe_fixup_src6_address(skb);
+ NF_HOOK_THRESH(PF_BRIDGE, NF_BR_POST_ROUTING, skb, NULL,
+ bridge_parent(skb->dev), br_dev_queue_push_xmit, 1);
+
+ return 0;
+}
+
+static unsigned int br_nf6_post_routing(unsigned int hook, struct sk_buff **pskb,
+const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff
+*))
+{
+ struct sk_buff *skb = *pskb;
+
+ if (skb->mac.ethernet->h_proto != __constant_htons(ETH_P_IPV6))
+ return NF_ACCEPT;
+
+ /* Sometimes we get packets with NULL ->dst here (for example,
+ * running a dhcp client daemon triggers this).
+ */
+ if (skb->dst == NULL)
+ return NF_ACCEPT;
+
+ store_orig_srcaddr(skb);
+ NF_HOOK(PF_INET6, NF_IP6_POST_ROUTING, skb, NULL,
+ bridge_parent(skb->dev), br_nf6_post_routing_finish);
+
+ return NF_STOLEN;
+}
+
+
/* IPv4/SABOTAGE *****************************************************/
static unsigned int ipv4_sabotage_in(unsigned int hook, struct sk_buff **pskb, const
struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *))
{
@@ -336,8 +593,44 @@
return NF_ACCEPT;
}
+/* IPv6/SABOTAGE *****************************************************/
+static unsigned int ipv6_sabotage_in(unsigned int hook, struct sk_buff **pskb, const
+struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *))
+{
+ /* Don't hand locally destined packets to PF_INET6/PRE_ROUTING for
+ * the second time. */
+ if (in->hard_start_xmit == br_dev_xmit &&
+ okfn != br_nf6_pre_routing_finish) {
+ okfn(*pskb);
+ return NF_STOLEN;
+ }
+
+ return NF_ACCEPT;
+}
+
+static unsigned int ipv6_sabotage_out(unsigned int hook, struct sk_buff **pskb, const
+struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *))
+{
+ /* Postpone execution of PF_INET6/FORWARD, PF_INET6/LOCAL_OUT and
+ * PF_INET6/POST_ROUTING until we have done the forwarding decision in
+ * the bridge code and have determined skb->physoutdev. */
+ if (out->hard_start_xmit == br_dev_xmit &&
+ okfn != br_nf6_forward_finish &&
+ okfn != br_nf6_local_out_finish &&
+ okfn != br_nf6_local_out_finish_forward &&
+ okfn != br_nf6_post_routing_finish) {
+ struct sk_buff *skb = *pskb;
+
+ if (hook == NF_IP6_FORWARD && skb->physindev == NULL)
+ skb->physindev = (struct net_device *)in;
+ okfn(skb);
+ return NF_STOLEN;
+ }
+
+ return NF_ACCEPT;
+}
+
static struct nf_hook_ops br_nf_ops[] = {
+ /* IPv4 */
{ { NULL, NULL }, br_nf_pre_routing, PF_BRIDGE, NF_BR_PRE_ROUTING, 0 },
{ { NULL, NULL }, br_nf_local_in, PF_BRIDGE, NF_BR_LOCAL_IN, 0 },
{ { NULL, NULL }, br_nf_forward, PF_BRIDGE, NF_BR_FORWARD, 0 },
@@ -349,6 +642,17 @@
{ { NULL, NULL }, ipv4_sabotage_out, PF_INET, NF_IP_FORWARD,
NF_IP_PRI_BRIDGE_SABOTAGE },
{ { NULL, NULL }, ipv4_sabotage_out, PF_INET, NF_IP_LOCAL_OUT,
NF_IP_PRI_BRIDGE_SABOTAGE },
{ { NULL, NULL }, ipv4_sabotage_out, PF_INET, NF_IP_POST_ROUTING,
NF_IP_PRI_FIRST },
+ /* IPv6 */
+ { { NULL, NULL }, br_nf6_pre_routing, PF_BRIDGE, NF_BR_PRE_ROUTING, 0 },
+ { { NULL, NULL }, br_nf6_local_in, PF_BRIDGE, NF_BR_LOCAL_IN, 0 },
+ { { NULL, NULL }, br_nf6_forward, PF_BRIDGE, NF_BR_FORWARD, 0 },
+ { { NULL, NULL }, br_nf6_local_out, PF_BRIDGE, NF_BR_LOCAL_OUT, 0 },
+ { { NULL, NULL }, br_nf6_post_routing, PF_BRIDGE, NF_BR_POST_ROUTING, 0 },
+
+ { { NULL, NULL }, ipv6_sabotage_in, PF_INET6, NF_IP6_PRE_ROUTING,
+NF_IP6_PRI_FIRST },
+ { { NULL, NULL }, ipv6_sabotage_out, PF_INET6, NF_IP6_FORWARD,
+NF_IP6_PRI_BRIDGE_SABOTAGE },
+ { { NULL, NULL }, ipv6_sabotage_out, PF_INET6, NF_IP6_LOCAL_OUT,
+NF_IP6_PRI_BRIDGE_SABOTAGE },
+ { { NULL, NULL }, ipv6_sabotage_out, PF_INET6, NF_IP6_POST_ROUTING,
+NF_IP6_PRI_FIRST },
};
#define NUMHOOKS (sizeof(br_nf_ops)/sizeof(br_nf_ops[0]))
--- linux/net/ipv6/netfilter/ip6_tables.c.v4orig Thu Oct 11 00:10:34 2001
+++ linux/net/ipv6/netfilter/ip6_tables.c Sun Mar 24 01:00:23 2002
@@ -2,6 +2,7 @@
* Packet matching code.
*
* Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
+ * bridge hooks: Jari T. Malinen
*/
#include <linux/config.h>
#include <linux/skbuff.h>
@@ -164,12 +165,15 @@
static inline int
ip6_packet_match(const struct ipv6hdr *ipv6,
const char *indev,
+ const char *physindev,
const char *outdev,
+ const char *physoutdev,
const struct ip6t_ip6 *ip6info,
int isfrag)
{
size_t i;
unsigned long ret;
+ unsigned long ret2;
#define FWINV(bool,invflg) ((bool) ^ !!(ip6info->invflags & invflg))
@@ -195,7 +199,13 @@
& ((const unsigned long *)ip6info->iniface_mask)[i];
}
- if (FWINV(ret != 0, IP6T_INV_VIA_IN)) {
+ for (i = 0, ret2 = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
+ ret2 |= (((const unsigned long *)physindev)[i]
+ ^ ((const unsigned long *)ip6info->iniface)[i])
+ & ((const unsigned long *)ip6info->iniface_mask)[i];
+ }
+
+ if (FWINV(ret != 0 && ret2 != 0, IP6T_INV_VIA_IN)) {
dprintf("VIA in mismatch (%s vs %s).%s\n",
indev, ip6info->iniface,
ip6info->invflags&IP6T_INV_VIA_IN ?" (INV)":"");
@@ -208,7 +218,13 @@
& ((const unsigned long *)ip6info->outiface_mask)[i];
}
- if (FWINV(ret != 0, IP6T_INV_VIA_OUT)) {
+ for (i = 0, ret2 = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
+ ret2 |= (((const unsigned long *)physoutdev)[i]
+ ^ ((const unsigned long *)ip6info->outiface)[i])
+ & ((const unsigned long *)ip6info->outiface_mask)[i];
+ }
+
+ if (FWINV(ret != 0 && ret2 != 0, IP6T_INV_VIA_OUT)) {
dprintf("VIA out mismatch (%s vs %s).%s\n",
outdev, ip6info->outiface,
ip6info->invflags&IP6T_INV_VIA_OUT ?" (INV)":"");
@@ -309,6 +325,7 @@
/* Initializing verdict to NF_DROP keeps gcc happy. */
unsigned int verdict = NF_DROP;
const char *indev, *outdev;
+ const char *physindev, *physoutdev;
void *table_base;
struct ip6t_entry *e, *back;
@@ -318,6 +335,7 @@
datalen = (*pskb)->len - IPV6_HDR_LEN;
indev = in ? in->name : nulldevname;
outdev = out ? out->name : nulldevname;
+ physindev = (*pskb)->physindev ? (*pskb)->physindev->name : nulldevname;
+ physoutdev = (*pskb)->physoutdev ? (*pskb)->physoutdev->name : nulldevname;
/* We handle fragments by dealing with the first fragment as
* if it was a normal packet. All other fragments are treated
@@ -352,7 +370,7 @@
IP_NF_ASSERT(e);
IP_NF_ASSERT(back);
(*pskb)->nfcache |= e->nfcache;
- if (ip6_packet_match(ipv6, indev, outdev, &e->ipv6, offset)) {
+ if (ip6_packet_match(ipv6, indev, physindev, outdev, physoutdev,
+&e->ipv6, offset)) {
struct ip6t_entry_target *t;
if (IP6T_MATCH_ITERATE(e, do_match,