Tree wide: Reformat with clang-format.
[collectd.git] / src / madwifi.c
1 /**
2  * collectd - src/madwifi.c
3  * Copyright (C) 2009  Ondrej 'SanTiago' Zajicek
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by the
7  * Free Software Foundation; only version 2 of the License is applicable.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
17  *
18  * Author:
19  *   Ondrej 'SanTiago' Zajicek <santiago@crfreenet.org>
20  *
21  *   based on some code from interfaces.c (collectd) and Madwifi driver
22  **/
23
24 /**
25  * There are several data streams provided by Madwifi plugin, some are
26  * connected to network interface, some are connected to each node
27  * associated to that interface. Nodes represents other sides in
28  * wireless communication, for example on network interface in AP mode,
29  * there is one node for each associated station. Node data streams
30  * contain MAC address of the node as the last part  of the type_instance
31  * field.
32  *
33  * Inteface data streams:
34  *      ath_nodes       The number of associated nodes
35  *      ath_stat        Device statistic counters
36  *
37  * Node data streams:
38  *      node_octets     RX and TX data count (octets/bytes)
39  *      node_rssi       Received RSSI of the node
40  *      node_tx_rate    Reported TX rate to that node
41  *      node_stat       Node statistic counters
42  *
43  * Both statistic counters have type instances for each counter returned
44  * by Madwifi. See madwifi.h for content of ieee80211_nodestats,
45  * ieee80211_stats and ath_stats structures. Type instances use the same
46  * name as fields in these structures (like ns_rx_dup). Some fields are
47  * not reported, because they are not counters (like ns_tx_deauth_code
48  * or ast_tx_rssi). Fields ns_rx_bytes and ns_tx_bytes are reported as
49  * node_octets data stream instead of type instance of node_stat.
50  * Statistics are not logged when they are zero.
51  *
52  * There are two sets of these counters - the first 'WatchList' is a
53  * set of counters that are individually logged. The second 'MiscList'
54  * is a set of counters that are summed together and the sum is logged.
55  * By default, the most important statistics are in the WatchList and
56  * many error statistics are in MiscList. There are also many statistics
57  * that are not in any of these sets, so they are not monitored by default.
58  * It is possible to alter these lists using configuration options:
59  *
60  *      WatchAdd X      Adds X to WachList
61  *      WatchRemove X   Removes X from WachList
62  *      WatchSet All    Adds all statistics to WatchList
63  *      WatchSet None   Removes all statistics from WachList
64  *
65  * There are also Misc* variants fo these options, they modifies MiscList
66  * instead of WatchList.
67  *
68  * Example:
69  *
70  *      WatchSet None
71  *      WatchAdd node_octets
72  *      WatchAdd node_rssi
73  *      WatchAdd is_rx_acl
74  *      WatchAdd is_scan_active
75  *
76  * That causes that just the four mentioned data streams are logged.
77  *
78  *
79  * By default, madwifi plugin enumerates network interfaces using /sys
80  * filesystem. Configuration option `Source' can change this to use
81  * /proc filesystem (which is useful for example when running on Linux
82  * 2.4). But without /sys filesystem, Madwifi plugin cannot check whether
83  * given interface is madwifi interface and there are private ioctls used,
84  * which may do something completely different on non-madwifi devices.
85  * Therefore, the /proc filesystem should always be used together with option
86  * `Interface', to limit found interfaces to madwifi interfaces only.
87  **/
88
89 #include "collectd.h"
90
91 #include "common.h"
92 #include "plugin.h"
93 #include "utils_ignorelist.h"
94
95 #include <dirent.h>
96 #include <sys/ioctl.h>
97
98 #if !KERNEL_LINUX
99 #error "No applicable input method."
100 #endif
101
102 #include <linux/wireless.h>
103 #include "madwifi.h"
104
105 struct stat_spec {
106   uint16_t flags;
107   uint16_t offset;
108   const char *name;
109 };
110
111 #define OFFSETOF(s, i) ((size_t) & ((s *)0)->i)
112
113 #define FLAG(i) (((uint32_t)1) << ((i) % 32))
114
115 #define SPC_STAT 0
116 #define NOD_STAT 1
117 #define IFA_STAT 2
118 #define ATH_STAT 3
119 #define SRC_MASK 3
120
121 /* By default, the item is disabled */
122 #define D 0
123
124 /* By default, the item is logged */
125 #define LOG 4
126
127 /* By default, the item is summed with other such items and logged together */
128 #define SU 8
129
130 #define SS_STAT(flags, name)                                                   \
131   { flags | SPC_STAT, 0, #name }
132 #define NS_STAT(flags, name)                                                   \
133   { flags | NOD_STAT, OFFSETOF(struct ieee80211_nodestats, name), #name }
134 #define IS_STAT(flags, name)                                                   \
135   { flags | IFA_STAT, OFFSETOF(struct ieee80211_stats, name), #name }
136 #define AS_STAT(flags, name)                                                   \
137   { flags | ATH_STAT, OFFSETOF(struct ath_stats, name), #name }
138
139 /*
140  * (Module-)Global variables
141  */
142
143 /* Indices of special stats in specs array */
144 #define STAT_NODE_OCTETS 0
145 #define STAT_NODE_RSSI 1
146 #define STAT_NODE_TX_RATE 2
147 #define STAT_ATH_NODES 3
148 #define STAT_NS_RX_BEACONS 4
149 #define STAT_AST_ANT_RX 5
150 #define STAT_AST_ANT_TX 6
151
152 static struct stat_spec specs[] = {
153
154     /* Special statistics */
155     SS_STAT(LOG, node_octets),  /* rx and tx data count (bytes) */
156     SS_STAT(LOG, node_rssi),    /* received RSSI of the node */
157     SS_STAT(LOG, node_tx_rate), /* used tx rate to the node */
158     SS_STAT(LOG, ath_nodes),    /* the number of associated nodes */
159     SS_STAT(D, ns_rx_beacons),  /* rx beacon frames */
160     SS_STAT(LOG, ast_ant_rx),   /* rx frames with antenna */
161     SS_STAT(LOG, ast_ant_tx),   /* tx frames with antenna */
162
163     /* Node statistics */
164     NS_STAT(LOG, ns_rx_data),        /* rx data frames */
165     NS_STAT(LOG, ns_rx_mgmt),        /* rx management frames */
166     NS_STAT(LOG, ns_rx_ctrl),        /* rx control frames */
167     NS_STAT(D, ns_rx_ucast),         /* rx unicast frames */
168     NS_STAT(D, ns_rx_mcast),         /* rx multi/broadcast frames */
169     NS_STAT(D, ns_rx_proberesp),     /* rx probe response frames */
170     NS_STAT(LOG, ns_rx_dup),         /* rx discard because it's a dup */
171     NS_STAT(SU, ns_rx_noprivacy),    /* rx w/ wep but privacy off */
172     NS_STAT(SU, ns_rx_wepfail),      /* rx wep processing failed */
173     NS_STAT(SU, ns_rx_demicfail),    /* rx demic failed */
174     NS_STAT(SU, ns_rx_decap),        /* rx decapsulation failed */
175     NS_STAT(SU, ns_rx_defrag),       /* rx defragmentation failed */
176     NS_STAT(D, ns_rx_disassoc),      /* rx disassociation */
177     NS_STAT(D, ns_rx_deauth),        /* rx deauthentication */
178     NS_STAT(SU, ns_rx_decryptcrc),   /* rx decrypt failed on crc */
179     NS_STAT(SU, ns_rx_unauth),       /* rx on unauthorized port */
180     NS_STAT(SU, ns_rx_unencrypted),  /* rx unecrypted w/ privacy */
181     NS_STAT(LOG, ns_tx_data),        /* tx data frames */
182     NS_STAT(LOG, ns_tx_mgmt),        /* tx management frames */
183     NS_STAT(D, ns_tx_ucast),         /* tx unicast frames */
184     NS_STAT(D, ns_tx_mcast),         /* tx multi/broadcast frames */
185     NS_STAT(D, ns_tx_probereq),      /* tx probe request frames */
186     NS_STAT(D, ns_tx_uapsd),         /* tx on uapsd queue */
187     NS_STAT(SU, ns_tx_novlantag),    /* tx discard due to no tag */
188     NS_STAT(SU, ns_tx_vlanmismatch), /* tx discard due to of bad tag */
189     NS_STAT(D, ns_tx_eosplost),      /* uapsd EOSP retried out */
190     NS_STAT(D, ns_ps_discard),       /* ps discard due to of age */
191     NS_STAT(D, ns_uapsd_triggers),   /* uapsd triggers */
192     NS_STAT(LOG, ns_tx_assoc),       /* [re]associations */
193     NS_STAT(LOG, ns_tx_auth),        /* [re]authentications */
194     NS_STAT(D, ns_tx_deauth),        /* deauthentications */
195     NS_STAT(D, ns_tx_disassoc),      /* disassociations */
196     NS_STAT(D, ns_psq_drops),        /* power save queue drops */
197
198     /* Iface statistics */
199     IS_STAT(SU, is_rx_badversion),       /* rx frame with bad version */
200     IS_STAT(SU, is_rx_tooshort),         /* rx frame too short */
201     IS_STAT(LOG, is_rx_wrongbss),        /* rx from wrong bssid */
202     IS_STAT(LOG, is_rx_dup),             /* rx discard due to it's a dup */
203     IS_STAT(SU, is_rx_wrongdir),         /* rx w/ wrong direction */
204     IS_STAT(D, is_rx_mcastecho),         /* rx discard due to of mcast echo */
205     IS_STAT(SU, is_rx_notassoc),         /* rx discard due to sta !assoc */
206     IS_STAT(SU, is_rx_noprivacy),        /* rx w/ wep but privacy off */
207     IS_STAT(SU, is_rx_unencrypted),      /* rx w/o wep and privacy on */
208     IS_STAT(SU, is_rx_wepfail),          /* rx wep processing failed */
209     IS_STAT(SU, is_rx_decap),            /* rx decapsulation failed */
210     IS_STAT(D, is_rx_mgtdiscard),        /* rx discard mgt frames */
211     IS_STAT(D, is_rx_ctl),               /* rx discard ctrl frames */
212     IS_STAT(D, is_rx_beacon),            /* rx beacon frames */
213     IS_STAT(D, is_rx_rstoobig),          /* rx rate set truncated */
214     IS_STAT(SU, is_rx_elem_missing),     /* rx required element missing*/
215     IS_STAT(SU, is_rx_elem_toobig),      /* rx element too big */
216     IS_STAT(SU, is_rx_elem_toosmall),    /* rx element too small */
217     IS_STAT(LOG, is_rx_elem_unknown),    /* rx element unknown */
218     IS_STAT(SU, is_rx_badchan),          /* rx frame w/ invalid chan */
219     IS_STAT(SU, is_rx_chanmismatch),     /* rx frame chan mismatch */
220     IS_STAT(SU, is_rx_nodealloc),        /* rx frame dropped */
221     IS_STAT(LOG, is_rx_ssidmismatch),    /* rx frame ssid mismatch  */
222     IS_STAT(SU, is_rx_auth_unsupported), /* rx w/ unsupported auth alg */
223     IS_STAT(SU, is_rx_auth_fail),        /* rx sta auth failure */
224     IS_STAT(SU, is_rx_auth_countermeasures), /* rx auth discard due to CM */
225     IS_STAT(SU, is_rx_assoc_bss),            /* rx assoc from wrong bssid */
226     IS_STAT(SU, is_rx_assoc_notauth),        /* rx assoc w/o auth */
227     IS_STAT(SU, is_rx_assoc_capmismatch),    /* rx assoc w/ cap mismatch */
228     IS_STAT(SU, is_rx_assoc_norate),         /* rx assoc w/ no rate match */
229     IS_STAT(SU, is_rx_assoc_badwpaie),       /* rx assoc w/ bad WPA IE */
230     IS_STAT(LOG, is_rx_deauth),              /* rx deauthentication */
231     IS_STAT(LOG, is_rx_disassoc),            /* rx disassociation */
232     IS_STAT(SU, is_rx_badsubtype),           /* rx frame w/ unknown subtype*/
233     IS_STAT(SU, is_rx_nobuf),                /* rx failed for lack of buf */
234     IS_STAT(SU, is_rx_decryptcrc),           /* rx decrypt failed on crc */
235     IS_STAT(D, is_rx_ahdemo_mgt),            /* rx discard ahdemo mgt frame*/
236     IS_STAT(SU, is_rx_bad_auth),             /* rx bad auth request */
237     IS_STAT(SU, is_rx_unauth),               /* rx on unauthorized port */
238     IS_STAT(SU, is_rx_badkeyid),             /* rx w/ incorrect keyid */
239     IS_STAT(D, is_rx_ccmpreplay),            /* rx seq# violation (CCMP), */
240     IS_STAT(D, is_rx_ccmpformat),            /* rx format bad (CCMP), */
241     IS_STAT(D, is_rx_ccmpmic),               /* rx MIC check failed (CCMP), */
242     IS_STAT(D, is_rx_tkipreplay),            /* rx seq# violation (TKIP), */
243     IS_STAT(D, is_rx_tkipformat),            /* rx format bad (TKIP), */
244     IS_STAT(D, is_rx_tkipmic),               /* rx MIC check failed (TKIP), */
245     IS_STAT(D, is_rx_tkipicv),               /* rx ICV check failed (TKIP), */
246     IS_STAT(D, is_rx_badcipher),             /* rx failed due to of key type */
247     IS_STAT(D, is_rx_nocipherctx),           /* rx failed due to key !setup */
248     IS_STAT(D, is_rx_acl),               /* rx discard due to of acl policy */
249     IS_STAT(D, is_rx_ffcnt),             /* rx fast frames */
250     IS_STAT(SU, is_rx_badathtnl),        /* driver key alloc failed */
251     IS_STAT(SU, is_tx_nobuf),            /* tx failed for lack of buf */
252     IS_STAT(SU, is_tx_nonode),           /* tx failed for no node */
253     IS_STAT(SU, is_tx_unknownmgt),       /* tx of unknown mgt frame */
254     IS_STAT(SU, is_tx_badcipher),        /* tx failed due to of key type */
255     IS_STAT(SU, is_tx_nodefkey),         /* tx failed due to no defkey */
256     IS_STAT(SU, is_tx_noheadroom),       /* tx failed due to no space */
257     IS_STAT(D, is_tx_ffokcnt),           /* tx fast frames sent success */
258     IS_STAT(D, is_tx_fferrcnt),          /* tx fast frames sent success */
259     IS_STAT(D, is_scan_active),          /* active scans started */
260     IS_STAT(D, is_scan_passive),         /* passive scans started */
261     IS_STAT(D, is_node_timeout),         /* nodes timed out inactivity */
262     IS_STAT(D, is_crypto_nomem),         /* no memory for crypto ctx */
263     IS_STAT(D, is_crypto_tkip),          /* tkip crypto done in s/w */
264     IS_STAT(D, is_crypto_tkipenmic),     /* tkip en-MIC done in s/w */
265     IS_STAT(D, is_crypto_tkipdemic),     /* tkip de-MIC done in s/w */
266     IS_STAT(D, is_crypto_tkipcm),        /* tkip counter measures */
267     IS_STAT(D, is_crypto_ccmp),          /* ccmp crypto done in s/w */
268     IS_STAT(D, is_crypto_wep),           /* wep crypto done in s/w */
269     IS_STAT(D, is_crypto_setkey_cipher), /* cipher rejected key */
270     IS_STAT(D, is_crypto_setkey_nokey),  /* no key index for setkey */
271     IS_STAT(D, is_crypto_delkey),        /* driver key delete failed */
272     IS_STAT(D, is_crypto_badcipher),     /* unknown cipher */
273     IS_STAT(D, is_crypto_nocipher),      /* cipher not available */
274     IS_STAT(D, is_crypto_attachfail),    /* cipher attach failed */
275     IS_STAT(D, is_crypto_swfallback),    /* cipher fallback to s/w */
276     IS_STAT(D, is_crypto_keyfail),       /* driver key alloc failed */
277     IS_STAT(D, is_crypto_enmicfail),     /* en-MIC failed */
278     IS_STAT(SU, is_ibss_capmismatch),    /* merge failed-cap mismatch */
279     IS_STAT(SU, is_ibss_norate),         /* merge failed-rate mismatch */
280     IS_STAT(D, is_ps_unassoc),           /* ps-poll for unassoc. sta */
281     IS_STAT(D, is_ps_badaid),            /* ps-poll w/ incorrect aid */
282     IS_STAT(D, is_ps_qempty),            /* ps-poll w/ nothing to send */
283
284     /* Atheros statistics */
285     AS_STAT(D, ast_watchdog),     /* device reset by watchdog */
286     AS_STAT(D, ast_hardware),     /* fatal hardware error interrupts */
287     AS_STAT(D, ast_bmiss),        /* beacon miss interrupts */
288     AS_STAT(D, ast_rxorn),        /* rx overrun interrupts */
289     AS_STAT(D, ast_rxeol),        /* rx eol interrupts */
290     AS_STAT(D, ast_txurn),        /* tx underrun interrupts */
291     AS_STAT(D, ast_mib),          /* mib interrupts */
292     AS_STAT(D, ast_tx_packets),   /* packet sent on the interface */
293     AS_STAT(D, ast_tx_mgmt),      /* management frames transmitted */
294     AS_STAT(LOG, ast_tx_discard), /* frames discarded prior to assoc */
295     AS_STAT(SU, ast_tx_invalid),  /* frames discarded due to is device gone */
296     AS_STAT(SU, ast_tx_qstop),    /* tx queue stopped because it's full */
297     AS_STAT(SU, ast_tx_encap),    /* tx encapsulation failed */
298     AS_STAT(SU, ast_tx_nonode),   /* tx failed due to of no node */
299     AS_STAT(SU, ast_tx_nobuf),    /* tx failed due to of no tx buffer (data), */
300     AS_STAT(SU, ast_tx_nobufmgt), /* tx failed due to of no tx buffer (mgmt),*/
301     AS_STAT(LOG, ast_tx_xretries),   /* tx failed due to of too many retries */
302     AS_STAT(SU, ast_tx_fifoerr),     /* tx failed due to of FIFO underrun */
303     AS_STAT(SU, ast_tx_filtered),    /* tx failed due to xmit filtered */
304     AS_STAT(LOG, ast_tx_shortretry), /* tx on-chip retries (short), */
305     AS_STAT(LOG, ast_tx_longretry),  /* tx on-chip retries (long), */
306     AS_STAT(SU, ast_tx_badrate),     /* tx failed due to of bogus xmit rate */
307     AS_STAT(D, ast_tx_noack),        /* tx frames with no ack marked */
308     AS_STAT(D, ast_tx_rts),          /* tx frames with rts enabled */
309     AS_STAT(D, ast_tx_cts),          /* tx frames with cts enabled */
310     AS_STAT(D, ast_tx_shortpre),     /* tx frames with short preamble */
311     AS_STAT(LOG, ast_tx_altrate),    /* tx frames with alternate rate */
312     AS_STAT(D, ast_tx_protect),      /* tx frames with protection */
313     AS_STAT(SU, ast_rx_orn),         /* rx failed due to of desc overrun */
314     AS_STAT(LOG, ast_rx_crcerr),     /* rx failed due to of bad CRC */
315     AS_STAT(SU, ast_rx_fifoerr),     /* rx failed due to of FIFO overrun */
316     AS_STAT(SU, ast_rx_badcrypt),    /* rx failed due to of decryption */
317     AS_STAT(SU, ast_rx_badmic),      /* rx failed due to of MIC failure */
318     AS_STAT(LOG, ast_rx_phyerr),     /* rx PHY error summary count */
319     AS_STAT(SU, ast_rx_tooshort),    /* rx discarded due to frame too short */
320     AS_STAT(SU, ast_rx_toobig),      /* rx discarded due to frame too large */
321     AS_STAT(SU, ast_rx_nobuf),       /* rx setup failed due to of no skbuff */
322     AS_STAT(D, ast_rx_packets),      /* packet recv on the interface */
323     AS_STAT(D, ast_rx_mgt),          /* management frames received */
324     AS_STAT(D, ast_rx_ctl),          /* control frames received */
325     AS_STAT(D, ast_be_xmit),         /* beacons transmitted */
326     AS_STAT(SU, ast_be_nobuf),       /* no skbuff available for beacon */
327     AS_STAT(D, ast_per_cal),         /* periodic calibration calls */
328     AS_STAT(D, ast_per_calfail),     /* periodic calibration failed */
329     AS_STAT(D, ast_per_rfgain),      /* periodic calibration rfgain reset */
330     AS_STAT(D, ast_rate_calls),      /* rate control checks */
331     AS_STAT(D, ast_rate_raise),      /* rate control raised xmit rate */
332     AS_STAT(D, ast_rate_drop),       /* rate control dropped xmit rate */
333     AS_STAT(D, ast_ant_defswitch),   /* rx/default antenna switches */
334     AS_STAT(D, ast_ant_txswitch)     /* tx antenna switches */
335 };
336
337 /* Bounds between SS, NS, IS and AS stats in stats array */
338 static int bounds[4];
339
340 #define WL_LEN 6
341 /* Bitmasks for logged and error items */
342 static uint32_t watch_items[WL_LEN];
343 static uint32_t misc_items[WL_LEN];
344
345 static const char *config_keys[] = {"Interface", "IgnoreSelected", "Source",
346                                     "WatchAdd",  "WatchRemove",    "WatchSet",
347                                     "MiscAdd",   "MiscRemove",     "MiscSet"};
348 static int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
349
350 static ignorelist_t *ignorelist = NULL;
351
352 static int use_sysfs = 1;
353 static int init_state = 0;
354
355 static inline int item_watched(int i) {
356   assert(i >= 0);
357   assert((size_t)i < (STATIC_ARRAY_SIZE(watch_items) * 32));
358   return watch_items[i / 32] & FLAG(i);
359 }
360
361 static inline int item_summed(int i) {
362   assert(i >= 0);
363   assert((size_t)i < (STATIC_ARRAY_SIZE(misc_items) * 32));
364   return misc_items[i / 32] & FLAG(i);
365 }
366
367 static inline void watchlist_add(uint32_t *wl, int item) {
368   assert(item >= 0);
369   assert(item < WL_LEN * 32);
370   wl[item / 32] |= FLAG(item);
371 }
372
373 static inline void watchlist_remove(uint32_t *wl, int item) {
374   assert(item >= 0);
375   assert(item < WL_LEN * 32);
376   wl[item / 32] &= ~FLAG(item);
377 }
378
379 static inline void watchlist_set(uint32_t *wl, uint32_t val) {
380   for (int i = 0; i < WL_LEN; i++)
381     wl[i] = val;
382 }
383
384 /* This is horribly inefficient, but it is called only during configuration */
385 static int watchitem_find(const char *name) {
386   int max = STATIC_ARRAY_SIZE(specs);
387
388   for (int i = 0; i < max; i++)
389     if (strcasecmp(name, specs[i].name) == 0)
390       return i;
391
392   return -1;
393 }
394
395 /* Collectd hooks */
396
397 /* We need init function called before madwifi_config */
398
399 static int madwifi_real_init(void) {
400   size_t max = STATIC_ARRAY_SIZE(specs);
401
402   for (size_t i = 0; i < STATIC_ARRAY_SIZE(bounds); i++)
403     bounds[i] = 0;
404
405   watchlist_set(watch_items, 0);
406   watchlist_set(misc_items, 0);
407
408   for (size_t i = 0; i < max; i++) {
409     bounds[specs[i].flags & SRC_MASK] = i;
410
411     if (specs[i].flags & LOG)
412       watch_items[i / 32] |= FLAG(i);
413
414     if (specs[i].flags & SU)
415       misc_items[i / 32] |= FLAG(i);
416   }
417
418   for (size_t i = 0; i < STATIC_ARRAY_SIZE(bounds); i++)
419     bounds[i]++;
420
421   return (0);
422 }
423
424 static int madwifi_config(const char *key, const char *value) {
425   if (init_state != 1)
426     madwifi_real_init();
427   init_state = 1;
428
429   if (ignorelist == NULL)
430     ignorelist = ignorelist_create(/* invert = */ 1);
431
432   if (strcasecmp(key, "Interface") == 0)
433     ignorelist_add(ignorelist, value);
434
435   else if (strcasecmp(key, "IgnoreSelected") == 0)
436     ignorelist_set_invert(ignorelist, IS_TRUE(value) ? 0 : 1);
437
438   else if (strcasecmp(key, "Source") == 0) {
439     if (strcasecmp(value, "ProcFS") == 0)
440       use_sysfs = 0;
441     else if (strcasecmp(value, "SysFS") == 0)
442       use_sysfs = 1;
443     else {
444       ERROR("madwifi plugin: The argument of the `Source' "
445             "option must either be `SysFS' or "
446             "`ProcFS'.");
447       return -1;
448     }
449   }
450
451   else if (strcasecmp(key, "WatchSet") == 0) {
452     if (strcasecmp(value, "All") == 0)
453       watchlist_set(watch_items, 0xFFFFFFFF);
454     else if (strcasecmp(value, "None") == 0)
455       watchlist_set(watch_items, 0);
456     else
457       return -1;
458   }
459
460   else if (strcasecmp(key, "WatchAdd") == 0) {
461     int id = watchitem_find(value);
462
463     if (id < 0)
464       return (-1);
465     else
466       watchlist_add(watch_items, id);
467   }
468
469   else if (strcasecmp(key, "WatchRemove") == 0) {
470     int id = watchitem_find(value);
471
472     if (id < 0)
473       return (-1);
474     else
475       watchlist_remove(watch_items, id);
476   }
477
478   else if (strcasecmp(key, "MiscSet") == 0) {
479     if (strcasecmp(value, "All") == 0)
480       watchlist_set(misc_items, 0xFFFFFFFF);
481     else if (strcasecmp(value, "None") == 0)
482       watchlist_set(misc_items, 0);
483     else
484       return -1;
485   }
486
487   else if (strcasecmp(key, "MiscAdd") == 0) {
488     int id = watchitem_find(value);
489
490     if (id < 0)
491       return (-1);
492     else
493       watchlist_add(misc_items, id);
494   }
495
496   else if (strcasecmp(key, "MiscRemove") == 0) {
497     int id = watchitem_find(value);
498
499     if (id < 0)
500       return (-1);
501     else
502       watchlist_remove(misc_items, id);
503   }
504
505   else
506     return (-1);
507
508   return (0);
509 }
510
511 static void submit(const char *dev, const char *type, const char *ti1,
512                    const char *ti2, value_t *val, int len) {
513   value_list_t vl = VALUE_LIST_INIT;
514
515   vl.values = val;
516   vl.values_len = len;
517   sstrncpy(vl.host, hostname_g, sizeof(vl.host));
518   sstrncpy(vl.plugin, "madwifi", sizeof(vl.plugin));
519   sstrncpy(vl.plugin_instance, dev, sizeof(vl.plugin_instance));
520   sstrncpy(vl.type, type, sizeof(vl.type));
521
522   if ((ti1 != NULL) && (ti2 != NULL))
523     ssnprintf(vl.type_instance, sizeof(vl.type_instance), "%s-%s", ti1, ti2);
524   else if ((ti1 != NULL) && (ti2 == NULL))
525     sstrncpy(vl.type_instance, ti1, sizeof(vl.type_instance));
526
527   plugin_dispatch_values(&vl);
528 }
529
530 static void submit_derive(const char *dev, const char *type, const char *ti1,
531                           const char *ti2, derive_t val) {
532   value_t item;
533   item.derive = val;
534   submit(dev, type, ti1, ti2, &item, 1);
535 }
536
537 static void submit_derive2(const char *dev, const char *type, const char *ti1,
538                            const char *ti2, derive_t val1, derive_t val2) {
539   value_t items[2];
540   items[0].derive = val1;
541   items[1].derive = val2;
542   submit(dev, type, ti1, ti2, items, 2);
543 }
544
545 static void submit_gauge(const char *dev, const char *type, const char *ti1,
546                          const char *ti2, gauge_t val) {
547   value_t item;
548   item.gauge = val;
549   submit(dev, type, ti1, ti2, &item, 1);
550 }
551
552 static void submit_antx(const char *dev, const char *name, u_int32_t *vals,
553                         int vals_num) {
554   char ti2[16];
555
556   for (int i = 0; i < vals_num; i++) {
557     if (vals[i] == 0)
558       continue;
559
560     ssnprintf(ti2, sizeof(ti2), "%i", i);
561     submit_derive(dev, "ath_stat", name, ti2, (derive_t)vals[i]);
562   }
563 }
564
565 static inline void macaddr_to_str(char *buf, size_t bufsize,
566                                   const uint8_t mac[IEEE80211_ADDR_LEN]) {
567   ssnprintf(buf, bufsize, "%02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1],
568             mac[2], mac[3], mac[4], mac[5]);
569 }
570
571 static void process_stat_struct(int which, const void *ptr, const char *dev,
572                                 const char *mac, const char *type_name,
573                                 const char *misc_name) {
574   uint32_t misc = 0;
575
576   assert(which >= 1);
577   assert(((size_t)which) < STATIC_ARRAY_SIZE(bounds));
578
579   for (int i = bounds[which - 1]; i < bounds[which]; i++) {
580     uint32_t val = *(uint32_t *)(((char *)ptr) + specs[i].offset);
581
582     if (item_watched(i) && (val != 0))
583       submit_derive(dev, type_name, specs[i].name, mac, val);
584
585     if (item_summed(i))
586       misc += val;
587   }
588
589   if (misc != 0)
590     submit_derive(dev, type_name, misc_name, mac, misc);
591 }
592
593 static int process_athstats(int sk, const char *dev) {
594   struct ifreq ifr;
595   struct ath_stats stats;
596   int status;
597
598   sstrncpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name));
599   ifr.ifr_data = (void *)&stats;
600   status = ioctl(sk, SIOCGATHSTATS, &ifr);
601   if (status < 0) {
602     /* Silent, because not all interfaces support all ioctls. */
603     DEBUG("madwifi plugin: Sending IO-control "
604           "SIOCGATHSTATS to device %s "
605           "failed with status %i.",
606           dev, status);
607     return (status);
608   }
609
610   /* These stats are handled as a special case, because they are
611      eight values each */
612
613   if (item_watched(STAT_AST_ANT_RX))
614     submit_antx(dev, "ast_ant_rx", stats.ast_ant_rx,
615                 STATIC_ARRAY_SIZE(stats.ast_ant_rx));
616
617   if (item_watched(STAT_AST_ANT_TX))
618     submit_antx(dev, "ast_ant_tx", stats.ast_ant_tx,
619                 STATIC_ARRAY_SIZE(stats.ast_ant_tx));
620
621   /* All other ath statistics */
622   process_stat_struct(ATH_STAT, &stats, dev, NULL, "ath_stat", "ast_misc");
623   return (0);
624 }
625
626 static int process_80211stats(int sk, const char *dev) {
627   struct ifreq ifr;
628   struct ieee80211_stats stats;
629   int status;
630
631   sstrncpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name));
632   ifr.ifr_data = (void *)&stats;
633   status = ioctl(sk, SIOCG80211STATS, &ifr);
634   if (status < 0) {
635     /* Silent, because not all interfaces support all ioctls. */
636     DEBUG("madwifi plugin: Sending IO-control "
637           "SIOCG80211STATS to device %s "
638           "failed with status %i.",
639           dev, status);
640     return (status);
641   }
642
643   process_stat_struct(IFA_STAT, &stats, dev, NULL, "ath_stat", "is_misc");
644   return (0);
645 }
646
647 static int process_station(int sk, const char *dev,
648                            struct ieee80211req_sta_info *si) {
649   static char mac[DATA_MAX_NAME_LEN];
650   struct ieee80211req_sta_stats stats;
651   const struct ieee80211_nodestats *ns = &stats.is_stats;
652   int status;
653
654   macaddr_to_str(mac, sizeof(mac), si->isi_macaddr);
655
656   if (item_watched(STAT_NODE_TX_RATE))
657     submit_gauge(dev, "node_tx_rate", mac, NULL,
658                  (si->isi_rates[si->isi_txrate] & IEEE80211_RATE_VAL) / 2);
659
660   if (item_watched(STAT_NODE_RSSI))
661     submit_gauge(dev, "node_rssi", mac, NULL, si->isi_rssi);
662
663   struct iwreq iwr = {.u.data.pointer = (void *)&stats,
664                       .u.data.length = sizeof(stats)};
665   sstrncpy(iwr.ifr_name, dev, sizeof(iwr.ifr_name));
666
667   memcpy(stats.is_u.macaddr, si->isi_macaddr, IEEE80211_ADDR_LEN);
668   status = ioctl(sk, IEEE80211_IOCTL_STA_STATS, &iwr);
669   if (status < 0) {
670     /* Silent, because not all interfaces support all ioctls. */
671     DEBUG("madwifi plugin: Sending IO-control "
672           "IEEE80211_IOCTL_STA_STATS to device %s "
673           "failed with status %i.",
674           dev, status);
675     return (status);
676   }
677
678   /* These two stats are handled as a special case as they are
679      a pair of 64bit values */
680   if (item_watched(STAT_NODE_OCTETS))
681     submit_derive2(dev, "node_octets", mac, NULL, ns->ns_rx_bytes,
682                    ns->ns_tx_bytes);
683
684   /* This stat is handled as a special case, because it is stored
685      as uin64_t, but we will ignore upper half */
686   if (item_watched(STAT_NS_RX_BEACONS))
687     submit_derive(dev, "node_stat", "ns_rx_beacons", mac,
688                   (ns->ns_rx_beacons & 0xFFFFFFFF));
689
690   /* All other node statistics */
691   process_stat_struct(NOD_STAT, ns, dev, mac, "node_stat", "ns_misc");
692   return (0);
693 }
694
695 static int process_stations(int sk, const char *dev) {
696   uint8_t buf[24 * 1024] = {0};
697   uint8_t *cp;
698   int nodes;
699   size_t len;
700   int status;
701
702   struct iwreq iwr = {.u.data.pointer = (void *)buf,
703                       .u.data.length = sizeof(buf)};
704   sstrncpy(iwr.ifr_name, dev, sizeof(iwr.ifr_name));
705
706   status = ioctl(sk, IEEE80211_IOCTL_STA_INFO, &iwr);
707   if (status < 0) {
708     /* Silent, because not all interfaces support all ioctls. */
709     DEBUG("madwifi plugin: Sending IO-control "
710           "IEEE80211_IOCTL_STA_INFO to device %s "
711           "failed with status %i.",
712           dev, status);
713     return (status);
714   }
715
716   len = iwr.u.data.length;
717
718   cp = buf;
719   nodes = 0;
720   while (len >= sizeof(struct ieee80211req_sta_info)) {
721     struct ieee80211req_sta_info *si = (void *)cp;
722     process_station(sk, dev, si);
723     cp += si->isi_len;
724     len -= si->isi_len;
725     nodes++;
726   }
727
728   if (item_watched(STAT_ATH_NODES))
729     submit_gauge(dev, "ath_nodes", NULL, NULL, nodes);
730   return (0);
731 }
732
733 static int process_device(int sk, const char *dev) {
734   int num_success = 0;
735   int status;
736
737   status = process_athstats(sk, dev);
738   if (status == 0)
739     num_success++;
740
741   status = process_80211stats(sk, dev);
742   if (status == 0)
743     num_success++;
744
745   status = process_stations(sk, dev);
746   if (status == 0)
747     num_success++;
748
749   return ((num_success == 0) ? -1 : 0);
750 }
751
752 static int check_devname(const char *dev) {
753   char buf[PATH_MAX];
754   char buf2[PATH_MAX];
755   int i;
756
757   if (dev[0] == '.')
758     return 0;
759
760   ssnprintf(buf, sizeof(buf), "/sys/class/net/%s/device/driver", dev);
761   buf[sizeof(buf) - 1] = '\0';
762
763   i = readlink(buf, buf2, sizeof(buf2) - 1);
764   if (i < 0)
765     return 0;
766
767   buf2[i] = '\0';
768
769   if (strstr(buf2, "/drivers/ath_") == NULL)
770     return 0;
771   return 1;
772 }
773
774 static int sysfs_iterate(int sk) {
775   struct dirent *de;
776   DIR *nets;
777   int status;
778   int num_success;
779   int num_fail;
780
781   nets = opendir("/sys/class/net/");
782   if (nets == NULL) {
783     WARNING("madwifi plugin: opening /sys/class/net failed");
784     return (-1);
785   }
786
787   num_success = 0;
788   num_fail = 0;
789   while ((de = readdir(nets))) {
790     if (check_devname(de->d_name) == 0)
791       continue;
792
793     if (ignorelist_match(ignorelist, de->d_name) != 0)
794       continue;
795
796     status = process_device(sk, de->d_name);
797     if (status != 0) {
798       ERROR("madwifi plugin: Processing interface "
799             "%s failed.",
800             de->d_name);
801       num_fail++;
802     } else {
803       num_success++;
804     }
805   } /* while (readdir) */
806
807   closedir(nets);
808
809   if ((num_success == 0) && (num_fail != 0))
810     return (-1);
811   return (0);
812 }
813
814 static int procfs_iterate(int sk) {
815   char buffer[1024];
816   char *device, *dummy;
817   FILE *fh;
818   int status;
819   int num_success;
820   int num_fail;
821
822   if ((fh = fopen("/proc/net/dev", "r")) == NULL) {
823     WARNING("madwifi plugin: opening /proc/net/dev failed");
824     return (-1);
825   }
826
827   num_success = 0;
828   num_fail = 0;
829   while (fgets(buffer, sizeof(buffer), fh) != NULL) {
830     dummy = strchr(buffer, ':');
831     if (dummy == NULL)
832       continue;
833     dummy[0] = 0;
834
835     device = buffer;
836     while (device[0] == ' ')
837       device++;
838
839     if (device[0] == 0)
840       continue;
841
842     if (ignorelist_match(ignorelist, device) != 0)
843       continue;
844
845     status = process_device(sk, device);
846     if (status != 0) {
847       ERROR("madwifi plugin: Processing interface "
848             "%s failed.",
849             device);
850       num_fail++;
851     } else {
852       num_success++;
853     }
854   } /* while (fgets) */
855
856   fclose(fh);
857
858   if ((num_success == 0) && (num_fail != 0))
859     return (-1);
860   return 0;
861 }
862
863 static int madwifi_read(void) {
864   int rv;
865   int sk;
866
867   if (init_state == 0)
868     madwifi_real_init();
869   init_state = 2;
870
871   sk = socket(AF_INET, SOCK_DGRAM, 0);
872   if (sk < 0)
873     return (-1);
874
875   /* procfs iteration is not safe because it does not check whether given
876      interface is madwifi interface and there are private ioctls used, which
877      may do something completely different on non-madwifi devices.
878      Therefore, it is not used unless explicitly enabled (and should be used
879      together with ignorelist). */
880
881   if (use_sysfs)
882     rv = sysfs_iterate(sk);
883   else
884     rv = procfs_iterate(sk);
885
886   close(sk);
887
888   return rv;
889 }
890
891 void module_register(void) {
892   plugin_register_config("madwifi", madwifi_config, config_keys,
893                          config_keys_num);
894
895   plugin_register_read("madwifi", madwifi_read);
896 }