| /* |
| * Wireless Tools |
| * |
| * Jean II - HPL 99->01 |
| * |
| * 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-2002 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> |
| //#define MSG_DONTWAIT 0x40 |
| /************************ 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) |
| { |
| socklen_t 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, &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 EVENT DECODING *********************/ |
| /* |
| * This is the bit I wrote... |
| */ |
| |
| #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 */ |
| int has_range) |
| { |
| char buffer[128]; /* Temporary buffer */ |
| |
| /* 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("NWID:off/any\n"); |
| else |
| printf("NWID:%X\n", event->u.nwid.value); |
| break; |
| case SIOCSIWFREQ: |
| { |
| float freq; /* Frequency/channel */ |
| freq = iw_freq2float(&(event->u.freq)); |
| iw_print_freq(buffer, freq); |
| printf("%s\n", buffer); |
| } |
| break; |
| case SIOCSIWMODE: |
| printf("Mode:%s\n", |
| iw_operation_mode[event->u.mode]); |
| break; |
| case SIOCSIWESSID: |
| { |
| char essid[IW_ESSID_MAX_SIZE+1]; |
| if((event->u.essid.pointer) && (event->u.essid.length)) |
| memcpy(essid, event->u.essid.pointer, event->u.essid.length); |
| essid[event->u.essid.length] = '\0'; |
| if(event->u.essid.flags) |
| { |
| /* Does it have an ESSID index ? */ |
| if((event->u.essid.flags & IW_ENCODE_INDEX) > 1) |
| printf("ESSID:\"%s\" [%d]\n", essid, |
| (event->u.essid.flags & IW_ENCODE_INDEX)); |
| else |
| printf("ESSID:\"%s\"\n", essid); |
| } |
| else |
| printf("ESSID:off/any\n"); |
| } |
| break; |
| case SIOCSIWENCODE: |
| { |
| unsigned char key[IW_ENCODING_TOKEN_MAX]; |
| if(event->u.data.pointer) |
| memcpy(key, event->u.essid.pointer, event->u.data.length); |
| else |
| event->u.data.flags |= IW_ENCODE_NOKEY; |
| printf("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, |
| 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(" Encryption mode:restricted"); |
| if(event->u.data.flags & IW_ENCODE_OPEN) |
| printf(" Encryption 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_pr_ether(buffer, (unsigned char *)event->u.ap_addr.sa_data)); |
| break; |
| case SIOCGIWSCAN: |
| printf("Scan request completed\n"); |
| break; |
| case IWEVTXDROP: |
| printf("Tx packet dropped:%s\n", |
| iw_pr_ether(buffer, (unsigned char *)event->u.addr.sa_data)); |
| break; |
| #if WIRELESS_EXT > 14 |
| 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, (unsigned char *)event->u.addr.sa_data)); |
| break; |
| case IWEVEXPIRED: |
| printf("Expired node:%s\n", |
| iw_pr_ether(buffer, (unsigned char *)event->u.addr.sa_data)); |
| break; |
| #endif /* WIRELESS_EXT > 14 */ |
| /* ----- junk ----- */ |
| /* other junk not currently in use */ |
| case SIOCGIWRATE: |
| iw_print_bitrate(buffer, event->u.bitrate.value); |
| printf("Bit Rate:%s\n", buffer); |
| break; |
| case IWEVQUAL: |
| { |
| event->u.qual.updated = 0x0; /* Not that reliable, disable */ |
| iw_print_stats(buffer, &event->u.qual, iwrange, 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(char * ifname, |
| char * data, |
| int len) |
| { |
| struct iw_event iwe; |
| struct stream_descr stream; |
| int i = 0; |
| int ret; |
| char buffer[64]; |
| struct timeval recv_time; |
| #if 0 |
| struct iw_range range; |
| int has_range; |
| #endif |
| |
| #if 0 |
| has_range = (iw_get_range_info(skfd, ifname, &range) < 0); |
| #endif |
| |
| /* In readable form */ |
| gettimeofday(&recv_time, NULL); |
| iw_print_timeval(buffer, &recv_time); |
| |
| iw_init_event_stream(&stream, data, len); |
| do |
| { |
| /* Extract an event and print it */ |
| ret = iw_extract_event_stream(&stream, &iwe); |
| if(ret != 0) |
| { |
| if(i++ == 0) |
| printf("%s %-8.8s ", buffer, ifname); |
| else |
| printf(" "); |
| if(ret > 0) |
| print_event_token(&iwe, NULL, 0); |
| else |
| printf("(Invalid event)\n"); |
| } |
| } |
| while(ret > 0); |
| |
| return(0); |
| } |
| #endif /* WIRELESS_EXT > 13 */ |
| |
| /*********************** 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 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 */ |
| |
| /* 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*)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))); |
| } |
| attr = RTA_NEXT(attr, attrlen); |
| } |
| } |
| #endif /* WIRELESS_EXT > 13 */ |
| |
| 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; |
| |
| 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: |
| 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); |
| } |
| |
| #if WIRELESS_EXT > 13 |
| fprintf(stderr, "Waiting for Wireless Events...\n"); |
| #else /* WIRELESS_EXT > 13 */ |
| fprintf(stderr, "Unsupported in Wireless Extensions <= 14 :-(\n"); |
| return(-1); |
| #endif /* WIRELESS_EXT > 13 */ |
| |
| /* Do what we have to do */ |
| wait_for_event(&rth); |
| |
| /* Cleanup - only if you are pedantic */ |
| rtnl_close(&rth); |
| |
| return(0); |
| } |