Commit 02879e96 authored by Bart De Schuymer's avatar Bart De Schuymer Committed by David S. Miller

[BRIDGE]: Add ipv6 packet filtering.

Signed-off-by: default avatarBart De Schuymer <bdschuym@pandora.be>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 21366d93
......@@ -837,6 +837,11 @@ bridge-nf-call-iptables - BOOLEAN
0 : disable this.
Default: 1
bridge-nf-call-ip6tables - BOOLEAN
1 : pass bridged IPv6 traffic to ip6tables' chains.
0 : disable this.
Default: 1
bridge-nf-filter-vlan-tagged - BOOLEAN
1 : pass bridged vlan-tagged ARP/IP traffic to arptables/iptables.
0 : disable this.
......
......@@ -58,8 +58,10 @@ enum nf_ip6_hook_priorities {
NF_IP6_PRI_FIRST = INT_MIN,
NF_IP6_PRI_SELINUX_FIRST = -225,
NF_IP6_PRI_CONNTRACK = -200,
NF_IP6_PRI_BRIDGE_SABOTAGE_FORWARD = -175,
NF_IP6_PRI_MANGLE = -150,
NF_IP6_PRI_NAT_DST = -100,
NF_IP6_PRI_BRIDGE_SABOTAGE_LOCAL_OUT = -50,
NF_IP6_PRI_FILTER = 0,
NF_IP6_PRI_NAT_SRC = 100,
NF_IP6_PRI_SELINUX_LAST = 225,
......
......@@ -11,6 +11,7 @@
* Jun 19 2003: let arptables see bridged ARP traffic (bdschuym)
* Oct 06 2003: filter encapsulated IP/ARP VLAN traffic on untagged bridge
* (bdschuym)
* Sep 01 2004: add IPv6 filtering (bdschuym)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
......@@ -29,9 +30,11 @@
#include <linux/if_vlan.h>
#include <linux/netfilter_bridge.h>
#include <linux/netfilter_ipv4.h>
#include <linux/netfilter_ipv6.h>
#include <linux/netfilter_arp.h>
#include <linux/in_route.h>
#include <net/ip.h>
#include <net/ipv6.h>
#include <asm/uaccess.h>
#include <asm/checksum.h>
#include "br_private.h"
......@@ -39,7 +42,6 @@
#include <linux/sysctl.h>
#endif
#define skb_origaddr(skb) (((struct bridge_skb_cb *) \
(skb->nf_bridge->data))->daddr.ipv4)
#define store_orig_dstaddr(skb) (skb_origaddr(skb) = (skb)->nh.iph->daddr)
......@@ -51,6 +53,7 @@
#ifdef CONFIG_SYSCTL
static struct ctl_table_header *brnf_sysctl_header;
static int brnf_call_iptables = 1;
static int brnf_call_ip6tables = 1;
static int brnf_call_arptables = 1;
static int brnf_filter_vlan_tagged = 1;
#else
......@@ -60,6 +63,9 @@ static int brnf_filter_vlan_tagged = 1;
#define IS_VLAN_IP (skb->protocol == __constant_htons(ETH_P_8021Q) && \
hdr->h_vlan_encapsulated_proto == __constant_htons(ETH_P_IP) && \
brnf_filter_vlan_tagged)
#define IS_VLAN_IPV6 (skb->protocol == __constant_htons(ETH_P_8021Q) && \
hdr->h_vlan_encapsulated_proto == __constant_htons(ETH_P_IPV6) && \
brnf_filter_vlan_tagged)
#define IS_VLAN_ARP (skb->protocol == __constant_htons(ETH_P_8021Q) && \
hdr->h_vlan_encapsulated_proto == __constant_htons(ETH_P_ARP) && \
brnf_filter_vlan_tagged)
......@@ -71,8 +77,7 @@ static int brnf_filter_vlan_tagged = 1;
* Currently, we fill in the PMTU entry because netfilter
* refragmentation needs it, and the rt_flags entry because
* ipt_REJECT needs it. Future netfilter modules might
* require us to fill additional fields.
*/
* require us to fill additional fields. */
static struct net_device __fake_net_device = {
.hard_header_len = ETH_HLEN
};
......@@ -91,6 +96,36 @@ static struct rtable __fake_rtable = {
/* PF_BRIDGE/PRE_ROUTING *********************************************/
/* Undo the changes made for ip6tables PREROUTING and continue the
* bridge PRE_ROUTING hook. */
static int br_nf_pre_routing_finish_ipv6(struct sk_buff *skb)
{
struct nf_bridge_info *nf_bridge = skb->nf_bridge;
#ifdef CONFIG_NETFILTER_DEBUG
skb->nf_debug ^= (1 << NF_BR_PRE_ROUTING);
#endif
if (nf_bridge->mask & BRNF_PKT_TYPE) {
skb->pkt_type = PACKET_OTHERHOST;
nf_bridge->mask ^= BRNF_PKT_TYPE;
}
nf_bridge->mask ^= BRNF_NF_BRIDGE_PREROUTING;
skb->dst = (struct dst_entry *)&__fake_rtable;
dst_hold(skb->dst);
skb->dev = nf_bridge->physindev;
if (skb->protocol == __constant_htons(ETH_P_8021Q)) {
skb_push(skb, VLAN_HLEN);
skb->nh.raw -= VLAN_HLEN;
}
NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
br_handle_frame_finish, 1);
return 0;
}
static void __br_dnat_complain(void)
{
static unsigned long last_complaint;
......@@ -102,7 +137,6 @@ static void __br_dnat_complain(void)
}
}
/* This requires some explaining. If DNAT has taken place,
* we will need to fix up the destination Ethernet address,
* and this is a tricky process.
......@@ -145,9 +179,7 @@ static void __br_dnat_complain(void)
*
* --Lennert, 20020411
* --Bart, 20020416 (updated)
* --Bart, 20021007 (updated)
*/
* --Bart, 20021007 (updated) */
static int br_nf_pre_routing_finish_bridge(struct sk_buff *skb)
{
#ifdef CONFIG_NETFILTER_DEBUG
......@@ -195,8 +227,7 @@ static int br_nf_pre_routing_finish(struct sk_buff *skb)
if (!ip_route_output_key(&rt, &fl)) {
/* Bridged-and-DNAT'ed traffic doesn't
* require ip_forwarding.
*/
* require ip_forwarding. */
if (((struct dst_entry *)rt)->dev == dev) {
skb->dst = (struct dst_entry *)rt;
goto bridged_dnat;
......@@ -210,8 +241,7 @@ static int br_nf_pre_routing_finish(struct sk_buff *skb)
if (skb->dst->dev == dev) {
bridged_dnat:
/* Tell br_nf_local_out this is a
* bridged frame
*/
* bridged frame */
nf_bridge->mask |= BRNF_BRIDGED_DNAT;
skb->dev = nf_bridge->physindev;
if (skb->protocol ==
......@@ -245,12 +275,81 @@ static int br_nf_pre_routing_finish(struct sk_buff *skb)
return 0;
}
/* Replicate the checks that IPv4 does on packet reception.
/* Some common code for IPv4/IPv6 */
static void setup_pre_routing(struct sk_buff *skb)
{
struct nf_bridge_info *nf_bridge = skb->nf_bridge;
if (skb->pkt_type == PACKET_OTHERHOST) {
skb->pkt_type = PACKET_HOST;
nf_bridge->mask |= BRNF_PKT_TYPE;
}
nf_bridge->mask |= BRNF_NF_BRIDGE_PREROUTING;
nf_bridge->physindev = skb->dev;
skb->dev = bridge_parent(skb->dev);
}
/* Replicate the checks that IPv6 does on packet reception and pass the packet
* to ip6tables, which doesn't support NAT, so things are fairly simple. */
static unsigned int br_nf_pre_routing_ipv6(unsigned int hook,
struct sk_buff *skb, const struct net_device *in,
const struct net_device *out, int (*okfn)(struct sk_buff *))
{
struct ipv6hdr *hdr;
u32 pkt_len;
struct nf_bridge_info *nf_bridge;
if (skb->len < sizeof(struct ipv6hdr))
goto inhdr_error;
if (!pskb_may_pull(skb, sizeof(struct ipv6hdr)))
goto inhdr_error;
hdr = skb->nh.ipv6h;
if (hdr->version != 6)
goto inhdr_error;
pkt_len = ntohs(hdr->payload_len);
if (pkt_len || hdr->nexthdr != NEXTHDR_HOP) {
if (pkt_len + sizeof(struct ipv6hdr) > skb->len)
goto inhdr_error;
if (pkt_len + sizeof(struct ipv6hdr) < skb->len &&
__pskb_trim(skb, pkt_len + sizeof(struct ipv6hdr)))
goto inhdr_error;
}
if (hdr->nexthdr == NEXTHDR_HOP) {
/* We only check the length. A bridge shouldn't do any
* hop-by-hop stuff anyway. */
unsigned char *raw = (u8*)(skb->nh.ipv6h+1);
if ((raw + ((int)(raw[1]+1)<<3)) - skb->data > skb_headlen(skb))
goto inhdr_error;
}
#ifdef CONFIG_NETFILTER_DEBUG
skb->nf_debug ^= (1 << NF_IP6_PRE_ROUTING);
#endif
if ((nf_bridge = nf_bridge_alloc(skb)) == NULL)
return NF_DROP;
setup_pre_routing(skb);
NF_HOOK(PF_INET6, NF_IP6_PRE_ROUTING, skb, skb->dev, NULL,
br_nf_pre_routing_finish_ipv6);
return NF_STOLEN;
inhdr_error:
return NF_DROP;
}
/* Direct IPv6 traffic to br_nf_pre_routing_ipv6.
* Replicate the checks that IPv4 does on packet reception.
* Set skb->dev to the bridge device (i.e. parent of the
* receiving device) to make netfilter happy, the REDIRECT
* target in particular. Save the original destination IP
* address to be able to detect DNAT afterwards.
*/
* address to be able to detect DNAT afterwards. */
static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff **pskb,
const struct net_device *in, const struct net_device *out,
int (*okfn)(struct sk_buff *))
......@@ -259,25 +358,39 @@ static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff **pskb,
__u32 len;
struct sk_buff *skb = *pskb;
struct nf_bridge_info *nf_bridge;
struct vlan_ethhdr *hdr = (struct vlan_ethhdr *)
((*pskb)->mac.ethernet);
if (skb->protocol == __constant_htons(ETH_P_IPV6) || IS_VLAN_IPV6) {
#ifdef CONFIG_SYSCTL
if (!brnf_call_ip6tables)
return NF_ACCEPT;
#endif
if ((skb = skb_share_check(*pskb, GFP_ATOMIC)) == NULL)
goto out;
if (skb->protocol == __constant_htons(ETH_P_8021Q)) {
skb_pull(skb, VLAN_HLEN);
(skb)->nh.raw += VLAN_HLEN;
}
return br_nf_pre_routing_ipv6(hook, skb, in, out, okfn);
}
#ifdef CONFIG_SYSCTL
if (!brnf_call_iptables)
return NF_ACCEPT;
#endif
if (skb->protocol != __constant_htons(ETH_P_IP)) {
struct vlan_ethhdr *hdr = (struct vlan_ethhdr *)
((*pskb)->mac.ethernet);
if (skb->protocol != __constant_htons(ETH_P_IP) && !IS_VLAN_IP)
return NF_ACCEPT;
if (!IS_VLAN_IP)
return NF_ACCEPT;
if ((skb = skb_share_check(*pskb, GFP_ATOMIC)) == NULL)
goto out;
skb_pull(*pskb, VLAN_HLEN);
(*pskb)->nh.raw += VLAN_HLEN;
} else if ((skb = skb_share_check(*pskb, GFP_ATOMIC)) == NULL)
if ((skb = skb_share_check(*pskb, GFP_ATOMIC)) == NULL)
goto out;
if (skb->protocol == __constant_htons(ETH_P_8021Q)) {
skb_pull(skb, VLAN_HLEN);
(skb)->nh.raw += VLAN_HLEN;
}
if (!pskb_may_pull(skb, sizeof(struct iphdr)))
goto inhdr_error;
......@@ -305,17 +418,9 @@ static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff **pskb,
#ifdef CONFIG_NETFILTER_DEBUG
skb->nf_debug ^= (1 << NF_IP_PRE_ROUTING);
#endif
if ((nf_bridge = nf_bridge_alloc(skb)) == NULL)
if ((nf_bridge = nf_bridge_alloc(skb)) == NULL)
return NF_DROP;
if (skb->pkt_type == PACKET_OTHERHOST) {
skb->pkt_type = PACKET_HOST;
nf_bridge->mask |= BRNF_PKT_TYPE;
}
nf_bridge->mask |= BRNF_NF_BRIDGE_PREROUTING;
nf_bridge->physindev = skb->dev;
skb->dev = bridge_parent(skb->dev);
setup_pre_routing(skb);
store_orig_dstaddr(skb);
NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, skb->dev, NULL,
......@@ -336,8 +441,7 @@ static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff **pskb,
* packet would pass through PRE_ROUTING again (which already
* took place when the packet entered the bridge), but we
* register an IPv4 PRE_ROUTING 'sabotage' hook that will
* prevent this from happening.
*/
* prevent this from happening. */
static unsigned int br_nf_local_in(unsigned int hook, struct sk_buff **pskb,
const struct net_device *in, const struct net_device *out,
int (*okfn)(struct sk_buff *))
......@@ -364,7 +468,7 @@ static int br_nf_forward_finish(struct sk_buff *skb)
skb->nf_debug ^= (1 << NF_BR_FORWARD);
#endif
if (skb->protocol == __constant_htons(ETH_P_IP) || IS_VLAN_IP) {
if (skb->protocol != __constant_htons(ETH_P_ARP) && !IS_VLAN_ARP) {
in = nf_bridge->physindev;
if (nf_bridge->mask & BRNF_PKT_TYPE) {
skb->pkt_type = PACKET_OTHERHOST;
......@@ -385,9 +489,8 @@ static int br_nf_forward_finish(struct sk_buff *skb)
/* This is the 'purely bridged' case. For IP, we pass the packet to
* netfilter with indev and outdev set to the bridge device,
* but we are still able to filter on the 'real' indev/outdev
* because of the ipt_physdev.c module. For ARP, indev and outdev are the
* bridge ports.
*/
* because of the physdev module. For ARP, indev and outdev are the
* bridge ports. */
static unsigned int br_nf_forward_ip(unsigned int hook, struct sk_buff **pskb,
const struct net_device *in, const struct net_device *out,
int (*okfn)(struct sk_buff *))
......@@ -395,15 +498,17 @@ static unsigned int br_nf_forward_ip(unsigned int hook, struct sk_buff **pskb,
struct sk_buff *skb = *pskb;
struct nf_bridge_info *nf_bridge;
struct vlan_ethhdr *hdr = (struct vlan_ethhdr *)(skb->mac.ethernet);
int pf;
#ifdef CONFIG_SYSCTL
if (!skb->nf_bridge)
return NF_ACCEPT;
#endif
if (skb->protocol != __constant_htons(ETH_P_IP)) {
if (!IS_VLAN_IP)
return NF_ACCEPT;
if (skb->protocol == __constant_htons(ETH_P_IP) || IS_VLAN_IP)
pf = PF_INET;
else
pf = PF_INET6;
if (skb->protocol == __constant_htons(ETH_P_8021Q)) {
skb_pull(*pskb, VLAN_HLEN);
(*pskb)->nh.raw += VLAN_HLEN;
}
......@@ -421,7 +526,7 @@ static unsigned int br_nf_forward_ip(unsigned int hook, struct sk_buff **pskb,
nf_bridge->mask |= BRNF_BRIDGED;
nf_bridge->physoutdev = skb->dev;
NF_HOOK(PF_INET, NF_IP_FORWARD, skb, bridge_parent(in),
NF_HOOK(pf, NF_IP_FORWARD, skb, bridge_parent(in),
bridge_parent(out), br_nf_forward_finish);
return NF_STOLEN;
......@@ -483,11 +588,10 @@ static int br_nf_local_out_finish(struct sk_buff *skb)
return 0;
}
/* This function sees both locally originated IP packets and forwarded
* IP packets (in both cases the destination device is a bridge
* device). It also sees bridged-and-DNAT'ed packets.
* To be able to filter on the physical bridge devices (with the ipt_physdev.c
* To be able to filter on the physical bridge devices (with the physdev
* module), we steal packets destined to a bridge device away from the
* PF_INET/FORWARD and PF_INET/OUTPUT hook functions, and give them back later,
* when we have determined the real output device. This is done in here.
......@@ -501,45 +605,44 @@ static int br_nf_local_out_finish(struct sk_buff *skb)
* this packet before, and so the packet was locally originated. We fake
* the PF_INET/LOCAL_OUT hook.
* Finally, if nf_bridge->physindev isn't NULL, then the packet was IP routed,
* so we fake the PF_INET/FORWARD hook. ipv4_sabotage_out() makes sure
* so we fake the PF_INET/FORWARD hook. ip_sabotage_out() makes sure
* even routed packets that didn't arrive on a bridge interface have their
* nf_bridge->physindev set.
*/
* nf_bridge->physindev set. */
static unsigned int br_nf_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 (*okfn)(struct sk_buff *))
{
int (*okfn)(struct sk_buff *skb);
struct net_device *realindev;
struct net_device *realindev, *realoutdev;
struct sk_buff *skb = *pskb;
struct nf_bridge_info *nf_bridge;
struct vlan_ethhdr *hdr = (struct vlan_ethhdr *)(skb->mac.ethernet);
int pf;
#ifdef CONFIG_SYSCTL
if (!skb->nf_bridge)
return NF_ACCEPT;
#endif
if (skb->protocol != __constant_htons(ETH_P_IP) && !IS_VLAN_IP)
return NF_ACCEPT;
if (skb->protocol == __constant_htons(ETH_P_IP) || IS_VLAN_IP)
pf = PF_INET;
else
pf = PF_INET6;
#ifdef CONFIG_NETFILTER_DEBUG
/* Sometimes we get packets with NULL ->dst here (for example,
* running a dhcp client daemon triggers this).
*/
if (skb->dst == NULL)
* running a dhcp client daemon triggers this). This should now
* be fixed, but let's keep the check around. */
if (skb->dst == NULL) {
printk(KERN_CRIT "br_netfilter: skb->dst == NULL.");
return NF_ACCEPT;
}
#endif
nf_bridge = skb->nf_bridge;
nf_bridge->physoutdev = skb->dev;
realindev = nf_bridge->physindev;
/* Bridged, take PF_BRIDGE/FORWARD.
* (see big note in front of br_nf_pre_routing_finish)
*/
* (see big note in front of br_nf_pre_routing_finish) */
if (nf_bridge->mask & BRNF_BRIDGED_DNAT) {
okfn = br_forward_finish;
if (nf_bridge->mask & BRNF_PKT_TYPE) {
skb->pkt_type = PACKET_OTHERHOST;
nf_bridge->mask ^= BRNF_PKT_TYPE;
......@@ -550,41 +653,41 @@ static unsigned int br_nf_local_out(unsigned int hook, struct sk_buff **pskb,
}
NF_HOOK(PF_BRIDGE, NF_BR_FORWARD, skb, realindev,
skb->dev, okfn);
} else {
struct net_device *realoutdev = bridge_parent(skb->dev);
skb->dev, br_forward_finish);
goto out;
}
realoutdev = bridge_parent(skb->dev);
#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
/* iptables should match -o br0.x */
if (nf_bridge->netoutdev)
realoutdev = nf_bridge->netoutdev;
/* iptables should match -o br0.x */
if (nf_bridge->netoutdev)
realoutdev = nf_bridge->netoutdev;
#endif
okfn = br_nf_local_out_finish;
if (skb->protocol == __constant_htons(ETH_P_8021Q)) {
skb_pull(skb, VLAN_HLEN);
(*pskb)->nh.raw += VLAN_HLEN;
}
/* IP forwarded traffic has a physindev, locally
* generated traffic hasn't.
*/
if (realindev != NULL) {
if (((nf_bridge->mask & BRNF_DONT_TAKE_PARENT) == 0) &&
has_bridge_parent(realindev))
realindev = bridge_parent(realindev);
NF_HOOK_THRESH(PF_INET, NF_IP_FORWARD, skb, realindev,
realoutdev, okfn,
NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD + 1);
} else {
if (skb->protocol == __constant_htons(ETH_P_8021Q)) {
skb_pull(skb, VLAN_HLEN);
(*pskb)->nh.raw += VLAN_HLEN;
}
/* IP forwarded traffic has a physindev, locally
* generated traffic hasn't. */
if (realindev != NULL) {
if (!(nf_bridge->mask & BRNF_DONT_TAKE_PARENT) &&
has_bridge_parent(realindev))
realindev = bridge_parent(realindev);
NF_HOOK_THRESH(pf, NF_IP_FORWARD, skb, realindev,
realoutdev, br_nf_local_out_finish,
NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD + 1);
} else {
#ifdef CONFIG_NETFILTER_DEBUG
skb->nf_debug ^= (1 << NF_IP_LOCAL_OUT);
skb->nf_debug ^= (1 << NF_IP_LOCAL_OUT);
#endif
NF_HOOK_THRESH(PF_INET, NF_IP_LOCAL_OUT, skb, realindev,
realoutdev, okfn,
NF_IP_PRI_BRIDGE_SABOTAGE_LOCAL_OUT + 1);
}
NF_HOOK_THRESH(pf, NF_IP_LOCAL_OUT, skb, realindev,
realoutdev, br_nf_local_out_finish,
NF_IP_PRI_BRIDGE_SABOTAGE_LOCAL_OUT + 1);
}
out:
return NF_STOLEN;
}
......@@ -598,6 +701,7 @@ static unsigned int br_nf_post_routing(unsigned int hook, struct sk_buff **pskb,
struct nf_bridge_info *nf_bridge = (*pskb)->nf_bridge;
struct vlan_ethhdr *hdr = (struct vlan_ethhdr *)(skb->mac.ethernet);
struct net_device *realoutdev = bridge_parent(skb->dev);
int pf;
#ifdef CONFIG_NETFILTER_DEBUG
/* Be very paranoid. This probably won't happen anymore, but let's
......@@ -609,19 +713,15 @@ static unsigned int br_nf_post_routing(unsigned int hook, struct sk_buff **pskb,
}
#endif
#ifdef CONFIG_SYSCTL
if (!nf_bridge)
return NF_ACCEPT;
#endif
if (skb->protocol != __constant_htons(ETH_P_IP) && !IS_VLAN_IP)
return NF_ACCEPT;
if (skb->protocol == __constant_htons(ETH_P_IP) || IS_VLAN_IP)
pf = PF_INET;
else
pf = PF_INET6;
#ifdef CONFIG_NETFILTER_DEBUG
/* Sometimes we get packets with NULL ->dst here (for example,
* running a dhcp client daemon triggers this). This should now
* be fixed, but let's keep the check around.
*/
if (skb->dst == NULL) {
printk(KERN_CRIT "br_netfilter: skb->dst == NULL.");
goto print_error;
......@@ -631,8 +731,7 @@ static unsigned int br_nf_post_routing(unsigned int hook, struct sk_buff **pskb,
#endif
/* We assume any code from br_dev_queue_push_xmit onwards doesn't care
* about the value of skb->pkt_type.
*/
* about the value of skb->pkt_type. */
if (skb->pkt_type == PACKET_OTHERHOST) {
skb->pkt_type = PACKET_HOST;
nf_bridge->mask |= BRNF_PKT_TYPE;
......@@ -649,8 +748,8 @@ static unsigned int br_nf_post_routing(unsigned int hook, struct sk_buff **pskb,
if (nf_bridge->netoutdev)
realoutdev = nf_bridge->netoutdev;
#endif
NF_HOOK(PF_INET, NF_IP_POST_ROUTING, skb, NULL,
realoutdev, br_dev_queue_push_xmit);
NF_HOOK(pf, NF_IP_POST_ROUTING, skb, NULL, realoutdev,
br_dev_queue_push_xmit);
return NF_STOLEN;
......@@ -668,12 +767,10 @@ static unsigned int br_nf_post_routing(unsigned int hook, struct sk_buff **pskb,
}
/* IPv4/SABOTAGE *****************************************************/
/* Don't hand locally destined packets to PF_INET/PRE_ROUTING
* for the second time.
*/
static unsigned int ipv4_sabotage_in(unsigned int hook, struct sk_buff **pskb,
/* IP/SABOTAGE *****************************************************/
/* Don't hand locally destined packets to PF_INET(6)/PRE_ROUTING
* for the second time. */
static unsigned int ip_sabotage_in(unsigned int hook, struct sk_buff **pskb,
const struct net_device *in, const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
......@@ -686,19 +783,27 @@ static unsigned int ipv4_sabotage_in(unsigned int hook, struct sk_buff **pskb,
return NF_ACCEPT;
}
/* Postpone execution of PF_INET/FORWARD, PF_INET/LOCAL_OUT
* and PF_INET/POST_ROUTING until we have done the forwarding
* decision in the bridge code and have determined skb->physoutdev.
*/
static unsigned int ipv4_sabotage_out(unsigned int hook, struct sk_buff **pskb,
/* Postpone execution of PF_INET(6)/FORWARD, PF_INET(6)/LOCAL_OUT
* and PF_INET(6)/POST_ROUTING until we have done the forwarding
* decision in the bridge code and have determined nf_bridge->physoutdev. */
static unsigned int ip_sabotage_out(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;
#ifdef CONFIG_SYSCTL
if (!brnf_call_iptables && !skb->nf_bridge)
return NF_ACCEPT;
if (!skb->nf_bridge) {
struct vlan_ethhdr *hdr =
(struct vlan_ethhdr *)(skb->mac.ethernet);
if (skb->protocol == __constant_htons(ETH_P_IP) ||
IS_VLAN_IP) {
if (!brnf_call_iptables)
return NF_ACCEPT;
} else if (!brnf_call_ip6tables)
return NF_ACCEPT;
}
#endif
if ((out->hard_start_xmit == br_dev_xmit &&
......@@ -721,8 +826,7 @@ static unsigned int ipv4_sabotage_out(unsigned int hook, struct sk_buff **pskb,
* will need the indev then. For a brouter, the real indev
* can be a bridge port, so we make sure br_nf_local_out()
* doesn't use the bridge parent of the indev by using
* the BRNF_DONT_TAKE_PARENT mask.
*/
* the BRNF_DONT_TAKE_PARENT mask. */
if (hook == NF_IP_FORWARD && nf_bridge->physindev == NULL) {
nf_bridge->mask &= BRNF_DONT_TAKE_PARENT;
nf_bridge->physindev = (struct net_device *)in;
......@@ -742,8 +846,7 @@ static unsigned int ipv4_sabotage_out(unsigned int hook, struct sk_buff **pskb,
/* For br_nf_local_out we need (prio = NF_BR_PRI_FIRST), to insure that innocent
* PF_BRIDGE/NF_BR_LOCAL_OUT functions don't get bridged traffic as input.
* For br_nf_post_routing, we need (prio = NF_BR_PRI_LAST), because
* ip_refrag() can return NF_STOLEN.
*/
* ip_refrag() can return NF_STOLEN. */
static struct nf_hook_ops br_nf_ops[] = {
{ .hook = br_nf_pre_routing,
.owner = THIS_MODULE,
......@@ -775,26 +878,46 @@ static struct nf_hook_ops br_nf_ops[] = {
.pf = PF_BRIDGE,
.hooknum = NF_BR_POST_ROUTING,
.priority = NF_BR_PRI_LAST, },
{ .hook = ipv4_sabotage_in,
{ .hook = ip_sabotage_in,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_PRE_ROUTING,
.priority = NF_IP_PRI_FIRST, },
{ .hook = ipv4_sabotage_out,
{ .hook = ip_sabotage_in,
.owner = THIS_MODULE,
.pf = PF_INET6,
.hooknum = NF_IP6_PRE_ROUTING,
.priority = NF_IP6_PRI_FIRST, },
{ .hook = ip_sabotage_out,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_FORWARD,
.priority = NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD, },
{ .hook = ipv4_sabotage_out,
{ .hook = ip_sabotage_out,
.owner = THIS_MODULE,
.pf = PF_INET6,
.hooknum = NF_IP6_FORWARD,
.priority = NF_IP6_PRI_BRIDGE_SABOTAGE_FORWARD, },
{ .hook = ip_sabotage_out,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_LOCAL_OUT,
.priority = NF_IP_PRI_BRIDGE_SABOTAGE_LOCAL_OUT, },
{ .hook = ipv4_sabotage_out,
{ .hook = ip_sabotage_out,
.owner = THIS_MODULE,
.pf = PF_INET6,
.hooknum = NF_IP6_LOCAL_OUT,
.priority = NF_IP6_PRI_BRIDGE_SABOTAGE_LOCAL_OUT, },
{ .hook = ip_sabotage_out,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_POST_ROUTING,
.priority = NF_IP_PRI_FIRST, },
{ .hook = ip_sabotage_out,
.owner = THIS_MODULE,
.pf = PF_INET6,
.hooknum = NF_IP6_POST_ROUTING,
.priority = NF_IP6_PRI_FIRST, },
};
#ifdef CONFIG_SYSCTL
......@@ -828,6 +951,14 @@ static ctl_table brnf_table[] = {
.mode = 0644,
.proc_handler = &brnf_sysctl_call_tables,
},
{
.ctl_name = NET_BRIDGE_NF_CALL_IP6TABLES,
.procname = "bridge-nf-call-ip6tables",
.data = &brnf_call_ip6tables,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &brnf_sysctl_call_tables,
},
{
.ctl_name = NET_BRIDGE_NF_FILTER_VLAN_TAGGED,
.procname = "bridge-nf-filter-vlan-tagged",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment