lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<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

Powered by Openwall GNU/*/Linux Powered by OpenVZ