iwinfo: add device id for Mikrotik R11e-5HacD miniPCIe card
[oweals/iwinfo.git] / iwinfo_nl80211.c
index 9855d88e4023970e7dce69a98de025eaee54e2cf..2b2a04386ee5faf8a38f8601d991795f503597c5 100644 (file)
@@ -26,6 +26,7 @@
 #include <glob.h>
 #include <fnmatch.h>
 #include <stdarg.h>
+#include <stdlib.h>
 
 #include "iwinfo_nl80211.h"
 
@@ -234,26 +235,49 @@ static struct nl80211_msg_conveyor * nl80211_ctl(int cmd, int flags)
 
 static int nl80211_phy_idx_from_uci_path(struct uci_section *s)
 {
-       const char *opt;
-       char buf[128];
+       size_t linklen, pathlen;
+       char buf[128], *link;
+       struct dirent *e;
+       const char *path;
        int idx = -1;
-       glob_t gl;
+       DIR *d;
 
-       opt = uci_lookup_option_string(uci_ctx, s, "path");
-       if (!opt)
+       path = uci_lookup_option_string(uci_ctx, s, "path");
+       if (!path)
                return -1;
 
-       snprintf(buf, sizeof(buf), "/sys/devices/%s/ieee80211/*/index", opt);  /**/
-       if (glob(buf, 0, NULL, &gl))
-               snprintf(buf, sizeof(buf), "/sys/devices/platform/%s/ieee80211/*/index", opt);  /**/
+       if ((d = opendir("/sys/class/ieee80211")) != NULL)
+       {
+               while ((e = readdir(d)) != NULL)
+               {
+                       snprintf(buf, sizeof(buf), "/sys/class/ieee80211/%s/device", e->d_name);
 
-       if (glob(buf, 0, NULL, &gl))
-               return -1;
+                       link = realpath(buf, NULL);
 
-       if (gl.gl_pathc > 0)
-               idx = nl80211_readint(gl.gl_pathv[0]);
+                       if (link == NULL)
+                               continue;
 
-       globfree(&gl);
+                       linklen = strlen(link);
+                       pathlen = strlen(path);
+
+                       if (pathlen >= linklen || strcmp(link + (linklen - pathlen), path))
+                               linklen = 0;
+
+                       free(link);
+
+                       if (linklen == 0)
+                               continue;
+
+                       snprintf(buf, sizeof(buf), "/sys/class/ieee80211/%s/index", e->d_name);
+
+                       idx = nl80211_readint(buf);
+
+                       if (idx >= 0)
+                               break;
+               }
+
+               closedir(d);
+       }
 
        return idx;
 }
