librenms / librenms-agent

LibreNMS Agent & Scripts
GNU General Public License v2.0
117 stars 188 forks source link

Wireless AP script for upcoming Wireless AP application #442

Closed bnerickson closed 1 year ago

bnerickson commented 1 year ago

This script is intended for usage on a Linux wireless AP to grab wireless statistics from all attached interfaces and all stations that are connected to said interfaces. All of the interfaces and stations are automatically discovered. This script should work independent of whether the server is OpenWRT, Fedora-based, Debian-based, etc.

Wireless statistics are grabbed using the following iw commands:

iw dev <interface> info
iw dev <interface> station get <mac>
iw <interface> survey dump

There are a couple of optional features that can be specified in the /etc/snmp/wireless.json configuration file:

  1. client_lifetime: This allows the administrator to influence how long (in days) "dead" stations will continue to be graphed in LibreNMS. Setting this to 0 never expires a dead client and setting this to any negative value will ensure that NO clients are graphed.
  2. mac_addr_to_friendly_name: This is a dictionary that allows the administrator to set a "friendly" name for a mac address. All this does is show the name in the graph titles in LibreNMS, so it's easier to correlate a device with its owner/purpose.

Example json output:

{
  "errorString": "",
  "error": 0,
  "version": 1,
  "data": {
    "client_lifetime": 365,
    "friendly_names": {
      "de:ad:be:ef:be:ef": "my_tablet"
    },
    "interfaces": {
      "wlp3s0": {
        "ssid": "example_SSID_1",
        "center1": 5775,
        "channel": 5745,
        "width": 80,
        "txpower": 30,
        "noise": -92,
        "channel_active_time": 93433419,
        "channel_busy_time": 6094300,
        "channel_receive_time": 88028,
        "channel_transmit_time": 495947,
        "clients": {
          "de:ad:be:ef:be:ef": {
            "inactive_time": 3061,
            "rx_bytes": 2625771,
            "rx_packets": 28259,
            "tx_bytes": 10931036,
            "tx_packets": 16486,
            "tx_retries": 5239,
            "tx_failed": 11,
            "rx_drop_misc": 55,
            "signal": -63,
            "tx_bitrate": 351,
            "rx_bitrate": 6,
            "rx_duration": 2310796,
            "dtim_inverval": 2,
            "beacon_interval": 100,
            "connected_time": 97243,
            "snr": 29
          },
          "ab:ab:ab:ab:ab:ab": {
            "inactive_time": 6679,
            "rx_bytes": 63374317,
            "rx_packets": 523670,
            "tx_bytes": 1585489277,
            "tx_packets": 1226672,
            "tx_retries": 70304,
            "tx_failed": 514,
            "rx_drop_misc": 154,
            "signal": -52,
            "tx_bitrate": 351,
            "rx_bitrate": 24,
            "rx_duration": 29518344,
            "dtim_inverval": 2,
            "beacon_interval": 100,
            "connected_time": 95616,
            "snr": 40
          }
        }
      },
      "wlp0s20u13": {
        "ssid": "example_SSID_2",
        "center1": 2412,
        "channel": 2412,
        "width": 20,
        "txpower": 20,
        "clients": {
          "de:ad:be:ef:be:ef": {
            "inactive_time": 96,
            "rx_bytes": 15566236,
            "rx_packets": 59569,
            "tx_bytes": 15958312,
            "tx_packets": 57323,
            "tx_retries": 0,
            "tx_failed": 0,
            "rx_drop_misc": 224,
            "tx_bitrate": 72.2,
            "rx_bitrate": 72.2,
            "rx_duration": 0,
            "dtim_inverval": 2,
            "beacon_interval": 100,
            "connected_time": 97278
          }
        }
      },
      "wlp4s0": {
        "ssid": "example_SSID_3",
        "center1": 5190,
        "channel": 5180,
        "width": 40,
        "txpower": 15,
        "noise": -95,
        "channel_active_time": 97180918,
        "channel_busy_time": 5502499,
        "channel_receive_time": 5281693,
        "channel_transmit_time": 194850,
        "clients": {}
      }
    }
  }
}

Here are a couple screenshots of what this will look like in LibreNMS once all is said and done: wireless_ap_1 wireless_ap_2

Improvements: I could see someone coming in and adding filter lists to exclude statistics for particular interfaces or stations from being collected for privacy or other reasons. In my use-case, I don't have those concerns.

bnerickson commented 1 year ago

I'm also open to changing the name of the script/application to something less generic than "Wireless AP". On Linux, wireless AP functionality is typically provided via the hostapd service. However, naming this script after hostapd seems wrong since hostapd is never queried directly by this script.

VVelox commented 1 year ago

I'm also open to changing the name of the script/application to something less generic than "Wireless AP". On Linux, wireless AP functionality is typically provided via the hostapd service. However, naming this script after hostapd seems wrong since hostapd is never queried directly by this script.

Personally I would go with linux_iw or something like that. All good though. :)

VVelox commented 1 year ago

In regards to client_lifetime, this is something I would personally move into the LibreNMS config as well as adding a optional setting of allowing it accept settings for that from the remote system.

bnerickson commented 1 year ago

The name change makes sense. I tested the script out on a wireless client and it does pull some stats for its connection to an AP, so I changed the name of the script to "linux_iw" and made a change to grab the type or mode the interface is operating under ("AP" for instance). The linux_iw application is being updated in https://github.com/librenms/librenms/pull/14677 such that the graphs will display whether the data corresponds to a client (when the polled device is an AP) or an AP (when the polled device is a wireless client).

I also updated the script to return "null" if no "client_lifetime" (now referred to as "linux_iw_cap_lifetime") is specified in the linux_iw.json config file, so a global default can be specified by the user in the LibreNMS config. The value specified in linux_iw.json will override the global default.

VVelox commented 1 year ago

Awesome! :)