ipt_physdev update (--physdev-is-{in,out,bridged}) by Bart de Schuymer
diff --git a/extensions/libipt_physdev.c b/extensions/libipt_physdev.c
index 30fcce9..9149d90 100644
--- a/extensions/libipt_physdev.c
+++ b/extensions/libipt_physdev.c
@@ -17,14 +17,20 @@
 {
 	printf(
 "physdev v%s options:\n"
-" --physdev-in [!] input name[+]	bridge port name ([+] for wildcard)\n"
+" --physdev-in [!] input name[+]		bridge port name ([+] for wildcard)\n"
 " --physdev-out [!] output name[+]	bridge port name ([+] for wildcard)\n"
+" [!] --physdev-is-in			arrived on a bridge device\n"
+" [!] --physdev-is-out			will leave on a bridge device\n"
+" [!] --physdev-is-bridged		it's a bridged packet\n"
 "\n", IPTABLES_VERSION);
 }
 
 static struct option opts[] = {
 	{ "physdev-in", 1, 0, '1' },
 	{ "physdev-out", 1, 0, '2' },
+	{ "physdev-is-in", 0, 0, '3' },
+	{ "physdev-is-out", 0, 0, '4' },
+	{ "physdev-is-bridged", 0, 0, '5' },
 	{0}
 };
 
@@ -83,26 +89,56 @@
 
 	switch (c) {
 	case '1':
-		if (*flags & IPT_PHYSDEV_OP_MATCH_IN)
-			exit_error(PARAMETER_PROBLEM,
-				   "multiple --physdev-in not allowed");
+		if (*flags & IPT_PHYSDEV_OP_IN)
+			goto multiple_use;
 		check_inverse(optarg, &invert, &optind, 0);
 		parse_interface(argv[optind-1], info->physindev, info->in_mask);
 		if (invert)
-			info->invert |= IPT_PHYSDEV_OP_MATCH_IN;
-		*flags |= IPT_PHYSDEV_OP_MATCH_IN;
+			info->invert |= IPT_PHYSDEV_OP_IN;
+		info->bitmask |= IPT_PHYSDEV_OP_IN;
+		*flags |= IPT_PHYSDEV_OP_IN;
 		break;
 
 	case '2':
-		if (*flags & IPT_PHYSDEV_OP_MATCH_OUT)
-			exit_error(PARAMETER_PROBLEM,
-				   "multiple --physdev-out not allowed");
+		if (*flags & IPT_PHYSDEV_OP_OUT)
+			goto multiple_use;
 		check_inverse(optarg, &invert, &optind, 0);
 		parse_interface(argv[optind-1], info->physoutdev,
 				info->out_mask);
 		if (invert)
-			info->invert |= IPT_PHYSDEV_OP_MATCH_OUT;
-		*flags |= IPT_PHYSDEV_OP_MATCH_OUT;
+			info->invert |= IPT_PHYSDEV_OP_OUT;
+		info->bitmask |= IPT_PHYSDEV_OP_OUT;
+		*flags |= IPT_PHYSDEV_OP_OUT;
+		break;
+
+	case '3':
+		if (*flags & IPT_PHYSDEV_OP_ISIN)
+			goto multiple_use;
+		check_inverse(optarg, &invert, &optind, 0);
+		info->bitmask |= IPT_PHYSDEV_OP_ISIN;
+		if (invert)
+			info->invert |= IPT_PHYSDEV_OP_ISIN;
+		*flags |= IPT_PHYSDEV_OP_ISIN;
+		break;
+
+	case '4':
+		if (*flags & IPT_PHYSDEV_OP_ISOUT)
+			goto multiple_use;
+		check_inverse(optarg, &invert, &optind, 0);
+		info->bitmask |= IPT_PHYSDEV_OP_ISOUT;
+		if (invert)
+			info->invert |= IPT_PHYSDEV_OP_ISOUT;
+		*flags |= IPT_PHYSDEV_OP_ISOUT;
+		break;
+
+	case '5':
+		if (*flags & IPT_PHYSDEV_OP_BRIDGED)
+			goto multiple_use;
+		check_inverse(optarg, &invert, &optind, 0);
+		if (invert)
+			info->invert |= IPT_PHYSDEV_OP_BRIDGED;
+		*flags |= IPT_PHYSDEV_OP_BRIDGED;
+		info->bitmask |= IPT_PHYSDEV_OP_BRIDGED;
 		break;
 
 	default:
@@ -110,26 +146,16 @@
 	}
 
 	return 1;
+multiple_use:
+	exit_error(PARAMETER_PROBLEM,
+	   "multiple use of the same physdev option is not allowed");
+
 }
 
 static void final_check(unsigned int flags)
 {
-}
-
-static void print_iface(u_int8_t invert, char *dev, char *prefix)
-{
-	char iface[IFNAMSIZ+2];
-
-	if (invert) {
-		iface[0] = '!';
-		iface[1] = '\0';
-	} else
-		iface[0] = '\0';
-
-	if (dev[0] != '\0') {
-		strcat(iface, dev);
-		printf("%s%s", prefix, iface);
-	}
+	if (flags == 0)
+		exit_error(PARAMETER_PROBLEM, "PHYSDEV: no physdev option specified");
 }
 
 static void
