]> git.ipfire.org Git - thirdparty/iw.git/blobdiff - scan.c
iw: implement scheduled scan
[thirdparty/iw.git] / scan.c
diff --git a/scan.c b/scan.c
index 90df93b5d3154429eb6758ddc8c2f2403149e7f1..e534fd48b4c6ed50b5c934a5256284ca2bfcf5f2 100644 (file)
--- a/scan.c
+++ b/scan.c
@@ -70,6 +70,205 @@ union ieee80211_country_ie_triplet {
        } __attribute__ ((packed)) ext;
 } __attribute__ ((packed));
 
+static int parse_random_mac_addr(struct nl_msg *msg, char *arg)
+{
+       char *a_addr, *a_mask, *sep;
+       unsigned char addr[ETH_ALEN], mask[ETH_ALEN];
+       char *addrs = arg + 9;
+
+       if (*addrs != '=')
+               return 0;
+
+       addrs++;
+       sep = strchr(addrs, '/');
+       a_addr = addrs;
+
+       if (!sep)
+               return 1;
+
+       *sep = 0;
+       a_mask = sep + 1;
+       if (mac_addr_a2n(addr, a_addr) || mac_addr_a2n(mask, a_mask))
+               return 1;
+
+       NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
+       NLA_PUT(msg, NL80211_ATTR_MAC_MASK, ETH_ALEN, mask);
+
+       return 0;
+ nla_put_failure:
+       return -ENOBUFS;
+}
+
+int parse_sched_scan(struct nl_msg *msg, int *argc, char ***argv)
+{
+       struct nl_msg *matchset = NULL, *freqs = NULL;
+       struct nlattr *match = NULL;
+       enum {
+               ND_TOPLEVEL,
+               ND_MATCH,
+               ND_FREQS,
+       } parse_state = ND_TOPLEVEL;
+       int c  = *argc;
+       char *end, **v = *argv;
+       int err = 0, i = 0;
+       unsigned int freq, interval = 0, delay = 0;
+       bool have_matchset = false, have_freqs = false;
+
+       matchset = nlmsg_alloc();
+       if (!matchset) {
+               err = -ENOBUFS;
+               goto out;
+       }
+
+       freqs = nlmsg_alloc();
+       if (!freqs) {
+               err = -ENOBUFS;
+               goto out;
+       }
+
+       while (c) {
+               switch (parse_state) {
+               case ND_TOPLEVEL:
+                       if (!strcmp(v[0], "interval")) {
+                               c--; v++;
+                               if (c == 0) {
+                                       err = -EINVAL;
+                                       goto nla_put_failure;
+                               }
+
+                               if (interval) {
+                                       err = -EINVAL;
+                                       goto nla_put_failure;
+                               }
+                               interval = strtoul(v[0], &end, 10);
+                               if (*end || !interval) {
+                                       err = -EINVAL;
+                                       goto nla_put_failure;
+                               }
+                               NLA_PUT_U32(msg,
+                                           NL80211_ATTR_SCHED_SCAN_INTERVAL,
+                                           interval);
+                       } else if (!strcmp(v[0], "delay")) {
+                               c--; v++;
+                               if (c == 0) {
+                                       err = -EINVAL;
+                                       goto nla_put_failure;
+                               }
+
+                               if (delay) {
+                                       err = -EINVAL;
+                                       goto nla_put_failure;
+                               }
+                               delay = strtoul(v[0], &end, 10);
+                               if (*end) {
+                                       err = -EINVAL;
+                                       goto nla_put_failure;
+                               }
+                               NLA_PUT_U32(msg,
+                                           NL80211_ATTR_SCHED_SCAN_DELAY,
+                                           delay);
+                       } else if (!strcmp(v[0], "matches")) {
+                               parse_state = ND_MATCH;
+                               if (have_matchset) {
+                                       err = -EINVAL;
+                                       goto nla_put_failure;
+                               }
+
+                               i = 0;
+                       } else if (!strcmp(v[0], "freqs")) {
+                               parse_state = ND_FREQS;
+                               if (have_freqs) {
+                                       err = -EINVAL;
+                                       goto nla_put_failure;
+                               }
+
+                               have_freqs = true;
+                               i = 0;
+                       } else {
+                               /* this element is not for us, so
+                                * return to continue parsing.
+                                */
+                               goto nla_put_failure;
+                       }
+                       c--; v++;
+
+                       break;
+               case ND_MATCH:
+                       if (!strcmp(v[0], "ssid")) {
+                               c--; v++;
+                               if (c == 0) {
+                                       err = -EINVAL;
+                                       goto nla_put_failure;
+                               }
+
+                               /* TODO: for now we can only have an
+                                * SSID in the match, so we can start
+                                * the match nest here.
+                                */
+                               match = nla_nest_start(matchset, i);
+                               if (!match) {
+                                       err = -ENOBUFS;
+                                       goto nla_put_failure;
+                               }
+
+                               NLA_PUT(matchset,
+                                       NL80211_SCHED_SCAN_MATCH_ATTR_SSID,
+                                       strlen(v[0]), v[0]);
+                               nla_nest_end(matchset, match);
+                               match = NULL;
+
+                               have_matchset = true;
+                               i++;
+                               c--; v++;
+                       } else {
+                               /* other element that cannot be part
+                                * of a match indicates the end of the
+                                * match. */
+                               /* need at least one match in the matchset */
+                               if (i == 0) {
+                                       err = -EINVAL;
+                                       goto nla_put_failure;
+                               }
+
+                               parse_state = ND_TOPLEVEL;
+                       }
+
+                       break;
+               case ND_FREQS:
+                       freq = strtoul(v[0], &end, 10);
+                       if (*end) {
+                               if (i == 0) {
+                                       err = -EINVAL;
+                                       goto nla_put_failure;
+                               }
+
+                               parse_state = ND_TOPLEVEL;
+                       } else {
+                               NLA_PUT_U32(freqs, i, freq);
+                               i++;
+                               c--; v++;
+                       }
+                       break;
+               }
+       }
+
+       if (have_freqs)
+               nla_put_nested(msg, NL80211_ATTR_SCAN_FREQUENCIES, freqs);
+       if (have_matchset)
+               nla_put_nested(msg, NL80211_ATTR_SCHED_SCAN_MATCH, matchset);
+
+nla_put_failure:
+       if (match)
+               nla_nest_end(msg, match);
+       nlmsg_free(freqs);
+       nlmsg_free(matchset);
+
+out:
+       *argc = c;
+       *argv = v;
+       return err;
+}
+
 static int handle_scan(struct nl80211_state *state,
                       struct nl_cb *cb,
                       struct nl_msg *msg,
@@ -92,7 +291,7 @@ static int handle_scan(struct nl80211_state *state,
        bool passive = false, have_ssids = false, have_freqs = false;
        size_t ies_len = 0, meshid_len = 0;
        unsigned char *ies = NULL, *meshid = NULL, *tmpies;
-       int flags = 0;
+       unsigned int flags = 0;
 
        ssids = nlmsg_alloc();
        if (!ssids)
@@ -115,17 +314,21 @@ static int handle_scan(struct nl80211_state *state,
                                parse = IES;
                                break;
                        } else if (strcmp(argv[i], "lowpri") == 0) {
-                               parse = NONE;
                                flags |= NL80211_SCAN_FLAG_LOW_PRIORITY;
                                break;
                        } else if (strcmp(argv[i], "flush") == 0) {
-                               parse = NONE;
                                flags |= NL80211_SCAN_FLAG_FLUSH;
                                break;
                        } else if (strcmp(argv[i], "ap-force") == 0) {
-                               parse = NONE;
                                flags |= NL80211_SCAN_FLAG_AP;
                                break;
+                       } else if (strncmp(argv[i], "randomise", 9) == 0 ||
+                                  strncmp(argv[i], "randomize", 9) == 0) {
+                               flags |= NL80211_SCAN_FLAG_RANDOM_ADDR;
+                               err = parse_random_mac_addr(msg, argv[i]);
+                               if (err)
+                                       goto nla_put_failure;
+                               break;
                        } else if (strcmp(argv[i], "ssid") == 0) {
                                parse = SSID;
                                have_ssids = true;
@@ -1763,7 +1966,7 @@ static int handle_scan_combined(struct nl80211_state *state,
        dump_argv[0] = argv[0];
        return handle_cmd(state, id, dump_argc, dump_argv);
 }
-TOPLEVEL(scan, "[-u] [freq <freq>*] [ies <hex as 00:11:..>] [meshid <meshid>] [lowpri,flush,ap-force] [ssid <ssid>*|passive]", 0, 0,
+TOPLEVEL(scan, "[-u] [freq <freq>*] [ies <hex as 00:11:..>] [meshid <meshid>] [lowpri,flush,ap-force] [randomise[=<addr>/<mask>]] [ssid <ssid>*|passive]", 0, 0,
         CIB_NETDEV, handle_scan_combined,
         "Scan on the given frequencies and probe for the given SSIDs\n"
         "(or wildcard if not given) unless passive scanning is requested.\n"
@@ -1773,7 +1976,36 @@ COMMAND(scan, dump, "[-u]",
        NL80211_CMD_GET_SCAN, NLM_F_DUMP, CIB_NETDEV, handle_scan_dump,
        "Dump the current scan results. If -u is specified, print unknown\n"
        "data in scan results.");
-COMMAND(scan, trigger, "[freq <freq>*] [ies <hex as 00:11:..>] [meshid <meshid>] [lowpri,flush,ap-force] [ssid <ssid>*|passive]",
+COMMAND(scan, trigger, "[freq <freq>*] [ies <hex as 00:11:..>] [meshid <meshid>] [lowpri,flush,ap-force] [randomise[=<addr>/<mask>]] [ssid <ssid>*|passive]",
        NL80211_CMD_TRIGGER_SCAN, 0, CIB_NETDEV, handle_scan,
         "Trigger a scan on the given frequencies with probing for the given\n"
         "SSIDs (or wildcard if not given) unless passive scanning is requested.");
+
+
+static int handle_start_sched_scan(struct nl80211_state *state,
+                                  struct nl_cb *cb, struct nl_msg *msg,
+                                  int argc, char **argv, enum id_input id)
+{
+       return parse_sched_scan(msg, &argc, &argv);
+}
+
+static int handle_stop_sched_scan(struct nl80211_state *state, struct nl_cb *cb,
+                                 struct nl_msg *msg, int argc, char **argv,
+                                 enum id_input id)
+{
+       if (argc != 0)
+               return 1;
+
+       return 0;
+}
+
+COMMAND(scan, sched_start,
+       SCHED_SCAN_OPTIONS,
+       NL80211_CMD_START_SCHED_SCAN, 0, CIB_NETDEV, handle_start_sched_scan,
+       "Start a scheduled scan at the specified interval on the given frequencies\n"
+       "with probing for the given SSIDs (or wildcard if not given) unless passive\n"
+       "scanning is requested.  If matches are specified, only matching results\n"
+       "will be returned.");
+COMMAND(scan, sched_stop, "",
+       NL80211_CMD_STOP_SCHED_SCAN, 0, CIB_NETDEV, handle_stop_sched_scan,
+       "Stop an ongoing scheduled scan.");