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