X-Git-Url: https://git.archive.openwrt.org/?p=project%2Fluci.git;a=blobdiff_plain;f=contrib%2Fpackage%2Fiwinfo%2Fsrc%2Fiwinfo_nl80211.c;h=91ede2e99b55dd56acc145201f94fc87c772e044;hp=fe099fc7283bb364228024122dbbaff34fa06962;hb=6ef55ba3276ba85cb30d07c528266d92067725d4;hpb=1423d3556eeaf140730ba5e7170356cc4cd1b1ae diff --git a/contrib/package/iwinfo/src/iwinfo_nl80211.c b/contrib/package/iwinfo/src/iwinfo_nl80211.c index fe099fc72..91ede2e99 100644 --- a/contrib/package/iwinfo/src/iwinfo_nl80211.c +++ b/contrib/package/iwinfo/src/iwinfo_nl80211.c @@ -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; @@ -1138,9 +1239,8 @@ static void nl80211_get_scancrypto(const char *spec, int nl80211_get_scanlist(const char *ifname, char *buf, int *len) { - int freq, rssi, qmax, count, is_tmp = 0; + 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; } } @@ -1302,7 +1392,6 @@ int nl80211_get_scanlist(const char *ifname, char *buf, int *len) int nl80211_get_freqlist(const char *ifname, char *buf, int *len) { - char *phy; int count = 0, bands_remain, freqs_remain; struct nl80211_msg_conveyor *req, *res; struct nlattr *bands[NL80211_BAND_ATTR_MAX + 1]; @@ -1310,18 +1399,6 @@ int nl80211_get_freqlist(const char *ifname, char *buf, int *len) struct nlattr *band, *freq; struct iwinfo_freqlist_entry *e = (struct iwinfo_freqlist_entry *)buf; - static struct nla_policy freq_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = { - [NL80211_FREQUENCY_ATTR_FREQ] = { .type = NLA_U32 }, - [NL80211_FREQUENCY_ATTR_DISABLED] = { .type = NLA_FLAG }, - [NL80211_FREQUENCY_ATTR_PASSIVE_SCAN] = { .type = NLA_FLAG }, - [NL80211_FREQUENCY_ATTR_NO_IBSS] = { .type = NLA_FLAG }, - [NL80211_FREQUENCY_ATTR_RADAR] = { .type = NLA_FLAG }, - [NL80211_FREQUENCY_ATTR_MAX_TX_POWER] = { .type = NLA_U32 }, - }; - - if( !wext_get_freqlist(ifname, buf, len) ) - return 0; - req = nl80211_msg(ifname, NL80211_CMD_GET_WIPHY, 0); if( req ) { @@ -1338,11 +1415,21 @@ int nl80211_get_freqlist(const char *ifname, char *buf, int *len) bands[NL80211_BAND_ATTR_FREQS], freqs_remain) { nla_parse(freqs, NL80211_FREQUENCY_ATTR_MAX, - nla_data(freq), nla_len(freq), freq_policy); + nla_data(freq), nla_len(freq), NULL); + + if( !freqs[NL80211_FREQUENCY_ATTR_FREQ] || + freqs[NL80211_FREQUENCY_ATTR_DISABLED] ) + continue; e->mhz = nla_get_u32(freqs[NL80211_FREQUENCY_ATTR_FREQ]); e->channel = nl80211_freq2channel(e->mhz); + e->restricted = ( + freqs[NL80211_FREQUENCY_ATTR_PASSIVE_SCAN] || + freqs[NL80211_FREQUENCY_ATTR_NO_IBSS] || + freqs[NL80211_FREQUENCY_ATTR_RADAR] + ) ? 1 : 0; + e++; count++; } @@ -1402,9 +1489,76 @@ int nl80211_get_countrylist(const char *ifname, char *buf, int *len) return 0; } +int nl80211_get_hwmodelist(const char *ifname, int *buf) +{ + int bands_remain, freqs_remain; + struct nl80211_msg_conveyor *req, *res; + struct nlattr *bands[NL80211_BAND_ATTR_MAX + 1]; + struct nlattr *freqs[NL80211_FREQUENCY_ATTR_MAX + 1]; + struct nlattr *band, *freq; + uint16_t caps = 0; + + req = nl80211_msg(ifname, NL80211_CMD_GET_WIPHY, 0); + if( req ) + { + res = nl80211_send(req); + if( res ) + { + nla_for_each_nested(band, + res->attr[NL80211_ATTR_WIPHY_BANDS], bands_remain) + { + nla_parse(bands, NL80211_BAND_ATTR_MAX, nla_data(band), + nla_len(band), NULL); + + if( bands[NL80211_BAND_ATTR_HT_CAPA] ) + caps = nla_get_u16(bands[NL80211_BAND_ATTR_HT_CAPA]); + + /* Treat any nonzero capability as 11n */ + if( caps > 0 ) + *buf |= IWINFO_80211_N; + + nla_for_each_nested(freq, + bands[NL80211_BAND_ATTR_FREQS], freqs_remain) + { + nla_parse(freqs, NL80211_FREQUENCY_ATTR_MAX, + nla_data(freq), nla_len(freq), NULL); + + if( !freqs[NL80211_FREQUENCY_ATTR_FREQ] ) + continue; + + if( nla_get_u32(freqs[NL80211_FREQUENCY_ATTR_FREQ]) < 2485 ) + { + *buf |= IWINFO_80211_B; + *buf |= IWINFO_80211_G; + } + else + { + *buf |= IWINFO_80211_A; + } + } + } + nl80211_free(res); + } + nl80211_free(req); + } + + return *buf ? 0 : -1; +} + 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; }