Эх сурвалжийг харах

qcacmn: IGMP leave message handling in MLO MCAST

IGMP leave message handling in MLO MCAST scenarios.

Change-Id: Ic0ac16dbe75b4ef2b27e97be3f42c498b503ca06
CRs-Fixed: 3359488
Sai Rupesh Chevuru 2 жил өмнө
parent
commit
88dd4c6970

+ 6 - 0
dp/wifi3.0/be/dp_be_rx.c

@@ -1283,6 +1283,12 @@ bool dp_rx_mlo_igmp_handler(struct dp_soc *soc,
 					       NULL);
 					       NULL);
 	}
 	}
 
 
+	if (qdf_nbuf_is_ipv4_igmp_leave_pkt(nbuf) ||
+	    qdf_nbuf_is_ipv6_igmp_leave_pkt(nbuf)) {
+		qdf_nbuf_free(nbuf);
+		return true;
+	}
+
 	dp_rx_dummy_src_mac(vdev, nbuf);
 	dp_rx_dummy_src_mac(vdev, nbuf);
 	dp_rx_deliver_to_stack(mcast_primary_vdev->pdev->soc,
 	dp_rx_deliver_to_stack(mcast_primary_vdev->pdev->soc,
 			       mcast_primary_vdev,
 			       mcast_primary_vdev,

+ 29 - 1
qdf/inc/qdf_nbuf.h

@@ -1,6 +1,6 @@
 /*
 /*
  * Copyright (c) 2014-2021 The Linux Foundation. All rights reserved.
  * Copyright (c) 2014-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved.
  *
  *
  * Permission to use, copy, modify, and/or distribute this software for
  * Permission to use, copy, modify, and/or distribute this software for
  * any purpose with or without fee is hereby granted, provided that the
  * any purpose with or without fee is hereby granted, provided that the
@@ -3624,6 +3624,34 @@ bool qdf_nbuf_is_ipv6_igmp_pkt(qdf_nbuf_t buf)
 	return __qdf_nbuf_data_is_ipv6_igmp_pkt(qdf_nbuf_data(buf));
 	return __qdf_nbuf_data_is_ipv6_igmp_pkt(qdf_nbuf_data(buf));
 }
 }
 
 
+/**
+ * qdf_nbuf_is_ipv4_igmp_leave_pkt() - check if it is a igmp leave packet or not
+ * @buf:  buffer
+ *
+ * This api is for ipv4 packet.
+ *
+ * Return: true if packet is igmp packet
+ */
+static inline
+bool qdf_nbuf_is_ipv4_igmp_leave_pkt(qdf_nbuf_t buf)
+{
+	return __qdf_nbuf_is_ipv4_igmp_leave_pkt(buf);
+}
+
+/**
+ * qdf_nbuf_is_ipv6_igmp_leave_pkt() - check if it is a igmp leave packet or not
+ * @buf:  buffer
+ *
+ * This api is for ipv6 packet.
+ *
+ * Return: true if packet is igmp packet
+ */
+static inline
+bool qdf_nbuf_is_ipv6_igmp_leave_pkt(qdf_nbuf_t buf)
+{
+	return __qdf_nbuf_is_ipv6_igmp_leave_pkt(buf);
+}
+
 /**
 /**
  * qdf_nbuf_is_ipv4_tdls_pkt() - check if packet is a tdls packet or not
  * qdf_nbuf_is_ipv4_tdls_pkt() - check if packet is a tdls packet or not
  * @buf:  buffer
  * @buf:  buffer

+ 3 - 1
qdf/linux/src/i_qdf_nbuf.h

@@ -1,6 +1,6 @@
 /*
 /*
  * Copyright (c) 2014-2021 The Linux Foundation. All rights reserved.
  * Copyright (c) 2014-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved.
  *
  *
  * Permission to use, copy, modify, and/or distribute this software for
  * Permission to use, copy, modify, and/or distribute this software for
  * any purpose with or without fee is hereby granted, provided that the
  * any purpose with or without fee is hereby granted, provided that the
@@ -922,6 +922,8 @@ bool __qdf_nbuf_data_is_ipv6_mdns_pkt(uint8_t *data);
 bool __qdf_nbuf_data_is_ipv4_eapol_pkt(uint8_t *data);
 bool __qdf_nbuf_data_is_ipv4_eapol_pkt(uint8_t *data);
 bool __qdf_nbuf_data_is_ipv4_igmp_pkt(uint8_t *data);
 bool __qdf_nbuf_data_is_ipv4_igmp_pkt(uint8_t *data);
 bool __qdf_nbuf_data_is_ipv6_igmp_pkt(uint8_t *data);
 bool __qdf_nbuf_data_is_ipv6_igmp_pkt(uint8_t *data);
+bool __qdf_nbuf_is_ipv4_igmp_leave_pkt(__qdf_nbuf_t buf);
+bool __qdf_nbuf_is_ipv6_igmp_leave_pkt(__qdf_nbuf_t buf);
 bool __qdf_nbuf_data_is_ipv4_arp_pkt(uint8_t *data);
 bool __qdf_nbuf_data_is_ipv4_arp_pkt(uint8_t *data);
 bool __qdf_nbuf_is_bcast_pkt(__qdf_nbuf_t nbuf);
 bool __qdf_nbuf_is_bcast_pkt(__qdf_nbuf_t nbuf);
 bool __qdf_nbuf_is_mcast_replay(__qdf_nbuf_t nbuf);
 bool __qdf_nbuf_is_mcast_replay(__qdf_nbuf_t nbuf);

+ 162 - 1
qdf/linux/src/qdf_nbuf.c

@@ -1,6 +1,6 @@
 /*
 /*
  * Copyright (c) 2014-2021 The Linux Foundation. All rights reserved.
  * Copyright (c) 2014-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved.
  *
  *
  * Permission to use, copy, modify, and/or distribute this software for
  * Permission to use, copy, modify, and/or distribute this software for
  * any purpose with or without fee is hereby granted, provided that the
  * any purpose with or without fee is hereby granted, provided that the
@@ -42,6 +42,8 @@
 #include <net/ieee80211_radiotap.h>
 #include <net/ieee80211_radiotap.h>
 #include <pld_common.h>
 #include <pld_common.h>
 #include <qdf_crypto.h>
 #include <qdf_crypto.h>
+#include <linux/igmp.h>
+#include <net/mld.h>
 
 
 #if defined(FEATURE_TSO)
 #if defined(FEATURE_TSO)
 #include <net/ipv6.h>
 #include <net/ipv6.h>
@@ -2036,6 +2038,165 @@ is_mld:
 
 
 qdf_export_symbol(__qdf_nbuf_data_is_ipv6_igmp_pkt);
 qdf_export_symbol(__qdf_nbuf_data_is_ipv6_igmp_pkt);
 
 
+/**
+ * __qdf_nbuf_is_ipv4_igmp_leave_pkt() - check if skb is a igmp leave packet
+ * @data: Pointer to network buffer
+ *
+ * This api is for ipv4 packet.
+ *
+ * Return: true if packet is igmp packet
+ *	   false otherwise.
+ */
+bool __qdf_nbuf_is_ipv4_igmp_leave_pkt(__qdf_nbuf_t buf)
+{
+	qdf_ether_header_t *eh = NULL;
+	uint16_t ether_type;
+	uint8_t eth_hdr_size = sizeof(qdf_ether_header_t);
+
+	eh = (qdf_ether_header_t *)qdf_nbuf_data(buf);
+	ether_type = eh->ether_type;
+
+	if (ether_type == htons(ETH_P_8021Q)) {
+		struct vlan_ethhdr *veth =
+				(struct vlan_ethhdr *)qdf_nbuf_data(buf);
+		ether_type = veth->h_vlan_encapsulated_proto;
+		eth_hdr_size = sizeof(struct vlan_ethhdr);
+	}
+
+	if (ether_type == QDF_SWAP_U16(QDF_NBUF_TRAC_IPV4_ETH_TYPE)) {
+		struct iphdr *iph = NULL;
+		struct igmphdr *ih = NULL;
+
+		iph = (struct iphdr *)(qdf_nbuf_data(buf) + eth_hdr_size);
+		ih = (struct igmphdr *)((uint8_t *)iph + iph->ihl * 4);
+		switch (ih->type) {
+		case IGMP_HOST_LEAVE_MESSAGE:
+			return true;
+		case IGMPV3_HOST_MEMBERSHIP_REPORT:
+		{
+			struct igmpv3_report *ihv3 = (struct igmpv3_report *)ih;
+			struct igmpv3_grec *grec = NULL;
+			int num = 0;
+			int i = 0;
+			int len = 0;
+			int type = 0;
+
+			num = ntohs(ihv3->ngrec);
+			for (i = 0; i < num; i++) {
+				grec = (void *)((uint8_t *)(ihv3->grec) + len);
+				type = grec->grec_type;
+				if ((type == IGMPV3_MODE_IS_INCLUDE) ||
+				    (type == IGMPV3_CHANGE_TO_INCLUDE))
+					return true;
+
+				len += sizeof(struct igmpv3_grec);
+				len += ntohs(grec->grec_nsrcs) * 4;
+			}
+			break;
+		}
+		default:
+			break;
+		}
+	}
+
+	return false;
+}
+
+qdf_export_symbol(__qdf_nbuf_is_ipv4_igmp_leave_pkt);
+
+/**
+ * __qdf_nbuf_is_ipv6_igmp_leave_pkt() - check if skb is a igmp leave packet
+ * @data: Pointer to network buffer
+ *
+ * This api is for ipv6 packet.
+ *
+ * Return: true if packet is igmp packet
+ *	   false otherwise.
+ */
+bool __qdf_nbuf_is_ipv6_igmp_leave_pkt(__qdf_nbuf_t buf)
+{
+	qdf_ether_header_t *eh = NULL;
+	uint16_t ether_type;
+	uint8_t eth_hdr_size = sizeof(qdf_ether_header_t);
+
+	eh = (qdf_ether_header_t *)qdf_nbuf_data(buf);
+	ether_type = eh->ether_type;
+
+	if (ether_type == htons(ETH_P_8021Q)) {
+		struct vlan_ethhdr *veth =
+				(struct vlan_ethhdr *)qdf_nbuf_data(buf);
+		ether_type = veth->h_vlan_encapsulated_proto;
+		eth_hdr_size = sizeof(struct vlan_ethhdr);
+	}
+
+	if (ether_type == QDF_SWAP_U16(QDF_NBUF_TRAC_IPV6_ETH_TYPE)) {
+		struct ipv6hdr *ip6h = NULL;
+		struct icmp6hdr *icmp6h = NULL;
+		uint8_t nexthdr;
+		uint16_t frag_off = 0;
+		int offset;
+		qdf_nbuf_t buf_copy = NULL;
+
+		ip6h = (struct ipv6hdr *)(qdf_nbuf_data(buf) + eth_hdr_size);
+		if (ip6h->nexthdr != IPPROTO_HOPOPTS ||
+		    ip6h->payload_len == 0)
+			return false;
+
+		buf_copy = qdf_nbuf_copy(buf);
+		if (qdf_likely(!buf_copy))
+			return false;
+
+		nexthdr = ip6h->nexthdr;
+		offset = ipv6_skip_exthdr(buf_copy,
+					  eth_hdr_size + sizeof(*ip6h),
+					  &nexthdr,
+					  &frag_off);
+		qdf_nbuf_free(buf_copy);
+		if (offset < 0 || nexthdr != IPPROTO_ICMPV6)
+			return false;
+
+		icmp6h = (struct icmp6hdr *)(qdf_nbuf_data(buf) + offset);
+
+		switch (icmp6h->icmp6_type) {
+		case ICMPV6_MGM_REDUCTION:
+			return true;
+		case ICMPV6_MLD2_REPORT:
+		{
+			struct mld2_report *mh = NULL;
+			struct mld2_grec *grec = NULL;
+			int num = 0;
+			int i = 0;
+			int len = 0;
+			int type = -1;
+
+			mh = (struct mld2_report *)icmp6h;
+			num = ntohs(mh->mld2r_ngrec);
+			for (i = 0; i < num; i++) {
+				grec = (void *)(((uint8_t *)mh->mld2r_grec) +
+						len);
+				type = grec->grec_type;
+				if ((type == MLD2_MODE_IS_INCLUDE) ||
+				    (type == MLD2_CHANGE_TO_INCLUDE))
+					return true;
+				else if (type == MLD2_BLOCK_OLD_SOURCES)
+					return true;
+
+				len += sizeof(struct mld2_grec);
+				len += ntohs(grec->grec_nsrcs) *
+						sizeof(struct in6_addr);
+			}
+			break;
+		}
+		default:
+			break;
+		}
+	}
+
+	return false;
+}
+
+qdf_export_symbol(__qdf_nbuf_is_ipv6_igmp_leave_pkt);
+
 /**
 /**
  * __qdf_nbuf_is_ipv4_tdls_pkt() - check if skb data is a tdls packet
  * __qdf_nbuf_is_ipv4_tdls_pkt() - check if skb data is a tdls packet
  * @skb: Pointer to network buffer
  * @skb: Pointer to network buffer