1/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Portions copyright (C) 2017 Broadcom Limited
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 *     http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18
19#include <stdint.h>
20#include <fcntl.h>
21#include <sys/socket.h>
22#include <netlink/genl/genl.h>
23#include <netlink/genl/family.h>
24#include <netlink/genl/ctrl.h>
25#include <linux/rtnetlink.h>
26#include <netpacket/packet.h>
27#include <linux/filter.h>
28#include <linux/errqueue.h>
29
30#include <linux/pkt_sched.h>
31#include <netlink/object-api.h>
32#include <netlink/netlink.h>
33#include <netlink/socket.h>
34#include <netlink-private/object-api.h>
35#include <netlink-private/types.h>
36
37
38#include "nl80211_copy.h"
39#include "sync.h"
40
41#define LOG_TAG  "WifiHAL"
42
43#include <log/log.h>
44
45#include "wifi_hal.h"
46#include "common.h"
47#include "cpp_bindings.h"
48
49typedef enum {
50    WIFI_OFFLOAD_START_MKEEP_ALIVE = ANDROID_NL80211_SUBCMD_WIFI_OFFLOAD_RANGE_START,
51    WIFI_OFFLOAD_STOP_MKEEP_ALIVE,
52} WIFI_OFFLOAD_SUB_COMMAND;
53
54typedef enum {
55    MKEEP_ALIVE_ATTRIBUTE_ID,
56    MKEEP_ALIVE_ATTRIBUTE_IP_PKT,
57    MKEEP_ALIVE_ATTRIBUTE_IP_PKT_LEN,
58    MKEEP_ALIVE_ATTRIBUTE_SRC_MAC_ADDR,
59    MKEEP_ALIVE_ATTRIBUTE_DST_MAC_ADDR,
60    MKEEP_ALIVE_ATTRIBUTE_PERIOD_MSEC
61} WIFI_MKEEP_ALIVE_ATTRIBUTE;
62
63typedef enum {
64    START_MKEEP_ALIVE,
65    STOP_MKEEP_ALIVE,
66} GetCmdType;
67
68///////////////////////////////////////////////////////////////////////////////
69class MKeepAliveCommand : public WifiCommand
70{
71    u8 mIndex;
72    u8 *mIpPkt;
73    u16 mIpPktLen;
74    u8 *mSrcMacAddr;
75    u8 *mDstMacAddr;
76    u32 mPeriodMsec;
77    GetCmdType mType;
78
79public:
80
81    // constructor for start sending
82    MKeepAliveCommand(wifi_interface_handle iface, u8 index, u8 *ip_packet, u16 ip_packet_len,
83            u8 *src_mac_addr, u8 *dst_mac_addr, u32 period_msec, GetCmdType cmdType)
84        : WifiCommand("MKeepAliveCommand", iface, 0), mIndex(index), mIpPkt(ip_packet),
85        mIpPktLen(ip_packet_len), mSrcMacAddr(src_mac_addr), mDstMacAddr(dst_mac_addr),
86        mPeriodMsec(period_msec), mType(cmdType)
87    { }
88
89    // constructor for stop sending
90    MKeepAliveCommand(wifi_interface_handle iface, u8 index, GetCmdType cmdType)
91        : WifiCommand("MKeepAliveCommand", iface, 0), mIndex(index), mType(cmdType)
92    { }
93
94    int createRequest(WifiRequest &request) {
95        int result;
96
97        switch (mType) {
98            case START_MKEEP_ALIVE:
99            {
100                result = request.create(GOOGLE_OUI, WIFI_OFFLOAD_START_MKEEP_ALIVE);
101                if (result != WIFI_SUCCESS) {
102                    ALOGE("Failed to create start keep alive request; result = %d", result);
103                    return result;
104                }
105
106                nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA);
107
108                result = request.put_u8(MKEEP_ALIVE_ATTRIBUTE_ID, mIndex);
109                if (result < 0) {
110                    ALOGE("Failed to put id request; result = %d", result);
111                    return result;
112                }
113
114                result = request.put_u16(MKEEP_ALIVE_ATTRIBUTE_IP_PKT_LEN, mIpPktLen);
115                if (result < 0) {
116                    ALOGE("Failed to put ip pkt len request; result = %d", result);
117                    return result;
118                }
119
120                result = request.put(MKEEP_ALIVE_ATTRIBUTE_IP_PKT, (u8*)mIpPkt, mIpPktLen);
121                if (result < 0) {
122                    ALOGE("Failed to put ip pkt request; result = %d", result);
123                    return result;
124                }
125
126                result = request.put_addr(MKEEP_ALIVE_ATTRIBUTE_SRC_MAC_ADDR, mSrcMacAddr);
127                if (result < 0) {
128                    ALOGE("Failed to put src mac address request; result = %d", result);
129                    return result;
130                }
131
132                result = request.put_addr(MKEEP_ALIVE_ATTRIBUTE_DST_MAC_ADDR, mDstMacAddr);
133                if (result < 0) {
134                    ALOGE("Failed to put dst mac address request; result = %d", result);
135                    return result;
136                }
137
138                result = request.put_u32(MKEEP_ALIVE_ATTRIBUTE_PERIOD_MSEC, mPeriodMsec);
139                if (result < 0) {
140                    ALOGE("Failed to put period request; result = %d", result);
141                    return result;
142                }
143
144                request.attr_end(data);
145                break;
146            }
147
148            case STOP_MKEEP_ALIVE:
149            {
150                result = request.create(GOOGLE_OUI, WIFI_OFFLOAD_STOP_MKEEP_ALIVE);
151                if (result != WIFI_SUCCESS) {
152                    ALOGE("Failed to create stop keep alive request; result = %d", result);
153                    return result;
154                }
155
156                nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA);
157
158                result = request.put_u8(MKEEP_ALIVE_ATTRIBUTE_ID, mIndex);
159                if (result < 0) {
160                    ALOGE("Failed to put id request; result = %d", result);
161                    return result;
162                }
163
164                request.attr_end(data);
165                break;
166            }
167
168            default:
169                ALOGE("Unknown wifi keep alive command");
170                result = WIFI_ERROR_UNKNOWN;
171        }
172        return result;
173    }
174
175    int start() {
176        ALOGD("Start mkeep_alive command");
177        WifiRequest request(familyId(), ifaceId());
178        int result = createRequest(request);
179        if (result != WIFI_SUCCESS) {
180            ALOGE("Failed to create keep alive request; result = %d", result);
181            return result;
182        }
183
184        result = requestResponse(request);
185        if (result != WIFI_SUCCESS) {
186            ALOGE("Failed to register keep alive response; result = %d", result);
187        }
188        return result;
189    }
190
191    virtual int handleResponse(WifiEvent& reply) {
192        ALOGD("In MKeepAliveCommand::handleResponse");
193
194        if (reply.get_cmd() != NL80211_CMD_VENDOR) {
195            ALOGD("Ignoring reply with cmd = %d", reply.get_cmd());
196            return NL_SKIP;
197        }
198
199        switch (mType) {
200            case START_MKEEP_ALIVE:
201            case STOP_MKEEP_ALIVE:
202                break;
203
204            default:
205                ALOGW("Unknown mkeep_alive command");
206        }
207        return NL_OK;
208    }
209
210    virtual int handleEvent(WifiEvent& event) {
211        /* NO events! */
212        return NL_SKIP;
213    }
214};
215
216
217/* API to send specified mkeep_alive packet periodically. */
218wifi_error wifi_start_sending_offloaded_packet(wifi_request_id index, wifi_interface_handle iface,
219        u8 *ip_packet, u16 ip_packet_len, u8 *src_mac_addr, u8 *dst_mac_addr, u32 period_msec)
220{
221    if ((index > 0 && index <= N_AVAIL_ID) && (ip_packet != NULL) && (src_mac_addr != NULL)
222            && (dst_mac_addr != NULL) && (period_msec > 0)
223            && (ip_packet_len <= MKEEP_ALIVE_IP_PKT_MAX)) {
224        MKeepAliveCommand *cmd = new MKeepAliveCommand(iface, index, ip_packet, ip_packet_len,
225                src_mac_addr, dst_mac_addr, period_msec, START_MKEEP_ALIVE);
226        NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY);
227        wifi_error result = (wifi_error)cmd->start();
228        cmd->releaseRef();
229        return result;
230    } else {
231        ALOGE("Invalid mkeep_alive parameters");
232        return  WIFI_ERROR_INVALID_ARGS;
233    }
234}
235
236/* API to stop sending mkeep_alive packet. */
237wifi_error wifi_stop_sending_offloaded_packet(wifi_request_id index, wifi_interface_handle iface)
238{
239    if (index > 0 && index <= N_AVAIL_ID) {
240        MKeepAliveCommand *cmd = new MKeepAliveCommand(iface, index, STOP_MKEEP_ALIVE);
241        NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY);
242        wifi_error result = (wifi_error)cmd->start();
243        cmd->releaseRef();
244        return result;
245    } else {
246        ALOGE("Invalid mkeep_alive parameters");
247        return  WIFI_ERROR_INVALID_ARGS;
248    }
249}
250