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 
49 typedef 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 
54 typedef 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     MKEEP_ALIVE_ATTRIBUTE_ETHER_TYPE
62 } WIFI_MKEEP_ALIVE_ATTRIBUTE;
63 
64 typedef enum {
65     START_MKEEP_ALIVE,
66     STOP_MKEEP_ALIVE,
67 } GetCmdType;
68 
69 ///////////////////////////////////////////////////////////////////////////////
70 class MKeepAliveCommand : public WifiCommand
71 {
72     u8 mIndex;
73     u8 *mIpPkt;
74     u16 mIpPktLen;
75     u8 *mSrcMacAddr;
76     u8 *mDstMacAddr;
77     u32 mPeriodMsec;
78     GetCmdType mType;
79     u16 mEther_type;
80 
81 public:
82 
83     // constructor for start sending
MKeepAliveCommand(wifi_interface_handle iface,u8 index,u16 ether_type,u8 * ip_packet,u16 ip_packet_len,u8 * src_mac_addr,u8 * dst_mac_addr,u32 period_msec,GetCmdType cmdType)84     MKeepAliveCommand(wifi_interface_handle iface, u8 index, u16 ether_type, u8 *ip_packet, u16 ip_packet_len,
85             u8 *src_mac_addr, u8 *dst_mac_addr, u32 period_msec, GetCmdType cmdType)
86         : WifiCommand("MKeepAliveCommand", iface, 0), mIndex(index), mEther_type(ether_type), mIpPkt(ip_packet),
87         mIpPktLen(ip_packet_len), mSrcMacAddr(src_mac_addr), mDstMacAddr(dst_mac_addr),
88         mPeriodMsec(period_msec), mType(cmdType)
89     { }
90 
91     // constructor for stop sending
MKeepAliveCommand(wifi_interface_handle iface,u8 index,GetCmdType cmdType)92     MKeepAliveCommand(wifi_interface_handle iface, u8 index, GetCmdType cmdType)
93         : WifiCommand("MKeepAliveCommand", iface, 0), mIndex(index), mType(cmdType)
94     {
95         mIpPkt = NULL;
96         mIpPktLen = 0;
97         mSrcMacAddr = NULL;
98         mDstMacAddr = NULL;
99         mPeriodMsec = 0;
100         mEther_type = 0;
101     }
102 
createRequest(WifiRequest & request)103     int createRequest(WifiRequest &request) {
104         int result = WIFI_SUCCESS;
105 
106         switch (mType) {
107             case START_MKEEP_ALIVE:
108             {
109                 result = request.create(GOOGLE_OUI, WIFI_OFFLOAD_START_MKEEP_ALIVE);
110                 if (result != WIFI_SUCCESS) {
111                     ALOGE("Failed to create start keep alive request; result = %d", result);
112                     return result;
113                 }
114 
115                 nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA);
116 
117                 result = request.put_u8(MKEEP_ALIVE_ATTRIBUTE_ID, mIndex);
118                 if (result < 0) {
119                     ALOGE("Failed to put id request; result = %d", result);
120                     return result;
121                 }
122 
123                 result = request.put_u16(MKEEP_ALIVE_ATTRIBUTE_IP_PKT_LEN, mIpPktLen);
124                 if (result < 0) {
125                     ALOGE("Failed to put ip pkt len request; result = %d", result);
126                     return result;
127                 }
128 
129                 result = request.put(MKEEP_ALIVE_ATTRIBUTE_IP_PKT, (u8*)mIpPkt, mIpPktLen);
130                 if (result < 0) {
131                     ALOGE("Failed to put ip pkt request; result = %d", result);
132                     return result;
133                 }
134 
135                 result = request.put_addr(MKEEP_ALIVE_ATTRIBUTE_SRC_MAC_ADDR, mSrcMacAddr);
136                 if (result < 0) {
137                     ALOGE("Failed to put src mac address request; result = %d", result);
138                     return result;
139                 }
140 
141                 result = request.put_addr(MKEEP_ALIVE_ATTRIBUTE_DST_MAC_ADDR, mDstMacAddr);
142                 if (result < 0) {
143                     ALOGE("Failed to put dst mac address request; result = %d", result);
144                     return result;
145                 }
146 
147                 result = request.put_u32(MKEEP_ALIVE_ATTRIBUTE_PERIOD_MSEC, mPeriodMsec);
148                 if (result < 0) {
149                     ALOGE("Failed to put period request; result = %d", result);
150                     return result;
151                 }
152                 result = request.put_u16(MKEEP_ALIVE_ATTRIBUTE_ETHER_TYPE, mEther_type);
153                 if (result < 0) {
154                     ALOGE("Failed to put ether type; result = %d", result);
155                     return result;
156                 }
157 
158                 request.attr_end(data);
159                 break;
160             }
161 
162             case STOP_MKEEP_ALIVE:
163             {
164                 result = request.create(GOOGLE_OUI, WIFI_OFFLOAD_STOP_MKEEP_ALIVE);
165                 if (result != WIFI_SUCCESS) {
166                     ALOGE("Failed to create stop keep alive request; result = %d", result);
167                     return result;
168                 }
169 
170                 nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA);
171 
172                 result = request.put_u8(MKEEP_ALIVE_ATTRIBUTE_ID, mIndex);
173                 if (result < 0) {
174                     ALOGE("Failed to put id request; result = %d", result);
175                     return result;
176                 }
177 
178                 request.attr_end(data);
179                 break;
180             }
181 
182             default:
183                 ALOGE("Unknown wifi keep alive command");
184                 result = WIFI_ERROR_UNKNOWN;
185         }
186         return result;
187     }
188 
start()189     int start() {
190         ALOGD("Start mkeep_alive command");
191         WifiRequest request(familyId(), ifaceId());
192         int result = createRequest(request);
193         if (result != WIFI_SUCCESS) {
194             ALOGE("Failed to create keep alive request; result = %d", result);
195             return result;
196         }
197 
198         result = requestResponse(request);
199         if (result != WIFI_SUCCESS) {
200             ALOGE("Failed to register keep alive response; result = %d", result);
201         }
202         return result;
203     }
204 
handleResponse(WifiEvent & reply)205     virtual int handleResponse(WifiEvent& reply) {
206         ALOGD("In MKeepAliveCommand::handleResponse");
207 
208         if (reply.get_cmd() != NL80211_CMD_VENDOR) {
209             ALOGD("Ignoring reply with cmd = %d", reply.get_cmd());
210             return NL_SKIP;
211         }
212 
213         switch (mType) {
214             case START_MKEEP_ALIVE:
215             case STOP_MKEEP_ALIVE:
216                 break;
217 
218             default:
219                 ALOGW("Unknown mkeep_alive command");
220         }
221         return NL_OK;
222     }
223 
handleEvent(WifiEvent & event)224     virtual int handleEvent(WifiEvent& event) {
225         /* NO events! */
226         return NL_SKIP;
227     }
228 };
229 
230 
231 /* API to send specified mkeep_alive packet periodically. */
wifi_start_sending_offloaded_packet(wifi_request_id index,wifi_interface_handle iface,u16 ether_type,u8 * ip_packet,u16 ip_packet_len,u8 * src_mac_addr,u8 * dst_mac_addr,u32 period_msec)232 wifi_error wifi_start_sending_offloaded_packet(wifi_request_id index, wifi_interface_handle iface,
233         u16 ether_type, u8 *ip_packet, u16 ip_packet_len, u8 *src_mac_addr, u8 *dst_mac_addr,
234         u32 period_msec)
235 {
236     if ((index > 0 && index <= N_AVAIL_ID) && (ip_packet != NULL) && (src_mac_addr != NULL)
237             && (dst_mac_addr != NULL) && (period_msec > 0)
238             && (ip_packet_len <= MKEEP_ALIVE_IP_PKT_MAX) && ((ether_type == ETHERTYPE_IP) ||
239             (ether_type == ETHERTYPE_IPV6))) {
240         MKeepAliveCommand *cmd = new MKeepAliveCommand(iface, index, ether_type, ip_packet, ip_packet_len,
241                 src_mac_addr, dst_mac_addr, period_msec, START_MKEEP_ALIVE);
242         NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY);
243         wifi_error result = (wifi_error)cmd->start();
244         cmd->releaseRef();
245         return result;
246     } else {
247         ALOGE("Invalid mkeep_alive parameters");
248         return  WIFI_ERROR_INVALID_ARGS;
249     }
250 }
251 
252 /* API to stop sending mkeep_alive packet. */
wifi_stop_sending_offloaded_packet(wifi_request_id index,wifi_interface_handle iface)253 wifi_error wifi_stop_sending_offloaded_packet(wifi_request_id index, wifi_interface_handle iface)
254 {
255     if (index > 0 && index <= N_AVAIL_ID) {
256         MKeepAliveCommand *cmd = new MKeepAliveCommand(iface, index, STOP_MKEEP_ALIVE);
257         NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY);
258         wifi_error result = (wifi_error)cmd->start();
259         cmd->releaseRef();
260         return result;
261     } else {
262         ALOGE("Invalid mkeep_alive parameters");
263         return  WIFI_ERROR_INVALID_ARGS;
264     }
265 }
266