From 03dfbc2efa31d3c4fd7e576111883ea488e3e179 Mon Sep 17 00:00:00 2001 From: Jonathan McCrohan Date: Sun, 5 Feb 2012 19:03:25 +0000 Subject: Imported Upstream version 0.6.10 --- iw_if.c | 618 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 618 insertions(+) create mode 100644 iw_if.c (limited to 'iw_if.c') diff --git a/iw_if.c b/iw_if.c new file mode 100644 index 0000000..2e22454 --- /dev/null +++ b/iw_if.c @@ -0,0 +1,618 @@ +/* + * wavemon - a wireless network monitoring aplication + * + * Copyright (c) 2001-2002 Jan Morgenstern + * + * wavemon is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2, or (at your option) any later + * version. + * + * wavemon is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along + * with wavemon; see the file COPYING. If not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#include "iw_if.h" + +/* + * Obtain network device information + */ + +/* Interface information */ +void if_getinf(char *ifname, struct if_info *info) +{ + struct ifreq ifr; + int skfd = socket(AF_INET, SOCK_DGRAM, 0); + + if (skfd < 0) + err_sys("%s: can not open socket", __func__); + + 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 */ + 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, SIOCGIFHWADDR, &ifr) >= 0) + memcpy(&info->hwaddr, &ifr.ifr_hwaddr.sa_data, 6); + if (ioctl(skfd, SIOCGIFNETMASK, &ifr) >= 0) + memcpy(&info->netmask, &ifr.ifr_netmask.sa_data[2], 4); + if (ioctl(skfd, SIOCGIFBRDADDR, &ifr) >= 0) + memcpy(&info->bcast, &ifr.ifr_broadaddr.sa_data[2], 4); + close(skfd); +} + +static FILE *open_proc_net(const char *filename) +{ + char path[128]; + FILE *fp; + + snprintf(path, sizeof(path), "/proc/net/%s", filename); + if (access(path, F_OK) != 0) + err_quit("'%s' not accessible - not compiled in?", path); + + fp = fopen(path, "r"); + if (fp == NULL) + err_sys("can not open %s", path); + + return fp; +} + +/* + * Populate list of available wireless interfaces + * Return index into array-of-lists ld. + */ +int iw_get_interface_list(void) +{ + char *lp, tmp[LISTVAL_MAX]; + int ld = ll_create(); + 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, " ")); + } + fclose(fp); + + if (ll_size(ld) == 0) + err_quit("no wireless interfaces found!"); + return ld; +} + +void if_getstat(char *ifname, struct if_stat *stat) +{ + char line[0x100]; + unsigned long d; + char *lp; + FILE *fp = open_proc_net("dev"); + + /* + * Inter-| Receive | Transmit + * face |bytes packets errs drop fifo frame compressed multicast|bytes packets + */ + while (fgets(line, sizeof(line), fp)) { + lp = line + strspn(line, " "); + if (!strncmp(lp, ifname, strlen(ifname))) { + lp += strlen(ifname) + 1; + lp += strspn(lp, " "); + + sscanf(lp, "%llu %llu %lu %lu %lu %lu %lu %lu %llu %llu", + &stat->rx_bytes, &stat->rx_packets, &d, &d, &d, &d, &d, &d, + &stat->tx_bytes, &stat->tx_packets); + } + } + fclose(fp); +} + +/* + * obtain dynamic device information + */ +void iw_getinf_dyn(char *ifname, struct iw_dyn_info *info) +{ + struct iwreq iwr; + int 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); + strncpy(info->name, iwr.u.name, IFNAMSIZ); + + iwr.u.essid.pointer = (caddr_t) info->essid; + iwr.u.essid.length = sizeof(info->essid); + iwr.u.essid.flags = 0; + if (ioctl(skfd, SIOCGIWESSID, &iwr) >= 0) { + 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); + } + + if (ioctl(skfd, SIOCGIWNWID, &iwr) >= 0) { + info->cap_nwid = 1; + memcpy(&info->nwid, &iwr.u.nwid, sizeof(info->nwid)); + } + + iwr.u.essid.pointer = (caddr_t) info->nickname; + iwr.u.essid.length = sizeof(info->nickname); + iwr.u.essid.flags = 0; + if (ioctl(skfd, SIOCGIWNICKN, &iwr) >= 0 && + iwr.u.data.length > 1) + info->cap_nickname = 1; + + if (ioctl(skfd, SIOCGIWFREQ, &iwr) >= 0) { + info->cap_freq = 1; + info->freq = freq_to_hz(&iwr.u.freq); + } + + if (ioctl(skfd, SIOCGIWSENS, &iwr) >= 0) { + info->cap_sens = 1; + 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, SIOCGIWTXPOW, &iwr) >= 0) { + info->cap_txpower = 1; + memcpy(&info->txpower, &iwr.u.txpower, sizeof(info->txpower)); + } + + if (ioctl(skfd, SIOCGIWPOWER, &iwr) >= 0) { + info->cap_power = 1; + memcpy(&info->power, &iwr.u.power, sizeof(info->power)); + } + + if (ioctl(skfd, SIOCGIWRETRY, &iwr) >= 0) { + info->cap_retry = 1; + memcpy(&info->retry, &iwr.u.retry, sizeof(info->retry)); + } + + if (ioctl(skfd, SIOCGIWRTS, &iwr) >= 0) { + info->cap_rts = 1; + memcpy(&info->rts, &iwr.u.rts, sizeof(info->rts)); + } + + if (ioctl(skfd, SIOCGIWFRAG, &iwr) >= 0) { + info->cap_frag = 1; + memcpy(&info->frag, &iwr.u.frag, sizeof(info->frag)); + } + + if (ioctl(skfd, SIOCGIWMODE, &iwr) >= 0) { + info->cap_mode = 1; + 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; + } + + if (ioctl(skfd, SIOCGIWAP, &iwr) >= 0) { + info->cap_ap = 1; + memcpy(&info->ap_addr, &iwr.u.ap_addr, sizeof(struct sockaddr)); + } + close(skfd); +} + +/* + * get range information + */ +void iw_getinf_range(char *ifname, struct iw_range *range) +{ + struct iwreq iwr; + int skfd = socket(AF_INET, SOCK_DGRAM, 0); + + if (skfd < 0) + 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); + iwr.u.data.flags = 0; + if (ioctl(skfd, SIOCGIWRANGE, &iwr) < 0) + err_sys("can not get range information"); + close(skfd); +} + +/* + * Obtain periodic IW statistics + */ +static int rnd_signal(int min, int max) +{ + 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; + } + 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; +} + +static int rnd_noise(int min, int max) +{ + 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; + } + 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; +} + +/* 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); +} + +/* 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); +} + +static void iw_getstat_new_style(struct iw_statistics *stat) +{ + struct iwreq wrq; + int skfd = socket(AF_INET, SOCK_DGRAM, 0); + + if (skfd < 0) + err_sys("%s: can not open socket", __func__); + + 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); + + if (ioctl(skfd, SIOCGIWSTATS, &wrq) < 0) { + /* + * iw_handler_get_iwstats() returns EOPNOTSUPP if + * there are no statistics. Bail out in this case. + */ + if (errno != EOPNOTSUPP) + err_sys("can not obtain iw statistics"); + errno = 0; + memset(&wrq, 0, sizeof(wrq)); + } + close(skfd); +} + +/* + * Generate dBm values and perform sanity checks on values. + * Code in part taken from wireless extensions #30 + * @range: range information, read-only + * @qual: wireless statistics, read-write + * @dbm: dBm level information, write-only + */ +void iw_sanitize(struct iw_range *range, struct iw_quality *qual, + struct iw_levelstat *dbm) +{ + memset(dbm, 0, sizeof(*dbm)); + + if (qual->level != 0 || (qual->updated & (IW_QUAL_DBM | IW_QUAL_RCPI))) { + /* + * RCPI (IEEE 802.11k) statistics: + * RCPI = int{(Power in dBm +110)*2} + * for 0 dBm > Power > -110 dBm + */ + if (qual->updated & IW_QUAL_RCPI) { + if (!(qual->updated & IW_QUAL_LEVEL_INVALID)) + dbm->signal = (double)(qual->level / 2.0) - 110.0; + if (!(qual->updated & IW_QUAL_NOISE_INVALID)) + 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; + } + } else { + /* + * Relative values (0 -> max) + */ + if (!(qual->updated & IW_QUAL_LEVEL_INVALID)) + dbm->signal = mw2dbm(qual->level); + if (!(qual->updated & IW_QUAL_NOISE_INVALID)) + dbm->noise = mw2dbm(qual->noise); + } + } else { + qual->updated |= IW_QUAL_ALL_INVALID; + } + + /* + * Value sanity checks + * + * These rules serve to avoid "insensible" level displays. Please do send + * comments and/or bug reports if you encounter room for improvement. + * + * 1) if noise level is valid, but signal level is not, displaying just + * the noise level does not reveal very much - can be omitted; + * 2) if the noise level is below an "invalid" magic value (see iw_if.h), + * declare the noise value to be invalid; + * 3) SNR is only displayed if both signal and noise values are valid. + */ + if (qual->updated & IW_QUAL_LEVEL_INVALID) + qual->updated |= IW_QUAL_NOISE_INVALID; + if (dbm->noise <= NOISE_DBM_SANE_MIN) + qual->updated |= IW_QUAL_NOISE_INVALID; +} + +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); + else + iw_getstat_old_style(&iw->stat); + + iw_sanitize(&iw->range, &iw->stat.qual, &iw->dbm); +} + +void dump_parameters(void) +{ + struct iw_dyn_info info; + struct iw_stat iw; + struct if_stat nstat; + int i; + + iw_getinf_dyn(conf.ifname, &info); + iw_getinf_range(conf.ifname, &iw.range); + iw_getstat(&iw); + if_getstat(conf.ifname, &nstat); + + printf("\n"); + printf("Configured device: %s\n", conf.ifname); + printf(" WE version: %d (source version %d)\n\n", + iw.range.we_version_compiled, iw.range.we_version_source); + + if (info.cap_essid) { + if (info.essid_ct > 1) + printf(" essid: \"%s\" [%d]\n", + info.essid, info.essid_ct); + else if (info.essid_ct) + printf(" essid: \"%s\"\n", info.essid); + else + printf(" essid: off/any\n"); + } + + if (info.cap_nickname) + printf(" nick: \"%s\"\n", info.nickname); + + if (info.cap_nwid) { + if (info.nwid.disabled) + printf(" nwid: off/any\n"); + else + printf(" nwid: %X\n", info.nwid.value); + } + + if (info.cap_freq) { + i = freq_to_channel(info.freq, &iw.range); + if (i >= 0) + printf(" channel: %d\n", i); + printf(" frequency: %g GHz\n", info.freq / 1.0e9); + } else + printf(" frequency: n/a\n"); + + if (info.cap_sens) { + if (info.sens < 0) + printf(" sensitivity: %d dBm\n", info.sens); + else + printf(" sensitivity: %d/%d\n", info.sens, + iw.range.sensitivity); + } + + if (info.cap_txpower && info.txpower.disabled) + printf(" tx-power: off\n"); + else if (info.cap_txpower && info.txpower.fixed) + printf(" tx-power: %s\n", format_txpower(&info.txpower)); + else if (info.cap_txpower) + printf(" TX-power: %s\n", format_txpower(&info.txpower)); + + printf(" mode: %s\n", iw_opmode(info.mode)); + + if (info.mode != 1 && info.cap_ap) + printf(" access point: %s\n", format_bssid(&info.ap_addr)); + + if (info.cap_bitrate) + printf(" bitrate: %g Mbit/s\n", info.bitrate / 1.0e6); + else + printf(" bitrate: n/a\n"); + + printf(" retry: "); + if (info.cap_retry) + printf("%s\n", format_retry(&info.retry, &iw.range)); + else + printf("n/a\n"); + + printf(" rts thr: "); + if (info.cap_rts) { + if (info.rts.disabled) + printf("off\n"); + else + printf("%d B %s\n", info.rts.value, + info.rts.fixed ? "" : "(auto-select)"); + } else + printf("n/a\n"); + + printf(" frag thr: "); + if (info.cap_frag) { + if (info.frag.disabled) + printf("off\n"); + else + printf("%d B %s\n", info.frag.value, + info.frag.fixed ? "" : "(auto-select)"); + } else { + printf("n/a\n"); + } + + printf(" encryption: "); + if (info.cap_key) { + if (info.key_flags & IW_ENCODE_DISABLED || info.key_size == 0) { + printf("off"); + } 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(", restricted"); + if (info.key_flags & IW_ENCODE_OPEN) + printf(", open"); + } + } else { + printf("n/a"); + } + + printf("\n"); + printf(" power management: "); + if (info.cap_power) + printf("%s\n", format_power(&info.power, &iw.range)); + else + printf("n/a\n"); + + printf("\n"); + printf(" link quality: %d/%d\n", iw.stat.qual.qual, + iw.range.max_qual.qual); + printf(" signal level: %.0f dBm (%s)\n", iw.dbm.signal, + dbm2units(iw.dbm.signal)); + printf(" noise level: %.0f dBm (%s)\n", iw.dbm.noise, + dbm2units(iw.dbm.noise)); + printf(" SNR: %.0f dB\n", iw.dbm.signal - iw.dbm.noise); + + /* RX stats */ + printf(" RX total: %'llu packets (%s)\n", nstat.rx_packets, + 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(" 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("\n"); +} -- cgit v1.2.3