nexmon – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 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, |
||
110 | (socklen_t *) &addr_len) < 0) { |
||
111 | perror("Cannot getsockname"); |
||
112 | return -1; |
||
113 | } |
||
114 | if (addr_len != sizeof(rth->local)) { |
||
115 | fprintf(stderr, "Wrong address length %d\n", addr_len); |
||
116 | return -1; |
||
117 | } |
||
118 | if (rth->local.nl_family != AF_NETLINK) { |
||
119 | fprintf(stderr, "Wrong address family %d\n", rth->local.nl_family); |
||
120 | return -1; |
||
121 | } |
||
122 | rth->seq = time(NULL); |
||
123 | return 0; |
||
124 | } |
||
125 | |||
126 | /******************* WIRELESS INTERFACE DATABASE *******************/ |
||
127 | /* |
||
128 | * We keep a few information about each wireless interface on the |
||
129 | * system. This avoid to query this info at each event, therefore |
||
130 | * reducing overhead. |
||
131 | * |
||
132 | * Each interface is indexed by the 'ifindex'. As opposed to interface |
||
133 | * names, 'ifindex' are never reused (even if you reactivate the same |
||
134 | * hardware), so the data we cache will never apply to the wrong |
||
135 | * interface. |
||
136 | * Because of that, we are pretty lazy when it come to purging the |
||
137 | * cache... |
||
138 | */ |
||
139 | |||
140 | /*------------------------------------------------------------------*/ |
||
141 | /* |
||
142 | * Get name of interface based on interface index... |
||
143 | */ |
||
144 | static inline int |
||
145 | index2name(int skfd, |
||
146 | int ifindex, |
||
147 | char * name) |
||
148 | { |
||
149 | struct ifreq irq; |
||
150 | int ret = 0; |
||
151 | |||
152 | memset(name, 0, IFNAMSIZ + 1); |
||
153 | |||
154 | /* Get interface name */ |
||
155 | irq.ifr_ifindex = ifindex; |
||
156 | if(ioctl(skfd, SIOCGIFNAME, &irq) < 0) |
||
157 | ret = -1; |
||
158 | else |
||
159 | strncpy(name, irq.ifr_name, IFNAMSIZ); |
||
160 | |||
161 | return(ret); |
||
162 | } |
||
163 | |||
164 | /*------------------------------------------------------------------*/ |
||
165 | /* |
||
166 | * Get interface data from cache or live interface |
||
167 | */ |
||
168 | static struct wireless_iface * |
||
169 | iw_get_interface_data(int ifindex) |
||
170 | { |
||
171 | struct wireless_iface * curr; |
||
172 | int skfd = -1; /* ioctl socket */ |
||
173 | |||
174 | /* Search for it in the database */ |
||
175 | curr = interface_cache; |
||
176 | while(curr != NULL) |
||
177 | { |
||
178 | /* Match ? */ |
||
179 | if(curr->ifindex == ifindex) |
||
180 | { |
||
181 | //printf("Cache : found %d-%s\n", curr->ifindex, curr->ifname); |
||
182 | |||
183 | /* Return */ |
||
184 | return(curr); |
||
185 | } |
||
186 | /* Next entry */ |
||
187 | curr = curr->next; |
||
188 | } |
||
189 | |||
190 | /* Create a channel to the NET kernel. Doesn't happen too often, so |
||
191 | * socket creation overhead is minimal... */ |
||
192 | if((skfd = iw_sockets_open()) < 0) |
||
193 | { |
||
194 | perror("iw_sockets_open"); |
||
195 | return(NULL); |
||
196 | } |
||
197 | |||
198 | /* Create new entry, zero, init */ |
||
199 | curr = calloc(1, sizeof(struct wireless_iface)); |
||
200 | if(!curr) |
||
201 | { |
||
202 | fprintf(stderr, "Malloc failed\n"); |
||
203 | return(NULL); |
||
204 | } |
||
205 | curr->ifindex = ifindex; |
||
206 | |||
207 | /* Extract static data */ |
||
208 | if(index2name(skfd, ifindex, curr->ifname) < 0) |
||
209 | { |
||
210 | perror("index2name"); |
||
211 | free(curr); |
||
212 | return(NULL); |
||
213 | } |
||
214 | curr->has_range = (iw_get_range_info(skfd, curr->ifname, &curr->range) >= 0); |
||
215 | //printf("Cache : create %d-%s\n", curr->ifindex, curr->ifname); |
||
216 | |||
217 | /* Done */ |
||
218 | iw_sockets_close(skfd); |
||
219 | |||
220 | /* Link it */ |
||
221 | curr->next = interface_cache; |
||
222 | interface_cache = curr; |
||
223 | |||
224 | return(curr); |
||
225 | } |
||
226 | |||
227 | /*------------------------------------------------------------------*/ |
||
228 | /* |
||
229 | * Remove interface data from cache (if it exist) |
||
230 | */ |
||
231 | static void |
||
232 | iw_del_interface_data(int ifindex) |
||
233 | { |
||
234 | struct wireless_iface * curr; |
||
235 | struct wireless_iface * prev = NULL; |
||
236 | struct wireless_iface * next; |
||
237 | |||
238 | /* Go through the list, find the interface, kills it */ |
||
239 | curr = interface_cache; |
||
240 | while(curr) |
||
241 | { |
||
242 | next = curr->next; |
||
243 | |||
244 | /* Got a match ? */ |
||
245 | if(curr->ifindex == ifindex) |
||
246 | { |
||
247 | /* Unlink. Root ? */ |
||
248 | if(!prev) |
||
249 | interface_cache = next; |
||
250 | else |
||
251 | prev->next = next; |
||
252 | //printf("Cache : purge %d-%s\n", curr->ifindex, curr->ifname); |
||
253 | |||
254 | /* Destroy */ |
||
255 | free(curr); |
||
256 | } |
||
257 | else |
||
258 | { |
||
259 | /* Keep as previous */ |
||
260 | prev = curr; |
||
261 | } |
||
262 | |||
263 | /* Next entry */ |
||
264 | curr = next; |
||
265 | } |
||
266 | } |
||
267 | |||
268 | /********************* WIRELESS EVENT DECODING *********************/ |
||
269 | /* |
||
270 | * Parse the Wireless Event and print it out |
||
271 | */ |
||
272 | |||
273 | /*------------------------------------------------------------------*/ |
||
274 | /* |
||
275 | * Dump a buffer as a serie of hex |
||
276 | * Maybe should go in iwlib... |
||
277 | * Maybe we should have better formatting like iw_print_key... |
||
278 | */ |
||
279 | static char * |
||
280 | iw_hexdump(char * buf, |
||
281 | size_t buflen, |
||
282 | const unsigned char *data, |
||
283 | size_t datalen) |
||
284 | { |
||
285 | size_t i; |
||
286 | char * pos = buf; |
||
287 | |||
288 | for(i = 0; i < datalen; i++) |
||
289 | pos += snprintf(pos, buf + buflen - pos, "%02X", data[i]); |
||
290 | return buf; |
||
291 | } |
||
292 | |||
293 | /*------------------------------------------------------------------*/ |
||
294 | /* |
||
295 | * Print one element from the scanning results |
||
296 | */ |
||
297 | static inline int |
||
298 | print_event_token(struct iw_event * event, /* Extracted token */ |
||
299 | struct iw_range * iw_range, /* Range info */ |
||
300 | int has_range) |
||
301 | { |
||
302 | char buffer[128]; /* Temporary buffer */ |
||
303 | char buffer2[30]; /* Temporary buffer */ |
||
304 | char * prefix = (IW_IS_GET(event->cmd) ? "New" : "Set"); |
||
305 | |||
306 | /* Now, let's decode the event */ |
||
307 | switch(event->cmd) |
||
308 | { |
||
309 | /* ----- set events ----- */ |
||
310 | /* Events that result from a "SET XXX" operation by the user */ |
||
311 | case SIOCSIWNWID: |
||
312 | if(event->u.nwid.disabled) |
||
313 | printf("Set NWID:off/any\n"); |
||
314 | else |
||
315 | printf("Set NWID:%X\n", event->u.nwid.value); |
||
316 | break; |
||
317 | case SIOCSIWFREQ: |
||
318 | case SIOCGIWFREQ: |
||
319 | { |
||
320 | double freq; /* Frequency/channel */ |
||
321 | int channel = -1; /* Converted to channel */ |
||
322 | freq = iw_freq2float(&(event->u.freq)); |
||
323 | if(has_range) |
||
324 | { |
||
325 | if(freq < KILO) |
||
326 | /* Convert channel to frequency if possible */ |
||
327 | channel = iw_channel_to_freq((int) freq, &freq, iw_range); |
||
328 | else |
||
329 | /* Convert frequency to channel if possible */ |
||
330 | channel = iw_freq_to_channel(freq, iw_range); |
||
331 | } |
||
332 | iw_print_freq(buffer, sizeof(buffer), |
||
333 | freq, channel, event->u.freq.flags); |
||
334 | printf("%s %s\n", prefix, buffer); |
||
335 | } |
||
336 | break; |
||
337 | case SIOCSIWMODE: |
||
338 | printf("Set Mode:%s\n", |
||
339 | iw_operation_mode[event->u.mode]); |
||
340 | break; |
||
341 | case SIOCSIWESSID: |
||
342 | case SIOCGIWESSID: |
||
343 | { |
||
344 | char essid[IW_ESSID_MAX_SIZE+1]; |
||
345 | memset(essid, '\0', sizeof(essid)); |
||
346 | if((event->u.essid.pointer) && (event->u.essid.length)) |
||
347 | memcpy(essid, event->u.essid.pointer, event->u.essid.length); |
||
348 | if(event->u.essid.flags) |
||
349 | { |
||
350 | /* Does it have an ESSID index ? */ |
||
351 | if((event->u.essid.flags & IW_ENCODE_INDEX) > 1) |
||
352 | printf("%s ESSID:\"%s\" [%d]\n", prefix, essid, |
||
353 | (event->u.essid.flags & IW_ENCODE_INDEX)); |
||
354 | else |
||
355 | printf("%s ESSID:\"%s\"\n", prefix, essid); |
||
356 | } |
||
357 | else |
||
358 | printf("%s ESSID:off/any\n", prefix); |
||
359 | } |
||
360 | break; |
||
361 | case SIOCSIWENCODE: |
||
362 | { |
||
363 | unsigned char key[IW_ENCODING_TOKEN_MAX]; |
||
364 | if(event->u.data.pointer) |
||
365 | memcpy(key, event->u.data.pointer, event->u.data.length); |
||
366 | else |
||
367 | event->u.data.flags |= IW_ENCODE_NOKEY; |
||
368 | printf("Set Encryption key:"); |
||
369 | if(event->u.data.flags & IW_ENCODE_DISABLED) |
||
370 | printf("off\n"); |
||
371 | else |
||
372 | { |
||
373 | /* Display the key */ |
||
374 | iw_print_key(buffer, sizeof(buffer), key, event->u.data.length, |
||
375 | event->u.data.flags); |
||
376 | printf("%s", buffer); |
||
377 | |||
378 | /* Other info... */ |
||
379 | if((event->u.data.flags & IW_ENCODE_INDEX) > 1) |
||
380 | printf(" [%d]", event->u.data.flags & IW_ENCODE_INDEX); |
||
381 | if(event->u.data.flags & IW_ENCODE_RESTRICTED) |
||
382 | printf(" Security mode:restricted"); |
||
383 | if(event->u.data.flags & IW_ENCODE_OPEN) |
||
384 | printf(" Security mode:open"); |
||
385 | printf("\n"); |
||
386 | } |
||
387 | } |
||
388 | break; |
||
389 | /* ----- driver events ----- */ |
||
390 | /* Events generated by the driver when something important happens */ |
||
391 | case SIOCGIWAP: |
||
392 | printf("New Access Point/Cell address:%s\n", |
||
393 | iw_sawap_ntop(&event->u.ap_addr, buffer)); |
||
394 | break; |
||
395 | case SIOCGIWSCAN: |
||
396 | printf("Scan request completed\n"); |
||
397 | break; |
||
398 | case IWEVTXDROP: |
||
399 | printf("Tx packet dropped:%s\n", |
||
400 | iw_saether_ntop(&event->u.addr, buffer)); |
||
401 | break; |
||
402 | case IWEVCUSTOM: |
||
403 | { |
||
404 | char custom[IW_CUSTOM_MAX+1]; |
||
405 | memset(custom, '\0', sizeof(custom)); |
||
406 | if((event->u.data.pointer) && (event->u.data.length)) |
||
407 | memcpy(custom, event->u.data.pointer, event->u.data.length); |
||
408 | printf("Custom driver event:%s\n", custom); |
||
409 | } |
||
410 | break; |
||
411 | case IWEVREGISTERED: |
||
412 | printf("Registered node:%s\n", |
||
413 | iw_saether_ntop(&event->u.addr, buffer)); |
||
414 | break; |
||
415 | case IWEVEXPIRED: |
||
416 | printf("Expired node:%s\n", |
||
417 | iw_saether_ntop(&event->u.addr, buffer)); |
||
418 | break; |
||
419 | case SIOCGIWTHRSPY: |
||
420 | { |
||
421 | struct iw_thrspy threshold; |
||
422 | if((event->u.data.pointer) && (event->u.data.length)) |
||
423 | { |
||
424 | memcpy(&threshold, event->u.data.pointer, |
||
425 | sizeof(struct iw_thrspy)); |
||
426 | printf("Spy threshold crossed on address:%s\n", |
||
427 | iw_saether_ntop(&threshold.addr, buffer)); |
||
428 | iw_print_stats(buffer, sizeof(buffer), |
||
429 | &threshold.qual, iw_range, has_range); |
||
430 | printf(" Link %s\n", buffer); |
||
431 | } |
||
432 | else |
||
433 | printf("Invalid Spy Threshold event\n"); |
||
434 | } |
||
435 | break; |
||
436 | /* ----- driver WPA events ----- */ |
||
437 | /* Events generated by the driver, used for WPA operation */ |
||
438 | case IWEVMICHAELMICFAILURE: |
||
439 | if(event->u.data.length >= sizeof(struct iw_michaelmicfailure)) |
||
440 | { |
||
441 | struct iw_michaelmicfailure mf; |
||
442 | memcpy(&mf, event->u.data.pointer, sizeof(mf)); |
||
443 | printf("Michael MIC failure flags:0x%X src_addr:%s tsc:%s\n", |
||
444 | mf.flags, |
||
445 | iw_saether_ntop(&mf.src_addr, buffer2), |
||
446 | iw_hexdump(buffer, sizeof(buffer), |
||
447 | mf.tsc, IW_ENCODE_SEQ_MAX_SIZE)); |
||
448 | } |
||
449 | break; |
||
450 | case IWEVASSOCREQIE: |
||
451 | printf("Association Request IEs:%s\n", |
||
452 | iw_hexdump(buffer, sizeof(buffer), |
||
453 | event->u.data.pointer, event->u.data.length)); |
||
454 | break; |
||
455 | case IWEVASSOCRESPIE: |
||
456 | printf("Association Response IEs:%s\n", |
||
457 | iw_hexdump(buffer, sizeof(buffer), |
||
458 | event->u.data.pointer, event->u.data.length)); |
||
459 | break; |
||
460 | case IWEVPMKIDCAND: |
||
461 | if(event->u.data.length >= sizeof(struct iw_pmkid_cand)) |
||
462 | { |
||
463 | struct iw_pmkid_cand cand; |
||
464 | memcpy(&cand, event->u.data.pointer, sizeof(cand)); |
||
465 | printf("PMKID candidate flags:0x%X index:%d bssid:%s\n", |
||
466 | cand.flags, cand.index, |
||
467 | iw_saether_ntop(&cand.bssid, buffer)); |
||
468 | } |
||
469 | break; |
||
470 | /* ----- junk ----- */ |
||
471 | /* other junk not currently in use */ |
||
472 | case SIOCGIWRATE: |
||
473 | iw_print_bitrate(buffer, sizeof(buffer), event->u.bitrate.value); |
||
474 | printf("New Bit Rate:%s\n", buffer); |
||
475 | break; |
||
476 | case SIOCGIWNAME: |
||
477 | printf("Protocol:%-1.16s\n", event->u.name); |
||
478 | break; |
||
479 | case IWEVQUAL: |
||
480 | { |
||
481 | event->u.qual.updated = 0x0; /* Not that reliable, disable */ |
||
482 | iw_print_stats(buffer, sizeof(buffer), |
||
483 | &event->u.qual, iw_range, has_range); |
||
484 | printf("Link %s\n", buffer); |
||
485 | break; |
||
486 | } |
||
487 | default: |
||
488 | printf("(Unknown Wireless event 0x%04X)\n", event->cmd); |
||
489 | } /* switch(event->cmd) */ |
||
490 | |||
491 | return(0); |
||
492 | } |
||
493 | |||
494 | /*------------------------------------------------------------------*/ |
||
495 | /* |
||
496 | * Print out all Wireless Events part of the RTNetlink message |
||
497 | * Most often, there will be only one event per message, but |
||
498 | * just make sure we read everything... |
||
499 | */ |
||
500 | static inline int |
||
501 | print_event_stream(int ifindex, |
||
502 | char * data, |
||
503 | int len) |
||
504 | { |
||
505 | struct iw_event iwe; |
||
506 | struct stream_descr stream; |
||
507 | int i = 0; |
||
508 | int ret; |
||
509 | char buffer[64]; |
||
510 | struct timeval recv_time; |
||
511 | struct timezone tz; |
||
512 | struct wireless_iface * wireless_data; |
||
513 | |||
514 | /* Get data from cache */ |
||
515 | wireless_data = iw_get_interface_data(ifindex); |
||
516 | if(wireless_data == NULL) |
||
517 | return(-1); |
||
518 | |||
519 | /* Print received time in readable form */ |
||
520 | gettimeofday(&recv_time, &tz); |
||
521 | iw_print_timeval(buffer, sizeof(buffer), &recv_time, &tz); |
||
522 | |||
523 | iw_init_event_stream(&stream, data, len); |
||
524 | do |
||
525 | { |
||
526 | /* Extract an event and print it */ |
||
527 | ret = iw_extract_event_stream(&stream, &iwe, |
||
528 | wireless_data->range.we_version_compiled); |
||
529 | if(ret != 0) |
||
530 | { |
||
531 | if(i++ == 0) |
||
532 | printf("%s %-8.16s ", buffer, wireless_data->ifname); |
||
533 | else |
||
534 | printf(" "); |
||
535 | if(ret > 0) |
||
536 | print_event_token(&iwe, |
||
537 | &wireless_data->range, wireless_data->has_range); |
||
538 | else |
||
539 | printf("(Invalid event)\n"); |
||
540 | /* Push data out *now*, in case we are redirected to a pipe */ |
||
541 | fflush(stdout); |
||
542 | } |
||
543 | } |
||
544 | while(ret > 0); |
||
545 | |||
546 | return(0); |
||
547 | } |
||
548 | |||
549 | /*********************** RTNETLINK EVENT DUMP***********************/ |
||
550 | /* |
||
551 | * Dump the events we receive from rtnetlink |
||
552 | * This code is mostly from Casey |
||
553 | */ |
||
554 | |||
555 | /*------------------------------------------------------------------*/ |
||
556 | /* |
||
557 | * Respond to a single RTM_NEWLINK event from the rtnetlink socket. |
||
558 | */ |
||
559 | static int |
||
560 | LinkCatcher(struct nlmsghdr *nlh) |
||
561 | { |
||
562 | struct ifinfomsg* ifi; |
||
563 | |||
564 | #if 0 |
||
565 | fprintf(stderr, "nlmsg_type = %d.\n", nlh->nlmsg_type); |
||
566 | #endif |
||
567 | |||
568 | ifi = NLMSG_DATA(nlh); |
||
569 | |||
570 | /* Code is ugly, but sort of works - Jean II */ |
||
571 | |||
572 | /* If interface is getting destoyed */ |
||
573 | if(nlh->nlmsg_type == RTM_DELLINK) |
||
574 | { |
||
575 | /* Remove from cache (if in cache) */ |
||
576 | iw_del_interface_data(ifi->ifi_index); |
||
577 | return 0; |
||
578 | } |
||
579 | |||
580 | /* Only keep add/change events */ |
||
581 | if(nlh->nlmsg_type != RTM_NEWLINK) |
||
582 | return 0; |
||
583 | |||
584 | /* Check for attributes */ |
||
585 | if (nlh->nlmsg_len > NLMSG_ALIGN(sizeof(struct ifinfomsg))) |
||
586 | { |
||
587 | int attrlen = nlh->nlmsg_len - NLMSG_ALIGN(sizeof(struct ifinfomsg)); |
||
588 | struct rtattr *attr = (void *) ((char *) ifi + |
||
589 | NLMSG_ALIGN(sizeof(struct ifinfomsg))); |
||
590 | |||
591 | while (RTA_OK(attr, attrlen)) |
||
592 | { |
||
593 | /* Check if the Wireless kind */ |
||
594 | if(attr->rta_type == IFLA_WIRELESS) |
||
595 | { |
||
596 | /* Go to display it */ |
||
597 | print_event_stream(ifi->ifi_index, |
||
598 | (char *) attr + RTA_ALIGN(sizeof(struct rtattr)), |
||
599 | attr->rta_len - RTA_ALIGN(sizeof(struct rtattr))); |
||
600 | } |
||
601 | attr = RTA_NEXT(attr, attrlen); |
||
602 | } |
||
603 | } |
||
604 | |||
605 | return 0; |
||
606 | } |
||
607 | |||
608 | /* ---------------------------------------------------------------- */ |
||
609 | /* |
||
610 | * We must watch the rtnelink socket for events. |
||
611 | * This routine handles those events (i.e., call this when rth.fd |
||
612 | * is ready to read). |
||
613 | */ |
||
614 | static inline void |
||
615 | handle_netlink_events(struct rtnl_handle * rth) |
||
616 | { |
||
617 | while(1) |
||
618 | { |
||
619 | struct sockaddr_nl sanl; |
||
620 | socklen_t sanllen = sizeof(struct sockaddr_nl); |
||
621 | |||
622 | struct nlmsghdr *h; |
||
623 | int amt; |
||
624 | char buf[8192]; |
||
625 | |||
626 | amt = recvfrom(rth->fd, buf, sizeof(buf), MSG_DONTWAIT, (struct sockaddr*)&sanl, &sanllen); |
||
627 | if(amt < 0) |
||
628 | { |
||
629 | if(errno != EINTR && errno != EAGAIN) |
||
630 | { |
||
631 | fprintf(stderr, "%s: error reading netlink: %s.\n", |
||
632 | __PRETTY_FUNCTION__, strerror(errno)); |
||
633 | } |
||
634 | return; |
||
635 | } |
||
636 | |||
637 | if(amt == 0) |
||
638 | { |
||
639 | fprintf(stderr, "%s: EOF on netlink??\n", __PRETTY_FUNCTION__); |
||
640 | return; |
||
641 | } |
||
642 | |||
643 | h = (struct nlmsghdr*)buf; |
||
644 | while(amt >= (int)sizeof(*h)) |
||
645 | { |
||
646 | int len = h->nlmsg_len; |
||
647 | int l = len - sizeof(*h); |
||
648 | |||
649 | if(l < 0 || len > amt) |
||
650 | { |
||
651 | fprintf(stderr, "%s: malformed netlink message: len=%d\n", __PRETTY_FUNCTION__, len); |
||
652 | break; |
||
653 | } |
||
654 | |||
655 | switch(h->nlmsg_type) |
||
656 | { |
||
657 | case RTM_NEWLINK: |
||
658 | case RTM_DELLINK: |
||
659 | LinkCatcher(h); |
||
660 | break; |
||
661 | default: |
||
662 | #if 0 |
||
663 | fprintf(stderr, "%s: got nlmsg of type %#x.\n", __PRETTY_FUNCTION__, h->nlmsg_type); |
||
664 | #endif |
||
665 | break; |
||
666 | } |
||
667 | |||
668 | len = NLMSG_ALIGN(len); |
||
669 | amt -= len; |
||
670 | h = (struct nlmsghdr*)((char*)h + len); |
||
671 | } |
||
672 | |||
673 | if(amt > 0) |
||
674 | fprintf(stderr, "%s: remnant of size %d on netlink\n", __PRETTY_FUNCTION__, amt); |
||
675 | } |
||
676 | } |
||
677 | |||
678 | /**************************** MAIN LOOP ****************************/ |
||
679 | |||
680 | /* ---------------------------------------------------------------- */ |
||
681 | /* |
||
682 | * Wait until we get an event |
||
683 | */ |
||
684 | static inline int |
||
685 | wait_for_event(struct rtnl_handle * rth) |
||
686 | { |
||
687 | #if 0 |
||
688 | struct timeval tv; /* Select timeout */ |
||
689 | #endif |
||
690 | |||
691 | /* Forever */ |
||
692 | while(1) |
||
693 | { |
||
694 | fd_set rfds; /* File descriptors for select */ |
||
695 | int last_fd; /* Last fd */ |
||
696 | int ret; |
||
697 | |||
698 | /* Guess what ? We must re-generate rfds each time */ |
||
699 | FD_ZERO(&rfds); |
||
700 | FD_SET(rth->fd, &rfds); |
||
701 | last_fd = rth->fd; |
||
702 | |||
703 | /* Wait until something happens */ |
||
704 | ret = select(last_fd + 1, &rfds, NULL, NULL, NULL); |
||
705 | |||
706 | /* Check if there was an error */ |
||
707 | if(ret < 0) |
||
708 | { |
||
709 | if(errno == EAGAIN || errno == EINTR) |
||
710 | continue; |
||
711 | fprintf(stderr, "Unhandled signal - exiting...\n"); |
||
712 | break; |
||
713 | } |
||
714 | |||
715 | /* Check if there was a timeout */ |
||
716 | if(ret == 0) |
||
717 | { |
||
718 | continue; |
||
719 | } |
||
720 | |||
721 | /* Check for interface discovery events. */ |
||
722 | if(FD_ISSET(rth->fd, &rfds)) |
||
723 | handle_netlink_events(rth); |
||
724 | } |
||
725 | |||
726 | return(0); |
||
727 | } |
||
728 | |||
729 | /******************************* MAIN *******************************/ |
||
730 | |||
731 | /* ---------------------------------------------------------------- */ |
||
732 | /* |
||
733 | * helper ;-) |
||
734 | */ |
||
735 | static void |
||
736 | iw_usage(int status) |
||
737 | { |
||
738 | fputs("Usage: iwevent [OPTIONS]\n" |
||
739 | " Monitors and displays Wireless Events.\n" |
||
740 | " Options are:\n" |
||
741 | " -h,--help Print this message.\n" |
||
742 | " -v,--version Show version of this program.\n", |
||
743 | status ? stderr : stdout); |
||
744 | exit(status); |
||
745 | } |
||
746 | /* Command line options */ |
||
747 | static const struct option long_opts[] = { |
||
748 | { "help", no_argument, NULL, 'h' }, |
||
749 | { "version", no_argument, NULL, 'v' }, |
||
750 | { NULL, 0, NULL, 0 } |
||
751 | }; |
||
752 | |||
753 | /* ---------------------------------------------------------------- */ |
||
754 | /* |
||
755 | * main body of the program |
||
756 | */ |
||
757 | int |
||
758 | main(int argc, |
||
759 | char * argv[]) |
||
760 | { |
||
761 | struct rtnl_handle rth; |
||
762 | int opt; |
||
763 | |||
764 | /* Check command line options */ |
||
765 | while((opt = getopt_long(argc, argv, "hv", long_opts, NULL)) > 0) |
||
766 | { |
||
767 | switch(opt) |
||
768 | { |
||
769 | case 'h': |
||
770 | iw_usage(0); |
||
771 | break; |
||
772 | |||
773 | case 'v': |
||
774 | return(iw_print_version_info("iwevent")); |
||
775 | break; |
||
776 | |||
777 | default: |
||
778 | iw_usage(1); |
||
779 | break; |
||
780 | } |
||
781 | } |
||
782 | if(optind < argc) |
||
783 | { |
||
784 | fputs("Too many arguments.\n", stderr); |
||
785 | iw_usage(1); |
||
786 | } |
||
787 | |||
788 | /* Open netlink channel */ |
||
789 | if(rtnl_open(&rth, RTMGRP_LINK) < 0) |
||
790 | { |
||
791 | perror("Can't initialize rtnetlink socket"); |
||
792 | return(1); |
||
793 | } |
||
794 | |||
795 | fprintf(stderr, "Waiting for Wireless Events from interfaces...\n"); |
||
796 | |||
797 | /* Do what we have to do */ |
||
798 | wait_for_event(&rth); |
||
799 | |||
800 | /* Cleanup - only if you are pedantic */ |
||
801 | rtnl_close(&rth); |
||
802 | |||
803 | return(0); |
||
804 | } |