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>
99 #include <sys/socket.h>
101 #if !KERNEL_LINUX
102 # error "No applicable input method."
103 #endif
105 #include <linux/wireless.h>
106 #include "madwifi.h"
110 struct stat_spec {
111 uint16_t flags;
112 uint16_t offset;
113 const char *name;
114 };
117 #define OFFSETOF(s, i) ((size_t)&((s *)0)->i)
119 #define FLAG(i) (((uint32_t) 1) << ((i) % 32))
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
127 /* By default, the item is disabled */
128 #define D 0
130 /* By default, the item is logged */
131 #define LOG 4
133 /* By default, the item is summed with other such items and logged together */
134 #define SU 8
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 }
142 /*
143 * (Module-)Global variables
144 */
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
155 static struct stat_spec specs[] = {
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 */
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 */
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 */
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 };
340 /* Bounds between SS, NS, IS and AS stats in stats array */
341 static int bounds[4];
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];
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);
363 static ignorelist_t *ignorelist = NULL;
365 static int use_sysfs = 1;
366 static int init_state = 0;
368 static inline int item_watched(int i)
369 {
370 assert (i >= 0);
371 assert (i < ((STATIC_ARRAY_SIZE (watch_items) + 1) * 32));
372 return watch_items[i / 32] & FLAG (i);
373 }
375 static inline int item_summed(int i)
376 {
377 assert (i >= 0);
378 assert (i < ((STATIC_ARRAY_SIZE (misc_items) + 1) * 32));
379 return misc_items[i / 32] & FLAG (i);
380 }
382 static inline void watchlist_add (uint32_t *wl, int item)
383 {
384 assert (item >= 0);
385 assert (item < ((WL_LEN + 1) * 32));
386 wl[item / 32] |= FLAG (item);
387 }
389 static inline void watchlist_remove (uint32_t *wl, int item)
390 {
391 assert (item >= 0);
392 assert (item < ((WL_LEN + 1) * 32));
393 wl[item / 32] &= ~FLAG (item);
394 }
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 }
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;
409 for (i = 0; i < max; i++)
410 if (strcasecmp (name, specs[i].name) == 0)
411 return i;
413 return -1;
414 }
417 /* Collectd hooks */
419 /* We need init function called before madwifi_config */
421 static int madwifi_real_init (void)
422 {
423 int max = STATIC_ARRAY_SIZE (specs);
424 int i;
426 for (i = 0; i < STATIC_ARRAY_SIZE (bounds); i++)
427 bounds[i] = 0;
429 watchlist_set(watch_items, 0);
430 watchlist_set(misc_items, 0);
432 for (i = 0; i < max; i++)
433 {
434 bounds[specs[i].flags & SRC_MASK] = i;
436 if (specs[i].flags & LOG)
437 watch_items[i / 32] |= FLAG (i);
439 if (specs[i].flags & SU)
440 misc_items[i / 32] |= FLAG (i);
441 }
443 for (i = 0; i < STATIC_ARRAY_SIZE (bounds); i++)
444 bounds[i]++;
446 return (0);
447 }
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;
455 if (ignorelist == NULL)
456 ignorelist = ignorelist_create (/* invert = */ 1);
458 if (strcasecmp (key, "Interface") == 0)
459 ignorelist_add (ignorelist, value);
461 else if (strcasecmp (key, "IgnoreSelected") == 0)
462 ignorelist_set_invert (ignorelist, IS_TRUE (value) ? 0 : 1);
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 }
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 }
488 else if (strcasecmp (key, "WatchAdd") == 0)
489 {
490 int id = watchitem_find (value);
492 if (id < 0)
493 return (-1);
494 else
495 watchlist_add (watch_items, id);
496 }
498 else if (strcasecmp (key, "WatchRemove") == 0)
499 {
500 int id = watchitem_find (value);
502 if (id < 0)
503 return (-1);
504 else
505 watchlist_remove (watch_items, id);
506 }
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 }
517 else if (strcasecmp (key, "MiscAdd") == 0)
518 {
519 int id = watchitem_find (value);
521 if (id < 0)
522 return (-1);
523 else
524 watchlist_add (misc_items, id);
525 }
527 else if (strcasecmp (key, "MiscRemove") == 0)
528 {
529 int id = watchitem_find (value);
531 if (id < 0)
532 return (-1);
533 else
534 watchlist_remove (misc_items, id);
535 }
537 else
538 return (-1);
540 return (0);
541 }
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;
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));
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));
561 plugin_dispatch_values (&vl);
562 }
564 static void submit_counter (const char *dev, const char *type, const char *ti1,
565 const char *ti2, counter_t val)
566 {
567 value_t item;
568 item.counter = val;
569 submit (dev, type, ti1, ti2, &item, 1);
570 }
572 static void submit_counter2 (const char *dev, const char *type, const char *ti1,
573 const char *ti2, counter_t val1, counter_t val2)
574 {
575 value_t items[2];
576 items[0].counter = val1;
577 items[1].counter = val2;
578 submit (dev, type, ti1, ti2, items, 2);
579 }
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 }
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;
595 for (i = 0; i < vals_num; i++)
596 {
597 if (vals[i] == 0)
598 continue;
600 ssnprintf (ti2, sizeof (ti2), "%i", i);
601 submit_counter (dev, "ath_stat", name, ti2,
602 (counter_t) vals[i]);
603 }
604 }
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 }
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;
620 assert (which >= 1);
621 assert (which < STATIC_ARRAY_SIZE (bounds));
623 for (i = bounds[which - 1]; i < bounds[which]; i++)
624 {
625 uint32_t val = *(uint32_t *)(((char *) ptr) + specs[i].offset) ;
627 if (item_watched (i) && (val != 0))
628 submit_counter (dev, type_name, specs[i].name, mac, val);
630 if (item_summed (i))
631 misc += val;
632 }
634 if (misc != 0)
635 submit_counter (dev, type_name, misc_name, mac, misc);
637 }
639 static int
640 process_athstats (int sk, const char *dev)
641 {
642 struct ifreq ifr;
643 struct ath_stats stats;
644 int status;
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 }
659 /* These stats are handled as a special case, because they are
660 eight values each */
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));
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));
670 /* All other ath statistics */
671 process_stat_struct (ATH_STAT, &stats, dev, NULL, "ath_stat", "ast_misc");
672 return (0);
673 }
675 static int
676 process_80211stats (int sk, const char *dev)
677 {
678 struct ifreq ifr;
679 struct ieee80211_stats stats;
680 int status;
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 }
695 process_stat_struct (IFA_STAT, &stats, dev, NULL, "ath_stat", "is_misc");
696 return (0);
697 }
700 static int
701 process_station (int sk, const char *dev, struct ieee80211req_sta_info *si)
702 {
703 struct iwreq iwr;
704 static char mac[DATA_MAX_NAME_LEN];
705 struct ieee80211req_sta_stats stats;
706 const struct ieee80211_nodestats *ns = &stats.is_stats;
707 int status;
709 macaddr_to_str (mac, sizeof (mac), si->isi_macaddr);
711 if (item_watched (STAT_NODE_TX_RATE))
712 submit_gauge (dev, "node_tx_rate", mac, NULL,
713 (si->isi_rates[si->isi_txrate] & IEEE80211_RATE_VAL) / 2);
715 if (item_watched (STAT_NODE_RSSI))
716 submit_gauge (dev, "node_rssi", mac, NULL, si->isi_rssi);
718 memset (&iwr, 0, sizeof (iwr));
719 sstrncpy(iwr.ifr_name, dev, sizeof (iwr.ifr_name));
720 iwr.u.data.pointer = (void *) &stats;
721 iwr.u.data.length = sizeof (stats);
722 memcpy(stats.is_u.macaddr, si->isi_macaddr, IEEE80211_ADDR_LEN);
723 status = ioctl(sk, IEEE80211_IOCTL_STA_STATS, &iwr);
724 if (status < 0)
725 {
726 /* Silent, because not all interfaces support all ioctls. */
727 DEBUG ("madwifi plugin: Sending IO-control "
728 "IEEE80211_IOCTL_STA_STATS to device %s "
729 "failed with status %i.",
730 dev, status);
731 return (status);
732 }
734 /* These two stats are handled as a special case as they are
735 a pair of 64bit values */
736 if (item_watched (STAT_NODE_OCTETS))
737 submit_counter2 (dev, "node_octets", mac, NULL,
738 ns->ns_rx_bytes, ns->ns_tx_bytes);
740 /* This stat is handled as a special case, because it is stored
741 as uin64_t, but we will ignore upper half */
742 if (item_watched (STAT_NS_RX_BEACONS))
743 submit_counter (dev, "node_stat", "ns_rx_beacons", mac,
744 (ns->ns_rx_beacons & 0xFFFFFFFF));
746 /* All other node statistics */
747 process_stat_struct (NOD_STAT, ns, dev, mac, "node_stat", "ns_misc");
748 return (0);
749 }
751 static int
752 process_stations (int sk, const char *dev)
753 {
754 uint8_t buf[24*1024];
755 struct iwreq iwr;
756 uint8_t *cp;
757 int len, nodes;
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 memset (buf2, 0, sizeof (buf2));
829 i = readlink (buf, buf2, sizeof (buf2) - 1);
830 if (i < 0)
831 return 0;
833 if (strstr (buf2, "/drivers/ath_") == NULL)
834 return 0;
835 return 1;
836 }
838 static int
839 sysfs_iterate(int sk)
840 {
841 struct dirent *de;
842 DIR *nets;
843 int status;
844 int num_success;
845 int num_fail;
847 nets = opendir ("/sys/class/net/");
848 if (nets == NULL)
849 {
850 WARNING ("madwifi plugin: opening /sys/class/net failed");
851 return (-1);
852 }
854 num_success = 0;
855 num_fail = 0;
856 while ((de = readdir (nets)))
857 {
858 if (check_devname (de->d_name) == 0)
859 continue;
861 if (ignorelist_match (ignorelist, de->d_name) != 0)
862 continue;
864 status = process_device (sk, de->d_name);
865 if (status != 0)
866 {
867 ERROR ("madwifi plugin: Processing interface "
868 "%s failed.", de->d_name);
869 num_fail++;
870 }
871 else
872 {
873 num_success++;
874 }
875 } /* while (readdir) */
877 closedir(nets);
879 if ((num_success == 0) && (num_fail != 0))
880 return (-1);
881 return (0);
882 }
884 static int
885 procfs_iterate(int sk)
886 {
887 char buffer[1024];
888 char *device, *dummy;
889 FILE *fh;
890 int status;
891 int num_success;
892 int num_fail;
894 if ((fh = fopen ("/proc/net/dev", "r")) == NULL)
895 {
896 WARNING ("madwifi plugin: opening /proc/net/dev failed");
897 return (-1);
898 }
900 num_success = 0;
901 num_fail = 0;
902 while (fgets (buffer, sizeof (buffer), fh) != NULL)
903 {
904 dummy = strchr(buffer, ':');
905 if (dummy == NULL)
906 continue;
907 dummy[0] = 0;
909 device = buffer;
910 while (device[0] == ' ')
911 device++;
913 if (device[0] == 0)
914 continue;
916 if (ignorelist_match (ignorelist, device) != 0)
917 continue;
919 status = process_device (sk, device);
920 if (status != 0)
921 {
922 ERROR ("madwifi plugin: Processing interface "
923 "%s failed.", device);
924 num_fail++;
925 }
926 else
927 {
928 num_success++;
929 }
930 } /* while (fgets) */
932 fclose(fh);
934 if ((num_success == 0) && (num_fail != 0))
935 return (-1);
936 return 0;
937 }
939 static int madwifi_read (void)
940 {
941 int rv;
942 int sk;
944 if (init_state == 0)
945 madwifi_real_init();
946 init_state = 2;
948 sk = socket(AF_INET, SOCK_DGRAM, 0);
949 if (sk < 0)
950 return (-1);
953 /* procfs iteration is not safe because it does not check whether given
954 interface is madwifi interface and there are private ioctls used, which
955 may do something completely different on non-madwifi devices.
956 Therefore, it is not used unless explicitly enabled (and should be used
957 together with ignorelist). */
959 if (use_sysfs)
960 rv = sysfs_iterate(sk);
961 else
962 rv = procfs_iterate(sk);
964 close(sk);
966 return rv;
967 }
969 void module_register (void)
970 {
971 plugin_register_config ("madwifi", madwifi_config,
972 config_keys, config_keys_num);
974 plugin_register_read ("madwifi", madwifi_read);
975 }