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