[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20251021144410.257905-9-skorodumov.dmitry@huawei.com>
Date: Tue, 21 Oct 2025 17:44:10 +0300
From: Dmitry Skorodumov <skorodumov.dmitry@...wei.com>
To: <netdev@...r.kernel.org>, <linux-kernel@...r.kernel.org>
CC: <andrey.bokhanko@...wei.com>, Dmitry Skorodumov
<skorodumov.dmitry@...wei.com>, Andrew Lunn <andrew+netdev@...n.ch>, "David
S. Miller" <davem@...emloft.net>, Eric Dumazet <edumazet@...gle.com>, Jakub
Kicinski <kuba@...nel.org>, Paolo Abeni <pabeni@...hat.com>
Subject: [PATCH net-next 8/8] ipvlan: Don't learn child with host-ip
When some child attempts to send a packet with host ip,
remember host IP in the list of ipvlan-addrs with mark "blocked".
Don't send anything if child tries to send a packet with IP of main.
ToDo: track addresses on main port and mark them as blocked if bridge
already learned some of them from some of the children.
Signed-off-by: Dmitry Skorodumov <skorodumov.dmitry@...wei.com>
---
drivers/net/ipvlan/ipvlan.h | 4 ++-
drivers/net/ipvlan/ipvlan_core.c | 61 +++++++++++++++++++++++++-------
drivers/net/ipvlan/ipvlan_main.c | 9 ++---
3 files changed, 57 insertions(+), 17 deletions(-)
diff --git a/drivers/net/ipvlan/ipvlan.h b/drivers/net/ipvlan/ipvlan.h
index 02a705bf9d42..7de9794efbda 100644
--- a/drivers/net/ipvlan/ipvlan.h
+++ b/drivers/net/ipvlan/ipvlan.h
@@ -74,6 +74,7 @@ struct ipvl_dev {
struct ipvl_addr {
struct ipvl_dev *master; /* Back pointer to master */
+ bool is_blocked; /* Blocked. Addr from main iface */
union {
struct in6_addr ip6; /* IPv6 address on logical interface */
struct in_addr ip4; /* IPv4 address on logical interface */
@@ -179,7 +180,8 @@ void ipvlan_multicast_enqueue(struct ipvl_port *port,
int ipvlan_queue_xmit(struct sk_buff *skb, struct net_device *dev);
void ipvlan_ht_addr_add(struct ipvl_dev *ipvlan, struct ipvl_addr *addr);
int ipvlan_add_addr(struct ipvl_dev *ipvlan,
- void *iaddr, bool is_v6, const u8 *hwaddr);
+ void *iaddr, bool is_v6, const u8 *hwaddr,
+ bool is_blocked);
void ipvlan_del_addr(struct ipvl_dev *ipvlan, void *iaddr, bool is_v6);
struct ipvl_addr *ipvlan_find_addr(const struct ipvl_dev *ipvlan,
const void *iaddr, bool is_v6);
diff --git a/drivers/net/ipvlan/ipvlan_core.c b/drivers/net/ipvlan/ipvlan_core.c
index ce06a06d8a28..8b2c2d455ea5 100644
--- a/drivers/net/ipvlan/ipvlan_core.c
+++ b/drivers/net/ipvlan/ipvlan_core.c
@@ -468,8 +468,30 @@ static inline bool is_ipv6_usable(const struct in6_addr *addr)
!ipv6_addr_any(addr);
}
-static void ipvlan_addr_learn(struct ipvl_dev *ipvlan, void *lyr3h,
- int addr_type, const u8 *hwaddr)
+static bool ipvlan_is_portaddr_busy(struct ipvl_dev *ipvlan,
+ void *addr, bool is_v6)
+{
+ const struct in_ifaddr *ifa;
+ struct in_device *in_dev;
+
+ if (is_v6)
+ return ipv6_chk_addr(dev_net(ipvlan->phy_dev), addr,
+ ipvlan->phy_dev, 1);
+
+ in_dev = __in_dev_get_rcu(ipvlan->phy_dev);
+ if (!in_dev)
+ return false;
+
+ in_dev_for_each_ifa_rcu(ifa, in_dev)
+ if (ifa->ifa_local == *(__be32 *)addr)
+ return true;
+
+ return false;
+}
+
+/* return -1 if frame should be dropped. */
+static int ipvlan_addr_learn(struct ipvl_dev *ipvlan, void *lyr3h,
+ int addr_type, const u8 *hwaddr)
{
struct ipvl_addr *ipvladdr;
void *addr = NULL;
@@ -483,7 +505,7 @@ static void ipvlan_addr_learn(struct ipvl_dev *ipvlan, void *lyr3h,
ip6h = (struct ipv6hdr *)lyr3h;
if (!is_ipv6_usable(&ip6h->saddr))
- return;
+ return 0;
is_v6 = true;
addr = &ip6h->saddr;
break;
@@ -496,7 +518,7 @@ static void ipvlan_addr_learn(struct ipvl_dev *ipvlan, void *lyr3h,
ip4h = (struct iphdr *)lyr3h;
i4addr = &ip4h->saddr;
if (!is_ipv4_usable(*i4addr))
- return;
+ return 0;
is_v6 = false;
addr = i4addr;
break;
@@ -511,17 +533,20 @@ static void ipvlan_addr_learn(struct ipvl_dev *ipvlan, void *lyr3h,
arp_ptr += ipvlan->port->dev->addr_len;
i4addr = (__be32 *)arp_ptr;
if (!is_ipv4_usable(*i4addr))
- return;
+ return 0;
is_v6 = false;
addr = i4addr;
break;
}
default:
- return;
+ return 0;
}
/* handle situation when MAC changed, but IP is the same. */
ipvladdr = ipvlan_ht_addr_lookup(ipvlan->port, addr, is_v6);
+ if (ipvladdr && ipvladdr->is_blocked)
+ return -1;
+
if (ipvladdr && !ether_addr_equal(ipvladdr->hwaddr, hwaddr)) {
/* del_addr is safe to call, because we are inside xmit*/
ipvlan_del_addr(ipvladdr->master, addr, is_v6);
@@ -529,11 +554,17 @@ static void ipvlan_addr_learn(struct ipvl_dev *ipvlan, void *lyr3h,
}
if (!ipvladdr) {
+ bool is_port_ip = ipvlan_is_portaddr_busy(ipvlan, addr, is_v6);
+
spin_lock_bh(&ipvlan->addrs_lock);
if (!ipvlan_addr_busy(ipvlan->port, addr, is_v6))
- ipvlan_add_addr(ipvlan, addr, is_v6, hwaddr);
+ ipvlan_add_addr(ipvlan, addr, is_v6, hwaddr, is_port_ip);
spin_unlock_bh(&ipvlan->addrs_lock);
+
+ return is_port_ip ? -1 : 0;
}
+
+ return 0;
}
static noinline_for_stack int ipvlan_process_v4_outbound(struct sk_buff *skb)
@@ -724,11 +755,12 @@ static int ipvlan_xmit_mode_l3(struct sk_buff *skb, struct net_device *dev)
if (!ipvlan_is_vepa(ipvlan->port)) {
addr = ipvlan_addr_lookup(ipvlan->port, lyr3h, addr_type, true);
- if (addr) {
+ if (addr && !addr->is_blocked) {
if (ipvlan_is_private(ipvlan->port)) {
consume_skb(skb);
return NET_XMIT_DROP;
}
+
ipvlan_rcv_frame(addr, addr_type, &skb, true);
return NET_XMIT_SUCCESS;
}
@@ -866,8 +898,12 @@ static int ipvlan_xmit_mode_l2(struct sk_buff *skb, struct net_device *dev)
lyr3h = ipvlan_get_L3_hdr(ipvlan->port, skb, &addr_type);
if (ipvlan_is_learnable(ipvlan->port)) {
- if (lyr3h)
- ipvlan_addr_learn(ipvlan, lyr3h, addr_type, eth->h_source);
+ if (lyr3h) {
+ if (ipvlan_addr_learn(ipvlan, lyr3h, addr_type,
+ eth->h_source) < 0)
+ goto out_drop;
+ }
+
/* Mark SKB in advance */
skb = skb_share_check(skb, GFP_ATOMIC);
if (!skb)
@@ -903,7 +939,7 @@ static int ipvlan_xmit_mode_l2(struct sk_buff *skb, struct net_device *dev)
if (lyr3h) {
addr = ipvlan_addr_lookup(ipvlan->port, lyr3h, addr_type, true);
- if (addr) {
+ if (addr && !addr->is_blocked) {
if (ipvlan_is_private(ipvlan->port))
goto out_drop;
@@ -1016,8 +1052,9 @@ static rx_handler_result_t ipvlan_handle_mode_l3(struct sk_buff **pskb,
goto out;
addr = ipvlan_addr_lookup(port, lyr3h, addr_type, true);
- if (addr)
+ if (addr && !addr->is_blocked)
ret = ipvlan_rcv_frame(addr, addr_type, pskb, false);
+
out:
return ret;
}
diff --git a/drivers/net/ipvlan/ipvlan_main.c b/drivers/net/ipvlan/ipvlan_main.c
index f1b1f91f94c0..5df6bdeadef5 100644
--- a/drivers/net/ipvlan/ipvlan_main.c
+++ b/drivers/net/ipvlan/ipvlan_main.c
@@ -151,7 +151,7 @@ static int ipvlan_port_receive(struct sk_buff *skb, struct net_device *wdev,
goto out;
addr = ipvlan_addr_lookup(port, lyr3h, addr_type, true);
- if (addr)
+ if (addr && !addr->is_blocked)
return ipvlan_receive(addr->master, skb);
out:
@@ -964,7 +964,7 @@ static int ipvlan_device_event(struct notifier_block *unused,
/* the caller must held the addrs lock */
int ipvlan_add_addr(struct ipvl_dev *ipvlan, void *iaddr, bool is_v6,
- const u8 *hwaddr)
+ const u8 *hwaddr, bool is_blocked)
{
struct ipvl_addr *addr;
@@ -973,6 +973,7 @@ int ipvlan_add_addr(struct ipvl_dev *ipvlan, void *iaddr, bool is_v6,
return -ENOMEM;
addr->master = ipvlan;
+ addr->is_blocked = is_blocked;
if (!is_v6) {
memcpy(&addr->ip4addr, iaddr, sizeof(struct in_addr));
addr->atype = IPVL_IPV4;
@@ -1024,7 +1025,7 @@ static int ipvlan_add_addr6(struct ipvl_dev *ipvlan, struct in6_addr *ip6_addr)
"Failed to add IPv6=%pI6c addr for %s intf\n",
ip6_addr, ipvlan->dev->name);
else
- ret = ipvlan_add_addr(ipvlan, ip6_addr, true, NULL);
+ ret = ipvlan_add_addr(ipvlan, ip6_addr, true, NULL, false);
spin_unlock_bh(&ipvlan->addrs_lock);
return ret;
}
@@ -1095,7 +1096,7 @@ static int ipvlan_add_addr4(struct ipvl_dev *ipvlan, struct in_addr *ip4_addr)
"Failed to add IPv4=%pI4 on %s intf.\n",
ip4_addr, ipvlan->dev->name);
else
- ret = ipvlan_add_addr(ipvlan, ip4_addr, false, NULL);
+ ret = ipvlan_add_addr(ipvlan, ip4_addr, false, NULL, false);
spin_unlock_bh(&ipvlan->addrs_lock);
return ret;
}
--
2.25.1
Powered by blists - more mailing lists