]> git.ipfire.org Git - thirdparty/iw.git/blame - wowlan.c
iw: bump version to 4.7
[thirdparty/iw.git] / wowlan.c
CommitLineData
3ff24563
JB
1#include <errno.h>
2#include <string.h>
819b78cc 3#include <stdio.h>
3ff24563
JB
4
5#include <netlink/genl/genl.h>
6#include <netlink/genl/family.h>
7#include <netlink/genl/ctrl.h>
8#include <netlink/msg.h>
9#include <netlink/attr.h>
10
819b78cc
JB
11#include <arpa/inet.h>
12
3ff24563
JB
13#include "nl80211.h"
14#include "iw.h"
15
16SECTION(wowlan);
17
819b78cc
JB
18static int wowlan_parse_tcp_file(struct nl_msg *msg, const char *fn)
19{
20 char buf[16768];
21 int err = 1;
22 FILE *f = fopen(fn, "r");
23 struct nlattr *tcp;
24
25 if (!f)
26 return 1;
27 tcp = nla_nest_start(msg, NL80211_WOWLAN_TRIG_TCP_CONNECTION);
28 if (!tcp)
29 goto nla_put_failure;
30
31 while (!feof(f)) {
32 char *eol;
33
34 if (!fgets(buf, sizeof(buf), f))
35 break;
36
37 eol = strchr(buf + 5, '\r');
38 if (eol)
39 *eol = 0;
40 eol = strchr(buf + 5, '\n');
41 if (eol)
42 *eol = 0;
43
44 if (strncmp(buf, "source=", 7) == 0) {
45 struct in_addr in_addr;
46 char *addr = buf + 7;
47 char *port = strchr(buf + 7, ':');
48
49 if (port) {
50 *port = 0;
51 port++;
52 }
53 if (inet_aton(addr, &in_addr) == 0)
54 goto close;
55 NLA_PUT_U32(msg, NL80211_WOWLAN_TCP_SRC_IPV4,
56 in_addr.s_addr);
57 if (port)
58 NLA_PUT_U16(msg, NL80211_WOWLAN_TCP_SRC_PORT,
59 atoi(port));
60 } else if (strncmp(buf, "dest=", 5) == 0) {
61 struct in_addr in_addr;
62 char *addr = buf + 5;
63 char *port = strchr(buf + 5, ':');
64 char *mac;
65 unsigned char macbuf[6];
66
67 if (!port)
68 goto close;
69 *port = 0;
70 port++;
71 mac = strchr(port, '@');
72 if (!mac)
73 goto close;
74 *mac = 0;
75 mac++;
76 if (inet_aton(addr, &in_addr) == 0)
77 goto close;
78 NLA_PUT_U32(msg, NL80211_WOWLAN_TCP_DST_IPV4,
79 in_addr.s_addr);
80 NLA_PUT_U16(msg, NL80211_WOWLAN_TCP_DST_PORT,
81 atoi(port));
82 if (mac_addr_a2n(macbuf, mac))
83 goto close;
84 NLA_PUT(msg, NL80211_WOWLAN_TCP_DST_MAC,
85 6, macbuf);
86 } else if (strncmp(buf, "data=", 5) == 0) {
87 size_t len;
88 unsigned char *pkt = parse_hex(buf + 5, &len);
89
90 if (!pkt)
91 goto close;
92 NLA_PUT(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD, len, pkt);
93 free(pkt);
94 } else if (strncmp(buf, "data.interval=", 14) == 0) {
95 NLA_PUT_U32(msg, NL80211_WOWLAN_TCP_DATA_INTERVAL,
509a5e5a 96 atoi(buf + 14));
819b78cc
JB
97 } else if (strncmp(buf, "wake=", 5) == 0) {
98 unsigned char *pat, *mask;
99 size_t patlen;
100
101 if (parse_hex_mask(buf + 5, &pat, &patlen, &mask))
102 goto close;
103 NLA_PUT(msg, NL80211_WOWLAN_TCP_WAKE_MASK,
104 DIV_ROUND_UP(patlen, 8), mask);
105 NLA_PUT(msg, NL80211_WOWLAN_TCP_WAKE_PAYLOAD,
106 patlen, pat);
107 free(mask);
108 free(pat);
109 } else if (strncmp(buf, "data.seq=", 9) == 0) {
110 struct nl80211_wowlan_tcp_data_seq seq = {};
111 char *len, *offs, *start;
112
113 len = buf + 9;
114 offs = strchr(len, ',');
115 if (!offs)
116 goto close;
117 *offs = 0;
118 offs++;
119 start = strchr(offs, ',');
120 if (start) {
121 *start = 0;
122 start++;
123 seq.start = atoi(start);
124 }
125 seq.len = atoi(len);
126 seq.offset = atoi(offs);
127
128 NLA_PUT(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ,
129 sizeof(seq), &seq);
130 } else if (strncmp(buf, "data.tok=", 9) == 0) {
131 struct nl80211_wowlan_tcp_data_token *tok;
132 size_t stream_len;
133 char *len, *offs, *toks;
134 unsigned char *stream;
135
136 len = buf + 9;
137 offs = strchr(len, ',');
138 if (!offs)
139 goto close;
140 *offs = 0;
141 offs++;
142 toks = strchr(offs, ',');
143 if (!toks)
144 goto close;
145 *toks = 0;
146 toks++;
147
148 stream = parse_hex(toks, &stream_len);
149 if (!stream)
150 goto close;
151 tok = malloc(sizeof(*tok) + stream_len);
152 if (!tok) {
153 free(stream);
154 err = -ENOMEM;
155 goto close;
156 }
157
158 tok->len = atoi(len);
159 tok->offset = atoi(offs);
160 memcpy(tok->token_stream, stream, stream_len);
161
ab65a092
OO
162 if (nla_put(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN,
163 sizeof(*tok) + stream_len, tok) < 0) {
164 free(stream);
165 free(tok);
166 goto nla_put_failure;
167 }
819b78cc
JB
168 free(stream);
169 free(tok);
170 } else {
171 if (buf[0] == '#')
172 continue;
173 goto close;
174 }
175 }
176
177 err = 0;
178 goto close;
179 nla_put_failure:
180 err = -ENOBUFS;
181 close:
182 fclose(f);
96fa73c7
AK
183 if (tcp)
184 nla_nest_end(msg, tcp);
819b78cc
JB
185 return err;
186}
187
d516c5bc
LC
188static int wowlan_parse_net_detect(struct nl_msg *msg, int *argc, char ***argv)
189{
b1622287
LC
190 struct nlattr *nd;
191 int err = 0;
d516c5bc
LC
192
193 nd = nla_nest_start(msg, NL80211_WOWLAN_TRIG_NET_DETECT);
b1622287
LC
194 if (!nd)
195 return -ENOBUFS;
d516c5bc 196
b1622287 197 err = parse_sched_scan(msg, argc, argv);
d516c5bc 198
d516c5bc 199 nla_nest_end(msg, nd);
b1622287 200
d516c5bc
LC
201 return err;
202}
203
34b23014 204static int handle_wowlan_enable(struct nl80211_state *state,
05514f95
JB
205 struct nl_msg *msg, int argc, char **argv,
206 enum id_input id)
3ff24563
JB
207{
208 struct nlattr *wowlan, *pattern;
209 struct nl_msg *patterns = NULL;
210 enum {
211 PS_REG,
212 PS_PAT,
213 } parse_state = PS_REG;
214 int err = -ENOBUFS;
215 unsigned char *pat, *mask;
216 size_t patlen;
1c8f49c5
AK
217 int patnum = 0, pkt_offset;
218 char *eptr, *value1, *value2, *sptr = NULL;
3ff24563
JB
219
220 wowlan = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS);
221 if (!wowlan)
222 return -ENOBUFS;
223
224 while (argc) {
225 switch (parse_state) {
226 case PS_REG:
227 if (strcmp(argv[0], "any") == 0)
228 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_ANY);
229 else if (strcmp(argv[0], "disconnect") == 0)
230 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_DISCONNECT);
231 else if (strcmp(argv[0], "magic-packet") == 0)
232 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT);
3a6636b1
JB
233 else if (strcmp(argv[0], "gtk-rekey-failure") == 0)
234 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE);
235 else if (strcmp(argv[0], "eap-identity-request") == 0)
236 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST);
237 else if (strcmp(argv[0], "4way-handshake") == 0)
238 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE);
239 else if (strcmp(argv[0], "rfkill-release") == 0)
240 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE);
819b78cc
JB
241 else if (strcmp(argv[0], "tcp") == 0) {
242 argv++;
243 argc--;
244 if (!argc) {
245 err = 1;
246 goto nla_put_failure;
247 }
248 err = wowlan_parse_tcp_file(msg, argv[0]);
249 if (err)
250 goto nla_put_failure;
251 } else if (strcmp(argv[0], "patterns") == 0) {
3ff24563
JB
252 parse_state = PS_PAT;
253 patterns = nlmsg_alloc();
254 if (!patterns) {
255 err = -ENOMEM;
256 goto nla_put_failure;
257 }
d516c5bc
LC
258 } else if (strcmp(argv[0], "net-detect") == 0) {
259 argv++;
260 argc--;
261 if (!argc) {
262 err = 1;
263 goto nla_put_failure;
264 }
265 err = wowlan_parse_net_detect(msg, &argc, &argv);
266 if (err)
267 goto nla_put_failure;
268 continue;
3ff24563
JB
269 } else {
270 err = 1;
271 goto nla_put_failure;
272 }
273 break;
274 case PS_PAT:
1c8f49c5
AK
275 value1 = strtok_r(argv[0], "+", &sptr);
276 value2 = strtok_r(NULL, "+", &sptr);
277
278 if (!value2) {
279 pkt_offset = 0;
280 value2 = value1;
281 } else {
282 pkt_offset = strtoul(value1, &eptr, 10);
283 if (eptr != value1 + strlen(value1)) {
284 err = 1;
285 goto nla_put_failure;
286 }
287 }
288
289 if (parse_hex_mask(value2, &pat, &patlen, &mask)) {
3ff24563
JB
290 err = 1;
291 goto nla_put_failure;
292 }
1c8f49c5 293
3ff24563 294 pattern = nla_nest_start(patterns, ++patnum);
c82868da 295 NLA_PUT(patterns, NL80211_PKTPAT_MASK,
3ff24563 296 DIV_ROUND_UP(patlen, 8), mask);
c82868da
AK
297 NLA_PUT(patterns, NL80211_PKTPAT_PATTERN, patlen, pat);
298 NLA_PUT_U32(patterns, NL80211_PKTPAT_OFFSET,
299 pkt_offset);
3ff24563
JB
300 nla_nest_end(patterns, pattern);
301 free(mask);
302 free(pat);
303 break;
304 }
305 argv++;
306 argc--;
307 }
308
309 if (patterns)
310 nla_put_nested(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN,
311 patterns);
312
313 nla_nest_end(msg, wowlan);
314 err = 0;
315 nla_put_failure:
316 nlmsg_free(patterns);
317 return err;
318}
3a6636b1 319COMMAND(wowlan, enable, "[any] [disconnect] [magic-packet] [gtk-rekey-failure] [eap-identity-request]"
b1622287 320 " [4way-handshake] [rfkill-release] [net-detect " SCHED_SCAN_OPTIONS "]"
d516c5bc 321 " [tcp <config-file>] [patterns [offset1+]<pattern1> ...]",
3ff24563
JB
322 NL80211_CMD_SET_WOWLAN, 0, CIB_PHY, handle_wowlan_enable,
323 "Enable WoWLAN with the given triggers.\n"
324 "Each pattern is given as a bytestring with '-' in places where any byte\n"
325 "may be present, e.g. 00:11:22:-:44 will match 00:11:22:33:44 and\n"
1c8f49c5
AK
326 "00:11:22:33:ff:44 etc.\n"
327 "Offset and pattern should be separated by '+', e.g. 18+43:34:00:12 will match "
328 "'43:34:00:12' after 18 bytes of offset in Rx packet.\n\n"
819b78cc
JB
329 "The TCP configuration file contains:\n"
330 " source=ip[:port]\n"
331 " dest=ip:port@mac\n"
332 " data=<hex data packet>\n"
333 " data.interval=seconds\n"
334 " [wake=<hex packet with masked out bytes indicated by '-'>]\n"
335 " [data.seq=len,offset[,start]]\n"
d516c5bc
LC
336 " [data.tok=len,offset,<token stream>]\n\n"
337 "Net-detect configuration example:\n"
29af2019 338 " iw phy0 wowlan enable net-detect interval 5000 delay 30 freqs 2412 2422 matches ssid foo ssid bar");
3ff24563
JB
339
340
34b23014 341static int handle_wowlan_disable(struct nl80211_state *state,
05514f95
JB
342 struct nl_msg *msg, int argc, char **argv,
343 enum id_input id)
3ff24563
JB
344{
345 /* just a set w/o wowlan attribute */
346 return 0;
347}
348COMMAND(wowlan, disable, "", NL80211_CMD_SET_WOWLAN, 0, CIB_PHY, handle_wowlan_disable,
349 "Disable WoWLAN.");
350
351
352static int print_wowlan_handler(struct nl_msg *msg, void *arg)
353{
354 struct nlattr *attrs[NL80211_ATTR_MAX + 1];
355 struct nlattr *trig[NUM_NL80211_WOWLAN_TRIG];
356 struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
357 struct nlattr *pattern;
358 int rem_pattern;
359
360 nla_parse(attrs, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
361 genlmsg_attrlen(gnlh, 0), NULL);
362
363 if (!attrs[NL80211_ATTR_WOWLAN_TRIGGERS]) {
364 printf("WoWLAN is disabled.\n");
365 return NL_SKIP;
366 }
367
368 /* XXX: use policy */
369 nla_parse(trig, MAX_NL80211_WOWLAN_TRIG,
370 nla_data(attrs[NL80211_ATTR_WOWLAN_TRIGGERS]),
371 nla_len(attrs[NL80211_ATTR_WOWLAN_TRIGGERS]),
372 NULL);
373
374 printf("WoWLAN is enabled:\n");
375 if (trig[NL80211_WOWLAN_TRIG_ANY])
376 printf(" * wake up on special any trigger\n");
377 if (trig[NL80211_WOWLAN_TRIG_DISCONNECT])
378 printf(" * wake up on disconnect\n");
379 if (trig[NL80211_WOWLAN_TRIG_MAGIC_PKT])
380 printf(" * wake up on magic packet\n");
3a6636b1
JB
381 if (trig[NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE])
382 printf(" * wake up on GTK rekeying failure\n");
383 if (trig[NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST])
384 printf(" * wake up on EAP identity request\n");
385 if (trig[NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE])
386 printf(" * wake up on 4-way handshake\n");
387 if (trig[NL80211_WOWLAN_TRIG_RFKILL_RELEASE])
388 printf(" * wake up on RF-kill release\n");
d7d39283
LC
389 if (trig[NL80211_WOWLAN_TRIG_NET_DETECT]) {
390 struct nlattr *match, *freq,
391 *nd[NUM_NL80211_ATTR], *tb[NUM_NL80211_ATTR];
392 int rem_match;
393
d516c5bc 394 printf(" * wake up on network detection\n");
d7d39283
LC
395 nla_parse(nd, NUM_NL80211_ATTR,
396 nla_data(trig[NL80211_WOWLAN_TRIG_NET_DETECT]),
397 nla_len(trig[NL80211_WOWLAN_TRIG_NET_DETECT]), NULL);
398
399 if (nd[NL80211_ATTR_SCHED_SCAN_INTERVAL])
29af2019 400 printf("\tscan interval: %u msecs\n",
d7d39283
LC
401 nla_get_u32(nd[NL80211_ATTR_SCHED_SCAN_INTERVAL]));
402
29af2019 403 if (nd[NL80211_ATTR_SCHED_SCAN_DELAY])
f66320a6 404 printf("\tinitial scan delay: %u secs\n",
29af2019
LC
405 nla_get_u32(nd[NL80211_ATTR_SCHED_SCAN_DELAY]));
406
d7d39283
LC
407 if (nd[NL80211_ATTR_SCHED_SCAN_MATCH]) {
408 printf("\tmatches:\n");
409 nla_for_each_nested(match,
410 nd[NL80211_ATTR_SCHED_SCAN_MATCH],
411 rem_match) {
412 nla_parse(tb, NUM_NL80211_ATTR, nla_data(match),
413 nla_len(match),
414 NULL);
415 printf("\t\tSSID: ");
416 print_ssid_escaped(
417 nla_len(tb[NL80211_SCHED_SCAN_MATCH_ATTR_SSID]),
418 nla_data(tb[NL80211_SCHED_SCAN_MATCH_ATTR_SSID]));
419 printf("\n");
420 }
421 }
422 if (nd[NL80211_ATTR_SCAN_FREQUENCIES]) {
423 printf("\tfrequencies:");
424 nla_for_each_nested(freq,
425 nd[NL80211_ATTR_SCAN_FREQUENCIES],
426 rem_match) {
427 printf(" %d", nla_get_u32(freq));
428 }
429 printf("\n");
430 }
431 }
3ff24563
JB
432 if (trig[NL80211_WOWLAN_TRIG_PKT_PATTERN]) {
433 nla_for_each_nested(pattern,
434 trig[NL80211_WOWLAN_TRIG_PKT_PATTERN],
435 rem_pattern) {
c82868da 436 struct nlattr *patattr[NUM_NL80211_PKTPAT];
19195f35 437 int i, patlen, masklen;
3ff24563 438 uint8_t *mask, *pat;
c82868da
AK
439 nla_parse(patattr, MAX_NL80211_PKTPAT,
440 nla_data(pattern), nla_len(pattern), NULL);
441 if (!patattr[NL80211_PKTPAT_MASK] ||
19195f35 442 !patattr[NL80211_PKTPAT_PATTERN]) {
3ff24563
JB
443 printf(" * (invalid pattern specification)\n");
444 continue;
445 }
c82868da
AK
446 masklen = nla_len(patattr[NL80211_PKTPAT_MASK]);
447 patlen = nla_len(patattr[NL80211_PKTPAT_PATTERN]);
3ff24563
JB
448 if (DIV_ROUND_UP(patlen, 8) != masklen) {
449 printf(" * (invalid pattern specification)\n");
450 continue;
451 }
19195f35
JB
452 if (patattr[NL80211_PKTPAT_OFFSET]) {
453 int pkt_offset =
454 nla_get_u32(patattr[NL80211_PKTPAT_OFFSET]);
455 printf(" * wake up on packet offset: %d", pkt_offset);
456 }
1c8f49c5 457 printf(" pattern: ");
c82868da
AK
458 pat = nla_data(patattr[NL80211_PKTPAT_PATTERN]);
459 mask = nla_data(patattr[NL80211_PKTPAT_MASK]);
3ff24563
JB
460 for (i = 0; i < patlen; i++) {
461 if (mask[i / 8] & (1 << (i % 8)))
462 printf("%.2x", pat[i]);
463 else
464 printf("--");
465 if (i != patlen - 1)
466 printf(":");
467 }
468 printf("\n");
469 }
470 }
819b78cc
JB
471 if (trig[NL80211_WOWLAN_TRIG_TCP_CONNECTION])
472 printf(" * wake up on TCP connection\n");
3ff24563
JB
473
474 return NL_SKIP;
475}
476
34b23014 477static int handle_wowlan_show(struct nl80211_state *state,
05514f95
JB
478 struct nl_msg *msg, int argc, char **argv,
479 enum id_input id)
3ff24563 480{
34b23014 481 register_handler(print_wowlan_handler, NULL);
3ff24563
JB
482
483 return 0;
484}
485COMMAND(wowlan, show, "", NL80211_CMD_GET_WOWLAN, 0, CIB_PHY, handle_wowlan_show,
486 "Show WoWLAN status.");