--- /dev/null
+/**
+ * collectd - src/madwifi.c
+ * Copyright (C) 2009 Ondrej 'SanTiago' Zajicek
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; only version 2 of the License is applicable.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Author:
+ * Ondrej 'SanTiago' Zajicek <santiago@crfreenet.org>
+ *
+ * based on some code from interfaces.c (collectd) and Madwifi driver
+ **/
+
+
+/**
+ * There are several data streams provided by Madwifi plugin, some are
+ * connected to network interface, some are connected to each node
+ * associated to that interface. Nodes represents other sides in
+ * wireless communication, for example on network interface in AP mode,
+ * there is one node for each associated station. Node data streams
+ * contain MAC address of the node as the last part of the type_instance
+ * field.
+ *
+ * Inteface data streams:
+ * ath_nodes The number of associated nodes
+ * ath_stat Device statistic counters
+ *
+ * Node data streams:
+ * node_octets RX and TX data count (octets/bytes)
+ * node_rssi Received RSSI of the node
+ * node_tx_rate Reported TX rate to that node
+ * node_stat Node statistic counters
+ *
+ * Both statistic counters have type instances for each counter returned
+ * by Madwifi. See madwifi.h for content of ieee80211_nodestats,
+ * ieee80211_stats and ath_stats structures. Type instances use the same
+ * name as fields in these structures (like ns_rx_dup). Some fields are
+ * not reported, because they are not counters (like ns_tx_deauth_code
+ * or ast_tx_rssi). Fields ns_rx_bytes and ns_tx_bytes are reported as
+ * node_octets data stream instead of type instance of node_stat.
+ * Statistics are not logged when they are zero.
+ *
+ * There are two sets of these counters - the first 'WatchList' is a
+ * set of counters that are individually logged. The second 'MiscList'
+ * is a set of counters that are summed together and the sum is logged.
+ * By default, the most important statistics are in the WatchList and
+ * many error statistics are in MiscList. There are also many statistics
+ * that are not in any of these sets, so they are not monitored by default.
+ * It is possible to alter these lists using configuration options:
+ *
+ * WatchAdd X Adds X to WachList
+ * WatchRemove X Removes X from WachList
+ * WatchSet All Adds all statistics to WatchList
+ * WatchSet None Removes all statistics from WachList
+ *
+ * There are also Misc* variants fo these options, they modifies MiscList
+ * instead of WatchList.
+ *
+ * Example:
+ *
+ * WatchSet None
+ * WatchAdd node_octets
+ * WatchAdd node_rssi
+ * WatchAdd is_rx_acl
+ * WatchAdd is_scan_active
+ *
+ * That causes that just the four mentioned data streams are logged.
+ *
+ *
+ * By default, madwifi plugin enumerates network interfaces using /sys
+ * filesystem. Configuration option `Source' can change this to use
+ * /proc filesystem (which is useful for example when running on Linux
+ * 2.4). But without /sys filesystem, Madwifi plugin cannot check whether
+ * given interface is madwifi interface and there are private ioctls used,
+ * which may do something completely different on non-madwifi devices.
+ * Therefore, the /proc filesystem should always be used together with option
+ * `Interface', to limit found interfaces to madwifi interfaces only.
+ **/
+
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+#include "configfile.h"
+#include "utils_ignorelist.h"
+
+#include <dirent.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#if !KERNEL_LINUX
+# error "No applicable input method."
+#endif
+
+#include <linux/wireless.h>
+#include "madwifi.h"
+
+
+
+struct stat_spec {
+ uint16_t flags;
+ uint16_t offset;
+ const char *name;
+};
+
+
+#define OFFSETOF(s, i) ((size_t)&((s *)0)->i)
+
+#define FLAG(i) (((uint32_t) 1) << ((i) % 32))
+
+#define SPC_STAT 0
+#define NOD_STAT 1
+#define IFA_STAT 2
+#define ATH_STAT 3
+#define SRC_MASK 3
+
+/* By default, the item is disabled */
+#define D 0
+
+/* By default, the item is logged */
+#define LOG 4
+
+/* By default, the item is summed with other such items and logged together */
+#define SU 8
+
+#define SS_STAT(flags, name) { flags | SPC_STAT, 0, #name }
+#define NS_STAT(flags, name) { flags | NOD_STAT, OFFSETOF(struct ieee80211_nodestats, name), #name }
+#define IS_STAT(flags, name) { flags | IFA_STAT, OFFSETOF(struct ieee80211_stats, name), #name }
+#define AS_STAT(flags, name) { flags | ATH_STAT, OFFSETOF(struct ath_stats, name), #name }
+
+
+/*
+ * (Module-)Global variables
+ */
+
+/* Indices of special stats in specs array */
+#define STAT_NODE_OCTETS 0
+#define STAT_NODE_RSSI 1
+#define STAT_NODE_TX_RATE 2
+#define STAT_ATH_NODES 3
+#define STAT_NS_RX_BEACONS 4
+#define STAT_AST_ANT_RX 5
+#define STAT_AST_ANT_TX 6
+
+static struct stat_spec specs[] = {
+
+/* Special statistics */
+SS_STAT(LOG, node_octets), /* rx and tx data count (bytes) */
+SS_STAT(LOG, node_rssi), /* received RSSI of the node */
+SS_STAT(LOG, node_tx_rate), /* used tx rate to the node */
+SS_STAT(LOG, ath_nodes), /* the number of associated nodes */
+SS_STAT(D, ns_rx_beacons), /* rx beacon frames */
+SS_STAT(LOG, ast_ant_rx), /* rx frames with antenna */
+SS_STAT(LOG, ast_ant_tx), /* tx frames with antenna */
+
+/* Node statistics */
+NS_STAT(LOG, ns_rx_data), /* rx data frames */
+NS_STAT(LOG, ns_rx_mgmt), /* rx management frames */
+NS_STAT(LOG, ns_rx_ctrl), /* rx control frames */
+NS_STAT(D, ns_rx_ucast), /* rx unicast frames */
+NS_STAT(D, ns_rx_mcast), /* rx multi/broadcast frames */
+NS_STAT(D, ns_rx_proberesp), /* rx probe response frames */
+NS_STAT(LOG, ns_rx_dup), /* rx discard because it's a dup */
+NS_STAT(SU, ns_rx_noprivacy), /* rx w/ wep but privacy off */
+NS_STAT(SU, ns_rx_wepfail), /* rx wep processing failed */
+NS_STAT(SU, ns_rx_demicfail), /* rx demic failed */
+NS_STAT(SU, ns_rx_decap), /* rx decapsulation failed */
+NS_STAT(SU, ns_rx_defrag), /* rx defragmentation failed */
+NS_STAT(D, ns_rx_disassoc), /* rx disassociation */
+NS_STAT(D, ns_rx_deauth), /* rx deauthentication */
+NS_STAT(SU, ns_rx_decryptcrc), /* rx decrypt failed on crc */
+NS_STAT(SU, ns_rx_unauth), /* rx on unauthorized port */
+NS_STAT(SU, ns_rx_unencrypted), /* rx unecrypted w/ privacy */
+NS_STAT(LOG, ns_tx_data), /* tx data frames */
+NS_STAT(LOG, ns_tx_mgmt), /* tx management frames */
+NS_STAT(D, ns_tx_ucast), /* tx unicast frames */
+NS_STAT(D, ns_tx_mcast), /* tx multi/broadcast frames */
+NS_STAT(D, ns_tx_probereq), /* tx probe request frames */
+NS_STAT(D, ns_tx_uapsd), /* tx on uapsd queue */
+NS_STAT(SU, ns_tx_novlantag), /* tx discard due to no tag */
+NS_STAT(SU, ns_tx_vlanmismatch), /* tx discard due to of bad tag */
+NS_STAT(D, ns_tx_eosplost), /* uapsd EOSP retried out */
+NS_STAT(D, ns_ps_discard), /* ps discard due to of age */
+NS_STAT(D, ns_uapsd_triggers), /* uapsd triggers */
+NS_STAT(LOG, ns_tx_assoc), /* [re]associations */
+NS_STAT(LOG, ns_tx_auth), /* [re]authentications */
+NS_STAT(D, ns_tx_deauth), /* deauthentications */
+NS_STAT(D, ns_tx_disassoc), /* disassociations */
+NS_STAT(D, ns_psq_drops), /* power save queue drops */
+
+/* Iface statistics */
+IS_STAT(SU, is_rx_badversion), /* rx frame with bad version */
+IS_STAT(SU, is_rx_tooshort), /* rx frame too short */
+IS_STAT(LOG, is_rx_wrongbss), /* rx from wrong bssid */
+IS_STAT(LOG, is_rx_dup), /* rx discard due to it's a dup */
+IS_STAT(SU, is_rx_wrongdir), /* rx w/ wrong direction */
+IS_STAT(D, is_rx_mcastecho), /* rx discard due to of mcast echo */
+IS_STAT(SU, is_rx_notassoc), /* rx discard due to sta !assoc */
+IS_STAT(SU, is_rx_noprivacy), /* rx w/ wep but privacy off */
+IS_STAT(SU, is_rx_unencrypted), /* rx w/o wep and privacy on */
+IS_STAT(SU, is_rx_wepfail), /* rx wep processing failed */
+IS_STAT(SU, is_rx_decap), /* rx decapsulation failed */
+IS_STAT(D, is_rx_mgtdiscard), /* rx discard mgt frames */
+IS_STAT(D, is_rx_ctl), /* rx discard ctrl frames */
+IS_STAT(D, is_rx_beacon), /* rx beacon frames */
+IS_STAT(D, is_rx_rstoobig), /* rx rate set truncated */
+IS_STAT(SU, is_rx_elem_missing), /* rx required element missing*/
+IS_STAT(SU, is_rx_elem_toobig), /* rx element too big */
+IS_STAT(SU, is_rx_elem_toosmall), /* rx element too small */
+IS_STAT(LOG, is_rx_elem_unknown), /* rx element unknown */
+IS_STAT(SU, is_rx_badchan), /* rx frame w/ invalid chan */
+IS_STAT(SU, is_rx_chanmismatch), /* rx frame chan mismatch */
+IS_STAT(SU, is_rx_nodealloc), /* rx frame dropped */
+IS_STAT(LOG, is_rx_ssidmismatch), /* rx frame ssid mismatch */
+IS_STAT(SU, is_rx_auth_unsupported), /* rx w/ unsupported auth alg */
+IS_STAT(SU, is_rx_auth_fail), /* rx sta auth failure */
+IS_STAT(SU, is_rx_auth_countermeasures),/* rx auth discard due to CM */
+IS_STAT(SU, is_rx_assoc_bss), /* rx assoc from wrong bssid */
+IS_STAT(SU, is_rx_assoc_notauth), /* rx assoc w/o auth */
+IS_STAT(SU, is_rx_assoc_capmismatch), /* rx assoc w/ cap mismatch */
+IS_STAT(SU, is_rx_assoc_norate), /* rx assoc w/ no rate match */
+IS_STAT(SU, is_rx_assoc_badwpaie), /* rx assoc w/ bad WPA IE */
+IS_STAT(LOG, is_rx_deauth), /* rx deauthentication */
+IS_STAT(LOG, is_rx_disassoc), /* rx disassociation */
+IS_STAT(SU, is_rx_badsubtype), /* rx frame w/ unknown subtype*/
+IS_STAT(SU, is_rx_nobuf), /* rx failed for lack of buf */
+IS_STAT(SU, is_rx_decryptcrc), /* rx decrypt failed on crc */
+IS_STAT(D, is_rx_ahdemo_mgt), /* rx discard ahdemo mgt frame*/
+IS_STAT(SU, is_rx_bad_auth), /* rx bad auth request */
+IS_STAT(SU, is_rx_unauth), /* rx on unauthorized port */
+IS_STAT(SU, is_rx_badkeyid), /* rx w/ incorrect keyid */
+IS_STAT(D, is_rx_ccmpreplay), /* rx seq# violation (CCMP), */
+IS_STAT(D, is_rx_ccmpformat), /* rx format bad (CCMP), */
+IS_STAT(D, is_rx_ccmpmic), /* rx MIC check failed (CCMP), */
+IS_STAT(D, is_rx_tkipreplay), /* rx seq# violation (TKIP), */
+IS_STAT(D, is_rx_tkipformat), /* rx format bad (TKIP), */
+IS_STAT(D, is_rx_tkipmic), /* rx MIC check failed (TKIP), */
+IS_STAT(D, is_rx_tkipicv), /* rx ICV check failed (TKIP), */
+IS_STAT(D, is_rx_badcipher), /* rx failed due to of key type */
+IS_STAT(D, is_rx_nocipherctx), /* rx failed due to key !setup */
+IS_STAT(D, is_rx_acl), /* rx discard due to of acl policy */
+IS_STAT(D, is_rx_ffcnt), /* rx fast frames */
+IS_STAT(SU, is_rx_badathtnl), /* driver key alloc failed */
+IS_STAT(SU, is_tx_nobuf), /* tx failed for lack of buf */
+IS_STAT(SU, is_tx_nonode), /* tx failed for no node */
+IS_STAT(SU, is_tx_unknownmgt), /* tx of unknown mgt frame */
+IS_STAT(SU, is_tx_badcipher), /* tx failed due to of key type */
+IS_STAT(SU, is_tx_nodefkey), /* tx failed due to no defkey */
+IS_STAT(SU, is_tx_noheadroom), /* tx failed due to no space */
+IS_STAT(D, is_tx_ffokcnt), /* tx fast frames sent success */
+IS_STAT(D, is_tx_fferrcnt), /* tx fast frames sent success */
+IS_STAT(D, is_scan_active), /* active scans started */
+IS_STAT(D, is_scan_passive), /* passive scans started */
+IS_STAT(D, is_node_timeout), /* nodes timed out inactivity */
+IS_STAT(D, is_crypto_nomem), /* no memory for crypto ctx */
+IS_STAT(D, is_crypto_tkip), /* tkip crypto done in s/w */
+IS_STAT(D, is_crypto_tkipenmic), /* tkip en-MIC done in s/w */
+IS_STAT(D, is_crypto_tkipdemic), /* tkip de-MIC done in s/w */
+IS_STAT(D, is_crypto_tkipcm), /* tkip counter measures */
+IS_STAT(D, is_crypto_ccmp), /* ccmp crypto done in s/w */
+IS_STAT(D, is_crypto_wep), /* wep crypto done in s/w */
+IS_STAT(D, is_crypto_setkey_cipher), /* cipher rejected key */
+IS_STAT(D, is_crypto_setkey_nokey), /* no key index for setkey */
+IS_STAT(D, is_crypto_delkey), /* driver key delete failed */
+IS_STAT(D, is_crypto_badcipher), /* unknown cipher */
+IS_STAT(D, is_crypto_nocipher), /* cipher not available */
+IS_STAT(D, is_crypto_attachfail), /* cipher attach failed */
+IS_STAT(D, is_crypto_swfallback), /* cipher fallback to s/w */
+IS_STAT(D, is_crypto_keyfail), /* driver key alloc failed */
+IS_STAT(D, is_crypto_enmicfail), /* en-MIC failed */
+IS_STAT(SU, is_ibss_capmismatch), /* merge failed-cap mismatch */
+IS_STAT(SU, is_ibss_norate), /* merge failed-rate mismatch */
+IS_STAT(D, is_ps_unassoc), /* ps-poll for unassoc. sta */
+IS_STAT(D, is_ps_badaid), /* ps-poll w/ incorrect aid */
+IS_STAT(D, is_ps_qempty), /* ps-poll w/ nothing to send */
+
+/* Atheros statistics */
+AS_STAT(D, ast_watchdog), /* device reset by watchdog */
+AS_STAT(D, ast_hardware), /* fatal hardware error interrupts */
+AS_STAT(D, ast_bmiss), /* beacon miss interrupts */
+AS_STAT(D, ast_rxorn), /* rx overrun interrupts */
+AS_STAT(D, ast_rxeol), /* rx eol interrupts */
+AS_STAT(D, ast_txurn), /* tx underrun interrupts */
+AS_STAT(D, ast_mib), /* mib interrupts */
+AS_STAT(D, ast_tx_packets), /* packet sent on the interface */
+AS_STAT(D, ast_tx_mgmt), /* management frames transmitted */
+AS_STAT(LOG, ast_tx_discard), /* frames discarded prior to assoc */
+AS_STAT(SU, ast_tx_invalid), /* frames discarded due to is device gone */
+AS_STAT(SU, ast_tx_qstop), /* tx queue stopped because it's full */
+AS_STAT(SU, ast_tx_encap), /* tx encapsulation failed */
+AS_STAT(SU, ast_tx_nonode), /* tx failed due to of no node */
+AS_STAT(SU, ast_tx_nobuf), /* tx failed due to of no tx buffer (data), */
+AS_STAT(SU, ast_tx_nobufmgt), /* tx failed due to of no tx buffer (mgmt),*/
+AS_STAT(LOG, ast_tx_xretries), /* tx failed due to of too many retries */
+AS_STAT(SU, ast_tx_fifoerr), /* tx failed due to of FIFO underrun */
+AS_STAT(SU, ast_tx_filtered), /* tx failed due to xmit filtered */
+AS_STAT(LOG, ast_tx_shortretry), /* tx on-chip retries (short), */
+AS_STAT(LOG, ast_tx_longretry), /* tx on-chip retries (long), */
+AS_STAT(SU, ast_tx_badrate), /* tx failed due to of bogus xmit rate */
+AS_STAT(D, ast_tx_noack), /* tx frames with no ack marked */
+AS_STAT(D, ast_tx_rts), /* tx frames with rts enabled */
+AS_STAT(D, ast_tx_cts), /* tx frames with cts enabled */
+AS_STAT(D, ast_tx_shortpre), /* tx frames with short preamble */
+AS_STAT(LOG, ast_tx_altrate), /* tx frames with alternate rate */
+AS_STAT(D, ast_tx_protect), /* tx frames with protection */
+AS_STAT(SU, ast_rx_orn), /* rx failed due to of desc overrun */
+AS_STAT(LOG, ast_rx_crcerr), /* rx failed due to of bad CRC */
+AS_STAT(SU, ast_rx_fifoerr), /* rx failed due to of FIFO overrun */
+AS_STAT(SU, ast_rx_badcrypt), /* rx failed due to of decryption */
+AS_STAT(SU, ast_rx_badmic), /* rx failed due to of MIC failure */
+AS_STAT(LOG, ast_rx_phyerr), /* rx PHY error summary count */
+AS_STAT(SU, ast_rx_tooshort), /* rx discarded due to frame too short */
+AS_STAT(SU, ast_rx_toobig), /* rx discarded due to frame too large */
+AS_STAT(SU, ast_rx_nobuf), /* rx setup failed due to of no skbuff */
+AS_STAT(D, ast_rx_packets), /* packet recv on the interface */
+AS_STAT(D, ast_rx_mgt), /* management frames received */
+AS_STAT(D, ast_rx_ctl), /* control frames received */
+AS_STAT(D, ast_be_xmit), /* beacons transmitted */
+AS_STAT(SU, ast_be_nobuf), /* no skbuff available for beacon */
+AS_STAT(D, ast_per_cal), /* periodic calibration calls */
+AS_STAT(D, ast_per_calfail), /* periodic calibration failed */
+AS_STAT(D, ast_per_rfgain), /* periodic calibration rfgain reset */
+AS_STAT(D, ast_rate_calls), /* rate control checks */
+AS_STAT(D, ast_rate_raise), /* rate control raised xmit rate */
+AS_STAT(D, ast_rate_drop), /* rate control dropped xmit rate */
+AS_STAT(D, ast_ant_defswitch), /* rx/default antenna switches */
+AS_STAT(D, ast_ant_txswitch) /* tx antenna switches */
+};
+
+/* Bounds between SS, NS, IS and AS stats in stats array */
+static int bounds[4];
+
+#define WL_LEN 6
+/* Bitmasks for logged and error items */
+static uint32_t watch_items[WL_LEN];
+static uint32_t misc_items[WL_LEN];
+
+
+static const char *config_keys[] =
+{
+ "Interface",
+ "IgnoreSelected",
+ "Source",
+ "WatchAdd",
+ "WatchRemove",
+ "WatchSet",
+ "MiscAdd",
+ "MiscRemove",
+ "MiscSet"
+};
+static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
+
+static ignorelist_t *ignorelist = NULL;
+
+static int use_sysfs = 1;
+static int init_state = 0;
+
+static inline int item_watched(int i)
+{
+ assert (i >= 0);
+ assert (i < ((STATIC_ARRAY_SIZE (watch_items) + 1) * 32));
+ return watch_items[i / 32] & FLAG (i);
+}
+
+static inline int item_summed(int i)
+{
+ assert (i >= 0);
+ assert (i < ((STATIC_ARRAY_SIZE (misc_items) + 1) * 32));
+ return misc_items[i / 32] & FLAG (i);
+}
+
+static inline void watchlist_add (uint32_t *wl, int item)
+{
+ assert (item >= 0);
+ assert (item < ((WL_LEN + 1) * 32));
+ wl[item / 32] |= FLAG (item);
+}
+
+static inline void watchlist_remove (uint32_t *wl, int item)
+{
+ assert (item >= 0);
+ assert (item < ((WL_LEN + 1) * 32));
+ wl[item / 32] &= ~FLAG (item);
+}
+
+static inline void watchlist_set (uint32_t *wl, uint32_t val)
+{
+ int i;
+ for (i = 0; i < WL_LEN; i++)
+ wl[i] = val;
+}
+
+/* This is horribly inefficient, but it is called only during configuration */
+static int watchitem_find (const char *name)
+{
+ int max = STATIC_ARRAY_SIZE (specs);
+ int i;
+
+ for (i = 0; i < max; i++)
+ if (strcasecmp (name, specs[i].name) == 0)
+ return i;
+
+ return -1;
+}
+
+
+/* Collectd hooks */
+
+/* We need init function called before madwifi_config */
+
+static int madwifi_real_init (void)
+{
+ int max = STATIC_ARRAY_SIZE (specs);
+ int i;
+
+ for (i = 0; i < STATIC_ARRAY_SIZE (bounds); i++)
+ bounds[i] = 0;
+
+ watchlist_set(watch_items, 0);
+ watchlist_set(misc_items, 0);
+
+ for (i = 0; i < max; i++)
+ {
+ bounds[specs[i].flags & SRC_MASK] = i;
+
+ if (specs[i].flags & LOG)
+ watch_items[i / 32] |= FLAG (i);
+
+ if (specs[i].flags & SU)
+ misc_items[i / 32] |= FLAG (i);
+ }
+
+ for (i = 0; i < STATIC_ARRAY_SIZE (bounds); i++)
+ bounds[i]++;
+
+ return (0);
+}
+
+static int madwifi_config (const char *key, const char *value)
+{
+ if (init_state != 1)
+ madwifi_real_init();
+ init_state = 1;
+
+ if (ignorelist == NULL)
+ ignorelist = ignorelist_create (/* invert = */ 1);
+
+ if (strcasecmp (key, "Interface") == 0)
+ ignorelist_add (ignorelist, value);
+
+ else if (strcasecmp (key, "IgnoreSelected") == 0)
+ ignorelist_set_invert (ignorelist, IS_TRUE (value) ? 0 : 1);
+
+ else if (strcasecmp (key, "Source") == 0)
+ {
+ if (strcasecmp (value, "ProcFS") == 0)
+ use_sysfs = 0;
+ else if (strcasecmp (value, "SysFS") == 0)
+ use_sysfs = 1;
+ else
+ {
+ ERROR ("madwifi plugin: The argument of the `Source' "
+ "option must either be `SysFS' or "
+ "`ProcFS'.");
+ return -1;
+ }
+ }
+
+ else if (strcasecmp (key, "WatchSet") == 0)
+ {
+ if (strcasecmp (value, "All") == 0)
+ watchlist_set (watch_items, 0xFFFFFFFF);
+ else if (strcasecmp (value, "None") == 0)
+ watchlist_set (watch_items, 0);
+ else return -1;
+ }
+
+ else if (strcasecmp (key, "WatchAdd") == 0)
+ {
+ int id = watchitem_find (value);
+
+ if (id < 0)
+ return (-1);
+ else
+ watchlist_add (watch_items, id);
+ }
+
+ else if (strcasecmp (key, "WatchRemove") == 0)
+ {
+ int id = watchitem_find (value);
+
+ if (id < 0)
+ return (-1);
+ else
+ watchlist_remove (watch_items, id);
+ }
+
+ else if (strcasecmp (key, "MiscSet") == 0)
+ {
+ if (strcasecmp (value, "All") == 0)
+ watchlist_set (misc_items, 0xFFFFFFFF);
+ else if (strcasecmp (value, "None") == 0)
+ watchlist_set (misc_items, 0);
+ else return -1;
+ }
+
+ else if (strcasecmp (key, "MiscAdd") == 0)
+ {
+ int id = watchitem_find (value);
+
+ if (id < 0)
+ return (-1);
+ else
+ watchlist_add (misc_items, id);
+ }
+
+ else if (strcasecmp (key, "MiscRemove") == 0)
+ {
+ int id = watchitem_find (value);
+
+ if (id < 0)
+ return (-1);
+ else
+ watchlist_remove (misc_items, id);
+ }
+
+ else
+ return (-1);
+
+ return (0);
+}
+
+
+static void submit (const char *dev, const char *type, const char *ti1,
+ const char *ti2, value_t *val, int len)
+{
+ value_list_t vl = VALUE_LIST_INIT;
+
+ vl.values = val;
+ vl.values_len = len;
+ sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+ sstrncpy (vl.plugin, "madwifi", sizeof (vl.plugin));
+ sstrncpy (vl.plugin_instance, dev, sizeof (vl.plugin_instance));
+ sstrncpy (vl.type, type, sizeof (vl.type));
+
+ if ((ti1 != NULL) && (ti2 != NULL))
+ ssnprintf (vl.type_instance, sizeof (vl.type_instance), "%s-%s", ti1, ti2);
+ else if ((ti1 != NULL) && (ti2 == NULL))
+ sstrncpy (vl.type_instance, ti1, sizeof (vl.type_instance));
+
+ plugin_dispatch_values (&vl);
+}
+
+static void submit_counter (const char *dev, const char *type, const char *ti1,
+ const char *ti2, counter_t val)
+{
+ value_t item;
+ item.counter = val;
+ submit (dev, type, ti1, ti2, &item, 1);
+}
+
+static void submit_counter2 (const char *dev, const char *type, const char *ti1,
+ const char *ti2, counter_t val1, counter_t val2)
+{
+ value_t items[2];
+ items[0].counter = val1;
+ items[1].counter = val2;
+ submit (dev, type, ti1, ti2, items, 2);
+}
+
+static void submit_gauge (const char *dev, const char *type, const char *ti1,
+ const char *ti2, gauge_t val)
+{
+ value_t item;
+ item.gauge = val;
+ submit (dev, type, ti1, ti2, &item, 1);
+}
+
+static void submit_antx (const char *dev, const char *name,
+ u_int32_t *vals, int vals_num)
+{
+ char ti2[16];
+ int i;
+
+ for (i = 0; i < vals_num; i++)
+ {
+ if (vals[i] == 0)
+ continue;
+
+ ssnprintf (ti2, sizeof (ti2), "antenna%i", i);
+ submit_counter (dev, "ath_stat", name, ti2,
+ (counter_t) vals[i]);
+ }
+}
+
+static inline void
+macaddr_to_str (char *buf, size_t bufsize, const uint8_t mac[IEEE80211_ADDR_LEN])
+{
+ ssnprintf (buf, bufsize, "%02x:%02x:%02x:%02x:%02x:%02x",
+ mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+}
+
+static void
+process_stat_struct (int which, const void *ptr, const char *dev, const char *mac,
+ const char *type_name, const char *misc_name)
+{
+ uint32_t misc = 0;
+ int i;
+
+ assert (which >= 1);
+ assert (which < STATIC_ARRAY_SIZE (bounds));
+
+ for (i = bounds[which - 1]; i < bounds[which]; i++)
+ {
+ uint32_t val = *(uint32_t *)(((char *) ptr) + specs[i].offset) ;
+
+ if (item_watched (i) && (val != 0))
+ submit_counter (dev, type_name, specs[i].name, mac, val);
+
+ if (item_summed (i))
+ misc += val;
+ }
+
+ if (misc != 0)
+ submit_counter (dev, type_name, misc_name, mac, misc);
+
+}
+
+static void
+process_athstats (int sk, const char *dev)
+{
+ struct ifreq ifr;
+ struct ath_stats stats;
+
+ sstrncpy (ifr.ifr_name, dev, sizeof (ifr.ifr_name));
+ ifr.ifr_data = (void *) &stats;
+ if (ioctl (sk, SIOCGATHSTATS, &ifr) < 0)
+ return;
+
+ /* These stats are handled as a special case, because they are
+ eight values each */
+
+ if (item_watched (STAT_AST_ANT_RX))
+ submit_antx (dev, "ast_ant_rx", stats.ast_ant_rx,
+ STATIC_ARRAY_SIZE (stats.ast_ant_rx));
+
+ if (item_watched (STAT_AST_ANT_TX))
+ submit_antx (dev, "ast_ant_tx", stats.ast_ant_tx,
+ STATIC_ARRAY_SIZE (stats.ast_ant_tx));
+
+ /* All other ath statistics */
+ process_stat_struct (ATH_STAT, &stats, dev, NULL, "ath_stat", "ast_misc");
+}
+
+static void
+process_80211stats (int sk, const char *dev)
+{
+ struct ifreq ifr;
+ struct ieee80211_stats stats;
+ sstrncpy (ifr.ifr_name, dev, sizeof (ifr.ifr_name));
+ ifr.ifr_data = (void *) &stats;
+ if (ioctl(sk, SIOCG80211STATS, &ifr) < 0)
+ return;
+
+ process_stat_struct (IFA_STAT, &stats, dev, NULL, "ath_stat", "is_misc");
+}
+
+
+static void
+process_station (int sk, const char *dev, struct ieee80211req_sta_info *si)
+{
+ struct iwreq iwr;
+ static char mac[DATA_MAX_NAME_LEN];
+ struct ieee80211req_sta_stats stats;
+ const struct ieee80211_nodestats *ns = &stats.is_stats;
+
+ macaddr_to_str (mac, sizeof (mac), si->isi_macaddr);
+
+ if (item_watched (STAT_NODE_TX_RATE))
+ submit_gauge (dev, "node_tx_rate", mac, NULL,
+ (si->isi_rates[si->isi_txrate] & IEEE80211_RATE_VAL) / 2);
+
+ if (item_watched (STAT_NODE_RSSI))
+ submit_gauge (dev, "node_rssi", mac, NULL, si->isi_rssi);
+
+ memset (&iwr, 0, sizeof (iwr));
+ sstrncpy(iwr.ifr_name, dev, sizeof (iwr.ifr_name));
+ iwr.u.data.pointer = (void *) &stats;
+ iwr.u.data.length = sizeof (stats);
+ memcpy(stats.is_u.macaddr, si->isi_macaddr, IEEE80211_ADDR_LEN);
+ if (ioctl(sk, IEEE80211_IOCTL_STA_STATS, &iwr) < 0)
+ return;
+
+ /* These two stats are handled as a special case as they are
+ a pair of 64bit values */
+ if (item_watched (STAT_NODE_OCTETS))
+ submit_counter2 (dev, "node_octets", mac, NULL,
+ ns->ns_rx_bytes, ns->ns_tx_bytes);
+
+ /* This stat is handled as a special case, because it is stored
+ as uin64_t, but we will ignore upper half */
+ if (item_watched (STAT_NS_RX_BEACONS))
+ submit_counter (dev, "node_stat", "ns_rx_beacons", mac,
+ (ns->ns_rx_beacons & 0xFFFFFFFF));
+
+ /* All other node statistics */
+ process_stat_struct (NOD_STAT, ns, dev, mac, "node_stat", "ns_misc");
+}
+
+static void
+process_stations (int sk, const char *dev)
+{
+ uint8_t buf[24*1024];
+ struct iwreq iwr;
+ uint8_t *cp;
+ int len, nodes;
+ int status;
+
+ memset (&iwr, 0, sizeof (iwr));
+ sstrncpy (iwr.ifr_name, dev, sizeof (iwr.ifr_name));
+ iwr.u.data.pointer = (void *) buf;
+ iwr.u.data.length = sizeof (buf);
+
+ status = ioctl (sk, IEEE80211_IOCTL_STA_INFO, &iwr);
+ if (status < 0)
+ {
+ ERROR ("madwifi plugin: Sending IO-control "
+ "IEEE80211_IOCTL_STA_INFO to device %s "
+ "failed with status %i.",
+ dev, status);
+ return;
+ }
+
+ len = iwr.u.data.length;
+
+ cp = buf;
+ nodes = 0;
+ while (len >= sizeof (struct ieee80211req_sta_info))
+ {
+ struct ieee80211req_sta_info *si = (void *) cp;
+ process_station(sk, dev, si);
+ cp += si->isi_len;
+ len -= si->isi_len;
+ nodes++;
+ }
+
+ if (item_watched (STAT_ATH_NODES))
+ submit_gauge (dev, "ath_nodes", NULL, NULL, nodes);
+}
+
+static void
+process_device (int sk, const char *dev)
+{
+ process_athstats (sk, dev);
+ process_80211stats (sk, dev);
+ process_stations (sk, dev);
+}
+
+static int
+check_devname (const char *dev)
+{
+ char buf[256];
+ char buf2[256];
+ int i;
+
+ if (dev[0] == '.')
+ return 0;
+
+ ssnprintf (buf, sizeof (buf), "/sys/class/net/%s/device/driver", dev);
+ buf[sizeof (buf) - 1] = 0;
+
+ i = readlink (buf, buf2, sizeof (buf2) - 1);
+ if (i < 0)
+ return 0;
+ buf2[sizeof (buf2) - 1] = 0;
+
+ if (strstr (buf2, "/drivers/ath_") == NULL)
+ return 0;
+ return 1;
+}
+
+static int
+sysfs_iterate(int sk)
+{
+ struct dirent *de;
+
+ DIR *nets = opendir ("/sys/class/net/");
+ if (nets == NULL)
+ {
+ WARNING ("madwifi plugin: opening /sys/class/net failed");
+ return (-1);
+ }
+
+ while ((de = readdir (nets)))
+ if (check_devname (de->d_name) &&
+ (ignorelist_match (ignorelist, de->d_name) == 0))
+ process_device (sk, de->d_name);
+
+ closedir(nets);
+
+ return 0;
+}
+
+static int
+procfs_iterate(int sk)
+{
+ char buffer[1024];
+ char *device, *dummy;
+ FILE *fh;
+
+ if ((fh = fopen ("/proc/net/dev", "r")) == NULL)
+ {
+ WARNING ("madwifi plugin: opening /proc/net/dev failed");
+ return (-1);
+ }
+
+ while (fgets (buffer, sizeof (buffer), fh) != NULL)
+ {
+ dummy = strchr(buffer, ':');
+ if (dummy == NULL)
+ continue;
+ dummy[0] = 0;
+
+ device = buffer;
+ while (device[0] == ' ')
+ device++;
+
+ if (device[0] == 0)
+ continue;
+
+ if (ignorelist_match (ignorelist, device) == 0)
+ process_device (sk, device);
+ }
+
+ fclose(fh);
+ return 0;
+}
+
+static int madwifi_read (void)
+{
+ int rv;
+ int sk;
+
+ if (init_state == 0)
+ madwifi_real_init();
+ init_state = 2;
+
+ sk = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sk < 0)
+ return (-1);
+
+
+/* procfs iteration is not safe because it does not check whether given
+ interface is madwifi interface and there are private ioctls used, which
+ may do something completely different on non-madwifi devices.
+ Therefore, it is not used unless explicitly enabled (and should be used
+ together with ignorelist). */
+
+ if (use_sysfs)
+ rv = sysfs_iterate(sk);
+ else
+ rv = procfs_iterate(sk);
+
+ close(sk);
+
+ return rv;
+}
+
+void module_register (void)
+{
+ plugin_register_config ("madwifi", madwifi_config,
+ config_keys, config_keys_num);
+
+ plugin_register_read ("madwifi", madwifi_read);
+}
--- /dev/null
+/**
+ * This file is compiled from several Madwifi header files.
+ * Original copyright is:
+ *
+ * Copyright (c) 2001 Atsushi Onoe
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef MADWIFI_H
+#define MADWIFI_H
+
+#define IEEE80211_ADDR_LEN 6 /* size of 802.11 address */
+#define IEEE80211_RATE_VAL 0x7f
+#define IEEE80211_RATE_SIZE 8 /* 802.11 standard */
+#define IEEE80211_RATE_MAXSIZE 15 /* max rates we'll handle */
+
+
+/*
+ * Per/node (station) statistics available when operating as an AP.
+ */
+struct ieee80211_nodestats {
+ u_int32_t ns_rx_data; /* rx data frames */
+ u_int32_t ns_rx_mgmt; /* rx management frames */
+ u_int32_t ns_rx_ctrl; /* rx control frames */
+ u_int32_t ns_rx_ucast; /* rx unicast frames */
+ u_int32_t ns_rx_mcast; /* rx multi/broadcast frames */
+ u_int64_t ns_rx_bytes; /* rx data count (bytes) */
+ u_int64_t ns_rx_beacons; /* rx beacon frames */
+ u_int32_t ns_rx_proberesp; /* rx probe response frames */
+
+ u_int32_t ns_rx_dup; /* rx discard because it's a dup */
+ u_int32_t ns_rx_noprivacy; /* rx w/ wep but privacy off */
+ u_int32_t ns_rx_wepfail; /* rx wep processing failed */
+ u_int32_t ns_rx_demicfail; /* rx demic failed */
+ u_int32_t ns_rx_decap; /* rx decapsulation failed */
+ u_int32_t ns_rx_defrag; /* rx defragmentation failed */
+ u_int32_t ns_rx_disassoc; /* rx disassociation */
+ u_int32_t ns_rx_deauth; /* rx deauthentication */
+ u_int32_t ns_rx_decryptcrc; /* rx decrypt failed on crc */
+ u_int32_t ns_rx_unauth; /* rx on unauthorized port */
+ u_int32_t ns_rx_unencrypted; /* rx unecrypted w/ privacy */
+
+ u_int32_t ns_tx_data; /* tx data frames */
+ u_int32_t ns_tx_mgmt; /* tx management frames */
+ u_int32_t ns_tx_ucast; /* tx unicast frames */
+ u_int32_t ns_tx_mcast; /* tx multi/broadcast frames */
+ u_int64_t ns_tx_bytes; /* tx data count (bytes) */
+ u_int32_t ns_tx_probereq; /* tx probe request frames */
+ u_int32_t ns_tx_uapsd; /* tx on uapsd queue */
+
+ u_int32_t ns_tx_novlantag; /* tx discard due to no tag */
+ u_int32_t ns_tx_vlanmismatch; /* tx discard due to of bad tag */
+
+ u_int32_t ns_tx_eosplost; /* uapsd EOSP retried out */
+
+ u_int32_t ns_ps_discard; /* ps discard due to of age */
+
+ u_int32_t ns_uapsd_triggers; /* uapsd triggers */
+
+ /* MIB-related state */
+ u_int32_t ns_tx_assoc; /* [re]associations */
+ u_int32_t ns_tx_assoc_fail; /* [re]association failures */
+ u_int32_t ns_tx_auth; /* [re]authentications */
+ u_int32_t ns_tx_auth_fail; /* [re]authentication failures*/
+ u_int32_t ns_tx_deauth; /* deauthentications */
+ u_int32_t ns_tx_deauth_code; /* last deauth reason */
+ u_int32_t ns_tx_disassoc; /* disassociations */
+ u_int32_t ns_tx_disassoc_code; /* last disassociation reason */
+ u_int32_t ns_psq_drops; /* power save queue drops */
+};
+
+/*
+ * Summary statistics.
+ */
+struct ieee80211_stats {
+ u_int32_t is_rx_badversion; /* rx frame with bad version */
+ u_int32_t is_rx_tooshort; /* rx frame too short */
+ u_int32_t is_rx_wrongbss; /* rx from wrong bssid */
+ u_int32_t is_rx_dup; /* rx discard due to it's a dup */
+ u_int32_t is_rx_wrongdir; /* rx w/ wrong direction */
+ u_int32_t is_rx_mcastecho; /* rx discard due to of mcast echo */
+ u_int32_t is_rx_notassoc; /* rx discard due to sta !assoc */
+ u_int32_t is_rx_noprivacy; /* rx w/ wep but privacy off */
+ u_int32_t is_rx_unencrypted; /* rx w/o wep and privacy on */
+ u_int32_t is_rx_wepfail; /* rx wep processing failed */
+ u_int32_t is_rx_decap; /* rx decapsulation failed */
+ u_int32_t is_rx_mgtdiscard; /* rx discard mgt frames */
+ u_int32_t is_rx_ctl; /* rx discard ctrl frames */
+ u_int32_t is_rx_beacon; /* rx beacon frames */
+ u_int32_t is_rx_rstoobig; /* rx rate set truncated */
+ u_int32_t is_rx_elem_missing; /* rx required element missing*/
+ u_int32_t is_rx_elem_toobig; /* rx element too big */
+ u_int32_t is_rx_elem_toosmall; /* rx element too small */
+ u_int32_t is_rx_elem_unknown; /* rx element unknown */
+ u_int32_t is_rx_badchan; /* rx frame w/ invalid chan */
+ u_int32_t is_rx_chanmismatch; /* rx frame chan mismatch */
+ u_int32_t is_rx_nodealloc; /* rx frame dropped */
+ u_int32_t is_rx_ssidmismatch; /* rx frame ssid mismatch */
+ u_int32_t is_rx_auth_unsupported;/* rx w/ unsupported auth alg */
+ u_int32_t is_rx_auth_fail; /* rx sta auth failure */
+ u_int32_t is_rx_auth_countermeasures;/* rx auth discard due to CM */
+ u_int32_t is_rx_assoc_bss; /* rx assoc from wrong bssid */
+ u_int32_t is_rx_assoc_notauth; /* rx assoc w/o auth */
+ u_int32_t is_rx_assoc_capmismatch;/* rx assoc w/ cap mismatch */
+ u_int32_t is_rx_assoc_norate; /* rx assoc w/ no rate match */
+ u_int32_t is_rx_assoc_badwpaie; /* rx assoc w/ bad WPA IE */
+ u_int32_t is_rx_deauth; /* rx deauthentication */
+ u_int32_t is_rx_disassoc; /* rx disassociation */
+ u_int32_t is_rx_badsubtype; /* rx frame w/ unknown subtype*/
+ u_int32_t is_rx_nobuf; /* rx failed for lack of buf */
+ u_int32_t is_rx_decryptcrc; /* rx decrypt failed on crc */
+ u_int32_t is_rx_ahdemo_mgt; /* rx discard ahdemo mgt frame*/
+ u_int32_t is_rx_bad_auth; /* rx bad auth request */
+ u_int32_t is_rx_unauth; /* rx on unauthorized port */
+ u_int32_t is_rx_badkeyid; /* rx w/ incorrect keyid */
+ u_int32_t is_rx_ccmpreplay; /* rx seq# violation (CCMP) */
+ u_int32_t is_rx_ccmpformat; /* rx format bad (CCMP) */
+ u_int32_t is_rx_ccmpmic; /* rx MIC check failed (CCMP) */
+ u_int32_t is_rx_tkipreplay; /* rx seq# violation (TKIP) */
+ u_int32_t is_rx_tkipformat; /* rx format bad (TKIP) */
+ u_int32_t is_rx_tkipmic; /* rx MIC check failed (TKIP) */
+ u_int32_t is_rx_tkipicv; /* rx ICV check failed (TKIP) */
+ u_int32_t is_rx_badcipher; /* rx failed due to of key type */
+ u_int32_t is_rx_nocipherctx; /* rx failed due to key !setup */
+ u_int32_t is_rx_acl; /* rx discard due to of acl policy */
+ u_int32_t is_rx_ffcnt; /* rx fast frames */
+ u_int32_t is_rx_badathtnl; /* driver key alloc failed */
+ u_int32_t is_tx_nobuf; /* tx failed for lack of buf */
+ u_int32_t is_tx_nonode; /* tx failed for no node */
+ u_int32_t is_tx_unknownmgt; /* tx of unknown mgt frame */
+ u_int32_t is_tx_badcipher; /* tx failed due to of key type */
+ u_int32_t is_tx_nodefkey; /* tx failed due to no defkey */
+ u_int32_t is_tx_noheadroom; /* tx failed due to no space */
+ u_int32_t is_tx_ffokcnt; /* tx fast frames sent success */
+ u_int32_t is_tx_fferrcnt; /* tx fast frames sent success */
+ u_int32_t is_scan_active; /* active scans started */
+ u_int32_t is_scan_passive; /* passive scans started */
+ u_int32_t is_node_timeout; /* nodes timed out inactivity */
+ u_int32_t is_crypto_nomem; /* no memory for crypto ctx */
+ u_int32_t is_crypto_tkip; /* tkip crypto done in s/w */
+ u_int32_t is_crypto_tkipenmic; /* tkip en-MIC done in s/w */
+ u_int32_t is_crypto_tkipdemic; /* tkip de-MIC done in s/w */
+ u_int32_t is_crypto_tkipcm; /* tkip counter measures */
+ u_int32_t is_crypto_ccmp; /* ccmp crypto done in s/w */
+ u_int32_t is_crypto_wep; /* wep crypto done in s/w */
+ u_int32_t is_crypto_setkey_cipher;/* cipher rejected key */
+ u_int32_t is_crypto_setkey_nokey;/* no key index for setkey */
+ u_int32_t is_crypto_delkey; /* driver key delete failed */
+ u_int32_t is_crypto_badcipher; /* unknown cipher */
+ u_int32_t is_crypto_nocipher; /* cipher not available */
+ u_int32_t is_crypto_attachfail; /* cipher attach failed */
+ u_int32_t is_crypto_swfallback; /* cipher fallback to s/w */
+ u_int32_t is_crypto_keyfail; /* driver key alloc failed */
+ u_int32_t is_crypto_enmicfail; /* en-MIC failed */
+ u_int32_t is_ibss_capmismatch; /* merge failed-cap mismatch */
+ u_int32_t is_ibss_norate; /* merge failed-rate mismatch */
+ u_int32_t is_ps_unassoc; /* ps-poll for unassoc. sta */
+ u_int32_t is_ps_badaid; /* ps-poll w/ incorrect aid */
+ u_int32_t is_ps_qempty; /* ps-poll w/ nothing to send */
+};
+
+/*
+ * Retrieve per-node statistics.
+ */
+struct ieee80211req_sta_stats {
+ union {
+ /* NB: explicitly force 64-bit alignment */
+ u_int8_t macaddr[IEEE80211_ADDR_LEN];
+ u_int64_t pad;
+ } is_u;
+ struct ieee80211_nodestats is_stats;
+};
+
+/*
+ * Station information block; the mac address is used
+ * to retrieve other data like stats, unicast key, etc.
+ */
+struct ieee80211req_sta_info {
+ u_int16_t isi_len; /* length (mult of 4) */
+ u_int16_t isi_freq; /* MHz */
+ u_int16_t isi_flags; /* channel flags */
+ u_int16_t isi_state; /* state flags */
+ u_int8_t isi_authmode; /* authentication algorithm */
+ u_int8_t isi_rssi;
+ u_int16_t isi_capinfo; /* capabilities */
+ u_int8_t isi_athflags; /* Atheros capabilities */
+ u_int8_t isi_erp; /* ERP element */
+ u_int8_t isi_macaddr[IEEE80211_ADDR_LEN];
+ u_int8_t isi_nrates; /* negotiated rates */
+ u_int8_t isi_rates[IEEE80211_RATE_MAXSIZE];
+ u_int8_t isi_txrate; /* index to isi_rates[] */
+ u_int16_t isi_ie_len; /* IE length */
+ u_int16_t isi_associd; /* assoc response */
+ u_int16_t isi_txpower; /* current tx power */
+ u_int16_t isi_vlan; /* vlan tag */
+ u_int16_t isi_txseqs[17]; /* seq to be transmitted */
+ u_int16_t isi_rxseqs[17]; /* seq previous for qos frames*/
+ u_int16_t isi_inact; /* inactivity timer */
+ u_int8_t isi_uapsd; /* UAPSD queues */
+ u_int8_t isi_opmode; /* sta operating mode */
+
+ /* XXX frag state? */
+ /* variable length IE data */
+};
+
+
+struct ath_stats {
+ u_int32_t ast_watchdog; /* device reset by watchdog */
+ u_int32_t ast_hardware; /* fatal hardware error interrupts */
+ u_int32_t ast_bmiss; /* beacon miss interrupts */
+ u_int32_t ast_rxorn; /* rx overrun interrupts */
+ u_int32_t ast_rxeol; /* rx eol interrupts */
+ u_int32_t ast_txurn; /* tx underrun interrupts */
+ u_int32_t ast_mib; /* mib interrupts */
+ u_int32_t ast_tx_packets; /* packet sent on the interface */
+ u_int32_t ast_tx_mgmt; /* management frames transmitted */
+ u_int32_t ast_tx_discard; /* frames discarded prior to assoc */
+ u_int32_t ast_tx_invalid; /* frames discarded due to is device gone */
+ u_int32_t ast_tx_qstop; /* tx queue stopped because it's full */
+ u_int32_t ast_tx_encap; /* tx encapsulation failed */
+ u_int32_t ast_tx_nonode; /* tx failed due to of no node */
+ u_int32_t ast_tx_nobuf; /* tx failed due to of no tx buffer (data) */
+ u_int32_t ast_tx_nobufmgt; /* tx failed due to of no tx buffer (mgmt)*/
+ u_int32_t ast_tx_xretries; /* tx failed due to of too many retries */
+ u_int32_t ast_tx_fifoerr; /* tx failed due to of FIFO underrun */
+ u_int32_t ast_tx_filtered; /* tx failed due to xmit filtered */
+ u_int32_t ast_tx_shortretry; /* tx on-chip retries (short) */
+ u_int32_t ast_tx_longretry; /* tx on-chip retries (long) */
+ u_int32_t ast_tx_badrate; /* tx failed due to of bogus xmit rate */
+ u_int32_t ast_tx_noack; /* tx frames with no ack marked */
+ u_int32_t ast_tx_rts; /* tx frames with rts enabled */
+ u_int32_t ast_tx_cts; /* tx frames with cts enabled */
+ u_int32_t ast_tx_shortpre; /* tx frames with short preamble */
+ u_int32_t ast_tx_altrate; /* tx frames with alternate rate */
+ u_int32_t ast_tx_protect; /* tx frames with protection */
+ u_int32_t ast_rx_orn; /* rx failed due to of desc overrun */
+ u_int32_t ast_rx_crcerr; /* rx failed due to of bad CRC */
+ u_int32_t ast_rx_fifoerr; /* rx failed due to of FIFO overrun */
+ u_int32_t ast_rx_badcrypt; /* rx failed due to of decryption */
+ u_int32_t ast_rx_badmic; /* rx failed due to of MIC failure */
+ u_int32_t ast_rx_phyerr; /* rx PHY error summary count */
+ u_int32_t ast_rx_phy[32]; /* rx PHY error per-code counts */
+ u_int32_t ast_rx_tooshort; /* rx discarded due to frame too short */
+ u_int32_t ast_rx_toobig; /* rx discarded due to frame too large */
+ u_int32_t ast_rx_nobuf; /* rx setup failed due to of no skbuff */
+ u_int32_t ast_rx_packets; /* packet recv on the interface */
+ u_int32_t ast_rx_mgt; /* management frames received */
+ u_int32_t ast_rx_ctl; /* control frames received */
+ int8_t ast_tx_rssi; /* tx rssi of last ack */
+ int8_t ast_rx_rssi; /* rx rssi from histogram */
+ u_int32_t ast_be_xmit; /* beacons transmitted */
+ u_int32_t ast_be_nobuf; /* no skbuff available for beacon */
+ u_int32_t ast_per_cal; /* periodic calibration calls */
+ u_int32_t ast_per_calfail; /* periodic calibration failed */
+ u_int32_t ast_per_rfgain; /* periodic calibration rfgain reset */
+ u_int32_t ast_rate_calls; /* rate control checks */
+ u_int32_t ast_rate_raise; /* rate control raised xmit rate */
+ u_int32_t ast_rate_drop; /* rate control dropped xmit rate */
+ u_int32_t ast_ant_defswitch; /* rx/default antenna switches */
+ u_int32_t ast_ant_txswitch; /* tx antenna switches */
+ u_int32_t ast_ant_rx[8]; /* rx frames with antenna */
+ u_int32_t ast_ant_tx[8]; /* tx frames with antenna */
+};
+
+#define SIOCGATHSTATS (SIOCDEVPRIVATE+0)
+#define SIOCGATHDIAG (SIOCDEVPRIVATE+1)
+#define SIOCGATHRADARSIG (SIOCDEVPRIVATE+2)
+#define SIOCGATHHALDIAG (SIOCDEVPRIVATE+3)
+#define SIOCG80211STATS (SIOCDEVPRIVATE+2)
+/* NB: require in+out parameters so cannot use wireless extensions, yech */
+#define IEEE80211_IOCTL_GETKEY (SIOCDEVPRIVATE+3)
+#define IEEE80211_IOCTL_GETWPAIE (SIOCDEVPRIVATE+4)
+#define IEEE80211_IOCTL_STA_STATS (SIOCDEVPRIVATE+5)
+#define IEEE80211_IOCTL_STA_INFO (SIOCDEVPRIVATE+6)
+#define SIOC80211IFCREATE (SIOCDEVPRIVATE+7)
+#define SIOC80211IFDESTROY (SIOCDEVPRIVATE+8)
+#define IEEE80211_IOCTL_SCAN_RESULTS (SIOCDEVPRIVATE+9)
+
+
+#endif
#include "configfile.h"
#include "utils_fbhash.h"
#include "utils_avltree.h"
+#include "utils_cache.h"
#include "network.h"
static value_list_t send_buffer_vl = VALUE_LIST_STATIC;
static pthread_mutex_t send_buffer_lock = PTHREAD_MUTEX_INITIALIZER;
-/* In this cache we store all the values we received, so we can send out only
- * those values which were *not* received via the network plugin, too. This is
- * used for the `Forward false' option. */
-static c_avl_tree_t *cache_tree = NULL;
-static pthread_mutex_t cache_lock = PTHREAD_MUTEX_INITIALIZER;
-static time_t cache_flush_last = 0;
-static int cache_flush_interval = 1800;
-
/*
* Private functions
*/
-static int cache_flush (void)
+static _Bool check_receive_okay (const value_list_t *vl) /* {{{ */
{
- char **keys = NULL;
- int keys_num = 0;
+ uint64_t time_sent = 0;
+ int status;
- char **tmp;
- int i;
+ status = uc_meta_data_get_unsigned_int (vl,
+ "network:time_sent", &time_sent);
- char *key;
- time_t *value;
- c_avl_iterator_t *iter;
+ /* This is a value we already sent. Don't allow it to be received again in
+ * order to avoid looping. */
+ if ((status == 0) && (time_sent >= ((uint64_t) vl->time)))
+ return (false);
- time_t curtime = time (NULL);
+ return (true);
+} /* }}} _Bool check_receive_okay */
- iter = c_avl_get_iterator (cache_tree);
- while (c_avl_iterator_next (iter, (void *) &key, (void *) &value) == 0)
- {
- if ((curtime - *value) <= cache_flush_interval)
- continue;
- tmp = (char **) realloc (keys,
- (keys_num + 1) * sizeof (char *));
- if (tmp == NULL)
- {
- sfree (keys);
- c_avl_iterator_destroy (iter);
- ERROR ("network plugin: cache_flush: realloc"
- " failed.");
- return (-1);
- }
- keys = tmp;
- keys[keys_num] = key;
- keys_num++;
- } /* while (c_avl_iterator_next) */
- c_avl_iterator_destroy (iter);
+static _Bool check_send_okay (const value_list_t *vl) /* {{{ */
+{
+ _Bool received = false;
+ int status;
- for (i = 0; i < keys_num; i++)
- {
- if (c_avl_remove (cache_tree, keys[i], (void *) &key,
- (void *) &value) != 0)
- {
- WARNING ("network plugin: cache_flush: c_avl_remove"
- " (%s) failed.", keys[i]);
- continue;
- }
+ if (network_config_forward != 0)
+ return (true);
- sfree (key);
- sfree (value);
- }
+ if (vl->meta == NULL)
+ return (true);
- sfree (keys);
+ status = meta_data_get_boolean (vl->meta, "network:received", &received);
+ if (status == -ENOENT)
+ return (true);
+ else if (status != 0)
+ {
+ ERROR ("network plugin: check_send_okay: meta_data_get_boolean failed "
+ "with status %i.", status);
+ return (true);
+ }
- DEBUG ("network plugin: cache_flush: Removed %i %s",
- keys_num, (keys_num == 1) ? "entry" : "entries");
- cache_flush_last = curtime;
- return (0);
-} /* int cache_flush */
+ /* By default, only *send* value lists that were not *received* by the
+ * network plugin. */
+ return (!received);
+} /* }}} _Bool check_send_okay */
-static int cache_check (const value_list_t *vl)
+static int network_dispatch_values (value_list_t *vl) /* {{{ */
{
- char key[1024];
- time_t *value = NULL;
- int retval = -1;
+ int status;
- if (cache_tree == NULL)
- return (-1);
+ if ((vl->time <= 0)
+ || (strlen (vl->host) <= 0)
+ || (strlen (vl->plugin) <= 0)
+ || (strlen (vl->type) <= 0))
+ return (-EINVAL);
- if (format_name (key, sizeof (key), vl->host, vl->plugin,
- vl->plugin_instance, vl->type, vl->type_instance))
- return (-1);
+ if (!check_receive_okay (vl))
+ {
+#if COLLECT_DEBUG
+ char name[6*DATA_MAX_NAME_LEN];
+ FORMAT_VL (name, sizeof (name), vl);
+ name[sizeof (name) - 1] = 0;
+ DEBUG ("network plugin: network_dispatch_values: "
+ "NOT dispatching %s.", name);
+#endif
+ return (0);
+ }
- pthread_mutex_lock (&cache_lock);
+ assert (vl->meta == NULL);
- if (c_avl_get (cache_tree, key, (void *) &value) == 0)
- {
- if (*value < vl->time)
- {
- *value = vl->time;
- retval = 0;
- }
- else
- {
- DEBUG ("network plugin: cache_check: *value = %i >= vl->time = %i",
- (int) *value, (int) vl->time);
- retval = 1;
- }
- }
- else
- {
- char *key_copy = strdup (key);
- value = malloc (sizeof (time_t));
- if ((key_copy != NULL) && (value != NULL))
- {
- *value = vl->time;
- c_avl_insert (cache_tree, key_copy, value);
- retval = 0;
- }
- else
- {
- sfree (key_copy);
- sfree (value);
- }
- }
+ vl->meta = meta_data_create ();
+ if (vl->meta == NULL)
+ {
+ ERROR ("network plugin: meta_data_create failed.");
+ return (-ENOMEM);
+ }
- if ((time (NULL) - cache_flush_last) > cache_flush_interval)
- cache_flush ();
+ status = meta_data_add_boolean (vl->meta, "network:received", true);
+ if (status != 0)
+ {
+ ERROR ("network plugin: meta_data_add_boolean failed.");
+ meta_data_destroy (vl->meta);
+ vl->meta = NULL;
+ return (status);
+ }
- pthread_mutex_unlock (&cache_lock);
+ plugin_dispatch_values (vl);
- return (retval);
-} /* int cache_check */
+ meta_data_destroy (vl->meta);
+ vl->meta = NULL;
+
+ return (0);
+} /* }}} int network_dispatch_values */
#if HAVE_LIBGCRYPT
static gcry_cipher_hd_t network_get_aes256_cypher (sockent_t *se, /* {{{ */
{
status = parse_part_values (&buffer, &buffer_size,
&vl.values, &vl.values_len);
-
if (status != 0)
break;
- if ((vl.time > 0)
- && (strlen (vl.host) > 0)
- && (strlen (vl.plugin) > 0)
- && (strlen (vl.type) > 0)
- && (cache_check (&vl) == 0))
- {
- plugin_dispatch_values (&vl);
- }
- else
- {
- DEBUG ("network plugin: parse_packet:"
- " NOT dispatching values");
- }
+ network_dispatch_values (&vl);
sfree (vl.values);
}
{
int status;
- /* If the value is already in the cache, we have received it via the
- * network. We write it again if forwarding is activated. It's then in
- * the cache and should we receive it again we will ignore it. */
- status = cache_check (vl);
- if ((network_config_forward == 0)
- && (status != 0))
- return (0);
+ if (!check_send_okay (vl))
+ {
+#if COLLECT_DEBUG
+ char name[6*DATA_MAX_NAME_LEN];
+ FORMAT_VL (name, sizeof (name), vl);
+ name[sizeof (name) - 1] = 0;
+ DEBUG ("network plugin: network_write: "
+ "NOT sending %s.", name);
+#endif
+ return (0);
+ }
+
+ uc_meta_data_add_unsigned_int (vl,
+ "network:time_sent", (uint64_t) vl->time);
pthread_mutex_lock (&send_buffer_lock);
return (0);
} /* }}} int network_config_add_server */
-static int network_config_set_cache_flush (const oconfig_item_t *ci) /* {{{ */
-{
- int tmp;
- if ((ci->values_num != 1)
- || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
- {
- WARNING ("network plugin: The `CacheFlush' config option needs exactly "
- "one numeric argument.");
- return (-1);
- }
-
- tmp = (int) ci->values[0].value.number;
- if (tmp > 0)
- network_config_ttl = tmp;
-
- return (0);
-} /* }}} int network_config_set_cache_flush */
-
static int network_config (oconfig_item_t *ci) /* {{{ */
{
int i;
else if (strcasecmp ("Forward", child->key) == 0)
network_config_set_boolean (child, &network_config_forward);
else if (strcasecmp ("CacheFlush", child->key) == 0)
- network_config_set_cache_flush (child);
+ /* no op for backwards compatibility only */;
else
{
WARNING ("network plugin: Option `%s' is not allowed here.",
if (send_buffer_fill > 0)
flush_buffer ();
- if (cache_tree != NULL)
- {
- void *key;
- void *value;
-
- while (c_avl_pick (cache_tree, &key, &value) == 0)
- {
- sfree (key);
- sfree (value);
- }
- c_avl_destroy (cache_tree);
- cache_tree = NULL;
- }
-
/* TODO: Close `sending_sockets' */
plugin_unregister_config ("network");
plugin_unregister_write ("network");
plugin_unregister_shutdown ("network");
- /* Let the init function do it's move again ;) */
- cache_flush_last = 0;
-
return (0);
} /* int network_shutdown */
static int network_init (void)
{
+ static _Bool have_init = false;
+
/* Check if we were already initialized. If so, just return - there's
* nothing more to do (for now, that is). */
- if (cache_flush_last != 0)
+ if (have_init)
return (0);
+ have_init = true;
plugin_register_shutdown ("network", network_shutdown);
network_init_buffer ();
- cache_tree = c_avl_create ((int (*) (const void *, const void *)) strcmp);
- cache_flush_last = time (NULL);
-
/* setup socket(s) and so on */
if (sending_sockets != NULL)
{
{
pthread_mutex_lock (&send_buffer_lock);
- if (((time (NULL) - cache_flush_last) >= timeout)
- && (send_buffer_fill > 0))
- {
- flush_buffer ();
- }
+ if (send_buffer_fill > 0)
+ flush_buffer ();
pthread_mutex_unlock (&send_buffer_lock);