瀏覽代碼

added support snmpv3 to perl backend

Roman Dmitriev 1 年之前
父節點
當前提交
cfb1d63

+ 4 - 0
Readme.en.md

@@ -41,6 +41,10 @@ cd Eye/
 cp -R scripts/ /opt/Eye/
 cp -R html/ /opt/Eye/
 
+patch perl snmp for support SHA512
+
+#cp -f docs/patches/USM.pm /usr/share/perl5/Net/SNMP/Security/USM.pm
+
 4. You can download additional scripts (prettiness)
 
 mkdir -p /opt/Eye/html/js/jq

+ 4 - 0
Readme.ru.md

@@ -41,6 +41,10 @@ cd Eye/
 cp -R scripts/ /opt/Eye/
 cp -R html/ /opt/Eye/
 
+patch perl snmp for support SHA512
+
+#cp -f docs/patches/USM.pm /usr/share/perl5/Net/SNMP/Security/USM.pm
+
 4. Можно скачать дополнительные скрипты (красивости)
 
 mkdir -p /opt/Eye/html/js/jq

+ 1587 - 0
docs/mysql/2-7-1/mysql.sql

@@ -0,0 +1,1587 @@
+-- phpMyAdmin SQL Dump
+-- version 5.2.1
+-- https://www.phpmyadmin.net/
+--
+-- Хост: localhost
+-- Время создания: Дек 03 2024 г., 14:57
+-- Версия сервера: 10.11.6-MariaDB-0+deb12u1-log
+-- Версия PHP: 8.2.26
+
+SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
+START TRANSACTION;
+SET time_zone = "+00:00";
+
+
+/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
+/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
+/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
+/*!40101 SET NAMES utf8mb4 */;
+
+--
+-- База данных: `stat`
+--
+
+-- --------------------------------------------------------
+
+--
+-- Структура таблицы `acl`
+--
+
+CREATE TABLE `acl` (
+  `id` int(11) NOT NULL,
+  `name` varchar(30) NOT NULL,
+  `description.english` varchar(250) NOT NULL,
+  `description.russian` varchar(250) NOT NULL
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+-- --------------------------------------------------------
+
+--
+-- Структура таблицы `ad_comp_cache`
+--
+
+CREATE TABLE `ad_comp_cache` (
+  `id` int(11) NOT NULL,
+  `name` varchar(63) NOT NULL,
+  `last_found` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp()
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+-- --------------------------------------------------------
+
+--
+-- Структура таблицы `auth_rules`
+--
+
+CREATE TABLE `auth_rules` (
+  `id` int(11) NOT NULL,
+  `user_id` int(11) DEFAULT NULL,
+  `ou_id` int(11) DEFAULT NULL,
+  `type` int(11) NOT NULL,
+  `rule` varchar(40) DEFAULT NULL,
+  `comment` varchar(250) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+-- --------------------------------------------------------
+
+--
+-- Структура таблицы `building`
+--
+
+CREATE TABLE `building` (
+  `id` int(11) NOT NULL,
+  `name` varchar(50) NOT NULL,
+  `comment` varchar(250) DEFAULT NULL
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+--
+-- Дамп данных таблицы `building`
+--
+
+INSERT INTO `building` (`id`, `name`, `comment`) VALUES(1, 'Earth', 'Somewhere');
+
+-- --------------------------------------------------------
+
+--
+-- Структура таблицы `config`
+--
+
+CREATE TABLE `config` (
+  `id` int(11) NOT NULL,
+  `option_id` int(11) DEFAULT NULL,
+  `value` varchar(250) DEFAULT NULL
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+--
+-- Дамп данных таблицы `config`
+--
+
+INSERT INTO `config` (`id`, `option_id`, `value`) VALUES(1, 1, '0');
+INSERT INTO `config` (`id`, `option_id`, `value`) VALUES(2, 11, 'public');
+INSERT INTO `config` (`id`, `option_id`, `value`) VALUES(3, 32, 'ORG');
+INSERT INTO `config` (`id`, `option_id`, `value`) VALUES(123, 19, '1');
+INSERT INTO `config` (`id`, `option_id`, `value`) VALUES(124, 35, '120');
+INSERT INTO `config` (`id`, `option_id`, `value`) VALUES(125, 9, '2');
+INSERT INTO `config` (`id`, `option_id`, `value`) VALUES(126, 41, '/opt/Eye/scripts/fetch_new_arp.pl');
+INSERT INTO `config` (`id`, `option_id`, `value`) VALUES(127, 26, '3');
+INSERT INTO `config` (`id`, `option_id`, `value`) VALUES(128, 27, '10');
+INSERT INTO `config` (`id`, `option_id`, `value`) VALUES(129, 48, '90');
+INSERT INTO `config` (`id`, `option_id`, `value`) VALUES(130, 49, '365');
+INSERT INTO `config` (`id`, `option_id`, `value`) VALUES(131, 47, '90');
+INSERT INTO `config` (`id`, `option_id`, `value`) VALUES(132, 53, '1');
+INSERT INTO `config` (`id`, `option_id`, `value`) VALUES(133, 55, '1');
+INSERT INTO `config` (`id`, `option_id`, `value`) VALUES(134, 56, '30');
+INSERT INTO `config` (`id`, `option_id`, `value`) VALUES(135, 34, '0');
+INSERT INTO `config` (`id`, `option_id`, `value`) VALUES(137, 65, '0');
+INSERT INTO `config` (`id`, `option_id`, `value`) VALUES(142, 54, '');
+INSERT INTO `config` (`id`, `option_id`, `value`) VALUES(143, 17, '1');
+INSERT INTO `config` (`id`, `option_id`, `value`) VALUES(144, 37, '/opt/Eye/scripts/sync_mikrotik.pl');
+INSERT INTO `config` (`id`, `option_id`, `value`) VALUES(145, 23, '1');
+INSERT INTO `config` (`id`, `option_id`, `value`) VALUES(148, 22, '1');
+
+-- --------------------------------------------------------
+
+--
+-- Структура таблицы `config_options`
+--
+
+CREATE TABLE `config_options` (
+  `id` int(11) NOT NULL,
+  `option_name` varchar(50) NOT NULL,
+  `description.russian` text DEFAULT NULL,
+  `description.english` text DEFAULT NULL,
+  `draft` tinyint(1) NOT NULL DEFAULT 0,
+  `uniq` tinyint(1) NOT NULL DEFAULT 1,
+  `type` varchar(100) NOT NULL,
+  `default_value` varchar(250) DEFAULT NULL,
+  `min_value` int(11) NOT NULL DEFAULT 0,
+  `max_value` int(11) NOT NULL DEFAULT 0
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+--
+-- Дамп данных таблицы `config_options`
+--
+
+INSERT INTO `config_options` (`id`, `option_name`, `description.russian`, `description.english`, `draft`, `uniq`, `type`, `default_value`, `min_value`, `max_value`) VALUES(1, 'KB', 'Еденица измерения трафика - Килобайт (0) или кибибайт (1,default)', 'Traffic measurement unit - Kilobyte (1000b) or kibibyte (1024b,default)', 0, 1, 'bool', '1024', 0, 1);
+INSERT INTO `config_options` (`id`, `option_name`, `description.russian`, `description.english`, `draft`, `uniq`, `type`, `default_value`, `min_value`, `max_value`) VALUES(3, 'dns server', 'ip-адрес DNS-сервера', 'DNS server ip address', 0, 1, 'text', '127.0.0.1', 0, 0);
+INSERT INTO `config_options` (`id`, `option_name`, `description.russian`, `description.english`, `draft`, `uniq`, `type`, `default_value`, `min_value`, `max_value`) VALUES(5, 'dhcp server', 'ip-адрес DHCP-сервера', 'ip address of the DHCP server', 0, 1, 'text', '127.0.0.1', 0, 0);
+INSERT INTO `config_options` (`id`, `option_name`, `description.russian`, `description.english`, `draft`, `uniq`, `type`, `default_value`, `min_value`, `max_value`) VALUES(9, 'default snmp version', 'Версия snmp по умолчанию. В настоящий момент поддерживаются 1 и 2. Поддержка версии 3 в разработке.', 'The default version of snmp. Currently, 1 and 2 are supported. Support for version 3 is in development.', 0, 1, 'int', '2', 1, 3);
+INSERT INTO `config_options` (`id`, `option_name`, `description.russian`, `description.english`, `draft`, `uniq`, `type`, `default_value`, `min_value`, `max_value`) VALUES(11, 'default snmp community', 'Read snmp community по умолчанию', 'Read snmp community by default', 0, 1, 'text', 'public', 0, 0);
+INSERT INTO `config_options` (`id`, `option_name`, `description.russian`, `description.english`, `draft`, `uniq`, `type`, `default_value`, `min_value`, `max_value`) VALUES(21, 'admin email', 'E-mail администратора', 'Administrator e-mail', 0, 1, 'text', 'root', 0, 0);
+INSERT INTO `config_options` (`id`, `option_name`, `description.russian`, `description.english`, `draft`, `uniq`, `type`, `default_value`, `min_value`, `max_value`) VALUES(22, 'add user from netflow', 'Создавать ли новые записи для неизвестных адресов из анализа трафика netflow. Не включать, если netflow снимает данные с маршрутизатора локальной сети', 'Whether to create new records for unknown addresses from netflow traffic analysis. Do not enable if netflow get data from the local network router', 0, 1, 'bool', '0', 0, 1);
+INSERT INTO `config_options` (`id`, `option_name`, `description.russian`, `description.english`, `draft`, `uniq`, `type`, `default_value`, `min_value`, `max_value`) VALUES(23, 'save traffic detail', 'Сохранять ли детализацию трафика из netflow по ip-адресам пользователей', 'Whether to keep the details of traffic from netflow by ip addresses of users', 0, 1, 'bool', '1', 0, 1);
+INSERT INTO `config_options` (`id`, `option_name`, `description.russian`, `description.english`, `draft`, `uniq`, `type`, `default_value`, `min_value`, `max_value`) VALUES(26, 'history detail traffic', 'Глубина хранения детализации в сутках. Установка значения больше 3-7 дней приведёт к разрастанию базы данных и увеличит время отображения детализации в интерфейсе администратора', 'Depth of detail storage in days. Setting a value greater than 3-7 days will cause the database to grow and increase the time about to display details in the admin interface', 0, 1, 'int', '3', 1, 7);
+INSERT INTO `config_options` (`id`, `option_name`, `description.russian`, `description.english`, `draft`, `uniq`, `type`, `default_value`, `min_value`, `max_value`) VALUES(27, 'history dhcp lease', 'Глубина хранения аренды dhcp-сервера', 'Storage depth of the dhcp server lease', 0, 1, 'int', '1', 0, 0);
+INSERT INTO `config_options` (`id`, `option_name`, `description.russian`, `description.english`, `draft`, `uniq`, `type`, `default_value`, `min_value`, `max_value`) VALUES(28, 'router_login', 'Логин для входа на сетевые устройства по умолчанию', 'Default login for network devices', 0, 1, 'text', 'admin', 0, 0);
+INSERT INTO `config_options` (`id`, `option_name`, `description.russian`, `description.english`, `draft`, `uniq`, `type`, `default_value`, `min_value`, `max_value`) VALUES(29, 'router_password', 'Пароль по умолчанию на сетевые устройства', 'Default password for network devices', 0, 1, 'text', 'admin', 0, 0);
+INSERT INTO `config_options` (`id`, `option_name`, `description.russian`, `description.english`, `draft`, `uniq`, `type`, `default_value`, `min_value`, `max_value`) VALUES(30, 'router_ssh_port', 'Порт ssh по умолчанию', 'SSH default port', 0, 1, 'int', '22', 22, 0);
+INSERT INTO `config_options` (`id`, `option_name`, `description.russian`, `description.english`, `draft`, `uniq`, `type`, `default_value`, `min_value`, `max_value`) VALUES(32, 'org name', 'Название организации', 'Organization name', 0, 1, 'text', 'ORG', 0, 0);
+INSERT INTO `config_options` (`id`, `option_name`, `description.russian`, `description.english`, `draft`, `uniq`, `type`, `default_value`, `min_value`, `max_value`) VALUES(33, 'office domain', 'Домен организации', 'Organization domain', 0, 1, 'text', 'local', 0, 0);
+INSERT INTO `config_options` (`id`, `option_name`, `description.russian`, `description.english`, `draft`, `uniq`, `type`, `default_value`, `min_value`, `max_value`) VALUES(34, 'debug', 'Включить отладку', 'Enable debugging', 0, 1, 'bool', '0', 0, 1);
+INSERT INTO `config_options` (`id`, `option_name`, `description.russian`, `description.english`, `draft`, `uniq`, `type`, `default_value`, `min_value`, `max_value`) VALUES(35, 'connections history, days', 'Время хранения истории мест подключения ip-адресов', 'Storage time of the history of connection locations of ip addresses', 0, 1, 'int', '90', 1, 365);
+INSERT INTO `config_options` (`id`, `option_name`, `description.russian`, `description.english`, `draft`, `uniq`, `type`, `default_value`, `min_value`, `max_value`) VALUES(37, 'refresh access lists', 'Расположение скрипта управления контролем доступа для роутеров Mikrotik', 'Location of the access control script for Mikrotik routers', 0, 1, 'text', '/opt/Eye/scripts/sync_mikrotik.pl', 0, 0);
+INSERT INTO `config_options` (`id`, `option_name`, `description.russian`, `description.english`, `draft`, `uniq`, `type`, `default_value`, `min_value`, `max_value`) VALUES(38, 'regenerate dhcp cconfig', 'Расположение скрипта управления конфигурацией dhcp-серверами', 'Location of the dhcp server configuration management script', 0, 1, 'text', '/opt/Eye/scripts/update-dnsmasq', 0, 0);
+INSERT INTO `config_options` (`id`, `option_name`, `description.russian`, `description.english`, `draft`, `uniq`, `type`, `default_value`, `min_value`, `max_value`) VALUES(39, 'regenerate dns cconfig', 'Расположение скрипта управления dns-сервером', 'Location of the dns server management script', 0, 1, 'text', '/opt/Eye/scripts/update-dns', 0, 0);
+INSERT INTO `config_options` (`id`, `option_name`, `description.russian`, `description.english`, `draft`, `uniq`, `type`, `default_value`, `min_value`, `max_value`) VALUES(40, 'regenerate nagios cconfig', 'Расположение скрипта конфигурирования Nagios', 'Location of the Nagios configuration script', 0, 1, 'text', '/etc/nagios/restart_nagios', 0, 0);
+INSERT INTO `config_options` (`id`, `option_name`, `description.russian`, `description.english`, `draft`, `uniq`, `type`, `default_value`, `min_value`, `max_value`) VALUES(41, 'discovery network', 'Расположение скрипта сканирования сети', 'Location of the network scan script', 0, 1, 'text', '/opt/Eye/scripts/fetch_new_arp.pl', 0, 0);
+INSERT INTO `config_options` (`id`, `option_name`, `description.russian`, `description.english`, `draft`, `uniq`, `type`, `default_value`, `min_value`, `max_value`) VALUES(44, 'Ignore hotspot dhcp log', 'Не писать лог событий dhcp-сервера хотспота. Имеет смысл вклчючать, поскольку время аренды в хот-споте как правило маленькое и в записях хот-спота становятся незаметны логи обычных пользователей', 'Do not write the event log of the hotspot dhcp server. It makes sense to include it, since the rental time in the hotspot is usually small and the logs of ordinary users become invisible in the hotspot records', 0, 1, 'bool', '1', 0, 1);
+INSERT INTO `config_options` (`id`, `option_name`, `description.russian`, `description.english`, `draft`, `uniq`, `type`, `default_value`, `min_value`, `max_value`) VALUES(45, 'ignore update dhcp event', 'Не писать события обновления ip-адреса dhcp-сервера. ', 'Do not write events for updating the IP address of the dhcp server. ', 0, 1, 'bool', '0', 0, 1);
+INSERT INTO `config_options` (`id`, `option_name`, `description.russian`, `description.english`, `draft`, `uniq`, `type`, `default_value`, `min_value`, `max_value`) VALUES(46, 'update hostname from dhcp', 'Обновлять имя хоста в DNS при получении адреса по DHCP', 'Update the hostname in DNS when receiving the address via DHCP', 0, 1, 'bool', '0', 0, 1);
+INSERT INTO `config_options` (`id`, `option_name`, `description.russian`, `description.english`, `draft`, `uniq`, `type`, `default_value`, `min_value`, `max_value`) VALUES(47, 'history worklog', 'Глубина хранения VERBOSE логов работы в интерфейсе администратора', 'Depth of VERBOSE work logs storage in the admin interface', 0, 1, 'int', '90', 0, 1095);
+INSERT INTO `config_options` (`id`, `option_name`, `description.russian`, `description.english`, `draft`, `uniq`, `type`, `default_value`, `min_value`, `max_value`) VALUES(48, 'history syslog', 'Глубина хранения логов работы syslog-сервера', 'Syslog server logs storage depth', 0, 1, 'int', '90', 0, 1095);
+INSERT INTO `config_options` (`id`, `option_name`, `description.russian`, `description.english`, `draft`, `uniq`, `type`, `default_value`, `min_value`, `max_value`) VALUES(49, 'history traffic stats', 'Глубина хранения статистики трафика юзеров', 'User traffic statistics storage depth', 0, 1, 'int', '365', 0, 0);
+INSERT INTO `config_options` (`id`, `option_name`, `description.russian`, `description.english`, `draft`, `uniq`, `type`, `default_value`, `min_value`, `max_value`) VALUES(50, 'urgent sync access', 'Немедленное изменение списков доступа на роутере после правки записи пользователя', 'Immediate change of access lists on the router after editing the user record ', 0, 1, 'bool', '0', 0, 1);
+INSERT INTO `config_options` (`id`, `option_name`, `description.russian`, `description.english`, `draft`, `uniq`, `type`, `default_value`, `min_value`, `max_value`) VALUES(51, 'Email_alert', 'Отправлять e-mail сообщения для уровней сообщений WARNING & ERROR', 'Send e-mail messages for message levels WARNING & ERROR', 0, 1, 'bool', '1', 0, 1);
+INSERT INTO `config_options` (`id`, `option_name`, `description.russian`, `description.english`, `draft`, `uniq`, `type`, `default_value`, `min_value`, `max_value`) VALUES(52, 'Sender email', 'E-mail адрес, с которого рассылается почта', 'E-mail address from which mail is sent', 0, 1, 'text', 'root', 0, 0);
+INSERT INTO `config_options` (`id`, `option_name`, `description.russian`, `description.english`, `draft`, `uniq`, `type`, `default_value`, `min_value`, `max_value`) VALUES(53, 'log level', 'Каждый уровень включает в себя предыдущий:\r\n0 - ERROR - писать только ошибки\r\n1 - WARNING - писать предупреждения\r\n2 - INFO - писать информационные сообщения\r\n3 - VERBOSE - писать подробную информацию о выполняемых операциях', 'Each level includes the previous one:\r\n0 - ERROR - write only errors\r\n1 - WARNING - write warnings\r\n2 - INFO - write informational\r\n3 - VERBOSE - write detailed information about the operations performed ', 0, 1, 'int', '2', 0, 3);
+INSERT INTO `config_options` (`id`, `option_name`, `description.russian`, `description.english`, `draft`, `uniq`, `type`, `default_value`, `min_value`, `max_value`) VALUES(54, 'enable_quotes', 'Включить обработку квот по трафику', 'Enable traffic quota processing', 0, 1, 'bool', '0', 0, 1);
+INSERT INTO `config_options` (`id`, `option_name`, `description.russian`, `description.english`, `draft`, `uniq`, `type`, `default_value`, `min_value`, `max_value`) VALUES(55, 'netflow_step', 'Интервал сброса данных из коллектора netflow, минуты', 'Data reset interval from netflow collector, minutes', 0, 1, 'int', '1', 1, 10);
+INSERT INTO `config_options` (`id`, `option_name`, `description.russian`, `description.english`, `draft`, `uniq`, `type`, `default_value`, `min_value`, `max_value`) VALUES(56, 'traffic_ipstat_history', 'Время хранения полной статистики по трафику для каждого ip-адреса в сутках. Таблица в 6 раз больше обычной часовой статистики. Врядли кому-то потребуется глубина хранения более месяца.', 'The storage time of complete traffic statistics for each ip address in days. The table is 6 times larger than the usual hourly statistic Hardly anyone will need a storage depth of more than a month.', 0, 1, 'int', '30', 0, 365);
+INSERT INTO `config_options` (`id`, `option_name`, `description.russian`, `description.english`, `draft`, `uniq`, `type`, `default_value`, `min_value`, `max_value`) VALUES(57, 'nagios_url', 'Адрес сайта nagios', 'nagios site address', 0, 1, 'text', 'http://127.0.0.1/nagios', 0, 0);
+INSERT INTO `config_options` (`id`, `option_name`, `description.russian`, `description.english`, `draft`, `uniq`, `type`, `default_value`, `min_value`, `max_value`) VALUES(58, 'cacti_url', 'Адрес сайта cacti', 'cacti site address', 0, 1, 'text', 'http://127.0.0.1/cacti', 0, 0);
+INSERT INTO `config_options` (`id`, `option_name`, `description.russian`, `description.english`, `draft`, `uniq`, `type`, `default_value`, `min_value`, `max_value`) VALUES(59, 'torrus_url', 'Адрес сайта Torrus', 'Torrus website address', 0, 1, 'text', 'http://127.0.0.1/torrus/CollectorName/', 0, 0);
+INSERT INTO `config_options` (`id`, `option_name`, `description.russian`, `description.english`, `draft`, `uniq`, `type`, `default_value`, `min_value`, `max_value`) VALUES(60, 'wiki_url', 'Адрес wiki', 'Wiki website address', 0, 1, 'text', 'http://127.0.0.1/wiki', 0, 0);
+INSERT INTO `config_options` (`id`, `option_name`, `description.russian`, `description.english`, `draft`, `uniq`, `type`, `default_value`, `min_value`, `max_value`) VALUES(61, 'wiki_path', 'Путь к каталогу данных вики', 'Path to wiki data directory', 0, 1, 'text', '/var/www/foswiki/data/', 0, 0);
+INSERT INTO `config_options` (`id`, `option_name`, `description.russian`, `description.english`, `draft`, `uniq`, `type`, `default_value`, `min_value`, `max_value`) VALUES(62, 'stat_url', 'Адрес этого сайта', 'Address of this site', 0, 1, 'text', 'http://127.0.0.1/stat', 0, 0);
+INSERT INTO `config_options` (`id`, `option_name`, `description.russian`, `description.english`, `draft`, `uniq`, `type`, `default_value`, `min_value`, `max_value`) VALUES(63, 'wiki_web', 'Web for Wiki. Default - Main. http://example.local/Main/WebHome', 'Web for Wiki. Default - Main. http://example.local/Main/WebHome', 0, 1, 'text', 'Main', 0, 0);
+INSERT INTO `config_options` (`id`, `option_name`, `description.russian`, `description.english`, `draft`, `uniq`, `type`, `default_value`, `min_value`, `max_value`) VALUES(64, 'auto_mac_rule', 'Создавать автоматическую привязку мак-адреса к юзеру. Т.е. все ip-адреса для найденного мака будут привязываться к одном и тому же юзеру.', 'Create an automatic binding of the mac address to the user. I.e. all ip addresses for the found mac will be bound to the same user.', 0, 1, 'bool', '0', 0, 1);
+INSERT INTO `config_options` (`id`, `option_name`, `description.russian`, `description.english`, `draft`, `uniq`, `type`, `default_value`, `min_value`, `max_value`) VALUES(65, 'mikrotik_command_interface', 'Используемый способ конфигурирования (0 - cli для ROS 6, 1 - rest api для ROS 7)', 'Configuration method used (0 - cli for ROS 6, 1 - rest api for ROS 7)', 1, 1, 'int', '0', 0, 1);
+INSERT INTO `config_options` (`id`, `option_name`, `description.russian`, `description.english`, `draft`, `uniq`, `type`, `default_value`, `min_value`, `max_value`) VALUES(66, 'mikrotik_rest_api_ssl', 'Использовать https для rest api', 'Use HTTPS for rest api', 1, 1, 'bool', '1', 0, 1);
+INSERT INTO `config_options` (`id`, `option_name`, `description.russian`, `description.english`, `draft`, `uniq`, `type`, `default_value`, `min_value`, `max_value`) VALUES(67, 'mikrotik_rest_api_port', 'Порт вэб-интерфейса для rest api', 'Web interface port for rest API', 1, 1, 'int', '443', 0, 0);
+INSERT INTO `config_options` (`id`, `option_name`, `description.russian`, `description.english`, `draft`, `uniq`, `type`, `default_value`, `min_value`, `max_value`) VALUES(68, 'config_mode', 'Режим конфигурирования. Скрипт опроса устройств не выполняется.', 'Configuration mode. The device polling script is not running.', 0, 1, 'bool', '0', 0, 1);
+INSERT INTO `config_options` (`id`, `option_name`, `description.russian`, `description.english`, `draft`, `uniq`, `type`, `default_value`, `min_value`, `max_value`) VALUES(69, 'clean_empty_user', 'Автоматически удалять записи пользователей, не содержащие ip-адресов или автоматических привязок', 'Automatically delete user records that do not contain IP addresses or automatic bindings', 0, 1, 'bool', '0', 0, 1);
+INSERT INTO `config_options` (`id`, `option_name`, `description.russian`, `description.english`, `draft`, `uniq`, `type`, `default_value`, `min_value`, `max_value`) VALUES(70, 'dns_server_type', 'Тип используемого dns-сервера: Windows, Bind. Если используется локальный dnsmasq - параметры dns-сервера указывать не надо.', 'The type of dns server used: Windows, Bind. If you are using a local dnsmasq, you do not need to specify the dns server parameters.', 0, 1, 'list;windows;bind', 'bind', 0, 0);
+INSERT INTO `config_options` (`id`, `option_name`, `description.russian`, `description.english`, `draft`, `uniq`, `type`, `default_value`, `min_value`, `max_value`) VALUES(71, 'enable_dns_updates', 'Включить обновления DNS имен при изменении dns-имени в ip-записи', 'Enable DNS name updates when dns name changes in an ip record', 0, 1, 'bool', '0', 0, 1);
+INSERT INTO `config_options` (`id`, `option_name`, `description.russian`, `description.english`, `draft`, `uniq`, `type`, `default_value`, `min_value`, `max_value`) VALUES(72, 'netflow_path', 'Каталог для хранения данных, полученных по netflow от маршрутизаторов', 'The directory for storing data received via netflow from routers', 0, 1, 'text', '/opt/Eye/netflow', 0, 0);
+INSERT INTO `config_options` (`id`, `option_name`, `description.russian`, `description.english`, `draft`, `uniq`, `type`, `default_value`, `min_value`, `max_value`) VALUES(73, 'check_computer_exists', 'Проверять существование компьютера в домене перед обновлением DNS по DHCP запросу', 'Verify the existence of a computer in the domain before updating DNS by DHCP request', 0, 1, 'bool', '1', 0, 0);
+
+-- --------------------------------------------------------
+
+--
+-- Структура таблицы `connections`
+--
+
+CREATE TABLE `connections` (
+  `id` bigint(20) UNSIGNED NOT NULL,
+  `device_id` bigint(20) UNSIGNED NOT NULL,
+  `port_id` bigint(20) UNSIGNED NOT NULL,
+  `auth_id` bigint(20) UNSIGNED NOT NULL
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+-- --------------------------------------------------------
+
+--
+-- Структура таблицы `Customers`
+--
+
+CREATE TABLE `Customers` (
+  `id` int(11) NOT NULL,
+  `Login` varchar(20) DEFAULT 'NULL',
+  `comment` varchar(100) DEFAULT 'NULL',
+  `password` varchar(255) DEFAULT 'NULL',
+  `api_key` varchar(255) DEFAULT NULL,
+  `rights` tinyint(1) NOT NULL DEFAULT 3
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+--
+-- Дамп данных таблицы `Customers`
+--
+
+INSERT INTO `Customers` (`id`, `Login`, `comment`, `password`, `api_key`, `rights`) VALUES(1, 'admin', '', '$2y$11$wohV8Tuqu0Yai9Shacei5OKfMxG5bnLxB5ACcZcJJ3pYEbIH0qLGG', 'Kot5miegiequaeseim3veequeiW2Iv3L', 1);
+
+-- --------------------------------------------------------
+
+--
+-- Структура таблицы `devices`
+--
+
+CREATE TABLE `devices` (
+  `id` int(11) NOT NULL,
+  `device_type` int(11) NOT NULL DEFAULT 1,
+  `device_model_id` int(11) DEFAULT 89,
+  `firmware` varchar(100) DEFAULT NULL,
+  `vendor_id` int(11) NOT NULL DEFAULT 1,
+  `device_name` varchar(50) DEFAULT NULL,
+  `building_id` int(11) NOT NULL DEFAULT 1,
+  `ip` varchar(15) DEFAULT NULL,
+  `login` varchar(50) DEFAULT NULL,
+  `password` varchar(255) DEFAULT NULL,
+  `protocol` int(11) NOT NULL DEFAULT 0,
+  `control_port` int(11) NOT NULL DEFAULT 23,
+  `port_count` int(11) NOT NULL DEFAULT 0,
+  `SN` varchar(80) DEFAULT NULL,
+  `comment` varchar(255) DEFAULT NULL,
+  `snmp_version` tinyint(4) NOT NULL DEFAULT 0,
+  `snmp3_auth_proto` varchar(10) NOT NULL DEFAULT 'sha512',
+  `snmp3_priv_proto` varchar(10) NOT NULL DEFAULT 'aes128',
+  `snmp3_user_rw` varchar(20) DEFAULT NULL,
+  `snmp3_user_rw_password` varchar(20) DEFAULT NULL,
+  `snmp3_user_ro` varchar(20) DEFAULT NULL,
+  `snmp3_user_ro_password` varchar(20) DEFAULT NULL,
+  `community` varchar(50) NOT NULL DEFAULT 'public',
+  `rw_community` varchar(50) NOT NULL DEFAULT 'private',
+  `fdb_snmp_index` tinyint(1) NOT NULL DEFAULT 0,
+  `discovery` tinyint(1) NOT NULL DEFAULT 1,
+  `netflow_save` tinyint(1) NOT NULL DEFAULT 0,
+  `user_acl` tinyint(1) NOT NULL DEFAULT 0,
+  `dhcp` tinyint(1) NOT NULL DEFAULT 0,
+  `nagios` tinyint(1) NOT NULL DEFAULT 0,
+  `active` tinyint(1) NOT NULL DEFAULT 1,
+  `nagios_status` varchar(10) NOT NULL DEFAULT 'UP',
+  `queue_enabled` tinyint(1) NOT NULL DEFAULT 0,
+  `connected_user_only` tinyint(1) NOT NULL DEFAULT 1,
+  `user_id` int(11) DEFAULT NULL,
+  `deleted` tinyint(1) NOT NULL DEFAULT 0,
+  `discovery_locked` tinyint(1) NOT NULL DEFAULT 0,
+  `locked_timestamp` timestamp NULL DEFAULT current_timestamp()
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+-- --------------------------------------------------------
+
+--
+-- Структура таблицы `device_l3_interfaces`
+--
+
+CREATE TABLE `device_l3_interfaces` (
+  `id` int(11) NOT NULL,
+  `device_id` int(11) DEFAULT NULL,
+  `snmpin` int(11) DEFAULT NULL,
+  `interface_type` int(11) NOT NULL DEFAULT 0,
+  `name` varchar(100) DEFAULT NULL
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+-- --------------------------------------------------------
+
+--
+-- Структура таблицы `device_models`
+--
+
+CREATE TABLE `device_models` (
+  `id` int(11) NOT NULL,
+  `model_name` varchar(200) DEFAULT NULL,
+  `vendor_id` int(11) DEFAULT 1,
+  `poe_in` tinyint(1) NOT NULL DEFAULT 0,
+  `poe_out` tinyint(1) NOT NULL DEFAULT 0,
+  `nagios_template` varchar(200) DEFAULT NULL
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+--
+-- Дамп данных таблицы `device_models`
+--
+
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(1, '2011LS', 9, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(2, '2011UAS-2HnD', 9, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(3, 'AT-8000S', 8, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(4, 'AT-8100S/48POE', 8, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(5, 'AT-9000/28', 8, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(6, 'AT-GS950/24', 8, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(7, 'CCR1009-7G-1C-1S+', 9, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(8, 'CCR1036-8G-2S+', 9, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(10, 'CRS317-1G-16S+', 9, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(11, 'CRS326-24S+2Q+', 9, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(12, 'CRS328-24P-4S+', 9, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(14, 'CRS328-4C-20S-4S+', 9, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(15, 'DGS-3120-48TC', 7, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(16, 'ES-2024', 4, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(17, 'ES-2024A', 4, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(18, 'ES-2108', 4, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(19, 'ES-2108-G', 4, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(20, 'ES-3124-4F', 4, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(21, 'GS110TP', 10, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(22, 'GS-4024', 4, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(23, 'HP 1910', 15, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(24, 'ISCOM2110A-MA', 5, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(25, 'ISCOM2110EA-MA', 5, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(26, 'ISCOM2126EA-MA', 5, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(27, 'ISCOM2128EA-MA', 5, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(28, 'Linux server', 1, 0, 0, '');
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(29, 'MES2124F', 2, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(30, 'MES2124MB', 2, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(31, 'MES5248', 2, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(32, 'RB2011UAS', 9, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(33, 'RB3011UiAS', 9, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(34, 'RB960PGS', 9, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(35, 'RBD52G-5HacD2HnD', 9, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(36, 'S2940-8G-v2', 6, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(37, 'S2980G-24T', 6, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(38, 'S3750G-24S-E', 6, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(39, 'S5300-52P-LI-AC', 3, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(40, 'S5320-52X-PWR-SI-AC', 3, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(41, 'S5321-28X-SI-AC', 3, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(42, 'S5321-52X-SI-AC', 3, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(43, 'S6320-54C-EI-48S-AC', 3, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(44, 'SNR-S2980G-24T', 6, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(45, 'V1910-16G', 15, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(46, 'V1910-24G-PoE', 15, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(47, 'Windows server', 1, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(48, 'WS-C2960G-24TC-L', 16, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(49, 'WS-C3560G-24TS-S', 16, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(50, 'x210-16GT', 8, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(51, 'x210-24GT', 8, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(52, 'x610-24Ts/X', 8, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(53, 'x610-48Ts', 8, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(54, 'XGS-4728', 4, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(55, 'ZyWall 310', 4, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(56, 'APC Smart-UPS_3000', 20, 0, 0, 'ups.cfg');
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(57, 'APC Smart-UPS_5000', 20, 0, 0, 'ups.cfg');
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(58, 'Schneider Smart-UPS_3000', 21, 0, 0, 'ups.cfg');
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(59, 'SMG-1016M', 2, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(60, 'EATON 9PX 1500i RT 2U', 64, 0, 0, 'ups.cfg');
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(61, 'EATON 9PX3000i_RT_2U', 64, 0, 0, 'ups.cfg');
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(62, 'EATON 9PX_6000i', 64, 0, 0, 'ups.cfg');
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(63, 'EATON PW9130_3000', 64, 0, 0, 'ups.cfg');
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(64, 'EATON PW9130_3000VA-R', 64, 0, 0, 'ups.cfg');
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(65, 'Epson WF-5620 Series', 59, 0, 0, 'epson.cfg');
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(66, 'Epson WF-8590 Series', 59, 0, 0, 'epson.cfg');
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(67, 'HP Officejet-7000', 15, 0, 0, 'hp.cfg');
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(68, 'OKI C610', 62, 0, 0, 'oki.cfg');
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(69, 'OKI MB472', 62, 0, 0, 'oki.cfg');
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(70, 'OKI MB491', 62, 0, 0, 'oki.cfg');
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(71, 'OKI MC562', 62, 0, 0, 'oki.cfg');
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(72, 'OKI MC573', 62, 0, 0, 'oki.cfg');
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(73, 'OKI MC861', 62, 0, 0, 'oki.cfg');
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(74, 'Panasonic KX-MB2000RU', 61, 0, 0, 'panasonic.cfg');
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(75, 'PT-MZ10KE', 61, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(76, 'PT-VX41', 61, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(77, 'Rave 522AA', 33, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(78, 'DZ570E', 61, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(79, 'DZ6700', 61, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(80, 'Rcq80', 61, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(81, 'RZ12K', 61, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(82, 'RZ660', 61, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(83, 'RZ770', 61, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(84, 'RZ970', 61, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(85, 'XVR-5216', 66, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(86, 'HWg-STE', 68, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(87, 'Computer', 1, 0, 0, '');
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(88, 'Mobile Phone', 1, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(89, 'Switch', 1, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(90, 'Projectiondesign F22', 34, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(91, 'DS-I252', 36, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(92, 'LTV-CNE-720-48', 37, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(93, 'U-100', 38, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(94, 'TAU-8', 2, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(95, 'SIP-T21P E2', 39, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(96, 'A510 IP', 40, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(97, 'W60B', 39, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(98, 'TAU-2M', 2, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(99, 'PAP2T', 41, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(100, 'VP-12', 2, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(101, 'SIP-T23P', 39, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(102, 'SPA-2102', 16, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(103, 'RB760iGS', 9, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(104, 'MES2324B', 2, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(105, 'MES2324FB', 2, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(106, 'MES2124P', 2, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(107, 'MES2428P', 2, 0, 0, NULL);
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(108, 'Symmetra LX 16000', 20, 0, 0, 'symmetra.cfg');
+INSERT INTO `device_models` (`id`, `model_name`, `vendor_id`, `poe_in`, `poe_out`, `nagios_template`) VALUES(109, 'SNR-UPS-ONT20', 6, 0, 0, 'ups.cfg');
+
+-- --------------------------------------------------------
+
+--
+-- Структура таблицы `device_ports`
+--
+
+CREATE TABLE `device_ports` (
+  `id` bigint(20) UNSIGNED NOT NULL,
+  `device_id` int(11) DEFAULT NULL,
+  `snmp_index` int(11) DEFAULT NULL,
+  `port` int(11) DEFAULT NULL,
+  `ifName` varchar(40) DEFAULT NULL,
+  `port_name` varchar(40) DEFAULT NULL,
+  `comment` varchar(50) DEFAULT NULL,
+  `target_port_id` int(11) NOT NULL DEFAULT 0,
+  `auth_id` bigint(20) UNSIGNED DEFAULT NULL,
+  `last_mac_count` int(11) DEFAULT 0,
+  `uplink` tinyint(1) NOT NULL DEFAULT 0,
+  `nagios` tinyint(1) NOT NULL DEFAULT 0,
+  `skip` tinyint(1) NOT NULL DEFAULT 0,
+  `vlan` int(11) NOT NULL DEFAULT 1,
+  `tagged_vlan` varchar(250) DEFAULT NULL,
+  `untagged_vlan` varchar(250) DEFAULT NULL,
+  `forbidden_vlan` varchar(250) DEFAULT NULL
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+-- --------------------------------------------------------
+
+--
+-- Структура таблицы `device_types`
+--
+
+CREATE TABLE `device_types` (
+  `id` int(11) NOT NULL,
+  `name.russian` varchar(50) DEFAULT NULL,
+  `name.english` varchar(50) DEFAULT NULL
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+--
+-- Дамп данных таблицы `device_types`
+--
+
+INSERT INTO `device_types` (`id`, `name.russian`, `name.english`) VALUES(0, 'Роутер', 'Router');
+INSERT INTO `device_types` (`id`, `name.russian`, `name.english`) VALUES(1, 'Свич', 'Switch');
+INSERT INTO `device_types` (`id`, `name.russian`, `name.english`) VALUES(2, 'Шлюз', 'Gateway');
+INSERT INTO `device_types` (`id`, `name.russian`, `name.english`) VALUES(3, 'Сервер', 'Server');
+INSERT INTO `device_types` (`id`, `name.russian`, `name.english`) VALUES(4, 'Точка доступа', 'Access Point');
+INSERT INTO `device_types` (`id`, `name.russian`, `name.english`) VALUES(5, 'Сетевое устройство', 'Network device');
+
+-- --------------------------------------------------------
+
+--
+-- Структура таблицы `dhcp_log`
+--
+
+CREATE TABLE `dhcp_log` (
+  `id` bigint(20) UNSIGNED NOT NULL,
+  `mac` varchar(17) NOT NULL,
+  `ip_int` bigint(20) UNSIGNED NOT NULL,
+  `ip` varchar(15) NOT NULL,
+  `action` varchar(10) NOT NULL,
+  `timestamp` timestamp NOT NULL DEFAULT current_timestamp(),
+  `auth_id` bigint(20) UNSIGNED NOT NULL,
+  `dhcp_hostname` varchar(250) DEFAULT NULL,
+  `circuit-id` varchar(255) DEFAULT NULL,
+  `remote-id` varchar(255) DEFAULT NULL,
+  `client-id` varchar(250) DEFAULT NULL
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+-- --------------------------------------------------------
+
+--
+-- Структура таблицы `dns_cache`
+--
+
+CREATE TABLE `dns_cache` (
+  `id` bigint(20) UNSIGNED NOT NULL,
+  `dns` varchar(250) DEFAULT NULL,
+  `ip` bigint(20) UNSIGNED DEFAULT NULL,
+  `timestamp` timestamp NOT NULL DEFAULT current_timestamp()
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+-- --------------------------------------------------------
+
+--
+-- Структура таблицы `dns_queue`
+--
+
+CREATE TABLE `dns_queue` (
+  `id` int(11) NOT NULL,
+  `auth_id` int(11) DEFAULT NULL,
+  `name_type` varchar(10) NOT NULL DEFAULT 'A',
+  `name` varchar(200) DEFAULT NULL,
+  `type` varchar(10) NOT NULL DEFAULT 'add',
+  `value` varchar(100) DEFAULT NULL
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+-- --------------------------------------------------------
+
+--
+-- Структура таблицы `Filter_list`
+--
+
+CREATE TABLE `Filter_list` (
+  `id` int(11) NOT NULL,
+  `name` varchar(50) DEFAULT NULL,
+  `comment` varchar(250) DEFAULT NULL,
+  `proto` varchar(10) DEFAULT NULL,
+  `dst` varchar(253) DEFAULT NULL,
+  `dstport` varchar(20) DEFAULT NULL,
+  `srcport` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
+  `type` int(10) UNSIGNED NOT NULL DEFAULT 0
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+--
+-- Дамп данных таблицы `Filter_list`
+--
+
+INSERT INTO `Filter_list` (`id`, `name`, `comment`, `proto`, `dst`, `dstport`, `srcport`, `type`) VALUES(1, 'pop3', NULL, 'tcp', '0/0', '110', NULL, 0);
+INSERT INTO `Filter_list` (`id`, `name`, `comment`, `proto`, `dst`, `dstport`, `srcport`, `type`) VALUES(3, 'http', NULL, 'tcp', '0/0', '80', NULL, 0);
+INSERT INTO `Filter_list` (`id`, `name`, `comment`, `proto`, `dst`, `dstport`, `srcport`, `type`) VALUES(4, 'https', NULL, 'tcp', '0/0', '443', NULL, 0);
+INSERT INTO `Filter_list` (`id`, `name`, `comment`, `proto`, `dst`, `dstport`, `srcport`, `type`) VALUES(5, 'icq', NULL, 'tcp', '0/0', '5190', NULL, 0);
+INSERT INTO `Filter_list` (`id`, `name`, `comment`, `proto`, `dst`, `dstport`, `srcport`, `type`) VALUES(6, 'jabber', NULL, 'tcp', '0/0', '5222', NULL, 0);
+INSERT INTO `Filter_list` (`id`, `name`, `comment`, `proto`, `dst`, `dstport`, `srcport`, `type`) VALUES(9, 'allow_all', 'любой трафик', 'all', '0/0', '0', '0', 0);
+INSERT INTO `Filter_list` (`id`, `name`, `comment`, `proto`, `dst`, `dstport`, `srcport`, `type`) VALUES(10, 'icmp', NULL, 'icmp', '0/0', '0', NULL, 0);
+INSERT INTO `Filter_list` (`id`, `name`, `comment`, `proto`, `dst`, `dstport`, `srcport`, `type`) VALUES(11, 'ftp', NULL, 'tcp', '0/0', '20-21', NULL, 0);
+INSERT INTO `Filter_list` (`id`, `name`, `comment`, `proto`, `dst`, `dstport`, `srcport`, `type`) VALUES(15, 'telnet', NULL, 'tcp', '0/0', '23', NULL, 0);
+INSERT INTO `Filter_list` (`id`, `name`, `comment`, `proto`, `dst`, `dstport`, `srcport`, `type`) VALUES(16, 'ssh', NULL, 'tcp', '0/0', '22', NULL, 0);
+INSERT INTO `Filter_list` (`id`, `name`, `comment`, `proto`, `dst`, `dstport`, `srcport`, `type`) VALUES(28, 'smtp', NULL, 'tcp', '0/0', '25', NULL, 0);
+INSERT INTO `Filter_list` (`id`, `name`, `comment`, `proto`, `dst`, `dstport`, `srcport`, `type`) VALUES(32, 'rdp', NULL, 'tcp', '0/0', '3389', NULL, 0);
+INSERT INTO `Filter_list` (`id`, `name`, `comment`, `proto`, `dst`, `dstport`, `srcport`, `type`) VALUES(40, 'ntp', NULL, 'udp', '0/0', '123', NULL, 0);
+INSERT INTO `Filter_list` (`id`, `name`, `comment`, `proto`, `dst`, `dstport`, `srcport`, `type`) VALUES(44, 'vnc', NULL, 'tcp', '0/0', '5800-5900', NULL, 0);
+INSERT INTO `Filter_list` (`id`, `name`, `comment`, `proto`, `dst`, `dstport`, `srcport`, `type`) VALUES(55, 'unprivileged tcp', NULL, 'tcp', '0/0', '1024-65500', NULL, 0);
+INSERT INTO `Filter_list` (`id`, `name`, `comment`, `proto`, `dst`, `dstport`, `srcport`, `type`) VALUES(76, 'ipsec', NULL, 'udp', '0/0', '500', NULL, 0);
+INSERT INTO `Filter_list` (`id`, `name`, `comment`, `proto`, `dst`, `dstport`, `srcport`, `type`) VALUES(77, 'isakmp', NULL, 'udp', '0/0', '4500', NULL, 0);
+INSERT INTO `Filter_list` (`id`, `name`, `comment`, `proto`, `dst`, `dstport`, `srcport`, `type`) VALUES(79, 'pop3s', NULL, 'tcp', '0/0', '995', NULL, 0);
+INSERT INTO `Filter_list` (`id`, `name`, `comment`, `proto`, `dst`, `dstport`, `srcport`, `type`) VALUES(80, 'smtps', NULL, 'tcp', '0/0', '465,587', NULL, 0);
+INSERT INTO `Filter_list` (`id`, `name`, `comment`, `proto`, `dst`, `dstport`, `srcport`, `type`) VALUES(81, 'imap', NULL, 'tcp', '0/0', '143', NULL, 0);
+INSERT INTO `Filter_list` (`id`, `name`, `comment`, `proto`, `dst`, `dstport`, `srcport`, `type`) VALUES(82, 'imaps', NULL, 'tcp', '0/0', '993', NULL, 0);
+INSERT INTO `Filter_list` (`id`, `name`, `comment`, `proto`, `dst`, `dstport`, `srcport`, `type`) VALUES(83, 'unprivileged udp', NULL, 'udp', '0/0', '1024-65000', NULL, 0);
+INSERT INTO `Filter_list` (`id`, `name`, `comment`, `proto`, `dst`, `dstport`, `srcport`, `type`) VALUES(84, 'pptp', NULL, 'tcp', '0/0', '1723', NULL, 0);
+INSERT INTO `Filter_list` (`id`, `name`, `comment`, `proto`, `dst`, `dstport`, `srcport`, `type`) VALUES(85, 'openvpn-udp', NULL, 'udp', '0/0', '1194', NULL, 0);
+INSERT INTO `Filter_list` (`id`, `name`, `comment`, `proto`, `dst`, `dstport`, `srcport`, `type`) VALUES(90, 'dns_udp', NULL, 'udp', '0/0', '53', NULL, 0);
+INSERT INTO `Filter_list` (`id`, `name`, `comment`, `proto`, `dst`, `dstport`, `srcport`, `type`) VALUES(91, 'dns_tcp', NULL, 'tcp', '0/0', '53', NULL, 0);
+INSERT INTO `Filter_list` (`id`, `name`, `comment`, `proto`, `dst`, `dstport`, `srcport`, `type`) VALUES(94, 'squid', NULL, 'tcp', '0/0', '3128', NULL, 0);
+INSERT INTO `Filter_list` (`id`, `name`, `comment`, `proto`, `dst`, `dstport`, `srcport`, `type`) VALUES(101, 'snmp', NULL, 'udp', '0/0', '161', NULL, 0);
+INSERT INTO `Filter_list` (`id`, `name`, `comment`, `proto`, `dst`, `dstport`, `srcport`, `type`) VALUES(105, 'http_udp', NULL, 'udp', '0/0', '80', NULL, 0);
+INSERT INTO `Filter_list` (`id`, `name`, `comment`, `proto`, `dst`, `dstport`, `srcport`, `type`) VALUES(106, 'https_udp', NULL, 'udp', '0/0', '443', NULL, 0);
+INSERT INTO `Filter_list` (`id`, `name`, `comment`, `proto`, `dst`, `dstport`, `srcport`, `type`) VALUES(107, 'l2tp-ipsec', NULL, 'udp', '0/0', '1701,4500,500', NULL, 0);
+INSERT INTO `Filter_list` (`id`, `name`, `comment`, `proto`, `dst`, `dstport`, `srcport`, `type`) VALUES(108, 'gre', NULL, 'gre', '0/0', NULL, NULL, 0);
+
+-- --------------------------------------------------------
+
+--
+-- Структура таблицы `gateway_subnets`
+--
+
+CREATE TABLE `gateway_subnets` (
+  `id` int(11) NOT NULL,
+  `device_id` int(11) DEFAULT NULL,
+  `subnet_id` int(11) DEFAULT NULL
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+-- --------------------------------------------------------
+
+--
+-- Структура таблицы `Group_filters`
+--
+
+CREATE TABLE `Group_filters` (
+  `id` int(11) NOT NULL,
+  `group_id` int(11) NOT NULL DEFAULT 0,
+  `filter_id` int(11) NOT NULL DEFAULT 0,
+  `order` int(11) NOT NULL DEFAULT 0,
+  `action` tinyint(1) NOT NULL DEFAULT 0
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+--
+-- Дамп данных таблицы `Group_filters`
+--
+
+INSERT INTO `Group_filters` (`id`, `group_id`, `filter_id`, `order`, `action`) VALUES(1, 2, 90, 1, 1);
+INSERT INTO `Group_filters` (`id`, `group_id`, `filter_id`, `order`, `action`) VALUES(2, 2, 91, 2, 1);
+INSERT INTO `Group_filters` (`id`, `group_id`, `filter_id`, `order`, `action`) VALUES(3, 2, 11, 3, 1);
+INSERT INTO `Group_filters` (`id`, `group_id`, `filter_id`, `order`, `action`) VALUES(5, 2, 3, 4, 1);
+INSERT INTO `Group_filters` (`id`, `group_id`, `filter_id`, `order`, `action`) VALUES(6, 2, 105, 5, 1);
+INSERT INTO `Group_filters` (`id`, `group_id`, `filter_id`, `order`, `action`) VALUES(7, 2, 4, 6, 1);
+INSERT INTO `Group_filters` (`id`, `group_id`, `filter_id`, `order`, `action`) VALUES(8, 2, 106, 7, 1);
+INSERT INTO `Group_filters` (`id`, `group_id`, `filter_id`, `order`, `action`) VALUES(9, 2, 10, 8, 1);
+INSERT INTO `Group_filters` (`id`, `group_id`, `filter_id`, `order`, `action`) VALUES(10, 2, 81, 9, 1);
+INSERT INTO `Group_filters` (`id`, `group_id`, `filter_id`, `order`, `action`) VALUES(11, 2, 82, 10, 1);
+INSERT INTO `Group_filters` (`id`, `group_id`, `filter_id`, `order`, `action`) VALUES(15, 2, 40, 11, 1);
+INSERT INTO `Group_filters` (`id`, `group_id`, `filter_id`, `order`, `action`) VALUES(16, 2, 1, 12, 1);
+INSERT INTO `Group_filters` (`id`, `group_id`, `filter_id`, `order`, `action`) VALUES(17, 2, 79, 13, 1);
+INSERT INTO `Group_filters` (`id`, `group_id`, `filter_id`, `order`, `action`) VALUES(18, 2, 80, 14, 1);
+
+-- --------------------------------------------------------
+
+--
+-- Структура таблицы `Group_list`
+--
+
+CREATE TABLE `Group_list` (
+  `id` int(11) NOT NULL,
+  `group_name` varchar(50) DEFAULT NULL,
+  `comment` varchar(250) DEFAULT NULL
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+--
+-- Дамп данных таблицы `Group_list`
+--
+
+INSERT INTO `Group_list` (`id`, `group_name`, `comment`) VALUES(0, 'default', NULL);
+INSERT INTO `Group_list` (`id`, `group_name`, `comment`) VALUES(1, 'Allow all', 'Разрешено всё');
+INSERT INTO `Group_list` (`id`, `group_name`, `comment`) VALUES(2, 'Users', 'Для пользователей');
+
+-- --------------------------------------------------------
+
+--
+-- Структура таблицы `mac_history`
+--
+
+CREATE TABLE `mac_history` (
+  `id` bigint(20) UNSIGNED NOT NULL,
+  `mac` varchar(12) DEFAULT NULL,
+  `timestamp` timestamp NOT NULL DEFAULT current_timestamp(),
+  `device_id` bigint(20) UNSIGNED DEFAULT NULL,
+  `port_id` bigint(20) UNSIGNED DEFAULT NULL,
+  `ip` varchar(16) NOT NULL DEFAULT '',
+  `auth_id` bigint(20) UNSIGNED DEFAULT NULL,
+  `dhcp_hostname` varchar(250) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+-- --------------------------------------------------------
+
+--
+-- Структура таблицы `mac_vendors`
+--
+
+CREATE TABLE `mac_vendors` (
+  `id` int(11) NOT NULL,
+  `oui` varchar(20) DEFAULT NULL,
+  `companyName` varchar(255) DEFAULT NULL,
+  `companyAddress` varchar(255) DEFAULT NULL
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+-- --------------------------------------------------------
+
+--
+-- Структура таблицы `OU`
+--
+
+CREATE TABLE `OU` (
+  `id` int(11) NOT NULL,
+  `ou_name` varchar(40) DEFAULT NULL,
+  `comment` varchar(250) DEFAULT NULL,
+  `default_users` tinyint(1) NOT NULL DEFAULT 0,
+  `default_hotspot` tinyint(1) NOT NULL DEFAULT 0,
+  `nagios_dir` varchar(255) DEFAULT NULL,
+  `nagios_host_use` varchar(50) DEFAULT NULL,
+  `nagios_ping` tinyint(1) NOT NULL DEFAULT 1,
+  `nagios_default_service` varchar(100) DEFAULT NULL,
+  `enabled` int(11) NOT NULL DEFAULT 0,
+  `filter_group_id` int(11) NOT NULL DEFAULT 0,
+  `queue_id` int(11) NOT NULL DEFAULT 0
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+--
+-- Дамп данных таблицы `OU`
+--
+
+INSERT INTO `OU` (`id`, `ou_name`, `comment`, `default_users`, `default_hotspot`, `nagios_dir`, `nagios_host_use`, `nagios_ping`, `nagios_default_service`, `enabled`, `filter_group_id`, `queue_id`) VALUES(0, '!Всё', NULL, 0, 0, '/etc/nagios/any', 'generic-host', 1, NULL, 0, 0, 0);
+INSERT INTO `OU` (`id`, `ou_name`, `comment`, `default_users`, `default_hotspot`, `nagios_dir`, `nagios_host_use`, `nagios_ping`, `nagios_default_service`, `enabled`, `filter_group_id`, `queue_id`) VALUES(1, 'Сервера', NULL, 0, 0, NULL, NULL, 1, NULL, 1, 1, 0);
+INSERT INTO `OU` (`id`, `ou_name`, `comment`, `default_users`, `default_hotspot`, `nagios_dir`, `nagios_host_use`, `nagios_ping`, `nagios_default_service`, `enabled`, `filter_group_id`, `queue_id`) VALUES(2, 'Администраторы', NULL, 0, 0, NULL, NULL, 1, NULL, 0, 0, 0);
+INSERT INTO `OU` (`id`, `ou_name`, `comment`, `default_users`, `default_hotspot`, `nagios_dir`, `nagios_host_use`, `nagios_ping`, `nagios_default_service`, `enabled`, `filter_group_id`, `queue_id`) VALUES(3, 'Пользователи', NULL, 0, 0, NULL, NULL, 1, NULL, 1, 1, 4);
+INSERT INTO `OU` (`id`, `ou_name`, `comment`, `default_users`, `default_hotspot`, `nagios_dir`, `nagios_host_use`, `nagios_ping`, `nagios_default_service`, `enabled`, `filter_group_id`, `queue_id`) VALUES(4, 'VOIP', NULL, 0, 0, 'voip', 'voip', 1, NULL, 1, 4, 5);
+INSERT INTO `OU` (`id`, `ou_name`, `comment`, `default_users`, `default_hotspot`, `nagios_dir`, `nagios_host_use`, `nagios_ping`, `nagios_default_service`, `enabled`, `filter_group_id`, `queue_id`) VALUES(5, 'IPCAM', NULL, 0, 0, 'videocam', 'ip-cam', 1, NULL, 0, 0, 0);
+INSERT INTO `OU` (`id`, `ou_name`, `comment`, `default_users`, `default_hotspot`, `nagios_dir`, `nagios_host_use`, `nagios_ping`, `nagios_default_service`, `enabled`, `filter_group_id`, `queue_id`) VALUES(6, 'Принтеры', NULL, 0, 0, 'printers', 'printers', 1, 'printer-service', 0, 0, 0);
+INSERT INTO `OU` (`id`, `ou_name`, `comment`, `default_users`, `default_hotspot`, `nagios_dir`, `nagios_host_use`, `nagios_ping`, `nagios_default_service`, `enabled`, `filter_group_id`, `queue_id`) VALUES(7, 'Свичи', NULL, 0, 0, 'switches', 'switches', 1, NULL, 0, 0, 0);
+INSERT INTO `OU` (`id`, `ou_name`, `comment`, `default_users`, `default_hotspot`, `nagios_dir`, `nagios_host_use`, `nagios_ping`, `nagios_default_service`, `enabled`, `filter_group_id`, `queue_id`) VALUES(8, 'UPS', NULL, 0, 0, 'ups', 'ups', 1, NULL, 0, 0, 0);
+INSERT INTO `OU` (`id`, `ou_name`, `comment`, `default_users`, `default_hotspot`, `nagios_dir`, `nagios_host_use`, `nagios_ping`, `nagios_default_service`, `enabled`, `filter_group_id`, `queue_id`) VALUES(9, 'Охрана', NULL, 0, 0, 'security', 'security', 1, NULL, 0, 0, 0);
+INSERT INTO `OU` (`id`, `ou_name`, `comment`, `default_users`, `default_hotspot`, `nagios_dir`, `nagios_host_use`, `nagios_ping`, `nagios_default_service`, `enabled`, `filter_group_id`, `queue_id`) VALUES(10, 'Роутеры', NULL, 0, 0, 'routers', 'routers', 1, NULL, 0, 0, 0);
+INSERT INTO `OU` (`id`, `ou_name`, `comment`, `default_users`, `default_hotspot`, `nagios_dir`, `nagios_host_use`, `nagios_ping`, `nagios_default_service`, `enabled`, `filter_group_id`, `queue_id`) VALUES(11, 'WiFi AP', NULL, 0, 0, 'ap', 'ap', 1, NULL, 0, 0, 0);
+INSERT INTO `OU` (`id`, `ou_name`, `comment`, `default_users`, `default_hotspot`, `nagios_dir`, `nagios_host_use`, `nagios_ping`, `nagios_default_service`, `enabled`, `filter_group_id`, `queue_id`) VALUES(12, 'WiFi', NULL, 0, 0, NULL, NULL, 1, NULL, 1, 1, 4);
+INSERT INTO `OU` (`id`, `ou_name`, `comment`, `default_users`, `default_hotspot`, `nagios_dir`, `nagios_host_use`, `nagios_ping`, `nagios_default_service`, `enabled`, `filter_group_id`, `queue_id`) VALUES(13, 'VPN', NULL, 0, 0, NULL, NULL, 1, NULL, 0, 0, 0);
+INSERT INTO `OU` (`id`, `ou_name`, `comment`, `default_users`, `default_hotspot`, `nagios_dir`, `nagios_host_use`, `nagios_ping`, `nagios_default_service`, `enabled`, `filter_group_id`, `queue_id`) VALUES(14, 'DHCP', NULL, 1, 0, NULL, NULL, 1, NULL, 0, 0, 0);
+INSERT INTO `OU` (`id`, `ou_name`, `comment`, `default_users`, `default_hotspot`, `nagios_dir`, `nagios_host_use`, `nagios_ping`, `nagios_default_service`, `enabled`, `filter_group_id`, `queue_id`) VALUES(15, 'Гости', NULL, 0, 0, NULL, NULL, 1, NULL, 1, 1, 4);
+
+-- --------------------------------------------------------
+
+--
+-- Структура таблицы `Queue_list`
+--
+
+CREATE TABLE `Queue_list` (
+  `id` int(11) NOT NULL,
+  `queue_name` varchar(20) NOT NULL,
+  `Download` int(11) NOT NULL DEFAULT 0,
+  `Upload` int(11) NOT NULL DEFAULT 0
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+--
+-- Дамп данных таблицы `Queue_list`
+--
+
+INSERT INTO `Queue_list` (`id`, `queue_name`, `Download`, `Upload`) VALUES(0, 'unlimited', 0, 0);
+INSERT INTO `Queue_list` (`id`, `queue_name`, `Download`, `Upload`) VALUES(1, '2M/2M', 2048, 2048);
+INSERT INTO `Queue_list` (`id`, `queue_name`, `Download`, `Upload`) VALUES(2, '10M/10M', 10240, 10240);
+INSERT INTO `Queue_list` (`id`, `queue_name`, `Download`, `Upload`) VALUES(3, '100M/100M', 102400, 102400);
+INSERT INTO `Queue_list` (`id`, `queue_name`, `Download`, `Upload`) VALUES(4, '50M/50M', 50000, 50000);
+INSERT INTO `Queue_list` (`id`, `queue_name`, `Download`, `Upload`) VALUES(5, '20M/20M', 20480, 20480);
+INSERT INTO `Queue_list` (`id`, `queue_name`, `Download`, `Upload`) VALUES(6, '200M/200M', 212400, 212400);
+INSERT INTO `Queue_list` (`id`, `queue_name`, `Download`, `Upload`) VALUES(7, '1G/1G', 1024000, 1024000);
+
+-- --------------------------------------------------------
+
+--
+-- Структура таблицы `remote_syslog`
+--
+
+CREATE TABLE `remote_syslog` (
+  `id` bigint(20) UNSIGNED NOT NULL,
+  `date` timestamp NOT NULL DEFAULT current_timestamp(),
+  `device_id` bigint(20) UNSIGNED NOT NULL,
+  `ip` varchar(15) NOT NULL,
+  `message` text NOT NULL
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+-- --------------------------------------------------------
+
+--
+-- Структура таблицы `sessions`
+--
+
+CREATE TABLE `sessions` (
+  `id` int(11) NOT NULL,
+  `customer_id` int(11) DEFAULT NULL,
+  `session_id` varchar(256) DEFAULT NULL,
+  `session_key` varchar(40) DEFAULT NULL,
+  `start_time` int(11) DEFAULT NULL
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci;
+
+-- --------------------------------------------------------
+
+--
+-- Структура таблицы `subnets`
+--
+
+CREATE TABLE `subnets` (
+  `id` int(11) NOT NULL,
+  `subnet` varchar(18) DEFAULT NULL,
+  `vlan_tag` int(11) NOT NULL DEFAULT 1,
+  `ip_int_start` bigint(20) NOT NULL,
+  `ip_int_stop` bigint(20) NOT NULL,
+  `dhcp_start` bigint(20) NOT NULL DEFAULT 0,
+  `dhcp_stop` bigint(20) NOT NULL DEFAULT 0,
+  `dhcp_lease_time` int(11) NOT NULL DEFAULT 480,
+  `gateway` bigint(20) NOT NULL DEFAULT 0,
+  `office` tinyint(1) NOT NULL DEFAULT 1,
+  `hotspot` tinyint(1) NOT NULL DEFAULT 0,
+  `vpn` tinyint(1) NOT NULL DEFAULT 0,
+  `free` tinyint(1) NOT NULL DEFAULT 0,
+  `dhcp` tinyint(1) NOT NULL DEFAULT 1,
+  `static` tinyint(1) NOT NULL DEFAULT 0,
+  `dhcp_update_hostname` tinyint(1) NOT NULL DEFAULT 0,
+  `discovery` tinyint(1) NOT NULL DEFAULT 1,
+  `comment` varchar(250) DEFAULT NULL
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+-- --------------------------------------------------------
+
+--
+-- Структура таблицы `Traffic_detail`
+--
+
+CREATE TABLE `Traffic_detail` (
+  `id` bigint(20) UNSIGNED NOT NULL,
+  `auth_id` bigint(20) UNSIGNED DEFAULT NULL,
+  `router_id` int(11) NOT NULL DEFAULT 0,
+  `timestamp` timestamp NULL DEFAULT NULL,
+  `proto` tinyint(3) UNSIGNED DEFAULT NULL,
+  `src_ip` int(10) UNSIGNED NOT NULL,
+  `dst_ip` int(10) UNSIGNED NOT NULL,
+  `src_port` smallint(5) UNSIGNED NOT NULL,
+  `dst_port` smallint(5) UNSIGNED NOT NULL,
+  `bytes` bigint(20) NOT NULL,
+  `pkt` int(10) UNSIGNED NOT NULL DEFAULT 0
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+-- --------------------------------------------------------
+
+--
+-- Структура таблицы `Unknown_mac`
+--
+
+CREATE TABLE `Unknown_mac` (
+  `id` bigint(20) UNSIGNED NOT NULL,
+  `mac` varchar(12) DEFAULT NULL,
+  `port_id` bigint(20) UNSIGNED DEFAULT NULL,
+  `device_id` int(11) DEFAULT NULL,
+  `timestamp` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp()
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+-- --------------------------------------------------------
+
+--
+-- Структура таблицы `User_auth`
+--
+
+CREATE TABLE `User_auth` (
+  `id` int(11) NOT NULL,
+  `user_id` bigint(20) UNSIGNED NOT NULL DEFAULT 0,
+  `ou_id` int(11) DEFAULT NULL,
+  `ip` varchar(18) NOT NULL DEFAULT '',
+  `ip_int` bigint(10) UNSIGNED NOT NULL DEFAULT 0,
+  `save_traf` tinyint(1) NOT NULL DEFAULT 0,
+  `enabled` tinyint(1) NOT NULL DEFAULT 0,
+  `dhcp` tinyint(1) NOT NULL DEFAULT 1,
+  `filter_group_id` tinyint(1) NOT NULL DEFAULT 0,
+  `deleted` tinyint(4) NOT NULL DEFAULT 0,
+  `comments` varchar(250) DEFAULT NULL,
+  `dns_name` varchar(100) DEFAULT NULL,
+  `WikiName` varchar(250) DEFAULT NULL,
+  `dhcp_acl` text DEFAULT NULL,
+  `queue_id` int(11) NOT NULL DEFAULT 0,
+  `mac` varchar(20) NOT NULL DEFAULT '',
+  `dhcp_action` varchar(10) NOT NULL DEFAULT '',
+  `dhcp_time` datetime NOT NULL DEFAULT current_timestamp(),
+  `dhcp_hostname` varchar(60) DEFAULT NULL,
+  `last_found` datetime NOT NULL DEFAULT current_timestamp(),
+  `blocked` tinyint(1) NOT NULL DEFAULT 0,
+  `day_quota` int(11) NOT NULL DEFAULT 0,
+  `month_quota` int(11) NOT NULL DEFAULT 0,
+  `device_model_id` int(11) DEFAULT 87,
+  `firmware` varchar(100) DEFAULT NULL,
+  `timestamp` timestamp NOT NULL DEFAULT current_timestamp(),
+  `client-id` varchar(250) DEFAULT NULL,
+  `nagios` tinyint(1) NOT NULL DEFAULT 0,
+  `nagios_status` varchar(10) NOT NULL DEFAULT '',
+  `nagios_handler` varchar(50) NOT NULL DEFAULT '',
+  `link_check` tinyint(1) NOT NULL DEFAULT 0,
+  `changed` tinyint(1) NOT NULL DEFAULT 0,
+  `dhcp_changed` int(11) NOT NULL DEFAULT 0,
+  `changed_time` datetime NOT NULL DEFAULT current_timestamp()
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+-- --------------------------------------------------------
+
+--
+-- Структура таблицы `User_auth_alias`
+--
+
+CREATE TABLE `User_auth_alias` (
+  `id` int(11) NOT NULL,
+  `auth_id` int(11) NOT NULL,
+  `alias` varchar(100) DEFAULT NULL,
+  `description` varchar(100) DEFAULT NULL,
+  `timestamp` timestamp NOT NULL DEFAULT current_timestamp()
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+-- --------------------------------------------------------
+
+--
+-- Структура таблицы `User_list`
+--
+
+CREATE TABLE `User_list` (
+  `id` bigint(20) UNSIGNED NOT NULL,
+  `timestamp` timestamp NOT NULL DEFAULT current_timestamp(),
+  `login` varchar(255) DEFAULT NULL,
+  `fio` varchar(255) DEFAULT NULL,
+  `enabled` tinyint(1) NOT NULL DEFAULT 1,
+  `blocked` tinyint(1) NOT NULL DEFAULT 0,
+  `deleted` tinyint(1) NOT NULL DEFAULT 0,
+  `ou_id` int(11) NOT NULL DEFAULT 0,
+  `device_id` int(11) DEFAULT NULL,
+  `filter_group_id` int(11) NOT NULL DEFAULT 0,
+  `queue_id` int(11) NOT NULL DEFAULT 0,
+  `day_quota` int(11) NOT NULL DEFAULT 0,
+  `month_quota` int(11) NOT NULL DEFAULT 0
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+-- --------------------------------------------------------
+
+--
+-- Структура таблицы `User_stats`
+--
+
+CREATE TABLE `User_stats` (
+  `id` bigint(20) UNSIGNED NOT NULL,
+  `router_id` bigint(20) UNSIGNED DEFAULT 0,
+  `auth_id` bigint(20) UNSIGNED NOT NULL DEFAULT 0,
+  `timestamp` datetime NOT NULL DEFAULT current_timestamp(),
+  `byte_in` bigint(20) NOT NULL DEFAULT 0,
+  `byte_out` bigint(20) NOT NULL DEFAULT 0
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+-- --------------------------------------------------------
+
+--
+-- Структура таблицы `User_stats_full`
+--
+
+CREATE TABLE `User_stats_full` (
+  `id` bigint(20) UNSIGNED NOT NULL,
+  `router_id` bigint(20) UNSIGNED DEFAULT 0,
+  `auth_id` bigint(20) UNSIGNED NOT NULL DEFAULT 0,
+  `timestamp` datetime NOT NULL DEFAULT current_timestamp(),
+  `byte_in` bigint(20) NOT NULL DEFAULT 0,
+  `byte_out` bigint(20) NOT NULL DEFAULT 0,
+  `pkt_in` int(11) DEFAULT NULL,
+  `pkt_out` int(11) DEFAULT NULL,
+  `step` int(11) NOT NULL DEFAULT 600
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+-- --------------------------------------------------------
+
+--
+-- Структура таблицы `variables`
+--
+
+CREATE TABLE `variables` (
+  `id` int(11) NOT NULL,
+  `name` varchar(30) NOT NULL,
+  `value` varchar(255) DEFAULT NULL,
+  `clear_time` timestamp NOT NULL DEFAULT current_timestamp(),
+  `created` timestamp NOT NULL DEFAULT current_timestamp()
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+-- --------------------------------------------------------
+
+--
+-- Структура таблицы `vendors`
+--
+
+CREATE TABLE `vendors` (
+  `id` int(11) NOT NULL,
+  `name` varchar(40) NOT NULL
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+--
+-- Дамп данных таблицы `vendors`
+--
+
+INSERT INTO `vendors` (`id`, `name`) VALUES(1, 'Unknown');
+INSERT INTO `vendors` (`id`, `name`) VALUES(2, 'Eltex');
+INSERT INTO `vendors` (`id`, `name`) VALUES(3, 'Huawei');
+INSERT INTO `vendors` (`id`, `name`) VALUES(4, 'Zyxel');
+INSERT INTO `vendors` (`id`, `name`) VALUES(5, 'Raisecom');
+INSERT INTO `vendors` (`id`, `name`) VALUES(6, 'SNR');
+INSERT INTO `vendors` (`id`, `name`) VALUES(7, 'Dlink');
+INSERT INTO `vendors` (`id`, `name`) VALUES(8, 'Allied Telesis');
+INSERT INTO `vendors` (`id`, `name`) VALUES(9, 'Mikrotik');
+INSERT INTO `vendors` (`id`, `name`) VALUES(10, 'NetGear');
+INSERT INTO `vendors` (`id`, `name`) VALUES(11, 'Ubiquiti');
+INSERT INTO `vendors` (`id`, `name`) VALUES(15, 'HP');
+INSERT INTO `vendors` (`id`, `name`) VALUES(16, 'Cisco');
+INSERT INTO `vendors` (`id`, `name`) VALUES(17, 'Maipu');
+INSERT INTO `vendors` (`id`, `name`) VALUES(18, 'Asus');
+INSERT INTO `vendors` (`id`, `name`) VALUES(19, 'Linux');
+INSERT INTO `vendors` (`id`, `name`) VALUES(20, 'APC');
+INSERT INTO `vendors` (`id`, `name`) VALUES(21, 'Schneider');
+INSERT INTO `vendors` (`id`, `name`) VALUES(33, 'QSC');
+INSERT INTO `vendors` (`id`, `name`) VALUES(34, 'Projectiondesign');
+INSERT INTO `vendors` (`id`, `name`) VALUES(35, 'Lenovo');
+INSERT INTO `vendors` (`id`, `name`) VALUES(36, 'HiWatch');
+INSERT INTO `vendors` (`id`, `name`) VALUES(37, 'LTV');
+INSERT INTO `vendors` (`id`, `name`) VALUES(38, 'Yeastar');
+INSERT INTO `vendors` (`id`, `name`) VALUES(39, 'Yealink');
+INSERT INTO `vendors` (`id`, `name`) VALUES(40, 'Gigaset');
+INSERT INTO `vendors` (`id`, `name`) VALUES(41, 'Linksys');
+INSERT INTO `vendors` (`id`, `name`) VALUES(42, 'Samsung');
+INSERT INTO `vendors` (`id`, `name`) VALUES(43, 'Supermicro');
+INSERT INTO `vendors` (`id`, `name`) VALUES(44, 'RDP');
+INSERT INTO `vendors` (`id`, `name`) VALUES(45, 'SANYO');
+INSERT INTO `vendors` (`id`, `name`) VALUES(46, 'Extreme');
+INSERT INTO `vendors` (`id`, `name`) VALUES(47, 'Intel');
+INSERT INTO `vendors` (`id`, `name`) VALUES(48, 'Micron');
+INSERT INTO `vendors` (`id`, `name`) VALUES(49, 'Gigabyte');
+INSERT INTO `vendors` (`id`, `name`) VALUES(50, 'Acer');
+INSERT INTO `vendors` (`id`, `name`) VALUES(51, 'Seagate');
+INSERT INTO `vendors` (`id`, `name`) VALUES(52, 'SanDisk');
+INSERT INTO `vendors` (`id`, `name`) VALUES(53, 'Toshiba');
+INSERT INTO `vendors` (`id`, `name`) VALUES(54, 'Kingston');
+INSERT INTO `vendors` (`id`, `name`) VALUES(55, 'AddPac');
+INSERT INTO `vendors` (`id`, `name`) VALUES(56, 'Devline');
+INSERT INTO `vendors` (`id`, `name`) VALUES(57, 'Canon');
+INSERT INTO `vendors` (`id`, `name`) VALUES(58, 'Brother');
+INSERT INTO `vendors` (`id`, `name`) VALUES(59, 'Epson');
+INSERT INTO `vendors` (`id`, `name`) VALUES(60, 'IP-COM');
+INSERT INTO `vendors` (`id`, `name`) VALUES(61, 'Panasonic');
+INSERT INTO `vendors` (`id`, `name`) VALUES(62, 'OKI');
+INSERT INTO `vendors` (`id`, `name`) VALUES(63, 'Apple');
+INSERT INTO `vendors` (`id`, `name`) VALUES(64, 'Eaton');
+INSERT INTO `vendors` (`id`, `name`) VALUES(65, 'Barco');
+INSERT INTO `vendors` (`id`, `name`) VALUES(66, 'Trassir');
+INSERT INTO `vendors` (`id`, `name`) VALUES(67, 'Testo');
+INSERT INTO `vendors` (`id`, `name`) VALUES(68, 'Hw-group');
+
+-- --------------------------------------------------------
+
+--
+-- Структура таблицы `version`
+--
+
+CREATE TABLE `version` (
+  `id` int(11) NOT NULL DEFAULT 1,
+  `version` varchar(10) NOT NULL DEFAULT '2.4.14'
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+--
+-- Дамп данных таблицы `version`
+--
+
+INSERT INTO `version` (`id`, `version`) VALUES(1, '2.7.0');
+
+-- --------------------------------------------------------
+
+--
+-- Структура таблицы `Wan_stats`
+--
+
+CREATE TABLE `Wan_stats` (
+  `id` int(11) NOT NULL,
+  `time` datetime NOT NULL DEFAULT current_timestamp(),
+  `router_id` int(11) DEFAULT NULL,
+  `interface_id` int(11) DEFAULT NULL,
+  `in` bigint(20) UNSIGNED NOT NULL DEFAULT 0,
+  `out` bigint(20) UNSIGNED NOT NULL DEFAULT 0,
+  `forward_in` bigint(20) UNSIGNED NOT NULL DEFAULT 0,
+  `forward_out` bigint(20) UNSIGNED NOT NULL DEFAULT 0
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+-- --------------------------------------------------------
+
+--
+-- Структура таблицы `worklog`
+--
+
+CREATE TABLE `worklog` (
+  `id` bigint(20) UNSIGNED NOT NULL,
+  `timestamp` timestamp NOT NULL DEFAULT current_timestamp(),
+  `auth_id` bigint(20) UNSIGNED NOT NULL DEFAULT 0,
+  `customer` varchar(50) NOT NULL DEFAULT 'system',
+  `message` text NOT NULL,
+  `level` int(11) NOT NULL DEFAULT 1
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+--
+-- Индексы сохранённых таблиц
+--
+
+--
+-- Индексы таблицы `acl`
+--
+ALTER TABLE `acl`
+  ADD PRIMARY KEY (`id`);
+
+--
+-- Индексы таблицы `ad_comp_cache`
+--
+ALTER TABLE `ad_comp_cache`
+  ADD PRIMARY KEY (`id`),
+  ADD UNIQUE KEY `comp_name` (`name`);
+
+--
+-- Индексы таблицы `auth_rules`
+--
+ALTER TABLE `auth_rules`
+  ADD PRIMARY KEY (`id`),
+  ADD UNIQUE KEY `rule` (`rule`),
+  ADD KEY `user_id` (`user_id`);
+
+--
+-- Индексы таблицы `building`
+--
+ALTER TABLE `building`
+  ADD PRIMARY KEY (`id`);
+
+--
+-- Индексы таблицы `config`
+--
+ALTER TABLE `config`
+  ADD PRIMARY KEY (`id`),
+  ADD KEY `option` (`option_id`);
+
+--
+-- Индексы таблицы `config_options`
+--
+ALTER TABLE `config_options`
+  ADD PRIMARY KEY (`id`);
+
+--
+-- Индексы таблицы `connections`
+--
+ALTER TABLE `connections`
+  ADD PRIMARY KEY (`id`),
+  ADD KEY `auth_id` (`auth_id`),
+  ADD KEY `device_id` (`device_id`,`port_id`);
+
+--
+-- Индексы таблицы `Customers`
+--
+ALTER TABLE `Customers`
+  ADD PRIMARY KEY (`id`);
+
+--
+-- Индексы таблицы `devices`
+--
+ALTER TABLE `devices`
+  ADD PRIMARY KEY (`id`),
+  ADD KEY `ip` (`ip`),
+  ADD KEY `device_type` (`device_type`);
+
+--
+-- Индексы таблицы `device_l3_interfaces`
+--
+ALTER TABLE `device_l3_interfaces`
+  ADD PRIMARY KEY (`id`);
+
+--
+-- Индексы таблицы `device_models`
+--
+ALTER TABLE `device_models`
+  ADD PRIMARY KEY (`id`);
+
+--
+-- Индексы таблицы `device_ports`
+--
+ALTER TABLE `device_ports`
+  ADD PRIMARY KEY (`id`),
+  ADD KEY `device_id` (`device_id`),
+  ADD KEY `port` (`port`),
+  ADD KEY `target_port_id` (`target_port_id`);
+
+--
+-- Индексы таблицы `device_types`
+--
+ALTER TABLE `device_types`
+  ADD PRIMARY KEY (`id`);
+
+--
+-- Индексы таблицы `dhcp_log`
+--
+ALTER TABLE `dhcp_log`
+  ADD PRIMARY KEY (`id`),
+  ADD KEY `timestamp` (`timestamp`,`action`);
+
+--
+-- Индексы таблицы `dns_cache`
+--
+ALTER TABLE `dns_cache`
+  ADD PRIMARY KEY (`id`),
+  ADD KEY `dns` (`dns`,`ip`),
+  ADD KEY `timestamp` (`timestamp`);
+
+--
+-- Индексы таблицы `Filter_list`
+--
+ALTER TABLE `Filter_list`
+  ADD PRIMARY KEY (`id`),
+  ADD KEY `Name` (`name`);
+
+--
+-- Индексы таблицы `gateway_subnets`
+--
+ALTER TABLE `gateway_subnets`
+  ADD PRIMARY KEY (`id`);
+
+--
+-- Индексы таблицы `Group_filters`
+--
+ALTER TABLE `Group_filters`
+  ADD PRIMARY KEY (`id`),
+  ADD KEY `GroupId` (`group_id`,`filter_id`);
+
+--
+-- Индексы таблицы `Group_list`
+--
+ALTER TABLE `Group_list`
+  ADD PRIMARY KEY (`id`);
+
+--
+-- Индексы таблицы `mac_history`
+--
+ALTER TABLE `mac_history`
+  ADD PRIMARY KEY (`id`),
+  ADD KEY `mac` (`mac`,`timestamp`),
+  ADD KEY `ip` (`ip`,`timestamp`),
+  ADD KEY `timestamp` (`timestamp`) USING BTREE,
+  ADD KEY `mac_2` (`mac`),
+  ADD KEY `ip_2` (`ip`);
+
+--
+-- Индексы таблицы `mac_vendors`
+--
+ALTER TABLE `mac_vendors`
+  ADD PRIMARY KEY (`id`),
+  ADD KEY `oui` (`oui`);
+
+--
+-- Индексы таблицы `OU`
+--
+ALTER TABLE `OU`
+  ADD PRIMARY KEY (`id`);
+
+--
+-- Индексы таблицы `Queue_list`
+--
+ALTER TABLE `Queue_list`
+  ADD PRIMARY KEY (`id`),
+  ADD UNIQUE KEY `id` (`id`);
+
+--
+-- Индексы таблицы `remote_syslog`
+--
+ALTER TABLE `remote_syslog`
+  ADD PRIMARY KEY (`id`),
+  ADD KEY `date` (`date`,`device_id`,`ip`);
+ALTER TABLE `remote_syslog` ADD FULLTEXT KEY `message` (`message`);
+
+--
+-- Индексы таблицы `sessions`
+--
+ALTER TABLE `sessions`
+  ADD PRIMARY KEY (`id`);
+
+--
+-- Индексы таблицы `subnets`
+--
+ALTER TABLE `subnets`
+  ADD PRIMARY KEY (`id`),
+  ADD KEY `ip_int_start` (`ip_int_start`,`ip_int_stop`),
+  ADD KEY `dhcp` (`dhcp`,`office`,`hotspot`,`static`);
+
+--
+-- Индексы таблицы `Traffic_detail`
+--
+ALTER TABLE `Traffic_detail`
+  ADD PRIMARY KEY (`id`),
+  ADD KEY `src` (`auth_id`,`timestamp`,`router_id`,`src_ip`),
+  ADD KEY `dst` (`auth_id`,`timestamp`,`router_id`,`dst_ip`);
+
+--
+-- Индексы таблицы `Unknown_mac`
+--
+ALTER TABLE `Unknown_mac`
+  ADD PRIMARY KEY (`id`),
+  ADD KEY `timestamp` (`timestamp`,`device_id`,`port_id`,`mac`);
+
+--
+-- Индексы таблицы `User_auth`
+--
+ALTER TABLE `User_auth`
+  ADD PRIMARY KEY (`id`),
+  ADD KEY `auth_index` (`id`,`user_id`,`ip_int`,`mac`,`ip`,`deleted`) USING BTREE,
+  ADD KEY `deleted` (`deleted`),
+  ADD KEY `ou_id` (`ou_id`);
+
+--
+-- Индексы таблицы `User_auth_alias`
+--
+ALTER TABLE `User_auth_alias`
+  ADD PRIMARY KEY (`id`),
+  ADD KEY `auth_id` (`auth_id`);
+
+--
+-- Индексы таблицы `User_list`
+--
+ALTER TABLE `User_list`
+  ADD PRIMARY KEY (`id`),
+  ADD KEY `users` (`id`,`ou_id`,`enabled`,`blocked`,`deleted`);
+
+--
+-- Индексы таблицы `User_stats`
+--
+ALTER TABLE `User_stats`
+  ADD PRIMARY KEY (`id`),
+  ADD KEY `timestamp` (`timestamp`,`auth_id`,`router_id`);
+
+--
+-- Индексы таблицы `User_stats_full`
+--
+ALTER TABLE `User_stats_full`
+  ADD PRIMARY KEY (`id`),
+  ADD KEY `timestamp` (`timestamp`,`auth_id`,`router_id`);
+
+--
+-- Индексы таблицы `variables`
+--
+ALTER TABLE `variables`
+  ADD PRIMARY KEY (`id`),
+  ADD UNIQUE KEY `name` (`name`),
+  ADD KEY `clear_time` (`clear_time`,`created`);
+
+--
+-- Индексы таблицы `vendors`
+--
+ALTER TABLE `vendors`
+  ADD PRIMARY KEY (`id`);
+
+--
+-- Индексы таблицы `version`
+--
+ALTER TABLE `version`
+  ADD UNIQUE KEY `id` (`id`);
+
+--
+-- Индексы таблицы `Wan_stats`
+--
+ALTER TABLE `Wan_stats`
+  ADD PRIMARY KEY (`id`),
+  ADD UNIQUE KEY `main` (`time`,`router_id`,`interface_id`),
+  ADD KEY `times` (`time`);
+
+--
+-- Индексы таблицы `worklog`
+--
+ALTER TABLE `worklog`
+  ADD PRIMARY KEY (`id`),
+  ADD KEY `timestamp` (`timestamp`) USING BTREE,
+  ADD KEY `level` (`level`),
+  ADD KEY `auth_id` (`auth_id`);
+ALTER TABLE `worklog` ADD FULLTEXT KEY `customer` (`customer`);
+
+
+--
+-- Индексы таблицы `dns_queue`
+--
+ALTER TABLE `dns_queue`
+  ADD PRIMARY KEY (`id`);
+
+--
+-- AUTO_INCREMENT для сохранённых таблиц
+--
+
+--
+-- AUTO_INCREMENT для таблицы `acl`
+--
+ALTER TABLE `acl`
+  MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
+
+--
+-- AUTO_INCREMENT для таблицы `ad_comp_cache`
+--
+ALTER TABLE `ad_comp_cache`
+  MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
+
+--
+-- AUTO_INCREMENT для таблицы `auth_rules`
+--
+ALTER TABLE `auth_rules`
+  MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
+
+--
+-- AUTO_INCREMENT для таблицы `building`
+--
+ALTER TABLE `building`
+  MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=8;
+
+--
+-- AUTO_INCREMENT для таблицы `config`
+--
+ALTER TABLE `config`
+  MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=149;
+
+--
+-- AUTO_INCREMENT для таблицы `config_options`
+--
+ALTER TABLE `config_options`
+  MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=74;
+
+--
+-- AUTO_INCREMENT для таблицы `connections`
+--
+ALTER TABLE `connections`
+  MODIFY `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT;
+
+--
+-- AUTO_INCREMENT для таблицы `Customers`
+--
+ALTER TABLE `Customers`
+  MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=14;
+
+--
+-- AUTO_INCREMENT для таблицы `devices`
+--
+ALTER TABLE `devices`
+  MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
+
+--
+-- AUTO_INCREMENT для таблицы `device_l3_interfaces`
+--
+ALTER TABLE `device_l3_interfaces`
+  MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
+
+--
+-- AUTO_INCREMENT для таблицы `device_models`
+--
+ALTER TABLE `device_models`
+  MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=10013;
+
+--
+-- AUTO_INCREMENT для таблицы `device_ports`
+--
+ALTER TABLE `device_ports`
+  MODIFY `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT;
+
+--
+-- AUTO_INCREMENT для таблицы `device_types`
+--
+ALTER TABLE `device_types`
+  MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=6;
+
+--
+-- AUTO_INCREMENT для таблицы `dhcp_log`
+--
+ALTER TABLE `dhcp_log`
+  MODIFY `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT;
+
+--
+-- AUTO_INCREMENT для таблицы `dns_cache`
+--
+ALTER TABLE `dns_cache`
+  MODIFY `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT;
+
+--
+-- AUTO_INCREMENT для таблицы `dns_queue`
+--
+ALTER TABLE `dns_queue`
+  MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
+
+--
+-- AUTO_INCREMENT для таблицы `Filter_list`
+--
+ALTER TABLE `Filter_list`
+  MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=111;
+
+--
+-- AUTO_INCREMENT для таблицы `gateway_subnets`
+--
+ALTER TABLE `gateway_subnets`
+  MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
+
+--
+-- AUTO_INCREMENT для таблицы `Group_filters`
+--
+ALTER TABLE `Group_filters`
+  MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=19;
+
+--
+-- AUTO_INCREMENT для таблицы `Group_list`
+--
+ALTER TABLE `Group_list`
+  MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=10;
+
+--
+-- AUTO_INCREMENT для таблицы `mac_history`
+--
+ALTER TABLE `mac_history`
+  MODIFY `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT;
+
+--
+-- AUTO_INCREMENT для таблицы `mac_vendors`
+--
+ALTER TABLE `mac_vendors`
+  MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
+
+--
+-- AUTO_INCREMENT для таблицы `OU`
+--
+ALTER TABLE `OU`
+  MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=30;
+
+--
+-- AUTO_INCREMENT для таблицы `Queue_list`
+--
+ALTER TABLE `Queue_list`
+  MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=8;
+
+--
+-- AUTO_INCREMENT для таблицы `remote_syslog`
+--
+ALTER TABLE `remote_syslog`
+  MODIFY `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT;
+
+--
+-- AUTO_INCREMENT для таблицы `sessions`
+--
+ALTER TABLE `sessions`
+  MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
+
+--
+-- AUTO_INCREMENT для таблицы `subnets`
+--
+ALTER TABLE `subnets`
+  MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
+
+--
+-- AUTO_INCREMENT для таблицы `Traffic_detail`
+--
+ALTER TABLE `Traffic_detail`
+  MODIFY `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT;
+
+--
+-- AUTO_INCREMENT для таблицы `Unknown_mac`
+--
+ALTER TABLE `Unknown_mac`
+  MODIFY `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT;
+
+--
+-- AUTO_INCREMENT для таблицы `User_auth`
+--
+ALTER TABLE `User_auth`
+  MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
+
+--
+-- AUTO_INCREMENT для таблицы `User_auth_alias`
+--
+ALTER TABLE `User_auth_alias`
+  MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
+
+--
+-- AUTO_INCREMENT для таблицы `User_list`
+--
+ALTER TABLE `User_list`
+  MODIFY `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT;
+
+--
+-- AUTO_INCREMENT для таблицы `User_stats`
+--
+ALTER TABLE `User_stats`
+  MODIFY `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT;
+
+--
+-- AUTO_INCREMENT для таблицы `User_stats_full`
+--
+ALTER TABLE `User_stats_full`
+  MODIFY `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT;
+
+--
+-- AUTO_INCREMENT для таблицы `variables`
+--
+ALTER TABLE `variables`
+  MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
+
+--
+-- AUTO_INCREMENT для таблицы `vendors`
+--
+ALTER TABLE `vendors`
+  MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=10023;
+
+--
+-- AUTO_INCREMENT для таблицы `Wan_stats`
+--
+ALTER TABLE `Wan_stats`
+  MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
+
+
+--
+-- AUTO_INCREMENT для таблицы `worklog`
+--
+ALTER TABLE `worklog`
+  MODIFY `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT;
+COMMIT;
+
+/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
+/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
+/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;

+ 1 - 1
docs/mysql/latest-mysql.sql

@@ -1 +1 @@
-./2-7-0/mysql.sql
+./2-7-1/mysql.sql

+ 1898 - 0
docs/patches/USM.pm

@@ -0,0 +1,1898 @@
+# -*- mode: perl -*-
+# ============================================================================
+
+package Net::SNMP::Security::USM;
+
+# $Id: USM.pm,v 4.1 2010/09/10 00:01:22 dtown Rel $
+
+# Object that implements the SNMPv3 User-based Security Model.
+
+# Copyright (c) 2001-2010 David M. Town <dtown@cpan.org>
+# All rights reserved.
+
+# This program is free software; you may redistribute it and/or modify it
+# under the same terms as the Perl 5 programming language system itself.
+
+# ============================================================================
+
+use strict;
+
+use Net::SNMP::Security qw( :ALL );
+
+use Net::SNMP::Message qw(
+   :msgFlags asn1_itoa OCTET_STRING SEQUENCE INTEGER SNMP_VERSION_3 TRUE FALSE
+);
+
+use Crypt::DES();
+use Digest::MD5();
+use Digest::SHA qw( hmac_sha1 hmac_sha224 hmac_sha256 hmac_sha384 hmac_sha512 );
+use Digest::HMAC_MD5 qw ( hmac_md5 );
+
+## Version of the Net::SNMP::Security::USM module
+
+our $VERSION = v4.0.1;
+
+## Handle importing/exporting of symbols
+
+use base qw( Net::SNMP::Security );
+
+our @EXPORT_OK;
+
+our %EXPORT_TAGS = (
+   authprotos => [
+      qw( AUTH_PROTOCOL_NONE AUTH_PROTOCOL_HMACMD5 AUTH_PROTOCOL_HMACSHA
+          AUTH_PROTOCOL_HMACSHA224 AUTH_PROTOCOL_HMACSHA256.
+          AUTH_PROTOCOL_HMACSHA384 AUTH_PROTOCOL_HMACSHA512 )
+   ],
+   levels     => [
+      qw( SECURITY_LEVEL_NOAUTHNOPRIV SECURITY_LEVEL_AUTHNOPRIV
+          SECURITY_LEVEL_AUTHPRIV )
+   ],
+   models     => [
+      qw( SECURITY_MODEL_ANY SECURITY_MODEL_SNMPV1 SECURITY_MODEL_SNMPV2C
+          SECURITY_MODEL_USM )
+   ],
+   privprotos => [
+      qw( PRIV_PROTOCOL_NONE PRIV_PROTOCOL_DES PRIV_PROTOCOL_AESCFB128
+          PRIV_PROTOCOL_DRAFT_3DESEDE PRIV_PROTOCOL_DRAFT_AESCFB128
+          PRIV_PROTOCOL_DRAFT_AESCFB192 PRIV_PROTOCOL_DRAFT_AESCFB256 )
+   ],
+);
+
+Exporter::export_ok_tags( qw( authprotos levels models privprotos ) );
+
+$EXPORT_TAGS{ALL} = [ @EXPORT_OK ];
+
+## RCC 3414 - Authentication protocols
+
+sub AUTH_PROTOCOL_NONE       { '1.3.6.1.6.3.10.1.1.1' } # usmNoAuthProtocol
+sub AUTH_PROTOCOL_HMACMD5    { '1.3.6.1.6.3.10.1.1.2' } # usmHMACMD5AuthProtocol
+sub AUTH_PROTOCOL_HMACSHA    { '1.3.6.1.6.3.10.1.1.3' } # usmHMACSHAAuthProtocol
+sub AUTH_PROTOCOL_HMACSHA224 { '1.3.6.1.6.3.10.1.1.4' } # usmHMAC128SHA224AuthProtocol
+sub AUTH_PROTOCOL_HMACSHA256 { '1.3.6.1.6.3.10.1.1.5' } # usmHMAC192SHA256AuthProtocol
+sub AUTH_PROTOCOL_HMACSHA384 { '1.3.6.1.6.3.10.1.1.6' } # usmHMAC256SHA384AuthProtocol
+sub AUTH_PROTOCOL_HMACSHA512 { '1.3.6.1.6.3.10.1.1.7' } # usmHMAC384SHA512AuthProtocol
+
+## RFC 3414 - Privacy protocols
+
+sub PRIV_PROTOCOL_NONE    { '1.3.6.1.6.3.10.1.2.1' } # usmNoPrivProtocol
+sub PRIV_PROTOCOL_DES     { '1.3.6.1.6.3.10.1.2.2' } # usmDESPrivProtocol
+
+## RFC 3826 - The AES Cipher Algorithm in the SNMP USM 
+
+# usmAesCfb128Protocol
+sub PRIV_PROTOCOL_AESCFB128        {  '1.3.6.1.6.3.10.1.2.4' }
+
+# The privacy protocols below have been implemented using the draft 
+# specifications intended to extend the User-based Security Model 
+# defined in RFC 3414.  Since the object definitions have not been 
+# standardized, they have been based on the Extended Security Options 
+# Consortium MIB found at http://www.snmp.com/eso/esoConsortiumMIB.txt.
+
+# Extension to Support Triple-DES EDE <draft-reeder-snmpv3-usm-3desede-00.txt> 
+# Reeder and Gudmunsson; October 1999, expired April 2000 
+
+# usm3DESPrivProtocol 
+sub PRIV_PROTOCOL_DRAFT_3DESEDE    { '1.3.6.1.4.1.14832.1.1' }
+
+# AES Cipher Algorithm in the USM <draft-blumenthal-aes-usm-04.txt>
+# Blumenthal, Maino, and McCloghrie; October 2002, expired April 2003 
+
+# usmAESCfb128PrivProtocol 
+sub PRIV_PROTOCOL_DRAFT_AESCFB128  { '1.3.6.1.4.1.14832.1.2' }
+
+# usmAESCfb192PrivProtocol 
+sub PRIV_PROTOCOL_DRAFT_AESCFB192  { '1.3.6.1.4.1.14832.1.3' }
+
+# usmAESCfb256PrivProtocol
+sub PRIV_PROTOCOL_DRAFT_AESCFB256  { '1.3.6.1.4.1.14832.1.4' }
+
+## Package variables
+
+our $ENGINE_ID;  # Our authoritative snmpEngineID                                                         
+# [public methods] -----------------------------------------------------------
+
+sub new
+{
+   my ($class, %argv) = @_;
+
+   # Create a new data structure for the object
+   my $this = bless {
+      '_error'              => undef,                 # Error message
+      '_version'            => SNMP_VERSION_3,        # version 
+      '_authoritative'      => FALSE,                 # Authoritative flag
+      '_discovered'         => FALSE,                 # Engine discovery flag
+      '_synchronized'       => FALSE,                 # Synchronization flag
+      '_engine_id'          => q{},                   # snmpEngineID
+      '_engine_boots'       => 0,                     # snmpEngineBoots
+      '_engine_time'        => 0,                     # snmpEngineTime
+      '_latest_engine_time' => 0,                     # latestReceivedEngineTime
+      '_time_epoc'          => time(),                # snmpEngineBoots epoc
+      '_user_name'          => q{},                   # securityName 
+      '_auth_data'          => undef,                 # Authentication data
+      '_auth_maclen'        => undef,                 # MAC length
+      '_auth_key'           => undef,                 # authKey 
+      '_auth_password'      => undef,                 # Authentication password 
+      '_auth_protocol'      => AUTH_PROTOCOL_HMACMD5, # authProtocol
+      '_priv_data'          => undef,                 # Privacy data
+      '_priv_key'           => undef,                 # privKey 
+      '_priv_password'      => undef,                 # Privacy password
+      '_priv_protocol'      => PRIV_PROTOCOL_DES,     # privProtocol
+      '_security_level'     => SECURITY_LEVEL_NOAUTHNOPRIV
+   }, $class;
+
+   # We first need to find out if we are an authoritative SNMP
+   # engine and set the authProtocol and privProtocol if they 
+   # have been provided.
+
+   foreach (keys %argv) {
+
+      if (/^-?authoritative$/i) {
+         $this->{_authoritative} = (delete $argv{$_}) ? TRUE : FALSE;
+      } elsif (/^-?authprotocol$/i) {
+         $this->_auth_protocol(delete $argv{$_});
+      } elsif (/^-?privprotocol$/i) {
+         $this->_priv_protocol(delete $argv{$_});
+      }
+
+      if (defined $this->{_error}) {
+         return wantarray ? (undef, $this->{_error}) : undef;
+      }
+   }
+
+   # Now validate the rest of the passed arguments
+
+   for (keys %argv) {
+
+      if (/^-?version$/i) {
+         $this->_version($argv{$_});
+      } elsif (/^-?debug$/i) {
+         $this->debug($argv{$_});
+      } elsif ((/^-?engineid$/i) && ($this->{_authoritative})) {
+         $this->_engine_id($argv{$_});
+      } elsif (/^-?username$/i) {
+         $this->_user_name($argv{$_});
+      } elsif (/^-?authkey$/i) {
+         $this->_auth_key($argv{$_});
+      } elsif (/^-?authpassword$/i) {
+         $this->_auth_password($argv{$_});
+      } elsif (/^-?privkey$/i) {
+         $this->_priv_key($argv{$_});
+      } elsif (/^-?privpassword$/i) {
+         $this->_priv_password($argv{$_});
+      } else {
+         $this->_error('The argument "%s" is unknown', $_);
+      }
+
+      if (defined $this->{_error}) {
+         return wantarray ? (undef, $this->{_error}) : undef;
+      }
+
+   }
+
+   # Generate a snmpEngineID and populate the object accordingly
+   # if we are an authoritative snmpEngine.
+
+   if ($this->{_authoritative}) {
+      $this->_snmp_engine_init();
+   }
+
+   # Define the securityParameters
+   if (!defined $this->_security_params()) {
+      return wantarray ? (undef, $this->{_error}) : undef;
+   }
+
+   # Return the object and an empty error message (in list context)
+   return wantarray ? ($this, q{}) : $this;
+}
+
+sub generate_request_msg
+{
+   my ($this, $pdu, $msg) = @_;
+
+   # Clear any previous errors
+   $this->_error_clear();
+
+   if (@_ < 3) {
+      return $this->_error('The required PDU and/or Message object is missing');
+   }
+
+   # Validate the SNMP version of the PDU
+   if ($pdu->version() != $this->{_version}) {
+      return $this->_error(
+         'The SNMP version %d was expected, but %d was found',
+         $this->{_version}, $pdu->version()
+      );
+   }
+
+   # Validate the securityLevel of the PDU
+   if ($pdu->security_level() > $this->{_security_level}) {
+      return $this->_error(
+         'The PDU securityLevel %d is greater than the configured value %d',
+         $pdu->security_level(), $this->{_security_level}
+      );
+   }
+
+   # Validate PDU type with snmpEngine type
+   if ($pdu->expect_response()) {
+      if ($this->{_authoritative}) {
+         return $this->_error(
+            'Must be a non-authoritative SNMP engine to generate a %s',
+            asn1_itoa($pdu->pdu_type())
+         );
+      }
+   } else {
+      if (!$this->{_authoritative}) {
+         return $this->_error(
+            'Must be an authoritative SNMP engine to generate a %s',
+            asn1_itoa($pdu->pdu_type())
+         );
+      }
+   }
+
+   # Extract the msgGlobalData out of the message
+   my $msg_global_data = $msg->clear();
+
+   # AES in the USM Section 3.1.2.1 - "The 128-bit IV is obtained as
+   # the concatenation of the... ...snmpEngineBoots, ...snmpEngineTime,
+   # and a local 64-bit integer.  We store the current snmpEngineBoots
+   # and snmpEngineTime before encrypting the PDU so that the computed
+   # IV matches the transmitted msgAuthoritativeEngineBoots and
+   # msgAuthoritativeEngineTime.
+
+   my $msg_engine_time  = $this->_engine_time();
+   my $msg_engine_boots = $this->_engine_boots();
+
+   # Copy the PDU into a "plain text" buffer
+   my $pdu_buffer  = $pdu->copy();
+   my $priv_params = q{};
+
+   # encryptedPDU::=OCTET STRING
+   if ($pdu->security_level() > SECURITY_LEVEL_AUTHNOPRIV) {
+      if (!defined $this->_encrypt_data($msg, $priv_params, $pdu_buffer)) {
+         return $this->_error();
+      }
+   }
+
+   # msgPrivacyParameters::=OCTET STRING
+   if (!defined $msg->prepare(OCTET_STRING, $priv_params)) {
+      return $this->_error($msg->error());
+   }
+
+   # msgAuthenticationParameters::=OCTET STRING
+
+   my $auth_params = q{};
+   my $auth_location = 0;
+
+   if ($pdu->security_level() > SECURITY_LEVEL_NOAUTHNOPRIV) {
+
+      # Save the location to fill in msgAuthenticationParameters later
+      $auth_location = $msg->length() + $this->{_auth_maclen} + length $pdu_buffer;
+
+      # Set the msgAuthenticationParameters to all zeros
+      $auth_params = pack "x$this->{_auth_maclen}";
+   }
+
+   if (!defined $msg->prepare(OCTET_STRING, $auth_params)) {
+      return $this->_error($msg->error());
+   }
+
+   # msgUserName::=OCTET STRING 
+   if (!defined $msg->prepare(OCTET_STRING, $pdu->security_name())) {
+      return $this->_error($msg->error());
+   }
+
+   # msgAuthoritativeEngineTime::=INTEGER  
+   if (!defined $msg->prepare(INTEGER, $msg_engine_time)) {
+      return $this->_error($msg->error());
+   }
+
+   # msgAuthoritativeEngineBoots::=INTEGER
+   if (!defined $msg->prepare(INTEGER, $msg_engine_boots)) {
+      return $this->_error($msg->error());
+   }
+
+   # msgAuthoritativeEngineID
+   if (!defined $msg->prepare(OCTET_STRING, $this->_engine_id())) {
+      return $this->_error($msg->error());
+   }
+
+   # UsmSecurityParameters::= SEQUENCE
+   if (!defined $msg->prepare(SEQUENCE)) {
+      return $this->_error($msg->error());
+   }
+
+   # msgSecurityParameters::=OCTET STRING
+   if (!defined $msg->prepare(OCTET_STRING, $msg->clear())) {
+      return $this->_error($msg->error());
+   }
+
+   # Append the PDU
+   if (!defined $msg->append($pdu_buffer)) {
+      return $this->_error($msg->error());
+   }
+
+   # Prepend the msgGlobalData
+   if (!defined $msg->prepend($msg_global_data)) {
+      return $this->_error($msg->error());
+   }
+
+   # version::=INTEGER
+   if (!defined $msg->prepare(INTEGER, $this->{_version})) {
+      return $this->_error($msg->error());
+   }
+
+   # message::=SEQUENCE
+   if (!defined $msg->prepare(SEQUENCE)) {
+      return $this->_error($msg->error());
+   }
+
+   # Apply authentication
+   if ($pdu->security_level() > SECURITY_LEVEL_NOAUTHNOPRIV) {
+      if (!defined $this->_authenticate_outgoing_msg($msg, $auth_location)) {
+         return $this->_error($msg->error());
+      }
+   }
+
+   # Return the Message
+   return $msg;
+}
+
+sub process_incoming_msg
+{
+   my ($this, $msg) = @_;
+
+   # Clear any previous errors
+   $this->_error_clear();
+
+   return $this->_error('The required Message object is missing') if (@_ < 2);
+
+   # msgSecurityParameters::=OCTET STRING
+
+   my $msg_params = $msg->process(OCTET_STRING);
+   return $this->_error($msg->error()) if !defined $msg_params;
+
+   # Need to move the buffer index back to the begining of the data
+   # portion of the OCTET STRING that contains the msgSecurityParameters.
+
+   $msg->index($msg->index() - length $msg_params);
+
+   # UsmSecurityParameters::=SEQUENCE
+   return $this->_error($msg->error()) if !defined $msg->process(SEQUENCE);
+
+   # msgAuthoritativeEngineID::=OCTET STRING
+   my $msg_engine_id;
+   if (!defined($msg_engine_id = $msg->process(OCTET_STRING))) {
+      return $this->_error($msg->error());
+   }
+
+   # msgAuthoritativeEngineBoots::=INTEGER (0..2147483647)
+   my $msg_engine_boots;
+   if (!defined ($msg_engine_boots = $msg->process(INTEGER))) {
+      return $this->_error($msg->error());
+   }
+   if (($msg_engine_boots < 0) || ($msg_engine_boots > 2147483647)) {
+      return $this->_error(
+         'The msgAuthoritativeEngineBoots value %d is out of range ' .
+         '(0..2147483647)', $msg_engine_boots
+      );
+   }
+
+   # msgAuthoritativeEngineTime::=INTEGER (0..2147483647)
+   my $msg_engine_time;
+   if (!defined ($msg_engine_time = $msg->process(INTEGER))) {
+      return $this->_error($msg->error());
+   }
+   if (($msg_engine_time < 0) || ($msg_engine_time > 2147483647)) {
+      return $this->_error(
+         'The msgAuthoritativeEngineTime value %d is out of range ' .
+         '(0..2147483647)', $msg_engine_time
+      );
+   }
+
+   # msgUserName::=OCTET STRING (SIZE(0..32))
+   if (!defined $msg->security_name($msg->process(OCTET_STRING))) {
+      return $this->_error($msg->error());
+   }
+
+   # msgAuthenticationParameters::=OCTET STRING
+   my $auth_params;
+   if (!defined ($auth_params = $msg->process(OCTET_STRING))) {
+      return $this->_error($msg->error());
+   }
+
+   # We need to zero out the msgAuthenticationParameters in order 
+   # to compute the HMAC properly.
+
+   if (my $len = length $auth_params) {
+      if ($len != $this->{_auth_maclen}) {
+         return $this->_error(
+            'The msgAuthenticationParameters length of %d is invalid', $len
+         );
+      }
+         substr ${$msg->reference}, ($msg->index() - $this->{_auth_maclen}), $this->{_auth_maclen}, pack "x$this->{_auth_maclen}";
+   }
+
+   # msgPrivacyParameters::=OCTET STRING
+   my $priv_params;
+   if (!defined ($priv_params = $msg->process(OCTET_STRING))) {
+      return $this->_error($msg->error());
+   }
+
+   # Validate the msgAuthoritativeEngineID and msgUserName
+
+   if ($this->{_discovered}) {
+
+      if ($msg_engine_id ne $this->_engine_id()) {
+         return $this->_error(
+            'The msgAuthoritativeEngineID "%s" was expected, but "%s" was ' .
+            'found', unpack('H*', $this->_engine_id()),
+            unpack 'H*', $msg_engine_id
+         );
+      }
+
+      if ($msg->security_name() ne $this->_user_name()) {
+         return $this->_error(
+            'The msgUserName "%s" was expected, but "%s" was found',
+            $this->_user_name(), $msg->security_name()
+         );
+      }
+
+   } else {
+
+      # Handle authoritativeEngineID discovery
+      if (!defined $this->_engine_id_discovery($msg_engine_id)) {
+         return $this->_error();
+      }
+
+   }
+
+   # Validate the incoming securityLevel
+
+   my $security_level = $msg->security_level();
+
+   if ($security_level > $this->{_security_level}) {
+      return $this->_error(
+          'The message securityLevel %d is greater than the configured ' .
+          'value %d', $security_level, $this->{_security_level}
+      );
+   }
+
+   if ($security_level > SECURITY_LEVEL_NOAUTHNOPRIV) {
+
+      # Authenticate the message
+      if (!defined $this->_authenticate_incoming_msg($msg, $auth_params)) {
+         return $this->_error();
+      }
+
+      # Synchronize the time
+      if (!$this->_synchronize($msg_engine_boots, $msg_engine_time)) {
+         return $this->_error();
+      }
+
+      # Check for timeliness
+      if (!defined $this->_timeliness($msg_engine_boots, $msg_engine_time)) {
+         return $this->_error();
+      }
+
+      if ($security_level > SECURITY_LEVEL_AUTHNOPRIV) {
+
+         # Validate the msgPrivacyParameters length.
+
+         if (length($priv_params) != 8) {
+            return $this->_error(
+               'The msgPrivacyParameters length of %d is invalid',
+               length $priv_params
+            );
+         }
+
+         # AES in the USM Section 3.1.2.1 - "The 128-bit IV is
+         # obtained as the concatenation of the... ...snmpEngineBoots,
+         # ...snmpEngineTime, and a local 64-bit integer.  ...The
+         # 64-bit integer must be placed in the msgPrivacyParameters
+         # field..."  We must prepend the snmpEngineBoots and
+         # snmpEngineTime as received in order to compute the IV.
+
+         if (($this->{_priv_protocol} eq PRIV_PROTOCOL_AESCFB128)       ||
+             ($this->{_priv_protocol} eq PRIV_PROTOCOL_DRAFT_AESCFB192) ||
+             ($this->{_priv_protocol} eq PRIV_PROTOCOL_DRAFT_AESCFB256))
+         {
+            substr $priv_params, 0, 0, pack 'NN', $msg_engine_boots,
+                                                  $msg_engine_time;
+         }
+
+         # encryptedPDU::=OCTET STRING
+
+         return $this->_decrypt_data($msg,
+                                     $priv_params,
+                                     $msg->process(OCTET_STRING));
+
+      }
+
+   }
+
+   return TRUE;
+}
+
+sub user_name
+{
+   return $_[0]->{_user_name};
+}
+
+sub auth_protocol
+{
+   my ($this) = @_;
+
+   if ($this->{_security_level} > SECURITY_LEVEL_NOAUTHNOPRIV) {
+      return $this->{_auth_protocol};
+   }
+
+   return AUTH_PROTOCOL_NONE;
+}
+
+sub auth_key
+{
+   return $_[0]->{_auth_key};
+}
+
+sub priv_protocol
+{
+   my ($this) = @_;
+
+   if ($this->{_security_level} > SECURITY_LEVEL_AUTHNOPRIV) {
+      return $this->{_priv_protocol};
+   }
+
+   return PRIV_PROTOCOL_NONE;
+}
+
+sub priv_key
+{
+   return $_[0]->{_priv_key};
+}
+
+sub engine_id
+{
+   return $_[0]->{_engine_id};
+}
+
+sub engine_boots
+{
+   goto _engine_boots;
+}
+
+sub engine_time
+{
+   goto &_engine_time;
+}
+
+sub security_level
+{
+   return $_[0]->{_security_level};
+}
+
+sub security_model
+{
+   # RFC 3411 - SnmpSecurityModel::=TEXTUAL-CONVENTION
+
+   return SECURITY_MODEL_USM;
+}
+
+sub security_name
+{
+   goto &_user_name;
+}
+
+sub discovered
+{
+   my ($this) = @_;
+
+   if ($this->{_security_level} > SECURITY_LEVEL_NOAUTHNOPRIV) {
+      return ($this->{_discovered} && $this->{_synchronized});
+   }
+
+   return $this->{_discovered};
+}
+
+# [private methods] ----------------------------------------------------------
+
+sub _version
+{
+   my ($this, $version) = @_;
+
+   if ($version != SNMP_VERSION_3) {
+      return $this->_error('The SNMP version %s is not supported', $version);
+   }
+
+   return $this->{_version} = $version;
+}
+
+sub _engine_id
+{
+   my ($this, $engine_id) = @_;
+
+   if (@_ < 2) {
+      return $this->{_engine_id};
+   }
+
+   if ($engine_id =~  m/^(?:0x)?([A-F0-9]+)$/i) {
+       my $eid = pack 'H*', length($1) %  2 ? '0'.$1 : $1;
+       my $len = length $eid;
+       if ($len < 5 || $len > 32) {
+          return $this->_error(
+             'The authoritativeEngineID length of %d is out of range (5..32)',
+             $len
+          );
+       }
+       $this->{_engine_id} = $eid;
+   } else {
+      return $this->_error(
+          'The authoritativeEngineID "%s" is expected in hexadecimal format',
+          $engine_id
+      );
+   }
+
+   return $this->{_engine_id};
+}
+
+sub _user_name
+{
+   my ($this, $user_name) = @_;
+
+   if (@_ == 2) {
+      if ($user_name eq q{}) {
+         return $this->_error('An empty userName was specified');
+      } elsif (length($user_name) > 32) {
+         return $this->_error(
+            'The userName length of %d is out of range (1..32)',
+            length $user_name
+         );
+      }
+      $this->{_user_name} = $user_name;
+   }
+
+   # RFC 3414 Section 4 - "Discovery... ...msgUserName of zero-length..."
+
+   return ($this->{_discovered}) ? $this->{_user_name} : q{};
+}
+
+sub _snmp_engine_init
+{
+   my ($this) = @_;
+
+   if ($this->{_engine_id} eq q{}) {
+
+      # Initialize our snmpEngineID using the algorithm described 
+      # in RFC 3411 - SnmpEngineID::=TEXTUAL-CONVENTION.
+
+      # The first bit is set to one to indicate that the RFC 3411
+      # algorithm is being used.  The first fours bytes are to be
+      # the agent's SNMP management private enterprise number, but
+      # they are set to all zeros. The fifth byte is set to one to
+      # indicate that the final four bytes are an IPv4 address.
+
+      if (!defined $ENGINE_ID) {
+         $ENGINE_ID = eval {
+            require Sys::Hostname;
+            pack('H10', '8000000001') . gethostbyname Sys::Hostname::hostname();
+         };
+
+         # Fallback in case gethostbyname() or hostname() fail
+         if ($@) {
+            $ENGINE_ID = pack 'x11H2', '01';
+         }
+      }
+
+      $this->{_engine_id} = $ENGINE_ID;
+   }
+
+   $this->{_engine_boots} = 1;
+   $this->{_time_epoc}    = $^T;
+   $this->{_synchronized} = TRUE;
+   $this->{_discovered}   = TRUE;
+
+   return TRUE;
+}
+
+sub _auth_key
+{
+   my ($this, $auth_key) = @_;
+
+   if (@_ == 2) {
+      if ($auth_key =~ m/^(?:0x)?([A-F0-9]+)$/i) {
+         $this->{_auth_key} = pack 'H*', length($1) % 2 ? '0'.$1 : $1;
+         if (!defined $this->_auth_key_validate()) {
+            return $this->_error();
+         }
+      } else {
+         return $this->_error(
+            'The authKey "%s" is expected in hexadecimal format', $auth_key
+         );
+      }
+   }
+
+   return $this->{_auth_key};
+}
+
+sub _auth_password
+{
+   my ($this, $auth_password) = @_;
+
+   if (@_ == 2) {
+      if ($auth_password eq q{}) {
+         return $this->_error('An empty authentication password was specified');
+      }
+      $this->{_auth_password} = $auth_password;
+   }
+
+   return $this->{_auth_password};
+}
+
+{
+   my $protocols = {
+      '(?:hmac-)?md5(?:-96)?',           AUTH_PROTOCOL_HMACMD5,
+      quotemeta AUTH_PROTOCOL_HMACMD5,   AUTH_PROTOCOL_HMACMD5,
+      '(?:hmac-)?sha(?:-?1|-96)?',       AUTH_PROTOCOL_HMACSHA,
+      quotemeta AUTH_PROTOCOL_HMACSHA,   AUTH_PROTOCOL_HMACSHA,
+      '(?:hmac-)?sha(?:-?224)',          AUTH_PROTOCOL_HMACSHA224,
+      'usmHMAC128SHA224AuthProtocol',    AUTH_PROTOCOL_HMACSHA224,
+      quotemeta AUTH_PROTOCOL_HMACSHA224,AUTH_PROTOCOL_HMACSHA224,
+      '(?:hmac-)?sha(?:-?256)',          AUTH_PROTOCOL_HMACSHA256,
+      'usmHMAC192SHA256AuthProtocol',    AUTH_PROTOCOL_HMACSHA256,
+      quotemeta AUTH_PROTOCOL_HMACSHA256,AUTH_PROTOCOL_HMACSHA256,
+      '(?:hmac-)?sha(?:-?384)',          AUTH_PROTOCOL_HMACSHA384,
+      'usmHMAC256SHA384AuthProtocol',    AUTH_PROTOCOL_HMACSHA384,
+      quotemeta AUTH_PROTOCOL_HMACSHA384,AUTH_PROTOCOL_HMACSHA384,
+      '(?:hmac-)?sha(?:-?512)',          AUTH_PROTOCOL_HMACSHA512,
+      'usmHMAC384SHA512AuthProtocol',    AUTH_PROTOCOL_HMACSHA512,
+      quotemeta AUTH_PROTOCOL_HMACSHA512,AUTH_PROTOCOL_HMACSHA512,
+   };
+
+   sub _auth_protocol
+   {
+      my ($this, $proto) = @_;
+
+      if (@_ < 2) {
+         return $this->{_auth_protocol};
+      }
+
+      if ($proto eq q{}) {
+         return $this->_error('An empty authProtocol was specified');
+      }
+
+      for (keys %{$protocols}) {
+         if ($proto =~ /^$_$/i) {
+            return $this->{_auth_protocol} = $protocols->{$_};
+         }
+      }
+
+      return $this->_error('The authProtocol "%s" is unknown', $proto);
+   }
+
+}
+
+sub _priv_key
+{
+   my ($this, $priv_key) = @_;
+
+   if (@_ == 2) {
+      if ($priv_key =~ m/^(?:0x)?([A-F0-9]+)$/i) {
+         $this->{_priv_key} = pack 'H*', length($1) % 2 ? '0'.$1 : $1;
+         if (!defined $this->_priv_key_validate()) {
+            return $this->_error();
+         }
+      } else {
+         return $this->_error(
+            'The privKey "%s" is expected in hexadecimal format', $priv_key
+         );
+      }
+   }
+
+   return $this->{_priv_key};
+}
+
+sub _priv_password
+{
+   my ($this, $priv_password) = @_;
+
+   if (@_ == 2) {
+      if ($priv_password eq q{}) {
+         return $this->_error('An empty privacy password was specified');
+      }
+      $this->{_priv_password} = $priv_password;
+   }
+
+   return $this->{_priv_password};
+}
+
+{
+   my $protocols = {
+      '(?:cbc-)?des',                           PRIV_PROTOCOL_DES,
+      quotemeta PRIV_PROTOCOL_DES,              PRIV_PROTOCOL_DES,
+      '(?:cbc-)?(?:3|triple-)des(?:-?ede)?',    PRIV_PROTOCOL_DRAFT_3DESEDE,
+      quotemeta PRIV_PROTOCOL_DRAFT_3DESEDE,    PRIV_PROTOCOL_DRAFT_3DESEDE,
+      '(?:(?:cfb)?128-?)?aes(?:-?128)?',        PRIV_PROTOCOL_AESCFB128,
+      quotemeta PRIV_PROTOCOL_AESCFB128,        PRIV_PROTOCOL_AESCFB128,
+      quotemeta PRIV_PROTOCOL_DRAFT_AESCFB128,  PRIV_PROTOCOL_AESCFB128,
+      '(?:(?:cfb)?192-?)aes(?:-?128)?',         PRIV_PROTOCOL_DRAFT_AESCFB192,
+      quotemeta PRIV_PROTOCOL_DRAFT_AESCFB192,  PRIV_PROTOCOL_DRAFT_AESCFB192,
+      '(?:(?:cfb)?256-?)aes(?:-?128)?',         PRIV_PROTOCOL_DRAFT_AESCFB256,
+      quotemeta PRIV_PROTOCOL_DRAFT_AESCFB256,  PRIV_PROTOCOL_DRAFT_AESCFB256,
+   };
+
+   sub _priv_protocol
+   {
+      my ($this, $proto) = @_;
+
+      if (@_ < 2) {
+         return $this->{_priv_protocol};
+      }
+
+      if ($proto eq q{}) {
+         return $this->_error('An empty privProtocol was specified');
+      }
+
+      my $priv_proto;
+
+      for (keys %{$protocols}) {
+         if ($proto =~ /^$_$/i) {
+            $priv_proto = $protocols->{$_};
+            last;
+         }
+      }
+
+      if (!defined $priv_proto) {
+         return $this->_error('The privProtocol "%s" is unknown', $proto);
+      }
+
+      # Validate the support of the AES cipher algorithm.  Attempt to 
+      # load the Crypt::Rijndael module.  If this module is not found, 
+      # do not provide support for the AES Cipher Algorithm.
+
+      if (($priv_proto eq PRIV_PROTOCOL_AESCFB128)       ||
+          ($priv_proto eq PRIV_PROTOCOL_DRAFT_AESCFB192) ||
+          ($priv_proto eq PRIV_PROTOCOL_DRAFT_AESCFB256))
+      {
+         if (defined (my $error = load_module('Crypt::Rijndael'))) {
+            return $this->_error(
+               'Support for privProtocol "%s" is unavailable %s', $proto, $error
+            );
+         }
+      }
+
+      return $this->{_priv_protocol} = $priv_proto;
+   }
+
+}
+
+sub _engine_boots
+{
+   return ($_[0]->{_synchronized}) ? $_[0]->{_engine_boots} : 0;
+}
+
+sub _engine_time
+{
+   my ($this) = @_;
+
+   return 0 if (!$this->{_synchronized});
+
+   $this->{_engine_time} = time() - $this->{_time_epoc};
+
+   if ($this->{_engine_time} > 2147483647) {
+      DEBUG_INFO('snmpEngineTime rollover');
+      if (++$this->{_engine_boots} == 2147483647) {
+         die 'FATAL: Unable to handle snmpEngineBoots value';
+      }
+      $this->{_engine_time} -= 2147483647;
+      $this->{_time_epoc} = time() - $this->{_engine_time};
+      if (!$this->{_authoritative}) {
+         $this->{_synchronized} = FALSE;
+         return $this->{_latest_engine_time} = 0;
+      }
+   }
+
+   if ($this->{_engine_time} < 0) {
+      die 'FATAL: Unable to handle negative snmpEngineTime value';
+   }
+
+   return $this->{_engine_time};
+}
+
+sub _security_params
+{
+   my ($this) = @_;
+
+   # Clear any previous error messages
+   $this->_error_clear();
+
+   # We must have an usmUserName
+   if ($this->{_user_name} eq q{}) {
+      return $this->_error('The required userName was not specified');
+   }
+
+   # Define the authentication parameters
+
+   if ((defined $this->{_auth_password}) && ($this->{_discovered})) {
+      if (!defined $this->{_auth_key}) {
+         return $this->_error() if !defined $this->_auth_key_generate();
+      }
+      $this->{_auth_password} = undef;
+   }
+
+   if (defined $this->{_auth_key}) {
+
+      # Validate the key based on the protocol
+      if (!defined $this->_auth_key_validate()) {
+         return $this->_error('The authKey is invalid');
+      }
+
+      # Initialize the authentication data 
+      if (!defined $this->_auth_data_init()) {
+         return $this->_error('Failed to initialize the authentication data');
+      }
+
+      if ($this->{_discovered}) {
+         $this->{_security_level} = SECURITY_LEVEL_AUTHNOPRIV;
+      }
+
+   }
+
+   # You must have authentication to have privacy
+
+   if (!defined ($this->{_auth_key}) && !defined $this->{_auth_password}) {
+      if (defined ($this->{_priv_key}) || defined $this->{_priv_password}) {
+         return $this->_error(
+            'The securityLevel is unsupported (privacy requires authentication)'
+         );
+      }
+   }
+
+   # Define the privacy parameters
+
+   if ((defined $this->{_priv_password}) && ($this->{_discovered})) {
+      if (!defined $this->{_priv_key}) {
+         return $this->_error() if !defined $this->_priv_key_generate();
+      }
+      $this->{_priv_password} = undef;
+   }
+
+   if (defined $this->{_priv_key}) {
+
+      # Validate the key based on the protocol
+      if (!defined $this->_priv_key_validate()) {
+         return $this->_error('The privKey is invalid');
+      }
+
+      # Initialize the privacy data 
+      if (!defined $this->_priv_data_init()) {
+         return $this->_error('Failed to initialize the privacy data');
+      }
+
+      if ($this->{_discovered}) {
+         $this->{_security_level} = SECURITY_LEVEL_AUTHPRIV;
+      }
+
+   }
+
+   DEBUG_INFO('securityLevel = %d', $this->{_security_level});
+
+   return $this->{_security_level};
+}
+
+sub _engine_id_discovery
+{
+   my ($this, $engine_id) = @_;
+
+   return TRUE if ($this->{_authoritative});
+
+   DEBUG_INFO('engineID = 0x%s', unpack 'H*', $engine_id || q{});
+
+   if (length($engine_id) < 5 || length($engine_id) > 32) {
+      return $this->_error(
+         'The msgAuthoritativeEngineID length of %d is out of range (5..32)',
+         length $engine_id
+      );
+   }
+
+   $this->{_engine_id}  = $engine_id;
+   $this->{_discovered} = TRUE;
+
+   if (!defined $this->_security_params()) {
+      $this->{_discovered} = FALSE;
+      return $this->_error();
+   }
+
+   return TRUE;
+}
+
+sub _synchronize
+{
+   my ($this, $msg_boots, $msg_time) = @_;
+
+   return TRUE if ($this->{_authoritative});
+   return TRUE if ($this->{_security_level} < SECURITY_LEVEL_AUTHNOPRIV);
+
+   if (($msg_boots > $this->_engine_boots()) ||
+       (($msg_boots == $this->_engine_boots()) &&
+        ($msg_time > $this->{_latest_engine_time})))
+   {
+      DEBUG_INFO(
+         'update: engineBoots = %d, engineTime = %d', $msg_boots, $msg_time
+      );
+
+      $this->{_engine_boots} = $msg_boots;
+      $this->{_latest_engine_time} = $this->{_engine_time} = $msg_time;
+      $this->{_time_epoc} = time() - $this->{_engine_time};
+
+      if (!$this->{_synchronized}) {
+         $this->{_synchronized} = TRUE;
+         if (!defined $this->_security_params()) {
+            return ($this->{_synchronized} = FALSE);
+         }
+      }
+
+      return TRUE;
+   }
+
+   DEBUG_INFO(
+      'no update: engineBoots = %d, msgBoots = %d; ' .
+      'latestTime = %d, msgTime = %d',
+      $this->_engine_boots(), $msg_boots,
+      $this->{_latest_engine_time}, $msg_time
+   );
+
+   return TRUE;
+}
+
+sub _timeliness
+{
+   my ($this, $msg_boots, $msg_time) = @_;
+
+   return TRUE if ($this->{_security_level} < SECURITY_LEVEL_AUTHNOPRIV);
+
+   # Retrieve a local copy of our snmpEngineBoots and snmpEngineTime 
+   # to avoid the possibilty of using different values in each of 
+   # the comparisons.
+
+   my $engine_time  = $this->_engine_time();
+   my $engine_boots = $this->_engine_boots();
+
+   if ($engine_boots == 2147483647) {
+      $this->{_synchronized} = FALSE;
+      return $this->_error('The system is not in the time window');
+   }
+
+   if (!$this->{_authoritative}) {
+
+      if ($msg_boots < $engine_boots) {
+         return $this->_error('The message is not in the time window');
+      }
+      if (($msg_boots == $engine_boots) && ($msg_time < ($engine_time - 150))) {
+         return $this->_error('The message is not in the time window');
+      }
+
+   } else {
+
+      if ($msg_boots != $engine_boots) {
+         return $this->_error('The message is not in the time window');
+      }
+      if (($msg_time < ($engine_time - 150)) ||
+          ($msg_time > ($engine_time + 150)))
+      {
+         return $this->_error('The message is not in the time window');
+      }
+
+   }
+
+   return TRUE;
+}
+
+sub _authenticate_outgoing_msg
+{
+   my ($this, $msg, $auth_location) = @_;
+
+   if (!$auth_location) {
+      return $this->_error(
+         'Authentication failure (Unable to set msgAuthenticationParameters)'
+      );
+   }
+
+   # Set the msgAuthenticationParameters
+   substr ${$msg->reference}, -$auth_location, $this->{_auth_maclen}, $this->_auth_hmac($msg);
+   return TRUE;
+}
+
+sub _authenticate_incoming_msg
+{
+   my ($this, $msg, $auth_params) = @_;
+
+   # Authenticate the message
+   if ($auth_params ne $this->_auth_hmac($msg)) {
+      return $this->_error('Authentication failure');
+   }
+
+   DEBUG_INFO('authentication passed');
+
+   return TRUE;
+}
+
+sub _auth_hmac
+{
+   my ($this, $msg) = @_;
+
+   return q{} if (!defined($this->{_auth_data}) || !defined $msg);
+
+   return substr
+      $this->{_auth_data}(${$msg->reference()}, $this->{_auth_key}), 0, $this->{_auth_maclen};
+}
+
+sub _auth_data_init
+{
+   my ($this) = @_;
+
+   if (!defined $this->{_auth_key}) {
+      return $this->_error('The required authKey is not defined');
+   }
+
+   return TRUE if defined $this->{_auth_data};
+
+   if ($this->{_auth_protocol} eq AUTH_PROTOCOL_HMACMD5) {
+
+      $this->{_auth_data} = \&hmac_md5;
+      $this->{_auth_maclen} = 12;
+
+   } elsif ($this->{_auth_protocol} eq AUTH_PROTOCOL_HMACSHA) {
+
+      $this->{_auth_data} = \&hmac_sha1;
+      $this->{_auth_maclen} = 12;
+
+   } elsif ($this->{_auth_protocol} eq AUTH_PROTOCOL_HMACSHA224) {
+
+      $this->{_auth_data} = \&hmac_sha224;
+      $this->{_auth_maclen} = 16;
+
+   } elsif ($this->{_auth_protocol} eq AUTH_PROTOCOL_HMACSHA256) {
+
+      $this->{_auth_data} = \&hmac_sha256;
+      $this->{_auth_maclen} = 24;
+
+   } elsif ($this->{_auth_protocol} eq AUTH_PROTOCOL_HMACSHA384) {
+
+      $this->{_auth_data} = \&hmac_sha384;
+      $this->{_auth_maclen} = 32;
+
+   } elsif ($this->{_auth_protocol} eq AUTH_PROTOCOL_HMACSHA512) {
+
+      $this->{_auth_data} = \&hmac_sha512;
+      $this->{_auth_maclen} = 48;
+
+   } else {
+
+      return $this->_error(
+         'The authProtocol "%s" is unknown', $this->{_auth_protocol}
+      );
+
+   }
+
+   return TRUE;
+}
+
+{
+   my $encrypt =
+   {
+      PRIV_PROTOCOL_DES,              \&_priv_encrypt_des,
+      PRIV_PROTOCOL_DRAFT_3DESEDE,    \&_priv_encrypt_3desede,
+      PRIV_PROTOCOL_AESCFB128,        \&_priv_encrypt_aescfbxxx,
+      PRIV_PROTOCOL_DRAFT_AESCFB192,  \&_priv_encrypt_aescfbxxx,
+      PRIV_PROTOCOL_DRAFT_AESCFB256,  \&_priv_encrypt_aescfbxxx
+   };
+
+   sub _encrypt_data
+   {
+   #  my ($this, $msg, $priv_params, $plain) = @_;
+
+      if (!exists $encrypt->{$_[0]->{_priv_protocol}}) {
+         return $_[0]->_error('Encryption error (Unknown protocol)');
+      }
+
+      if (!defined
+            $_[1]->prepare(
+               OCTET_STRING,
+               $_[0]->${\$encrypt->{$_[0]->{_priv_protocol}}}($_[2], $_[3])
+            )
+         )
+      {
+         return $_[0]->_error('Encryption error');
+      }
+
+      # Set the PDU buffer equal to the encryptedPDU
+      return $_[3] = $_[1]->clear();
+   }
+}
+
+{
+   my $decrypt =
+   {
+      PRIV_PROTOCOL_DES,              \&_priv_decrypt_des,
+      PRIV_PROTOCOL_DRAFT_3DESEDE,    \&_priv_decrypt_3desede,
+      PRIV_PROTOCOL_AESCFB128,        \&_priv_decrypt_aescfbxxx,
+      PRIV_PROTOCOL_DRAFT_AESCFB192,  \&_priv_decrypt_aescfbxxx,
+      PRIV_PROTOCOL_DRAFT_AESCFB256,  \&_priv_decrypt_aescfbxxx
+   };
+
+   sub _decrypt_data
+   {
+   #  my ($this, $msg, $priv_params, $cipher) = @_;
+
+      # Make sure there is data to decrypt.
+      if (!defined $_[3]) {
+         return $_[0]->_error($_[1]->error() || 'Decryption error (No data)');
+      }
+
+      if (!exists $decrypt->{$_[0]->{_priv_protocol}}) {
+         return $_[0]->_error('Decryption error (Unknown protocol)');
+      }
+
+      # Clear the Message buffer
+      $_[1]->clear();
+
+      # Put the decrypted data back into the Message buffer
+      if (!defined
+            $_[1]->prepend(
+               $_[0]->${\$decrypt->{$_[0]->{_priv_protocol}}}($_[2], $_[3])
+            )
+         )
+      {
+         return $_[0]->_error($_[1]->error());
+      }
+      return $_[0]->_error($_[1]->error()) if (!$_[1]->length());
+
+      # See if the decrypted data starts with a SEQUENCE 
+      # and has a reasonable length.
+
+      my $msglen = $_[1]->process(SEQUENCE);
+      if ((!defined $msglen) || ($msglen > $_[1]->length())) {
+         return $_[0]->_error('Decryption error');
+      }
+      $_[1]->index(0); # Reset the index
+
+      DEBUG_INFO('privacy passed');
+
+      return TRUE;
+   }
+}
+
+sub _priv_data_init
+{
+   my ($this) = @_;
+
+   if (!defined $this->{_priv_key}) {
+      return $this->_error('The required privKey is not defined');
+   }
+
+   return TRUE if defined $this->{_priv_data};
+
+   my $init =
+   {
+      PRIV_PROTOCOL_DES,              \&_priv_data_init_des,
+      PRIV_PROTOCOL_DRAFT_3DESEDE,    \&_priv_data_init_3desede,
+      PRIV_PROTOCOL_AESCFB128,        \&_priv_data_init_aescfbxxx,
+      PRIV_PROTOCOL_DRAFT_AESCFB192,  \&_priv_data_init_aescfbxxx,
+      PRIV_PROTOCOL_DRAFT_AESCFB256,  \&_priv_data_init_aescfbxxx
+   };
+
+   if (!exists $init->{$this->{_priv_protocol}}) {
+      return $this->_error(
+         'The privProtocol "%s" is unknown', $this->{_priv_protocol}
+      );
+   }
+
+   return $this->${\$init->{$this->{_priv_protocol}}}();
+}
+
+sub _priv_data_init_des
+{
+   my ($this) = @_;
+
+   if (!defined $this->{_priv_key}) {
+      return $this->_error('The required privKey is not defined');
+   }
+
+   # Create the DES object
+   $this->{_priv_data}->{des} =
+      Crypt::DES->new(substr $this->{_priv_key}, 0, 8);
+
+   # Extract the pre-IV
+   $this->{_priv_data}->{pre_iv} = substr $this->{_priv_key}, 8, 8;
+
+   # Initialize the salt
+   $this->{_priv_data}->{salt} = int rand ~0;
+
+   return TRUE;
+}
+
+sub _priv_encrypt_des
+{
+#  my ($this, $priv_params, $plain) = @_;
+
+   if (!defined $_[0]->{_priv_data}) {
+      return $_[0]->_error('The required privacy data is not defined');
+   }
+
+   # Always pad the plain text data.  "The actual pad value is 
+   # irrelevant..." according RFC 3414 Section 8.1.1.2.  However,
+   # there are some agents out there that expect "standard block
+   # padding" where each of the padding byte(s) are set to the size 
+   # of the padding (even for data that is a multiple of block size).
+
+   my $pad = 8 - (length($_[2]) % 8);
+   $_[2] .= pack('C', $pad) x $pad;
+
+   # Create and set the salt
+   if ($_[0]->{_priv_data}->{salt}++ == ~0) {
+      $_[0]->{_priv_data}->{salt} = 0;
+   }
+   $_[1] = pack 'NN', $_[0]->{_engine_boots}, $_[0]->{_priv_data}->{salt};
+
+   # Create the initial vector (IV)
+   my $iv = $_[0]->{_priv_data}->{pre_iv} ^ $_[1];
+
+   my $cipher = q{};
+
+   # Perform Cipher Block Chaining (CBC) 
+   while ($_[2] =~ /(.{8})/gs) {
+      $cipher .= $iv = $_[0]->{_priv_data}->{des}->encrypt($1 ^ $iv);
+   }
+
+   return $cipher;
+}
+
+sub _priv_decrypt_des
+{
+#  my ($this, $priv_params, $cipher) = @_;
+
+   if (!defined $_[0]->{_priv_data}) {
+      return $_[0]->_error('The required privacy data is not defined');
+   }
+
+   if (length($_[1]) != 8) {
+      return $_[0]->_error(
+        'The msgPrivParameters length of %d is invalid', length $_[1]
+      );
+   }
+
+   if (length($_[2]) % 8) {
+      return $_[0]->_error(
+         'The DES cipher length is not a multiple of the block size'
+      );
+   }
+
+   # Create the initial vector (IV)
+   my $iv = $_[0]->{_priv_data}->{pre_iv} ^ $_[1];
+
+   my $plain = q{};
+
+   # Perform Cipher Block Chaining (CBC) 
+   while ($_[2] =~ /(.{8})/gs) {
+      $plain .= $iv ^ $_[0]->{_priv_data}->{des}->decrypt($1);
+      $iv = $1;
+   }
+
+   return $plain;
+}
+
+sub _priv_data_init_3desede
+{
+   my ($this) = @_;
+
+   if (!defined $this->{_priv_key}) {
+      return $this->_error('The required privKey is not defined');
+   }
+
+   # Create the 3 DES objects
+
+   $this->{_priv_data}->{des1} =
+      Crypt::DES->new(substr $this->{_priv_key}, 0, 8);
+   $this->{_priv_data}->{des2} =
+      Crypt::DES->new(substr $this->{_priv_key}, 8, 8);
+   $this->{_priv_data}->{des3} =
+      Crypt::DES->new(substr $this->{_priv_key}, 16, 8);
+
+   # Extract the pre-IV
+   $this->{_priv_data}->{pre_iv} = substr $this->{_priv_key}, 24, 8;
+
+   # Initialize the salt
+   $this->{_priv_data}->{salt} = int rand ~0;
+
+   # Assign a hash algorithm to "bit spread" the salt
+
+   if ($this->{_auth_protocol} eq AUTH_PROTOCOL_HMACMD5) {
+      $this->{_priv_data}->{hash} = Digest::MD5->new();
+   } elsif ($this->{_auth_protocol} eq AUTH_PROTOCOL_HMACSHA) {
+      $this->{_priv_data}->{hash} = Digest::SHA->new();
+   }
+
+   return TRUE;
+}
+
+sub _priv_encrypt_3desede
+{
+#  my ($this, $priv_params, $plain) = @_;
+
+   if (!defined $_[0]->{_priv_data}) {
+      return $_[0]->_error('The required privacy data is not defined');
+   }
+
+   # Pad the plain text data using "standard block padding". 
+   my $pad = 8 - (length($_[2]) % 8);
+   $_[2] .= pack('C', $pad) x $pad;
+
+   # Create and set the salt
+   if ($_[0]->{_priv_data}->{salt}++ == ~0) {
+      $_[0]->{_priv_data}->{salt} = 0;
+   }
+   $_[1] = pack 'NN', $_[0]->{_engine_boots}, $_[0]->{_priv_data}->{salt};
+
+   # Draft 3DES-EDE for USM Section 5.1.1.1.2 - "To achieve effective 
+   # bit spreading, the complete 8-octet 'salt' value SHOULD be 
+   # hashed using the usmUserAuthProtocol."
+
+   if (exists $_[0]->{_priv_data}->{hash}) {
+      $_[1] = substr $_[0]->{_priv_data}->{hash}->add($_[1])->digest(), 0, 8;
+   }
+
+   # Create the initial vector (IV)
+   my $iv = $_[0]->{_priv_data}->{pre_iv} ^ $_[1];
+
+   my $cipher = q{};
+
+   # Perform Cipher Block Chaining (CBC)
+   while ($_[2] =~ /(.{8})/gs) {
+      $cipher .= $iv =
+         $_[0]->{_priv_data}->{des3}->encrypt(
+            $_[0]->{_priv_data}->{des2}->decrypt(
+               $_[0]->{_priv_data}->{des1}->encrypt($1 ^ $iv)
+            )
+         );
+   }
+
+   return $cipher;
+}
+
+sub _priv_decrypt_3desede
+{
+#  my ($this, $priv_params, $cipher) = @_;
+
+   if (!defined $_[0]->{_priv_data}) {
+      return $_[0]->_error('The required privacy data is not defined');
+   }
+
+   if (length($_[1]) != 8) {
+      return $_[0]->_error(
+        'The msgPrivParameters length of %d is invalid', length $_[1]
+      );
+   }
+
+   if (length($_[2]) % 8) {
+      return $_[0]->_error(
+         'The CBC-3DES-EDE cipher length is not a multiple of the block size'
+      );
+   }
+
+   # Create the initial vector (IV)
+   my $iv = $_[0]->{_priv_data}->{pre_iv} ^ $_[1];
+
+   my $plain = q{};
+
+   # Perform Cipher Block Chaining (CBC) 
+   while ($_[2] =~ /(.{8})/gs) {
+      $plain .=
+         $iv ^ $_[0]->{_priv_data}->{des1}->decrypt(
+                  $_[0]->{_priv_data}->{des2}->encrypt(
+                     $_[0]->{_priv_data}->{des3}->decrypt($1)
+                  )
+               );
+      $iv = $1;
+   }
+
+   return $plain;
+}
+
+sub _priv_data_init_aescfbxxx
+{
+   my ($this) = @_;
+
+   if (!defined $this->{_priv_key}) {
+      return $this->_error('The required privKey is not defined');
+   }
+
+   {
+      # Avoid a "strict subs" error if Crypt::Rijndael is not loaded.
+      no strict 'subs';
+
+      # Create the AES (Rijndael) object with a 128, 192, or 256 bit key.
+
+      $this->{_priv_data}->{aes} =
+         Crypt::Rijndael->new($this->{_priv_key}, Crypt::Rijndael::MODE_CFB());
+   }
+
+   # Initialize the salt
+   $this->{_priv_data}->{salt1} = int rand ~0;
+   $this->{_priv_data}->{salt2} = int rand ~0;
+
+   return TRUE;
+}
+
+sub _priv_encrypt_aescfbxxx
+{
+#  my ($this, $priv_params, $plain) = @_;
+
+   if (!defined $_[0]->{_priv_data}) {
+      return $_[0]->_error('The required privacy data is not defined');
+   }
+
+   # Validate the plain text length
+   my $length = length $_[2];
+   if ($length <= 16) {
+      return $_[0]->_error(
+         'The AES plain text length is not greater than the block size'
+      );
+   }
+
+   # Create and set the salt
+   if ($_[0]->{_priv_data}->{salt1}++ == ~0) {
+      $_[0]->{_priv_data}->{salt1} = 0;
+      if ($_[0]->{_priv_data}->{salt2}++ == ~0) {
+         $_[0]->{_priv_data}->{salt2} = 0;
+      }
+   }
+   $_[1] = pack 'NN', $_[0]->{_priv_data}->{salt2},
+                      $_[0]->{_priv_data}->{salt1};
+
+   # AES in the USM Section - Section 3.1.3 "The last ciphertext 
+   # block is produced by exclusive-ORing the last plaintext segment 
+   # of r bits (r is less or equal to 128) with the segment of the r 
+   # most significant bits of the last output block."  
+
+   # This operation is identical to those performed on the previous 
+   # blocks except for the fact that the block can be less than the 
+   # block size.  We can just pad the last block and operate on it as 
+   # usual and then ignore the padding after encrypting.
+
+   $_[2] .= "\000" x (16 - ($length % 16));
+
+   # Create the IV by concatenating "...the generating SNMP engine's 
+   # 32-bit snmpEngineBoots, the SNMP engine's 32-bit  snmpEngineTime, 
+   # and a local 64-bit integer..." 
+
+   $_[0]->{_priv_data}->{aes}->set_iv(
+      pack('NN', $_[0]->{_engine_boots}, $_[0]->{_engine_time}) . $_[1]
+   );
+
+   # Let the Crypt::Rijndael module perform 128 bit Cipher Feedback 
+   # (CFB) and return the result minus the "internal" padding.
+
+   return substr $_[0]->{_priv_data}->{aes}->encrypt($_[2]), 0, $length;
+}
+
+sub _priv_decrypt_aescfbxxx
+{
+#  my ($this, $priv_params, $cipher) = @_;
+
+   if (!defined $_[0]->{_priv_data}) {
+      return $_[0]->_error('The required privacy data is not defined');
+   }
+
+   # Validate the msgPrivParameters length.  We assume that the
+   # msgAuthoritativeEngineBoots and msgAuthoritativeEngineTime
+   # have been prepended to the msgPrivParameters to create the
+   # required 128 bit IV.
+
+   if (length($_[1]) != 16) {
+       return $_[0]->_error(
+          'The AES IV length of %d is invalid', length $_[1]
+       );
+   }
+
+   # Validate the cipher length
+   my $length = length $_[2];
+   if ($length <= 16) {
+      return $_[0]->_error(
+         'The AES cipher length is not greater than the block size'
+      );
+   }
+
+   # AES in the USM Section - Section 3.1.4 "The last ciphertext 
+   # block (whose size r is less or equal to 128) is less or equal 
+   # to 128) is exclusive-ORed with the segment of the r most 
+   # significant bits of the last output block to recover the last 
+   # plaintext block of r bits."
+
+   # This operation is identical to those performed on the previous
+   # blocks except for the fact that the block can be less than the
+   # block size.  We can just pad the last block and operate on it as
+   # usual and then ignore the padding after decrypting.
+
+   $_[2] .= "\000" x (16 - ($length % 16));
+
+   # Use the msgPrivParameters as the IV.
+   $_[0]->{_priv_data}->{aes}->set_iv($_[1]);
+
+   # Let the Crypt::Rijndael module perform 128 bit Cipher Feedback
+   # (CFB) and return the result minus the "internal" padding.
+
+   return substr $_[0]->{_priv_data}->{aes}->decrypt($_[2]), 0, $length;
+}
+
+sub _auth_key_generate
+{
+   my ($this) = @_;
+
+   if (!defined($this->{_engine_id}) || !defined $this->{_auth_password}) {
+      return $this->_error('Unable to generate the authKey');
+   }
+
+   $this->{_auth_key} = $this->_password_localize($this->{_auth_password});
+
+   return $this->{_auth_key};
+}
+
+sub _auth_key_validate
+{
+   my ($this) = @_;
+
+   my $key_len =
+   {
+      AUTH_PROTOCOL_HMACMD5,    [ 16, 'HMAC-MD5'  ],
+      AUTH_PROTOCOL_HMACSHA,    [ 20, 'HMAC-SHA' ],
+      AUTH_PROTOCOL_HMACSHA224, [ 28, 'HMAC-SHA224' ],
+      AUTH_PROTOCOL_HMACSHA256, [ 32, 'HMAC-SHA256' ],
+      AUTH_PROTOCOL_HMACSHA384, [ 48, 'HMAC-SHA384' ],
+      AUTH_PROTOCOL_HMACSHA512, [ 64, 'HMAC-SHA512' ],
+   };
+
+   if (!exists $key_len->{$this->{_auth_protocol}}) {
+      return $this->_error(
+         'The authProtocol "%s" is unknown', $this->{_auth_protocol}
+      );
+   }
+
+   if (length($this->{_auth_key}) != $key_len->{$this->{_auth_protocol}}->[0])
+   {
+      return $this->_error(
+         'The %s authKey length of %d is invalid, expected %d',
+         $key_len->{$this->{_auth_protocol}}->[1], length($this->{_auth_key}),
+         $key_len->{$this->{_auth_protocol}}->[0]
+      );
+   }
+
+   return TRUE;
+}
+
+sub _priv_key_generate
+{
+   my ($this) = @_;
+
+   if (!defined($this->{_engine_id}) || !defined $this->{_priv_password}) {
+      return $this->_error('Unable to generate the privKey');
+   }
+
+   $this->{_priv_key} = $this->_password_localize($this->{_priv_password});
+
+   return $this->_error() if !defined $this->{_priv_key};
+
+   if ($this->{_priv_protocol} eq PRIV_PROTOCOL_DRAFT_3DESEDE) {
+
+      # Draft 3DES-EDE for USM Section 2.1 - "To acquire the necessary 
+      # number of key bits, the password-to-key algorithm may be chained
+      # using its output as further input in order to generate an
+      # appropriate number of key bits."
+
+      $this->{_priv_key} .= $this->_password_localize($this->{_priv_key});
+
+   } elsif (($this->{_priv_protocol} eq PRIV_PROTOCOL_DRAFT_AESCFB192) ||
+            ($this->{_priv_protocol} eq PRIV_PROTOCOL_DRAFT_AESCFB256))
+   {
+      # Draft AES in the USM Section 3.1.2.1 - "...if the size of the 
+      # localized key is not large enough to generate an encryption 
+      # key... ...set Kul = Kul || Hnnn(Kul) where Hnnn is the hash 
+      # function for the authentication protocol..."
+
+      my $hnnn;
+
+      if ($this->{_auth_protocol} eq AUTH_PROTOCOL_HMACMD5) {
+         $hnnn = Digest::MD5->new();
+      } elsif ($this->{_auth_protocol} eq AUTH_PROTOCOL_HMACSHA) {
+         $hnnn = Digest::SHA->new();
+      } else {
+         return $this->_error(
+            'The authProtocol "%s" is unknown', $this->{_auth_protocol}
+         );
+      }
+
+      $this->{_priv_key} .= $hnnn->add($this->{_priv_key})->digest();
+
+   }
+
+   # Truncate the privKey to the appropriate length.
+
+   my $key_len =
+   {
+      PRIV_PROTOCOL_DES,              16,  # RFC 3414 Section 8.2.1
+      PRIV_PROTOCOL_DRAFT_3DESEDE,    32,  # Draft 3DES for USM Section 5.2.1
+      PRIV_PROTOCOL_AESCFB128,        16,  # AES in the USM Section 3.2.1
+      PRIV_PROTOCOL_DRAFT_AESCFB192,  24,  # Draft AES in the USM Section 3.2.1
+      PRIV_PROTOCOL_DRAFT_AESCFB256,  32   # Draft AES in the USM Section 3.2.1
+   };
+
+   if (!exists $key_len->{$this->{_priv_protocol}}) {
+      return $this->_error(
+         'The privProtocol "%s" is unknown', $this->{_priv_protocol}
+      );
+   }
+
+   $this->{_priv_key} =
+      substr $this->{_priv_key}, 0, $key_len->{$this->{_priv_protocol}};
+
+   return $this->{_priv_key};
+}
+
+sub _priv_key_validate
+{
+   my ($this) = @_;
+
+   my $key_len =
+   {
+      PRIV_PROTOCOL_DES,              [ 16, 'CBC-DES'        ],
+      PRIV_PROTOCOL_DRAFT_3DESEDE,    [ 32, 'CBC-3DES-EDE'   ],
+      PRIV_PROTOCOL_AESCFB128,        [ 16, 'CFB128-AES-128' ],
+      PRIV_PROTOCOL_DRAFT_AESCFB192,  [ 24, 'CFB128-AES-192' ],
+      PRIV_PROTOCOL_DRAFT_AESCFB256,  [ 32, 'CFB128-AES-256' ]
+   };
+
+   if (!exists $key_len->{$this->{_priv_protocol}}) {
+      return $this->_error(
+         'The privProtocol "%s" is unknown', $this->{_priv_protocol}
+      );
+   }
+
+   if (length($this->{_priv_key}) != $key_len->{$this->{_priv_protocol}}->[0])
+   {
+      return $this->_error(
+         'The %s privKey length of %d is invalid, expected %d',
+         $key_len->{$this->{_priv_protocol}}->[1], length($this->{_priv_key}),
+         $key_len->{$this->{_priv_protocol}}->[0]
+      );
+   }
+
+   if ($this->{_priv_protocol} eq PRIV_PROTOCOL_DRAFT_3DESEDE) {
+
+      # Draft 3DES-EDE for USM Section 5.1.1.1.1 "The checks for difference 
+      # and weakness... ...should be performed when the key is assigned.
+      # If any of the mandated tests fail, then the whole key MUST be 
+      # discarded and an appropriate exception noted."
+
+      if (substr($this->{_priv_key}, 0, 8) eq substr $this->{_priv_key}, 8, 8)
+      {
+         return $this->_error(
+            'The CBC-3DES-EDE privKey is invalid (K1 equals K2)'
+         );
+      }
+
+      if (substr($this->{_priv_key}, 8, 8) eq substr $this->{_priv_key}, 16, 8)
+      {
+         return $this->_error(
+            'The CBC-3DES-EDE privKey is invalid (K2 equals K3)'
+         );
+      }
+
+      if (substr($this->{_priv_key}, 0, 8) eq substr $this->{_priv_key}, 16, 8)
+      {
+         return $this->_error(
+            'The CBC-3DES-EDE privKey is invalid (K1 equals K3)'
+         );
+      }
+
+   }
+
+   return TRUE;
+}
+
+sub _password_localize
+{
+   my ($this, $password) = @_;
+
+   my $digests =
+   {
+      AUTH_PROTOCOL_HMACMD5,    ['Digest::MD5', ],
+      AUTH_PROTOCOL_HMACSHA,    ['Digest::SHA', 1],
+      AUTH_PROTOCOL_HMACSHA224, ['Digest::SHA', 224],
+      AUTH_PROTOCOL_HMACSHA256, ['Digest::SHA', 256],
+      AUTH_PROTOCOL_HMACSHA384, ['Digest::SHA', 384],
+      AUTH_PROTOCOL_HMACSHA512, ['Digest::SHA', 512],
+  };
+
+   if (!exists $digests->{$this->{_auth_protocol}}) {
+      return $this->_error(
+         'The authProtocol "%s" is unknown', $this->{_auth_protocol}
+      );
+   }
+
+   my $digest;
+   if (!defined($digests->{$this->{_auth_protocol}}[1])) {
+       $digest = $digests->{$this->{_auth_protocol}}[0]->new;
+       } else {
+       $digest = $digests->{$this->{_auth_protocol}}[0]->new($digests->{$this->{_auth_protocol}}[1]);
+       }
+
+   # Create the initial digest using the password
+
+   my $d = my $pad = $password x ((2048 / length $password) + 1);
+
+   for (my $count = 0; $count < 2**20; $count += 2048) {
+      $digest->add(substr $d, 0, 2048, q{});
+      $d .= $pad;
+   }
+   $d = $digest->digest;
+
+   # Localize the key with the authoritativeEngineID
+
+   return $digest->add($d . $this->{_engine_id} . $d)->digest();
+}
+
+{
+   my %modules;
+
+   sub load_module
+   {
+      my ($module) = @_;
+
+      # We attempt to load the required module under the protection of an
+      # eval statement.  If there is a failure, typically it is due to a
+      # missing module required by the requested module and we attempt to
+      # simplify the error message by just listing that module.  We also
+      # need to track failures since require() only produces an error on
+      # the first attempt to load the module.
+
+      # NOTE: Contrary to our typical convention, a return value of "undef"
+      # actually means success and a defined value means error.
+
+      return $modules{$module} if exists $modules{$module};
+
+      if (!eval "require $module") {
+         if ($@ =~ /locate (\S+\.pm)/) {
+            $modules{$module} = sprintf '(Required module %s not found)', $1;
+         } else {
+            $modules{$module} = sprintf '(%s)', $@;
+         }
+      } else {
+         $modules{$module} = undef;
+      }
+
+      return $modules{$module};
+   }
+}
+
+# ============================================================================
+1; # [end Net::SNMP::Security::USM]
+

+ 195 - 0
docs/patches/sha512.patch

@@ -0,0 +1,195 @@
+--- USM.pm.default	2025-01-24 17:51:21.777264690 +0300
++++ USM.pm	2025-01-24 18:03:43.338158040 +0300
+@@ -25,8 +25,8 @@
+ 
+ use Crypt::DES();
+ use Digest::MD5();
+-use Digest::SHA();
+-use Digest::HMAC();
++use Digest::SHA qw( hmac_sha1 hmac_sha224 hmac_sha256 hmac_sha384 hmac_sha512 );
++use Digest::HMAC_MD5 qw ( hmac_md5 );
+ 
+ ## Version of the Net::SNMP::Security::USM module
+ 
+@@ -40,7 +40,9 @@
+ 
+ our %EXPORT_TAGS = (
+    authprotos => [
+-      qw( AUTH_PROTOCOL_NONE AUTH_PROTOCOL_HMACMD5 AUTH_PROTOCOL_HMACSHA )
++      qw( AUTH_PROTOCOL_NONE AUTH_PROTOCOL_HMACMD5 AUTH_PROTOCOL_HMACSHA
++          AUTH_PROTOCOL_HMACSHA224 AUTH_PROTOCOL_HMACSHA256.
++          AUTH_PROTOCOL_HMACSHA384 AUTH_PROTOCOL_HMACSHA512 )
+    ],
+    levels     => [
+       qw( SECURITY_LEVEL_NOAUTHNOPRIV SECURITY_LEVEL_AUTHNOPRIV
+@@ -63,9 +65,13 @@
+ 
+ ## RCC 3414 - Authentication protocols
+ 
+-sub AUTH_PROTOCOL_NONE    { '1.3.6.1.6.3.10.1.1.1' } # usmNoAuthProtocol
+-sub AUTH_PROTOCOL_HMACMD5 { '1.3.6.1.6.3.10.1.1.2' } # usmHMACMD5AuthProtocol
+-sub AUTH_PROTOCOL_HMACSHA { '1.3.6.1.6.3.10.1.1.3' } # usmHMACSHAAuthProtocol
++sub AUTH_PROTOCOL_NONE       { '1.3.6.1.6.3.10.1.1.1' } # usmNoAuthProtocol
++sub AUTH_PROTOCOL_HMACMD5    { '1.3.6.1.6.3.10.1.1.2' } # usmHMACMD5AuthProtocol
++sub AUTH_PROTOCOL_HMACSHA    { '1.3.6.1.6.3.10.1.1.3' } # usmHMACSHAAuthProtocol
++sub AUTH_PROTOCOL_HMACSHA224 { '1.3.6.1.6.3.10.1.1.4' } # usmHMAC128SHA224AuthProtocol
++sub AUTH_PROTOCOL_HMACSHA256 { '1.3.6.1.6.3.10.1.1.5' } # usmHMAC192SHA256AuthProtocol
++sub AUTH_PROTOCOL_HMACSHA384 { '1.3.6.1.6.3.10.1.1.6' } # usmHMAC256SHA384AuthProtocol
++sub AUTH_PROTOCOL_HMACSHA512 { '1.3.6.1.6.3.10.1.1.7' } # usmHMAC384SHA512AuthProtocol
+ 
+ ## RFC 3414 - Privacy protocols
+ 
+@@ -124,6 +130,7 @@
+       '_time_epoc'          => time(),                # snmpEngineBoots epoc
+       '_user_name'          => q{},                   # securityName 
+       '_auth_data'          => undef,                 # Authentication data
++      '_auth_maclen'        => undef,                 # MAC length
+       '_auth_key'           => undef,                 # authKey 
+       '_auth_password'      => undef,                 # Authentication password 
+       '_auth_protocol'      => AUTH_PROTOCOL_HMACMD5, # authProtocol
+@@ -280,10 +287,10 @@
+    if ($pdu->security_level() > SECURITY_LEVEL_NOAUTHNOPRIV) {
+ 
+       # Save the location to fill in msgAuthenticationParameters later
+-      $auth_location = $msg->length() + 12 + length $pdu_buffer;
++      $auth_location = $msg->length() + $this->{_auth_maclen} + length $pdu_buffer;
+ 
+       # Set the msgAuthenticationParameters to all zeros
+-      $auth_params = pack 'x12';
++      $auth_params = pack "x$this->{_auth_maclen}";
+    }
+ 
+    if (!defined $msg->prepare(OCTET_STRING, $auth_params)) {
+@@ -418,12 +425,12 @@
+    # to compute the HMAC properly.
+ 
+    if (my $len = length $auth_params) {
+-      if ($len != 12) {
++      if ($len != $this->{_auth_maclen}) {
+          return $this->_error(
+             'The msgAuthenticationParameters length of %d is invalid', $len
+          );
+       }
+-      substr ${$msg->reference}, ($msg->index() - 12), 12, pack 'x12';
++         substr ${$msg->reference}, ($msg->index() - $this->{_auth_maclen}), $this->{_auth_maclen}, pack "x$this->{_auth_maclen}";
+    }
+ 
+    # msgPrivacyParameters::=OCTET STRING
+@@ -747,6 +754,18 @@
+       quotemeta AUTH_PROTOCOL_HMACMD5,   AUTH_PROTOCOL_HMACMD5,
+       '(?:hmac-)?sha(?:-?1|-96)?',       AUTH_PROTOCOL_HMACSHA,
+       quotemeta AUTH_PROTOCOL_HMACSHA,   AUTH_PROTOCOL_HMACSHA,
++      '(?:hmac-)?sha(?:-?224)',          AUTH_PROTOCOL_HMACSHA224,
++      'usmHMAC128SHA224AuthProtocol',    AUTH_PROTOCOL_HMACSHA224,
++      quotemeta AUTH_PROTOCOL_HMACSHA224,AUTH_PROTOCOL_HMACSHA224,
++      '(?:hmac-)?sha(?:-?256)',          AUTH_PROTOCOL_HMACSHA256,
++      'usmHMAC192SHA256AuthProtocol',    AUTH_PROTOCOL_HMACSHA256,
++      quotemeta AUTH_PROTOCOL_HMACSHA256,AUTH_PROTOCOL_HMACSHA256,
++      '(?:hmac-)?sha(?:-?384)',          AUTH_PROTOCOL_HMACSHA384,
++      'usmHMAC256SHA384AuthProtocol',    AUTH_PROTOCOL_HMACSHA384,
++      quotemeta AUTH_PROTOCOL_HMACSHA384,AUTH_PROTOCOL_HMACSHA384,
++      '(?:hmac-)?sha(?:-?512)',          AUTH_PROTOCOL_HMACSHA512,
++      'usmHMAC384SHA512AuthProtocol',    AUTH_PROTOCOL_HMACSHA512,
++      quotemeta AUTH_PROTOCOL_HMACSHA512,AUTH_PROTOCOL_HMACSHA512,
+    };
+ 
+    sub _auth_protocol
+@@ -1099,8 +1118,7 @@
+    }
+ 
+    # Set the msgAuthenticationParameters
+-   substr ${$msg->reference}, -$auth_location, 12, $this->_auth_hmac($msg);
+-
++   substr ${$msg->reference}, -$auth_location, $this->{_auth_maclen}, $this->_auth_hmac($msg);
+    return TRUE;
+ }
+ 
+@@ -1125,7 +1143,7 @@
+    return q{} if (!defined($this->{_auth_data}) || !defined $msg);
+ 
+    return substr
+-      $this->{_auth_data}->reset()->add(${$msg->reference()})->digest(), 0, 12;
++      $this->{_auth_data}(${$msg->reference()}, $this->{_auth_key}), 0, $this->{_auth_maclen};
+ }
+ 
+ sub _auth_data_init
+@@ -1140,13 +1158,33 @@
+ 
+    if ($this->{_auth_protocol} eq AUTH_PROTOCOL_HMACMD5) {
+ 
+-      $this->{_auth_data} =
+-         Digest::HMAC->new($this->{_auth_key}, 'Digest::MD5');
++      $this->{_auth_data} = \&hmac_md5;
++      $this->{_auth_maclen} = 12;
+ 
+    } elsif ($this->{_auth_protocol} eq AUTH_PROTOCOL_HMACSHA) {
+ 
+-      $this->{_auth_data} =
+-         Digest::HMAC->new($this->{_auth_key}, 'Digest::SHA');
++      $this->{_auth_data} = \&hmac_sha1;
++      $this->{_auth_maclen} = 12;
++
++   } elsif ($this->{_auth_protocol} eq AUTH_PROTOCOL_HMACSHA224) {
++
++      $this->{_auth_data} = \&hmac_sha224;
++      $this->{_auth_maclen} = 16;
++
++   } elsif ($this->{_auth_protocol} eq AUTH_PROTOCOL_HMACSHA256) {
++
++      $this->{_auth_data} = \&hmac_sha256;
++      $this->{_auth_maclen} = 24;
++
++   } elsif ($this->{_auth_protocol} eq AUTH_PROTOCOL_HMACSHA384) {
++
++      $this->{_auth_data} = \&hmac_sha384;
++      $this->{_auth_maclen} = 32;
++
++   } elsif ($this->{_auth_protocol} eq AUTH_PROTOCOL_HMACSHA512) {
++
++      $this->{_auth_data} = \&hmac_sha512;
++      $this->{_auth_maclen} = 48;
+ 
+    } else {
+ 
+@@ -1627,6 +1665,10 @@
+    {
+       AUTH_PROTOCOL_HMACMD5,    [ 16, 'HMAC-MD5'  ],
+       AUTH_PROTOCOL_HMACSHA,    [ 20, 'HMAC-SHA' ],
++      AUTH_PROTOCOL_HMACSHA224, [ 28, 'HMAC-SHA224' ],
++      AUTH_PROTOCOL_HMACSHA256, [ 32, 'HMAC-SHA256' ],
++      AUTH_PROTOCOL_HMACSHA384, [ 48, 'HMAC-SHA384' ],
++      AUTH_PROTOCOL_HMACSHA512, [ 64, 'HMAC-SHA512' ],
+    };
+ 
+    if (!exists $key_len->{$this->{_auth_protocol}}) {
+@@ -1782,9 +1824,13 @@
+ 
+    my $digests =
+    {
+-      AUTH_PROTOCOL_HMACMD5,  'Digest::MD5',
+-      AUTH_PROTOCOL_HMACSHA,  'Digest::SHA',
+-   };
++      AUTH_PROTOCOL_HMACMD5,    ['Digest::MD5', ],
++      AUTH_PROTOCOL_HMACSHA,    ['Digest::SHA', 1],
++      AUTH_PROTOCOL_HMACSHA224, ['Digest::SHA', 224],
++      AUTH_PROTOCOL_HMACSHA256, ['Digest::SHA', 256],
++      AUTH_PROTOCOL_HMACSHA384, ['Digest::SHA', 384],
++      AUTH_PROTOCOL_HMACSHA512, ['Digest::SHA', 512],
++  };
+ 
+    if (!exists $digests->{$this->{_auth_protocol}}) {
+       return $this->_error(
+@@ -1792,7 +1838,12 @@
+       );
+    }
+ 
+-   my $digest = $digests->{$this->{_auth_protocol}}->new;
++   my $digest;
++   if (!defined($digests->{$this->{_auth_protocol}}[1])) {
++       $digest = $digests->{$this->{_auth_protocol}}[0]->new;
++       } else {
++       $digest = $digests->{$this->{_auth_protocol}}[0]->new($digests->{$this->{_auth_protocol}}[1]);
++       }
+ 
+    # Create the initial digest using the password
+ 

+ 38 - 34
html/admin/devices/editdevice.php

@@ -54,6 +54,8 @@ if (isset($_POST["editdevice"]) and isset($id)) {
     //snmp
     if (isset($_POST["f_snmp_version"])) { $new['snmp_version'] = $_POST["f_snmp_version"] * 1; }
     if (isset($_POST["f_community"])) { $new['community'] = substr($_POST["f_community"], 0, 50); }
+    if (isset($_POST["f_snmp3_auth_proto"])) { $new['snmp3_auth_proto'] = trim(substr($_POST["f_snmp3_auth_proto"], 0, 10)); }
+    if (isset($_POST["f_snmp3_priv_proto"])) { $new['snmp3_priv_proto'] = trim(substr($_POST["f_snmp3_priv_proto"], 0, 10)); }
     if (isset($_POST["f_rw_community"])) { $new['rw_community'] = substr($_POST["f_rw_community"], 0, 50); }
     if (isset($_POST["f_snmp3_user_rw"])) { $new['snmp3_user_rw'] = substr($_POST["f_snmp3_user_rw"], 0, 20); }
     if (isset($_POST["f_snmp3_user_ro"])) { $new['snmp3_user_ro'] = substr($_POST["f_snmp3_user_ro"], 0, 20); }
@@ -188,50 +190,52 @@ if ($device['device_type']<=2) {
     print "<td class='data'><input type='text' name='f_control_port' value=".$device['control_port']."></td>\n";
     print "</tr>";
     //snmp settings & discovery & nagios
-    print "<tr><td>".WEB_snmp_version."</td><td>".WEB_network_discovery."</td><td>".WEB_nagios."</td><td>".WEB_device_save_netflow."</td></tr>";
-    print "<tr><td class='data'>"; print_snmp_select('f_snmp_version', $device['snmp_version']); print "</td>\n";
+    print "<tr><td>".WEB_network_discovery."</td><td>".WEB_nagios."</td><td>".WEB_device_save_netflow."</td><td></td></tr>";
+    print "<tr>";
     print "<td class='data'>"; print_qa_select('f_discovery', $device['discovery']); print "</td>\n";
     print "<td class='data'>"; print_qa_select('f_nagios', $device['nagios']); print "</td>\n";
     print "<td class='data'>"; print_qa_select('f_save_netflow', $device['netflow_save']); print "</td>\n";
-    print "</tr>";
-    if ($device['snmp_version'] ==3) {
-        print "<tr><td>".WEB_snmp_v3_user_ro."</td><td>".WEB_snmp_v3_user_rw."</td><td>".WEB_snmp_v3_ro_password."</td><td>".WEB_snmp_v3_rw_password."</td><td></td>";
-	    print "</tr><tr>";
+    print "<td class='data'></td></tr>";
+    }
+
+if ($device['snmp_version'] ==3) {
+        //snmp settings
+	print "<tr><td>".WEB_snmp_version."</td><td>".WEB_snmp_v3_auth_proto."</td><td>".WEB_snmp_v3_priv_proto."</td><td></td></tr>";
+        print "<tr><td class='data'>"; print_snmp_select('f_snmp_version', $device['snmp_version']); print "</td>\n";
+        print "<td class='data'>"; print_snmp_auth_proto_select('f_snmp3_auth_proto', $device['snmp3_auth_proto']); print "</td>\n";
+        print "<td class='data'>"; print_snmp_priv_proto_select('f_snmp3_priv_proto', $device['snmp3_priv_proto']); print "</td>\n";
+    	print "<td class='data'></td>";
+        print "</tr>";
+        print "<tr><td>".WEB_snmp_v3_user_ro."</td><td>".WEB_snmp_v3_ro_password."</td><td>".WEB_snmp_v3_user_rw."</td><td>".WEB_snmp_v3_rw_password."</td><td></td>";
+	print "</tr><tr>";
         print "<td class='data'><input type='text' name='f_snmp3_user_ro' value=".$device['snmp3_user_ro']."></td>\n";
-	    print "<td class='data'><input type='text' name='f_snmp3_user_rw' value=".$device['snmp3_user_rw']."></td>\n";
         print "<td class='data'><input type='text' name='f_snmp3_user_ro_password' value=".$device['snmp3_user_ro_password']."></td>\n";
-	    print "<td class='data'><input type='text' name='f_snmp3_user_rw_password' value=".$device['snmp3_user_rw_password']."></td>\n";
+	print "<td class='data'><input type='text' name='f_snmp3_user_rw' value=".$device['snmp3_user_rw']."></td>\n";
+	print "<td class='data'><input type='text' name='f_snmp3_user_rw_password' value=".$device['snmp3_user_rw_password']."></td>\n";
         print "</tr>\n";
+	} else {
+	print "<tr><td>".WEB_snmp_version."</td><td></td><td></td><td></td></tr>";
+        print "<tr><td class='data'>"; print_snmp_select('f_snmp_version', $device['snmp_version']); print "</td><td class='data' colspan=3></td>\n";
+        print "</tr>";
+	if ($device['snmp_version']>0) {
+            print "<tr><td>".WEB_snmp_community_ro."</td><td>".WEB_snmp_community_rw."</td><td></td><td></td></tr>";
+	    print "<tr>\n";
+    	    print "<td class='data'><input type='text' name='f_community' value=".$device['community']."></td>\n";
+	    print "<td class='data'><input type='text' name='f_rw_community' value=".$device['rw_community']."></td>\n";
+    	    print "<td class='data' colspan=2></td>";
+	    print "</tr>";
 	    }
-    print "<tr><td>".WEB_snmp_community_ro."</td><td>".WEB_snmp_community_rw."</td><td></td><td></td></tr>";
-    print "<tr>\n";
-    print "<td class='data'><input type='text' name='f_community' value=".$device['community']."></td>\n";
-    print "<td class='data'><input type='text' name='f_rw_community' value=".$device['rw_community']."></td>\n";
+	}
+
+//save button
+if ($device['snmp_version']>0) {
+    print "<tr><td colspan=2>".$device['ip']."::". get_device_model_name($db_link,$device['device_model_id'])."</td>";
     print "<td><button name='mac_walk' onclick=\"window.open('mactable.php?id=" . $id . "')\">".WEB_device_mac_table."</button>";
     print "<button name='port_walk' onclick=\"window.open('snmpwalk.php?id=" . $id . "')\">".WEB_device_walk_port_list."</button></td>";
-    print "<td></td>";
-    print "</tr>";
-    }
-
-//only snmp for other devices
-if ($device['device_type']>2) {
-    print "<tr><td>".WEB_snmp_version."</td><td>".WEB_snmp_community_ro."</td><td>".WEB_snmp_community_rw."</td><td></td></tr>";
-    print "<tr><td class='data'>"; print_snmp_select('f_snmp_version', $device['snmp_version']); print "</td>\n";
-    print "<td class='data'><input type='text' name='f_community' value=".$device['community']."></td>\n";
-    print "<td class='data'><input type='text' name='f_rw_community' value=".$device['rw_community']."></td>\n";
-    print "<td></td></tr>";
-    if ($device['snmp_version'] ==3) {
-        print "<tr><td>".WEB_snmp_v3_user_ro."</td><td>".WEB_snmp_v3_user_rw."</td><td>".WEB_snmp_v3_ro_password."</td><td>".WEB_snmp_v3_rw_password."</td><td></td>";
-	    print "</tr><tr>";
-        print "<td class='data'><input type='text' name='f_snmp3_user_ro' value=".$device['snmp3_user_ro']."></td>\n";
-	    print "<td class='data'><input type='text' name='f_snmp3_user_rw' value=".$device['snmp3_user_rw']."></td>\n";
-        print "<td class='data'><input type='text' name='f_snmp3_user_ro_password' value=".$device['snmp3_user_ro_password']."></td>\n";
-	    print "<td class='data'><input type='text' name='f_snmp3_user_rw_password' value=".$device['snmp3_user_rw_password']."></td>\n";
-        print "<td></td></tr>\n";
-	    }
+    } else {
+    print "<tr><td colspan=3>".$device['ip']."::". get_device_model_name($db_link,$device['device_model_id'])."</td>";
     }
-//save button
-print "<tr><td colspan=3>".$device['ip']."::". get_device_model_name($db_link,$device['device_model_id'])."</td><td align=right><input type='submit' name='editdevice' value='".WEB_btn_save."'></td></tr>";
+print "<td align=right><input type='submit' name='editdevice' value='".WEB_btn_save."'></td></tr>";
 print "</table>\n";
 ?>
 </form>

+ 37 - 0
html/inc/common.php

@@ -1000,6 +1000,26 @@ function print_qa_rule_select($qa_name, $qa_value = 1)
     print "</select>\n";
 }
 
+function print_snmp_auth_proto_select($qa_name, $qa_value = 'sha512')
+{
+    print "<select name=\"$qa_name\">\n";
+    print_select_item('sha512', 'sha512', $qa_value);
+    print_select_item('sha256', 'sha256', $qa_value);
+    print_select_item('sha128', 'sha128', $qa_value);
+    print_select_item('sha1', 'sha1', $qa_value);
+    print_select_item('md5', 'md5', $qa_value);
+    print "</select>\n";
+}
+
+function print_snmp_priv_proto_select($qa_name, $qa_value = 'aes128')
+{
+    print "<select name=\"$qa_name\">\n";
+    print_select_item('aes128', 'aes128', $qa_value);
+    print_select_item('aes', 'aes', $qa_value);
+    print_select_item('des', 'des', $qa_value);
+    print "</select>\n";
+}
+
 function get_int($qa_value = 0)
 {
     if (empty($qa_value)) { $qa_value = 0; } else { $qa_value = (int)$qa_value * 1; }
@@ -2706,6 +2726,7 @@ function walk_snmp($ip, $community, $version, $oid)
 {
     snmp_set_oid_output_format(SNMP_OID_OUTPUT_NUMERIC);
     $result = NULL;
+
     if ($version == 2) {
         $result = snmp2_real_walk($ip, $community, $oid, SNMP_timeout, SNMP_retry);
     }
@@ -2715,6 +2736,22 @@ function walk_snmp($ip, $community, $version, $oid)
     return $result;
 }
 
+function getSnmpAccess($device) {
+$result['port']         = 161;
+$result['timeout']      = 5;
+$result['version']      = $device['snmp_version'];
+$result['ro-community'] = $device['community'];
+$result['rw-community'] = $device['rw_community'];
+#snmpv3
+$result['auth-proto']   = $device['snmp3_auth_proto'];
+$result['priv-proto']   = $device['snmp3_priv_proto'];
+$result['ro-user']      = $device['snmp3_user_ro'];
+$result['rw-user']      = $device['snmp3_user_rw'];
+$result['ro-password']  = $device['snmp3_user_ro_password'];
+$result['rw-password']  = $device['snmp3_user_rw_password'];
+return $result;
+}
+
 function get_snmp_module_id($modules_oids, $port_name)
 {
     $port_name = preg_quote(trim($port_name), '/');

+ 2 - 0
html/inc/languages/english.php

@@ -54,6 +54,8 @@ define("WEB_msg_export_selected","Export only selected?");
 define("WEB_snmp_version","SNMP version");
 define("WEB_snmp_v3_user_ro","Snmpv3 RO user");
 define("WEB_snmp_v3_user_rw","Snmpv3 RW user");
+define("WEB_snmp_v3_auth_proto","Auth Proto");
+define("WEB_snmp_v3_priv_proto","Priv Proto");
 define("WEB_snmp_v3_ro_password","Snmpv3 RO password");
 define("WEB_snmp_v3_rw_password","Snmpv3 RW password");
 define("WEB_snmp_community_ro","Snmp RO Community");

+ 2 - 0
html/inc/languages/russian.php

@@ -54,6 +54,8 @@ define("WEB_msg_export_selected","Экспортировать только вы
 define("WEB_snmp_version","SNMP version");
 define("WEB_snmp_v3_user_ro","Snmpv3 RO user");
 define("WEB_snmp_v3_user_rw","Snmpv3 RW user");
+define("WEB_snmp_v3_auth_proto","Auth Proto");
+define("WEB_snmp_v3_priv_proto","Priv Proto");
 define("WEB_snmp_v3_ro_password","Snmpv3 RO password");
 define("WEB_snmp_v3_rw_password","Snmpv3 RW password");
 define("WEB_snmp_community_ro","Snmp RO Community");

+ 2 - 1
scripts/eye-statd.pl

@@ -95,8 +95,9 @@ $timeshift = get_option($hdb,55)*60;
 
 #router device_id by known device ip
 foreach my $row (@router_ref) {
+    setCommunity($row);
     $routers{$row->{id}}=$row;
-    my $l3_list = getIpAdEntIfIndex($row->{ip},$row->{community},$row->{snmp_version});
+    my $l3_list = getIpAdEntIfIndex($row->{ip},$row->{snmp});
 
     #create hash for interface snmp index => ip-address at interface =1;
     foreach my $router_ip (keys %$l3_list) { $routers_svi{$row->{id}}{$l3_list->{$router_ip}}{$router_ip}=1; }

+ 180 - 138
scripts/eyelib/snmp.pm

@@ -49,6 +49,8 @@ $cisco_vlan_oid
 $dot1qPortVlanEntry
 $fdb_table;
 $snmp_timeout
+setCommunity
+init_snmp
 );
 
 
@@ -84,17 +86,11 @@ our $snmp_timeout = 15;
 sub snmp_get_request {
 my $ip = shift;
 my $oid = shift;
-my $community = shift || $snmp_default_community;
-my $port = shift || '161';
-my $snmp_version = shift || '2';
-my ($session, $error) = Net::SNMP->session(
-   -hostname  => $ip,
-   -community => $community,
-   -port      => $port,
-   -version   => $snmp_version,
-   -timeout   => 5
-);
-return if (!defined($session));
+my $snmp = shift;
+
+my $session = init_snmp ($ip,$snmp);
+return if (!defined($session) or !$session);
+
 my $result = $session->get_request( -varbindlist => [$oid]);
 $session->close;
 return if (!$result->{$oid});
@@ -107,18 +103,11 @@ sub snmp_set_int {
 my $ip = shift;
 my $oid = shift;
 my $value = shift;
-my $community = shift || $snmp_default_community;
-my $port = shift || '161';
-my $snmp_version = shift || '2';
-
-my ($session, $error) = Net::SNMP->session(
-   -hostname  => $ip,
-   -community => $community,
-   -port      => $port,
-   -version   => $snmp_version,
-   -timeout   => $snmp_timeout
-);
-return if (!defined($session));
+my $snmp = shift;
+
+my $session = init_snmp ($ip,$snmp,1);
+return if (!defined($session) or !$session);
+
 my $result = $session->set_request( -varbindlist => [$oid,INTEGER,$value]);
 $session->close;
 return $result->{$oid};
@@ -127,20 +116,17 @@ return $result->{$oid};
 #-------------------------------------------------------------------------------------
 
 sub get_arp_table {
-    my ($host,$community,$version) = @_;
-#    return if (!HostIsLive($host));
-    my $port = 161;
-    my $timeout = 5;
-    if (!$version) { $version='2'; }
+    my ($host,$snmp) = @_;
 
-    ### open SNMP session
-    my ($snmp_session, $error) = Net::SNMP->session( -hostname  => $host, -community => $community , -version=>$version, -timeout   => $snmp_timeout);
-    return if (!defined($snmp_session));
-    $snmp_session->translate([-all]);
+    my $session = init_snmp ($host,$snmp,0);
+    return if (!defined($session) or !$session);
+
+    $session->translate([-all]);
 
     my $arp;
-    my $arp_table1 = $snmp_session->get_table($arp_oid);
-    my $arp_table2 = $snmp_session->get_table($ipNetToMediaPhysAddress);
+    my $arp_table1 = $session->get_table($arp_oid);
+    my $arp_table2 = $session->get_table($ipNetToMediaPhysAddress);
+    $session->close();
 
     if ($arp_table1) {
         foreach my $row (keys(%$arp_table1)) {
@@ -179,15 +165,14 @@ sub get_arp_table {
 
 sub get_ifmib_index_table {
 my $ip = shift;
-my $community = shift;
-my $version = shift;
+my $snmp = shift;
 my $ifmib_map;
 
-my $is_mikrotik = snmp_get_request($ip, '.1.3.6.1.2.1.9999.1.1.1.1.0', $community, 161, $version);
+my $is_mikrotik = snmp_get_request($ip, '.1.3.6.1.2.1.9999.1.1.1.1.0', $snmp);
 my $mk_ros_version = 0;
 
 if ($is_mikrotik=~/MikroTik/i) {
-    my $mikrotik_version = snmp_get_request($ip, '.1.0.8802.1.1.2.1.3.4.0', $community, 161, $version);
+    my $mikrotik_version = snmp_get_request($ip, '.1.0.8802.1.1.2.1.3.4.0', $snmp);
     $mk_ros_version = 6491;
     #"MikroTik RouterOS 6.46.8 (long-term) CRS326-24S+2Q+"
     if ($mikrotik_version =~/RouterOS\s+(\d)\.(\d{1,3})\.(\d{1,3})\s+/) {
@@ -196,8 +181,8 @@ if ($is_mikrotik=~/MikroTik/i) {
     }
 
 if (!$mk_ros_version or $mk_ros_version > 6468) {
-    my $index_map_table =  snmp_get_oid($ip, $community, $ifIndex_map, $version);
-    if (!$index_map_table) { $index_map_table =  snmp_walk_oid($ip, $community, $ifIndex_map, $version); }
+    my $index_map_table =  snmp_get_oid($ip, $snmp, $ifIndex_map);
+    if (!$index_map_table) { $index_map_table =  snmp_walk_oid($ip, $snmp, $ifIndex_map); }
     if ($index_map_table) {
         foreach my $row (keys(%$index_map_table)) {
             my $port_index = $index_map_table->{$row};
@@ -211,8 +196,8 @@ if (!$mk_ros_version or $mk_ros_version > 6468) {
     }
 
 if (!$ifmib_map) {
-    my $index_table =  snmp_get_oid($ip, $community, $ifIndex, $version);
-    if (!$index_table) { $index_table =  snmp_walk_oid($ip, $community, $ifIndex, $version); }
+    my $index_table =  snmp_get_oid($ip, $snmp, $ifIndex);
+    if (!$index_table) { $index_table =  snmp_walk_oid($ip, $snmp, $ifIndex); }
     foreach my $row (keys(%$index_table)) {
             my $port_index = $index_table->{$row};
             next if (!$port_index);
@@ -228,15 +213,12 @@ return $ifmib_map;
 #-------------------------------------------------------------------------------------
 
 sub get_mac_table {
-    my ($host,$community,$oid,$version,$index_map) = @_;
-    my $port = 161;
-    my $timeout = 5;
-    if (!$version) { $version='2'; }
+    my ($host,$snmp,$oid,$index_map) = @_;
     my $fdb;
     #need for callback
     $fdb_table=$oid;
-    my $fdb_table1 = snmp_get_oid($host,$community,$oid,$version);
-    if (!$fdb_table1) { $fdb_table1=snmp_walk_oid($host,$community,$oid,$version,undef); }
+    my $fdb_table1 = snmp_get_oid($host,$snmp,$oid);
+    if (!$fdb_table1) { $fdb_table1=snmp_walk_oid($host,$snmp,$oid,undef); }
     if ($fdb_table1) {
         foreach my $row (keys(%$fdb_table1)) {
                 my $port_index = $fdb_table1->{$row};
@@ -256,15 +238,11 @@ sub get_mac_table {
 #-------------------------------------------------------------------------------------
 
 sub get_fdb_table {
-    my ($host,$community,$version,$iflist) = @_;
-#    return if (!HostIsLive($host));
-    my $port = 161;
-    my $timeout = 5;
-    if (!$version) { $version='2'; }
+    my ($host,$snmp,$iflist) = @_;
 
-    my $ifindex_map = get_ifmib_index_table($host,$community,$version);
-    my $fdb1=get_mac_table($host,$community,$fdb_table_oid,$version,$ifindex_map);
-    my $fdb2=get_mac_table($host,$community,$fdb_table_oid2,$version,$ifindex_map);
+    my $ifindex_map = get_ifmib_index_table($host,$snmp);
+    my $fdb1=get_mac_table($host,$snmp,$fdb_table_oid,$ifindex_map);
+    my $fdb2=get_mac_table($host,$snmp,$fdb_table_oid2,$ifindex_map);
 
     my $fdb3;
 
@@ -282,8 +260,8 @@ sub get_fdb_table {
 
     #maybe cisco?!
     if (!$fdb) {
-        my $vlan_table=snmp_get_oid($host,$community,$cisco_vlan_oid,$version);
-        if (!$vlan_table) { $vlan_table=snmp_walk_oid($host,$community,$cisco_vlan_oid,$version); }
+        my $vlan_table=snmp_get_oid($host,$snmp,$cisco_vlan_oid);
+        if (!$vlan_table) { $vlan_table=snmp_walk_oid($host,$snmp,$cisco_vlan_oid); }
         #fuck!
         if (!$vlan_table) { return; }
         my %fdb_vlan;
@@ -293,8 +271,8 @@ sub get_fdb_table {
                 if ($vlan_oid=~/\.([0-9]{1,4})$/) { $vlan_id=$1; }
                 next if (!$vlan_id);
                 next if ($vlan_id>1000 and $vlan_id<=1009);
-                $fdb_vlan{$vlan_id}=get_mac_table($host,$community.'@'.$vlan_id,$fdb_table_oid,$version,$ifindex_map);
-                if (!$fdb_vlan{$vlan_id}) { $fdb_vlan{$vlan_id}=get_mac_table($host,$community.'@'.$vlan_id,$fdb_table_oid2,$version,$ifindex_map); }
+                $fdb_vlan{$vlan_id}=get_mac_table($host,$snmp.'@'.$vlan_id,$fdb_table_oid,$ifindex_map);
+                if (!$fdb_vlan{$vlan_id}) { $fdb_vlan{$vlan_id}=get_mac_table($host,$snmp.'@'.$vlan_id,$fdb_table_oid2,$ifindex_map); }
                 }
             foreach my $vlan_id (keys %fdb_vlan) {
                 next if (!exists $fdb_vlan{$vlan_id});
@@ -313,13 +291,9 @@ sub get_fdb_table {
 #-------------------------------------------------------------------------------------
 
 sub get_vlan_at_port {
-    my ($host,$community,$version,$port_index) = @_;
-    my $port = 161;
-    my $timeout = 5;
-    if (!$version) { $version='2'; }
+    my ($host,$snmp,$port_index) = @_;
     my $vlan_oid=$dot1qPortVlanEntry.".".$port_index;
-#    print "$host,$community,$vlan_oid,$version\n";
-    my $vlan = snmp_get_req($host,$community,$vlan_oid,$version);
+    my $vlan = snmp_get_req($host,$snmp,$vlan_oid);
     return "1" if (!$vlan);
     return "1" if ($vlan=~/noSuchObject/i);
     return "1" if ($vlan=~/noSuchInstance/i);
@@ -329,14 +303,11 @@ sub get_vlan_at_port {
 #-------------------------------------------------------------------------------------
 
 sub get_switch_vlans {
-    my ($host,$community,$version) = @_;
-    my $port = 161;
-    my $timeout = 5;
-    if (!$version) { $version='2'; }
+    my ($host,$snmp) = @_;
     my $result;
     #need for callback
-    my $vlan_table = snmp_get_oid($host,$community,$dot1qPortVlanEntry,$version);
-    if (!$vlan_table) { $vlan_table=snmp_walk_oid($host,$community,$dot1qPortVlanEntry,$version); }
+    my $vlan_table = snmp_get_oid($host,$snmp,$dot1qPortVlanEntry);
+    if (!$vlan_table) { $vlan_table=snmp_walk_oid($host,$snmp,$dot1qPortVlanEntry); }
     if ($vlan_table) {
         foreach my $vlan_oid (keys %$vlan_table) {
             if ($vlan_oid=~/\.([0-9]*)$/) { $result->{$1} = $vlan_table->{$vlan_oid}; }
@@ -348,17 +319,18 @@ sub get_switch_vlans {
 #-------------------------------------------------------------------------------------
 
 sub get_snmp_ifindex {
-    my ($host,$community,$snmp) = @_;
-    ### open SNMP session
-    my ($snmp_session, $error) = Net::SNMP->session( -hostname  => $host, -community => $community, -version => $snmp, -timeout => 5);
-    return if (!defined($snmp_session));
-    my $if_index = $snmp_session->get_table($ifIndex);
+    my ($host,$snmp) = @_;
+    my $session = init_snmp($host,$snmp,0);
+    return if (!defined($session) or !$session);
+
+    my $if_index = $session->get_table($ifIndex);
     my $result;
     foreach my $row (keys(%$if_index)) {
         my $value = $if_index->{$row};
         $row=~s/^$ifIndex\.//;
         $result->{$row}=$value;
         };
+    $session->close();
     return $result;
 }
 
@@ -366,36 +338,36 @@ sub get_snmp_ifindex {
 
 #get ip interfaces
 sub getIpAdEntIfIndex {
-    my ($host,$community,$snmp) = @_;
-    my $port = 161;
-    ### open SNMP session
-    my ($snmp_session, $error) = Net::SNMP->session( -hostname  => $host, -community => $community, -version => $snmp, -timeout => $snmp_timeout );
-    return if (!defined($snmp_session));
-    $snmp_session->translate([-timeticks]);
-    my $if_ipaddr = $snmp_session->get_table($ipAdEntIfIndex);
+    my ($host,$snmp) = @_;
+    my $session = init_snmp ($host,$snmp,0);
+    return if (!defined($session) or !$session);
+
+    $session->translate([-timeticks]);
+    my $if_ipaddr = $session->get_table($ipAdEntIfIndex);
     my $l3_list;
     foreach my $row (keys(%$if_ipaddr)) {
         my $ipaddr = $row;
         $ipaddr=~s/$ipAdEntIfIndex\.//;
         $l3_list->{$ipaddr}=$if_ipaddr->{$row};
     }
+    $session->close();
     return $l3_list;
 }
 
 #-------------------------------------------------------------------------------------
 
 sub get_interfaces {
-    my ($host,$community,$snmp,$skip_empty) = @_;
-#    return if (!HostIsLive($host));
-    my $port = 161;
-    ### open SNMP session
-    my ($snmp_session, $error) = Net::SNMP->session( -hostname  => $host, -community => $community, -version => $snmp, -timeout => $snmp_timeout );
-    return if (!defined($snmp_session));
-    $snmp_session->translate([-timeticks]);
-    my $if_name = $snmp_session->get_table($ifName);
-    my $if_alias = $snmp_session->get_table($ifAlias);
-    my $if_descr = $snmp_session->get_table($ifDescr);
-    my $if_index = $snmp_session->get_table($ifIndex);
+    my ($host,$snmp,$skip_empty) = @_;
+
+    my $session = init_snmp ($host,$snmp,0);
+    return if (!defined($session) or !$session);
+
+    $session->translate([-timeticks]);
+    my $if_name = $session->get_table($ifName);
+    my $if_alias = $session->get_table($ifAlias);
+    my $if_descr = $session->get_table($ifDescr);
+    my $if_index = $session->get_table($ifIndex);
+    $session->close();
     my $dev_cap;
 
     foreach my $row (keys(%$if_index)) {
@@ -423,44 +395,37 @@ sub get_interfaces {
 #-------------------------------------------------------------------------------------
 
 sub get_router_state {
-    my ($host,$community,$snmp,$skip_empty) = @_;
-#    return if (!HostIsLive($host));
-    my $port = 161;
-    ### open SNMP session
-    my ($snmp_session, $error) = Net::SNMP->session( -hostname  => $host, -community => $community, -version => $snmp, -timeout => $snmp_timeout );
-    return if (!defined($snmp_session));
-    $snmp_session->translate([-timeticks]);
-    my $router_status = $snmp_session->get_table("1.3.6.1.4.1.10.1");
+    my ($host,$snmp,$skip_empty) = @_;
+    my $session = init_snmp ($host,$snmp,0);
+    return if (!defined($session) or !$session);
+    $session->translate([-timeticks]);
+    my $router_status = $session->get_table("1.3.6.1.4.1.10.1");
+    $session->close();
     return ($router_status);
 }
 
 #-------------------------------------------------------------------------------------
 
 sub snmp_get_req {
-my ($host,$community,$oid,$version) = @_;
-#return if (!HostIsLive($host));
-if (!$version) { $version='2'; }
-### open SNMP session
-my ($snmp_session, $error) = Net::SNMP->session( -hostname  => $host, -community => $community , -version=>$version, -timeout => $snmp_timeout );
-return if (!defined($snmp_session));
-$snmp_session->translate([-timeticks]);
-my $result = $snmp_session->get_request(-varbindlist => [$oid]) or return;
-$snmp_session->close();
+my ($host,$snmp,$oid) = @_;
+my $session = init_snmp ($host,$snmp,0);
+return if (!defined($session) or !$session);
+$session->translate([-timeticks]);
+my $result = $session->get_request(-varbindlist => [$oid]) or return;
+$session->close();
 return $result->{$oid};
 }
 
 #-------------------------------------------------------------------------------------
 
 sub snmp_get_oid {
-my ($host,$community,$oid,$version) = @_;
-#return if (!HostIsLive($host));
-if (!$version) { $version='2'; }
-### open SNMP session
-my ($snmp_session, $error) = Net::SNMP->session( -hostname  => $host, -community => $community , -version=>$version , -timeout     => $snmp_timeout, );
-return if (!defined($snmp_session));
-$snmp_session->translate([-timeticks]);
-my $table = $snmp_session->get_table($oid);
-$snmp_session->close();
+my ($host,$snmp,$oid) = @_;
+my $port = 161;
+my $session = init_snmp ($host,$snmp,0);
+return if (!defined($session) or !$session);
+$session->translate([-timeticks]);
+my $table = $session->get_table($oid);
+$session->close();
 return $table;
 }
 
@@ -469,25 +434,40 @@ return $table;
 sub snmp_walk_oid {
 
 my $host = shift;
-my $community = shift;
+my $snmp = shift;
 my $oid = shift;
-my $version = shift || '2c';
+my $rw = 'ro';
 
-#return if (!HostIsLive($host));
-
-my ($session, $error) = Net::SNMP->session(
-      -hostname    => $host,
-      -community   => $community,
-      -nonblocking => 1,
-      -translate   => [-octetstring => 0],
-      -version     => $version,
-      -timeout     => $snmp_timeout,
-   );
-
-if (!defined $session) {
-      printf "ERROR: %s.\n", $error;
-      return;
-}
+### open SNMP session
+my ($session, $error);
+
+if ($snmp->{version} <= 2) {
+        ($session, $error) = Net::SNMP->session(
+		-hostname  => $host,
+		-community => $snmp->{'ro-community'} ,
+		-version   => $snmp->{version},
+		-port      => $snmp->{port},
+		-timeout   => $snmp->{timeout},
+		-nonblocking => 1,
+		-translate   => [-octetstring => 0],
+		);
+	} else {
+	($session, $error) = Net::SNMP->session(
+		-hostname     => $host,
+		-version      => 'snmpv3',
+		-username     => $snmp->{$rw.'-user'},
+		-authprotocol => $snmp->{'auth-proto'},
+		-privprotocol => $snmp->{'priv-proto'},
+		-authpassword => $snmp->{$rw.'-password'},
+		-privpassword => $snmp->{$rw.'-password'},
+		-port         => $snmp->{port},
+		-timeout      => $snmp->{timeout},
+		-nonblocking  => 1,
+		-translate    => [-octetstring => 0],
+		);
+	}
+
+return if (!defined($session) or !$session);
 
 my %table; # Hash to store the results
 
@@ -531,6 +511,68 @@ if (!defined $result) {
 return;
 }
 
+
+#-------------------------------------------------------------------------------------
+
+sub init_snmp {
+
+    my ($host,$snmp,$rw) = @_;
+
+    return if (!$host);
+
+    my $community = $snmp->{'ro-community'};
+    if (!$rw) { $rw = 'ro' }
+	    else {
+	    $rw = 'rw';
+	    $community = $snmp->{'rw-community'};
+	    }
+
+    ### open SNMP session
+    my ($session, $error);
+
+    if ($snmp->{version} <=2) {
+        ($session, $error) = Net::SNMP->session(
+		-hostname  => $host,
+		-community => $community ,
+		-version   => $snmp->{'version'},
+		-port      => $snmp->{port},
+		-timeout   => $snmp->{timeout},
+		);
+	} else {
+	($session, $error) = Net::SNMP->session(
+		-hostname     => $host,
+		-version      => 'snmpv3',
+		-username     => $snmp->{$rw.'-user'},
+		-authprotocol => $snmp->{'auth-proto'},
+		-privprotocol => $snmp->{'priv-proto'},
+		-authpassword => $snmp->{$rw.'-password'},
+		-privpassword => $snmp->{$rw.'-password'},
+		-port         => $snmp->{port},
+		-timeout      => $snmp->{timeout},
+		);
+	}
+    return if (!defined($session) or !$session);
+    return $session;
+}
+
+#-------------------------------------------------------------------------------------
+
+sub setCommunity {
+my $device = shift;
+$device->{snmp}->{'port'}         = 161;
+$device->{snmp}->{'timeout'}      = $snmp_timeout;
+$device->{snmp}->{'version'}      = $device->{snmp_version} || '2';
+$device->{snmp}->{'ro-community'} = $device->{community} || $snmp_default_community;
+$device->{snmp}->{'rw-community'} = $device->{rw_community} || $snmp_default_community;
+#snmpv3
+$device->{snmp}->{'auth-proto'}   = $device->{snmp3_auth_proto} || 'sha512';
+$device->{snmp}->{'priv-proto'}   = $device->{snmp3_priv_proto} || 'aes128';
+$device->{snmp}->{'ro-user'}      = $device->{snmp3_user_ro} || '';
+$device->{snmp}->{'rw-user'}      = $device->{snmp3_user_rw} || '';
+$device->{snmp}->{'ro-password'}  = $device->{snmp3_user_ro_password} || $snmp_default_community;
+$device->{snmp}->{'rw-password'}  = $device->{snmp3_user_rw_password} || $snmp_default_community;
+}
+
 #-------------------------------------------------------------------------------------
 
 1;

+ 5 - 5
scripts/fetch_new_arp.pl

@@ -87,14 +87,13 @@ sub {
 DATA_LOOP:
 foreach my $router (@router_ref) {
 my $router_ip=$router->{ip};
-my $snmp_version=$router->{snmp_version};
-my $community=$router->{community};
+setCommunity($router);
 if (!HostIsLive($router_ip)) { log_info("Host id: $router->{id} name: $router->{device_name} ip: $router_ip is down! Skip."); next; }
 $pm_arp->start() and next DATA_LOOP;
 my $arp_table;
 my $tmp_dbh=init_db();
 if (apply_device_lock($tmp_dbh,$router->{id})) {
-    $arp_table=get_arp_table($router_ip,$community,$snmp_version);
+    $arp_table=get_arp_table($router_ip,$router->{snmp});
     unset_lock_discovery($tmp_dbh,$router->{id});
     }
 $tmp_dbh->disconnect;
@@ -229,13 +228,14 @@ $dbh->disconnect;
 
 FDB_LOOP:
 foreach my $device (@device_list) {
-my $int_list = get_snmp_ifindex($device->{ip},$device->{community},$device->{snmp_version});
+setCommunity($device);
+my $int_list = get_snmp_ifindex($device->{ip},$device->{snmp});
 if (!$int_list) { log_info("Host id: $device->{id} name: $device->{device_name} ip: $device->{ip} is down! Skip."); next; }
 $pm_fdb->start() and next FDB_LOOP;
 my $result;
 my $tmp_dbh = init_db();
 if (apply_device_lock($tmp_dbh,$device->{id})) {
-    my $fdb=get_fdb_table($device->{ip},$device->{community},$device->{snmp_version},$dev_ifindex{$device->{id}});
+    my $fdb=get_fdb_table($device->{ip},$device->{snmp},$dev_ifindex{$device->{id}});
     unset_lock_discovery($tmp_dbh,$device->{id});
     $result->{id}=$device->{id};
     $result->{fdb} = $fdb;

+ 11 - 7
scripts/restart_port_snmp.pl

@@ -47,12 +47,18 @@ my $auth_name = $auth_rec->{dns_name};
 my $auth_ident = $HOST_IP;
 if ($auth_name) { $auth_ident = $auth_name."[".$HOST_IP."]"; }
 
-my $d_sql="SELECT D.ip, D.device_name, D.vendor_id, D.device_model_id, DP.port, DP.snmp_index, D.rw_community, D.snmp_version  FROM devices AS D, device_ports AS DP, connections AS C WHERE D.snmp_version>0 and D.id = DP.device_id AND DP.id = C.port_id AND C.auth_id=$auth_id AND DP.uplink=0";
+my $d_sql="SELECT D.id, D.ip, D.device_name, D.vendor_id, D.device_model_id, DP.port, DP.snmp_index  FROM devices AS D, device_ports AS DP, connections AS C WHERE D.snmp_version>0 and D.id = DP.device_id AND DP.id = C.port_id AND C.auth_id=$auth_id AND DP.uplink=0";
 
 my $dev_port = get_record_sql($dbh,$d_sql);
 
 if (!$dev_port) { db_log_error($dbh,"Connection for $HOST_IP not found! Bye."); exit; }
 
+my $switch = get_record_sql($dbh,'SELECT * FROM devices WHERE id='.$dev_port->{id});
+
+if (!$switch) { db_log_error($dbh,"Switch for $HOST_IP not found! Bye."); exit; }
+
+setCommunity($switch);
+
 my $ip=$dev_port->{ip};
 my $model_id=$dev_port->{device_model_id};
 my $model_rec = get_record_sql($dbh,'SELECT model_name FROM device_models WHERE id='.$model_id);
@@ -60,8 +66,6 @@ my $model = $model_rec->{model_name};
 my $port=$dev_port->{port};
 my $vendor_id = $dev_port->{vendor_id};
 my $snmp_index=$dev_port->{snmp_index};
-my $community=$dev_port->{rw_community};
-my $snmp_version=$dev_port->{snmp_version};
 my $device_name = $dev_port->{device_name};
 
 db_log_warning($dbh,"Restart $auth_ident at $device_name ($model $ip) [$port] request found. Try.");
@@ -100,13 +104,13 @@ db_log_debug($dbh,"POE oid: $poe_oid");
 db_log_debug($dbh,"Admin oid: $admin_oid");
 
 if ($poe_oid) {
-    $ret=snmp_set_int($ip,$poe_oid,$poe_disabled_value,$community,161,$snmp_version);
+    $ret=snmp_set_int($ip,$poe_oid,$poe_disabled_value,$switch->{snmp});
     db_log_info($dbh,"Try disable POE at port $port.");
     db_log_debug($dbh,"Send to oid: $poe_oid value: $poe_disabled_value");
     }
 
 if ($admin_oid) {
-    $ret=snmp_set_int($ip,$admin_oid,$poe_disabled_value,$community,161,$snmp_version); 
+    $ret=snmp_set_int($ip,$admin_oid,$poe_disabled_value,$switch->{snmp});
     db_log_info($dbh,"Try shutdown port $port.");
     db_log_debug($dbh,"Send to oid: $admin_oid value: $poe_disabled_value");
     }
@@ -114,13 +118,13 @@ if ($admin_oid) {
 sleep($sleep_time);
 
 if ($admin_oid) {
-    $ret=snmp_set_int($ip,$admin_oid,$poe_enabled_value,$community,161,$snmp_version);
+    $ret=snmp_set_int($ip,$admin_oid,$poe_enabled_value,$switch->{snmp});
     db_log_info($dbh,"Enable POE at port $port.");
     db_log_debug($dbh,"Send to oid: $admin_oid value: $poe_enabled_value");
     }
 
 if ($poe_oid) {
-    $ret=snmp_set_int($ip,$poe_oid,$poe_enabled_value,$community,161,$snmp_version); 
+    $ret=snmp_set_int($ip,$poe_oid,$poe_enabled_value,$switch->{snmp});
     db_log_info($dbh,"Up port $port.");
     db_log_debug($dbh,"Send to oid: $poe_oid value: $poe_enabled_value");
     }

+ 1 - 0
scripts/updates/2-7-1/devices.sql

@@ -0,0 +1 @@
+ALTER TABLE `devices` ADD `snmp3_auth_proto` VARCHAR(10) NOT NULL DEFAULT 'sha512' AFTER `snmp_version`, ADD `snmp3_priv_proto` VARCHAR(10) NOT NULL DEFAULT 'aes128' AFTER `snmp3_auth_proto`;

+ 1 - 0
scripts/updates/2-7-1/version.sql

@@ -0,0 +1 @@
+REPLACE INTO `version` (`version`) VALUES ('2.7.1');

+ 2 - 1
scripts/updates/upgrade.pl

@@ -34,7 +34,8 @@ my @old_releases = (
 '2.6.1',
 '2.6.2',
 '2.6.3',
-'2.7.0'
+'2.7.0',
+'2.7.1',
 );
 
 my $r_index = 0;

+ 7 - 11
scripts/utils/set_port_descr.pl

@@ -52,9 +52,9 @@ $auth_ref{$auth->{id}}{description}=~s/\)/_/g;
 
 my %port_info;
 
-my $d_sql="SELECT DP.id, D.ip, D.device_name, D.device_model_id, DP.port, DP.snmp_index, DP.comment, D.community, D.snmp_version, DP.target_port_id, D.vendor_id, D.device_type
+my $d_sql="SELECT DP.id, D.ip, D.device_name, D.device_model_id, DP.port, DP.snmp_index, DP.comment, DP.target_port_id, D.vendor_id, D.device_type
 FROM devices AS D, device_ports AS DP
-WHERE D.snmp_version>0 and D.id = DP.device_id AND D.community IS NOT NULL AND (D.device_type <=1) AND D.deleted=0
+WHERE D.id = DP.device_id AND (D.device_type <=1) AND D.deleted=0
 ORDER BY D.device_name,DP.port";
 
 my @port_list = get_records_sql($dbh,$d_sql);
@@ -66,9 +66,7 @@ $port_info{$port->{id}}{ip}=$port->{ip};
 $port_info{$port->{id}}{device_model_id}=$port->{device_model_id};
 $port_info{$port->{id}}{port}=$port->{port};
 $port_info{$port->{id}}{snmp_index}=$port->{snmp_index};
-$port_info{$port->{id}}{community}=$port->{community};
 $port_info{$port->{id}}{comment}=$port->{comment};
-$port_info{$port->{id}}{snmp_version}=$port->{snmp_version};
 $port_info{$port->{id}}{target_port_id}=$port->{target_port_id};
 $port_info{$port->{id}}{vendor_id}=$port->{vendor_id};
 $port_info{$port->{id}}{device_type}=$port->{device_type};
@@ -123,12 +121,10 @@ $devices{$port_info{$port_id}{device_name}}{ip}=$port_info{$port_id}{ip};
 $devices{$port_info{$port_id}{device_name}}{device_model_id}=$port_info{$port_id}{device_model_id};
 $devices{$port_info{$port_id}{device_name}}{vendor_id}=$port_info{$port_id}{vendor_id};
 $devices{$port_info{$port_id}{device_name}}{device_type}=$port_info{$port_id}{device_type};
-$devices{$port_info{$port_id}{device_name}}{community}=$port_info{$port_id}{community};
-$devices{$port_info{$port_id}{device_name}}{snmp_version}=$port_info{$port_id}{snmp_version};
 }
 
 
-$Net::OpenSSH::debug=-1;
+#$Net::OpenSSH::debug=-1;
 
 foreach my $device_name (sort keys %devices) {
 my $device = $devices{$device_name};
@@ -138,22 +134,22 @@ next if (!$switch_auth{$device->{vendor_id}});
 
 
 my $ip = $device->{ip};
-my $community = $device->{community};
-my $snmp_version = $device->{snmp_version};
 
 my $netdev = get_record_sql($dbh,"SELECT * FROM devices WHERE ip='".$ip."'");
 
 next if (!$netdev);
 
-print "Device: $device_name IP: $ip community: $community version: $snmp_version ";
+print "Device: $device_name IP: $ip ";
 
 if (!HostIsLive($ip)) { print "... Down! Skip.\n"; next; }
 
 print "... Programming:\n";
 
+setCommunity($netdev);
+
 eval {
 #get interface names
-my $int = get_interfaces($ip,$community,$snmp_version,0);
+my $int = get_interfaces($ip,$netdev->{snmp},0);
 
 $netdev = netdev_set_auth($netdev);