|
@@ -1,6 +1,6 @@
|
|
|
/*
|
|
|
* 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
|
|
|
* any purpose with or without fee is hereby granted, provided that the
|
|
@@ -42,6 +42,8 @@
|
|
|
#include <net/ieee80211_radiotap.h>
|
|
|
#include <pld_common.h>
|
|
|
#include <qdf_crypto.h>
|
|
|
+#include <linux/igmp.h>
|
|
|
+#include <net/mld.h>
|
|
|
|
|
|
#if defined(FEATURE_TSO)
|
|
|
#include <net/ipv6.h>
|
|
@@ -2036,6 +2038,165 @@ is_mld:
|
|
|
|
|
|
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
|
|
|
* @skb: Pointer to network buffer
|