]> git.ipfire.org Git - thirdparty/iw.git/commitdiff
support TCP wakeup API
authorJohannes Berg <johannes.berg@intel.com>
Tue, 19 Feb 2013 22:31:27 +0000 (23:31 +0100)
committerJohannes Berg <johannes.berg@intel.com>
Tue, 19 Feb 2013 22:31:27 +0000 (23:31 +0100)
Since the configuration is complex, read it from a file.

event.c
info.c
wowlan.c

diff --git a/event.c b/event.c
index 812483db097373fc91d420b82ad35a80495887f1..fbe3e0e36182db11aad9bca0cc66e928f8790ff5 100644 (file)
--- a/event.c
+++ b/event.c
@@ -199,6 +199,67 @@ static void parse_mic_failure(struct nlattr **attrs)
        printf("\n");
 }
 
+static void parse_wowlan_wake_event(struct nlattr **attrs)
+{
+       struct nlattr *tb[NUM_NL80211_WOWLAN_TRIG];
+
+       printf("WoWLAN wakeup\n");
+       if (!attrs[NL80211_ATTR_WOWLAN_TRIGGERS]) {
+               printf("\twakeup not due to WoWLAN\n");
+               return;
+       }
+
+       nla_parse(tb, MAX_NL80211_WOWLAN_TRIG,
+                 nla_data(attrs[NL80211_ATTR_WOWLAN_TRIGGERS]),
+                 nla_len(attrs[NL80211_ATTR_WOWLAN_TRIGGERS]), NULL);
+
+       if (tb[NL80211_WOWLAN_TRIG_DISCONNECT])
+               printf("\t* was disconnected\n");
+       if (tb[NL80211_WOWLAN_TRIG_MAGIC_PKT])
+               printf("\t* magic packet received\n");
+       if (tb[NL80211_WOWLAN_TRIG_PKT_PATTERN])
+               printf("\t* pattern index: %u\n",
+                      nla_get_u32(tb[NL80211_WOWLAN_TRIG_PKT_PATTERN]));
+       if (tb[NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE])
+               printf("\t* GTK rekey failure\n");
+       if (tb[NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST])
+               printf("\t* EAP identity request\n");
+       if (tb[NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE])
+               printf("\t* 4-way handshake\n");
+       if (tb[NL80211_WOWLAN_TRIG_RFKILL_RELEASE])
+               printf("\t* RF-kill released\n");
+       if (tb[NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211]) {
+               uint8_t *d = nla_data(tb[NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211]);
+               int l = nla_len(tb[NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211]);
+               int i;
+               printf("\t* packet (might be truncated): ");
+               for (i = 0; i < l; i++) {
+                       if (i > 0)
+                               printf(":");
+                       printf("%.2x", d[i]);
+               }
+               printf("\n");
+       }
+       if (tb[NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023]) {
+               uint8_t *d = nla_data(tb[NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023]);
+               int l = nla_len(tb[NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023]);
+               int i;
+               printf("\t* packet (might be truncated): ");
+               for (i = 0; i < l; i++) {
+                       if (i > 0)
+                               printf(":");
+                       printf("%.2x", d[i]);
+               }
+               printf("\n");
+       }
+       if (tb[NL80211_WOWLAN_TRIG_WAKEUP_TCP_MATCH])
+               printf("\t* TCP connection wakeup received\n");
+       if (tb[NL80211_WOWLAN_TRIG_WAKEUP_TCP_CONNLOST])
+               printf("\t* TCP connection lost\n");
+       if (tb[NL80211_WOWLAN_TRIG_WAKEUP_TCP_NOMORETOKENS])
+               printf("\t* TCP connection ran out of tokens\n");
+}
+
 static int print_event(struct nl_msg *msg, void *arg)
 {
        struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
@@ -453,6 +514,9 @@ static int print_event(struct nl_msg *msg, void *arg)
        case NL80211_CMD_PMKSA_CANDIDATE:
                printf("PMKSA candidate found\n");
                break;
+       case NL80211_CMD_SET_WOWLAN:
+               parse_wowlan_wake_event(tb);
+               break;
        default:
                printf("unknown event %d\n", gnlh->cmd);
                break;
diff --git a/info.c b/info.c
index 1b9e3b569a1a8d90258bfc395345eb348546b1d1..4cdbda70a5a628be2a7a45e7bc3b1a5adee6871d 100644 (file)
--- a/info.c
+++ b/info.c
@@ -408,6 +408,7 @@ broken_combination:
                        [NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST] = { .type = NLA_FLAG },
                        [NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE] = { .type = NLA_FLAG },
                        [NL80211_WOWLAN_TRIG_RFKILL_RELEASE] = { .type = NLA_FLAG },
+                       [NL80211_WOWLAN_TRIG_TCP_CONNECTION] = { .type = NLA_NESTED },
                };
                struct nl80211_wowlan_pattern_support *pat;
                int err;
