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