[Openvpn-devel,RFC,ovpn,net-next,3/5] ovpn: filter incoming multicast data packets

Message ID 20260514095210.288979-3-marco@mandelbit.com
State New
Headers show
Series [Openvpn-devel,RFC,ovpn,net-next,1/5] ovpn: add multicast/braodcast packet transmission support | expand

Commit Message

Marco Baffo May 14, 2026, 9:52 a.m. UTC
When the Ovpn server receive a multicast data packet that
has to be trasmitted to the peers, if its IP destination
is a multicast group to which no peer is currently subscribed,
the packet is dropped instead of being broadcasted.
Multicast control messages (IGMP/MLD) are still broadcasted
to all peers.

Signed-off-by: Marco Baffo <marco@mandelbit.com>
---
 drivers/net/ovpn/mcast.c | 41 ++++++++++++++++++++++++++++++++++++++--
 drivers/net/ovpn/mcast.h |  1 +
 drivers/net/ovpn/peer.c  |  7 +++++--
 3 files changed, 45 insertions(+), 4 deletions(-)

Patch

diff --git a/drivers/net/ovpn/mcast.c b/drivers/net/ovpn/mcast.c
index c90ef2b8d8b8..59b0b62afcde 100644
--- a/drivers/net/ovpn/mcast.c
+++ b/drivers/net/ovpn/mcast.c
@@ -358,12 +358,49 @@  bool ovpn_mcast_snoop_skb(struct ovpn_peer *peer, struct sk_buff *skb)
 {
 	if (peer->ovpn->mode != OVPN_MODE_MP)
 		return false;
+
 	if (skb->protocol == htons(ETH_P_IP)) {
 		if (ip_hdr(skb)->protocol == IPPROTO_IGMP)
 			return ovpn_mcast_snoop_igmp(peer, skb);
 	} else if (skb->protocol == htons(ETH_P_IPV6)) {
-		if (ipv6_hdr(skb)->nexthdr == IPPROTO_ICMPV6)
-			return ovpn_mcast_snoop_mld(peer, skb);
+		return ovpn_mcast_snoop_mld(peer, skb);
+	}
+
+	return false;
+}
+
+/**
+ * ovpn_mcast_is_control - determine whether an skb is multicast control traffic
+ * @skb: the packet to inspect
+ *
+ * Return: true if the skb contains IGMP or MLD control traffic,
+ *         false otherwise
+ */
+bool ovpn_mcast_is_control(struct sk_buff *skb)
+{
+	unsigned int offset;
+	struct icmp6hdr *ih;
+
+	if (skb->protocol == htons(ETH_P_IP))
+		return ip_hdr(skb)->protocol == IPPROTO_IGMP;
+
+	if (skb->protocol != htons(ETH_P_IPV6))
+		return false;
+
+	if (!ovpn_mcast_mld_offset(skb, &offset))
+		return false;
+
+	if (!pskb_may_pull(skb, offset + sizeof(*ih)))
+		return false;
+
+	ih = (struct icmp6hdr *)(skb_network_header(skb) + offset);
+	switch (ih->icmp6_type) {
+	case ICMPV6_MGM_QUERY:
+	case ICMPV6_MGM_REPORT:
+	case ICMPV6_MGM_REDUCTION:
+	case ICMPV6_MLD2_REPORT:
+		return true;
 	}
+
 	return false;
 }
diff --git a/drivers/net/ovpn/mcast.h b/drivers/net/ovpn/mcast.h
index e9e14d807270..9e06e893a355 100644
--- a/drivers/net/ovpn/mcast.h
+++ b/drivers/net/ovpn/mcast.h
@@ -22,6 +22,7 @@  void ovpn_mcast_leave_all(struct ovpn_peer *peer);
 bool ovpn_peer_list_get_by_mcast_group(struct ovpn_priv *ovpn,
 				       const struct in6_addr *group_addr,
 				       struct llist_head *list);
+bool ovpn_mcast_is_control(struct sk_buff *skb);
 bool ovpn_mcast_snoop_skb(struct ovpn_peer *peer, struct sk_buff *skb);
 
 #endif /* _NET_OVPN_MCAST_H_ */
diff --git a/drivers/net/ovpn/peer.c b/drivers/net/ovpn/peer.c
index 5159a8f9dfba..a9728a157210 100644
--- a/drivers/net/ovpn/peer.c
+++ b/drivers/net/ovpn/peer.c
@@ -779,8 +779,10 @@  void ovpn_peer_list_get_by_dst(struct ovpn_priv *ovpn, struct sk_buff *skb,
 		addr_type = inet_dev_addr_type(dev_net(ovpn->dev), ovpn->dev, addr4);
 		if (addr_type == RTN_MULTICAST) {
 			ipv6_addr_set_v4mapped(addr4, &addr6);
-			if (!ovpn_peer_list_get_by_mcast_group(ovpn, &addr6, list))
+			if (!ovpn_peer_list_get_by_mcast_group(ovpn, &addr6, list) &&
+			    ovpn_mcast_is_control(skb)) {
 				ovpn_peer_list_get_all(ovpn, list);
+			}
 		} else if (addr_type == RTN_BROADCAST) {
 			ovpn_peer_list_get_all(ovpn, list);
 		}
@@ -795,7 +797,8 @@  void ovpn_peer_list_get_by_dst(struct ovpn_priv *ovpn, struct sk_buff *skb,
 
 		rcu_read_unlock();
 		if (ipv6_addr_is_multicast(&addr6) &&
-		    !ovpn_peer_list_get_by_mcast_group(ovpn, &addr6, list)) {
+		    !ovpn_peer_list_get_by_mcast_group(ovpn, &addr6, list) &&
+		    ovpn_mcast_is_control(skb)) {
 			ovpn_peer_list_get_all(ovpn, list);
 		}
 		return;