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 ...@@ -837,6 +837,11 @@ bridge-nf-call-iptables - BOOLEAN
0 : disable this. 0 : disable this.
Default: 1 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 bridge-nf-filter-vlan-tagged - BOOLEAN
1 : pass bridged vlan-tagged ARP/IP traffic to arptables/iptables. 1 : pass bridged vlan-tagged ARP/IP traffic to arptables/iptables.
0 : disable this. 0 : disable this.
......
...@@ -58,8 +58,10 @@ enum nf_ip6_hook_priorities { ...@@ -58,8 +58,10 @@ enum nf_ip6_hook_priorities {
NF_IP6_PRI_FIRST = INT_MIN, NF_IP6_PRI_FIRST = INT_MIN,
NF_IP6_PRI_SELINUX_FIRST = -225, NF_IP6_PRI_SELINUX_FIRST = -225,
NF_IP6_PRI_CONNTRACK = -200, NF_IP6_PRI_CONNTRACK = -200,
NF_IP6_PRI_BRIDGE_SABOTAGE_FORWARD = -175,
NF_IP6_PRI_MANGLE = -150, NF_IP6_PRI_MANGLE = -150,
NF_IP6_PRI_NAT_DST = -100, NF_IP6_PRI_NAT_DST = -100,
NF_IP6_PRI_BRIDGE_SABOTAGE_LOCAL_OUT = -50,
NF_IP6_PRI_FILTER = 0, NF_IP6_PRI_FILTER = 0,
NF_IP6_PRI_NAT_SRC = 100, NF_IP6_PRI_NAT_SRC = 100,
NF_IP6_PRI_SELINUX_LAST = 225, NF_IP6_PRI_SELINUX_LAST = 225,
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
* Jun 19 2003: let arptables see bridged ARP traffic (bdschuym) * Jun 19 2003: let arptables see bridged ARP traffic (bdschuym)
* Oct 06 2003: filter encapsulated IP/ARP VLAN traffic on untagged bridge * Oct 06 2003: filter encapsulated IP/ARP VLAN traffic on untagged bridge
* (bdschuym) * (bdschuym)
* Sep 01 2004: add IPv6 filtering (bdschuym)
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
...@@ -29,9 +30,11 @@ ...@@ -29,9 +30,11 @@
#include <linux/if_vlan.h> #include <linux/if_vlan.h>
#include <linux/netfilter_bridge.h> #include <linux/netfilter_bridge.h>
#include <linux/netfilter_ipv4.h> #include <linux/netfilter_ipv4.h>
#include <linux/netfilter_ipv6.h>
#include <linux/netfilter_arp.h> #include <linux/netfilter_arp.h>
#include <linux/in_route.h> #include <linux/in_route.h>
#include <net/ip.h> #include <net/ip.h>
#include <net/ipv6.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <asm/checksum.h> #include <asm/checksum.h>
#include "br_private.h" #include "br_private.h"
...@@ -39,7 +42,6 @@ ...@@ -39,7 +42,6 @@
#include <linux/sysctl.h> #include <linux/sysctl.h>
#endif #endif
#define skb_origaddr(skb) (((struct bridge_skb_cb *) \ #define skb_origaddr(skb) (((struct bridge_skb_cb *) \
(skb->nf_bridge->data))->daddr.ipv4) (skb->nf_bridge->data))->daddr.ipv4)
#define store_orig_dstaddr(skb) (skb_origaddr(skb) = (skb)->nh.iph->daddr) #define store_orig_dstaddr(skb) (skb_origaddr(skb) = (skb)->nh.iph->daddr)
...@@ -51,6 +53,7 @@ ...@@ -51,6 +53,7 @@
#ifdef CONFIG_SYSCTL #ifdef CONFIG_SYSCTL
static struct ctl_table_header *brnf_sysctl_header; static struct ctl_table_header *brnf_sysctl_header;
static int brnf_call_iptables = 1; static int brnf_call_iptables = 1;
static int brnf_call_ip6tables = 1;
static int brnf_call_arptables = 1; static int brnf_call_arptables = 1;
static int brnf_filter_vlan_tagged = 1; static int brnf_filter_vlan_tagged = 1;
#else #else
...@@ -60,6 +63,9 @@ static int brnf_filter_vlan_tagged = 1; ...@@ -60,6 +63,9 @@ static int brnf_filter_vlan_tagged = 1;
#define IS_VLAN_IP (skb->protocol == __constant_htons(ETH_P_8021Q) && \ #define IS_VLAN_IP (skb->protocol == __constant_htons(ETH_P_8021Q) && \
hdr->h_vlan_encapsulated_proto == __constant_htons(ETH_P_IP) && \ hdr->h_vlan_encapsulated_proto == __constant_htons(ETH_P_IP) && \
brnf_filter_vlan_tagged) 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) && \ #define IS_VLAN_ARP (skb->protocol == __constant_htons(ETH_P_8021Q) && \
hdr->h_vlan_encapsulated_proto == __constant_htons(ETH_P_ARP) && \ hdr->h_vlan_encapsulated_proto == __constant_htons(ETH_P_ARP) && \
brnf_filter_vlan_tagged) brnf_filter_vlan_tagged)
...@@ -71,8 +77,7 @@ static int brnf_filter_vlan_tagged = 1; ...@@ -71,8 +77,7 @@ static int brnf_filter_vlan_tagged = 1;
* Currently, we fill in the PMTU entry because netfilter * Currently, we fill in the PMTU entry because netfilter
* refragmentation needs it, and the rt_flags entry because * refragmentation needs it, and the rt_flags entry because
* ipt_REJECT needs it. Future netfilter modules might * 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 = { static struct net_device __fake_net_device = {
.hard_header_len = ETH_HLEN .hard_header_len = ETH_HLEN
}; };
...@@ -91,6 +96,36 @@ static struct rtable __fake_rtable = { ...@@ -91,6 +96,36 @@ static struct rtable __fake_rtable = {
/* PF_BRIDGE/PRE_ROUTING *********************************************/ /* 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 void __br_dnat_complain(void)
{ {
static unsigned long last_complaint; static unsigned long last_complaint;
...@@ -102,7 +137,6 @@ static void __br_dnat_complain(void) ...@@ -102,7 +137,6 @@ static void __br_dnat_complain(void)
} }
} }
/* This requires some explaining. If DNAT has taken place, /* This requires some explaining. If DNAT has taken place,
* we will need to fix up the destination Ethernet address, * we will need to fix up the destination Ethernet address,
* and this is a tricky process. * and this is a tricky process.
...@@ -145,9 +179,7 @@ static void __br_dnat_complain(void) ...@@ -145,9 +179,7 @@ static void __br_dnat_complain(void)
* *
* --Lennert, 20020411 * --Lennert, 20020411
* --Bart, 20020416 (updated) * --Bart, 20020416 (updated)
* --Bart, 20021007 (updated) * --Bart, 20021007 (updated) */
*/
static int br_nf_pre_routing_finish_bridge(struct sk_buff *skb) static int br_nf_pre_routing_finish_bridge(struct sk_buff *skb)
{ {
#ifdef CONFIG_NETFILTER_DEBUG #ifdef CONFIG_NETFILTER_DEBUG
...@@ -195,8 +227,7 @@ static int br_nf_pre_routing_finish(struct sk_buff *skb) ...@@ -195,8 +227,7 @@ static int br_nf_pre_routing_finish(struct sk_buff *skb)
if (!ip_route_output_key(&rt, &fl)) { if (!ip_route_output_key(&rt, &fl)) {
/* Bridged-and-DNAT'ed traffic doesn't /* Bridged-and-DNAT'ed traffic doesn't
* require ip_forwarding. * require ip_forwarding. */
*/
if (((struct dst_entry *)rt)->dev == dev) { if (((struct dst_entry *)rt)->dev == dev) {
skb->dst = (struct dst_entry *)rt; skb->dst = (struct dst_entry *)rt;
goto bridged_dnat; goto bridged_dnat;
...@@ -210,8 +241,7 @@ static int br_nf_pre_routing_finish(struct sk_buff *skb) ...@@ -210,8 +241,7 @@ static int br_nf_pre_routing_finish(struct sk_buff *skb)
if (skb->dst->dev == dev) { if (skb->dst->dev == dev) {
bridged_dnat: bridged_dnat:
/* Tell br_nf_local_out this is a /* Tell br_nf_local_out this is a
* bridged frame * bridged frame */
*/
nf_bridge->mask |= BRNF_BRIDGED_DNAT; nf_bridge->mask |= BRNF_BRIDGED_DNAT;
skb->dev = nf_bridge->physindev; skb->dev = nf_bridge->physindev;
if (skb->protocol == if (skb->protocol ==
...@@ -245,12 +275,81 @@ static int br_nf_pre_routing_finish(struct sk_buff *skb) ...@@ -245,12 +275,81 @@ static int br_nf_pre_routing_finish(struct sk_buff *skb)
return 0; 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 * Set skb->dev to the bridge device (i.e. parent of the
* receiving device) to make netfilter happy, the REDIRECT * receiving device) to make netfilter happy, the REDIRECT
* target in particular. Save the original destination IP * 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, static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff **pskb,
const struct net_device *in, const struct net_device *out, const struct net_device *in, const struct net_device *out,
int (*okfn)(struct sk_buff *)) int (*okfn)(struct sk_buff *))
...@@ -259,24 +358,38 @@ static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff **pskb, ...@@ -259,24 +358,38 @@ static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff **pskb,
__u32 len; __u32 len;
struct sk_buff *skb = *pskb; struct sk_buff *skb = *pskb;
struct nf_bridge_info *nf_bridge; 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 #ifdef CONFIG_SYSCTL
if (!brnf_call_iptables) if (!brnf_call_ip6tables)
return NF_ACCEPT; return NF_ACCEPT;
#endif #endif
if ((skb = skb_share_check(*pskb, GFP_ATOMIC)) == NULL)
goto out;
if (skb->protocol != __constant_htons(ETH_P_IP)) { if (skb->protocol == __constant_htons(ETH_P_8021Q)) {
struct vlan_ethhdr *hdr = (struct vlan_ethhdr *) skb_pull(skb, VLAN_HLEN);
((*pskb)->mac.ethernet); (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 (!IS_VLAN_IP) if (skb->protocol != __constant_htons(ETH_P_IP) && !IS_VLAN_IP)
return NF_ACCEPT; return NF_ACCEPT;
if ((skb = skb_share_check(*pskb, GFP_ATOMIC)) == NULL) if ((skb = skb_share_check(*pskb, GFP_ATOMIC)) == NULL)
goto out; goto out;
skb_pull(*pskb, VLAN_HLEN);
(*pskb)->nh.raw += VLAN_HLEN; if (skb->protocol == __constant_htons(ETH_P_8021Q)) {
} else if ((skb = skb_share_check(*pskb, GFP_ATOMIC)) == NULL) skb_pull(skb, VLAN_HLEN);
goto out; (skb)->nh.raw += VLAN_HLEN;
}
if (!pskb_may_pull(skb, sizeof(struct iphdr))) if (!pskb_may_pull(skb, sizeof(struct iphdr)))
goto inhdr_error; goto inhdr_error;
...@@ -307,15 +420,7 @@ static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff **pskb, ...@@ -307,15 +420,7 @@ static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff **pskb,
#endif #endif
if ((nf_bridge = nf_bridge_alloc(skb)) == NULL) if ((nf_bridge = nf_bridge_alloc(skb)) == NULL)
return NF_DROP; return NF_DROP;
setup_pre_routing(skb);
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);
store_orig_dstaddr(skb); store_orig_dstaddr(skb);
NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, skb->dev, NULL, 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, ...@@ -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 * packet would pass through PRE_ROUTING again (which already
* took place when the packet entered the bridge), but we * took place when the packet entered the bridge), but we
* register an IPv4 PRE_ROUTING 'sabotage' hook that will * 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, static unsigned int br_nf_local_in(unsigned int hook, struct sk_buff **pskb,
const struct net_device *in, const struct net_device *out, const struct net_device *in, const struct net_device *out,
int (*okfn)(struct sk_buff *)) int (*okfn)(struct sk_buff *))
...@@ -364,7 +468,7 @@ static int br_nf_forward_finish(struct sk_buff *skb) ...@@ -364,7 +468,7 @@ static int br_nf_forward_finish(struct sk_buff *skb)
skb->nf_debug ^= (1 << NF_BR_FORWARD); skb->nf_debug ^= (1 << NF_BR_FORWARD);
#endif #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; in = nf_bridge->physindev;
if (nf_bridge->mask & BRNF_PKT_TYPE) { if (nf_bridge->mask & BRNF_PKT_TYPE) {
skb->pkt_type = PACKET_OTHERHOST; skb->pkt_type = PACKET_OTHERHOST;
...@@ -385,9 +489,8 @@ static int br_nf_forward_finish(struct sk_buff *skb) ...@@ -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 /* This is the 'purely bridged' case. For IP, we pass the packet to
* netfilter with indev and outdev set to the bridge device, * netfilter with indev and outdev set to the bridge device,
* but we are still able to filter on the 'real' indev/outdev * 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 * because of the physdev module. For ARP, indev and outdev are the
* bridge ports. * bridge ports. */
*/
static unsigned int br_nf_forward_ip(unsigned int hook, struct sk_buff **pskb, static unsigned int br_nf_forward_ip(unsigned int hook, struct sk_buff **pskb,
const struct net_device *in, const struct net_device *out, const struct net_device *in, const struct net_device *out,
int (*okfn)(struct sk_buff *)) int (*okfn)(struct sk_buff *))
...@@ -395,15 +498,17 @@ static unsigned int br_nf_forward_ip(unsigned int hook, struct sk_buff **pskb, ...@@ -395,15 +498,17 @@ static unsigned int br_nf_forward_ip(unsigned int hook, struct sk_buff **pskb,
struct sk_buff *skb = *pskb; struct sk_buff *skb = *pskb;
struct nf_bridge_info *nf_bridge; struct nf_bridge_info *nf_bridge;
struct vlan_ethhdr *hdr = (struct vlan_ethhdr *)(skb->mac.ethernet); struct vlan_ethhdr *hdr = (struct vlan_ethhdr *)(skb->mac.ethernet);
int pf;
#ifdef CONFIG_SYSCTL
if (!skb->nf_bridge) if (!skb->nf_bridge)
return NF_ACCEPT; return NF_ACCEPT;
#endif
if (skb->protocol != __constant_htons(ETH_P_IP)) { if (skb->protocol == __constant_htons(ETH_P_IP) || IS_VLAN_IP)
if (!IS_VLAN_IP) pf = PF_INET;
return NF_ACCEPT; else
pf = PF_INET6;
if (skb->protocol == __constant_htons(ETH_P_8021Q)) {
skb_pull(*pskb, VLAN_HLEN); skb_pull(*pskb, VLAN_HLEN);
(*pskb)->nh.raw += 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, ...@@ -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->mask |= BRNF_BRIDGED;
nf_bridge->physoutdev = skb->dev; 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); bridge_parent(out), br_nf_forward_finish);
return NF_STOLEN; return NF_STOLEN;
...@@ -483,11 +588,10 @@ static int br_nf_local_out_finish(struct sk_buff *skb) ...@@ -483,11 +588,10 @@ static int br_nf_local_out_finish(struct sk_buff *skb)
return 0; return 0;
} }
/* This function sees both locally originated IP packets and forwarded /* This function sees both locally originated IP packets and forwarded
* IP packets (in both cases the destination device is a bridge * IP packets (in both cases the destination device is a bridge
* device). It also sees bridged-and-DNAT'ed packets. * 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 * 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, * 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. * 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) ...@@ -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 * this packet before, and so the packet was locally originated. We fake
* the PF_INET/LOCAL_OUT hook. * the PF_INET/LOCAL_OUT hook.
* Finally, if nf_bridge->physindev isn't NULL, then the packet was IP routed, * 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 * 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, static unsigned int br_nf_local_out(unsigned int hook, struct sk_buff **pskb,
const struct net_device *in, const struct net_device *out, 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, *realoutdev;
struct net_device *realindev;
struct sk_buff *skb = *pskb; struct sk_buff *skb = *pskb;
struct nf_bridge_info *nf_bridge; struct nf_bridge_info *nf_bridge;
struct vlan_ethhdr *hdr = (struct vlan_ethhdr *)(skb->mac.ethernet); struct vlan_ethhdr *hdr = (struct vlan_ethhdr *)(skb->mac.ethernet);
int pf;
#ifdef CONFIG_SYSCTL
if (!skb->nf_bridge) if (!skb->nf_bridge)
return NF_ACCEPT; return NF_ACCEPT;
#endif
if (skb->protocol != __constant_htons(ETH_P_IP) && !IS_VLAN_IP) if (skb->protocol == __constant_htons(ETH_P_IP) || IS_VLAN_IP)
return NF_ACCEPT; pf = PF_INET;
else
pf = PF_INET6;
#ifdef CONFIG_NETFILTER_DEBUG
/* Sometimes we get packets with NULL ->dst here (for example, /* Sometimes we get packets with NULL ->dst here (for example,
* running a dhcp client daemon triggers this). * running a dhcp client daemon triggers this). This should now
*/ * be fixed, but let's keep the check around. */
if (skb->dst == NULL) if (skb->dst == NULL) {
printk(KERN_CRIT "br_netfilter: skb->dst == NULL.");
return NF_ACCEPT; return NF_ACCEPT;
}
#endif
nf_bridge = skb->nf_bridge; nf_bridge = skb->nf_bridge;
nf_bridge->physoutdev = skb->dev; nf_bridge->physoutdev = skb->dev;
realindev = nf_bridge->physindev; realindev = nf_bridge->physindev;
/* Bridged, take PF_BRIDGE/FORWARD. /* 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) { if (nf_bridge->mask & BRNF_BRIDGED_DNAT) {
okfn = br_forward_finish;
if (nf_bridge->mask & BRNF_PKT_TYPE) { if (nf_bridge->mask & BRNF_PKT_TYPE) {
skb->pkt_type = PACKET_OTHERHOST; skb->pkt_type = PACKET_OTHERHOST;
nf_bridge->mask ^= BRNF_PKT_TYPE; nf_bridge->mask ^= BRNF_PKT_TYPE;
...@@ -550,41 +653,41 @@ static unsigned int br_nf_local_out(unsigned int hook, struct sk_buff **pskb, ...@@ -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, NF_HOOK(PF_BRIDGE, NF_BR_FORWARD, skb, realindev,
skb->dev, okfn); skb->dev, br_forward_finish);
} else { goto out;
struct net_device *realoutdev = bridge_parent(skb->dev); }
realoutdev = bridge_parent(skb->dev);
#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE) #if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
/* iptables should match -o br0.x */ /* iptables should match -o br0.x */
if (nf_bridge->netoutdev) if (nf_bridge->netoutdev)
realoutdev = nf_bridge->netoutdev; realoutdev = nf_bridge->netoutdev;
#endif #endif
okfn = br_nf_local_out_finish;
if (skb->protocol == __constant_htons(ETH_P_8021Q)) { if (skb->protocol == __constant_htons(ETH_P_8021Q)) {
skb_pull(skb, VLAN_HLEN); skb_pull(skb, VLAN_HLEN);
(*pskb)->nh.raw += VLAN_HLEN; (*pskb)->nh.raw += VLAN_HLEN;
} }
/* IP forwarded traffic has a physindev, locally /* IP forwarded traffic has a physindev, locally
* generated traffic hasn't. * generated traffic hasn't. */
*/
if (realindev != NULL) { if (realindev != NULL) {
if (((nf_bridge->mask & BRNF_DONT_TAKE_PARENT) == 0) && if (!(nf_bridge->mask & BRNF_DONT_TAKE_PARENT) &&
has_bridge_parent(realindev)) has_bridge_parent(realindev))
realindev = bridge_parent(realindev); realindev = bridge_parent(realindev);
NF_HOOK_THRESH(PF_INET, NF_IP_FORWARD, skb, realindev,
realoutdev, okfn, NF_HOOK_THRESH(pf, NF_IP_FORWARD, skb, realindev,
realoutdev, br_nf_local_out_finish,
NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD + 1); NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD + 1);
} else { } else {
#ifdef CONFIG_NETFILTER_DEBUG #ifdef CONFIG_NETFILTER_DEBUG
skb->nf_debug ^= (1 << NF_IP_LOCAL_OUT); skb->nf_debug ^= (1 << NF_IP_LOCAL_OUT);
#endif #endif
NF_HOOK_THRESH(PF_INET, NF_IP_LOCAL_OUT, skb, realindev, NF_HOOK_THRESH(pf, NF_IP_LOCAL_OUT, skb, realindev,
realoutdev, okfn, realoutdev, br_nf_local_out_finish,
NF_IP_PRI_BRIDGE_SABOTAGE_LOCAL_OUT + 1); NF_IP_PRI_BRIDGE_SABOTAGE_LOCAL_OUT + 1);
} }
}
out:
return NF_STOLEN; return NF_STOLEN;
} }
...@@ -598,6 +701,7 @@ static unsigned int br_nf_post_routing(unsigned int hook, struct sk_buff **pskb, ...@@ -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 nf_bridge_info *nf_bridge = (*pskb)->nf_bridge;
struct vlan_ethhdr *hdr = (struct vlan_ethhdr *)(skb->mac.ethernet); struct vlan_ethhdr *hdr = (struct vlan_ethhdr *)(skb->mac.ethernet);
struct net_device *realoutdev = bridge_parent(skb->dev); struct net_device *realoutdev = bridge_parent(skb->dev);
int pf;
#ifdef CONFIG_NETFILTER_DEBUG #ifdef CONFIG_NETFILTER_DEBUG
/* Be very paranoid. This probably won't happen anymore, but let's /* 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, ...@@ -609,19 +713,15 @@ static unsigned int br_nf_post_routing(unsigned int hook, struct sk_buff **pskb,
} }
#endif #endif
#ifdef CONFIG_SYSCTL
if (!nf_bridge) if (!nf_bridge)
return NF_ACCEPT; return NF_ACCEPT;
#endif
if (skb->protocol != __constant_htons(ETH_P_IP) && !IS_VLAN_IP) if (skb->protocol == __constant_htons(ETH_P_IP) || IS_VLAN_IP)
return NF_ACCEPT; pf = PF_INET;
else
pf = PF_INET6;
#ifdef CONFIG_NETFILTER_DEBUG #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) { if (skb->dst == NULL) {
printk(KERN_CRIT "br_netfilter: skb->dst == NULL."); printk(KERN_CRIT "br_netfilter: skb->dst == NULL.");
goto print_error; goto print_error;
...@@ -631,8 +731,7 @@ static unsigned int br_nf_post_routing(unsigned int hook, struct sk_buff **pskb, ...@@ -631,8 +731,7 @@ static unsigned int br_nf_post_routing(unsigned int hook, struct sk_buff **pskb,
#endif #endif
/* We assume any code from br_dev_queue_push_xmit onwards doesn't care /* 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) { if (skb->pkt_type == PACKET_OTHERHOST) {
skb->pkt_type = PACKET_HOST; skb->pkt_type = PACKET_HOST;
nf_bridge->mask |= BRNF_PKT_TYPE; nf_bridge->mask |= BRNF_PKT_TYPE;
...@@ -649,8 +748,8 @@ static unsigned int br_nf_post_routing(unsigned int hook, struct sk_buff **pskb, ...@@ -649,8 +748,8 @@ static unsigned int br_nf_post_routing(unsigned int hook, struct sk_buff **pskb,
if (nf_bridge->netoutdev) if (nf_bridge->netoutdev)
realoutdev = nf_bridge->netoutdev; realoutdev = nf_bridge->netoutdev;
#endif #endif
NF_HOOK(PF_INET, NF_IP_POST_ROUTING, skb, NULL, NF_HOOK(pf, NF_IP_POST_ROUTING, skb, NULL, realoutdev,
realoutdev, br_dev_queue_push_xmit); br_dev_queue_push_xmit);
return NF_STOLEN; return NF_STOLEN;
...@@ -668,12 +767,10 @@ static unsigned int br_nf_post_routing(unsigned int hook, struct sk_buff **pskb, ...@@ -668,12 +767,10 @@ static unsigned int br_nf_post_routing(unsigned int hook, struct sk_buff **pskb,
} }
/* IPv4/SABOTAGE *****************************************************/ /* IP/SABOTAGE *****************************************************/
/* Don't hand locally destined packets to PF_INET(6)/PRE_ROUTING
/* Don't hand locally destined packets to PF_INET/PRE_ROUTING * for the second time. */
* for the second time. static unsigned int ip_sabotage_in(unsigned int hook, struct sk_buff **pskb,
*/
static unsigned int ipv4_sabotage_in(unsigned int hook, struct sk_buff **pskb,
const struct net_device *in, const struct net_device *out, const struct net_device *in, const struct net_device *out,
int (*okfn)(struct sk_buff *)) int (*okfn)(struct sk_buff *))
{ {
...@@ -686,19 +783,27 @@ static unsigned int ipv4_sabotage_in(unsigned int hook, struct sk_buff **pskb, ...@@ -686,19 +783,27 @@ static unsigned int ipv4_sabotage_in(unsigned int hook, struct sk_buff **pskb,
return NF_ACCEPT; return NF_ACCEPT;
} }
/* Postpone execution of PF_INET/FORWARD, PF_INET/LOCAL_OUT /* Postpone execution of PF_INET(6)/FORWARD, PF_INET(6)/LOCAL_OUT
* and PF_INET/POST_ROUTING until we have done the forwarding * and PF_INET(6)/POST_ROUTING until we have done the forwarding
* decision in the bridge code and have determined skb->physoutdev. * decision in the bridge code and have determined nf_bridge->physoutdev. */
*/ static unsigned int ip_sabotage_out(unsigned int hook, struct sk_buff **pskb,
static unsigned int ipv4_sabotage_out(unsigned int hook, struct sk_buff **pskb,
const struct net_device *in, const struct net_device *out, const struct net_device *in, const struct net_device *out,
int (*okfn)(struct sk_buff *)) int (*okfn)(struct sk_buff *))
{ {
struct sk_buff *skb = *pskb; struct sk_buff *skb = *pskb;
#ifdef CONFIG_SYSCTL #ifdef CONFIG_SYSCTL
if (!brnf_call_iptables && !skb->nf_bridge) 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; return NF_ACCEPT;
}
#endif #endif
if ((out->hard_start_xmit == br_dev_xmit && 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, ...@@ -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 * will need the indev then. For a brouter, the real indev
* can be a bridge port, so we make sure br_nf_local_out() * can be a bridge port, so we make sure br_nf_local_out()
* doesn't use the bridge parent of the indev by using * 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) { if (hook == NF_IP_FORWARD && nf_bridge->physindev == NULL) {
nf_bridge->mask &= BRNF_DONT_TAKE_PARENT; nf_bridge->mask &= BRNF_DONT_TAKE_PARENT;
nf_bridge->physindev = (struct net_device *)in; nf_bridge->physindev = (struct net_device *)in;
...@@ -742,8 +846,7 @@ static unsigned int ipv4_sabotage_out(unsigned int hook, struct sk_buff **pskb, ...@@ -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 /* 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. * 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 * 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[] = { static struct nf_hook_ops br_nf_ops[] = {
{ .hook = br_nf_pre_routing, { .hook = br_nf_pre_routing,
.owner = THIS_MODULE, .owner = THIS_MODULE,
...@@ -775,26 +878,46 @@ static struct nf_hook_ops br_nf_ops[] = { ...@@ -775,26 +878,46 @@ static struct nf_hook_ops br_nf_ops[] = {
.pf = PF_BRIDGE, .pf = PF_BRIDGE,
.hooknum = NF_BR_POST_ROUTING, .hooknum = NF_BR_POST_ROUTING,
.priority = NF_BR_PRI_LAST, }, .priority = NF_BR_PRI_LAST, },
{ .hook = ipv4_sabotage_in, { .hook = ip_sabotage_in,
.owner = THIS_MODULE, .owner = THIS_MODULE,
.pf = PF_INET, .pf = PF_INET,
.hooknum = NF_IP_PRE_ROUTING, .hooknum = NF_IP_PRE_ROUTING,
.priority = NF_IP_PRI_FIRST, }, .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, .owner = THIS_MODULE,
.pf = PF_INET, .pf = PF_INET,
.hooknum = NF_IP_FORWARD, .hooknum = NF_IP_FORWARD,
.priority = NF_IP_PRI_BRIDGE_SABOTAGE_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, .owner = THIS_MODULE,
.pf = PF_INET, .pf = PF_INET,
.hooknum = NF_IP_LOCAL_OUT, .hooknum = NF_IP_LOCAL_OUT,
.priority = NF_IP_PRI_BRIDGE_SABOTAGE_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, .owner = THIS_MODULE,
.pf = PF_INET, .pf = PF_INET,
.hooknum = NF_IP_POST_ROUTING, .hooknum = NF_IP_POST_ROUTING,
.priority = NF_IP_PRI_FIRST, }, .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 #ifdef CONFIG_SYSCTL
...@@ -828,6 +951,14 @@ static ctl_table brnf_table[] = { ...@@ -828,6 +951,14 @@ static ctl_table brnf_table[] = {
.mode = 0644, .mode = 0644,
.proc_handler = &brnf_sysctl_call_tables, .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, .ctl_name = NET_BRIDGE_NF_FILTER_VLAN_TAGGED,
.procname = "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