diff options
Diffstat (limited to '')
-rw-r--r-- | iw_if.c | 391 |
1 files changed, 204 insertions, 187 deletions
@@ -19,12 +19,50 @@ */ #include "iw_if.h" +/* Determine the artificial spreading of random samples (best: 1..10) */ +#define WAVE_RAND_SPREAD 1 +/* Fallback maximum quality level when using random samples. */ +#define WAVE_RAND_QUAL_MAX 100 + /* * Obtain network device information */ +static int if_get_flags(int skfd, const char *ifname) +{ + struct ifreq ifr; + + memset(&ifr, 0, sizeof(struct ifreq)); + strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name) - 1); + + if (ioctl(skfd, SIOCGIFFLAGS, &ifr) < 0) + err_sys("can not get interface flags for %s", ifname); + return ifr.ifr_flags; +} + +/* Return true if @ifname is known to be up */ +bool if_is_up(int skfd, const char *ifname) +{ + return if_get_flags(skfd, ifname) & IFF_UP; +} + +/** Bring @ifname up if not already up. Return 0 if ok, < 0 on error. */ +int if_set_up(int skfd, const char *ifname) +{ + struct ifreq ifr; + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name) - 1); + + ifr.ifr_flags = if_get_flags(skfd, ifname); + if (ifr.ifr_flags & IFF_UP) + return 0; + + ifr.ifr_flags |= IFF_UP; + return ioctl(skfd, SIOCSIFFLAGS, &ifr); +} /* Interface information */ -void if_getinf(char *ifname, struct if_info *info) +void if_getinf(const char *ifname, struct if_info *info) { struct ifreq ifr; int skfd = socket(AF_INET, SOCK_DGRAM, 0); @@ -35,12 +73,20 @@ void if_getinf(char *ifname, struct if_info *info) memset(&ifr, 0, sizeof(struct ifreq)); memset(info, 0, sizeof(struct if_info)); - /* Copy the 6 byte Ethernet address and the 4 byte struct in_addrs */ + info->flags = if_get_flags(skfd, ifname); + strncpy(ifr.ifr_name, ifname, IFNAMSIZ); - if (ioctl(skfd, SIOCGIFADDR, &ifr) >= 0) - memcpy(&info->addr, &ifr.ifr_addr.sa_data[2], 4); + if (ioctl(skfd, SIOCGIFMTU, &ifr) == 0) + info->mtu = ifr.ifr_mtu; + + if (ioctl(skfd, SIOCGIFTXQLEN, &ifr) >= 0) + info->txqlen = ifr.ifr_qlen; + + /* Copy the 6 byte Ethernet address and the 4 byte struct in_addrs */ if (ioctl(skfd, SIOCGIFHWADDR, &ifr) >= 0) memcpy(&info->hwaddr, &ifr.ifr_hwaddr.sa_data, 6); + if (ioctl(skfd, SIOCGIFADDR, &ifr) >= 0) + memcpy(&info->addr, &ifr.ifr_addr.sa_data[2], 4); if (ioctl(skfd, SIOCGIFNETMASK, &ifr) >= 0) memcpy(&info->netmask, &ifr.ifr_netmask.sa_data[2], 4); if (ioctl(skfd, SIOCGIFBRDADDR, &ifr) >= 0) @@ -64,29 +110,28 @@ static FILE *open_proc_net(const char *filename) return fp; } -/* - * Populate list of available wireless interfaces - * Return index into array-of-lists ld. +/** + * iw_get_interface_list - Return NULL-terminated array of WiFi interfaces. */ -int iw_get_interface_list(void) +char **iw_get_interface_list(void) { - char *lp, tmp[LISTVAL_MAX]; - int ld = ll_create(); + char **if_list = NULL, *p, tmp[BUFSIZ]; + int nifs = 1; /* if_list[nifs-1] = NULL */ FILE *fp = open_proc_net("wireless"); - while (fgets(tmp, LISTVAL_MAX, fp)) - if ((lp = strchr(tmp, ':'))) { - *lp = '\0'; - ll_push(ld, "s", tmp + strspn(tmp, " ")); + while (fgets(tmp, sizeof(tmp), fp)) + if ((p = strchr(tmp, ':'))) { + if_list = realloc(if_list, sizeof(char *) * (nifs + 1)); + for (*p = '\0', p = tmp; isspace(*p); ) + p++; + if_list[nifs-1] = strdup(p); + if_list[nifs++] = NULL; } fclose(fp); - - if (ll_size(ld) == 0) - err_quit("no wireless interfaces found!"); - return ld; + return if_list; } -void if_getstat(char *ifname, struct if_stat *stat) +void if_getstat(const char *ifname, struct if_stat *stat) { char line[0x100]; unsigned long d; @@ -111,22 +156,26 @@ void if_getstat(char *ifname, struct if_stat *stat) fclose(fp); } -/* - * obtain dynamic device information +/** + * iw_dyn_info_get - populate dynamic information + * @info: information to populate + * @ifname: interface name + * @if: range information to use (number of encryption keys) */ -void iw_getinf_dyn(char *ifname, struct iw_dyn_info *info) +void dyn_info_get(struct iw_dyn_info *info, + const char *ifname, struct iw_range *ir) { struct iwreq iwr; - int skfd = socket(AF_INET, SOCK_DGRAM, 0); + int i, skfd = socket(AF_INET, SOCK_DGRAM, 0); if (skfd < 0) err_sys("%s: can not open socket", __func__); memset(info, 0, sizeof(struct iw_dyn_info)); - strncpy(iwr.ifr_name, ifname, IFNAMSIZ); + if (ioctl(skfd, SIOCGIWNAME, &iwr) < 0) - err_sys("can not open device '%s'", iwr.u.name); + err_sys("can not open device '%s'", ifname); strncpy(info->name, iwr.u.name, IFNAMSIZ); iwr.u.essid.pointer = (caddr_t) info->essid; @@ -136,7 +185,7 @@ void iw_getinf_dyn(char *ifname, struct iw_dyn_info *info) info->cap_essid = 1; /* Convert potential ESSID index to count > 0 */ info->essid_ct = iwr.u.essid.flags & IW_ENCODE_INDEX ? : 1; - strncpy(info->essid, iwr.u.essid.pointer, IW_ESSID_MAX_SIZE); + info->essid[iwr.u.essid.length] = '\0'; } if (ioctl(skfd, SIOCGIWNWID, &iwr) >= 0) { @@ -161,10 +210,8 @@ void iw_getinf_dyn(char *ifname, struct iw_dyn_info *info) info->sens = iwr.u.sens.value; } - if (ioctl(skfd, SIOCGIWRATE, &iwr) >= 0) { - info->cap_bitrate = 1; - info->bitrate = iwr.u.bitrate.value; - } + if (ioctl(skfd, SIOCGIWRATE, &iwr) >= 0) + info->bitrate = iwr.u.bitrate.value; if (ioctl(skfd, SIOCGIWTXPOW, &iwr) >= 0) { info->cap_txpower = 1; @@ -196,13 +243,41 @@ void iw_getinf_dyn(char *ifname, struct iw_dyn_info *info) info->mode = iwr.u.mode; } - iwr.u.data.pointer = (caddr_t) info->key; - iwr.u.data.length = sizeof(info->key); - iwr.u.data.flags = 0; - if (ioctl(skfd, SIOCGIWENCODE, &iwr) >= 0) { - info->cap_key = 1; - info->key_flags = iwr.u.data.flags; - info->key_size = iwr.u.data.length; + info->nkeys = ir->max_encoding_tokens; + if (info->nkeys) { + info->keys = calloc(info->nkeys, sizeof(*info->keys)); + if (info->keys == NULL) + err_sys("malloc(key array)"); + + /* Get index of default key first */ + iwr.u.data.pointer = info->keys[0].key; + iwr.u.data.length = sizeof(info->keys[0].key); + iwr.u.data.flags = 0; + if (ioctl(skfd, SIOCGIWENCODE, &iwr) < 0) { + free(info->keys); + info->keys = NULL; + info->nkeys = 0; + } else { + info->active_key = iwr.u.data.flags & IW_ENCODE_INDEX; + } + } + /* If successful, populate the key array */ + for (i = 0; i < info->nkeys; i++) { + iwr.u.data.pointer = info->keys[i].key; + iwr.u.data.length = sizeof(info->keys->key); + iwr.u.data.flags = i + 1; /* counts 1..n instead of 0..n-1 */ + if (ioctl(skfd, SIOCGIWENCODE, &iwr) < 0) { + free(info->keys); + info->nkeys = 0; + break; + } + info->keys[i].size = iwr.u.data.length; + info->keys[i].flags = iwr.u.data.flags; + + /* Validate whether the current key is indeed active */ + if (i + 1 == info->active_key && (info->keys[i].size == 0 || + (info->keys[i].flags & IW_ENCODE_DISABLED))) + info->active_key = 0; } if (ioctl(skfd, SIOCGIWAP, &iwr) >= 0) { @@ -212,10 +287,17 @@ void iw_getinf_dyn(char *ifname, struct iw_dyn_info *info) close(skfd); } +void dyn_info_cleanup(struct iw_dyn_info *info) +{ + if (info) + free(info->keys); +} + + /* * get range information */ -void iw_getinf_range(char *ifname, struct iw_range *range) +void iw_getinf_range(const char *ifname, struct iw_range *range) { struct iwreq iwr; int skfd = socket(AF_INET, SOCK_DGRAM, 0); @@ -224,10 +306,7 @@ void iw_getinf_range(char *ifname, struct iw_range *range) err_sys("%s: can not open socket", __func__); memset(range, 0, sizeof(struct iw_range)); - strncpy(iwr.ifr_name, ifname, IFNAMSIZ); - if (ioctl(skfd, SIOCGIWNAME, &iwr) < 0) - err_sys("can not open device '%s'", iwr.u.name); iwr.u.data.pointer = (caddr_t) range; iwr.u.data.length = sizeof(struct iw_range); @@ -240,121 +319,48 @@ void iw_getinf_range(char *ifname, struct iw_range *range) /* * Obtain periodic IW statistics */ -static int rnd_signal(int min, int max) +static int rand_wave(float *rlvl, float *step, float *rlvl_next, float range) { - static float rlvl, rlvl_next; - static float step = 1.0; int i; - for (i = 0; i < 1; i++) - if (rlvl < rlvl_next) { - if (rlvl_next - rlvl < step) - step /= 2; - rlvl += step; - } else if (rlvl > rlvl_next) { - if (rlvl - rlvl_next < step) - step /= 2; - rlvl -= step; + for (i = 0; i < WAVE_RAND_SPREAD; i++) + if (*rlvl < *rlvl_next) { + if (*rlvl_next - *rlvl < *step) + *step /= 2.0; + *rlvl += *step; + } else if (*rlvl > *rlvl_next) { + if (*rlvl - *rlvl_next < *step) + *step /= 2.0; + *rlvl -= *step; } - step += (rand() / (float)RAND_MAX) - 0.5; - if ((rlvl == rlvl_next) || (step < 0.05)) { - rlvl_next = (rand() / (float)RAND_MAX) * (max - min) + min; - step = rand() / (float)RAND_MAX; + *step += (random() / (float)RAND_MAX) - 0.5; + if (*rlvl == *rlvl_next || *step < 0.05) { + *rlvl_next = (range * random()) / RAND_MAX; + *step = random() / (float)RAND_MAX; } - return rlvl; + return *rlvl; } -static int rnd_noise(int min, int max) +/* Random signal/noise/quality levels */ +static void iw_getstat_random(struct iw_stat *iw) { - static float rlvl, rlvl_next; - static float step = 1.0; - int i; + static float rnd_sig, snext, sstep = 1.0, rnd_noise, nnext, nstep = 1.0; - for (i = 0; i < 1; i++) - if (rlvl < rlvl_next) { - if (rlvl_next - rlvl < step) - step /= 2; - rlvl += step; - } else if (rlvl > rlvl_next) { - if (rlvl - rlvl_next < step) - step /= 2; - rlvl -= step; - } - step += (rand() / (float)RAND_MAX) - 0.5; - if ((rlvl == rlvl_next) || (step < 0.05)) { - rlvl_next = (rand() / (float)RAND_MAX) * (max - min) + min; - step = rand() / (float)RAND_MAX; - } - return rlvl; -} + rand_wave(&rnd_sig, &sstep, &snext, conf.sig_max - conf.sig_min); + rand_wave(&rnd_noise, &nstep, &nnext, conf.noise_max - conf.noise_min); -/* Random signal/noise */ -static void iw_getstat_random(struct iw_statistics *stat) -{ - stat->qual.level = rnd_signal(-102, 10); - stat->qual.noise = rnd_noise(-102, -30); -} + if (iw->range.max_qual.qual == 0) + iw->range.max_qual.qual = WAVE_RAND_QUAL_MAX; -/* Code in part taken from wireless extensions #30 */ -static void iw_getstat_old_style(struct iw_statistics *stat) -{ - char line[0x100], *lp; - int tmp; - FILE *fp = open_proc_net("wireless"); - - while (fgets(line, sizeof(line), fp)) { - for (lp = line; *lp && isspace(*lp); lp++) - ; - if (strncmp(lp, conf.ifname, strlen(conf.ifname)) == 0 && - lp[strlen(conf.ifname)] == ':') { - lp += strlen(conf.ifname) + 1; - - /* status */ - lp = strtok(lp, " "); - sscanf(lp, "%X", &tmp); - stat->status = (unsigned short)tmp; - - /* link quality */ - lp = strtok(NULL, " "); - if (strchr(lp, '.') != NULL) - stat->qual.updated |= IW_QUAL_QUAL_UPDATED; - sscanf(lp, "%d", &tmp); - stat->qual.qual = (unsigned char)tmp; - - /* signal level */ - lp = strtok(NULL, " "); - if (strchr(lp, '.') != NULL) - stat->qual.updated |= IW_QUAL_LEVEL_UPDATED; - sscanf(lp, "%d", &tmp); - stat->qual.level = (unsigned char)tmp; - - /* noise level */ - lp = strtok(NULL, " "); - if (strchr(lp, '.') != NULL) - stat->qual.updated |= IW_QUAL_NOISE_UPDATED; - sscanf(lp, "%d", &tmp); - stat->qual.noise = (unsigned char)tmp; - - /* # of packets w/ invalid nwid */ - lp = strtok(NULL, " "); - sscanf(lp, "%u", &stat->discard.nwid); - - /* # of packets w/ invalid key */ - lp = strtok(NULL, " "); - sscanf(lp, "%u", &stat->discard.code); - - /* # of packets w/ bad attitude */ - lp = strtok(NULL, " "); - sscanf(lp, "%u", &stat->discard.misc); - - /* each interface appears just once */ - break; - } - } - fclose(fp); + iw->stat.qual.level = dbm_to_u8(conf.sig_min + rnd_sig); + iw->stat.qual.noise = dbm_to_u8(conf.noise_min + rnd_noise); + iw->stat.qual.updated = IW_QUAL_DBM; + iw->stat.qual.qual = map_range(conf.sig_min + rnd_sig, + conf.sig_min, conf.sig_max, + 0, iw->range.max_qual.qual); } -static void iw_getstat_new_style(struct iw_statistics *stat) +static void iw_getstat_real(struct iw_statistics *stat) { struct iwreq wrq; int skfd = socket(AF_INET, SOCK_DGRAM, 0); @@ -365,7 +371,7 @@ static void iw_getstat_new_style(struct iw_statistics *stat) wrq.u.data.pointer = (caddr_t) stat; wrq.u.data.length = sizeof(*stat); wrq.u.data.flags = 0; - strncpy(wrq.ifr_name, conf.ifname, IFNAMSIZ); + strncpy(wrq.ifr_name, conf_ifname(), IFNAMSIZ); if (ioctl(skfd, SIOCGIWSTATS, &wrq) < 0) { /* @@ -405,22 +411,11 @@ void iw_sanitize(struct iw_range *range, struct iw_quality *qual, dbm->noise = (double)(qual->noise / 2.0) - 110.0; } else if ((qual->updated & IW_QUAL_DBM) || - /* - * Statistics in dBm (absolute power measurement) - * These are encoded in the range -192 .. 63 - */ qual->level > range->max_qual.level) { - - if (!(qual->updated & IW_QUAL_LEVEL_INVALID)) { - dbm->signal = qual->level; - if (qual->level >= 64) - dbm->signal -= 0x100; - } - if (!(qual->updated & IW_QUAL_NOISE_INVALID)) { - dbm->noise = qual->noise; - if (qual->noise >= 64) - dbm->noise -= 0x100; - } + if (!(qual->updated & IW_QUAL_LEVEL_INVALID)) + dbm->signal = u8_to_dbm(qual->level); + if (!(qual->updated & IW_QUAL_NOISE_INVALID)) + dbm->noise = u8_to_dbm(qual->noise); } else { /* * Relative values (0 -> max) @@ -457,11 +452,9 @@ void iw_getstat(struct iw_stat *iw) memset(&iw->stat, 0, sizeof(iw->stat)); if (conf.random) - iw_getstat_random(&iw->stat); - else if (iw->range.we_version_compiled > 11) - iw_getstat_new_style(&iw->stat); + iw_getstat_random(iw); else - iw_getstat_old_style(&iw->stat); + iw_getstat_real(&iw->stat); iw_sanitize(&iw->range, &iw->stat.qual, &iw->dbm); } @@ -473,13 +466,32 @@ void dump_parameters(void) struct if_stat nstat; int i; - iw_getinf_dyn(conf.ifname, &info); - iw_getinf_range(conf.ifname, &iw.range); + iw_getinf_range(conf_ifname(), &iw.range); + dyn_info_get(&info, conf_ifname(), &iw.range); iw_getstat(&iw); - if_getstat(conf.ifname, &nstat); + if_getstat(conf_ifname(), &nstat); printf("\n"); - printf("Configured device: %s\n", conf.ifname); + printf("Configured device: %s (%s)\n", conf_ifname(), info.name); + printf(" Security: %s\n", iw.range.enc_capa ? + format_enc_capab(iw.range.enc_capa, ", ") : "WEP"); + if (iw.range.num_encoding_sizes && + iw.range.num_encoding_sizes < IW_MAX_ENCODING_SIZES) { + + printf(" Key sizes: "); + for (i = 0; i < iw.range.num_encoding_sizes; i++) { + if (i) + printf(", "); + if (iw.range.encoding_size[i] == 5) + printf("WEP-40"); + else if (iw.range.encoding_size[i] == 13) + printf("WEP-104"); + else + printf("%u bits", + iw.range.encoding_size[i] * 8); + } + printf("\n"); + } printf(" WE version: %d (source version %d)\n\n", iw.range.we_version_compiled, iw.range.we_version_source); @@ -503,7 +515,10 @@ void dump_parameters(void) printf(" nwid: %X\n", info.nwid.value); } - if (info.cap_freq) { + /* Some drivers only return the channel (e.g. ipw2100) */ + if (info.cap_freq && info.freq < 256) + info.freq = channel_to_freq(info.freq, &iw.range); + if (info.cap_freq && info.freq > 1e3) { i = freq_to_channel(info.freq, &iw.range); if (i >= 0) printf(" channel: %d\n", i); @@ -531,7 +546,7 @@ void dump_parameters(void) if (info.mode != 1 && info.cap_ap) printf(" access point: %s\n", format_bssid(&info.ap_addr)); - if (info.cap_bitrate) + if (info.bitrate) printf(" bitrate: %g Mbit/s\n", info.bitrate / 1.0e6); else printf(" bitrate: n/a\n"); @@ -564,24 +579,28 @@ void dump_parameters(void) } printf(" encryption: "); - if (info.cap_key) { - if (info.key_flags & IW_ENCODE_DISABLED || info.key_size == 0) { - printf("off"); + if (!info.nkeys && has_net_admin_capability()) + printf("no information available\n"); + else if (!info.nkeys) + printf("n/a (requires CAP_NET_ADMIN permissions)\n"); + for (i = 0; i < info.nkeys; i++) { + if (i) + printf(" "); + /* Current key is marked by `=' sign */ + printf("[%u]%s ", i + 1, i + 1 == info.active_key ? "=" : ":"); + + if (info.keys[i].flags & IW_ENCODE_DISABLED || !info.keys[i].size) { + printf("off\n"); } else { - printf("%s", format_key(info.key, info.key_size)); - i = info.key_flags & IW_ENCODE_INDEX; - if (i > 1) - printf(" [%d]", i); - if (info.key_flags & IW_ENCODE_RESTRICTED) + printf("%s", format_key(info.keys + i)); + if (info.keys[i].flags & IW_ENCODE_RESTRICTED) printf(", restricted"); - if (info.key_flags & IW_ENCODE_OPEN) + if (info.keys[i].flags & IW_ENCODE_OPEN) printf(", open"); + printf("\n"); } - } else { - printf("n/a"); } - printf("\n"); printf(" power management: "); if (info.cap_power) printf("%s\n", format_power(&info.power, &iw.range)); @@ -602,17 +621,15 @@ void dump_parameters(void) byte_units(nstat.rx_bytes)); printf(" invalid nwid: %'u\n", iw.stat.discard.nwid); printf(" invalid key: %'u\n", iw.stat.discard.code); - if (iw.range.we_version_compiled > 11) { - printf(" invalid fragm.: %'u\n", iw.stat.discard.fragment); - printf(" missed beacons: %'u\n", iw.stat.miss.beacon); - } + printf(" invalid fragm.: %'u\n", iw.stat.discard.fragment); + printf(" missed beacons: %'u\n", iw.stat.miss.beacon); printf(" misc errors: %'u\n", iw.stat.discard.misc); /* TX stats */ printf(" TX total: %'llu packets (%s)\n", nstat.tx_packets, byte_units(nstat.tx_bytes)); - if (iw.range.we_version_compiled > 11) - printf(" exc. MAC retries: %'u\n", iw.stat.discard.retries); + printf(" exc. MAC retries: %'u\n", iw.stat.discard.retries); printf("\n"); + dyn_info_cleanup(&info); } |