Allow use of --physdev-out for routed packets by querying the bridge fdb. Signed-off-by: Philip Craig --- linux-2.6.x/net/netfilter/xt_physdev.c 26 Apr 2007 11:17:49 -0000 1.1.1.6 +++ linux-2.6.x/net/netfilter/xt_physdev.c 12 Jul 2007 06:09:19 -0000 @@ -14,6 +14,10 @@ #include #include #include +#include +#include +#include +#include "../bridge/br_private.h" #define MATCH 1 #define NOMATCH 0 @@ -23,6 +27,42 @@ MODULE_DESCRIPTION("iptables bridge phys MODULE_ALIAS("ipt_physdev"); MODULE_ALIAS("ip6t_physdev"); +static inline struct nf_bridge_info *nf_bridge_alloc(struct sk_buff *skb) +{ + skb->nf_bridge = kzalloc(sizeof(struct nf_bridge_info), GFP_ATOMIC); + if (likely(skb->nf_bridge)) + atomic_set(&(skb->nf_bridge->use), 1); + + return skb->nf_bridge; +} + +static void get_outdev(const struct sk_buff* skb, const struct net_device *out) +{ + struct neighbour *neigh; + struct nf_bridge_info *nf_bridge; + const unsigned char *dest; + struct net_bridge_fdb_entry *fdb; + + nf_bridge = skb->nf_bridge; + if ((nf_bridge && nf_bridge->physoutdev) || + !out || out->hard_start_xmit != br_dev_xmit || !br_fdb_get_hook) + return; + + if (!nf_bridge && !(nf_bridge = nf_bridge_alloc(skb))) + return; + nf_bridge->physoutdev = out; /* so that --physdev-is-out matches */ + + neigh = skb->dst->neighbour; + if (!neigh || neigh_event_send(neigh, NULL)) + return; + dest = neigh->ha; + if (!is_multicast_ether_addr(dest) && + (fdb = br_fdb_get_hook(netdev_priv(out), dest)) != NULL) { + nf_bridge->physoutdev = fdb->dst->dev; + br_fdb_put_hook(fdb); + } +} + static int match(const struct sk_buff *skb, const struct net_device *in, @@ -51,16 +91,10 @@ match(const struct sk_buff *skb, if ((info->bitmask & XT_PHYSDEV_OP_ISIN) && !(info->invert & XT_PHYSDEV_OP_ISIN)) return NOMATCH; - if ((info->bitmask & XT_PHYSDEV_OP_ISOUT) && - !(info->invert & XT_PHYSDEV_OP_ISOUT)) - return NOMATCH; if ((info->bitmask & XT_PHYSDEV_OP_IN) && !(info->invert & XT_PHYSDEV_OP_IN)) return NOMATCH; - if ((info->bitmask & XT_PHYSDEV_OP_OUT) && - !(info->invert & XT_PHYSDEV_OP_OUT)) - return NOMATCH; - return MATCH; + goto match_outdev; } /* This only makes sense in the FORWARD and POSTROUTING chains */ @@ -69,10 +103,8 @@ match(const struct sk_buff *skb, !(info->invert & XT_PHYSDEV_OP_BRIDGED))) return NOMATCH; - if ((info->bitmask & XT_PHYSDEV_OP_ISIN && - (!nf_bridge->physindev ^ !!(info->invert & XT_PHYSDEV_OP_ISIN))) || - (info->bitmask & XT_PHYSDEV_OP_ISOUT && - (!nf_bridge->physoutdev ^ !!(info->invert & XT_PHYSDEV_OP_ISOUT)))) + if (info->bitmask & XT_PHYSDEV_OP_ISIN && + (!nf_bridge->physindev ^ !!(info->invert & XT_PHYSDEV_OP_ISIN))) return NOMATCH; if (!(info->bitmask & XT_PHYSDEV_OP_IN)) @@ -88,8 +120,26 @@ match(const struct sk_buff *skb, return NOMATCH; match_outdev: - if (!(info->bitmask & XT_PHYSDEV_OP_OUT)) + if (!(info->bitmask & (XT_PHYSDEV_OP_ISOUT | XT_PHYSDEV_OP_OUT))) + return MATCH; + get_outdev(skb, out); + if (!(nf_bridge = skb->nf_bridge)) { + if ((info->bitmask & XT_PHYSDEV_OP_ISOUT) && + !(info->invert & XT_PHYSDEV_OP_ISOUT)) + return NOMATCH; + if ((info->bitmask & XT_PHYSDEV_OP_OUT) && + !(info->invert & XT_PHYSDEV_OP_OUT)) + return NOMATCH; return MATCH; + } + + if (info->bitmask & XT_PHYSDEV_OP_ISOUT && + (!nf_bridge->physoutdev ^ !!(info->invert & XT_PHYSDEV_OP_ISOUT))) + return NOMATCH; + + if (!(info->bitmask & XT_PHYSDEV_OP_IN)) + return MATCH; + goto match_outdev; outdev = nf_bridge->physoutdev ? nf_bridge->physoutdev->name : nulldevname; for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned int); i++) { @@ -113,17 +163,6 @@ checkentry(const char *tablename, if (!(info->bitmask & XT_PHYSDEV_OP_MASK) || info->bitmask & ~XT_PHYSDEV_OP_MASK) return 0; - if (info->bitmask & XT_PHYSDEV_OP_OUT && - (!(info->bitmask & XT_PHYSDEV_OP_BRIDGED) || - info->invert & XT_PHYSDEV_OP_BRIDGED) && - hook_mask & ((1 << NF_IP_LOCAL_OUT) | (1 << NF_IP_FORWARD) | - (1 << NF_IP_POST_ROUTING))) { - printk(KERN_WARNING "physdev match: using --physdev-out in the " - "OUTPUT, FORWARD and POSTROUTING chains for non-bridged " - "traffic is not supported anymore.\n"); - if (hook_mask & (1 << NF_IP_LOCAL_OUT)) - return 0; - } return 1; }