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