@@ -441,6 +442,8 @@ broken_combination:
                                printf("\t\t * wake up on 4-way handshake\n");
                        if (tb_wowlan[NL80211_WOWLAN_TRIG_RFKILL_RELEASE])
                                printf("\t\t * wake up on rfkill release\n");
+                       if (tb_wowlan[NL80211_WOWLAN_TRIG_TCP_CONNECTION])
+                               printf("\t\t * wake up on TCP connection\n");
                }
        }
 
index 6d324efc51d94e607be0b69c58683e6738b52b04..0093a870241c86c84babb004f3812762954b8462 100644 (file)
--- a/wowlan.c
+++ b/wowlan.c
@@ -1,6 +1,7 @@
 #include <net/if.h>
 #include <errno.h>
 #include <string.h>
+#include <stdio.h>
 
 #include <netlink/genl/genl.h>
 #include <netlink/genl/family.h>
 #include <netlink/msg.h>
 #include <netlink/attr.h>
 
+#include <arpa/inet.h>
+
 #include "nl80211.h"
 #include "iw.h"
 
 SECTION(wowlan);
 
+static int wowlan_parse_tcp_file(struct nl_msg *msg, const char *fn)
+{
+       char buf[16768];
+       int err = 1;
+       FILE *f = fopen(fn, "r");
+       struct nlattr *tcp;
+
+       if (!f)
+               return 1;
+       tcp = nla_nest_start(msg, NL80211_WOWLAN_TRIG_TCP_CONNECTION);
+       if (!tcp)
+               goto nla_put_failure;
+
+       while (!feof(f)) {
+               char *eol;
+
+               if (!fgets(buf, sizeof(buf), f))
+                       break;
+
+               eol = strchr(buf + 5, '\r');
+               if (eol)
+                       *eol = 0;
+               eol = strchr(buf + 5, '\n');
+               if (eol)
+                       *eol = 0;
+
+               if (strncmp(buf, "source=", 7) == 0) {
+                       struct in_addr in_addr;
+                       char *addr = buf + 7;
+                       char *port = strchr(buf + 7, ':');
+
+                       if (port) {
+                               *port = 0;
+                               port++;
+                       }
+                       if (inet_aton(addr, &in_addr) == 0)
+                               goto close;
+                       NLA_PUT_U32(msg, NL80211_WOWLAN_TCP_SRC_IPV4,
+                                   in_addr.s_addr);
+                       if (port)
+                               NLA_PUT_U16(msg, NL80211_WOWLAN_TCP_SRC_PORT,
+                                           atoi(port));
+               } else if (strncmp(buf, "dest=", 5) == 0) {
+                       struct in_addr in_addr;
+                       char *addr = buf + 5;
+                       char *port = strchr(buf + 5, ':');
+                       char *mac;
+                       unsigned char macbuf[6];
+
+                       if (!port)
+                               goto close;
+                       *port = 0;
+                       port++;
+                       mac = strchr(port, '@');
+                       if (!mac)
+                               goto close;
+                       *mac = 0;
+                       mac++;
+                       if (inet_aton(addr, &in_addr) == 0)
+                               goto close;
+                       NLA_PUT_U32(msg, NL80211_WOWLAN_TCP_DST_IPV4,
+                                   in_addr.s_addr);
+                       NLA_PUT_U16(msg, NL80211_WOWLAN_TCP_DST_PORT,
+                                   atoi(port));
+                       if (mac_addr_a2n(macbuf, mac))
+                               goto close;
+                       NLA_PUT(msg, NL80211_WOWLAN_TCP_DST_MAC,
+                               6, macbuf);
+               } else if (strncmp(buf, "data=", 5) == 0) {
+                       size_t len;
+                       unsigned char *pkt = parse_hex(buf + 5, &len);
+
+                       if (!pkt)
+                               goto close;
+                       NLA_PUT(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD, len, pkt);
+                       free(pkt);
+               } else if (strncmp(buf, "data.interval=", 14) == 0) {
+                       NLA_PUT_U32(msg, NL80211_WOWLAN_TCP_DATA_INTERVAL,
+                                   atoi(buf));
+               } else if (strncmp(buf, "wake=", 5) == 0) {
+                       unsigned char *pat, *mask;
+                       size_t patlen;
+
+                       if (parse_hex_mask(buf + 5, &pat, &patlen, &mask))
+                               goto close;
+                       NLA_PUT(msg, NL80211_WOWLAN_TCP_WAKE_MASK,
+                               DIV_ROUND_UP(patlen, 8), mask);
+                       NLA_PUT(msg, NL80211_WOWLAN_TCP_WAKE_PAYLOAD,
+                               patlen, pat);
+                       free(mask);
+                       free(pat);
+               } else if (strncmp(buf, "data.seq=", 9) == 0) {
+                       struct nl80211_wowlan_tcp_data_seq seq = {};
+                       char *len, *offs, *start;
+
+                       len = buf + 9;
+                       offs = strchr(len, ',');
+                       if (!offs)
+                               goto close;
+                       *offs = 0;
+                       offs++;
+                       start = strchr(offs, ',');
+                       if (start) {
+                               *start = 0;
+                               start++;
+                               seq.start = atoi(start);
+                       }
+                       seq.len = atoi(len);
+                       seq.offset = atoi(offs);
+
+                       NLA_PUT(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ,
+                               sizeof(seq), &seq);
+               } else if (strncmp(buf, "data.tok=", 9) == 0) {
+                       struct nl80211_wowlan_tcp_data_token *tok;
+                       size_t stream_len;
+                       char *len, *offs, *toks;
+                       unsigned char *stream;
+
+                       len = buf + 9;
+                       offs = strchr(len, ',');
+                       if (!offs)
+                               goto close;
+                       *offs = 0;
+                       offs++;
+                       toks = strchr(offs, ',');
+                       if (!toks)
+                               goto close;
+                       *toks = 0;
+                       toks++;
+
+                       stream = parse_hex(toks, &stream_len);
+                       if (!stream)
+                               goto close;
+                       tok = malloc(sizeof(*tok) + stream_len);
+                       if (!tok) {
+                               free(stream);
+                               err = -ENOMEM;
+                               goto close;
+                       }
+
+                       tok->len = atoi(len);
+                       tok->offset = atoi(offs);
+                       memcpy(tok->token_stream, stream, stream_len);
+
+                       NLA_PUT(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN,
+                               sizeof(*tok) + stream_len, tok);
+                       free(stream);
+                       free(tok);
+               } else {
+                       if (buf[0] == '#')
+                               continue;
+                       goto close;
+               }
+       }
+
+       err = 0;
+       goto close;
+ nla_put_failure:
+       err = -ENOBUFS;
+ close:
+       fclose(f);
+       nla_nest_end(msg, tcp);
+       return err;
+}
+
 static int handle_wowlan_enable(struct nl80211_state *state, struct nl_cb *cb,
                                struct nl_msg *msg, int argc, char **argv,
                                enum id_input id)
