diff -urp v6.9/linux/include/uapi/linux/if_bridge.h linux/include/uapi/linux/if_bridge.h
--- v6.9/linux/include/uapi/linux/if_bridge.h	2024-03-13 10:53:59.000000000 +0200
+++ linux/include/uapi/linux/if_bridge.h	2024-05-21 16:56:36.406861575 +0300
@@ -45,6 +45,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 v6.9/linux/net/bridge/br_device.c linux/net/bridge/br_device.c
--- v6.9/linux/net/bridge/br_device.c	2024-05-21 13:35:02.000000000 +0300
+++ linux/net/bridge/br_device.c	2024-05-21 16:56:36.407861561 +0300
@@ -507,6 +507,7 @@ void br_dev_setup(struct net_device *dev
 
 	ether_addr_copy(br->group_addr, eth_stp_addr);
 
+	br->ip_mode = 0;
 	br->stp_enabled = BR_NO_STP;
 	br->group_fwd_mask = BR_GROUPFWD_DEFAULT;
 	br->group_fwd_mask_required = BR_GROUPFWD_DEFAULT;
diff -urp v6.9/linux/net/bridge/br_input.c linux/net/bridge/br_input.c
--- v6.9/linux/net/bridge/br_input.c	2024-05-21 13:35:02.000000000 +0300
+++ linux/net/bridge/br_input.c	2024-05-21 17:15:15.617628080 +0300
@@ -84,6 +84,8 @@ int br_handle_frame_finish(struct net *n
 	struct net_bridge_mcast *brmctx;
 	struct net_bridge_vlan *vlan;
 	struct net_bridge *br;
+	int ip_mode, ret = 0;
+	__be16 h_proto;
 	bool promisc;
 	u16 vid = 0;
 	u8 state;
@@ -142,6 +144,10 @@ int br_handle_frame_finish(struct net *n
 
 	promisc = !!(br->dev->flags & IFF_PROMISC);
 	local_rcv = promisc;
+	h_proto = eth_hdr(skb)->h_proto;
+	ip_mode = !local_rcv && (br->ip_mode != 0) &&
+		  (htons(ETH_P_IP) == h_proto ||
+		   htons(ETH_P_ARP) == h_proto);
 
 	if (is_multicast_ether_addr(eth_hdr(skb)->h_dest)) {
 		/* by definition the broadcast is also a multicast address */
@@ -201,15 +207,48 @@ int br_handle_frame_finish(struct net *n
 		break;
 	}
 
-	if (dst) {
-		unsigned long now = jiffies;
+	if (ip_mode) {
+		bool fwd = true;
+
+		if (htons(ETH_P_IP) == h_proto) {
+			/* deliver IP on port, not on br */
+			ret = 1;
+			if (local_rcv) {
+				/* No packet on br */
+				local_rcv = false;
+			} else
+				fwd = false;	/* Do not forward IP */
+		} else {
+			/* deliver ARP on port and on br */
+			if (local_rcv) {
+				struct sk_buff *skb2 = skb_clone(skb,
+								 GFP_ATOMIC);
+
+				ret = 1;
+				/* Preserve original skb, use clone */
+				if (skb2) {
+					BR_INPUT_SKB_CB(skb2)->brdev = br->dev;
+					br_pass_frame_up(skb2, promisc);
+				}
+			}
+		}
+		if (ret && PACKET_OTHERHOST == skb->pkt_type) {
+			memcpy(eth_hdr(skb)->h_dest, p->dev->dev_addr,
+			       p->dev->addr_len);
+			skb->pkt_type = PACKET_HOST;
+		}
+		if (!fwd)
+			goto out;
+	}
 
-		if (test_bit(BR_FDB_LOCAL, &dst->flags))
-			return br_pass_frame_up(skb, false);
+	if (dst) {
+		if (!test_bit(BR_FDB_LOCAL, &dst->flags)) {
+			unsigned long now = jiffies;
 
-		if (now != dst->used)
-			dst->used = now;
-		br_forward(dst->dst, skb, local_rcv, false);
+			if (now != dst->used)
+				dst->used = now;
+			br_forward(dst->dst, skb, local_rcv, false);
+		}
 	} else {
 		if (!mcast_hit)
 			br_flood(br, skb, pkt_type, local_rcv, false, vid);
@@ -217,11 +256,11 @@ int br_handle_frame_finish(struct net *n
 			br_multicast_flood(mdst, skb, brmctx, local_rcv, false);
 	}
 
-	if (local_rcv)
-		return br_pass_frame_up(skb, promisc);
+	if (local_rcv && !ret)
+		br_pass_frame_up(skb, promisc);
 
 out:
-	return 0;
+	return ret;
 drop:
 	kfree_skb(skb);
 	goto out;
@@ -421,6 +460,14 @@ defer_stp_filtering:
 		if (ether_addr_equal(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(dev_net(skb->dev), NULL,
+						   skb)) {
+				*pskb = skb;
+				return RX_HANDLER_PASS;
+			}
+			break;
+		}
 		return nf_hook_bridge_pre(skb, pskb);
 	default:
 drop:
diff -urp v6.9/linux/net/bridge/br_ioctl.c linux/net/bridge/br_ioctl.c
--- v6.9/linux/net/bridge/br_ioctl.c	2023-01-14 15:07:35.000000000 +0200
+++ linux/net/bridge/br_ioctl.c	2024-05-21 16:56:36.408861549 +0300
@@ -184,6 +184,7 @@ int br_dev_siocdevprivate(struct net_dev
 		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_work.timer);
+		b.ip_mode = br->ip_mode;
 		rcu_read_unlock();
 
 		if (copy_to_user((void __user *)args[1], &b, sizeof(b)))
@@ -322,6 +323,14 @@ int br_dev_siocdevprivate(struct net_dev
 	case BRCTL_GET_FDB_ENTRIES:
 		return get_fdb_entries(br, argp, args[2], args[3]);
 
+	case BRCTL_SET_BRIDGE_IP_MODE:
+		if (!ns_capable(dev_net(dev)->user_ns, CAP_NET_ADMIN))
+			return -EPERM;
+
+		br->ip_mode = args[1]?1:0;
+		ret = 0;
+		break;
+
 	default:
 		ret = -EOPNOTSUPP;
 	}
diff -urp v6.9/linux/net/bridge/br_private.h linux/net/bridge/br_private.h
--- v6.9/linux/net/bridge/br_private.h	2024-05-21 13:35:02.000000000 +0300
+++ linux/net/bridge/br_private.h	2024-05-21 16:56:36.410861523 +0300
@@ -526,6 +526,7 @@ struct net_bridge {
 	u32				root_path_cost;
 
 	u8				group_addr[ETH_ALEN];
+	unsigned char			ip_mode;
 
 	enum {
 		BR_NO_STP, 		/* no spanning tree */
diff -urp v6.9/linux/net/bridge/br_sysfs_br.c linux/net/bridge/br_sysfs_br.c
--- v6.9/linux/net/bridge/br_sysfs_br.c	2023-01-14 15:07:35.000000000 +0200
+++ linux/net/bridge/br_sysfs_br.c	2024-05-21 16:56:36.410861523 +0300
@@ -196,6 +196,28 @@ static ssize_t group_fwd_mask_store(stru
 }
 static DEVICE_ATTR_RW(group_fwd_mask);
 
+static ssize_t ipmode_show(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,
+		      struct netlink_ext_ack *extack)
+{
+	br->ip_mode = val;
+	return 0;
+}
+
+static ssize_t ipmode_store(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_RW(ipmode);
+
 static ssize_t priority_show(struct device *d, struct device_attribute *attr,
 			     char *buf)
 {
@@ -940,6 +962,7 @@ static struct attribute *bridge_attrs[]
 	&dev_attr_ageing_time.attr,
 	&dev_attr_stp_state.attr,
 	&dev_attr_group_fwd_mask.attr,
+	&dev_attr_ipmode.attr,
 	&dev_attr_priority.attr,
 	&dev_attr_bridge_id.attr,
 	&dev_attr_root_id.attr,
