Access Point Registration Table
Таблица уровней клиентов для точек доступа Cisco
Задача
Собирать данные о траффике и уровне сигнала с точек доступа
- Клиенты динамические, их мак-адреса заранее не известны
- SSID добавляются и удаляются по мере необходимости
- Точки могут иметь как один интерфейс так и два
Для того что бы получить информацию по SNMP о клиентах подключенных к точке доступа, существует специальный MIB CISCO-DOT11-ASSOCIATION-MIB
Он доступен достаточно давно, как минимум он работает на точках 1200 и 1100 серий, ну и на более современных тоже.
Просмотр OID
Пример с использованием display-hint
# snmpwalk -v2c -c MON 192.168.77.2 .1.3.6.1.4.1.9.9.273.1.3.1 -OX
CISCO-DOT11-ASSOCIATION-MIB::cDot11ClientCurrentTxRateSet[1]["450"][STRING: da:20:60:64:36:95] = Hex-STRING: 07 CISCO-DOT11-ASSOCIATION-MIB::cDot11ClientCurrentTxRateSet[2]["450"][STRING: a4:45:19:5a:ef:48] = Hex-STRING: 01 CISCO-DOT11-ASSOCIATION-MIB::cDot11ClientUpTime[1]["450"][STRING: da:20:60:64:36:95] = Gauge32: 15898 second CISCO-DOT11-ASSOCIATION-MIB::cDot11ClientUpTime[2]["450"][STRING: a4:45:19:5a:ef:48] = Gauge32: 425 second CISCO-DOT11-ASSOCIATION-MIB::cDot11ClientSignalStrength[1]["450"][STRING: da:20:60:64:36:95] = INTEGER: -64 dBm CISCO-DOT11-ASSOCIATION-MIB::cDot11ClientSignalStrength[2]["450"][STRING: a4:45:19:5a:ef:48] = INTEGER: -89 dBm CISCO-DOT11-ASSOCIATION-MIB::cDot11ClientSigQuality[1]["450"][STRING: da:20:60:64:36:95] = Gauge32: 24 percentage CISCO-DOT11-ASSOCIATION-MIB::cDot11ClientSigQuality[2]["450"][STRING: a4:45:19:5a:ef:48] = Gauge32: 9 percentage CISCO-DOT11-ASSOCIATION-MIB::cDot11ClientAgingLeft[1]["450"][STRING: da:20:60:64:36:95] = Gauge32: 58 second CISCO-DOT11-ASSOCIATION-MIB::cDot11ClientAgingLeft[2]["450"][STRING: a4:45:19:5a:ef:48] = Gauge32: 34 second CISCO-DOT11-ASSOCIATION-MIB::cDot11ClientPacketsReceived[1]["450"][STRING: da:20:60:64:36:95] = Counter32: 149529 packet CISCO-DOT11-ASSOCIATION-MIB::cDot11ClientPacketsReceived[2]["450"][STRING: a4:45:19:5a:ef:48] = Counter32: 346 packet CISCO-DOT11-ASSOCIATION-MIB::cDot11ClientBytesReceived[1]["450"][STRING: da:20:60:64:36:95] = Counter32: 23101125 byte CISCO-DOT11-ASSOCIATION-MIB::cDot11ClientBytesReceived[2]["450"][STRING: a4:45:19:5a:ef:48] = Counter32: 113287 byte CISCO-DOT11-ASSOCIATION-MIB::cDot11ClientPacketsSent[1]["450"][STRING: da:20:60:64:36:95] = Counter32: 617733 packet CISCO-DOT11-ASSOCIATION-MIB::cDot11ClientPacketsSent[2]["450"][STRING: a4:45:19:5a:ef:48] = Counter32: 251 packet CISCO-DOT11-ASSOCIATION-MIB::cDot11ClientBytesSent[1]["450"][STRING: da:20:60:64:36:95] = Counter32: 745190393 byte CISCO-DOT11-ASSOCIATION-MIB::cDot11ClientBytesSent[2]["450"][STRING: a4:45:19:5a:ef:48] = Counter32: 47693 byte CISCO-DOT11-ASSOCIATION-MIB::cDot11ClientDuplicates[1]["450"][STRING: da:20:60:64:36:95] = Counter32: 2389 packet CISCO-DOT11-ASSOCIATION-MIB::cDot11ClientDuplicates[2]["450"][STRING: a4:45:19:5a:ef:48] = Counter32: 12 packet CISCO-DOT11-ASSOCIATION-MIB::cDot11ClientMsduRetries[1]["450"][STRING: da:20:60:64:36:95] = Counter32: 159859 packet CISCO-DOT11-ASSOCIATION-MIB::cDot11ClientMsduRetries[2]["450"][STRING: a4:45:19:5a:ef:48] = Counter32: 331 packet CISCO-DOT11-ASSOCIATION-MIB::cDot11ClientMsduFails[1]["450"][STRING: da:20:60:64:36:95] = Counter32: 0 packet CISCO-DOT11-ASSOCIATION-MIB::cDot11ClientMsduFails[2]["450"][STRING: a4:45:19:5a:ef:48] = Counter32: 0 packet CISCO-DOT11-ASSOCIATION-MIB::cDot11ClientWepErrors[1]["450"][STRING: da:20:60:64:36:95] = Counter32: 0 packet CISCO-DOT11-ASSOCIATION-MIB::cDot11ClientWepErrors[2]["450"][STRING: a4:45:19:5a:ef:48] = Counter32: 0 packet CISCO-DOT11-ASSOCIATION-MIB::cDot11ClientMicErrors[1]["450"][STRING: da:20:60:64:36:95] = Counter32: 0 error CISCO-DOT11-ASSOCIATION-MIB::cDot11ClientMicErrors[2]["450"][STRING: a4:45:19:5a:ef:48] = Counter32: 0 error CISCO-DOT11-ASSOCIATION-MIB::cDot11ClientMicMissingFrames[1]["450"][STRING: da:20:60:64:36:95] = Counter32: 0 packet CISCO-DOT11-ASSOCIATION-MIB::cDot11ClientMicMissingFrames[2]["450"][STRING: a4:45:19:5a:ef:48] = Counter32: 0 packet
Или без использования
# snmpwalk -v2c -c MON 192.168.77.2 .1.3.6.1.4.1.9.9.273.1.3.1 -On
.1.3.6.1.4.1.9.9.273.1.3.1.1.1.1.3.52.53.48.218.32.96.100.54.149 = Hex-STRING: 05 .1.3.6.1.4.1.9.9.273.1.3.1.1.1.2.3.52.53.48.164.69.25.90.239.72 = Hex-STRING: 01 .1.3.6.1.4.1.9.9.273.1.3.1.1.2.1.3.52.53.48.218.32.96.100.54.149 = Gauge32: 15911 second .1.3.6.1.4.1.9.9.273.1.3.1.1.2.2.3.52.53.48.164.69.25.90.239.72 = Gauge32: 439 second .1.3.6.1.4.1.9.9.273.1.3.1.1.3.1.3.52.53.48.218.32.96.100.54.149 = INTEGER: -63 dBm .1.3.6.1.4.1.9.9.273.1.3.1.1.3.2.3.52.53.48.164.69.25.90.239.72 = INTEGER: -89 dBm .1.3.6.1.4.1.9.9.273.1.3.1.1.4.1.3.52.53.48.218.32.96.100.54.149 = Gauge32: 31 percentage .1.3.6.1.4.1.9.9.273.1.3.1.1.4.2.3.52.53.48.164.69.25.90.239.72 = Gauge32: 10 percentage .1.3.6.1.4.1.9.9.273.1.3.1.1.5.1.3.52.53.48.218.32.96.100.54.149 = Gauge32: 58 second .1.3.6.1.4.1.9.9.273.1.3.1.1.5.2.3.52.53.48.164.69.25.90.239.72 = Gauge32: 51 second .1.3.6.1.4.1.9.9.273.1.3.1.1.6.1.3.52.53.48.218.32.96.100.54.149 = Counter32: 149587 packet .1.3.6.1.4.1.9.9.273.1.3.1.1.6.2.3.52.53.48.164.69.25.90.239.72 = Counter32: 347 packet .1.3.6.1.4.1.9.9.273.1.3.1.1.7.1.3.52.53.48.218.32.96.100.54.149 = Counter32: 23109939 byte .1.3.6.1.4.1.9.9.273.1.3.1.1.7.2.3.52.53.48.164.69.25.90.239.72 = Counter32: 113333 byte .1.3.6.1.4.1.9.9.273.1.3.1.1.8.1.3.52.53.48.218.32.96.100.54.149 = Counter32: 618046 packet .1.3.6.1.4.1.9.9.273.1.3.1.1.8.2.3.52.53.48.164.69.25.90.239.72 = Counter32: 251 packet .1.3.6.1.4.1.9.9.273.1.3.1.1.9.1.3.52.53.48.218.32.96.100.54.149 = Counter32: 745583754 byte .1.3.6.1.4.1.9.9.273.1.3.1.1.9.2.3.52.53.48.164.69.25.90.239.72 = Counter32: 47693 byte .1.3.6.1.4.1.9.9.273.1.3.1.1.10.1.3.52.53.48.218.32.96.100.54.149 = Counter32: 2392 packet .1.3.6.1.4.1.9.9.273.1.3.1.1.10.2.3.52.53.48.164.69.25.90.239.72 = Counter32: 12 packet .1.3.6.1.4.1.9.9.273.1.3.1.1.11.1.3.52.53.48.218.32.96.100.54.149 = Counter32: 159964 packet .1.3.6.1.4.1.9.9.273.1.3.1.1.11.2.3.52.53.48.164.69.25.90.239.72 = Counter32: 331 packet .1.3.6.1.4.1.9.9.273.1.3.1.1.12.1.3.52.53.48.218.32.96.100.54.149 = Counter32: 0 packet .1.3.6.1.4.1.9.9.273.1.3.1.1.12.2.3.52.53.48.164.69.25.90.239.72 = Counter32: 0 packet .1.3.6.1.4.1.9.9.273.1.3.1.1.13.1.3.52.53.48.218.32.96.100.54.149 = Counter32: 0 packet .1.3.6.1.4.1.9.9.273.1.3.1.1.13.2.3.52.53.48.164.69.25.90.239.72 = Counter32: 0 packet .1.3.6.1.4.1.9.9.273.1.3.1.1.14.1.3.52.53.48.218.32.96.100.54.149 = Counter32: 0 error .1.3.6.1.4.1.9.9.273.1.3.1.1.14.2.3.52.53.48.164.69.25.90.239.72 = Counter32: 0 error .1.3.6.1.4.1.9.9.273.1.3.1.1.15.1.3.52.53.48.218.32.96.100.54.149 = Counter32: 0 packet .1.3.6.1.4.1.9.9.273.1.3.1.1.15.2.3.52.53.48.164.69.25.90.239.72 = Counter32: 0 packet
Разбор OID
Как можно видеть, сам OID состоит из нескольких частей Для определенности рассмотрим уровни сигнала:
.1.3.6.1.4.1.9.9.273.1.3.1.1.3.1.3.52.53.48.218.32.96.100.54.149 = INTEGER: -63 dBm .1.3.6.1.4.1.9.9.273.1.3.1.1.3.2.3.52.53.48.164.69.25.90.239.72 = INTEGER: -89 dBm
CISCO-DOT11-ASSOCIATION-MIB::cDot11ClientSignalStrength[1]["450"][STRING: da:20:60:64:36:95] = INTEGER: -64 dBm CISCO-DOT11-ASSOCIATION-MIB::cDot11ClientSignalStrength[2]["450"][STRING: a4:45:19:5a:ef:48] = INTEGER: -89 dBm
Часть .1.3.6.1.4.1.9.9.273.1.3.1.1.3 это и есть OID CISCO-DOT11-ASSOCIATION и она общая Далее в самом OID закодировано значение "кому принадлежит сигнал", т е какому клиенту
Выделенное значение и его описание:
- 2.3.52.53.48.164.69.25.90.239.72 - номер интерфейса, он же ifIndex, dot11Radio0 (1), или dot11Radio1 (2), в примере клиент подключен к 5Ghz интерфейсу (dot11Radio1)
- 2.3.52.53.48.164.69.25.90.239.72 - Длинна SSID в байтах, в пример 3 символа (SSID в примере "450")
- 2.3.52.53.48.164.69.25.90.239.72 - коды ASCII символов SSID, их три - "450"
- 2.3.52.53.48.164.69.25.90.239.72 - мак-адрес, 6 байт, записаны в деятичной форме, "a4:45:19:5a:ef:48", например 164(десятичное) = a4(hex), 69(десятичное) = 45 (hex) и так далее.
Интеграция с Zabbix
SNMP autodiscovery
Есть дока но мне она не нравится =)
В целом идея такая:
- В поле SNMP OID указываю discovery[{#S},1.3.6.1.4.1.9.9.273.1.3.1.1.3]
- Заббикс сам делает snmpwalk 1.3.6.1.4.1.9.9.273.1.3.1.1.3 и в результате получает список клиентов и их сигналы
snmpwalk -v2c -c MON 192.168.77.2 1.3.6.1.4.1.9.9.273.1.3.1.1.3 -OX Bad operator (INTEGER): At line 73 in /usr/share/snmp/mibs/ietf/SNMPv2-PDU CISCO-DOT11-ASSOCIATION-MIB::cDot11ClientSignalStrength[1]["450"][STRING: 0:8:ca:2b:1:24] = INTEGER: -65 dBm CISCO-DOT11-ASSOCIATION-MIB::cDot11ClientSignalStrength[2]["450"][STRING: a4:45:19:5a:ef:48] = INTEGER: -67 dBm
и на выходе формирует JSON в котором есть поля "{#SNMPINDEX}" - это часть которую нужно дописать к OID указанному в discovery что бы получить значение
[ { "{#SNMPINDEX}": "1.3.52.53.48.0.8.202.43.1.36", "{#S}": "-66" }, { "{#SNMPINDEX}": "2.3.52.53.48.164.69.25.90.239.72", "{#S}": "-66" } ]
Это выглядит странновато, но суть этого - получить список суффиксов, которые потом можно использовать для получения других значений
В поле "{#S}" записан сигнал и его значение я нигде не использую, но в общем случае (для других MIB/OID) тут может быть что-то что можно использовать (в оригинальной документации это имена интерфейсов)
В общем случае можно указать несколько OID которые нужно обходить snmpwalk-ом, но при этом нужно держать в голове что SNMPINDEX должен совпадать - так работает для таблицы интерфейсов где по разным префиксам но с одним и тем же ifIndex можно получить данные с интерфейса - имя, дескрипшен, байты и т.п.
Если же указать 2 OID с разной структурой (например интерфейсы и таблицу регистрации клиентов) то ничего хорошего не получится - данные будут перемешаны (подробно я не смотрел код как именно и почему )
Далее при создании прототипов элементов данных можно использовать "{#SNMPINDEX}" и тогда все остальные переменны в JSON полученном при discovery будут доступны
Например, если при создании прототипа данныхь указать OID=.1.3.6.1.4.1.9.9.273.1.3.1.1.2.{#SNMPINDEX} то (для моего примера будет автоматически создано 2 элемента данных, в именах которых можно использовать например значение макроса "{$S}". Это хорошо ложиться на пример с интерфейсом, но в моем случае этот макрос как и остальные совершенно бесполезен так как вся статическая информация закодирована в OID который собственно доступен через макрос "{SNMPINDEX}"
Processing
Для того что бы получить нужные данные в удобном виде полученный при discovery JSON можно редактировать с помошью встроенного JavaScript
В моем случае на скорую руку вышел вот такой скрипт:
var records = JSON.parse(value); for (i = 0; i < records.length; i++) { var OID = records[i]["{#SNMPINDEX}"].split('.') // IFINDEX AND IFNAME var dot1RadioIfIndex = OID.shift(); records[i]['{#DOT1RADIO_IFINDEX}'] = dot1RadioIfIndex records[i]['{#DOT1RADIO_IFNAME}'] = "Dot11Radio" + (parseInt(dot1RadioIfIndex) - 1).toString() // get mac as 6 last digits converted dec -> hex and joined with ':' var MAC = "" var MACDigitHex for (j = 0; j < 6; j++) { MACDigitDec = OID.pop(); MACDigitHex = parseInt(MACDigitDec).toString(16); MAC = MACDigitHex + " " + MAC } records[i]['{#MAC}'] = MAC.trim().split(" ").join(":") // SSID. The first octet is length, others are ASCII characters SSID = "" ssidLength = OID.shift(); for (j = 0; j < ssidLength; j++) { SSID += String.fromCharCode(parseInt(OID[j])); } records[i]['{#SSID}'] = SSID } return JSON.stringify(records);
Идея в том что бы добавить поля построенные на основе SNMPINDEX в Json, и в дальнейшем они будут доступны при создании элементов данных
В результате из исходного JSON получаю такой
[ { "{#SNMPINDEX}": "1.3.52.53.48.0.8.202.43.1.36", "{#S}": "-62", "{#DOT1RADIO_IFINDEX}": "1", "{#DOT1RADIO_IFNAME}": "Dot11Radio0", "{#MAC}": "0:8:ca:2b:1:24", "{#SSID}": "450" }, { "{#SNMPINDEX}": "2.3.52.53.48.164.69.25.90.239.72", "{#S}": "-79", "{#DOT1RADIO_IFINDEX}": "2", "{#DOT1RADIO_IFNAME}": "Dot11Radio1", "{#MAC}": "a4:45:19:5a:ef:48", "{#SSID}": "450" } ]
который уже содержит статическую информацию о клиенте и который можно использовать для создания элеметов данных
Прототпипы жлементов данных
Формируем имя и ключ на основе данных из Json, например
Name cDot11ClientSignalStrength [ {#MAC}@{#DOT1RADIO_IFNAME}:{#SSID} ]
где для кажого клиента будет создан отдельный элемент данных, при этом используются три уникальных значения, клиент с тем же маком но подключившись в другом диапазоне (другой интерфейс) рассматривается как другой клиент даже если используется один SSID для разных частот)