@@ -141,10 +167,22 @@
 		(struct ipt_physdev_info*)match->data;
 
 	printf("PHYSDEV match");
-	print_iface(info->invert & IPT_PHYSDEV_OP_MATCH_IN, info->physindev,
-		    " physindev=");
-	print_iface(info->invert & IPT_PHYSDEV_OP_MATCH_OUT, info->physoutdev,
-		    " physoutdev=");
+	if (info->bitmask & IPT_PHYSDEV_OP_ISIN)
+		printf("%s --physdev-is-in",
+		       info->invert & IPT_PHYSDEV_OP_ISIN ? " !":"");
+	if (info->bitmask & IPT_PHYSDEV_OP_IN)
+		printf("%s --physdev-in %s",
+		(info->invert & IPT_PHYSDEV_OP_IN) ? " !":"", info->physindev);
+
+	if (info->bitmask & IPT_PHYSDEV_OP_ISOUT)
+		printf("%s --physdev-is-out",
+		       info->invert & IPT_PHYSDEV_OP_ISOUT ? " !":"");
+	if (info->bitmask & IPT_PHYSDEV_OP_OUT)
+		printf("%s --physdev-out %s",
+		(info->invert & IPT_PHYSDEV_OP_OUT) ? " !":"", info->physoutdev);
+	if (info->bitmask & IPT_PHYSDEV_OP_BRIDGED)
+		printf("%s --physdev-is-bridged",
+		       info->invert & IPT_PHYSDEV_OP_BRIDGED ? " !":"");
 	printf(" ");
 }
 
@@ -153,10 +191,22 @@
 	struct ipt_physdev_info *info =
 		(struct ipt_physdev_info*)match->data;
 
-	print_iface(info->invert & IPT_PHYSDEV_OP_MATCH_IN, info->physindev,
-		    "--physdev-in ");
-	print_iface(info->invert & IPT_PHYSDEV_OP_MATCH_OUT, info->physoutdev,
-		    "--physdev-out ");
+	if (info->bitmask & IPT_PHYSDEV_OP_ISIN)
+		printf("%s --physdev-is-in",
+		       info->invert & IPT_PHYSDEV_OP_ISIN ? " !":"");
+	if (info->bitmask & IPT_PHYSDEV_OP_IN)
+		printf("%s --physdev-in %s",
+		(info->invert & IPT_PHYSDEV_OP_IN) ? " !":"", info->physindev);
+
+	if (info->bitmask & IPT_PHYSDEV_OP_ISOUT)
+		printf("%s --physdev-is-out",
+		       info->invert & IPT_PHYSDEV_OP_ISOUT ? " !":"");
+	if (info->bitmask & IPT_PHYSDEV_OP_OUT)
+		printf("%s --physdev-out %s",
+		(info->invert & IPT_PHYSDEV_OP_OUT) ? " !":"", info->physoutdev);
+	if (info->bitmask & IPT_PHYSDEV_OP_BRIDGED)
+		printf("%s --physdev-is-bridged",
+		       info->invert & IPT_PHYSDEV_OP_BRIDGED ? " !":"");
 	printf(" ");
 }
 
diff --git a/include/linux/netfilter_ipv4/ipt_physdev.h b/include/linux/netfilter_ipv4/ipt_physdev.h
index 668e1a9..01684a1 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/iptables.8 b/iptables.8
index c1039fb..df5951a 100644
--- a/iptables.8
+++ b/iptables.8
@@ -557,8 +557,7 @@
 supporting this feature)
 .SS physdev
 This module matches on the bridge port input and output devices enslaved
-to a bridge device. This is only useful if the input device or output device
-is a bridge device. This module is a part of the infrastructure that enables
+to a bridge device. This module is a part of the infrastructure that enables
 a transparent bridging IP firewall and is only useful for kernel versions
 above version 2.5.44.
 .TP
@@ -570,7 +569,8 @@
 and
 .B PREROUTING
 chains). If the interface name ends in a "+", then any
-interface which begins with this name will match.
+interface which begins with this name will match. If the packet didn't arrive
+through a bridge device, this packet won't match this option, unless '!' is used.
 .TP
 .B --physdev-out name
 Name of a bridge port via which a packet is going to be sent (for packets
@@ -585,7 +585,19 @@
 .B OUTPUT
 chains one cannot match on the bridge output port, however one can in the
 .B "filter OUTPUT"
-chain.
+chain. If the packet won't leave by a bridge device or it is yet unknown what
+the output device will be, then the packet won't match this option, unless
+'!' is used.
+.TP
+.B --physdev-is-in
+Matches if the packet has entered through a bridge interface.
+.TP
+.B --physdev-is-out
+Matches if the packet will leave through a bridge interface.
+.TP
+.B --physdev-is-bridged
+Matches if the packet is being bridged and therefore is not being routed.
+This is only useful in the FORWARD and POSTROUTING chains.
 .SS pkttype
 This module matches the link-layer packet type.
 .TP