diff -urp v7.1/linux/include/uapi/linux/if_bridge.h linux/include/uapi/linux/if_bridge.h --- v7.1/linux/include/uapi/linux/if_bridge.h 2026-01-08 20:23:34.000000000 +0200 +++ linux/include/uapi/linux/if_bridge.h 2026-08-10 16:43:12.354673418 +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 v7.1/linux/net/bridge/br_device.c linux/net/bridge/br_device.c --- v7.1/linux/net/bridge/br_device.c 2026-08-10 13:42:23.000000000 +0300 +++ linux/net/bridge/br_device.c 2026-08-10 16:43:12.355673408 +0300 @@ -517,6 +517,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->stp_mode = BR_STP_MODE_AUTO; br->group_fwd_mask = BR_GROUPFWD_DEFAULT; diff -urp v7.1/linux/net/bridge/br_input.c linux/net/bridge/br_input.c --- v7.1/linux/net/bridge/br_input.c 2026-05-20 19:39:30.000000000 +0300 +++ linux/net/bridge/br_input.c 2026-08-10 16:43:12.356673397 +0300 @@ -85,6 +85,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; @@ -145,6 +147,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 */ @@ -215,15 +221,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 != READ_ONCE(dst->used)) - WRITE_ONCE(dst->used, now); - br_forward(dst->dst, skb, local_rcv, false); + if (now != READ_ONCE(dst->used)) + WRITE_ONCE(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); @@ -231,11 +270,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_reason(skb, reason); goto out; @@ -439,6 +478,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: reason = SKB_DROP_REASON_BRIDGE_INGRESS_STP_STATE; diff -urp v7.1/linux/net/bridge/br_ioctl.c linux/net/bridge/br_ioctl.c --- v7.1/linux/net/bridge/br_ioctl.c 2026-05-20 19:39:30.000000000 +0300 +++ linux/net/bridge/br_ioctl.c 2026-08-10 16:43:12.357783609 +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 v7.1/linux/net/bridge/br_private.h linux/net/bridge/br_private.h --- v7.1/linux/net/bridge/br_private.h 2026-08-10 13:42:23.000000000 +0300 +++ linux/net/bridge/br_private.h 2026-08-10 16:43:12.358421019 +0300 @@ -536,6 +536,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 v7.1/linux/net/bridge/br_sysfs_br.c linux/net/bridge/br_sysfs_br.c --- v7.1/linux/net/bridge/br_sysfs_br.c 2026-05-20 19:39:30.000000000 +0300 +++ linux/net/bridge/br_sysfs_br.c 2026-08-10 16:43:12.358673374 +0300 @@ -197,6 +197,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) { @@ -939,6 +961,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,