OSDN Git Service

v27
[android-x86/external-wireless-tools.git] / wireless_tools / iwevent.c
1 /*
2  *      Wireless Tools
3  *
4  *              Jean II - HPL 99->04
5  *
6  * Main code for "iwevent". This listent for wireless events on rtnetlink.
7  * You need to link this code against "iwcommon.c" and "-lm".
8  *
9  * Part of this code is from Alexey Kuznetsov, part is from Casey Carter,
10  * I've just put the pieces together...
11  * By the way, if you know a way to remove the root restrictions, tell me
12  * about it...
13  *
14  * This file is released under the GPL license.
15  *     Copyright (c) 1997-2004 Jean Tourrilhes <jt@hpl.hp.com>
16  */
17
18 /***************************** INCLUDES *****************************/
19
20 #include "iwlib.h"              /* Header */
21
22 #include <linux/netlink.h>
23 #include <linux/rtnetlink.h>
24
25 #include <getopt.h>
26 #include <time.h>
27 #include <sys/time.h>
28
29 /* Ugly backward compatibility :-( */
30 #ifndef IFLA_WIRELESS
31 #define IFLA_WIRELESS   (IFLA_MASTER + 1)
32 #endif /* IFLA_WIRELESS */
33
34 /****************************** TYPES ******************************/
35
36 /*
37  * Static information about wireless interface.
38  * We cache this info for performance reason.
39  */
40 typedef struct wireless_iface
41 {
42   /* Linked list */
43   struct wireless_iface *       next;
44
45   /* Interface identification */
46   int           ifindex;                /* Interface index == black magic */
47
48   /* Interface data */
49   char                  ifname[IFNAMSIZ + 1];   /* Interface name */
50   struct iw_range       range;                  /* Wireless static data */
51   int                   has_range;
52 } wireless_iface;
53
54 /**************************** VARIABLES ****************************/
55
56 /* Cache of wireless interfaces */
57 struct wireless_iface * interface_cache = NULL;
58
59 /************************ RTNETLINK HELPERS ************************/
60 /*
61  * The following code is extracted from :
62  * ----------------------------------------------
63  * libnetlink.c RTnetlink service routines.
64  *
65  *              This program is free software; you can redistribute it and/or
66  *              modify it under the terms of the GNU General Public License
67  *              as published by the Free Software Foundation; either version
68  *              2 of the License, or (at your option) any later version.
69  *
70  * Authors:     Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
71  * -----------------------------------------------
72  */
73
74 struct rtnl_handle
75 {
76         int                     fd;
77         struct sockaddr_nl      local;
78         struct sockaddr_nl      peer;
79         __u32                   seq;
80         __u32                   dump;
81 };
82
83 static inline void rtnl_close(struct rtnl_handle *rth)
84 {
85         close(rth->fd);
86 }
87
88 static inline int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions)
89 {
90         int addr_len;
91
92         memset(rth, 0, sizeof(rth));
93
94         rth->fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
95         if (rth->fd < 0) {
96                 perror("Cannot open netlink socket");
97                 return -1;
98         }
99
100         memset(&rth->local, 0, sizeof(rth->local));
101         rth->local.nl_family = AF_NETLINK;
102         rth->local.nl_groups = subscriptions;
103
104         if (bind(rth->fd, (struct sockaddr*)&rth->local, sizeof(rth->local)) < 0) {
105                 perror("Cannot bind netlink socket");
106                 return -1;
107         }
108         addr_len = sizeof(rth->local);
109         if (getsockname(rth->fd, (struct sockaddr*)&rth->local, &addr_len) < 0) {
110                 perror("Cannot getsockname");
111                 return -1;
112         }
113         if (addr_len != sizeof(rth->local)) {
114                 fprintf(stderr, "Wrong address length %d\n", addr_len);
115                 return -1;
116         }
117         if (rth->local.nl_family != AF_NETLINK) {
118                 fprintf(stderr, "Wrong address family %d\n", rth->local.nl_family);
119                 return -1;
120         }
121         rth->seq = time(NULL);
122         return 0;
123 }
124
125 /******************* WIRELESS INTERFACE DATABASE *******************/
126 /*
127  * We keep a few information about each wireless interface on the
128  * system. This avoid to query this info at each event, therefore
129  * reducing overhead.
130  *
131  * Each interface is indexed by the 'ifindex'. As opposed to interface
132  * names, 'ifindex' are never reused (even if you reactivate the same
133  * hardware), so the data we cache will never apply to the wrong
134  * interface.
135  * Because of that, we are pretty lazy when it come to purging the
136  * cache...
137  */
138
139 /*------------------------------------------------------------------*/
140 /*
141  * Get name of interface based on interface index...
142  */
143 static inline int
144 index2name(int          skfd,
145            int          ifindex,
146            char *       name)
147 {
148   struct ifreq  irq;
149   int           ret = 0;
150
151   memset(name, 0, IFNAMSIZ + 1);
152
153   /* Get interface name */
154   irq.ifr_ifindex = ifindex;
155   if(ioctl(skfd, SIOCGIFNAME, &irq) < 0)
156     ret = -1;
157   else
158     strncpy(name, irq.ifr_name, IFNAMSIZ);
159
160   return(ret);
161 }
162
163 /*------------------------------------------------------------------*/
164 /*
165  * Get interface data from cache or live interface
166  */
167 static struct wireless_iface *
168 iw_get_interface_data(int       ifindex)
169 {
170   struct wireless_iface *       curr;
171   int                           skfd = -1;      /* ioctl socket */
172
173   /* Search for it in the database */
174   curr = interface_cache;
175   while(curr != NULL)
176     {
177       /* Match ? */
178       if(curr->ifindex == ifindex)
179         {
180           //printf("Cache : found %d-%s\n", curr->ifindex, curr->ifname);
181
182           /* Return */
183           return(curr);
184         }
185       /* Next entry */
186       curr = curr->next;
187     }
188
189   /* Create a channel to the NET kernel. Doesn't happen too often, so
190    * socket creation overhead is minimal... */
191   if((skfd = iw_sockets_open()) < 0)
192     {
193       perror("iw_sockets_open");
194       return(NULL);
195     }
196
197   /* Create new entry, zero, init */
198   curr = calloc(1, sizeof(struct wireless_iface));
199   if(!curr)
200     {
201       fprintf(stderr, "Malloc failed\n");
202       return(NULL);
203     }
204   curr->ifindex = ifindex;
205
206   /* Extract static data */
207   if(index2name(skfd, ifindex, curr->ifname) < 0)
208     {
209       perror("index2name");
210       free(curr);
211       return(NULL);
212     }
213   curr->has_range = (iw_get_range_info(skfd, curr->ifname, &curr->range) >= 0);
214   //printf("Cache : create %d-%s\n", curr->ifindex, curr->ifname);
215
216   /* Done */
217   iw_sockets_close(skfd);
218
219   /* Link it */
220   curr->next = interface_cache;
221   interface_cache = curr;
222
223   return(curr);
224 }
225
226 /*------------------------------------------------------------------*/
227 /*
228  * Remove interface data from cache (if it exist)
229  */
230 static void
231 iw_del_interface_data(int       ifindex)
232 {
233   struct wireless_iface *       curr;
234   struct wireless_iface *       prev = NULL;
235   struct wireless_iface *       next;
236
237   /* Go through the list, find the interface, kills it */
238   curr = interface_cache;
239   while(curr)
240     {
241       next = curr->next;
242
243       /* Got a match ? */
244       if(curr->ifindex == ifindex)
245         {
246           /* Unlink. Root ? */
247           if(!prev)
248             interface_cache = next;
249           else
250             prev->next = next;
251           //printf("Cache : purge %d-%s\n", curr->ifindex, curr->ifname);
252
253           /* Destroy */
254           free(curr);
255         }
256       else
257         {
258           /* Keep as previous */
259           prev = curr;
260         }
261
262       /* Next entry */
263       curr = next;
264     }
265 }
266
267 /********************* WIRELESS EVENT DECODING *********************/
268 /*
269  * Parse the Wireless Event and print it out
270  */
271
272 /*------------------------------------------------------------------*/
273 /*
274  * Print one element from the scanning results
275  */
276 static inline int
277 print_event_token(struct iw_event *     event,          /* Extracted token */
278                   struct iw_range *     iw_range,       /* Range info */
279                   int                   has_range)
280 {
281   char          buffer[128];    /* Temporary buffer */
282   char *        prefix = (IW_IS_GET(event->cmd) ? "New" : "Set");
283
284   /* Now, let's decode the event */
285   switch(event->cmd)
286     {
287       /* ----- set events ----- */
288       /* Events that result from a "SET XXX" operation by the user */
289     case SIOCSIWNWID:
290       if(event->u.nwid.disabled)
291         printf("Set NWID:off/any\n");
292       else
293         printf("Set NWID:%X\n", event->u.nwid.value);
294       break;
295     case SIOCSIWFREQ:
296     case SIOCGIWFREQ:
297       {
298         double          freq;                   /* Frequency/channel */
299         int             channel = -1;           /* Converted to channel */
300         freq = iw_freq2float(&(event->u.freq));
301         if(has_range)
302           {
303             if(freq < KILO)
304               /* Convert channel to frequency if possible */
305               channel = iw_channel_to_freq((int) freq, &freq, iw_range);
306             else
307               /* Convert frequency to channel if possible */
308               channel = iw_freq_to_channel(freq, iw_range);
309           }
310         iw_print_freq(buffer, sizeof(buffer),
311                       freq, channel, event->u.freq.flags);
312         printf("%s %s\n", prefix, buffer);
313       }
314       break;
315     case SIOCSIWMODE:
316       printf("Set Mode:%s\n",
317              iw_operation_mode[event->u.mode]);
318       break;
319     case SIOCSIWESSID:
320     case SIOCGIWESSID:
321       {
322         char essid[IW_ESSID_MAX_SIZE+1];
323         if((event->u.essid.pointer) && (event->u.essid.length))
324           memcpy(essid, event->u.essid.pointer, event->u.essid.length);
325         essid[event->u.essid.length] = '\0';
326         if(event->u.essid.flags)
327           {
328             /* Does it have an ESSID index ? */
329             if((event->u.essid.flags & IW_ENCODE_INDEX) > 1)
330               printf("%s ESSID:\"%s\" [%d]\n", prefix, essid,
331                      (event->u.essid.flags & IW_ENCODE_INDEX));
332             else
333               printf("%s ESSID:\"%s\"\n", prefix, essid);
334           }
335         else
336           printf("%s ESSID:off/any\n", prefix);
337       }
338       break;
339     case SIOCSIWENCODE:
340       {
341         unsigned char   key[IW_ENCODING_TOKEN_MAX];
342         if(event->u.data.pointer)
343           memcpy(key, event->u.essid.pointer, event->u.data.length);
344         else
345           event->u.data.flags |= IW_ENCODE_NOKEY;
346         printf("Set Encryption key:");
347         if(event->u.data.flags & IW_ENCODE_DISABLED)
348           printf("off\n");
349         else
350           {
351             /* Display the key */
352             iw_print_key(buffer, sizeof(buffer), key, event->u.data.length,
353                          event->u.data.flags);
354             printf("%s", buffer);
355
356             /* Other info... */
357             if((event->u.data.flags & IW_ENCODE_INDEX) > 1)
358               printf(" [%d]", event->u.data.flags & IW_ENCODE_INDEX);
359             if(event->u.data.flags & IW_ENCODE_RESTRICTED)
360               printf("   Security mode:restricted");
361             if(event->u.data.flags & IW_ENCODE_OPEN)
362               printf("   Security mode:open");
363             printf("\n");
364           }
365       }
366       break;
367       /* ----- driver events ----- */
368       /* Events generated by the driver when something important happens */
369     case SIOCGIWAP:
370       printf("New Access Point/Cell address:%s\n",
371              iw_pr_ether(buffer, event->u.ap_addr.sa_data));
372       break;
373     case SIOCGIWSCAN:
374       printf("Scan request completed\n");
375       break;
376     case IWEVTXDROP:
377       printf("Tx packet dropped:%s\n",
378              iw_pr_ether(buffer, event->u.addr.sa_data));
379       break;
380     case IWEVCUSTOM:
381       {
382         char custom[IW_CUSTOM_MAX+1];
383         if((event->u.data.pointer) && (event->u.data.length))
384           memcpy(custom, event->u.data.pointer, event->u.data.length);
385         custom[event->u.data.length] = '\0';
386         printf("Custom driver event:%s\n", custom);
387       }
388       break;
389     case IWEVREGISTERED:
390       printf("Registered node:%s\n",
391              iw_pr_ether(buffer, event->u.addr.sa_data));
392       break;
393     case IWEVEXPIRED:
394       printf("Expired node:%s\n",
395              iw_pr_ether(buffer, event->u.addr.sa_data));
396       break;
397     case SIOCGIWTHRSPY:
398       {
399         struct iw_thrspy        threshold;
400         if((event->u.data.pointer) && (event->u.data.length))
401           {
402             memcpy(&threshold, event->u.data.pointer,
403                    sizeof(struct iw_thrspy));
404             printf("Spy threshold crossed on address:%s\n",
405                    iw_pr_ether(buffer, threshold.addr.sa_data));
406             iw_print_stats(buffer, sizeof(buffer),
407                            &threshold.qual, iw_range, has_range);
408             printf("                            Link %s\n", buffer);
409           }
410         else
411           printf("Invalid Spy Threshold event\n");
412       }
413       break;
414       /* ----- junk ----- */
415       /* other junk not currently in use */
416     case SIOCGIWRATE:
417       iw_print_bitrate(buffer, sizeof(buffer), event->u.bitrate.value);
418       printf("New Bit Rate:%s\n", buffer);
419       break;
420     case SIOCGIWNAME:
421       printf("Protocol:%-1.16s\n", event->u.name);
422       break;
423     case IWEVQUAL:
424       {
425         event->u.qual.updated = 0x0;    /* Not that reliable, disable */
426         iw_print_stats(buffer, sizeof(buffer),
427                        &event->u.qual, iw_range, has_range);
428         printf("Link %s\n", buffer);
429         break;
430       }
431     default:
432       printf("(Unknown Wireless event 0x%04X)\n", event->cmd);
433     }   /* switch(event->cmd) */
434
435   return(0);
436 }
437
438 /*------------------------------------------------------------------*/
439 /*
440  * Print out all Wireless Events part of the RTNetlink message
441  * Most often, there will be only one event per message, but
442  * just make sure we read everything...
443  */
444 static inline int
445 print_event_stream(int          ifindex,
446                    char *       data,
447                    int          len)
448 {
449   struct iw_event       iwe;
450   struct stream_descr   stream;
451   int                   i = 0;
452   int                   ret;
453   char                  buffer[64];
454   struct timeval        recv_time;
455   struct wireless_iface *       wireless_data;
456
457   /* Get data from cache */
458   wireless_data = iw_get_interface_data(ifindex);
459   if(wireless_data == NULL)
460     return(-1);
461
462   /* Print received time in readable form */
463   gettimeofday(&recv_time, NULL);
464   iw_print_timeval(buffer, sizeof(buffer), &recv_time);
465
466   iw_init_event_stream(&stream, data, len);
467   do
468     {
469       /* Extract an event and print it */
470       ret = iw_extract_event_stream(&stream, &iwe,
471                                     wireless_data->range.we_version_compiled);
472       if(ret != 0)
473         {
474           if(i++ == 0)
475             printf("%s   %-8.16s ", buffer, wireless_data->ifname);
476           else
477             printf("                           ");
478           if(ret > 0)
479             print_event_token(&iwe,
480                               &wireless_data->range, wireless_data->has_range);
481           else
482             printf("(Invalid event)\n");
483           /* Push data out *now*, in case we are redirected to a pipe */
484           fflush(stdout);
485         }
486     }
487   while(ret > 0);
488
489   return(0);
490 }
491
492 /*********************** RTNETLINK EVENT DUMP***********************/
493 /*
494  * Dump the events we receive from rtnetlink
495  * This code is mostly from Casey
496  */
497
498 /*------------------------------------------------------------------*/
499 /*
500  * Respond to a single RTM_NEWLINK event from the rtnetlink socket.
501  */
502 static int
503 LinkCatcher(struct nlmsghdr *nlh)
504 {
505   struct ifinfomsg* ifi;
506
507 #if 0
508   fprintf(stderr, "nlmsg_type = %d.\n", nlh->nlmsg_type);
509 #endif
510
511   ifi = NLMSG_DATA(nlh);
512
513   /* Code is ugly, but sort of works - Jean II */
514
515   /* If interface is getting destoyed */
516   if(nlh->nlmsg_type == RTM_DELLINK)
517     {
518       /* Remove from cache (if in cache) */
519       iw_del_interface_data(ifi->ifi_index);
520       return 0;
521     }
522
523   /* Only keep add/change events */
524   if(nlh->nlmsg_type != RTM_NEWLINK)
525     return 0;
526
527   /* Check for attributes */
528   if (nlh->nlmsg_len > NLMSG_ALIGN(sizeof(struct ifinfomsg)))
529     {
530       int attrlen = nlh->nlmsg_len - NLMSG_ALIGN(sizeof(struct ifinfomsg));
531       struct rtattr *attr = (void *) ((char *) ifi +
532                                       NLMSG_ALIGN(sizeof(struct ifinfomsg)));
533
534       while (RTA_OK(attr, attrlen))
535         {
536           /* Check if the Wireless kind */
537           if(attr->rta_type == IFLA_WIRELESS)
538             {
539               /* Go to display it */
540               print_event_stream(ifi->ifi_index,
541                                  (char *) attr + RTA_ALIGN(sizeof(struct rtattr)),
542                                  attr->rta_len - RTA_ALIGN(sizeof(struct rtattr)));
543             }
544           attr = RTA_NEXT(attr, attrlen);
545         }
546     }
547
548   return 0;
549 }
550
551 /* ---------------------------------------------------------------- */
552 /*
553  * We must watch the rtnelink socket for events.
554  * This routine handles those events (i.e., call this when rth.fd
555  * is ready to read).
556  */
557 static inline void
558 handle_netlink_events(struct rtnl_handle *      rth)
559 {
560   while(1)
561     {
562       struct sockaddr_nl sanl;
563       socklen_t sanllen = sizeof(struct sockaddr_nl);
564
565       struct nlmsghdr *h;
566       int amt;
567       char buf[8192];
568
569       amt = recvfrom(rth->fd, buf, sizeof(buf), MSG_DONTWAIT, (struct sockaddr*)&sanl, &sanllen);
570       if(amt < 0)
571         {
572           if(errno != EINTR && errno != EAGAIN)
573             {
574               fprintf(stderr, "%s: error reading netlink: %s.\n",
575                       __PRETTY_FUNCTION__, strerror(errno));
576             }
577           return;
578         }
579
580       if(amt == 0)
581         {
582           fprintf(stderr, "%s: EOF on netlink??\n", __PRETTY_FUNCTION__);
583           return;
584         }
585
586       h = (struct nlmsghdr*)buf;
587       while(amt >= (int)sizeof(*h))
588         {
589           int len = h->nlmsg_len;
590           int l = len - sizeof(*h);
591
592           if(l < 0 || len > amt)
593             {
594               fprintf(stderr, "%s: malformed netlink message: len=%d\n", __PRETTY_FUNCTION__, len);
595               break;
596             }
597
598           switch(h->nlmsg_type)
599             {
600             case RTM_NEWLINK:
601             case RTM_DELLINK:
602               LinkCatcher(h);
603               break;
604             default:
605 #if 0
606               fprintf(stderr, "%s: got nlmsg of type %#x.\n", __PRETTY_FUNCTION__, h->nlmsg_type);
607 #endif
608               break;
609             }
610
611           len = NLMSG_ALIGN(len);
612           amt -= len;
613           h = (struct nlmsghdr*)((char*)h + len);
614         }
615
616       if(amt > 0)
617         fprintf(stderr, "%s: remnant of size %d on netlink\n", __PRETTY_FUNCTION__, amt);
618     }
619 }
620
621 /**************************** MAIN LOOP ****************************/
622
623 /* ---------------------------------------------------------------- */
624 /*
625  * Wait until we get an event
626  */
627 static inline int
628 wait_for_event(struct rtnl_handle *     rth)
629 {
630 #if 0
631   struct timeval        tv;     /* Select timeout */
632 #endif
633
634   /* Forever */
635   while(1)
636     {
637       fd_set            rfds;           /* File descriptors for select */
638       int               last_fd;        /* Last fd */
639       int               ret;
640
641       /* Guess what ? We must re-generate rfds each time */
642       FD_ZERO(&rfds);
643       FD_SET(rth->fd, &rfds);
644       last_fd = rth->fd;
645
646       /* Wait until something happens */
647       ret = select(last_fd + 1, &rfds, NULL, NULL, NULL);
648
649       /* Check if there was an error */
650       if(ret < 0)
651         {
652           if(errno == EAGAIN || errno == EINTR)
653             continue;
654           fprintf(stderr, "Unhandled signal - exiting...\n");
655           break;
656         }
657
658       /* Check if there was a timeout */
659       if(ret == 0)
660         {
661           continue;
662         }
663
664       /* Check for interface discovery events. */
665       if(FD_ISSET(rth->fd, &rfds))
666         handle_netlink_events(rth);
667     }
668
669   return(0);
670 }
671
672 /******************************* MAIN *******************************/
673
674 /* ---------------------------------------------------------------- */
675 /*
676  * helper ;-)
677  */
678 static void
679 iw_usage(int status)
680 {
681   fputs("Usage: iwevent [OPTIONS]\n"
682         "   Monitors and displays Wireless Events.\n"
683         "   Options are:\n"
684         "     -h,--help     Print this message.\n"
685         "     -v,--version  Show version of this program.\n",
686         status ? stderr : stdout);
687   exit(status);
688 }
689 /* Command line options */
690 static const struct option long_opts[] = {
691   { "help", no_argument, NULL, 'h' },
692   { "version", no_argument, NULL, 'v' },
693   { NULL, 0, NULL, 0 }
694 };
695
696 /* ---------------------------------------------------------------- */
697 /*
698  * main body of the program
699  */
700 int
701 main(int        argc,
702      char *     argv[])
703 {
704   struct rtnl_handle    rth;
705   int opt;
706
707   /* Check command line options */
708   while((opt = getopt_long(argc, argv, "hv", long_opts, NULL)) > 0)
709     {
710       switch(opt)
711         {
712         case 'h':
713           iw_usage(0);
714           break;
715
716         case 'v':
717           return(iw_print_version_info("iwevent"));
718           break;
719
720         default:
721           iw_usage(1);
722           break;
723         }
724     }
725   if(optind < argc)
726     {
727       fputs("Too many arguments.\n", stderr);
728       iw_usage(1);
729     }
730
731   /* Open netlink channel */
732   if(rtnl_open(&rth, RTMGRP_LINK) < 0)
733     {
734       perror("Can't initialize rtnetlink socket");
735       return(1);
736     }
737
738   fprintf(stderr, "Waiting for Wireless Events from interfaces...\n");
739
740   /* Do what we have to do */
741   wait_for_event(&rth);
742
743   /* Cleanup - only if you are pedantic */
744   rtnl_close(&rth);
745
746   return(0);
747 }