diff options
| -rw-r--r-- | include/linux/netfilter_bridge.h | 1 | ||||
| -rw-r--r-- | include/linux/netfilter_ipv4/ipt_physdev.h | 9 | ||||
| -rw-r--r-- | net/bridge/br_netfilter.c | 2 | ||||
| -rw-r--r-- | net/ipv4/netfilter/ipt_physdev.c | 58 |
4 files changed, 59 insertions, 11 deletions
diff --git a/include/linux/netfilter_bridge.h b/include/linux/netfilter_bridge.h index 0f07bda8a7cf..016991871a12 100644 --- a/include/linux/netfilter_bridge.h +++ b/include/linux/netfilter_bridge.h @@ -30,6 +30,7 @@ #define BRNF_PKT_TYPE 0x01 #define BRNF_BRIDGED_DNAT 0x02 #define BRNF_DONT_TAKE_PARENT 0x04 +#define BRNF_BRIDGED 0x08 enum nf_br_hook_priorities { NF_BR_PRI_FIRST = INT_MIN, diff --git a/include/linux/netfilter_ipv4/ipt_physdev.h b/include/linux/netfilter_ipv4/ipt_physdev.h index 668e1a9faf6f..01684a12d672 100644 --- a/include/linux/netfilter_ipv4/ipt_physdev.h +++ b/include/linux/netfilter_ipv4/ipt_physdev.h @@ -5,11 +5,16 @@ #include <linux/if.h> #endif -#define IPT_PHYSDEV_OP_MATCH_IN 0x01 -#define IPT_PHYSDEV_OP_MATCH_OUT 0x02 +#define IPT_PHYSDEV_OP_IN 0x01 +#define IPT_PHYSDEV_OP_OUT 0x02 +#define IPT_PHYSDEV_OP_BRIDGED 0x04 +#define IPT_PHYSDEV_OP_ISIN 0x08 +#define IPT_PHYSDEV_OP_ISOUT 0x10 +#define IPT_PHYSDEV_OP_MASK (0x20 - 1) struct ipt_physdev_info { u_int8_t invert; + u_int8_t bitmask; char physindev[IFNAMSIZ]; char in_mask[IFNAMSIZ]; char physoutdev[IFNAMSIZ]; diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c index 6ba4dd7459ac..e0a61b6b9ac2 100644 --- a/net/bridge/br_netfilter.c +++ b/net/bridge/br_netfilter.c @@ -348,6 +348,8 @@ static unsigned int br_nf_forward(unsigned int hook, struct sk_buff **pskb, if (skb->pkt_type == PACKET_OTHERHOST) { skb->pkt_type = PACKET_HOST; nf_bridge->mask |= BRNF_PKT_TYPE; + /* The physdev module checks on this */ + nf_bridge->mask |= BRNF_BRIDGED; } nf_bridge->physoutdev = skb->dev; diff --git a/net/ipv4/netfilter/ipt_physdev.c b/net/ipv4/netfilter/ipt_physdev.c index 82f72776a02f..642d32d3288b 100644 --- a/net/ipv4/netfilter/ipt_physdev.c +++ b/net/ipv4/netfilter/ipt_physdev.c @@ -4,6 +4,9 @@ #include <linux/skbuff.h> #include <linux/netfilter_ipv4/ipt_physdev.h> #include <linux/netfilter_ipv4/ip_tables.h> +#include <linux/netfilter_bridge.h> +#define MATCH 1 +#define NOMATCH 0 static int match(const struct sk_buff *skb, @@ -25,29 +28,62 @@ match(const struct sk_buff *skb, /* Not a bridged IP packet or no info available yet: * LOCAL_OUT/mangle and LOCAL_OUT/nat don't know if * the destination device will be a bridge. */ - if (!(nf_bridge = skb->nf_bridge)) - return 1; + if (!(nf_bridge = skb->nf_bridge)) { + /* Return MATCH if the invert flags of the used options are on */ + if ((info->bitmask & IPT_PHYSDEV_OP_BRIDGED) && + !(info->invert & IPT_PHYSDEV_OP_BRIDGED)) + return NOMATCH; + if ((info->bitmask & IPT_PHYSDEV_OP_ISIN) && + !(info->invert & IPT_PHYSDEV_OP_ISIN)) + return NOMATCH; + if ((info->bitmask & IPT_PHYSDEV_OP_ISOUT) && + !(info->invert & IPT_PHYSDEV_OP_ISOUT)) + return NOMATCH; + if ((info->bitmask & IPT_PHYSDEV_OP_IN) && + !(info->invert & IPT_PHYSDEV_OP_IN)) + return NOMATCH; + if ((info->bitmask & IPT_PHYSDEV_OP_OUT) && + !(info->invert & IPT_PHYSDEV_OP_OUT)) + return NOMATCH; + return MATCH; + } - indev = nf_bridge->physindev ? nf_bridge->physindev->name : nulldevname; - outdev = nf_bridge->physoutdev ? - nf_bridge->physoutdev->name : nulldevname; + /* This only makes sense in the FORWARD and POSTROUTING chains */ + if ((info->bitmask & IPT_PHYSDEV_OP_BRIDGED) && + (!!(nf_bridge->mask & BRNF_BRIDGED) ^ + !(info->invert & IPT_PHYSDEV_OP_BRIDGED))) + return NOMATCH; + if ((info->bitmask & IPT_PHYSDEV_OP_ISIN && + (!nf_bridge->physindev ^ !!(info->invert & IPT_PHYSDEV_OP_ISIN))) || + (info->bitmask & IPT_PHYSDEV_OP_ISOUT && + (!nf_bridge->physoutdev ^ !!(info->invert & IPT_PHYSDEV_OP_ISOUT)))) + return NOMATCH; + + if (!(info->bitmask & IPT_PHYSDEV_OP_IN)) + goto match_outdev; + indev = nf_bridge->physindev ? nf_bridge->physindev->name : nulldevname; for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) { ret |= (((const unsigned long *)indev)[i] ^ ((const unsigned long *)info->physindev)[i]) & ((const unsigned long *)info->in_mask)[i]; } - if ((ret == 0) ^ !(info->invert & IPT_PHYSDEV_OP_MATCH_IN)) - return 0; + if ((ret == 0) ^ !(info->invert & IPT_PHYSDEV_OP_IN)) + return NOMATCH; +match_outdev: + if (!(info->bitmask & IPT_PHYSDEV_OP_OUT)) + return MATCH; + outdev = nf_bridge->physoutdev ? + nf_bridge->physoutdev->name : nulldevname; for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) { ret |= (((const unsigned long *)outdev)[i] ^ ((const unsigned long *)info->physoutdev)[i]) & ((const unsigned long *)info->out_mask)[i]; } - return (ret != 0) ^ !(info->invert & IPT_PHYSDEV_OP_MATCH_OUT); + return (ret != 0) ^ !(info->invert & IPT_PHYSDEV_OP_OUT); } static int @@ -57,9 +93,13 @@ checkentry(const char *tablename, unsigned int matchsize, unsigned int hook_mask) { + const struct ipt_physdev_info *info = matchinfo; + if (matchsize != IPT_ALIGN(sizeof(struct ipt_physdev_info))) return 0; - + if (!(info->bitmask & IPT_PHYSDEV_OP_MASK) || + info->bitmask & ~IPT_PHYSDEV_OP_MASK) + return 0; return 1; } |
