OSDN Git Service

54bf96bf211ae11e5882aa4f2eb4dfd62808f589
[android-x86/external-wireless-tools.git] / wireless_tools / iwevent.c
1 /*
2  *      Wireless Tools
3  *
4  *              Jean II - HPL 99->01
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-2002 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 /************************ RTNETLINK HELPERS ************************/
30 /*
31  * The following code is extracted from :
32  * ----------------------------------------------
33  * libnetlink.c RTnetlink service routines.
34  *
35  *              This program is free software; you can redistribute it and/or
36  *              modify it under the terms of the GNU General Public License
37  *              as published by the Free Software Foundation; either version
38  *              2 of the License, or (at your option) any later version.
39  *
40  * Authors:     Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
41  * -----------------------------------------------
42  */
43
44 struct rtnl_handle
45 {
46         int                     fd;
47         struct sockaddr_nl      local;
48         struct sockaddr_nl      peer;
49         __u32                   seq;
50         __u32                   dump;
51 };
52
53 static inline void rtnl_close(struct rtnl_handle *rth)
54 {
55         close(rth->fd);
56 }
57
58 static inline int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions)
59 {
60         int addr_len;
61
62         memset(rth, 0, sizeof(rth));
63
64         rth->fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
65         if (rth->fd < 0) {
66                 perror("Cannot open netlink socket");
67                 return -1;
68         }
69
70         memset(&rth->local, 0, sizeof(rth->local));
71         rth->local.nl_family = AF_NETLINK;
72         rth->local.nl_groups = subscriptions;
73
74         if (bind(rth->fd, (struct sockaddr*)&rth->local, sizeof(rth->local)) < 0) {
75                 perror("Cannot bind netlink socket");
76                 return -1;
77         }
78         addr_len = sizeof(rth->local);
79         if (getsockname(rth->fd, (struct sockaddr*)&rth->local, &addr_len) < 0) {
80                 perror("Cannot getsockname");
81                 return -1;
82         }
83         if (addr_len != sizeof(rth->local)) {
84                 fprintf(stderr, "Wrong address length %d\n", addr_len);
85                 return -1;
86         }
87         if (rth->local.nl_family != AF_NETLINK) {
88                 fprintf(stderr, "Wrong address family %d\n", rth->local.nl_family);
89                 return -1;
90         }
91         rth->seq = time(NULL);
92         return 0;
93 }
94
95 /********************* WIRELESS EVENT DECODING *********************/
96 /*
97  * This is the bit I wrote...
98  */
99
100 #if WIRELESS_EXT > 13
101 /*------------------------------------------------------------------*/
102 /*
103  * Print one element from the scanning results
104  */
105 static inline int
106 print_event_token(struct iw_event *     event,  /* Extracted token */
107                   struct iw_range *     iwrange,        /* Range info */
108                   int                   has_range)
109 {
110   char          buffer[128];    /* Temporary buffer */
111
112   /* Now, let's decode the event */
113   switch(event->cmd)
114     {
115       /* ----- set events ----- */
116       /* Events that result from a "SET XXX" operation by the user */
117     case SIOCSIWNWID:
118       if(event->u.nwid.disabled)
119         printf("NWID:off/any\n");
120       else
121         printf("NWID:%X\n", event->u.nwid.value);
122       break;
123     case SIOCSIWFREQ:
124       {
125         float           freq;                   /* Frequency/channel */
126         freq = iw_freq2float(&(event->u.freq));
127         iw_print_freq(buffer, freq);
128         printf("%s\n", buffer);
129       }
130       break;
131     case SIOCSIWMODE:
132       printf("Mode:%s\n",
133              iw_operation_mode[event->u.mode]);
134       break;
135     case SIOCSIWESSID:
136       {
137         char essid[IW_ESSID_MAX_SIZE+1];
138         if((event->u.essid.pointer) && (event->u.essid.length))
139           memcpy(essid, event->u.essid.pointer, event->u.essid.length);
140         essid[event->u.essid.length] = '\0';
141         if(event->u.essid.flags)
142           {
143             /* Does it have an ESSID index ? */
144             if((event->u.essid.flags & IW_ENCODE_INDEX) > 1)
145               printf("ESSID:\"%s\" [%d]\n", essid,
146                      (event->u.essid.flags & IW_ENCODE_INDEX));
147             else
148               printf("ESSID:\"%s\"\n", essid);
149           }
150         else
151           printf("ESSID:off/any\n");
152       }
153       break;
154     case SIOCSIWENCODE:
155       {
156         unsigned char   key[IW_ENCODING_TOKEN_MAX];
157         if(event->u.data.pointer)
158           memcpy(key, event->u.essid.pointer, event->u.data.length);
159         else
160           event->u.data.flags |= IW_ENCODE_NOKEY;
161         printf("Encryption key:");
162         if(event->u.data.flags & IW_ENCODE_DISABLED)
163           printf("off\n");
164         else
165           {
166             /* Display the key */
167             iw_print_key(buffer, key, event->u.data.length,
168                          event->u.data.flags);
169             printf("%s", buffer);
170
171             /* Other info... */
172             if((event->u.data.flags & IW_ENCODE_INDEX) > 1)
173               printf(" [%d]", event->u.data.flags & IW_ENCODE_INDEX);
174             if(event->u.data.flags & IW_ENCODE_RESTRICTED)
175               printf("   Encryption mode:restricted");
176             if(event->u.data.flags & IW_ENCODE_OPEN)
177               printf("   Encryption mode:open");
178             printf("\n");
179           }
180       }
181       break;
182       /* ----- driver events ----- */
183       /* Events generated by the driver when something important happens */
184     case SIOCGIWAP:
185       printf("New Access Point/Cell address:%s\n",
186              iw_pr_ether(buffer, event->u.ap_addr.sa_data));
187       break;
188     case SIOCGIWSCAN:
189       printf("Scan request completed\n");
190       break;
191     case IWEVTXDROP:
192       printf("Tx packet dropped:%s\n",
193              iw_pr_ether(buffer, event->u.addr.sa_data));
194       break;
195 #if WIRELESS_EXT > 14
196     case IWEVCUSTOM:
197       {
198         char custom[IW_CUSTOM_MAX+1];
199         if((event->u.data.pointer) && (event->u.data.length))
200           memcpy(custom, event->u.data.pointer, event->u.data.length);
201         custom[event->u.data.length] = '\0';
202         printf("Custom driver event:%s\n", custom);
203       }
204       break;
205     case IWEVREGISTERED:
206       printf("Registered node:%s\n",
207              iw_pr_ether(buffer, event->u.addr.sa_data));
208       break;
209     case IWEVEXPIRED:
210       printf("Expired node:%s\n",
211              iw_pr_ether(buffer, event->u.addr.sa_data));
212       break;
213 #endif /* WIRELESS_EXT > 14 */
214       /* ----- junk ----- */
215       /* other junk not currently in use */
216     case SIOCGIWRATE:
217       iw_print_bitrate(buffer, event->u.bitrate.value);
218       printf("Bit Rate:%s\n", buffer);
219       break;
220     case IWEVQUAL:
221       {
222         event->u.qual.updated = 0x0;    /* Not that reliable, disable */
223         iw_print_stats(buffer, &event->u.qual, iwrange, has_range);
224         printf("Link %s\n", buffer);
225         break;
226       }
227     default:
228       printf("(Unknown Wireless event 0x%04X)\n", event->cmd);
229     }   /* switch(event->cmd) */
230
231   return(0);
232 }
233
234 /*------------------------------------------------------------------*/
235 /*
236  * Print out all Wireless Events part of the RTNetlink message
237  * Most often, there will be only one event per message, but
238  * just make sure we read everything...
239  */
240 static inline int
241 print_event_stream(char *       ifname,
242                    char *       data,
243                    int          len)
244 {
245   struct iw_event       iwe;
246   struct stream_descr   stream;
247   int                   i = 0;
248   int                   ret;
249   char                  buffer[64];
250   struct timeval        recv_time;
251 #if 0
252   struct iw_range       range;
253   int                   has_range;
254 #endif
255
256 #if 0
257   has_range = (iw_get_range_info(skfd, ifname, &range) < 0);
258 #endif
259
260   /* In readable form */
261   gettimeofday(&recv_time, NULL);
262   iw_print_timeval(buffer, &recv_time);
263
264   iw_init_event_stream(&stream, data, len);
265   do
266     {
267       /* Extract an event and print it */
268       ret = iw_extract_event_stream(&stream, &iwe);
269       if(ret != 0)
270         {
271           if(i++ == 0)
272             printf("%s   %-8.8s ", buffer, ifname);
273           else
274             printf("                           ");
275           if(ret > 0)
276             print_event_token(&iwe, NULL, 0);
277           else
278             printf("(Invalid event)\n");
279         }
280     }
281   while(ret > 0);
282
283   return(0);
284 }
285 #endif  /* WIRELESS_EXT > 13 */
286
287 /*********************** RTNETLINK EVENT DUMP***********************/
288 /*
289  * Dump the events we receive from rtnetlink
290  * This code is mostly from Casey
291  */
292
293 /*------------------------------------------------------------------*/
294 /*
295  * Respond to a single RTM_NEWLINK event from the rtnetlink socket.
296  */
297 static inline int
298 index2name(int  index, char *name)
299 {
300   int           skfd = -1;      /* generic raw socket desc.     */
301   struct ifreq  irq;
302   int           ret = 0;
303
304   memset(name, 0, IFNAMSIZ + 1);
305
306   /* Create a channel to the NET kernel. */
307   if((skfd = iw_sockets_open()) < 0)
308     {
309       perror("socket");
310       exit(-1);
311     }
312
313   /* Get interface name */
314   irq.ifr_ifindex = index;
315   if(ioctl(skfd, SIOCGIFNAME, &irq) < 0)
316     ret = -1;
317   else
318     strncpy(name, irq.ifr_name, IFNAMSIZ);
319
320   close(skfd);
321   return(ret);
322 }
323
324
325 /*------------------------------------------------------------------*/
326 /*
327  * Respond to a single RTM_NEWLINK event from the rtnetlink socket.
328  */
329 static int
330 LinkCatcher(struct nlmsghdr *nlh)
331 {
332   struct ifinfomsg* ifi;
333   char ifname[IFNAMSIZ + 1];
334
335 #if 0
336   fprintf(stderr, "nlmsg_type = %d.\n", nlh->nlmsg_type);
337 #endif
338
339   if(nlh->nlmsg_type != RTM_NEWLINK)
340     return 0;
341
342   ifi = NLMSG_DATA(nlh);
343
344   /* Get a name... */
345   index2name(ifi->ifi_index, ifname);
346
347 #if WIRELESS_EXT > 13
348   /* Code is ugly, but sort of works - Jean II */
349
350   /* Check for attributes */
351   if (nlh->nlmsg_len > NLMSG_ALIGN(sizeof(struct ifinfomsg))) {
352       int attrlen = nlh->nlmsg_len - NLMSG_ALIGN(sizeof(struct ifinfomsg));
353       struct rtattr *attr = (void*)ifi + NLMSG_ALIGN(sizeof(struct ifinfomsg));
354
355       while (RTA_OK(attr, attrlen)) {
356         /* Check if the Wireless kind */
357         if(attr->rta_type == IFLA_WIRELESS) {
358           /* Go to display it */
359           print_event_stream(ifname,
360                              (void *)attr + RTA_ALIGN(sizeof(struct rtattr)),
361                              attr->rta_len - RTA_ALIGN(sizeof(struct rtattr)));
362         }
363         attr = RTA_NEXT(attr, attrlen);
364       }
365   }
366 #endif  /* WIRELESS_EXT > 13 */
367
368   return 0;
369 }
370
371 /* ---------------------------------------------------------------- */
372 /*
373  * We must watch the rtnelink socket for events.
374  * This routine handles those events (i.e., call this when rth.fd
375  * is ready to read).
376  */
377 static inline void
378 handle_netlink_events(struct rtnl_handle *      rth)
379 {
380   while(1)
381     {
382       struct sockaddr_nl sanl;
383       socklen_t sanllen;
384
385       struct nlmsghdr *h;
386       int amt;
387       char buf[8192];
388
389       amt = recvfrom(rth->fd, buf, sizeof(buf), MSG_DONTWAIT, (struct sockaddr*)&sanl, &sanllen);
390       if(amt < 0)
391         {
392           if(errno != EINTR && errno != EAGAIN)
393             {
394               fprintf(stderr, "%s: error reading netlink: %s.\n",
395                       __PRETTY_FUNCTION__, strerror(errno));
396             }
397           return;
398         }
399
400       if(amt == 0)
401         {
402           fprintf(stderr, "%s: EOF on netlink??\n", __PRETTY_FUNCTION__);
403           return;
404         }
405
406       h = (struct nlmsghdr*)buf;
407       while(amt >= (int)sizeof(*h))
408         {
409           int len = h->nlmsg_len;
410           int l = len - sizeof(*h);
411
412           if(l < 0 || len > amt)
413             {
414               fprintf(stderr, "%s: malformed netlink message: len=%d\n", __PRETTY_FUNCTION__, len);
415               break;
416             }
417
418           switch(h->nlmsg_type)
419             {
420             case RTM_NEWLINK:
421               LinkCatcher(h);
422               break;
423             default:
424 #if 0
425               fprintf(stderr, "%s: got nlmsg of type %#x.\n", __PRETTY_FUNCTION__, h->nlmsg_type);
426 #endif
427               break;
428             }
429
430           len = NLMSG_ALIGN(len);
431           amt -= len;
432           h = (struct nlmsghdr*)((char*)h + len);
433         }
434
435       if(amt > 0)
436         fprintf(stderr, "%s: remnant of size %d on netlink\n", __PRETTY_FUNCTION__, amt);
437     }
438 }
439
440 /**************************** MAIN LOOP ****************************/
441
442 /* ---------------------------------------------------------------- */
443 /*
444  * Wait until we get an event
445  */
446 static inline int
447 wait_for_event(struct rtnl_handle *     rth)
448 {
449 #if 0
450   struct timeval        tv;     /* Select timeout */
451 #endif
452
453   /* Forever */
454   while(1)
455     {
456       fd_set            rfds;           /* File descriptors for select */
457       int               last_fd;        /* Last fd */
458       int               ret;
459
460       /* Guess what ? We must re-generate rfds each time */
461       FD_ZERO(&rfds);
462       FD_SET(rth->fd, &rfds);
463       last_fd = rth->fd;
464
465       /* Wait until something happens */
466       ret = select(last_fd + 1, &rfds, NULL, NULL, NULL);
467
468       /* Check if there was an error */
469       if(ret < 0)
470         {
471           if(errno == EAGAIN || errno == EINTR)
472             continue;
473           fprintf(stderr, "Unhandled signal - exiting...\n");
474           break;
475         }
476
477       /* Check if there was a timeout */
478       if(ret == 0)
479         {
480           continue;
481         }
482
483       /* Check for interface discovery events. */
484       if(FD_ISSET(rth->fd, &rfds))
485         handle_netlink_events(rth);
486     }
487
488   return(0);
489 }
490
491 /******************************* MAIN *******************************/
492
493 /* ---------------------------------------------------------------- */
494 /*
495  * helper ;-)
496  */
497 static void
498 iw_usage(int status)
499 {
500   fputs("Usage: iwevent [OPTIONS]\n"
501         "   Monitors and displays Wireless Events.\n"
502         "   Options are:\n"
503         "     -h,--help     Print this message.\n"
504         "     -v,--version  Show version of this program.\n",
505         status ? stderr : stdout);
506   exit(status);
507 }
508 /* Command line options */
509 static const struct option long_opts[] = {
510   { "help", no_argument, NULL, 'h' },
511   { "version", no_argument, NULL, 'v' },
512   { NULL, 0, NULL, 0 }
513 };
514
515 /* ---------------------------------------------------------------- */
516 /*
517  * main body of the program
518  */
519 int
520 main(int        argc,
521      char *     argv[])
522 {
523   struct rtnl_handle    rth;
524   int opt;
525
526   /* Check command line options */
527   while((opt = getopt_long(argc, argv, "hv", long_opts, NULL)) > 0)
528     {
529       switch(opt)
530         {
531         case 'h':
532           iw_usage(0);
533           break;
534
535         case 'v':
536           return(iw_print_version_info("iwevent"));
537           break;
538
539         default:
540           iw_usage(1);
541           break;
542         }
543     }
544   if(optind < argc)
545     {
546       fputs("Too many arguments.\n", stderr);
547       iw_usage(1);
548     }
549
550   /* Open netlink channel */
551   if(rtnl_open(&rth, RTMGRP_LINK) < 0)
552     {
553       perror("Can't initialize rtnetlink socket");
554       return(1);
555     }
556
557 #if WIRELESS_EXT > 13
558   fprintf(stderr, "Waiting for Wireless Events...\n");
559 #else   /* WIRELESS_EXT > 13 */
560   fprintf(stderr, "Unsupported in Wireless Extensions <= 14 :-(\n");
561   return(-1);
562 #endif  /* WIRELESS_EXT > 13 */
563
564   /* Do what we have to do */
565   wait_for_event(&rth);
566
567   /* Cleanup - only if you are pedantic */
568   rtnl_close(&rth);
569
570   return(0);
571 }