@@ -343,7 +367,8 @@ static struct nl80211_msg_conveyor * nl80211_msg(const char *ifname,
                phyidx = atoi(&ifname[3]);
        else if (!strncmp(ifname, "radio", 5))
                phyidx = nl80211_phy_idx_from_uci(ifname);
-       else if (!strncmp(ifname, "mon.", 4))
+
+       if (!strncmp(ifname, "mon.", 4))
                ifidx = if_nametoindex(&ifname[4]);
        else
                ifidx = if_nametoindex(ifname);
@@ -356,10 +381,9 @@ static struct nl80211_msg_conveyor * nl80211_msg(const char *ifname,
        if (!cv)
                return NULL;
 
-       if (ifidx > -1)
+       if (ifidx > 0)
                NLA_PUT_U32(cv->msg, NL80211_ATTR_IFINDEX, ifidx);
-
-       if (phyidx > -1)
+       else if (phyidx > -1)
                NLA_PUT_U32(cv->msg, NL80211_ATTR_WIPHY, phyidx);
 
        return cv;
@@ -870,7 +894,9 @@ static int __nl80211_wpactl_query(const char *ifname, ...)
        if (nl80211_get_mode(ifname, &mode))
                return 0;
 
-       if (mode != IWINFO_OPMODE_CLIENT && mode != IWINFO_OPMODE_ADHOC)
+       if (mode != IWINFO_OPMODE_CLIENT &&
+           mode != IWINFO_OPMODE_ADHOC &&
+           mode != IWINFO_OPMODE_MESHPOINT)
                return 0;
 
        sock = nl80211_wpactl_connect(ifname, &local);
@@ -1593,9 +1619,11 @@ static void parse_wpa_ciphers(const char *str, uint8_t *ciphers)
 static int nl80211_get_encryption(const char *ifname, char *buf)
 {
        char *p;
+       int opmode;
        uint8_t wpa_version = 0;
        char wpa[2], wpa_key_mgmt[64], wpa_pairwise[16], wpa_groupwise[16];
        char auth_algs[2], wep_key0[27], wep_key1[27], wep_key2[27], wep_key3[27];
+       char mode[16];
 
        struct iwinfo_crypto_entry *c = (struct iwinfo_crypto_entry *)buf;
 
@@ -1603,7 +1631,8 @@ static int nl80211_get_encryption(const char *ifname, char *buf)
        if (nl80211_wpactl_query(ifname,
                        "pairwise_cipher", wpa_pairwise,  sizeof(wpa_pairwise),
                        "group_cipher",    wpa_groupwise, sizeof(wpa_groupwise),
-                       "key_mgmt",        wpa_key_mgmt,  sizeof(wpa_key_mgmt)))
+                       "key_mgmt",        wpa_key_mgmt,  sizeof(wpa_key_mgmt),
+                       "mode",            mode,          sizeof(mode)))
        {
                /* WEP or Open */
                if (!strcmp(wpa_key_mgmt, "NONE"))
@@ -1622,6 +1651,16 @@ static int nl80211_get_encryption(const char *ifname, char *buf)
                        }
                }
 
+               /* MESH with SAE */
+               else if (!strcmp(mode, "mesh") && !strcmp(wpa_key_mgmt, "UNKNOWN"))
+               {
+                       c->enabled = 1;
+                       c->wpa_version = 4;
+                       c->auth_suites = IWINFO_KMGMT_SAE;
+                       c->pair_ciphers = IWINFO_CIPHER_CCMP;
+                       c->group_ciphers = IWINFO_CIPHER_CCMP;
+               }
+
                /* WPA */
                else
                {
@@ -1709,6 +1748,17 @@ static int nl80211_get_encryption(const char *ifname, char *buf)
                return 0;
        }
 
+       /* Ad-Hoc or Mesh interfaces without wpa_supplicant are open */
+       else if (!nl80211_get_mode(ifname, &opmode) &&
+                (opmode == IWINFO_OPMODE_ADHOC ||
+                 opmode == IWINFO_OPMODE_MESHPOINT))
+       {
+               c->enabled = 0;
+
+               return 0;
+       }
+
+
        return -1;
 }
 
