Hi Bart,
could you give this a quick glance?
thanks,
Lennert
--- br_netfilter.c.2 Thu Apr 11 15:33:53 2002
+++ br_netfilter.c Thu Apr 11 15:33:34 2002
@@ -60,6 +60,10 @@
#define bridge_parent(device) (&((device)->br_port->br->dev))
+/* As opposed to the DNAT case, for the SNAT case it's not quite
+ * clear what we should do with ethernet addresses in NAT'ed
+ * packets. Use this heuristic for now.
+ */
static inline void __maybe_fixup_src_address(struct sk_buff *skb)
{
if (snat_took_place(skb) &&
@@ -70,6 +74,16 @@
}
}
+
+/* We need these fake structures to make netfilter happy --
+ * lots of places assume that skb->dst != NULL, which isn't
+ * all that unreasonable.
+ *
+ * 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.
+ */
static struct net_device __fake_net_device = {
hard_header_len: ETH_HLEN
};
@@ -99,6 +113,50 @@
}
}
+/* 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.
+ *
+ * There are two cases to consider:
+ * 1. The packet was DNAT'ed to a device in the same bridge
+ * port group as it was received on. We can still bridge
+ * the packet.
+ * 2. The packet was DNAT'ed to a different device, either
+ * a non-bridged device or another bridge port group.
+ * The packet will need to be routed.
+ *
+ * The only way to detect case 2 is to hand the packet to
+ * ip_route_input -- if that function returns us a valid
+ * route, the packet will need to be routed, so we overwrite
+ * the destination ethernet address with our own ethernet
+ * address, which will make it go up the stack. This is
+ * actually an easy case, since the IP routing code will
+ * handle the neighbour discovery for us, etc.
+ *
+ * If ip_route_input didn't return a route, it usually means
+ * that the source and destination IP address are in the same
+ * subnet, and we will have to bridge the packet. This is the
+ * hard case: we need to do neighbour discovery (i.e. ARP)
+ * ourselves. Unfortunately, the existing interface for neighbour
+ * discovery isn't quite what we would like -- basically, there
+ * is no other way than to hand your packet to skb->dst->output
+ * and never see it back again (and this also overwrites the
+ * packet's source ethernet address, by the way). However, we
+ * know that the device the packet will eventually leave on is
+ * a bridge device, so we can detect these DNAT'ed packets by
+ * adding a PF_BRIDGE/LOCAL_OUT hook and continue our processing
+ * from there. Gross, but it works.
+ *
+ * NB: There is another reason why the initial ip_route_input
+ * can fail: IP forwarding might be disabled. Cross-bridge
+ * DNAT requires IP forwarding because what happens in this
+ * case really is forwarding and not bridging (think about
+ * that for a while). ip_route_input does not tell us why it
+ * fails, but fortunately we are able to detect this case by
+ * checking if we have an output route for the new IP address.
+ * If we do, cross-bridge DNAT was attempted with disabled IP
+ * forwarding. Warn the user, and keep him off my back. --Lennert, 20020411
+ */
static int br_nf_pre_routing_finish(struct sk_buff *skb)
{
struct net_device *dev = skb->dev;
@@ -119,8 +177,6 @@
}
skb->dst = (struct dst_entry *)rt;
-
- /* This changes source ethernet address. */
skb->dst->output(skb);
return 0;
} else {
@@ -138,6 +194,12 @@
return 0;
}
+/* 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.
+ */
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 *))
{
struct iphdr *iph;
@@ -192,6 +254,13 @@
/* PF_BRIDGE/LOCAL_IN ************************************************/
+/* The packet is locally destined, which requires a real
+ * dst_entry, so detach the fake one. On the way up, the
+ * 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.
+ */
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 *))
{
struct sk_buff *skb = *pskb;
@@ -217,6 +286,14 @@
return 0;
}
+/* This is the 'purely bridged' case. 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 another bit of the bridge-nf patch overloads the
+ * '-i' and '-o' iptables interface checks to take
+ * skb->phys{in,out}dev into account as well (so both the real
+ * device and the bridge device will match).
+ */
static unsigned int br_nf_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;
@@ -249,6 +326,29 @@
return 0;
}
+
+/* This hook sees both locally originated IP packets and forwarded
+ * IP packets (in both cases the destination device is a bridge
+ * device). For the sake of interface transparency (i.e. properly
+ * overloading the '-o' option), we steal packets destined to
+ * a bridge device away from the IPv4 FORWARD and OUTPUT hooks,
+ * and reinject them later, when we have determined the real
+ * output device. This reinjecting happens here.
+ *
+ * If skb->physindev is NULL, the bridge-nf code never touched
+ * this packet before, and so the packet was locally originated.
+ * We call the IPv4 LOCAL_OUT hook.
+ *
+ * If skb->physindev isn't NULL, there are two cases:
+ * 1. The packet was IP routed.
+ * 2. The packet was cross-bridge DNAT'ed (see the comment near
+ * PF_BRIDGE/PRE_ROUTING).
+ * In both cases, we call the IPv4 FORWARD hook. In case 1,
+ * if the packet originally came from a bridge device, and in
+ * case 2, skb->physindev will have a bridge device as parent,
+ * so we use that parent device as indev. Otherwise, we just
+ * use physindev.
+ */
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 hookno;
@@ -328,10 +428,10 @@
/* 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, const
struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *))
{
- /* Don't hand locally destined packets to PF_INET/PRE_ROUTING for
- * the second time. */
if (in->hard_start_xmit == br_dev_xmit &&
okfn != br_nf_pre_routing_finish) {
okfn(*pskb);
@@ -341,11 +441,12 @@
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, const
struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *))
{
- /* 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. */
if (out->hard_start_xmit == br_dev_xmit &&
okfn != br_nf_forward_finish &&
okfn != br_nf_local_out_finish &&
_______________________________________________
Bridge mailing list
[EMAIL PROTECTED]
http://www.math.leidenuniv.nl/mailman/listinfo/bridge