1 /*
2  * Copyright (c) 2017, The Linux Foundation. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *     * Redistributions of source code must retain the above copyright
8  *       notice, this list of conditions and the following disclaimer.
9  *     * Redistributions in binary form must reproduce the above
10  *       copyright notice, this list of conditions and the following
11  *       disclaimer in the documentation and/or other materials provided
12  *       with the distribution.
13  *     * Neither the name of The Linux Foundation nor the names of its
14  *       contributors may be used to endorse or promote products derived
15  *       from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
18  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
21  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
24  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
26  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
27  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29 
30 #include "sync.h"
31 
32 #define LOG_TAG  "WifiHAL"
33 
34 #include <utils/Log.h>
35 
36 #include "wifi_hal.h"
37 #include "common.h"
38 #include "cpp_bindings.h"
39 #include "wifihal_vendorcommand.h"
40 
41 //Singleton Static Instance
42 NUDStatsCommand* NUDStatsCommand::mNUDStatsCommandInstance  = NULL;
43 
44 // This function implements creation of Vendor command
45 // For NUDStats just call base Vendor command create
create()46 wifi_error NUDStatsCommand::create() {
47     wifi_error ret = mMsg.create(NL80211_CMD_VENDOR, 0, 0);
48     if (ret != WIFI_SUCCESS) {
49         return ret;
50     }
51     // insert the oui in the msg
52     ret = mMsg.put_u32(NL80211_ATTR_VENDOR_ID, mVendor_id);
53     if (ret != WIFI_SUCCESS)
54         goto out;
55 
56     // insert the subcmd in the msg
57     ret = mMsg.put_u32(NL80211_ATTR_VENDOR_SUBCMD, mSubcmd);
58     if (ret != WIFI_SUCCESS)
59         goto out;
60 
61 out:
62     return ret;
63 }
64 
NUDStatsCommand(wifi_handle handle,int id,u32 vendor_id,u32 subcmd)65 NUDStatsCommand::NUDStatsCommand(wifi_handle handle, int id, u32 vendor_id, u32 subcmd)
66         : WifiVendorCommand(handle, id, vendor_id, subcmd)
67 {
68     memset(&mStats, 0,sizeof(nud_stats));
69 }
70 
~NUDStatsCommand()71 NUDStatsCommand::~NUDStatsCommand()
72 {
73     mNUDStatsCommandInstance = NULL;
74 }
75 
instance(wifi_handle handle)76 NUDStatsCommand* NUDStatsCommand::instance(wifi_handle handle)
77 {
78     if (handle == NULL) {
79         ALOGE("Interface Handle is invalid");
80         return NULL;
81     }
82     if (mNUDStatsCommandInstance == NULL) {
83         mNUDStatsCommandInstance = new NUDStatsCommand(handle, 0,
84                 OUI_QCA,
85                 QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_SET);
86         return mNUDStatsCommandInstance;
87     }
88     else
89     {
90         if (handle != getWifiHandle(mNUDStatsCommandInstance->mInfo))
91         {
92             /* upper layer must have cleaned up the handle and reinitialized,
93                so we need to update the same */
94             ALOGE("Handle different, update the handle");
95             mNUDStatsCommandInstance->mInfo = (hal_info *)handle;
96         }
97     }
98     return mNUDStatsCommandInstance;
99 }
100 
setSubCmd(u32 subcmd)101 void NUDStatsCommand::setSubCmd(u32 subcmd)
102 {
103     mSubcmd = subcmd;
104 }
105 
requestResponse()106 wifi_error NUDStatsCommand::requestResponse()
107 {
108     return WifiCommand::requestResponse(mMsg);
109 }
110 
handleResponse(WifiEvent & reply)111 int NUDStatsCommand::handleResponse(WifiEvent &reply)
112 {
113     int status = WIFI_ERROR_NONE;
114     WifiVendorCommand::handleResponse(reply);
115 
116     // Parse the vendordata and get the attribute
117 
118     switch(mSubcmd)
119     {
120         case QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_GET:
121         {
122             struct nlattr *tb_vendor[QCA_ATTR_NUD_STATS_GET_MAX + 1];
123             nud_stats *stats = &mStats;
124 
125             memset(stats, 0, sizeof(nud_stats));
126             nla_parse(tb_vendor, QCA_ATTR_NUD_STATS_GET_MAX,
127                       (struct nlattr *)mVendorData, mDataLen, NULL);
128 
129             if (!tb_vendor[QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_FROM_NETDEV])
130             {
131                 ALOGE("%s: QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_FROM_NETDEV"
132                       " not found", __FUNCTION__);
133                 status = WIFI_ERROR_INVALID_ARGS;
134                 goto cleanup;
135             }
136             stats->arp_req_count_from_netdev = nla_get_u16(tb_vendor[
137                             QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_FROM_NETDEV]);
138 
139             if (!tb_vendor[QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_TO_LOWER_MAC])
140             {
141                 ALOGE("%s: QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_TO_LOWER_MAC"
142                       " not found", __FUNCTION__);
143                 status = WIFI_ERROR_INVALID_ARGS;
144                 goto cleanup;
145             }
146             stats->arp_req_count_to_lower_mac = nla_get_u16(tb_vendor[
147                             QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_TO_LOWER_MAC]);
148 
149             if (!tb_vendor[QCA_ATTR_NUD_STATS_ARP_REQ_RX_COUNT_BY_LOWER_MAC])
150             {
151                 ALOGE("%s: QCA_ATTR_NUD_STATS_ARP_REQ_RX_COUNT_BY_LOWER_MAC"
152                       " not found", __FUNCTION__);
153                 status = WIFI_ERROR_INVALID_ARGS;
154                 goto cleanup;
155             }
156             stats->arp_req_rx_count_by_lower_mac = nla_get_u16(tb_vendor[
157                             QCA_ATTR_NUD_STATS_ARP_REQ_RX_COUNT_BY_LOWER_MAC]);
158 
159             if (!tb_vendor[QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_TX_SUCCESS])
160             {
161                 ALOGE("%s: QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_TX_SUCCESS"
162                       " not found", __FUNCTION__);
163                 status = WIFI_ERROR_INVALID_ARGS;
164                 goto cleanup;
165             }
166             stats->arp_req_count_tx_success = nla_get_u16(tb_vendor[
167                             QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_TX_SUCCESS]);
168 
169             if (!tb_vendor[QCA_ATTR_NUD_STATS_ARP_RSP_RX_COUNT_BY_LOWER_MAC])
170             {
171                 ALOGE("%s: QCA_ATTR_NUD_STATS_ARP_RSP_RX_COUNT_BY_LOWER_MAC"
172                       " not found", __FUNCTION__);
173                 status = WIFI_ERROR_INVALID_ARGS;
174                 goto cleanup;
175             }
176             stats->arp_rsp_rx_count_by_lower_mac = nla_get_u16(tb_vendor[
177                             QCA_ATTR_NUD_STATS_ARP_RSP_RX_COUNT_BY_LOWER_MAC]);
178 
179             if (!tb_vendor[QCA_ATTR_NUD_STATS_ARP_RSP_RX_COUNT_BY_UPPER_MAC])
180             {
181                 ALOGE("%s: QCA_ATTR_NUD_STATS_ARP_RSP_RX_COUNT_BY_UPPER_MAC"
182                       " not found", __FUNCTION__);
183                 status = WIFI_ERROR_INVALID_ARGS;
184                 goto cleanup;
185             }
186             stats->arp_rsp_rx_count_by_upper_mac = nla_get_u16(tb_vendor[
187                             QCA_ATTR_NUD_STATS_ARP_RSP_RX_COUNT_BY_UPPER_MAC]);
188 
189             if (!tb_vendor[QCA_ATTR_NUD_STATS_ARP_RSP_COUNT_TO_NETDEV])
190             {
191                 ALOGE("%s: QCA_ATTR_NUD_STATS_ARP_RSP_COUNT_TO_NETDEV"
192                       " not found", __FUNCTION__);
193                 status = WIFI_ERROR_INVALID_ARGS;
194                 goto cleanup;
195             }
196             stats->arp_rsp_count_to_netdev = nla_get_u16(tb_vendor[
197                             QCA_ATTR_NUD_STATS_ARP_RSP_COUNT_TO_NETDEV]);
198 
199             if (!tb_vendor[QCA_ATTR_NUD_STATS_ARP_RSP_COUNT_OUT_OF_ORDER_DROP])
200             {
201                 ALOGE("%s: QCA_ATTR_NUD_STATS_ARP_RSP_COUNT_OUT_OF_ORDER_DROP"
202                       " not found", __FUNCTION__);
203                 status = WIFI_ERROR_INVALID_ARGS;
204                 goto cleanup;
205             }
206             stats->arp_rsp_count_out_of_order_drop = nla_get_u16(tb_vendor[
207                            QCA_ATTR_NUD_STATS_ARP_RSP_COUNT_OUT_OF_ORDER_DROP]);
208 
209             if (tb_vendor[QCA_ATTR_NUD_STATS_AP_LINK_ACTIVE])
210                 stats->ap_link_active = 1;
211 
212             if (tb_vendor[QCA_ATTR_NUD_STATS_IS_DAD])
213                 stats->is_duplicate_addr_detection = 1;
214 
215             ALOGV(" req_from_netdev %d count_to_lower :%d"
216                   " count_by_lower :%d"
217                   " count_tx_succ :%d rsp_count_lower :%d"
218                   " rsp_count_upper :%d  rsp_count_netdev :%d"
219                   " out_of_order_drop :%d active_aplink %d"
220                   " DAD %d ",
221                   stats->arp_req_count_from_netdev,
222                   stats->arp_req_count_to_lower_mac,
223                   stats->arp_req_rx_count_by_lower_mac,
224                   stats->arp_req_count_tx_success,
225                   stats->arp_rsp_rx_count_by_lower_mac,
226                   stats->arp_rsp_rx_count_by_upper_mac,
227                   stats->arp_rsp_count_to_netdev,
228                   stats->arp_rsp_count_out_of_order_drop,
229                   stats->ap_link_active,
230                   stats->is_duplicate_addr_detection);
231         }
232     }
233 cleanup:
234     if (status == WIFI_ERROR_INVALID_ARGS)
235        memset(&mStats,0,sizeof(nud_stats));
236 
237     return status;
238 }
239 
copyStats(nud_stats * stats)240 void NUDStatsCommand::copyStats(nud_stats *stats)
241 {
242     memcpy(stats, &mStats, sizeof(nud_stats));
243 }
244 
wifi_set_nud_stats(wifi_interface_handle iface,u32 gw_addr)245 wifi_error wifi_set_nud_stats(wifi_interface_handle iface, u32 gw_addr)
246 {
247     wifi_error ret;
248     NUDStatsCommand *NUDCommand;
249     struct nlattr *nl_data;
250     interface_info *iinfo = getIfaceInfo(iface);
251     wifi_handle handle = getWifiHandle(iface);
252 
253     ALOGV("gw_addr : %x", gw_addr);
254     NUDCommand = NUDStatsCommand::instance(handle);
255     if (NUDCommand == NULL) {
256         ALOGE("%s: Error NUDStatsCommand NULL", __FUNCTION__);
257         return WIFI_ERROR_INVALID_ARGS;
258     }
259     NUDCommand->setSubCmd(QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_SET);
260 
261     /* create the message */
262     ret = NUDCommand->create();
263     if (ret != WIFI_SUCCESS)
264         goto cleanup;
265 
266     ret = NUDCommand->set_iface_id(iinfo->name);
267     if (ret != WIFI_SUCCESS)
268         goto cleanup;
269 
270     /*add the attributes*/
271     nl_data = NUDCommand->attr_start(NL80211_ATTR_VENDOR_DATA);
272     if (!nl_data)
273         goto cleanup;
274     /**/
275     ret = NUDCommand->put_flag(QCA_ATTR_NUD_STATS_SET_START);
276 
277     ret = NUDCommand->put_u32(QCA_ATTR_NUD_STATS_GW_IPV4, gw_addr);
278     if (ret != WIFI_SUCCESS)
279         goto cleanup;
280     /**/
281     NUDCommand->attr_end(nl_data);
282 
283     ret = NUDCommand->requestResponse();
284     if (ret != WIFI_SUCCESS) {
285         ALOGE("%s: requestResponse Error:%d",__FUNCTION__, ret);
286     }
287 
288 cleanup:
289     return ret;
290 }
291 
292 
wifi_get_nud_stats(wifi_interface_handle iface,nud_stats * stats)293 wifi_error wifi_get_nud_stats(wifi_interface_handle iface,
294                               nud_stats *stats)
295 {
296     wifi_error ret;
297     NUDStatsCommand *NUDCommand;
298     struct nlattr *nl_data;
299     interface_info *iinfo = getIfaceInfo(iface);
300     wifi_handle handle = getWifiHandle(iface);
301 
302     if (stats == NULL) {
303         ALOGE("%s: Error stats is NULL", __FUNCTION__);
304         return WIFI_ERROR_INVALID_ARGS;
305     }
306 
307     NUDCommand = NUDStatsCommand::instance(handle);
308     if (NUDCommand == NULL) {
309         ALOGE("%s: Error NUDStatsCommand NULL", __FUNCTION__);
310         return WIFI_ERROR_INVALID_ARGS;
311     }
312     NUDCommand->setSubCmd(QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_GET);
313 
314     /* create the message */
315     ret = NUDCommand->create();
316     if (ret != WIFI_SUCCESS)
317         goto cleanup;
318 
319     ret = NUDCommand->set_iface_id(iinfo->name);
320     if (ret != WIFI_SUCCESS)
321         goto cleanup;
322     /*add the attributes*/
323     nl_data = NUDCommand->attr_start(NL80211_ATTR_VENDOR_DATA);
324     if (!nl_data)
325         goto cleanup;
326     /**/
327     NUDCommand->attr_end(nl_data);
328 
329     ret = NUDCommand->requestResponse();
330     if (ret != WIFI_SUCCESS) {
331         ALOGE("%s: requestResponse Error:%d",__FUNCTION__, ret);
332         goto cleanup;
333     }
334 
335     NUDCommand->copyStats(stats);
336 
337 cleanup:
338     return ret;
339 }
340 
341 
wifi_clear_nud_stats(wifi_interface_handle iface)342 wifi_error wifi_clear_nud_stats(wifi_interface_handle iface)
343 {
344     wifi_error ret;
345     NUDStatsCommand *NUDCommand;
346     struct nlattr *nl_data;
347     interface_info *iinfo = getIfaceInfo(iface);
348     wifi_handle handle = getWifiHandle(iface);
349 
350     NUDCommand = NUDStatsCommand::instance(handle);
351     if (NUDCommand == NULL) {
352         ALOGE("%s: Error NUDStatsCommand NULL", __FUNCTION__);
353         return WIFI_ERROR_INVALID_ARGS;
354     }
355     NUDCommand->setSubCmd(QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_SET);
356 
357     /* create the message */
358     ret = NUDCommand->create();
359     if (ret != WIFI_SUCCESS)
360         goto cleanup;
361 
362     ret = NUDCommand->set_iface_id(iinfo->name);
363     if (ret != WIFI_SUCCESS)
364         goto cleanup;
365 
366     /*add the attributes*/
367     nl_data = NUDCommand->attr_start(NL80211_ATTR_VENDOR_DATA);
368     if (!nl_data)
369         goto cleanup;
370 
371     NUDCommand->attr_end(nl_data);
372 
373     ret = NUDCommand->requestResponse();
374     if (ret != WIFI_SUCCESS)
375         ALOGE("%s: requestResponse Error:%d",__FUNCTION__, ret);
376 
377 cleanup:
378     return ret;
379 }
380