diff -urp v3.0/linux/include/linux/if_bridge.h linux/include/linux/if_bridge.h
--- v3.0/linux/include/linux/if_bridge.h	2011-03-20 10:55:55.000000000 +0200
+++ linux/include/linux/if_bridge.h	2011-07-23 01:38:52.475381868 +0300
@@ -42,6 +42,7 @@
 #define BRCTL_SET_PORT_PRIORITY 16
 #define BRCTL_SET_PATH_COST 17
 #define BRCTL_GET_FDB_ENTRIES 18
+#define BRCTL_SET_BRIDGE_IP_MODE 19
 
 #define BR_STATE_DISABLED 0
 #define BR_STATE_LISTENING 1
@@ -69,6 +70,7 @@ struct __bridge_info {
 	__u32 tcn_timer_value;
 	__u32 topology_change_timer_value;
 	__u32 gc_timer_value;
+	__u32 ip_mode;
 };
 
 struct __port_info {
diff -urp v3.0/linux/net/bridge/br_device.c linux/net/bridge/br_device.c
--- v3.0/linux/net/bridge/br_device.c	2011-07-22 09:43:32.758083232 +0300
+++ linux/net/bridge/br_device.c	2011-07-23 01:42:35.754431762 +0300
@@ -361,6 +361,7 @@ void br_dev_setup(struct net_device *dev
 	memcpy(br->group_addr, br_group_address, ETH_ALEN);
 
 	br->stp_enabled = BR_NO_STP;
+	br->ip_mode = 0;
 	br->designated_root = br->bridge_id;
 	br->bridge_max_age = br->max_age = 20 * HZ;
 	br->bridge_hello_time = br->hello_time = 2 * HZ;
diff -urp v3.0/linux/net/bridge/br_input.c linux/net/bridge/br_input.c
--- v3.0/linux/net/bridge/br_input.c	2011-07-22 09:43:32.000000000 +0300
+++ linux/net/bridge/br_input.c	2011-07-23 01:48:33.571381726 +0300
@@ -46,12 +46,15 @@ static int br_pass_frame_up(struct sk_bu
 /* note: already called with rcu_read_lock */
 int br_handle_frame_finish(struct sk_buff *skb)
 {
-	const unsigned char *dest = eth_hdr(skb)->h_dest;
+	unsigned char *dest = eth_hdr(skb)->h_dest;
 	struct net_bridge_port *p = br_port_get_rcu(skb->dev);
 	struct net_bridge *br;
 	struct net_bridge_fdb_entry *dst;
 	struct net_bridge_mdb_entry *mdst;
 	struct sk_buff *skb2;
+	int ip_mode, ret = 0;
+	struct sk_buff *orig_skb = skb;
+	__be16 h_proto;
 
 	if (!p || p->state == BR_STATE_DISABLED)
 		goto drop;
@@ -72,8 +75,14 @@ int br_handle_frame_finish(struct sk_buf
 	/* The packet skb2 goes to the local host (NULL to skip). */
 	skb2 = NULL;
 
+	ip_mode = 0;
+	h_proto = eth_hdr(skb)->h_proto;
 	if (br->dev->flags & IFF_PROMISC)
 		skb2 = skb;
+	else
+		ip_mode = (br->ip_mode != 0) &&
+			(htons(ETH_P_IP) == h_proto ||
+			htons(ETH_P_ARP) == h_proto);
 
 	dst = NULL;
 
@@ -97,21 +106,51 @@ int br_handle_frame_finish(struct sk_buf
 		skb2 = skb;
 		/* Do not forward the packet since it's local. */
 		skb = NULL;
+	} else if (ip_mode) {
+		if (!dst || dst->dst->dev == skb->dev)
+			ip_mode = 0;
+	}
+
+	if (ip_mode) {
+		if (htons(ETH_P_IP) == h_proto) {
+			/* deliver IP on port, not on br */
+			ret = 1;
+			if (skb2) {
+				/* No packet on br */
+				skb2 = NULL;
+			} else
+				skb = NULL;	/* Do not forward IP */
+		} else {
+			/* deliver ARP on port and on br */
+			if (skb2) {
+				ret = 1;
+				/* Preserve original skb, use clone */
+				skb2 = skb_clone(skb2, GFP_ATOMIC);
+				if (skb2)
+					BR_INPUT_SKB_CB(skb2)->brdev = br->dev;
+			}
+		}
+		if (ret && PACKET_OTHERHOST == orig_skb->pkt_type) {
+			memcpy(dest, p->dev->dev_addr, p->dev->addr_len);
+			orig_skb->pkt_type = PACKET_HOST;
+		}
 	}
 
 	if (skb) {
+		struct sk_buff *skb_up = ret ? orig_skb : skb2;
+
 		if (dst) {
 			dst->used = jiffies;
-			br_forward(dst->dst, skb, skb2);
+			br_forward(dst->dst, skb, skb_up);
 		} else
-			br_flood_forward(br, skb, skb2);
+			br_flood_forward(br, skb, skb_up);
 	}
 
 	if (skb2)
-		return br_pass_frame_up(skb2);
+		br_pass_frame_up(skb2);
 
 out:
-	return 0;
+	return ret;
 drop:
 	kfree_skb(skb);
 	goto out;
@@ -195,6 +234,13 @@ forward:
 		if (!compare_ether_addr(p->br->dev->dev_addr, dest))
 			skb->pkt_type = PACKET_HOST;
 
+		if (p->br->ip_mode && !(p->br->dev->flags & IFF_PROMISC)) {
+			if (br_handle_frame_finish(skb)) {
+				*pskb = skb;
+				return RX_HANDLER_PASS;
+			}
+			break;
+		}
 		NF_HOOK(NFPROTO_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
 			br_handle_frame_finish);
 		break;
diff -urp v3.0/linux/net/bridge/br_ioctl.c linux/net/bridge/br_ioctl.c
--- v3.0/linux/net/bridge/br_ioctl.c	2011-07-22 09:43:32.000000000 +0300
+++ linux/net/bridge/br_ioctl.c	2011-07-23 01:38:52.495381693 +0300
@@ -146,6 +146,7 @@ static int old_dev_ioctl(struct net_devi
 		b.tcn_timer_value = br_timer_value(&br->tcn_timer);
 		b.topology_change_timer_value = br_timer_value(&br->topology_change_timer);
 		b.gc_timer_value = br_timer_value(&br->gc_timer);
+		b.ip_mode = br->ip_mode;
 		rcu_read_unlock();
 
 		if (copy_to_user((void __user *)args[1], &b, sizeof(b)))
@@ -289,6 +290,10 @@ static int old_dev_ioctl(struct net_devi
 	case BRCTL_GET_FDB_ENTRIES:
 		return get_fdb_entries(br, (void __user *)args[1],
 				       args[2], args[3]);
+
+	case BRCTL_SET_BRIDGE_IP_MODE:
+		br->ip_mode = args[1]?1:0;
+		return 0;
 	}
 
 	return -EOPNOTSUPP;
diff -urp v3.0/linux/net/bridge/br_private.h linux/net/bridge/br_private.h
--- v3.0/linux/net/bridge/br_private.h	2011-07-22 09:43:32.000000000 +0300
+++ linux/net/bridge/br_private.h	2011-07-23 01:38:52.496381942 +0300
@@ -215,6 +215,7 @@ struct net_bridge
 
 	unsigned char			topology_change;
 	unsigned char			topology_change_detected;
+	unsigned char			ip_mode;
 
 #ifdef CONFIG_BRIDGE_IGMP_SNOOPING
 	unsigned char			multicast_router;
diff -urp v3.0/linux/net/bridge/br_sysfs_br.c linux/net/bridge/br_sysfs_br.c
--- v3.0/linux/net/bridge/br_sysfs_br.c	2011-07-22 09:43:32.000000000 +0300
+++ linux/net/bridge/br_sysfs_br.c	2011-07-23 01:38:52.496381942 +0300
@@ -149,6 +149,29 @@ static ssize_t store_stp_state(struct de
 static DEVICE_ATTR(stp_state, S_IRUGO | S_IWUSR, show_stp_state,
 		   store_stp_state);
 
+static ssize_t show_ipmode(struct device *d,
+			   struct device_attribute *attr, char *buf)
+{
+	struct net_bridge *br = to_bridge(d);
+	return sprintf(buf, "%d\n", br->ip_mode);
+}
+
+static int set_ipmode(struct net_bridge *br, unsigned long val)
+{
+	br->ip_mode = val;
+	return 0;
+}
+
+static ssize_t store_ipmode(struct device *d,
+			    struct device_attribute *attr, const char *buf,
+			    size_t len)
+{
+	return store_bridge_parm(d, buf, len, set_ipmode);
+}
+
+static DEVICE_ATTR(ipmode, S_IRUGO | S_IWUSR, show_ipmode,
+		   store_ipmode);
+
 static ssize_t show_priority(struct device *d, struct device_attribute *attr,
 			     char *buf)
 {
@@ -652,6 +675,7 @@ static struct attribute *bridge_attrs[] 
 	&dev_attr_max_age.attr,
 	&dev_attr_ageing_time.attr,
 	&dev_attr_stp_state.attr,
+	&dev_attr_ipmode.attr,
 	&dev_attr_priority.attr,
 	&dev_attr_bridge_id.attr,
 	&dev_attr_root_id.attr,
