qcacmn: IGMP leave message handling in MLO MCAST

IGMP leave message handling in MLO MCAST scenarios.

Change-Id: Ic0ac16dbe75b4ef2b27e97be3f42c498b503ca06
CRs-Fixed: 3359488
This commit is contained in:
Sai Rupesh Chevuru
2022-12-29 16:37:08 +05:30
committed by Madan Koyyalamudi
parent cbc0170b1c
commit 88dd4c6970
4 changed files with 200 additions and 3 deletions

View File

@@ -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