blob: efe96f517d518d66f076b0c21b1f384c3770d42a [file] [log] [blame]
b.liue9582032025-04-17 19:18:16 +08001/*
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//#define MSG_DONTWAIT 0x40
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
44struct rtnl_handle
45{
46 int fd;
47 struct sockaddr_nl local;
48 struct sockaddr_nl peer;
49 __u32 seq;
50 __u32 dump;
51};
52
53static inline void rtnl_close(struct rtnl_handle *rth)
54{
55 close(rth->fd);
56}
57
58static inline int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions)
59{
60 socklen_t 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 */
105static inline int
106print_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, (unsigned char *)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, (unsigned char *)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, (unsigned char *)event->u.addr.sa_data));
208 break;
209 case IWEVEXPIRED:
210 printf("Expired node:%s\n",
211 iw_pr_ether(buffer, (unsigned char *)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 */
240static inline int
241print_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 */
297static inline int
298index2name(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 */
329static int
330LinkCatcher(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 */
377static inline void
378handle_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 */
446static inline int
447wait_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 */
497static void
498iw_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 */
509static 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 */
519int
520main(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}