@@ -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;
}
@@ -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_ */
@@ -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;
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(-)