libiwinfo: madwifi fixes, use direct ioctl calls for vap creation, ifup and ifdown...
[project/luci.git] / contrib / package / iwinfo / src / iwinfo_nl80211.c
index 33013fc..91ede2e 100644 (file)
@@ -29,6 +29,7 @@
 
 extern struct iwinfo_iso3166_label ISO3166_Names[];
 static struct nl80211_state *nls = NULL;
+static int nl80211_ioctlsock = -1;
 
 static int nl80211_init(void)
 {
@@ -36,6 +37,19 @@ static int nl80211_init(void)
 
        if( !nls )
        {
+               nl80211_ioctlsock = socket(AF_INET, SOCK_DGRAM, 0);
+               if( nl80211_ioctlsock < 0 )
+               {
+                       err = -ENOLINK;
+                       goto err;
+               }
+               else if( fcntl(nl80211_ioctlsock, F_SETFD,
+                                          fcntl(nl80211_ioctlsock, F_GETFD) | FD_CLOEXEC) < 0 )
+               {
+                       err = -EINVAL;
+                       goto err;
+               }
+
                nls = malloc(sizeof(struct nl80211_state));
                if( !nls ) {
                        err = -ENOMEM;
@@ -145,7 +159,9 @@ static struct nl80211_msg_conveyor * nl80211_msg(const char *ifname, int cmd, in
        if( nl80211_init() < 0 )
                goto err;
 
-       if( !strncmp(ifname, "radio", 5) )
+       if( !strncmp(ifname, "phy", 3) )
+               phyidx = atoi(&ifname[3]);
+       else if( !strncmp(ifname, "radio", 5) )
                phyidx = atoi(&ifname[5]);
        else if( !strncmp(ifname, "mon.", 4) )
                ifidx = if_nametoindex(&ifname[4]);
@@ -236,13 +252,16 @@ static int nl80211_freq2channel(int freq)
     return (freq / 5) - 1000;
 }
 
-static char * nl80211_getval(const char *buf, const char *key)
+static char * nl80211_getval(const char *ifname, const char *buf, const char *key)
 {
        int i, len;
        char lkey[64] = { 0 };
        const char *ln = buf;
        static char lval[256] = { 0 };
 
+       int matched_if = ifname ? 0 : 1;
+
+
        for( i = 0, len = strlen(buf); i < len; i++ )
        {
                if( !lkey[0] && (buf[i] == ' ' || buf[i] == '\t') )
@@ -256,12 +275,20 @@ static char * nl80211_getval(const char *buf, const char *key)
                }
                else if( buf[i] == '\n' )
                {
-                       if( lkey[0] && !strcmp(lkey, key) )
+                       if( lkey[0] )
                        {
                                memcpy(lval, ln + strlen(lkey) + 1,
                                        min(sizeof(lval) - 1, &buf[i] - ln - strlen(lkey) - 1));
 
-                               return lval;
+                               if( (ifname != NULL ) &&
+                                   (!strcmp(lkey, "interface") || !strcmp(lkey, "bss")) )
+                               {
+                                       matched_if = !strcmp(lval, ifname);
+                               }
+                               else if( matched_if && !strcmp(lkey, key) )
+                               {
+                                       return lval;
+                               }
                        }
 
                        ln = &buf[i+1];
@@ -380,24 +407,29 @@ static char * nl80211_wpasupp_info(const char *ifname, const char *cmd)
 
 out:
        close(sock);
-       unlink(local.sun_path);
+
+       if( local.sun_family )
+               unlink(local.sun_path);
 
        return rv;
 }
 
 static char * nl80211_phy2ifname(const char *ifname)
 {
-       int fd, phyidx = 0;
+       int fd, phyidx = -1;
        char buffer[64];
        static char nif[IFNAMSIZ] = { 0 };
 
        DIR *d;
        struct dirent *e;
 
-       if( !strncmp(ifname, "radio", 5) )
-       {
+       if( !strncmp(ifname, "phy", 3) )
+               phyidx = atoi(&ifname[3]);
+       else if( !strncmp(ifname, "radio", 5) )
                phyidx = atoi(&ifname[5]);
 
+       if( phyidx > -1 )
+       {
                if( (d = opendir("/sys/class/net")) != NULL )
                {
                        while( (e = readdir(d)) != NULL )
@@ -427,7 +459,7 @@ static char * nl80211_phy2ifname(const char *ifname)
        return nif[0] ? nif : NULL;
 }
 
-static char * nl80211_add_tempif(const char *ifname)
+static char * nl80211_ifadd(const char *ifname)
 {
        int phyidx;
        char *rv = NULL;
@@ -456,9 +488,9 @@ static char * nl80211_add_tempif(const char *ifname)
        return rv;
 }
 
-static void nl80211_del_tempif(const char *ifname)
+static void nl80211_ifdel(const char *ifname)
 {
-       struct nl80211_msg_conveyor *req, *res;
+       struct nl80211_msg_conveyor *req;
 
        req = nl80211_msg(ifname, NL80211_CMD_DEL_INTERFACE, 0);
        if( req )
@@ -472,6 +504,71 @@ static void nl80211_del_tempif(const char *ifname)
        }
 }
 
+static int nl80211_ifup(const char *ifname)
+{
+       struct ifreq ifr;
+
+       strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
+
+       if( ioctl(nl80211_ioctlsock, SIOCGIFFLAGS, &ifr) )
+               return 0;
+
+       ifr.ifr_flags |= (IFF_UP | IFF_RUNNING);
+
+       return !ioctl(nl80211_ioctlsock, SIOCSIFFLAGS, &ifr);
+}
+
+static int nl80211_ifdown(const char *ifname)
+{
+       struct ifreq ifr;
+
+       strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
+
+       if( ioctl(nl80211_ioctlsock, SIOCGIFFLAGS, &ifr) )
+               return 0;
+
+       ifr.ifr_flags &= ~(IFF_UP | IFF_RUNNING);
+
+       return !ioctl(nl80211_ioctlsock, SIOCSIFFLAGS, &ifr);
+}
+
+static int nl80211_ifmac(const char *ifname)
+{
+       struct ifreq ifr;
+
+       strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
+
+       if( ioctl(nl80211_ioctlsock, SIOCGIFHWADDR, &ifr) )
+               return 0;
+
+       ifr.ifr_hwaddr.sa_data[1]++;
+       ifr.ifr_hwaddr.sa_data[2]++;
+
+       return !ioctl(nl80211_ioctlsock, SIOCSIFHWADDR, &ifr);
+}
+
+static void nl80211_hostapd_hup(const char *ifname)
+{
+       int fd, pid = 0;
+       char buf[32];
+       char *phy = nl80211_ifname2phy(ifname);
+
+       if( phy )
+       {
+               snprintf(buf, sizeof(buf), "/var/run/wifi-%s.pid", phy);
+               if( (fd = open(buf, O_RDONLY)) > 0 )
+               {
+                       if( read(fd, buf, sizeof(buf)) > 0 )
+                               pid = atoi(buf);
+
+                       close(fd);
+               }
+
+               if( pid > 0 )
+                       kill(pid, 1);
+       }
+}
+
 
 int nl80211_probe(const char *ifname)
 {
@@ -480,6 +577,11 @@ int nl80211_probe(const char *ifname)
 
 void nl80211_close(void)
 {
+       if( nl80211_ioctlsock > -1 )
+       {
+               close(nl80211_ioctlsock);
+       }
+
        if( nls )
        {
                if( nls->nl_sock )
@@ -507,7 +609,7 @@ int nl80211_get_ssid(const char *ifname, char *buf)
                return 0;
        }
        else if( (ssid = nl80211_hostapd_info(ifname)) &&
-                (ssid = nl80211_getval(ssid, "ssid")) )
+                (ssid = nl80211_getval(ifname, ssid, "ssid")) )
        {
                memcpy(buf, ssid, strlen(ssid));
                return 0;
@@ -526,7 +628,7 @@ int nl80211_get_bssid(const char *ifname, char *buf)
                return 0;
        }
        else if( (bssid = nl80211_hostapd_info(ifname)) &&
-                (bssid = nl80211_getval(bssid, "bssid")) )
+                (bssid = nl80211_getval(ifname, bssid, "bssid")) )
        {
                mac[0] = strtol(&bssid[0],  NULL, 16);
                mac[1] = strtol(&bssid[3],  NULL, 16);
@@ -768,10 +870,9 @@ int nl80211_get_encryption(const char *ifname, char *buf)
        struct iwinfo_crypto_entry *c = (struct iwinfo_crypto_entry *)buf;
 
        /* Hostapd */
-       if( (res = nl80211_hostapd_info(ifname)) &&
-           nl80211_getval(res, "interface") )
+       if( (res = nl80211_hostapd_info(ifname)) )
        {
-               if( (val = nl80211_getval(res, "auth_algs")) && (val > 0) )
+               if( (val = nl80211_getval(ifname, res, "auth_algs")) && (val > 0) )
                {
                        c->auth_suites |= IWINFO_KMGMT_NONE;
 
@@ -797,7 +898,7 @@ int nl80211_get_encryption(const char *ifname, char *buf)
                        {
                                snprintf(k, sizeof(k), "wep_key%d", i);
 
-                               if( (val = nl80211_getval(res, k)) )
+                               if( (val = nl80211_getval(ifname, res, k)) )
                                {
                                        if( (strlen(val) == 5) || (strlen(val) == 10) )
                                                c->pair_ciphers |= IWINFO_CIPHER_WEP40;
@@ -813,11 +914,11 @@ int nl80211_get_encryption(const char *ifname, char *buf)
                }
 
 
-               if( (val = nl80211_getval(res, "wpa")) != NULL )
+               if( (val = nl80211_getval(ifname, res, "wpa")) != NULL )
                        c->wpa_version = atoi(val);
 
 
-               val = nl80211_getval(res, "wpa_key_mgmt");
+               val = nl80211_getval(ifname, res, "wpa_key_mgmt");
 
                if( !val || strstr(val, "PSK") )
                        c->auth_suites |= IWINFO_KMGMT_PSK;
@@ -829,7 +930,7 @@ int nl80211_get_encryption(const char *ifname, char *buf)
                        c->auth_suites |= IWINFO_KMGMT_NONE;
 
 
-               if( (val = nl80211_getval(res, "wpa_pairwise")) != NULL )
+               if( (val = nl80211_getval(ifname, res, "wpa_pairwise")) != NULL )
                {
                        if( strstr(val, "TKIP") )
                                c->pair_ciphers |= IWINFO_CIPHER_TKIP;
@@ -850,7 +951,7 @@ int nl80211_get_encryption(const char *ifname, char *buf)
 
        /* WPA supplicant */
        else if( (res = nl80211_wpasupp_info(ifname, "STATUS")) &&
-                (val = nl80211_getval(res, "pairwise_cipher")) )
+                (val = nl80211_getval(NULL, res, "pairwise_cipher")) )
        {
                /* WEP */
                if( strstr(val, "WEP") )
@@ -887,7 +988,7 @@ int nl80211_get_encryption(const char *ifname, char *buf)
                                c->pair_ciphers |= IWINFO_CIPHER_WEP104;
 
 
-                       if( (val = nl80211_getval(res, "group_cipher")) )
+                       if( (val = nl80211_getval(NULL, res, "group_cipher")) )
                        {
                                if( strstr(val, "TKIP") )
                                        c->group_ciphers |= IWINFO_CIPHER_TKIP;
@@ -906,7 +1007,7 @@ int nl80211_get_encryption(const char *ifname, char *buf)
                        }
 
 
-                       if( (val = nl80211_getval(res, "key_mgmt")) )
+                       if( (val = nl80211_getval(NULL, res, "key_mgmt")) )
                        {
                                if( strstr(val, "WPA2") )
                                        c->wpa_version = 2;
@@ -1140,7 +1241,6 @@ int nl80211_get_scanlist(const char *ifname, char *buf, int *len)
 {
        int freq, rssi, qmax, count;
        char *res;
-       char cmd[256];
        char ssid[128] = { 0 };
        char bssid[18] = { 0 };
        char cipher[256] = { 0 };
@@ -1155,10 +1255,10 @@ int nl80211_get_scanlist(const char *ifname, char *buf, int *len)
                }
 
                /* Need to spawn a temporary iface for scanning */
-               else if( (res = nl80211_add_tempif(ifname)) != NULL )
+               else if( (res = nl80211_ifadd(ifname)) != NULL )
                {
                        count = nl80211_get_scanlist(res, buf, len);
-                       nl80211_del_tempif(res);
+                       nl80211_ifdel(res);
                        return count;
                }
        }
@@ -1243,57 +1343,47 @@ int nl80211_get_scanlist(const char *ifname, char *buf, int *len)
        /* AP scan */
        else
        {
-               if( (res = nl80211_ifname2phy(ifname)) != NULL )
+               /* Got a temp interface, don't create yet another one */
+               if( !strncmp(ifname, "tmp.", 4) )
                {
-                       /* Got a temp interface, don't create yet another one */
-                       if( !strncmp(ifname, "tmp.", 4) )
-                       {
-                               sprintf(cmd, "ifconfig %s up 2>/dev/null", ifname);
-                               if( WEXITSTATUS(system(cmd)) )
-                                       return -1;
+                       if( !nl80211_ifup(ifname) )
+                               return -1;
 
-                               wext_get_scanlist(ifname, buf, len);
+                       wext_get_scanlist(ifname, buf, len);
+                       nl80211_ifdown(ifname);
+                       return 0;
+               }
 
-                               sprintf(cmd, "ifconfig %s down 2>/dev/null", ifname);
-                               (void) WEXITSTATUS(system(cmd));
+               /* Spawn a new scan interface */
+               else
+               {
+                       if( !(res = nl80211_ifadd(ifname)) )
+                               goto out;
 
-                               return 0;
-                       }
+                       if( !nl80211_ifmac(res) )
+                               goto out;
 
-                       /* Spawn a new scan interface */
-                       else
+                       /* if we can take the new interface up, the driver supports an
+                        * additional interface and there's no need to tear down the ap */
+                       if( nl80211_ifup(res) )
                        {
-                               sprintf(cmd, "ifconfig %s down 2>/dev/null", ifname);
-                               if( WEXITSTATUS(system(cmd)) )
-                                       goto out;
-
-                               sprintf(cmd, "iw phy %s interface add scan.%s "
-                                       "type station 2>/dev/null", res, ifname);
-                               if( WEXITSTATUS(system(cmd)) )
-                                       goto out;
-
-                               sprintf(cmd, "ifconfig scan.%s up 2>/dev/null", ifname);
-                               if( WEXITSTATUS(system(cmd)) )
-                                       goto out;
-
-                               sprintf(cmd, "scan.%s", ifname);
-                               wext_get_scanlist(cmd, buf, len);
-
-                       out:
-                               sprintf(cmd, "ifconfig scan.%s down 2>/dev/null", ifname);
-                               (void) WEXITSTATUS(system(cmd));
-
-                               sprintf(cmd, "iw dev scan.%s del 2>/dev/null", ifname);
-                               (void) WEXITSTATUS(system(cmd));
-
-                               sprintf(cmd, "ifconfig %s up 2>/dev/null", ifname);
-                               (void) WEXITSTATUS(system(cmd));
-
-                               sprintf(cmd, "killall -HUP hostapd 2>/dev/null");
-                               (void) WEXITSTATUS(system(cmd));
+                               wext_get_scanlist(res, buf, len);
+                               nl80211_ifdown(res);
+                       }
 
-                               return 0;
+                       /* driver cannot create secondary interface, take down ap
+                        * during scan */
+                       else if( nl80211_ifdown(ifname) && nl80211_ifup(res) )
+                       {
+                               wext_get_scanlist(res, buf, len);
+                               nl80211_ifdown(res);
+                               nl80211_ifup(ifname);
+                               nl80211_hostapd_hup(ifname);
                        }
+
+               out:
+                       nl80211_ifdel(res);
+                       return 0;
                }
        }
 
@@ -1423,8 +1513,8 @@ int nl80211_get_hwmodelist(const char *ifname, int *buf)
                                if( bands[NL80211_BAND_ATTR_HT_CAPA] )
                                        caps = nla_get_u16(bands[NL80211_BAND_ATTR_HT_CAPA]);
 
-                               /* Treat HT20/HT40 as 11n */
-                               if( caps & (1 << 1) )
+                               /* Treat any nonzero capability as 11n */
+                               if( caps > 0 )
                                        *buf |= IWINFO_80211_N;
 
                                nla_for_each_nested(freq,
@@ -1457,7 +1547,18 @@ int nl80211_get_hwmodelist(const char *ifname, int *buf)
 
 int nl80211_get_mbssid_support(const char *ifname, int *buf)
 {
-       /* We assume that multi bssid is always possible */
-       *buf = 1;
-       return 0;
+       /* Test whether we can create another interface */
+       char *nif = nl80211_ifadd(ifname);
+
+       if( nif )
+       {
+               *buf = (nl80211_ifmac(nif) && nl80211_ifup(nif));
+
+               nl80211_ifdown(nif);
+               nl80211_ifdel(nif);
+
+               return 0;
+       }
+
+       return -1;
 }