[<prev] [next>] [day] [month] [year] [list]
Message-ID: <20251021144410.257905-4-skorodumov.dmitry@huawei.com>
Date: Tue, 21 Oct 2025 17:44:05 +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 3/8] ipvlan: Handle rx mcast-ip and unicast eth
Some WiFi enfironments sometimes send mcast packets
with unicast eth_dst. Forcibly replace eth_dst to be bcast in this case
if bridge is in L2E mode.
Signed-off-by: Dmitry Skorodumov <skorodumov.dmitry@...wei.com>
---
drivers/net/ipvlan/ipvlan_core.c | 57 ++++++++++++++++++++++++++++++--
1 file changed, 55 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ipvlan/ipvlan_core.c b/drivers/net/ipvlan/ipvlan_core.c
index 9af0dcc307da..41059639f307 100644
--- a/drivers/net/ipvlan/ipvlan_core.c
+++ b/drivers/net/ipvlan/ipvlan_core.c
@@ -855,18 +855,69 @@ static rx_handler_result_t ipvlan_handle_mode_l3(struct sk_buff **pskb,
return ret;
}
+static bool ipvlan_is_mcast(struct ipvl_port *port, void *lyr3h, int addr_type)
+{
+ switch (addr_type) {
+#if IS_ENABLED(CONFIG_IPV6)
+ /* ToDo: handle ICMPV6*/
+ case IPVL_IPV6:
+ return !is_ipv6_usable(&((struct ipv6hdr *)lyr3h)->daddr);
+#endif
+ case IPVL_IPV4: {
+ /* Treat mcast, bcast and zero as multicast. */
+ __be32 i4addr = ((struct iphdr *)lyr3h)->daddr;
+
+ return !is_ipv4_usable(i4addr);
+ }
+ case IPVL_ARP: {
+ struct arphdr *arph;
+ unsigned char *arp_ptr;
+ __be32 i4addr;
+
+ arph = (struct arphdr *)lyr3h;
+ arp_ptr = (unsigned char *)(arph + 1);
+ arp_ptr += (2 * port->dev->addr_len) + 4;
+ i4addr = *(__be32 *)arp_ptr;
+ return !is_ipv4_usable(i4addr);
+ }
+ }
+ return false;
+}
+
+static bool ipvlan_is_l2_mcast(struct ipvl_port *port, struct sk_buff *skb,
+ bool *need_eth_fix)
+{
+ void *lyr3h;
+ int addr_type;
+
+ /* In some wifi environments unicast dest address means nothing.
+ * IP still can be a mcast and frame should be treated as mcast.
+ */
+ *need_eth_fix = false;
+ if (is_multicast_ether_addr(eth_hdr(skb)->h_dest))
+ return true;
+
+ if (!ipvlan_is_learnable(port))
+ return false;
+
+ lyr3h = ipvlan_get_L3_hdr(port, skb, &addr_type);
+ *need_eth_fix = lyr3h && ipvlan_is_mcast(port, lyr3h, addr_type);
+
+ return *need_eth_fix;
+}
+
static rx_handler_result_t ipvlan_handle_mode_l2(struct sk_buff **pskb,
struct ipvl_port *port)
{
struct sk_buff *skb = *pskb;
- struct ethhdr *eth = eth_hdr(skb);
rx_handler_result_t ret = RX_HANDLER_PASS;
+ bool need_eth_fix;
/* Ignore already seen packets. */
if (ipvlan_is_skb_marked(skb, port->dev))
return RX_HANDLER_PASS;
- if (is_multicast_ether_addr(eth->h_dest)) {
+ if (ipvlan_is_l2_mcast(port, skb, &need_eth_fix)) {
if (ipvlan_external_frame(skb, port)) {
/* External frames are queued for device local
* distribution, but a copy is given to master
@@ -877,6 +928,8 @@ static rx_handler_result_t ipvlan_handle_mode_l2(struct sk_buff **pskb,
struct sk_buff *nskb = skb_clone(skb, GFP_ATOMIC);
if (nskb) {
+ if (need_eth_fix)
+ memset(eth_hdr(nskb)->h_dest, 0xff, ETH_ALEN);
ipvlan_mark_skb(skb, port->dev);
ipvlan_skb_crossing_ns(nskb, NULL);
ipvlan_multicast_enqueue(port, nskb, false);
--
2.25.1
Powered by blists - more mailing lists