@@ -2185,11 +2235,11 @@ static int nl80211_get_txpwrlist(const char *ifname, char *buf, int *len)
 static void nl80211_get_scancrypto(char *spec, struct iwinfo_crypto_entry *c)
 {
        int wpa_version = 0;
-       char *p, *proto, *suites;
+       char *p, *q, *proto, *suites;
 
        c->enabled = 0;
 
-       for (p = strtok(spec, "[]"); p != NULL; p = strtok(NULL, "[]")) {
+       for (p = strtok_r(spec, "[]", &q); p; p = strtok_r(NULL, "[]", &q)) {
                if (!strcmp(p, "WEP")) {
                        c->enabled      = 1;
                        c->auth_suites  = IWINFO_KMGMT_NONE;
@@ -2471,8 +2521,14 @@ static int nl80211_get_scanlist_wpactl(const char *ifname, char *buf, int *len)
                        tries--;
                }
 
-               /* got a failure reply */
+               /* scanning already in progress, keep awaiting results */
                else if (!strcmp(reply, "FAIL-BUSY\n"))
+               {
+                       tries--;
+               }
+
+               /* another failure, abort */
+               else if (!strncmp(reply, "FAIL-", 5))
                {
                        break;
                }
@@ -2504,7 +2560,7 @@ static int nl80211_get_scanlist_wpactl(const char *ifname, char *buf, int *len)
                        flags  = strtok(NULL, "\t");
                        ssid   = strtok(NULL, "\n");
 
-                       if (!bssid || !freq || !signal || !flags || !ssid)
+                       if (!bssid || !freq || !signal || !flags)
                                continue;
 
                        /* BSSID */
@@ -2516,7 +2572,10 @@ static int nl80211_get_scanlist_wpactl(const char *ifname, char *buf, int *len)
                        e->mac[5] = strtol(&bssid[15], NULL, 16);
 
                        /* SSID */
-                       wpasupp_ssid_decode(ssid, e->ssid, sizeof(e->ssid));
+                       if (ssid)
+                               wpasupp_ssid_decode(ssid, e->ssid, sizeof(e->ssid));
+                       else
+                               e->ssid[0] = 0;
 
                        /* Mode */
                        if (strstr(flags, "[MESH]"))
@@ -2899,6 +2958,75 @@ out:
        return -1;
 }
 
+struct chan_info {
+       int width;
+       int mode;
+};
+
+static int nl80211_get_htmode_cb(struct nl_msg *msg, void *arg)
+{
+       struct nlattr **tb = nl80211_parse(msg);
+       struct nlattr *cur;
+       struct chan_info *chn = arg;
+
+       if ((cur = tb[NL80211_ATTR_CHANNEL_WIDTH]))
+               chn->width = nla_get_u32(cur);
+
+       if ((cur = tb[NL80211_ATTR_BSS_HT_OPMODE]))
+               chn->mode = nla_get_u32(cur);
+
+       return NL_SKIP;
+}
+
+static int nl80211_get_htmode(const char *ifname, int *buf)
+{
+       struct chan_info chn = { .width = 0, .mode = 0 };
+       char *res;
+       int err;
+
+       res = nl80211_phy2ifname(ifname);
+       *buf = 0;
+
+       err =  nl80211_request(res ? res : ifname,
+                               NL80211_CMD_GET_INTERFACE, 0,
+                               nl80211_get_htmode_cb, &chn);
+       if (err)
+               return -1;
+
+       switch (chn.width) {
+       case NL80211_CHAN_WIDTH_20:
+               if (chn.mode == -1)
+                       *buf = IWINFO_HTMODE_VHT20;
+               else
+                       *buf = IWINFO_HTMODE_HT20;
+               break;
+       case NL80211_CHAN_WIDTH_40:
+               if (chn.mode == -1)
+                       *buf = IWINFO_HTMODE_VHT40;
+               else
+                       *buf = IWINFO_HTMODE_HT40;
+               break;
+       case NL80211_CHAN_WIDTH_80:
+               *buf = IWINFO_HTMODE_VHT80;
+               break;
+       case NL80211_CHAN_WIDTH_80P80:
+               *buf = IWINFO_HTMODE_VHT80_80;
+               break;
+       case NL80211_CHAN_WIDTH_160:
+               *buf = IWINFO_HTMODE_VHT160;
+               break;
+       case NL80211_CHAN_WIDTH_5:
+       case NL80211_CHAN_WIDTH_10:
+       case NL80211_CHAN_WIDTH_20_NOHT:
+               *buf = IWINFO_HTMODE_NOHT;
+               break;
+       default:
+               return -1;
+       }
+
+       return 0;
+}
+
 static int nl80211_get_htmodelist(const char *ifname, int *buf)
 {
        struct nl80211_modes m = { 0 };
@@ -3088,6 +3216,7 @@ const struct iwinfo_ops nl80211_ops = {
        .mbssid_support   = nl80211_get_mbssid_support,
        .hwmodelist       = nl80211_get_hwmodelist,
        .htmodelist       = nl80211_get_htmodelist,
+       .htmode           = nl80211_get_htmode,
        .mode             = nl80211_get_mode,
        .ssid             = nl80211_get_ssid,
        .bssid            = nl80211_get_bssid,