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 **/
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 **/
91 #include "collectd.h"
92 #include "common.h"
93 #include "plugin.h"
94 #include "configfile.h"
95 #include "utils_ignorelist.h"
97 #include <dirent.h>
98 #include <sys/ioctl.h>
100 #if !KERNEL_LINUX
101 # error "No applicable input method."
102 #endif
104 #include <linux/wireless.h>
105 #include "madwifi.h"
109 struct stat_spec {
110 uint16_t flags;
111 uint16_t offset;
112 const char *name;
113 };
116 #define OFFSETOF(s, i) ((size_t)&((s *)0)->i)
118 #define FLAG(i) (((uint32_t) 1) << ((i) % 32))
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
126 /* By default, the item is disabled */
127 #define D 0
129 /* By default, the item is logged */
130 #define LOG 4
132 /* By default, the item is summed with other such items and logged together */
133 #define SU 8
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 }
141 /*
142 * (Module-)Global variables
143 */
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
154 static struct stat_spec specs[] = {
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 */
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 */
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 */
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 };
339 /* Bounds between SS, NS, IS and AS stats in stats array */
340 static int bounds[4];
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];
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);
362 static ignorelist_t *ignorelist = NULL;
364 static int use_sysfs = 1;
365 static int init_state = 0;
367 static inline int item_watched(int i)
368 {
369 assert (i >= 0);
370 assert (((size_t) i) < ((STATIC_ARRAY_SIZE (watch_items) + 1) * 32));
371 return watch_items[i / 32] & FLAG (i);
372 }
374 static inline int item_summed(int i)
375 {
376 assert (i >= 0);
377 assert (((size_t) i) < ((STATIC_ARRAY_SIZE (misc_items) + 1) * 32));
378 return misc_items[i / 32] & FLAG (i);
379 }
381 static inline void watchlist_add (uint32_t *wl, int item)
382 {
383 assert (item >= 0);
384 assert (item < ((WL_LEN + 1) * 32));
385 wl[item / 32] |= FLAG (item);
386 }
388 static inline void watchlist_remove (uint32_t *wl, int item)
389 {
390 assert (item >= 0);
391 assert (item < ((WL_LEN + 1) * 32));
392 wl[item / 32] &= ~FLAG (item);
393 }
395 static inline void watchlist_set (uint32_t *wl, uint32_t val)
396 {
397 int i;
398 for (i = 0; i < WL_LEN; i++)
399 wl[i] = val;
400 }
402 /* This is horribly inefficient, but it is called only during configuration */
403 static int watchitem_find (const char *name)
404 {
405 int max = STATIC_ARRAY_SIZE (specs);
406 int i;
408 for (i = 0; i < max; i++)
409 if (strcasecmp (name, specs[i].name) == 0)
410 return i;
412 return -1;
413 }
416 /* Collectd hooks */
418 /* We need init function called before madwifi_config */
420 static int madwifi_real_init (void)
421 {
422 size_t max = STATIC_ARRAY_SIZE (specs);
423 size_t i;
425 for (i = 0; i < STATIC_ARRAY_SIZE (bounds); i++)
426 bounds[i] = 0;
428 watchlist_set(watch_items, 0);
429 watchlist_set(misc_items, 0);
431 for (i = 0; i < max; i++)
432 {
433 bounds[specs[i].flags & SRC_MASK] = i;
435 if (specs[i].flags & LOG)
436 watch_items[i / 32] |= FLAG (i);
438 if (specs[i].flags & SU)
439 misc_items[i / 32] |= FLAG (i);
440 }
442 for (i = 0; i < STATIC_ARRAY_SIZE (bounds); i++)
443 bounds[i]++;
445 return (0);
446 }
448 static int madwifi_config (const char *key, const char *value)
449 {
450 if (init_state != 1)
451 madwifi_real_init();
452 init_state = 1;
454 if (ignorelist == NULL)
455 ignorelist = ignorelist_create (/* invert = */ 1);
457 if (strcasecmp (key, "Interface") == 0)
458 ignorelist_add (ignorelist, value);
460 else if (strcasecmp (key, "IgnoreSelected") == 0)
461 ignorelist_set_invert (ignorelist, IS_TRUE (value) ? 0 : 1);
463 else if (strcasecmp (key, "Source") == 0)
464 {
465 if (strcasecmp (value, "ProcFS") == 0)
466 use_sysfs = 0;
467 else if (strcasecmp (value, "SysFS") == 0)
468 use_sysfs = 1;
469 else
470 {
471 ERROR ("madwifi plugin: The argument of the `Source' "
472 "option must either be `SysFS' or "
473 "`ProcFS'.");
474 return -1;
475 }
476 }
478 else if (strcasecmp (key, "WatchSet") == 0)
479 {
480 if (strcasecmp (value, "All") == 0)
481 watchlist_set (watch_items, 0xFFFFFFFF);
482 else if (strcasecmp (value, "None") == 0)
483 watchlist_set (watch_items, 0);
484 else return -1;
485 }
487 else if (strcasecmp (key, "WatchAdd") == 0)
488 {
489 int id = watchitem_find (value);
491 if (id < 0)
492 return (-1);
493 else
494 watchlist_add (watch_items, id);
495 }
497 else if (strcasecmp (key, "WatchRemove") == 0)
498 {
499 int id = watchitem_find (value);
501 if (id < 0)
502 return (-1);
503 else
504 watchlist_remove (watch_items, id);
505 }
507 else if (strcasecmp (key, "MiscSet") == 0)
508 {
509 if (strcasecmp (value, "All") == 0)
510 watchlist_set (misc_items, 0xFFFFFFFF);
511 else if (strcasecmp (value, "None") == 0)
512 watchlist_set (misc_items, 0);
513 else return -1;
514 }
516 else if (strcasecmp (key, "MiscAdd") == 0)
517 {
518 int id = watchitem_find (value);
520 if (id < 0)
521 return (-1);
522 else
523 watchlist_add (misc_items, id);
524 }
526 else if (strcasecmp (key, "MiscRemove") == 0)
527 {
528 int id = watchitem_find (value);
530 if (id < 0)
531 return (-1);
532 else
533 watchlist_remove (misc_items, id);
534 }
536 else
537 return (-1);
539 return (0);
540 }
543 static void submit (const char *dev, const char *type, const char *ti1,
544 const char *ti2, value_t *val, int len)
545 {
546 value_list_t vl = VALUE_LIST_INIT;
548 vl.values = val;
549 vl.values_len = len;
550 sstrncpy (vl.host, hostname_g, sizeof (vl.host));
551 sstrncpy (vl.plugin, "madwifi", sizeof (vl.plugin));
552 sstrncpy (vl.plugin_instance, dev, sizeof (vl.plugin_instance));
553 sstrncpy (vl.type, type, sizeof (vl.type));
555 if ((ti1 != NULL) && (ti2 != NULL))
556 ssnprintf (vl.type_instance, sizeof (vl.type_instance), "%s-%s", ti1, ti2);
557 else if ((ti1 != NULL) && (ti2 == NULL))
558 sstrncpy (vl.type_instance, ti1, sizeof (vl.type_instance));
560 plugin_dispatch_values (&vl);
561 }
563 static void submit_derive (const char *dev, const char *type, const char *ti1,
564 const char *ti2, derive_t val)
565 {
566 value_t item;
567 item.derive = val;
568 submit (dev, type, ti1, ti2, &item, 1);
569 }
571 static void submit_derive2 (const char *dev, const char *type, const char *ti1,
572 const char *ti2, derive_t val1, derive_t val2)
573 {
574 value_t items[2];
575 items[0].derive = val1;
576 items[1].derive = val2;
577 submit (dev, type, ti1, ti2, items, 2);
578 }
580 static void submit_gauge (const char *dev, const char *type, const char *ti1,
581 const char *ti2, gauge_t val)
582 {
583 value_t item;
584 item.gauge = val;
585 submit (dev, type, ti1, ti2, &item, 1);
586 }
588 static void submit_antx (const char *dev, const char *name,
589 u_int32_t *vals, int vals_num)
590 {
591 char ti2[16];
592 int i;
594 for (i = 0; i < vals_num; i++)
595 {
596 if (vals[i] == 0)
597 continue;
599 ssnprintf (ti2, sizeof (ti2), "%i", i);
600 submit_derive (dev, "ath_stat", name, ti2,
601 (derive_t) vals[i]);
602 }
603 }
605 static inline void
606 macaddr_to_str (char *buf, size_t bufsize, const uint8_t mac[IEEE80211_ADDR_LEN])
607 {
608 ssnprintf (buf, bufsize, "%02x:%02x:%02x:%02x:%02x:%02x",
609 mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
610 }
612 static void
613 process_stat_struct (int which, const void *ptr, const char *dev, const char *mac,
614 const char *type_name, const char *misc_name)
615 {
616 uint32_t misc = 0;
617 int i;
619 assert (which >= 1);
620 assert (((size_t) which) < STATIC_ARRAY_SIZE (bounds));
622 for (i = bounds[which - 1]; i < bounds[which]; i++)
623 {
624 uint32_t val = *(uint32_t *)(((char *) ptr) + specs[i].offset) ;
626 if (item_watched (i) && (val != 0))
627 submit_derive (dev, type_name, specs[i].name, mac, val);
629 if (item_summed (i))
630 misc += val;
631 }
633 if (misc != 0)
634 submit_derive (dev, type_name, misc_name, mac, misc);
636 }
638 static int
639 process_athstats (int sk, const char *dev)
640 {
641 struct ifreq ifr;
642 struct ath_stats stats;
643 int status;
645 sstrncpy (ifr.ifr_name, dev, sizeof (ifr.ifr_name));
646 ifr.ifr_data = (void *) &stats;
647 status = ioctl (sk, SIOCGATHSTATS, &ifr);
648 if (status < 0)
649 {
650 /* Silent, because not all interfaces support all ioctls. */
651 DEBUG ("madwifi plugin: Sending IO-control "
652 "SIOCGATHSTATS to device %s "
653 "failed with status %i.",
654 dev, status);
655 return (status);
656 }
658 /* These stats are handled as a special case, because they are
659 eight values each */
661 if (item_watched (STAT_AST_ANT_RX))
662 submit_antx (dev, "ast_ant_rx", stats.ast_ant_rx,
663 STATIC_ARRAY_SIZE (stats.ast_ant_rx));
665 if (item_watched (STAT_AST_ANT_TX))
666 submit_antx (dev, "ast_ant_tx", stats.ast_ant_tx,
667 STATIC_ARRAY_SIZE (stats.ast_ant_tx));
669 /* All other ath statistics */
670 process_stat_struct (ATH_STAT, &stats, dev, NULL, "ath_stat", "ast_misc");
671 return (0);
672 }
674 static int
675 process_80211stats (int sk, const char *dev)
676 {
677 struct ifreq ifr;
678 struct ieee80211_stats stats;
679 int status;
681 sstrncpy (ifr.ifr_name, dev, sizeof (ifr.ifr_name));
682 ifr.ifr_data = (void *) &stats;
683 status = ioctl(sk, SIOCG80211STATS, &ifr);
684 if (status < 0)
685 {
686 /* Silent, because not all interfaces support all ioctls. */
687 DEBUG ("madwifi plugin: Sending IO-control "
688 "SIOCG80211STATS to device %s "
689 "failed with status %i.",
690 dev, status);
691 return (status);
692 }
694 process_stat_struct (IFA_STAT, &stats, dev, NULL, "ath_stat", "is_misc");
695 return (0);
696 }
699 static int
700 process_station (int sk, const char *dev, struct ieee80211req_sta_info *si)
701 {
702 struct iwreq iwr;
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;
708 macaddr_to_str (mac, sizeof (mac), si->isi_macaddr);
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);
714 if (item_watched (STAT_NODE_RSSI))
715 submit_gauge (dev, "node_rssi", mac, NULL, si->isi_rssi);
717 memset (&iwr, 0, sizeof (iwr));
718 sstrncpy(iwr.ifr_name, dev, sizeof (iwr.ifr_name));
719 iwr.u.data.pointer = (void *) &stats;
720 iwr.u.data.length = sizeof (stats);
721 memcpy(stats.is_u.macaddr, si->isi_macaddr, IEEE80211_ADDR_LEN);
722 status = ioctl(sk, IEEE80211_IOCTL_STA_STATS, &iwr);
723 if (status < 0)
724 {
725 /* Silent, because not all interfaces support all ioctls. */
726 DEBUG ("madwifi plugin: Sending IO-control "
727 "IEEE80211_IOCTL_STA_STATS to device %s "
728 "failed with status %i.",
729 dev, status);
730 return (status);
731 }
733 /* These two stats are handled as a special case as they are
734 a pair of 64bit values */
735 if (item_watched (STAT_NODE_OCTETS))
736 submit_derive2 (dev, "node_octets", mac, NULL,
737 ns->ns_rx_bytes, ns->ns_tx_bytes);
739 /* This stat is handled as a special case, because it is stored
740 as uin64_t, but we will ignore upper half */
741 if (item_watched (STAT_NS_RX_BEACONS))
742 submit_derive (dev, "node_stat", "ns_rx_beacons", mac,
743 (ns->ns_rx_beacons & 0xFFFFFFFF));
745 /* All other node statistics */
746 process_stat_struct (NOD_STAT, ns, dev, mac, "node_stat", "ns_misc");
747 return (0);
748 }
750 static int
751 process_stations (int sk, const char *dev)
752 {
753 uint8_t buf[24*1024];
754 struct iwreq iwr;
755 uint8_t *cp;
756 int nodes;
757 size_t len;
758 int status;
760 memset (&iwr, 0, sizeof (iwr));
761 sstrncpy (iwr.ifr_name, dev, sizeof (iwr.ifr_name));
762 iwr.u.data.pointer = (void *) buf;
763 iwr.u.data.length = sizeof (buf);
765 status = ioctl (sk, IEEE80211_IOCTL_STA_INFO, &iwr);
766 if (status < 0)
767 {
768 /* Silent, because not all interfaces support all ioctls. */
769 DEBUG ("madwifi plugin: Sending IO-control "
770 "IEEE80211_IOCTL_STA_INFO to device %s "
771 "failed with status %i.",
772 dev, status);
773 return (status);
774 }
776 len = iwr.u.data.length;
778 cp = buf;
779 nodes = 0;
780 while (len >= sizeof (struct ieee80211req_sta_info))
781 {
782 struct ieee80211req_sta_info *si = (void *) cp;
783 process_station(sk, dev, si);
784 cp += si->isi_len;
785 len -= si->isi_len;
786 nodes++;
787 }
789 if (item_watched (STAT_ATH_NODES))
790 submit_gauge (dev, "ath_nodes", NULL, NULL, nodes);
791 return (0);
792 }
794 static int
795 process_device (int sk, const char *dev)
796 {
797 int num_success = 0;
798 int status;
800 status = process_athstats (sk, dev);
801 if (status == 0)
802 num_success++;
804 status = process_80211stats (sk, dev);
805 if (status == 0)
806 num_success++;
808 status = process_stations (sk, dev);
809 if (status == 0)
810 num_success++;
812 return ((num_success == 0) ? -1 : 0);
813 }
815 static int
816 check_devname (const char *dev)
817 {
818 char buf[PATH_MAX];
819 char buf2[PATH_MAX];
820 int i;
822 if (dev[0] == '.')
823 return 0;
825 ssnprintf (buf, sizeof (buf), "/sys/class/net/%s/device/driver", dev);
826 buf[sizeof (buf) - 1] = '\0';
828 i = readlink (buf, buf2, sizeof (buf2) - 1);
829 if (i < 0)
830 return 0;
832 buf2[i] = '\0';
834 if (strstr (buf2, "/drivers/ath_") == NULL)
835 return 0;
836 return 1;
837 }
839 static int
840 sysfs_iterate(int sk)
841 {
842 struct dirent *de;
843 DIR *nets;
844 int status;
845 int num_success;
846 int num_fail;
848 nets = opendir ("/sys/class/net/");
849 if (nets == NULL)
850 {
851 WARNING ("madwifi plugin: opening /sys/class/net failed");
852 return (-1);
853 }
855 num_success = 0;
856 num_fail = 0;
857 while ((de = readdir (nets)))
858 {
859 if (check_devname (de->d_name) == 0)
860 continue;
862 if (ignorelist_match (ignorelist, de->d_name) != 0)
863 continue;
865 status = process_device (sk, de->d_name);
866 if (status != 0)
867 {
868 ERROR ("madwifi plugin: Processing interface "
869 "%s failed.", de->d_name);
870 num_fail++;
871 }
872 else
873 {
874 num_success++;
875 }
876 } /* while (readdir) */
878 closedir(nets);
880 if ((num_success == 0) && (num_fail != 0))
881 return (-1);
882 return (0);
883 }
885 static int
886 procfs_iterate(int sk)
887 {
888 char buffer[1024];
889 char *device, *dummy;
890 FILE *fh;
891 int status;
892 int num_success;
893 int num_fail;
895 if ((fh = fopen ("/proc/net/dev", "r")) == NULL)
896 {
897 WARNING ("madwifi plugin: opening /proc/net/dev failed");
898 return (-1);
899 }
901 num_success = 0;
902 num_fail = 0;
903 while (fgets (buffer, sizeof (buffer), fh) != NULL)
904 {
905 dummy = strchr(buffer, ':');
906 if (dummy == NULL)
907 continue;
908 dummy[0] = 0;
910 device = buffer;
911 while (device[0] == ' ')
912 device++;
914 if (device[0] == 0)
915 continue;
917 if (ignorelist_match (ignorelist, device) != 0)
918 continue;
920 status = process_device (sk, device);
921 if (status != 0)
922 {
923 ERROR ("madwifi plugin: Processing interface "
924 "%s failed.", device);
925 num_fail++;
926 }
927 else
928 {
929 num_success++;
930 }
931 } /* while (fgets) */
933 fclose(fh);
935 if ((num_success == 0) && (num_fail != 0))
936 return (-1);
937 return 0;
938 }
940 static int madwifi_read (void)
941 {
942 int rv;
943 int sk;
945 if (init_state == 0)
946 madwifi_real_init();
947 init_state = 2;
949 sk = socket(AF_INET, SOCK_DGRAM, 0);
950 if (sk < 0)
951 return (-1);
954 /* procfs iteration is not safe because it does not check whether given
955 interface is madwifi interface and there are private ioctls used, which
956 may do something completely different on non-madwifi devices.
957 Therefore, it is not used unless explicitly enabled (and should be used
958 together with ignorelist). */
960 if (use_sysfs)
961 rv = sysfs_iterate(sk);
962 else
963 rv = procfs_iterate(sk);
965 close(sk);
967 return rv;
968 }
970 void module_register (void)
971 {
972 plugin_register_config ("madwifi", madwifi_config,
973 config_keys, config_keys_num);
975 plugin_register_read ("madwifi", madwifi_read);
976 }