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>
12 #include <arpa/inet.h>
19 static int wowlan_parse_tcp_file(struct nl_msg
*msg
, const char *fn
)
23 FILE *f
= fopen(fn
, "r");
28 tcp
= nla_nest_start(msg
, NL80211_WOWLAN_TRIG_TCP_CONNECTION
);
35 if (!fgets(buf
, sizeof(buf
), f
))
38 eol
= strchr(buf
+ 5, '\r');
41 eol
= strchr(buf
+ 5, '\n');
45 if (strncmp(buf
, "source=", 7) == 0) {
46 struct in_addr in_addr
;
48 char *port
= strchr(buf
+ 7, ':');
54 if (inet_aton(addr
, &in_addr
) == 0)
56 NLA_PUT_U32(msg
, NL80211_WOWLAN_TCP_SRC_IPV4
,
59 NLA_PUT_U16(msg
, NL80211_WOWLAN_TCP_SRC_PORT
,
61 } else if (strncmp(buf
, "dest=", 5) == 0) {
62 struct in_addr in_addr
;
64 char *port
= strchr(buf
+ 5, ':');
66 unsigned char macbuf
[6];
72 mac
= strchr(port
, '@');
77 if (inet_aton(addr
, &in_addr
) == 0)
79 NLA_PUT_U32(msg
, NL80211_WOWLAN_TCP_DST_IPV4
,
81 NLA_PUT_U16(msg
, NL80211_WOWLAN_TCP_DST_PORT
,
83 if (mac_addr_a2n(macbuf
, mac
))
85 NLA_PUT(msg
, NL80211_WOWLAN_TCP_DST_MAC
,
87 } else if (strncmp(buf
, "data=", 5) == 0) {
89 unsigned char *pkt
= parse_hex(buf
+ 5, &len
);
93 NLA_PUT(msg
, NL80211_WOWLAN_TCP_DATA_PAYLOAD
, len
, pkt
);
95 } else if (strncmp(buf
, "data.interval=", 14) == 0) {
96 NLA_PUT_U32(msg
, NL80211_WOWLAN_TCP_DATA_INTERVAL
,
98 } else if (strncmp(buf
, "wake=", 5) == 0) {
99 unsigned char *pat
, *mask
;
102 if (parse_hex_mask(buf
+ 5, &pat
, &patlen
, &mask
))
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
,
110 } else if (strncmp(buf
, "data.seq=", 9) == 0) {
111 struct nl80211_wowlan_tcp_data_seq seq
= {};
112 char *len
, *offs
, *start
;
115 offs
= strchr(len
, ',');
120 start
= strchr(offs
, ',');
124 seq
.start
= atoi(start
);
127 seq
.offset
= atoi(offs
);
129 NLA_PUT(msg
, NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ
,
131 } else if (strncmp(buf
, "data.tok=", 9) == 0) {
132 struct nl80211_wowlan_tcp_data_token
*tok
;
134 char *len
, *offs
, *toks
;
135 unsigned char *stream
;
138 offs
= strchr(len
, ',');
143 toks
= strchr(offs
, ',');
149 stream
= parse_hex(toks
, &stream_len
);
152 tok
= malloc(sizeof(*tok
) + stream_len
);
159 tok
->len
= atoi(len
);
160 tok
->offset
= atoi(offs
);
161 memcpy(tok
->token_stream
, stream
, stream_len
);
163 NLA_PUT(msg
, NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN
,
164 sizeof(*tok
) + stream_len
, tok
);
180 nla_nest_end(msg
, tcp
);
184 static int wowlan_parse_net_detect(struct nl_msg
*msg
, int *argc
, char ***argv
)
186 struct nl_msg
*matchset
= NULL
, *freqs
= NULL
;
187 struct nlattr
*nd
, *match
= NULL
;
192 } parse_state
= ND_TOPLEVEL
;
194 char *end
, **v
= *argv
;
196 unsigned int freq
, interval
= 0;
197 bool have_matchset
= false, have_freqs
= false;
199 nd
= nla_nest_start(msg
, NL80211_WOWLAN_TRIG_NET_DETECT
);
205 matchset
= nlmsg_alloc();
211 freqs
= nlmsg_alloc();
218 switch (parse_state
) {
220 if (!strcmp(v
[0], "interval")) {
224 goto nla_put_failure
;
229 goto nla_put_failure
;
231 interval
= strtoul(v
[0], &end
, 10);
232 if (*end
|| !interval
) {
234 goto nla_put_failure
;
237 NL80211_ATTR_SCHED_SCAN_INTERVAL
,
239 } else if (!strcmp(v
[0], "matches")) {
240 parse_state
= ND_MATCH
;
243 goto nla_put_failure
;
247 } else if (!strcmp(v
[0], "freqs")) {
248 parse_state
= ND_FREQS
;
251 goto nla_put_failure
;
257 /* this element is not for us, so
258 * return to continue parsing.
260 goto nla_put_failure
;
266 if (!strcmp(v
[0], "ssid")) {
270 goto nla_put_failure
;
273 /* TODO: for now we can only have an
274 * SSID in the match, so we can start
275 * the match nest here.
277 match
= nla_nest_start(matchset
, i
);
280 goto nla_put_failure
;
284 NL80211_SCHED_SCAN_MATCH_ATTR_SSID
,
286 nla_nest_end(matchset
, match
);
289 have_matchset
= true;
293 /* other element that cannot be part
294 * of a match indicates the end of the
296 /* need at least one match in the matchset */
299 goto nla_put_failure
;
302 parse_state
= ND_TOPLEVEL
;
307 freq
= strtoul(v
[0], &end
, 10);
311 goto nla_put_failure
;
314 parse_state
= ND_TOPLEVEL
;
316 NLA_PUT_U32(freqs
, i
, freq
);
325 nla_put_nested(msg
, NL80211_ATTR_SCAN_FREQUENCIES
, freqs
);
327 nla_put_nested(msg
, NL80211_ATTR_SCHED_SCAN_MATCH
, matchset
);
331 nla_nest_end(msg
, match
);
333 nlmsg_free(matchset
);
334 nla_nest_end(msg
, nd
);
341 static int handle_wowlan_enable(struct nl80211_state
*state
, struct nl_cb
*cb
,
342 struct nl_msg
*msg
, int argc
, char **argv
,
345 struct nlattr
*wowlan
, *pattern
;
346 struct nl_msg
*patterns
= NULL
;
350 } parse_state
= PS_REG
;
352 unsigned char *pat
, *mask
;
354 int patnum
= 0, pkt_offset
;
355 char *eptr
, *value1
, *value2
, *sptr
= NULL
;
357 wowlan
= nla_nest_start(msg
, NL80211_ATTR_WOWLAN_TRIGGERS
);
362 switch (parse_state
) {
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
);
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
);
378 else if (strcmp(argv
[0], "tcp") == 0) {
383 goto nla_put_failure
;
385 err
= wowlan_parse_tcp_file(msg
, argv
[0]);
387 goto nla_put_failure
;
388 } else if (strcmp(argv
[0], "patterns") == 0) {
389 parse_state
= PS_PAT
;
390 patterns
= nlmsg_alloc();
393 goto nla_put_failure
;
395 } else if (strcmp(argv
[0], "net-detect") == 0) {
400 goto nla_put_failure
;
402 err
= wowlan_parse_net_detect(msg
, &argc
, &argv
);
404 goto nla_put_failure
;
408 goto nla_put_failure
;
412 value1
= strtok_r(argv
[0], "+", &sptr
);
413 value2
= strtok_r(NULL
, "+", &sptr
);
419 pkt_offset
= strtoul(value1
, &eptr
, 10);
420 if (eptr
!= value1
+ strlen(value1
)) {
422 goto nla_put_failure
;
426 if (parse_hex_mask(value2
, &pat
, &patlen
, &mask
)) {
428 goto nla_put_failure
;
431 pattern
= nla_nest_start(patterns
, ++patnum
);
432 NLA_PUT(patterns
, NL80211_PKTPAT_MASK
,
433 DIV_ROUND_UP(patlen
, 8), mask
);
434 NLA_PUT(patterns
, NL80211_PKTPAT_PATTERN
, patlen
, pat
);
435 NLA_PUT_U32(patterns
, NL80211_PKTPAT_OFFSET
,
437 nla_nest_end(patterns
, pattern
);
447 nla_put_nested(msg
, NL80211_WOWLAN_TRIG_PKT_PATTERN
,
450 nla_nest_end(msg
, wowlan
);
453 nlmsg_free(patterns
);
456 COMMAND(wowlan
, enable
, "[any] [disconnect] [magic-packet] [gtk-rekey-failure] [eap-identity-request]"
457 " [4way-handshake] [rfkill-release] [net-detect interval <in_msecs> [freqs <freq>+] [matches [ssid <ssid>]+]]"
458 " [tcp <config-file>] [patterns [offset1+]<pattern1> ...]",
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"
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"
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"
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");
478 static int handle_wowlan_disable(struct nl80211_state
*state
, struct nl_cb
*cb
,
479 struct nl_msg
*msg
, int argc
, char **argv
,
482 /* just a set w/o wowlan attribute */
485 COMMAND(wowlan
, disable
, "", NL80211_CMD_SET_WOWLAN
, 0, CIB_PHY
, handle_wowlan_disable
,
489 static int print_wowlan_handler(struct nl_msg
*msg
, void *arg
)
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
;
497 nla_parse(attrs
, NL80211_ATTR_MAX
, genlmsg_attrdata(gnlh
, 0),
498 genlmsg_attrlen(gnlh
, 0), NULL
);
500 if (!attrs
[NL80211_ATTR_WOWLAN_TRIGGERS
]) {
501 printf("WoWLAN is disabled.\n");
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
]),
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");
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");
526 if (trig
[NL80211_WOWLAN_TRIG_NET_DETECT
]) {
527 struct nlattr
*match
, *freq
,
528 *nd
[NUM_NL80211_ATTR
], *tb
[NUM_NL80211_ATTR
];
531 printf(" * wake up on network detection\n");
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
);
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
]));
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
],
545 nla_parse(tb
, NUM_NL80211_ATTR
, nla_data(match
),
548 printf("\t\tSSID: ");
550 nla_len(tb
[NL80211_SCHED_SCAN_MATCH_ATTR_SSID
]),
551 nla_data(tb
[NL80211_SCHED_SCAN_MATCH_ATTR_SSID
]));
555 if (nd
[NL80211_ATTR_SCAN_FREQUENCIES
]) {
556 printf("\tfrequencies:");
557 nla_for_each_nested(freq
,
558 nd
[NL80211_ATTR_SCAN_FREQUENCIES
],
560 printf(" %d", nla_get_u32(freq
));
565 if (trig
[NL80211_WOWLAN_TRIG_PKT_PATTERN
]) {
566 nla_for_each_nested(pattern
,
567 trig
[NL80211_WOWLAN_TRIG_PKT_PATTERN
],
569 struct nlattr
*patattr
[NUM_NL80211_PKTPAT
];
570 int i
, patlen
, masklen
;
572 nla_parse(patattr
, MAX_NL80211_PKTPAT
,
573 nla_data(pattern
), nla_len(pattern
), NULL
);
574 if (!patattr
[NL80211_PKTPAT_MASK
] ||
575 !patattr
[NL80211_PKTPAT_PATTERN
]) {
576 printf(" * (invalid pattern specification)\n");
579 masklen
= nla_len(patattr
[NL80211_PKTPAT_MASK
]);
580 patlen
= nla_len(patattr
[NL80211_PKTPAT_PATTERN
]);
581 if (DIV_ROUND_UP(patlen
, 8) != masklen
) {
582 printf(" * (invalid pattern specification)\n");
585 if (patattr
[NL80211_PKTPAT_OFFSET
]) {
587 nla_get_u32(patattr
[NL80211_PKTPAT_OFFSET
]);
588 printf(" * wake up on packet offset: %d", pkt_offset
);
590 printf(" pattern: ");
591 pat
= nla_data(patattr
[NL80211_PKTPAT_PATTERN
]);
592 mask
= nla_data(patattr
[NL80211_PKTPAT_MASK
]);
593 for (i
= 0; i
< patlen
; i
++) {
594 if (mask
[i
/ 8] & (1 << (i
% 8)))
595 printf("%.2x", pat
[i
]);
604 if (trig
[NL80211_WOWLAN_TRIG_TCP_CONNECTION
])
605 printf(" * wake up on TCP connection\n");
610 static int handle_wowlan_show(struct nl80211_state
*state
, struct nl_cb
*cb
,
611 struct nl_msg
*msg
, int argc
, char **argv
,
614 nl_cb_set(cb
, NL_CB_VALID
, NL_CB_CUSTOM
,
615 print_wowlan_handler
, NULL
);
619 COMMAND(wowlan
, show
, "", NL80211_CMD_GET_WOWLAN
, 0, CIB_PHY
, handle_wowlan_show
,
620 "Show WoWLAN status.");