diff -urp v2.6.22/linux/include/linux/if_bridge.h linux/include/linux/if_bridge.h
--- v2.6.22/linux/include/linux/if_bridge.h	2007-07-10 09:18:42.000000000 +0300
+++ linux/include/linux/if_bridge.h	2007-07-12 08:41:27.000000000 +0300
@@ -44,6 +44,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
@@ -72,6 +73,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 v2.6.22/linux/net/bridge/br_if.c linux/net/bridge/br_if.c
--- v2.6.22/linux/net/bridge/br_if.c	2007-07-10 09:18:43.000000000 +0300
+++ linux/net/bridge/br_if.c	2007-07-12 08:47:23.000000000 +0300
@@ -206,6 +206,7 @@ static struct net_device *new_bridge_dev
 
 	br->feature_mask = dev->features;
 	br->stp_enabled = BR_NO_STP;
+	br->ip_mode = 0;
 	br->designated_root = br->bridge_id;
 	br->root_path_cost = 0;
 	br->root_port = 0;
diff -urp v2.6.22/linux/net/bridge/br_input.c linux/net/bridge/br_input.c
--- v2.6.22/linux/net/bridge/br_input.c	2007-07-10 09:18:43.000000000 +0300
+++ linux/net/bridge/br_input.c	2007-07-12 09:18:28.000000000 +0300
@@ -22,7 +22,7 @@
 /* Bridge group multicast address 802.1d (pg 51). */
 const u8 br_group_address[ETH_ALEN] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 };
 
-static void br_pass_frame_up(struct net_bridge *br, struct sk_buff *skb)
+static void br_pass_frame_up(struct net_bridge *br, struct sk_buff *skb, int force_local)
 {
 	struct net_device *indev;
 
@@ -31,19 +31,54 @@ static void br_pass_frame_up(struct net_
 
 	indev = skb->dev;
 	skb->dev = br->dev;
+	if (skb->pkt_type == PACKET_OTHERHOST && force_local)
+		skb->pkt_type = PACKET_HOST;
 
 	NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_IN, skb, indev, NULL,
 		netif_receive_skb);
 }
 
+#define frame_up(br, skb, clone, force_local) {				\
+	if (ip_mode) {							\
+		if (htons(ETH_P_ARP) == eth_hdr(skb)->h_proto) {	\
+			struct sk_buff *skb2;				\
+									\
+			if ((skb2 = skb_clone(skb, GFP_ATOMIC)))	\
+				br_pass_frame_up(br, skb2, 1);		\
+		}							\
+		passedup = 1;						\
+	} else if (clone) {						\
+		struct sk_buff *skb2;					\
+									\
+		skb2 = skb_clone(skb, GFP_ATOMIC);			\
+		if (skb2) {						\
+			passedup = 1;					\
+			br_pass_frame_up(br, skb2,			\
+					 force_local);			\
+		}							\
+	} else {							\
+		br_pass_frame_up(br, skb, force_local);			\
+	}								\
+}
+
+#define handle_frame(br, p, skb)					\
+	if (ip_mode && passedup) { 					\
+		ret = 1;						\
+		if (PACKET_OTHERHOST != skb->pkt_type)			\
+			goto out;					\
+		memcpy(dest, p->dev->dev_addr, p->dev->addr_len);	\
+		skb->pkt_type = PACKET_HOST;				\
+		goto out;						\
+	}
+
 /* note: already called with rcu_read_lock (preempt_disabled) */
 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 = rcu_dereference(skb->dev->br_port);
 	struct net_bridge *br;
 	struct net_bridge_fdb_entry *dst;
-	int passedup = 0;
+	int passedup = 0, ip_mode, ret = 0;
 
 	if (!p || p->state == BR_STATE_DISABLED)
 		goto drop;
@@ -56,32 +91,41 @@ int br_handle_frame_finish(struct sk_buf
 		goto drop;
 
 	if (br->dev->flags & IFF_PROMISC) {
-		struct sk_buff *skb2;
-
-		skb2 = skb_clone(skb, GFP_ATOMIC);
-		if (skb2 != NULL) {
-			passedup = 1;
-			br_pass_frame_up(br, skb2);
-		}
+		ip_mode = 0;
+		frame_up(br, skb, 1, 0);
+	} else {
+		ip_mode = (br->ip_mode != 0);
 	}
 
 	if (is_multicast_ether_addr(dest)) {
 		br->statistics.multicast++;
-		br_flood_forward(br, skb, !passedup);
+		br_flood_forward(br, skb, !passedup || ip_mode);
 		if (!passedup)
-			br_pass_frame_up(br, skb);
+			frame_up(br, skb, 0, 1);
+		handle_frame(br, p, skb);
 		goto out;
 	}
 
 	dst = __br_fdb_get(br, dest);
-	if (dst != NULL && dst->is_local) {
-		if (!passedup)
-			br_pass_frame_up(br, skb);
-		else
+	if (dst != NULL &&
+	    (dst->is_local ||
+	     (ip_mode && dst->dst->dev != skb->dev &&
+	      htons(ETH_P_IP) == eth_hdr(skb)->h_proto))) {
+		if (!passedup) {
+			frame_up(br, skb, 0, 1);
+			handle_frame(br, p, skb);
+		} else {
+			handle_frame(br, p, skb);
 			kfree_skb(skb);
+		}
 		goto out;
 	}
 
+	if (ip_mode && passedup) {
+		ip_mode = 0;
+		frame_up(br, skb, 1, 0);
+	}
+
 	if (dst != NULL) {
 		br_forward(dst->dst, skb);
 		goto out;
@@ -90,7 +134,7 @@ int br_handle_frame_finish(struct sk_buf
 	br_flood_forward(br, skb, 0);
 
 out:
-	return 0;
+	return ret;
 drop:
 	kfree_skb(skb);
 	goto out;
@@ -159,6 +203,11 @@ struct sk_buff *br_handle_frame(struct n
 		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))
+				return skb;
+			break;
+		}
 		NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
 			br_handle_frame_finish);
 		break;
diff -urp v2.6.22/linux/net/bridge/br_ioctl.c linux/net/bridge/br_ioctl.c
--- v2.6.22/linux/net/bridge/br_ioctl.c	2007-07-10 09:18:43.000000000 +0300
+++ linux/net/bridge/br_ioctl.c	2007-07-12 08:41:27.000000000 +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)))
@@ -305,6 +306,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 v2.6.22/linux/net/bridge/br_private.h linux/net/bridge/br_private.h
--- v2.6.22/linux/net/bridge/br_private.h	2007-07-10 09:18:43.000000000 +0300
+++ linux/net/bridge/br_private.h	2007-07-12 08:41:27.000000000 +0300
@@ -119,6 +119,7 @@ struct net_bridge
 
 	unsigned char			topology_change;
 	unsigned char			topology_change_detected;
+	unsigned char			ip_mode;
 
 	struct timer_list		hello_timer;
 	struct timer_list		tcn_timer;
diff -urp v2.6.22/linux/net/bridge/br_sysfs_br.c linux/net/bridge/br_sysfs_br.c
--- v2.6.22/linux/net/bridge/br_sysfs_br.c	2007-07-10 09:18:43.000000000 +0300
+++ linux/net/bridge/br_sysfs_br.c	2007-07-12 08:41:27.000000000 +0300
@@ -165,6 +165,28 @@ 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 void set_ipmode(struct net_bridge *br, unsigned long val)
+{
+	br->ip_mode = val;
+}
+
+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)
 {
@@ -333,6 +355,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,
