summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/linux/netfilter_bridge.h1
-rw-r--r--include/linux/netfilter_ipv4/ipt_physdev.h9
-rw-r--r--net/bridge/br_netfilter.c2
-rw-r--r--net/ipv4/netfilter/ipt_physdev.c58
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;
}