@@ -49,7 +217,17 @@ static int handle_wowlan_enable(struct nl80211_state *state, struct nl_cb *cb,
                                NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE);
                        else if (strcmp(argv[0], "rfkill-release") == 0)
                                NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE);
-                       else if (strcmp(argv[0], "patterns") == 0) {
+                       else if (strcmp(argv[0], "tcp") == 0) {
+                               argv++;
+                               argc--;
+                               if (!argc) {
+                                       err = 1;
+                                       goto nla_put_failure;
+                               }
+                               err = wowlan_parse_tcp_file(msg, argv[0]);
+                               if (err)
+                                       goto nla_put_failure;
+                       } else if (strcmp(argv[0], "patterns") == 0) {
                                parse_state = PS_PAT;
                                patterns = nlmsg_alloc();
                                if (!patterns) {
@@ -91,12 +269,20 @@ static int handle_wowlan_enable(struct nl80211_state *state, struct nl_cb *cb,
        return err;
 }
 COMMAND(wowlan, enable, "[any] [disconnect] [magic-packet] [gtk-rekey-failure] [eap-identity-request]"
-       " [4way-handshake] [rfkill-release] [patterns <pattern>*]",
+       " [4way-handshake] [rfkill-release] [tcp <config-file>] [patterns <pattern>*]",
        NL80211_CMD_SET_WOWLAN, 0, CIB_PHY, handle_wowlan_enable,
        "Enable WoWLAN with the given triggers.\n"
        "Each pattern is given as a bytestring with '-' in places where any byte\n"
        "may be present, e.g. 00:11:22:-:44 will match 00:11:22:33:44 and\n"
-       "00:11:22:33:ff:44 etc.");
+       "00:11:22:33:ff:44 etc.\n\n"
+       "The TCP configuration file contains:\n"
+       "  source=ip[:port]\n"
+       "  dest=ip:port@mac\n"
+       "  data=<hex data packet>\n"
+       "  data.interval=seconds\n"
+       "  [wake=<hex packet with masked out bytes indicated by '-'>]\n"
+       "  [data.seq=len,offset[,start]]\n"
+       "  [data.tok=len,offset,<token stream>]");
 
 
 static int handle_wowlan_disable(struct nl80211_state *state, struct nl_cb *cb,
@@ -182,6 +368,8 @@ static int print_wowlan_handler(struct nl_msg *msg, void *arg)
                        printf("\n");
                }
        }
+       if (trig[NL80211_WOWLAN_TRIG_TCP_CONNECTION])
+               printf(" * wake up on TCP connection\n");
 
        return NL_SKIP;
 }