| /* |
| * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following |
| * disclaimer in the documentation and/or other materials provided |
| * with the distribution. |
| * * Neither the name of The Linux Foundation nor the names of its |
| * contributors may be used to endorse or promote products derived |
| * from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED |
| * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
| * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS |
| * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR |
| * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
| * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE |
| * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN |
| * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| * Changes from Qualcomm Innovation Center are provided under the following license: |
| |
| * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved. |
| * SPDX-License-Identifier: BSD-3-Clause-Clear |
| */ |
| |
| #include "sync.h" |
| |
| #define LOG_TAG "WifiHAL" |
| |
| #include <utils/Log.h> |
| |
| #include "wifi_hal.h" |
| #include "common.h" |
| #include "cpp_bindings.h" |
| #include "wifihal_vendorcommand.h" |
| |
| #define MAX_INFO 1 |
| //Singleton Static Instance |
| NUDStatsCommand* NUDStatsCommand::mNUDStatsCommandInstance = NULL; |
| |
| // This function implements creation of Vendor command |
| // For NUDStats just call base Vendor command create |
| wifi_error NUDStatsCommand::create() { |
| wifi_error ret = mMsg.create(NL80211_CMD_VENDOR, 0, 0); |
| if (ret != WIFI_SUCCESS) { |
| return ret; |
| } |
| // insert the oui in the msg |
| ret = mMsg.put_u32(NL80211_ATTR_VENDOR_ID, mVendor_id); |
| if (ret != WIFI_SUCCESS) |
| goto out; |
| |
| // insert the subcmd in the msg |
| ret = mMsg.put_u32(NL80211_ATTR_VENDOR_SUBCMD, mSubcmd); |
| if (ret != WIFI_SUCCESS) |
| goto out; |
| |
| out: |
| return ret; |
| } |
| |
| NUDStatsCommand::NUDStatsCommand(wifi_handle handle, int id, u32 vendor_id, u32 subcmd) |
| : WifiVendorCommand(handle, id, vendor_id, subcmd) |
| { |
| memset(&mStats, 0,sizeof(nud_stats)); |
| mpktInfo = NULL; |
| mnumStats = 0; |
| memset(&mHandler, 0, sizeof(pkt_stats_result_handler)); |
| } |
| |
| NUDStatsCommand::~NUDStatsCommand() |
| { |
| mNUDStatsCommandInstance = NULL; |
| } |
| |
| NUDStatsCommand* NUDStatsCommand::instance(wifi_handle handle) |
| { |
| if (handle == NULL) { |
| ALOGE("Interface Handle is invalid"); |
| return NULL; |
| } |
| if (mNUDStatsCommandInstance == NULL) { |
| mNUDStatsCommandInstance = new NUDStatsCommand(handle, 0, |
| OUI_QCA, |
| QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_SET); |
| return mNUDStatsCommandInstance; |
| } |
| else |
| { |
| if (handle != getWifiHandle(mNUDStatsCommandInstance->mInfo)) |
| { |
| /* upper layer must have cleaned up the handle and reinitialized, |
| so we need to update the same */ |
| ALOGE("Handle different, update the handle"); |
| mNUDStatsCommandInstance->mInfo = (hal_info *)handle; |
| } |
| } |
| return mNUDStatsCommandInstance; |
| } |
| |
| void NUDStatsCommand::setSubCmd(u32 subcmd) |
| { |
| mSubcmd = subcmd; |
| } |
| |
| void NUDStatsCommand::setHandler(pkt_stats_result_handler handler) |
| { |
| mHandler = handler; |
| } |
| |
| wifi_error NUDStatsCommand::requestResponse() |
| { |
| return WifiCommand::requestResponse(mMsg); |
| } |
| |
| wifi_error NUDStatsCommand::notifyResponse() |
| { |
| wifi_error ret = WIFI_SUCCESS; |
| |
| if (mHandler.on_pkt_stats_results) { |
| mHandler.on_pkt_stats_results(&mStats, mnumStats, |
| mpktInfo); |
| } else { |
| ret = WIFI_ERROR_INVALID_ARGS; |
| } |
| return ret; |
| } |
| |
| int NUDStatsCommand::handleResponse(WifiEvent &reply) |
| { |
| wifi_error status = WIFI_ERROR_NONE; |
| WifiVendorCommand::handleResponse(reply); |
| |
| // Parse the vendordata and get the attribute |
| |
| switch(mSubcmd) |
| { |
| case QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_GET: |
| { |
| struct nlattr *tb_vendor[QCA_ATTR_NUD_STATS_GET_MAX + 1]; |
| nud_stats *stats = &mStats; |
| |
| memset(stats, 0, sizeof(nud_stats)); |
| nla_parse(tb_vendor, QCA_ATTR_NUD_STATS_GET_MAX, |
| (struct nlattr *)mVendorData, mDataLen, NULL); |
| |
| if (!tb_vendor[QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_FROM_NETDEV]) |
| { |
| ALOGE("%s: QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_FROM_NETDEV" |
| " not found", __FUNCTION__); |
| status = WIFI_ERROR_INVALID_ARGS; |
| goto cleanup; |
| } |
| stats->arp_req_count_from_netdev = nla_get_u16(tb_vendor[ |
| QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_FROM_NETDEV]); |
| |
| if (!tb_vendor[QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_TO_LOWER_MAC]) |
| { |
| ALOGE("%s: QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_TO_LOWER_MAC" |
| " not found", __FUNCTION__); |
| status = WIFI_ERROR_INVALID_ARGS; |
| goto cleanup; |
| } |
| stats->arp_req_count_to_lower_mac = nla_get_u16(tb_vendor[ |
| QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_TO_LOWER_MAC]); |
| |
| if (!tb_vendor[QCA_ATTR_NUD_STATS_ARP_REQ_RX_COUNT_BY_LOWER_MAC]) |
| { |
| ALOGE("%s: QCA_ATTR_NUD_STATS_ARP_REQ_RX_COUNT_BY_LOWER_MAC" |
| " not found", __FUNCTION__); |
| status = WIFI_ERROR_INVALID_ARGS; |
| goto cleanup; |
| } |
| stats->arp_req_rx_count_by_lower_mac = nla_get_u16(tb_vendor[ |
| QCA_ATTR_NUD_STATS_ARP_REQ_RX_COUNT_BY_LOWER_MAC]); |
| |
| if (!tb_vendor[QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_TX_SUCCESS]) |
| { |
| ALOGE("%s: QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_TX_SUCCESS" |
| " not found", __FUNCTION__); |
| status = WIFI_ERROR_INVALID_ARGS; |
| goto cleanup; |
| } |
| stats->arp_req_count_tx_success = nla_get_u16(tb_vendor[ |
| QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_TX_SUCCESS]); |
| |
| if (!tb_vendor[QCA_ATTR_NUD_STATS_ARP_RSP_RX_COUNT_BY_LOWER_MAC]) |
| { |
| ALOGE("%s: QCA_ATTR_NUD_STATS_ARP_RSP_RX_COUNT_BY_LOWER_MAC" |
| " not found", __FUNCTION__); |
| status = WIFI_ERROR_INVALID_ARGS; |
| goto cleanup; |
| } |
| stats->arp_rsp_rx_count_by_lower_mac = nla_get_u16(tb_vendor[ |
| QCA_ATTR_NUD_STATS_ARP_RSP_RX_COUNT_BY_LOWER_MAC]); |
| |
| if (!tb_vendor[QCA_ATTR_NUD_STATS_ARP_RSP_RX_COUNT_BY_UPPER_MAC]) |
| { |
| ALOGE("%s: QCA_ATTR_NUD_STATS_ARP_RSP_RX_COUNT_BY_UPPER_MAC" |
| " not found", __FUNCTION__); |
| status = WIFI_ERROR_INVALID_ARGS; |
| goto cleanup; |
| } |
| stats->arp_rsp_rx_count_by_upper_mac = nla_get_u16(tb_vendor[ |
| QCA_ATTR_NUD_STATS_ARP_RSP_RX_COUNT_BY_UPPER_MAC]); |
| |
| if (!tb_vendor[QCA_ATTR_NUD_STATS_ARP_RSP_COUNT_TO_NETDEV]) |
| { |
| ALOGE("%s: QCA_ATTR_NUD_STATS_ARP_RSP_COUNT_TO_NETDEV" |
| " not found", __FUNCTION__); |
| status = WIFI_ERROR_INVALID_ARGS; |
| goto cleanup; |
| } |
| stats->arp_rsp_count_to_netdev = nla_get_u16(tb_vendor[ |
| QCA_ATTR_NUD_STATS_ARP_RSP_COUNT_TO_NETDEV]); |
| |
| if (!tb_vendor[QCA_ATTR_NUD_STATS_ARP_RSP_COUNT_OUT_OF_ORDER_DROP]) |
| { |
| ALOGE("%s: QCA_ATTR_NUD_STATS_ARP_RSP_COUNT_OUT_OF_ORDER_DROP" |
| " not found", __FUNCTION__); |
| status = WIFI_ERROR_INVALID_ARGS; |
| goto cleanup; |
| } |
| stats->arp_rsp_count_out_of_order_drop = nla_get_u16(tb_vendor[ |
| QCA_ATTR_NUD_STATS_ARP_RSP_COUNT_OUT_OF_ORDER_DROP]); |
| |
| if (tb_vendor[QCA_ATTR_NUD_STATS_AP_LINK_ACTIVE]) |
| stats->ap_link_active = 1; |
| |
| if (tb_vendor[QCA_ATTR_NUD_STATS_IS_DAD]) |
| stats->is_duplicate_addr_detection = 1; |
| |
| ALOGV(" req_from_netdev %d count_to_lower :%d" |
| " count_by_lower :%d" |
| " count_tx_succ :%d rsp_count_lower :%d" |
| " rsp_count_upper :%d rsp_count_netdev :%d" |
| " out_of_order_drop :%d active_aplink %d" |
| " DAD %d ", |
| stats->arp_req_count_from_netdev, |
| stats->arp_req_count_to_lower_mac, |
| stats->arp_req_rx_count_by_lower_mac, |
| stats->arp_req_count_tx_success, |
| stats->arp_rsp_rx_count_by_lower_mac, |
| stats->arp_rsp_rx_count_by_upper_mac, |
| stats->arp_rsp_count_to_netdev, |
| stats->arp_rsp_count_out_of_order_drop, |
| stats->ap_link_active, |
| stats->is_duplicate_addr_detection); |
| |
| if (tb_vendor[QCA_ATTR_NUD_STATS_DATA_PKT_STATS]) { |
| mNUDStatsCommandInstance->GetPktInfo(tb_vendor); |
| } |
| } |
| } |
| cleanup: |
| if (status == WIFI_ERROR_INVALID_ARGS) |
| memset(&mStats,0,sizeof(nud_stats)); |
| if(mpktInfo != NULL) |
| free(mpktInfo); |
| |
| return status; |
| } |
| |
| void NUDStatsCommand::GetPktInfo(struct nlattr **tbvendor) |
| { |
| struct nlattr *tb; |
| int rem; |
| cmdData *pkt_stats; |
| char ipv6_address[INET6_ADDRSTRLEN]; |
| cmdData pktstats; |
| int nbuff = 0; |
| |
| for (tb = (struct nlattr *) nla_data(tbvendor[QCA_ATTR_NUD_STATS_DATA_PKT_STATS]), |
| rem = nla_len(tbvendor[QCA_ATTR_NUD_STATS_DATA_PKT_STATS]); |
| nla_ok(tb, rem); tb = nla_next(tb, &(rem))) |
| { |
| struct nlattr *tb2[QCA_ATTR_NUD_DATA_STATS_MAX + 1]; |
| nla_parse(tb2, QCA_ATTR_NUD_DATA_STATS_MAX, |
| (struct nlattr *) nla_data(tb), nla_len(tb), NULL); |
| |
| memset(&pktstats, 0, sizeof(cmdData)); |
| |
| if (tb2[QCA_ATTR_NUD_STATS_PKT_TYPE]) |
| { |
| pktstats.pkt_Type = nla_get_u32(tb2[QCA_ATTR_NUD_STATS_PKT_TYPE]); |
| } |
| |
| if (tb2[QCA_ATTR_NUD_STATS_PKT_DNS_DOMAIN_NAME]) |
| { |
| pktstats.domain_name = nla_get_string(tb2[QCA_ATTR_NUD_STATS_PKT_DNS_DOMAIN_NAME]); |
| } |
| |
| if (tb2[QCA_ATTR_NUD_STATS_PKT_SRC_PORT]) |
| { |
| pktstats.src_port = nla_get_u32(tb2[QCA_ATTR_NUD_STATS_PKT_SRC_PORT]); |
| } |
| |
| if (tb2[QCA_ATTR_NUD_STATS_PKT_DEST_PORT]) |
| { |
| pktstats.dst_port = nla_get_u32(tb2[QCA_ATTR_NUD_STATS_PKT_DEST_PORT]); |
| } |
| |
| if (tb2[QCA_ATTR_NUD_STATS_PKT_DEST_IPV4]) |
| { |
| pktstats.ipv4_addr.s_addr = nla_get_u32(tb2[QCA_ATTR_NUD_STATS_PKT_DEST_IPV4]); |
| } |
| |
| if (tb2[QCA_ATTR_NUD_STATS_PKT_DEST_IPV6]) |
| { |
| memcpy(pktstats.ipv6_addr, nla_data(tb2[QCA_ATTR_NUD_STATS_PKT_DEST_IPV6]), |
| sizeof(pktstats.ipv6_addr)); |
| } |
| |
| if (tb2[QCA_ATTR_NUD_STATS_PKT_REQ_COUNT_FROM_NETDEV]) |
| { |
| pktstats.stats.pkt_req_count_from_netdev = nla_get_u16(tb2[ |
| QCA_ATTR_NUD_STATS_PKT_REQ_COUNT_FROM_NETDEV]); |
| } |
| |
| if (tb2[QCA_ATTR_NUD_STATS_PKT_REQ_COUNT_TO_LOWER_MAC]) |
| { |
| pktstats.stats.pkt_req_count_to_lower_mac = nla_get_u16(tb2[ |
| QCA_ATTR_NUD_STATS_PKT_REQ_COUNT_TO_LOWER_MAC]); |
| } |
| |
| if (tb2[QCA_ATTR_NUD_STATS_PKT_REQ_RX_COUNT_BY_LOWER_MAC]) |
| { |
| pktstats.stats.pkt_req_rx_count_by_lower_mac = nla_get_u16(tb2[ |
| QCA_ATTR_NUD_STATS_PKT_REQ_RX_COUNT_BY_LOWER_MAC]); |
| } |
| |
| if (tb2[QCA_ATTR_NUD_STATS_PKT_REQ_COUNT_TX_SUCCESS]) |
| { |
| pktstats.stats.pkt_req_count_tx_success = nla_get_u16(tb2[ |
| QCA_ATTR_NUD_STATS_PKT_REQ_COUNT_TX_SUCCESS]); |
| } |
| |
| if (tb2[QCA_ATTR_NUD_STATS_PKT_RSP_RX_COUNT_BY_LOWER_MAC]) |
| { |
| pktstats.stats.pkt_rsp_rx_count_by_lower_mac = nla_get_u16(tb2[ |
| QCA_ATTR_NUD_STATS_PKT_RSP_RX_COUNT_BY_LOWER_MAC]); |
| } |
| |
| if (tb2[QCA_ATTR_NUD_STATS_PKT_RSP_RX_COUNT_BY_UPPER_MAC]) |
| { |
| pktstats.stats.pkt_rsp_rx_count_by_upper_mac = nla_get_u16(tb2[ |
| QCA_ATTR_NUD_STATS_PKT_RSP_RX_COUNT_BY_UPPER_MAC]); |
| } |
| |
| if (tb2[QCA_ATTR_NUD_STATS_PKT_RSP_COUNT_TO_NETDEV]) |
| { |
| pktstats.stats.pkt_rsp_count_to_netdev = nla_get_u16(tb2[ |
| QCA_ATTR_NUD_STATS_PKT_RSP_COUNT_TO_NETDEV]); |
| } |
| |
| if (tb2[QCA_ATTR_NUD_STATS_PKT_RSP_COUNT_OUT_OF_ORDER_DROP]) |
| { |
| pktstats.stats.pkt_rsp_count_out_of_order_drop = nla_get_u16(tb2[ |
| QCA_ATTR_NUD_STATS_PKT_RSP_COUNT_OUT_OF_ORDER_DROP]); |
| } |
| |
| if (inet_ntop(AF_INET6, pktstats.ipv6_addr, ipv6_address, |
| INET6_ADDRSTRLEN) == NULL) { |
| ALOGE("%s: failed to convert ipv6 address format", __FUNCTION__); |
| } |
| |
| ALOGV(" pkt_type %d domain_name :%s" |
| " src_port %d dst_port :%d" |
| " ipv4_address :%x ipv6_address %s" |
| " req_from_netdev %d count_to_lower :%d" |
| " count_by_lower :%d" |
| " count_tx_succ :%d rsp_count_lower :%d" |
| " rsp_count_upper :%d rsp_count_netdev :%d" |
| " out_of_order_drop :%d ", |
| pktstats.pkt_Type, pktstats.domain_name, |
| pktstats.src_port, pktstats.dst_port, |
| pktstats.ipv4_addr.s_addr, ipv6_address, |
| pktstats.stats.pkt_req_count_from_netdev, |
| pktstats.stats.pkt_req_count_to_lower_mac, |
| pktstats.stats.pkt_req_rx_count_by_lower_mac, |
| pktstats.stats.pkt_req_count_tx_success, |
| pktstats.stats.pkt_rsp_rx_count_by_lower_mac, |
| pktstats.stats.pkt_rsp_rx_count_by_upper_mac, |
| pktstats.stats.pkt_rsp_count_to_netdev, |
| pktstats.stats.pkt_rsp_count_out_of_order_drop); |
| |
| if (nbuff == 0) |
| pkt_stats = (cmdData *)malloc(sizeof(cmdData)); |
| else |
| pkt_stats = (cmdData *)realloc(pkt_stats,sizeof(cmdData) * (nbuff + 1)); |
| |
| mpktInfo = pkt_stats; |
| if (pkt_stats != NULL) { |
| memcpy(&pkt_stats[nbuff], &pktstats,sizeof(cmdData)); |
| nbuff++; |
| mnumStats = nbuff; |
| } |
| } |
| } |
| |
| void NUDStatsCommand::copyStats(nud_stats *stats, cmdData *pktstats) |
| { |
| memcpy(stats, &mStats, sizeof(nud_stats)); |
| pktstats = mpktInfo; |
| } |
| |
| wifi_error wifi_set_nud_stats(wifi_interface_handle iface, |
| u32 gw_addr, cmdData Data) |
| { |
| wifi_error ret; |
| NUDStatsCommand *NUDCommand; |
| struct nlattr *nl_data,*nl_pktInfo; |
| interface_info *iinfo = getIfaceInfo(iface); |
| wifi_handle handle = getWifiHandle(iface); |
| cmdData mData = Data; |
| cmdData pktstats = Data; |
| |
| ALOGV("gw_addr : %x", gw_addr); |
| NUDCommand = NUDStatsCommand::instance(handle); |
| if (NUDCommand == NULL) { |
| ALOGE("%s: Error NUDStatsCommand NULL", __FUNCTION__); |
| return WIFI_ERROR_INVALID_ARGS; |
| } |
| NUDCommand->setSubCmd(QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_SET); |
| |
| /* create the message */ |
| ret = NUDCommand->create(); |
| if (ret != WIFI_SUCCESS) |
| goto cleanup; |
| |
| ret = NUDCommand->set_iface_id(iinfo->name); |
| if (ret != WIFI_SUCCESS) |
| goto cleanup; |
| |
| /*add the attributes*/ |
| nl_data = NUDCommand->attr_start(NL80211_ATTR_VENDOR_DATA); |
| if (!nl_data) |
| goto cleanup; |
| /**/ |
| ret = NUDCommand->put_flag(QCA_ATTR_NUD_STATS_SET_START); |
| |
| ret = NUDCommand->put_u32(QCA_ATTR_NUD_STATS_GW_IPV4, gw_addr); |
| if (ret != WIFI_SUCCESS) |
| goto cleanup; |
| |
| if (mData.pkt_Type) { |
| /*start the packet info attributes in nested*/ |
| nl_pktInfo = NUDCommand->attr_start(QCA_ATTR_NUD_STATS_SET_DATA_PKT_INFO); |
| if (!nl_pktInfo) |
| goto cleanup; |
| else { |
| ALOGV(" pkt_type %d domain_name :%s" |
| " src_port %d dst_port :%d" |
| " ipv4_address :%x ipv6_address %s", |
| pktstats.pkt_Type, pktstats.domain_name, |
| pktstats.src_port, pktstats.dst_port, |
| pktstats.ipv4_addr.s_addr,pktstats.ipv6_addr); |
| |
| for (int i=0; i < MAX_INFO ; i++) { |
| /*add the packet type attributes*/ |
| struct nlattr *tb_tmp; |
| tb_tmp = NUDCommand->attr_start(i); |
| |
| ret = NUDCommand->put_u32(QCA_ATTR_NUD_STATS_DATA_PKT_INFO_TYPE,mData.pkt_Type); |
| if (ret != WIFI_SUCCESS) |
| goto cleanup; |
| |
| if (mData.domain_name) { |
| /*add the domain name attributes*/ |
| ret = NUDCommand->put_string(QCA_ATTR_NUD_STATS_DATA_PKT_INFO_DNS_DOMAIN_NAME, |
| mData.domain_name); |
| if (ret != WIFI_SUCCESS) |
| goto cleanup; |
| } |
| /*add the source port attributes*/ |
| ret = NUDCommand->put_u32(QCA_ATTR_NUD_STATS_DATA_PKT_INFO_SRC_PORT, |
| mData.src_port); |
| if (ret != WIFI_SUCCESS) |
| goto cleanup; |
| |
| /*add the dest port attributes*/ |
| ret = NUDCommand->put_u32(QCA_ATTR_NUD_STATS_DATA_PKT_INFO_DEST_PORT, |
| mData.dst_port); |
| if (ret != WIFI_SUCCESS) |
| goto cleanup; |
| |
| /*add the ipv4 address attributes*/ |
| ret = NUDCommand->put_u32(QCA_ATTR_NUD_STATS_DATA_PKT_INFO_DEST_IPV4, |
| mData.ipv4_addr.s_addr); |
| if (ret != WIFI_SUCCESS) |
| goto cleanup; |
| |
| /*add the ipv6 address attributes*/ |
| ret = NUDCommand->put_ipv6_addr(QCA_ATTR_NUD_STATS_DATA_PKT_INFO_DEST_IPV6, |
| mData.ipv6_addr); |
| if (ret != WIFI_SUCCESS) |
| goto cleanup; |
| NUDCommand->attr_end(tb_tmp); |
| } |
| } |
| NUDCommand->attr_end(nl_pktInfo); |
| } |
| NUDCommand->attr_end(nl_data); |
| |
| ret = NUDCommand->requestResponse(); |
| if (ret != WIFI_SUCCESS) { |
| ALOGE("%s: requestResponse Error:%d",__FUNCTION__, ret); |
| } |
| |
| cleanup: |
| return ret; |
| } |
| |
| |
| wifi_error wifi_get_nud_stats(wifi_interface_handle iface, |
| pkt_stats_result_handler handler) |
| { |
| wifi_error ret; |
| NUDStatsCommand *NUDCommand; |
| struct nlattr *nl_data; |
| interface_info *iinfo = getIfaceInfo(iface); |
| wifi_handle handle = getWifiHandle(iface); |
| |
| NUDCommand = NUDStatsCommand::instance(handle); |
| if (NUDCommand == NULL) { |
| ALOGE("%s: Error NUDStatsCommand NULL", __FUNCTION__); |
| return WIFI_ERROR_INVALID_ARGS; |
| } |
| NUDCommand->setSubCmd(QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_GET); |
| |
| NUDCommand->setHandler(handler); |
| |
| /* create the message */ |
| ret = NUDCommand->create(); |
| if (ret != WIFI_SUCCESS) |
| goto cleanup; |
| |
| ret = NUDCommand->set_iface_id(iinfo->name); |
| if (ret != WIFI_SUCCESS) |
| goto cleanup; |
| /*add the attributes*/ |
| nl_data = NUDCommand->attr_start(NL80211_ATTR_VENDOR_DATA); |
| if (!nl_data){ |
| ret = WIFI_ERROR_UNKNOWN; |
| goto cleanup; |
| } |
| /**/ |
| NUDCommand->attr_end(nl_data); |
| |
| ret = NUDCommand->requestResponse(); |
| if (ret != WIFI_SUCCESS) { |
| ALOGE("%s: requestResponse Error:%d",__FUNCTION__, ret); |
| goto cleanup; |
| } |
| |
| ret = NUDCommand->notifyResponse(); |
| if (ret != WIFI_SUCCESS) { |
| ALOGE("%s: requestResponse Error:%d",__FUNCTION__, ret); |
| goto cleanup; |
| } |
| |
| cleanup: |
| return ret; |
| } |
| |
| |
| wifi_error wifi_clear_nud_stats(wifi_interface_handle iface, |
| cmdData Data) |
| { |
| wifi_error ret; |
| NUDStatsCommand *NUDCommand; |
| struct nlattr *nl_data,*nl_pktInfo; |
| interface_info *iinfo = getIfaceInfo(iface); |
| wifi_handle handle = getWifiHandle(iface); |
| cmdData mData = Data; |
| |
| NUDCommand = NUDStatsCommand::instance(handle); |
| if (NUDCommand == NULL) { |
| ALOGE("%s: Error NUDStatsCommand NULL", __FUNCTION__); |
| return WIFI_ERROR_INVALID_ARGS; |
| } |
| NUDCommand->setSubCmd(QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_SET); |
| |
| /* create the message */ |
| ret = NUDCommand->create(); |
| if (ret != WIFI_SUCCESS) |
| goto cleanup; |
| |
| ret = NUDCommand->set_iface_id(iinfo->name); |
| if (ret != WIFI_SUCCESS) |
| goto cleanup; |
| |
| /*add the attributes*/ |
| nl_data = NUDCommand->attr_start(NL80211_ATTR_VENDOR_DATA); |
| if (!nl_data){ |
| ret = WIFI_ERROR_UNKNOWN; |
| goto cleanup; |
| } |
| if (mData.pkt_Type) { |
| /*set the packet info attributes in nested*/ |
| nl_pktInfo = NUDCommand->attr_start(QCA_ATTR_NUD_STATS_SET_DATA_PKT_INFO); |
| if (!nl_pktInfo){ |
| ret = WIFI_ERROR_UNKNOWN; |
| goto cleanup; |
| } |
| else { |
| ALOGV(" %s: pkt_type %d domain_name :%s" |
| " src_port %d dst_port :%d" |
| " ipv4_address :%x ipv6_address %s", |
| __FUNCTION__,mData.pkt_Type, mData.domain_name, |
| mData.src_port, mData.dst_port, |
| mData.ipv4_addr.s_addr,mData.ipv6_addr); |
| |
| for (int i=0; i < MAX_INFO ; i++) { |
| /*add the packet type attributes*/ |
| struct nlattr *tb_tmp; |
| tb_tmp = NUDCommand->attr_start(i); |
| |
| ret = NUDCommand->put_u32(QCA_ATTR_NUD_STATS_DATA_PKT_INFO_TYPE,mData.pkt_Type); |
| if (ret != WIFI_SUCCESS) |
| goto cleanup; |
| |
| NUDCommand->attr_end(tb_tmp); |
| } |
| } |
| NUDCommand->attr_end(nl_pktInfo); |
| } |
| NUDCommand->attr_end(nl_data); |
| |
| ret = NUDCommand->requestResponse(); |
| if (ret != WIFI_SUCCESS) |
| ALOGE("%s: requestResponse Error:%d",__FUNCTION__, ret); |
| |
| cleanup: |
| return ret; |
| } |