1 /*
2 * Driver interaction with Linux nl80211/cfg80211
3 * Copyright (c) 2002-2015, Jouni Malinen <[email protected]>
4 * Copyright (c) 2003-2004, Instant802 Networks, Inc.
5 * Copyright (c) 2005-2006, Devicescape Software, Inc.
6 * Copyright (c) 2007, Johannes Berg <[email protected]>
7 * Copyright (c) 2009-2010, Atheros Communications
8 * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions are
12 * met:
13 * * Redistributions of source code must retain the above copyright
14 * * Redistributions in binary form must reproduce the above
15 * copyright notice, this list of conditions and the following
16 * disclaimer in the documentation and/or other materials provided
17 * with the distribution.
18 * * Neither the name of The Linux Foundation nor the names of its
19 * contributors may be used to endorse or promote products derived
20 * from this software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
23 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
24 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
26 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
29 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
30 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
31 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
32 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE
33 *
34 */
35
36 #include <errno.h>
37 #include <netlink/genl/family.h>
38 #include <netlink/genl/ctrl.h>
39 #include <linux/pkt_sched.h>
40 #include <unistd.h>
41 #include "cld80211_lib.h"
42
43 #ifndef LE_BUILD
44 #include <log/log.h>
45 #undef LOG_TAG
46 #define LOG_TAG "CLD80211"
47 #else
48 #include <stdlib.h>
49 #include <syslog.h>
50 #include <unistd.h>
51 #define ALOGI(fmt, args...) syslog(LOG_INFO, fmt, ## args)
52 #define ALOGE(fmt, args...) syslog(LOG_ERR, fmt, ## args)
53 extern const char *__progname;
getprogname()54 const char *getprogname() { return (__progname); }
55 #endif
56
57 #define SOCK_BUF_SIZE (256*1024)
58
59 struct family_data {
60 const char *group;
61 int id;
62 };
63
64
create_nl_socket(int protocol)65 static struct nl_sock * create_nl_socket(int protocol)
66 {
67 struct nl_sock *sock;
68
69 sock = nl_socket_alloc();
70 if (sock == NULL) {
71 ALOGE("%s: Failed to create NL socket, err: %d",
72 getprogname(), errno);
73 return NULL;
74 }
75
76 if (nl_connect(sock, protocol)) {
77 ALOGE("%s: Could not connect sock, err: %d",
78 getprogname(), errno);
79 nl_socket_free(sock);
80 return NULL;
81 }
82
83 return sock;
84 }
85
86
init_exit_sockets(struct cld80211_ctx * ctx)87 static int init_exit_sockets(struct cld80211_ctx *ctx)
88 {
89 ctx->exit_sockets[0] = -1;
90 ctx->exit_sockets[1] = -1;
91 if (socketpair(AF_UNIX, SOCK_STREAM, 0, &ctx->exit_sockets[0]) == -1) {
92 ALOGE("%s: Failed to create exit socket pair", getprogname());
93 return -1;
94 }
95 ALOGI("%s: initialized exit socket pair", getprogname());
96
97 return 0;
98 }
99
100
cleanup_exit_sockets(struct cld80211_ctx * ctx)101 static void cleanup_exit_sockets(struct cld80211_ctx *ctx)
102 {
103 if (ctx->exit_sockets[0] >= 0) {
104 close(ctx->exit_sockets[0]);
105 ctx->exit_sockets[0] = -1;
106 }
107
108 if (ctx->exit_sockets[1] >= 0) {
109 close(ctx->exit_sockets[1]);
110 ctx->exit_sockets[1] = -1;
111 }
112 }
113
114
exit_cld80211_recv(struct cld80211_ctx * ctx)115 void exit_cld80211_recv(struct cld80211_ctx *ctx)
116 {
117 if (!ctx) {
118 ALOGE("%s: ctx is NULL: %s", getprogname(), __func__);
119 return;
120 }
121 TEMP_FAILURE_RETRY(write(ctx->exit_sockets[0], "E", 1));
122 ALOGI("%s: Sent msg on exit sock to unblock poll()", getprogname());
123 }
124
125
126 /* Event handlers */
response_handler(struct nl_msg * msg,void * arg)127 static int response_handler(struct nl_msg *msg, void *arg)
128 {
129 UNUSED(msg);
130 UNUSED(arg);
131 ALOGI("%s: Received nlmsg response: no callback registered;drop it",
132 getprogname());
133
134 return NL_SKIP;
135 }
136
137
ack_handler(struct nl_msg * msg,void * arg)138 static int ack_handler(struct nl_msg *msg, void *arg)
139 {
140 int *err = (int *)arg;
141 *err = 0;
142 UNUSED(msg);
143 return NL_STOP;
144 }
145
146
finish_handler(struct nl_msg * msg,void * arg)147 static int finish_handler(struct nl_msg *msg, void *arg)
148 {
149 int *ret = (int *)arg;
150 *ret = 0;
151 UNUSED(msg);
152 return NL_SKIP;
153 }
154
155
error_handler(struct sockaddr_nl * nla,struct nlmsgerr * err,void * arg)156 static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err,
157 void *arg)
158 {
159 int *ret = (int *)arg;
160 *ret = err->error;
161
162 UNUSED(nla);
163 ALOGE("%s: error_handler received : %d", getprogname(), err->error);
164 return NL_SKIP;
165 }
166
167
no_seq_check(struct nl_msg * msg,void * arg)168 static int no_seq_check(struct nl_msg *msg, void *arg)
169 {
170 UNUSED(msg);
171 UNUSED(arg);
172 return NL_OK;
173 }
174
175
cld80211_recv_msg(struct nl_sock * sock,struct nl_cb * cb)176 int cld80211_recv_msg(struct nl_sock *sock, struct nl_cb *cb)
177 {
178 if (!sock || !cb) {
179 ALOGE("%s: %s is NULL", getprogname(), sock?"cb":"sock");
180 return -EINVAL;
181 }
182
183 int res = nl_recvmsgs(sock, cb);
184 if(res)
185 ALOGE("%s: Error :%d while reading nl msg , err: %d",
186 getprogname(), res, errno);
187 return res;
188 }
189
190
cld80211_handle_event(int events,struct nl_sock * sock,struct nl_cb * cb)191 static void cld80211_handle_event(int events, struct nl_sock *sock,
192 struct nl_cb *cb)
193 {
194 if (events & POLLERR) {
195 ALOGE("%s: Error reading from socket", getprogname());
196 cld80211_recv_msg(sock, cb);
197 } else if (events & POLLHUP) {
198 ALOGE("%s: Remote side hung up", getprogname());
199 } else if (events & POLLIN) {
200 cld80211_recv_msg(sock, cb);
201 } else {
202 ALOGE("%s: Unknown event - %0x", getprogname(), events);
203 }
204 }
205
206
family_handler(struct nl_msg * msg,void * arg)207 static int family_handler(struct nl_msg *msg, void *arg)
208 {
209 struct family_data *res = arg;
210 struct nlattr *tb[CTRL_ATTR_MAX + 1];
211 struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
212 struct nlattr *mcgrp;
213 int i;
214
215 nla_parse(tb, CTRL_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
216 genlmsg_attrlen(gnlh, 0), NULL);
217 if (!tb[CTRL_ATTR_MCAST_GROUPS])
218 return NL_SKIP;
219
220 nla_for_each_nested(mcgrp, tb[CTRL_ATTR_MCAST_GROUPS], i) {
221 struct nlattr *tb2[CTRL_ATTR_MCAST_GRP_MAX + 1];
222 nla_parse(tb2, CTRL_ATTR_MCAST_GRP_MAX, nla_data(mcgrp),
223 nla_len(mcgrp), NULL);
224
225 if (!tb2[CTRL_ATTR_MCAST_GRP_NAME] ||
226 !tb2[CTRL_ATTR_MCAST_GRP_ID] ||
227 strncmp(nla_data(tb2[CTRL_ATTR_MCAST_GRP_NAME]),
228 res->group,
229 nla_len(tb2[CTRL_ATTR_MCAST_GRP_NAME])) != 0)
230 continue;
231 res->id = nla_get_u32(tb2[CTRL_ATTR_MCAST_GRP_ID]);
232 break;
233 };
234
235 return NL_SKIP;
236 }
237
238
get_multicast_id(struct cld80211_ctx * ctx,const char * group)239 static int get_multicast_id(struct cld80211_ctx *ctx, const char *group)
240 {
241 struct family_data res = { group, -ENOENT };
242 struct nl_msg *nlmsg = nlmsg_alloc();
243
244 if (!nlmsg) {
245 return -1;
246 }
247
248 genlmsg_put(nlmsg, 0, 0, ctx->nlctrl_familyid, 0, 0,
249 CTRL_CMD_GETFAMILY, 0);
250 nla_put_string(nlmsg, CTRL_ATTR_FAMILY_NAME, "cld80211");
251
252 cld80211_send_recv_msg(ctx, nlmsg, family_handler, &res);
253 ALOGI("%s: nlctrl family id: %d group: %s mcast_id: %d", getprogname(),
254 ctx->nlctrl_familyid, group, res.id);
255 nlmsg_free(nlmsg);
256 return res.id;
257 }
258
259
cld80211_add_mcast_group(struct cld80211_ctx * ctx,const char * mcgroup)260 int cld80211_add_mcast_group(struct cld80211_ctx *ctx, const char* mcgroup)
261 {
262 if (!ctx || !mcgroup) {
263 ALOGE("%s: ctx/mcgroup is NULL: %s", getprogname(), __func__);
264 return 0;
265 }
266 int id = get_multicast_id(ctx, mcgroup);
267 if (id < 0) {
268 ALOGE("%s: Could not find group %s, errno: %d id: %d",
269 getprogname(), mcgroup, errno, id);
270 return id;
271 }
272
273 int ret = nl_socket_add_membership(ctx->sock, id);
274 if (ret < 0) {
275 ALOGE("%s: Could not add membership to group %s, errno: %d",
276 getprogname(), mcgroup, errno);
277 }
278
279 return ret;
280 }
281
282
cld80211_remove_mcast_group(struct cld80211_ctx * ctx,const char * mcgroup)283 int cld80211_remove_mcast_group(struct cld80211_ctx *ctx, const char* mcgroup)
284 {
285 // Drop membership is not a necessary cleanup action so comment it out.
286 #if 0
287 if (!ctx || !mcgroup) {
288 ALOGE("%s: ctx/mcgroup is NULL: %s", getprogname(), __func__);
289 return 0;
290 }
291 int id = get_multicast_id(ctx, mcgroup);
292 if (id < 0) {
293 ALOGE("%s: Could not find group %s, errno: %d id: %d",
294 getprogname(), mcgroup, errno, id);
295 return id;
296 }
297
298 int ret = nl_socket_drop_membership(ctx->sock, id);
299 if (ret < 0) {
300 ALOGE("%s: Could not drop membership from group %s, errno: %d,"
301 " ret: %d", getprogname(), mcgroup, errno, ret);
302 return ret;
303 }
304 #endif
305 return 0;
306 }
307
308
cld80211_msg_alloc(struct cld80211_ctx * ctx,int cmd,struct nlattr ** nla_data,int pid)309 struct nl_msg *cld80211_msg_alloc(struct cld80211_ctx *ctx, int cmd,
310 struct nlattr **nla_data, int pid)
311 {
312 struct nl_msg *nlmsg;
313
314 if (!ctx || !nla_data) {
315 ALOGE("%s: ctx is null: %s", getprogname(), __func__);
316 return NULL;
317 }
318
319 nlmsg = nlmsg_alloc();
320 if (nlmsg == NULL) {
321 ALOGE("%s: Out of memory", getprogname());
322 return NULL;
323 }
324
325 genlmsg_put(nlmsg, pid, /* seq = */ 0, ctx->netlink_familyid,
326 0, 0, cmd, /* version = */ 0);
327
328 *nla_data = nla_nest_start(nlmsg, CLD80211_ATTR_VENDOR_DATA);
329 if (!*nla_data)
330 goto cleanup;
331
332 return nlmsg;
333
334 cleanup:
335 if (nlmsg)
336 nlmsg_free(nlmsg);
337 return NULL;
338 }
339
340
cld80211_send_msg(struct cld80211_ctx * ctx,struct nl_msg * nlmsg)341 int cld80211_send_msg(struct cld80211_ctx *ctx, struct nl_msg *nlmsg)
342 {
343 int err;
344
345 if (!ctx || !ctx->sock || !nlmsg) {
346 ALOGE("%s: Invalid data from client", getprogname());
347 return -EINVAL;
348 }
349
350 err = nl_send_auto_complete(ctx->sock, nlmsg); /* send message */
351 if (err < 0) {
352 ALOGE("%s: failed to send msg: %d", getprogname(), err);
353 return err;
354 }
355
356 return 0;
357 }
358
359
cld80211_send_recv_msg(struct cld80211_ctx * ctx,struct nl_msg * nlmsg,int (* valid_handler)(struct nl_msg *,void *),void * valid_data)360 int cld80211_send_recv_msg(struct cld80211_ctx *ctx, struct nl_msg *nlmsg,
361 int (*valid_handler)(struct nl_msg *, void *),
362 void *valid_data)
363 {
364 int err;
365
366 if (!ctx || !ctx->sock || !nlmsg) {
367 ALOGE("%s: Invalid data from client", getprogname());
368 return -EINVAL;
369 }
370
371 struct nl_cb *cb = nl_cb_alloc(NL_CB_DEFAULT);
372 if (!cb)
373 return -ENOMEM;
374
375 err = nl_send_auto_complete(ctx->sock, nlmsg); /* send message */
376 if (err < 0)
377 goto out;
378
379 err = 1;
380
381 nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, no_seq_check, NULL);
382 nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &err);
383 nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &err);
384 nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &err);
385
386 if (valid_handler)
387 nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM,
388 valid_handler, valid_data);
389 else
390 nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM,
391 response_handler, valid_data);
392
393 while (err > 0) { /* wait for reply */
394 int res = nl_recvmsgs(ctx->sock, cb);
395 if (res) {
396 ALOGE("%s: cld80211: nl_recvmsgs failed: %d",
397 getprogname(), res);
398 }
399 }
400 out:
401 nl_cb_put(cb);
402 return err;
403 }
404
405
cld80211_recv(struct cld80211_ctx * ctx,int timeout,bool recv_multi_msg,int (* valid_handler)(struct nl_msg *,void *),void * cbctx)406 int cld80211_recv(struct cld80211_ctx *ctx, int timeout, bool recv_multi_msg,
407 int (*valid_handler)(struct nl_msg *, void *),
408 void *cbctx)
409 {
410 struct pollfd pfd[2];
411 struct nl_cb *cb;
412 int err;
413
414 if (!ctx || !ctx->sock || !valid_handler) {
415 ALOGE("%s: Invalid data from client", getprogname());
416 return -EINVAL;
417 }
418
419 cb = nl_cb_alloc(NL_CB_DEFAULT);
420 if (!cb)
421 return -ENOMEM;
422
423 memset(&pfd[0], 0, 2*sizeof(struct pollfd));
424
425 err = 1;
426
427 nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &err);
428 nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &err);
429 nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &err);
430 nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, no_seq_check, NULL);
431 nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, valid_handler, cbctx);
432
433 pfd[0].fd = nl_socket_get_fd(ctx->sock);
434 pfd[0].events = POLLIN;
435
436 pfd[1].fd = ctx->exit_sockets[1];
437 pfd[1].events = POLLIN;
438
439 do {
440 pfd[0].revents = 0;
441 pfd[1].revents = 0;
442 int result = poll(pfd, 2, timeout);
443 if (result < 0) {
444 ALOGE("%s: Error polling socket", getprogname());
445 } else if (pfd[0].revents & (POLLIN | POLLHUP | POLLERR)) {
446 cld80211_handle_event(pfd[0].revents, ctx->sock, cb);
447 if (!recv_multi_msg)
448 break;
449 } else {
450 ALOGI("%s: Exiting poll", getprogname());
451 break;
452 }
453 } while (1);
454
455 nl_cb_put(cb);
456 return 0;
457 }
458
459
cld80211_init(void)460 struct cld80211_ctx * cld80211_init(void)
461 {
462 struct cld80211_ctx *ctx;
463
464 ctx = (struct cld80211_ctx *)malloc(sizeof(struct cld80211_ctx));
465 if (ctx == NULL) {
466 ALOGE("%s: Failed to alloc cld80211_ctx", getprogname());
467 return NULL;
468 }
469 memset(ctx, 0, sizeof(struct cld80211_ctx));
470
471 ctx->sock = create_nl_socket(NETLINK_GENERIC);
472 if (ctx->sock == NULL) {
473 ALOGE("%s: Failed to create socket port", getprogname());
474 goto cleanup;
475 }
476
477 /* Set the socket buffer size */
478 if (nl_socket_set_buffer_size(ctx->sock, SOCK_BUF_SIZE , 0) < 0) {
479 ALOGE("%s: Could not set nl_socket RX buffer size for sock: %s",
480 getprogname(), strerror(errno));
481 /* continue anyway with the default (smaller) buffer */
482 }
483
484 ctx->netlink_familyid = genl_ctrl_resolve(ctx->sock, "cld80211");
485 if (ctx->netlink_familyid < 0) {
486 ALOGE("%s: Could not resolve cld80211 familty id",
487 getprogname());
488 goto cleanup;
489 }
490
491 ctx->nlctrl_familyid = genl_ctrl_resolve(ctx->sock, "nlctrl");
492 if (ctx->nlctrl_familyid < 0) {
493 ALOGE("%s: net link family nlctrl is not present: %d err:%d",
494 getprogname(), ctx->nlctrl_familyid, errno);
495 goto cleanup;
496 }
497
498
499 if (init_exit_sockets(ctx) != 0) {
500 ALOGE("%s: Failed to initialize exit sockets", getprogname());
501 goto cleanup;
502 }
503
504 return ctx;
505 cleanup:
506 if (ctx->sock) {
507 nl_socket_free(ctx->sock);
508 }
509 free (ctx);
510 return NULL;
511 }
512
513
cld80211_deinit(struct cld80211_ctx * ctx)514 void cld80211_deinit(struct cld80211_ctx *ctx)
515 {
516 if (!ctx || !ctx->sock) {
517 ALOGE("%s: ctx/sock is NULL", getprogname());
518 return;
519 }
520 nl_socket_free(ctx->sock);
521 cleanup_exit_sockets(ctx);
522 free (ctx);
523 }
524