/*
* Wireless Tools
*
- * Jean II - HPL 99->01
+ * Jean II - HPL 99->04
*
* Main code for "iwevent". This listent for wireless events on rtnetlink.
* You need to link this code against "iwcommon.c" and "-lm".
* about it...
*
* This file is released under the GPL license.
- * Copyright (c) 1997-2002 Jean Tourrilhes <jt@hpl.hp.com>
+ * Copyright (c) 1997-2004 Jean Tourrilhes <jt@hpl.hp.com>
*/
/***************************** INCLUDES *****************************/
#include <time.h>
#include <sys/time.h>
+/* Ugly backward compatibility :-( */
+#ifndef IFLA_WIRELESS
+#define IFLA_WIRELESS (IFLA_MASTER + 1)
+#endif /* IFLA_WIRELESS */
+
+/****************************** TYPES ******************************/
+
+/*
+ * Static information about wireless interface.
+ * We cache this info for performance reason.
+ */
+typedef struct wireless_iface
+{
+ /* Linked list */
+ struct wireless_iface * next;
+
+ /* Interface identification */
+ int ifindex; /* Interface index == black magic */
+
+ /* Interface data */
+ char ifname[IFNAMSIZ + 1]; /* Interface name */
+ struct iw_range range; /* Wireless static data */
+ int has_range;
+} wireless_iface;
+
+/**************************** VARIABLES ****************************/
+
+/* Cache of wireless interfaces */
+struct wireless_iface * interface_cache = NULL;
+
/************************ RTNETLINK HELPERS ************************/
/*
* The following code is extracted from :
return 0;
}
+/******************* WIRELESS INTERFACE DATABASE *******************/
+/*
+ * We keep a few information about each wireless interface on the
+ * system. This avoid to query this info at each event, therefore
+ * reducing overhead.
+ *
+ * Each interface is indexed by the 'ifindex'. As opposed to interface
+ * names, 'ifindex' are never reused (even if you reactivate the same
+ * hardware), so the data we cache will never apply to the wrong
+ * interface.
+ * Because of that, we are pretty lazy when it come to purging the
+ * cache...
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * Get name of interface based on interface index...
+ */
+static inline int
+index2name(int skfd,
+ int ifindex,
+ char * name)
+{
+ struct ifreq irq;
+ int ret = 0;
+
+ memset(name, 0, IFNAMSIZ + 1);
+
+ /* Get interface name */
+ irq.ifr_ifindex = ifindex;
+ if(ioctl(skfd, SIOCGIFNAME, &irq) < 0)
+ ret = -1;
+ else
+ strncpy(name, irq.ifr_name, IFNAMSIZ);
+
+ return(ret);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Get interface data from cache or live interface
+ */
+static struct wireless_iface *
+iw_get_interface_data(int ifindex)
+{
+ struct wireless_iface * curr;
+ int skfd = -1; /* ioctl socket */
+
+ /* Search for it in the database */
+ curr = interface_cache;
+ while(curr != NULL)
+ {
+ /* Match ? */
+ if(curr->ifindex == ifindex)
+ {
+ //printf("Cache : found %d-%s\n", curr->ifindex, curr->ifname);
+
+ /* Return */
+ return(curr);
+ }
+ /* Next entry */
+ curr = curr->next;
+ }
+
+ /* Create a channel to the NET kernel. Doesn't happen too often, so
+ * socket creation overhead is minimal... */
+ if((skfd = iw_sockets_open()) < 0)
+ {
+ perror("iw_sockets_open");
+ return(NULL);
+ }
+
+ /* Create new entry, zero, init */
+ curr = calloc(1, sizeof(struct wireless_iface));
+ if(!curr)
+ {
+ fprintf(stderr, "Malloc failed\n");
+ return(NULL);
+ }
+ curr->ifindex = ifindex;
+
+ /* Extract static data */
+ if(index2name(skfd, ifindex, curr->ifname) < 0)
+ {
+ perror("index2name");
+ free(curr);
+ return(NULL);
+ }
+ curr->has_range = (iw_get_range_info(skfd, curr->ifname, &curr->range) >= 0);
+ //printf("Cache : create %d-%s\n", curr->ifindex, curr->ifname);
+
+ /* Done */
+ iw_sockets_close(skfd);
+
+ /* Link it */
+ curr->next = interface_cache;
+ interface_cache = curr;
+
+ return(curr);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Remove interface data from cache (if it exist)
+ */
+static void
+iw_del_interface_data(int ifindex)
+{
+ struct wireless_iface * curr;
+ struct wireless_iface * prev = NULL;
+ struct wireless_iface * next;
+
+ /* Go through the list, find the interface, kills it */
+ curr = interface_cache;
+ while(curr)
+ {
+ next = curr->next;
+
+ /* Got a match ? */
+ if(curr->ifindex == ifindex)
+ {
+ /* Unlink. Root ? */
+ if(!prev)
+ interface_cache = next;
+ else
+ prev->next = next;
+ //printf("Cache : purge %d-%s\n", curr->ifindex, curr->ifname);
+
+ /* Destroy */
+ free(curr);
+ }
+ else
+ {
+ /* Keep as previous */
+ prev = curr;
+ }
+
+ /* Next entry */
+ curr = next;
+ }
+}
+
/********************* WIRELESS EVENT DECODING *********************/
/*
- * This is the bit I wrote...
+ * Parse the Wireless Event and print it out
*/
-#if WIRELESS_EXT > 13
/*------------------------------------------------------------------*/
/*
* Print one element from the scanning results
*/
static inline int
-print_event_token(struct iw_event * event, /* Extracted token */
- struct iw_range * iwrange, /* Range info */
+print_event_token(struct iw_event * event, /* Extracted token */
+ struct iw_range * iw_range, /* Range info */
int has_range)
{
char buffer[128]; /* Temporary buffer */
+ char * prefix = (IW_IS_GET(event->cmd) ? "New" : "Set");
/* Now, let's decode the event */
switch(event->cmd)
/* Events that result from a "SET XXX" operation by the user */
case SIOCSIWNWID:
if(event->u.nwid.disabled)
- printf("NWID:off/any\n");
+ printf("Set NWID:off/any\n");
else
- printf("NWID:%X\n", event->u.nwid.value);
+ printf("Set NWID:%X\n", event->u.nwid.value);
break;
case SIOCSIWFREQ:
+ case SIOCGIWFREQ:
{
- float freq; /* Frequency/channel */
+ double freq; /* Frequency/channel */
+ int channel = -1; /* Converted to channel */
freq = iw_freq2float(&(event->u.freq));
- iw_print_freq(buffer, freq);
- printf("%s\n", buffer);
+ if(has_range)
+ {
+ if(freq < KILO)
+ /* Convert channel to frequency if possible */
+ channel = iw_channel_to_freq((int) freq, &freq, iw_range);
+ else
+ /* Convert frequency to channel if possible */
+ channel = iw_freq_to_channel(freq, iw_range);
+ }
+ iw_print_freq(buffer, sizeof(buffer),
+ freq, channel, event->u.freq.flags);
+ printf("%s %s\n", prefix, buffer);
}
break;
case SIOCSIWMODE:
- printf("Mode:%s\n",
+ printf("Set Mode:%s\n",
iw_operation_mode[event->u.mode]);
break;
case SIOCSIWESSID:
+ case SIOCGIWESSID:
{
char essid[IW_ESSID_MAX_SIZE+1];
if((event->u.essid.pointer) && (event->u.essid.length))
{
/* Does it have an ESSID index ? */
if((event->u.essid.flags & IW_ENCODE_INDEX) > 1)
- printf("ESSID:\"%s\" [%d]\n", essid,
+ printf("%s ESSID:\"%s\" [%d]\n", prefix, essid,
(event->u.essid.flags & IW_ENCODE_INDEX));
else
- printf("ESSID:\"%s\"\n", essid);
+ printf("%s ESSID:\"%s\"\n", prefix, essid);
}
else
- printf("ESSID:off/any\n");
+ printf("%s ESSID:off/any\n", prefix);
}
break;
case SIOCSIWENCODE:
memcpy(key, event->u.essid.pointer, event->u.data.length);
else
event->u.data.flags |= IW_ENCODE_NOKEY;
- printf("Encryption key:");
+ printf("Set Encryption key:");
if(event->u.data.flags & IW_ENCODE_DISABLED)
printf("off\n");
else
{
/* Display the key */
- iw_print_key(buffer, key, event->u.data.length,
+ iw_print_key(buffer, sizeof(buffer), key, event->u.data.length,
event->u.data.flags);
printf("%s", buffer);
if((event->u.data.flags & IW_ENCODE_INDEX) > 1)
printf(" [%d]", event->u.data.flags & IW_ENCODE_INDEX);
if(event->u.data.flags & IW_ENCODE_RESTRICTED)
- printf(" Encryption mode:restricted");
+ printf(" Security mode:restricted");
if(event->u.data.flags & IW_ENCODE_OPEN)
- printf(" Encryption mode:open");
+ printf(" Security mode:open");
printf("\n");
}
}
break;
case IWEVTXDROP:
printf("Tx packet dropped:%s\n",
- iw_pr_ether(buffer, event->u.ap_addr.sa_data));
+ iw_pr_ether(buffer, event->u.addr.sa_data));
+ break;
+ case IWEVCUSTOM:
+ {
+ char custom[IW_CUSTOM_MAX+1];
+ if((event->u.data.pointer) && (event->u.data.length))
+ memcpy(custom, event->u.data.pointer, event->u.data.length);
+ custom[event->u.data.length] = '\0';
+ printf("Custom driver event:%s\n", custom);
+ }
+ break;
+ case IWEVREGISTERED:
+ printf("Registered node:%s\n",
+ iw_pr_ether(buffer, event->u.addr.sa_data));
+ break;
+ case IWEVEXPIRED:
+ printf("Expired node:%s\n",
+ iw_pr_ether(buffer, event->u.addr.sa_data));
+ break;
+ case SIOCGIWTHRSPY:
+ {
+ struct iw_thrspy threshold;
+ if((event->u.data.pointer) && (event->u.data.length))
+ {
+ memcpy(&threshold, event->u.data.pointer,
+ sizeof(struct iw_thrspy));
+ printf("Spy threshold crossed on address:%s\n",
+ iw_pr_ether(buffer, threshold.addr.sa_data));
+ iw_print_stats(buffer, sizeof(buffer),
+ &threshold.qual, iw_range, has_range);
+ printf(" Link %s\n", buffer);
+ }
+ else
+ printf("Invalid Spy Threshold event\n");
+ }
break;
/* ----- junk ----- */
/* other junk not currently in use */
case SIOCGIWRATE:
- iw_print_bitrate(buffer, event->u.bitrate.value);
- printf("Bit Rate:%s\n", buffer);
+ iw_print_bitrate(buffer, sizeof(buffer), event->u.bitrate.value);
+ printf("New Bit Rate:%s\n", buffer);
+ break;
+ case SIOCGIWNAME:
+ printf("Protocol:%-1.16s\n", event->u.name);
break;
case IWEVQUAL:
{
event->u.qual.updated = 0x0; /* Not that reliable, disable */
- iw_print_stats(buffer, &event->u.qual, iwrange, has_range);
+ iw_print_stats(buffer, sizeof(buffer),
+ &event->u.qual, iw_range, has_range);
printf("Link %s\n", buffer);
break;
}
* just make sure we read everything...
*/
static inline int
-print_event_stream(char * ifname,
+print_event_stream(int ifindex,
char * data,
int len)
{
int ret;
char buffer[64];
struct timeval recv_time;
-#if 0
- struct iw_range range;
- int has_range;
-#endif
+ struct wireless_iface * wireless_data;
-#if 0
- has_range = (iw_get_range_info(skfd, ifname, &range) < 0);
-#endif
+ /* Get data from cache */
+ wireless_data = iw_get_interface_data(ifindex);
+ if(wireless_data == NULL)
+ return(-1);
- /* In readable form */
+ /* Print received time in readable form */
gettimeofday(&recv_time, NULL);
- iw_print_timeval(buffer, &recv_time);
+ iw_print_timeval(buffer, sizeof(buffer), &recv_time);
iw_init_event_stream(&stream, data, len);
do
{
/* Extract an event and print it */
- ret = iw_extract_event_stream(&stream, &iwe);
+ ret = iw_extract_event_stream(&stream, &iwe,
+ wireless_data->range.we_version_compiled);
if(ret != 0)
{
if(i++ == 0)
- printf("%s %-8.8s ", buffer, ifname);
+ printf("%s %-8.16s ", buffer, wireless_data->ifname);
else
printf(" ");
if(ret > 0)
- print_event_token(&iwe, NULL, 0);
+ print_event_token(&iwe,
+ &wireless_data->range, wireless_data->has_range);
else
printf("(Invalid event)\n");
+ /* Push data out *now*, in case we are redirected to a pipe */
+ fflush(stdout);
}
}
while(ret > 0);
return(0);
}
-#endif /* WIRELESS_EXT > 13 */
/*********************** RTNETLINK EVENT DUMP***********************/
/*
/*
* Respond to a single RTM_NEWLINK event from the rtnetlink socket.
*/
-static inline int
-index2name(int index, char *name)
-{
- int skfd = -1; /* generic raw socket desc. */
- struct ifreq irq;
- int ret = 0;
-
- memset(name, 0, IFNAMSIZ + 1);
-
- /* Create a channel to the NET kernel. */
- if((skfd = iw_sockets_open()) < 0)
- {
- perror("socket");
- exit(-1);
- }
-
- /* Get interface name */
- irq.ifr_ifindex = index;
- if(ioctl(skfd, SIOCGIFNAME, &irq) < 0)
- ret = -1;
- else
- strncpy(name, irq.ifr_name, IFNAMSIZ);
-
- close(skfd);
- return(ret);
-}
-
-
-/*------------------------------------------------------------------*/
-/*
- * Respond to a single RTM_NEWLINK event from the rtnetlink socket.
- */
static int
LinkCatcher(struct nlmsghdr *nlh)
{
struct ifinfomsg* ifi;
- char ifname[IFNAMSIZ + 1];
#if 0
fprintf(stderr, "nlmsg_type = %d.\n", nlh->nlmsg_type);
#endif
- if(nlh->nlmsg_type != RTM_NEWLINK)
- return 0;
-
ifi = NLMSG_DATA(nlh);
- /* Get a name... */
- index2name(ifi->ifi_index, ifname);
-
-#if WIRELESS_EXT > 13
/* Code is ugly, but sort of works - Jean II */
+ /* If interface is getting destoyed */
+ if(nlh->nlmsg_type == RTM_DELLINK)
+ {
+ /* Remove from cache (if in cache) */
+ iw_del_interface_data(ifi->ifi_index);
+ return 0;
+ }
+
+ /* Only keep add/change events */
+ if(nlh->nlmsg_type != RTM_NEWLINK)
+ return 0;
+
/* Check for attributes */
- if (nlh->nlmsg_len > NLMSG_ALIGN(sizeof(struct ifinfomsg))) {
+ if (nlh->nlmsg_len > NLMSG_ALIGN(sizeof(struct ifinfomsg)))
+ {
int attrlen = nlh->nlmsg_len - NLMSG_ALIGN(sizeof(struct ifinfomsg));
- struct rtattr *attr = (void*)ifi + NLMSG_ALIGN(sizeof(struct ifinfomsg));
-
- while (RTA_OK(attr, attrlen)) {
- /* Check if the Wireless kind */
- if(attr->rta_type == IFLA_WIRELESS) {
- /* Go to display it */
- print_event_stream(ifname,
- (void *)attr + RTA_ALIGN(sizeof(struct rtattr)),
- attr->rta_len - RTA_ALIGN(sizeof(struct rtattr)));
+ struct rtattr *attr = (void *) ((char *) ifi +
+ NLMSG_ALIGN(sizeof(struct ifinfomsg)));
+
+ while (RTA_OK(attr, attrlen))
+ {
+ /* Check if the Wireless kind */
+ if(attr->rta_type == IFLA_WIRELESS)
+ {
+ /* Go to display it */
+ print_event_stream(ifi->ifi_index,
+ (char *) attr + RTA_ALIGN(sizeof(struct rtattr)),
+ attr->rta_len - RTA_ALIGN(sizeof(struct rtattr)));
+ }
+ attr = RTA_NEXT(attr, attrlen);
}
- attr = RTA_NEXT(attr, attrlen);
- }
- }
-#endif /* WIRELESS_EXT > 13 */
+ }
return 0;
}
while(1)
{
struct sockaddr_nl sanl;
- socklen_t sanllen;
+ socklen_t sanllen = sizeof(struct sockaddr_nl);
struct nlmsghdr *h;
int amt;
switch(h->nlmsg_type)
{
case RTM_NEWLINK:
+ case RTM_DELLINK:
LinkCatcher(h);
break;
default:
fputs("Usage: iwevent [OPTIONS]\n"
" Monitors and displays Wireless Events.\n"
" Options are:\n"
- " -h,--help Print this message.\n",
+ " -h,--help Print this message.\n"
+ " -v,--version Show version of this program.\n",
status ? stderr : stdout);
exit(status);
}
/* Command line options */
static const struct option long_opts[] = {
{ "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, 'v' },
{ NULL, 0, NULL, 0 }
};
int opt;
/* Check command line options */
- while((opt = getopt_long(argc, argv, "h", long_opts, NULL)) > 0)
+ while((opt = getopt_long(argc, argv, "hv", long_opts, NULL)) > 0)
{
switch(opt)
{
iw_usage(0);
break;
+ case 'v':
+ return(iw_print_version_info("iwevent"));
+ break;
+
default:
iw_usage(1);
break;
return(1);
}
+ fprintf(stderr, "Waiting for Wireless Events from interfaces...\n");
+
/* Do what we have to do */
wait_for_event(&rth);