aboutsummaryrefslogtreecommitdiffstats
path: root/iw_if.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--iw_if.c391
1 files changed, 204 insertions, 187 deletions
diff --git a/iw_if.c b/iw_if.c
index 2e22454..708ff85 100644
--- a/iw_if.c
+++ b/iw_if.c
@@ -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);
}