123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804 |
- /*
- * Wireless Tools
- *
- * 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".
- *
- * Part of this code is from Alexey Kuznetsov, part is from Casey Carter,
- * I've just put the pieces together...
- * By the way, if you know a way to remove the root restrictions, tell me
- * about it...
- *
- * This file is released under the GPL license.
- * Copyright (c) 1997-2004 Jean Tourrilhes <jt@hpl.hp.com>
- */
- /***************************** INCLUDES *****************************/
- #include "iwlib.h" /* Header */
- #include <linux/netlink.h>
- #include <linux/rtnetlink.h>
- #include <getopt.h>
- #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 :
- * ----------------------------------------------
- * libnetlink.c RTnetlink service routines.
- *
- * This program 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 of the License, or (at your option) any later version.
- *
- * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
- * -----------------------------------------------
- */
- struct rtnl_handle
- {
- int fd;
- struct sockaddr_nl local;
- struct sockaddr_nl peer;
- __u32 seq;
- __u32 dump;
- };
- static inline void rtnl_close(struct rtnl_handle *rth)
- {
- close(rth->fd);
- }
- static inline int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions)
- {
- int addr_len;
- memset(rth, 0, sizeof(rth));
- rth->fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
- if (rth->fd < 0) {
- perror("Cannot open netlink socket");
- return -1;
- }
- memset(&rth->local, 0, sizeof(rth->local));
- rth->local.nl_family = AF_NETLINK;
- rth->local.nl_groups = subscriptions;
- if (bind(rth->fd, (struct sockaddr*)&rth->local, sizeof(rth->local)) < 0) {
- perror("Cannot bind netlink socket");
- return -1;
- }
- addr_len = sizeof(rth->local);
- if (getsockname(rth->fd, (struct sockaddr*)&rth->local,
- (socklen_t *) &addr_len) < 0) {
- perror("Cannot getsockname");
- return -1;
- }
- if (addr_len != sizeof(rth->local)) {
- fprintf(stderr, "Wrong address length %d\n", addr_len);
- return -1;
- }
- if (rth->local.nl_family != AF_NETLINK) {
- fprintf(stderr, "Wrong address family %d\n", rth->local.nl_family);
- return -1;
- }
- rth->seq = time(NULL);
- 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 *********************/
- /*
- * Parse the Wireless Event and print it out
- */
- /*------------------------------------------------------------------*/
- /*
- * Dump a buffer as a serie of hex
- * Maybe should go in iwlib...
- * Maybe we should have better formatting like iw_print_key...
- */
- static char *
- iw_hexdump(char * buf,
- size_t buflen,
- const unsigned char *data,
- size_t datalen)
- {
- size_t i;
- char * pos = buf;
- for(i = 0; i < datalen; i++)
- pos += snprintf(pos, buf + buflen - pos, "%02X", data[i]);
- return buf;
- }
- /*------------------------------------------------------------------*/
- /*
- * Print one element from the scanning results
- */
- static inline int
- 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 buffer2[30]; /* Temporary buffer */
- char * prefix = (IW_IS_GET(event->cmd) ? "New" : "Set");
- /* Now, let's decode the event */
- switch(event->cmd)
- {
- /* ----- set events ----- */
- /* Events that result from a "SET XXX" operation by the user */
- case SIOCSIWNWID:
- if(event->u.nwid.disabled)
- printf("Set NWID:off/any\n");
- else
- printf("Set NWID:%X\n", event->u.nwid.value);
- break;
- case SIOCSIWFREQ:
- case SIOCGIWFREQ:
- {
- double freq; /* Frequency/channel */
- int channel = -1; /* Converted to channel */
- freq = iw_freq2float(&(event->u.freq));
- 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("Set Mode:%s\n",
- iw_operation_mode[event->u.mode]);
- break;
- case SIOCSIWESSID:
- case SIOCGIWESSID:
- {
- char essid[IW_ESSID_MAX_SIZE+1];
- memset(essid, '\0', sizeof(essid));
- if((event->u.essid.pointer) && (event->u.essid.length))
- memcpy(essid, event->u.essid.pointer, event->u.essid.length);
- if(event->u.essid.flags)
- {
- /* Does it have an ESSID index ? */
- if((event->u.essid.flags & IW_ENCODE_INDEX) > 1)
- printf("%s ESSID:\"%s\" [%d]\n", prefix, essid,
- (event->u.essid.flags & IW_ENCODE_INDEX));
- else
- printf("%s ESSID:\"%s\"\n", prefix, essid);
- }
- else
- printf("%s ESSID:off/any\n", prefix);
- }
- break;
- case SIOCSIWENCODE:
- {
- unsigned char key[IW_ENCODING_TOKEN_MAX];
- if(event->u.data.pointer)
- memcpy(key, event->u.data.pointer, event->u.data.length);
- else
- event->u.data.flags |= IW_ENCODE_NOKEY;
- printf("Set Encryption key:");
- if(event->u.data.flags & IW_ENCODE_DISABLED)
- printf("off\n");
- else
- {
- /* Display the key */
- iw_print_key(buffer, sizeof(buffer), key, event->u.data.length,
- event->u.data.flags);
- printf("%s", buffer);
- /* Other info... */
- 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(" Security mode:restricted");
- if(event->u.data.flags & IW_ENCODE_OPEN)
- printf(" Security mode:open");
- printf("\n");
- }
- }
- break;
- /* ----- driver events ----- */
- /* Events generated by the driver when something important happens */
- case SIOCGIWAP:
- printf("New Access Point/Cell address:%s\n",
- iw_sawap_ntop(&event->u.ap_addr, buffer));
- break;
- case SIOCGIWSCAN:
- printf("Scan request completed\n");
- break;
- case IWEVTXDROP:
- printf("Tx packet dropped:%s\n",
- iw_saether_ntop(&event->u.addr, buffer));
- break;
- case IWEVCUSTOM:
- {
- char custom[IW_CUSTOM_MAX+1];
- memset(custom, '\0', sizeof(custom));
- if((event->u.data.pointer) && (event->u.data.length))
- memcpy(custom, event->u.data.pointer, event->u.data.length);
- printf("Custom driver event:%s\n", custom);
- }
- break;
- case IWEVREGISTERED:
- printf("Registered node:%s\n",
- iw_saether_ntop(&event->u.addr, buffer));
- break;
- case IWEVEXPIRED:
- printf("Expired node:%s\n",
- iw_saether_ntop(&event->u.addr, buffer));
- 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_saether_ntop(&threshold.addr, buffer));
- 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;
- /* ----- driver WPA events ----- */
- /* Events generated by the driver, used for WPA operation */
- case IWEVMICHAELMICFAILURE:
- if(event->u.data.length >= sizeof(struct iw_michaelmicfailure))
- {
- struct iw_michaelmicfailure mf;
- memcpy(&mf, event->u.data.pointer, sizeof(mf));
- printf("Michael MIC failure flags:0x%X src_addr:%s tsc:%s\n",
- mf.flags,
- iw_saether_ntop(&mf.src_addr, buffer2),
- iw_hexdump(buffer, sizeof(buffer),
- mf.tsc, IW_ENCODE_SEQ_MAX_SIZE));
- }
- break;
- case IWEVASSOCREQIE:
- printf("Association Request IEs:%s\n",
- iw_hexdump(buffer, sizeof(buffer),
- event->u.data.pointer, event->u.data.length));
- break;
- case IWEVASSOCRESPIE:
- printf("Association Response IEs:%s\n",
- iw_hexdump(buffer, sizeof(buffer),
- event->u.data.pointer, event->u.data.length));
- break;
- case IWEVPMKIDCAND:
- if(event->u.data.length >= sizeof(struct iw_pmkid_cand))
- {
- struct iw_pmkid_cand cand;
- memcpy(&cand, event->u.data.pointer, sizeof(cand));
- printf("PMKID candidate flags:0x%X index:%d bssid:%s\n",
- cand.flags, cand.index,
- iw_saether_ntop(&cand.bssid, buffer));
- }
- break;
- /* ----- junk ----- */
- /* other junk not currently in use */
- case SIOCGIWRATE:
- 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, sizeof(buffer),
- &event->u.qual, iw_range, has_range);
- printf("Link %s\n", buffer);
- break;
- }
- default:
- printf("(Unknown Wireless event 0x%04X)\n", event->cmd);
- } /* switch(event->cmd) */
- return(0);
- }
- /*------------------------------------------------------------------*/
- /*
- * Print out all Wireless Events part of the RTNetlink message
- * Most often, there will be only one event per message, but
- * just make sure we read everything...
- */
- static inline int
- print_event_stream(int ifindex,
- char * data,
- int len)
- {
- struct iw_event iwe;
- struct stream_descr stream;
- int i = 0;
- int ret;
- char buffer[64];
- struct timeval recv_time;
- struct timezone tz;
- struct wireless_iface * wireless_data;
- /* Get data from cache */
- wireless_data = iw_get_interface_data(ifindex);
- if(wireless_data == NULL)
- return(-1);
- /* Print received time in readable form */
- gettimeofday(&recv_time, &tz);
- iw_print_timeval(buffer, sizeof(buffer), &recv_time, &tz);
- iw_init_event_stream(&stream, data, len);
- do
- {
- /* Extract an event and print it */
- ret = iw_extract_event_stream(&stream, &iwe,
- wireless_data->range.we_version_compiled);
- if(ret != 0)
- {
- if(i++ == 0)
- printf("%s %-8.16s ", buffer, wireless_data->ifname);
- else
- printf(" ");
- if(ret > 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);
- }
- /*********************** RTNETLINK EVENT DUMP***********************/
- /*
- * Dump the events we receive from rtnetlink
- * This code is mostly from Casey
- */
- /*------------------------------------------------------------------*/
- /*
- * Respond to a single RTM_NEWLINK event from the rtnetlink socket.
- */
- static int
- LinkCatcher(struct nlmsghdr *nlh)
- {
- struct ifinfomsg* ifi;
- #if 0
- fprintf(stderr, "nlmsg_type = %d.\n", nlh->nlmsg_type);
- #endif
- ifi = NLMSG_DATA(nlh);
- /* 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)))
- {
- int attrlen = nlh->nlmsg_len - NLMSG_ALIGN(sizeof(struct ifinfomsg));
- 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);
- }
- }
- return 0;
- }
- /* ---------------------------------------------------------------- */
- /*
- * We must watch the rtnelink socket for events.
- * This routine handles those events (i.e., call this when rth.fd
- * is ready to read).
- */
- static inline void
- handle_netlink_events(struct rtnl_handle * rth)
- {
- while(1)
- {
- struct sockaddr_nl sanl;
- socklen_t sanllen = sizeof(struct sockaddr_nl);
- struct nlmsghdr *h;
- int amt;
- char buf[8192];
- amt = recvfrom(rth->fd, buf, sizeof(buf), MSG_DONTWAIT, (struct sockaddr*)&sanl, &sanllen);
- if(amt < 0)
- {
- if(errno != EINTR && errno != EAGAIN)
- {
- fprintf(stderr, "%s: error reading netlink: %s.\n",
- __PRETTY_FUNCTION__, strerror(errno));
- }
- return;
- }
- if(amt == 0)
- {
- fprintf(stderr, "%s: EOF on netlink??\n", __PRETTY_FUNCTION__);
- return;
- }
- h = (struct nlmsghdr*)buf;
- while(amt >= (int)sizeof(*h))
- {
- int len = h->nlmsg_len;
- int l = len - sizeof(*h);
- if(l < 0 || len > amt)
- {
- fprintf(stderr, "%s: malformed netlink message: len=%d\n", __PRETTY_FUNCTION__, len);
- break;
- }
- switch(h->nlmsg_type)
- {
- case RTM_NEWLINK:
- case RTM_DELLINK:
- LinkCatcher(h);
- break;
- default:
- #if 0
- fprintf(stderr, "%s: got nlmsg of type %#x.\n", __PRETTY_FUNCTION__, h->nlmsg_type);
- #endif
- break;
- }
- len = NLMSG_ALIGN(len);
- amt -= len;
- h = (struct nlmsghdr*)((char*)h + len);
- }
- if(amt > 0)
- fprintf(stderr, "%s: remnant of size %d on netlink\n", __PRETTY_FUNCTION__, amt);
- }
- }
- /**************************** MAIN LOOP ****************************/
- /* ---------------------------------------------------------------- */
- /*
- * Wait until we get an event
- */
- static inline int
- wait_for_event(struct rtnl_handle * rth)
- {
- #if 0
- struct timeval tv; /* Select timeout */
- #endif
- /* Forever */
- while(1)
- {
- fd_set rfds; /* File descriptors for select */
- int last_fd; /* Last fd */
- int ret;
- /* Guess what ? We must re-generate rfds each time */
- FD_ZERO(&rfds);
- FD_SET(rth->fd, &rfds);
- last_fd = rth->fd;
- /* Wait until something happens */
- ret = select(last_fd + 1, &rfds, NULL, NULL, NULL);
- /* Check if there was an error */
- if(ret < 0)
- {
- if(errno == EAGAIN || errno == EINTR)
- continue;
- fprintf(stderr, "Unhandled signal - exiting...\n");
- break;
- }
- /* Check if there was a timeout */
- if(ret == 0)
- {
- continue;
- }
- /* Check for interface discovery events. */
- if(FD_ISSET(rth->fd, &rfds))
- handle_netlink_events(rth);
- }
- return(0);
- }
- /******************************* MAIN *******************************/
- /* ---------------------------------------------------------------- */
- /*
- * helper ;-)
- */
- static void
- iw_usage(int status)
- {
- fputs("Usage: iwevent [OPTIONS]\n"
- " Monitors and displays Wireless Events.\n"
- " Options are:\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 }
- };
- /* ---------------------------------------------------------------- */
- /*
- * main body of the program
- */
- int
- main(int argc,
- char * argv[])
- {
- struct rtnl_handle rth;
- int opt;
- /* Check command line options */
- while((opt = getopt_long(argc, argv, "hv", long_opts, NULL)) > 0)
- {
- switch(opt)
- {
- case 'h':
- iw_usage(0);
- break;
- case 'v':
- return(iw_print_version_info("iwevent"));
- break;
- default:
- iw_usage(1);
- break;
- }
- }
- if(optind < argc)
- {
- fputs("Too many arguments.\n", stderr);
- iw_usage(1);
- }
- /* Open netlink channel */
- if(rtnl_open(&rth, RTMGRP_LINK) < 0)
- {
- perror("Can't initialize rtnetlink socket");
- return(1);
- }
- fprintf(stderr, "Waiting for Wireless Events from interfaces...\n");
- /* Do what we have to do */
- wait_for_event(&rth);
- /* Cleanup - only if you are pedantic */
- rtnl_close(&rth);
- return(0);
- }
|