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