惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

www.infosecurity-magazine.com
www.infosecurity-magazine.com
Vercel News
Vercel News
G
Google Developers Blog
MyScale Blog
MyScale Blog
The Register - Security
The Register - Security
I
InfoQ
Blog — PlanetScale
Blog — PlanetScale
D
DataBreaches.Net
Microsoft Security Blog
Microsoft Security Blog
V
Visual Studio Blog
V2EX - 技术
V2EX - 技术
F
Fortinet All Blogs
博客园_首页
S
Secure Thoughts
GbyAI
GbyAI
S
Security Affairs
N
News | PayPal Newsroom
Forbes - Security
Forbes - Security
Recent Announcements
Recent Announcements
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Security Archives - TechRepublic
Security Archives - TechRepublic
宝玉的分享
宝玉的分享
Hugging Face - Blog
Hugging Face - Blog
Hacker News - Newest:
Hacker News - Newest: "LLM"
H
Heimdal Security Blog
A
About on SuperTechFans
P
Proofpoint News Feed
H
Help Net Security
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
Y
Y Combinator Blog
L
LINUX DO - 最新话题
Apple Machine Learning Research
Apple Machine Learning Research
L
LangChain Blog
博客园 - 叶小钗
A
Arctic Wolf
Cisco Talos Blog
Cisco Talos Blog
T
The Exploit Database - CXSecurity.com
人人都是产品经理
人人都是产品经理
T
Threat Research - Cisco Blogs
N
News and Events Feed by Topic
Security Latest
Security Latest
The Hacker News
The Hacker News
T
Tor Project blog
O
OpenAI News
博客园 - 三生石上(FineUI控件)
PCI Perspectives
PCI Perspectives
量子位
大猫的无限游戏
大猫的无限游戏
Stack Overflow Blog
Stack Overflow Blog

Все публикации подряд на Хабре

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания Автономность как точка невозврата: кто будет субъектом в цифровом будущем Обучение ИИ в «диких» условиях: как рутинные действия превращаются в датасеты Как измерить LLM для задач кибербеза: обзор открытых бенчмарков Где хранить код? Сравнение GitHub, GitLab и Bitbucket Математика объясняет, почему нормальное распределение встречается повсюду Почему ваш FinOps не работает: 12 тезисов от практиков Как подписать проектную документацию УКЭП с использованием бесплатных лицензий Pilot Адаптивное администрирование Sigla Vision Я грузил уран в бочки, а потом 20 лет строил ИТ в атомной отрасли Чем позвонить с Эвереста? История и обзор спутниковой связи. Часть 2 Как языковая модель помогает контролировать качество инструктажей по охране труда в металлургии Как не передать на desktop свой IP в РКН Анатомия SAP Privileges: как устроено управление правами в macOS MoneyDev: Сказка про три главных слова Обновлённый токенизатор видео K-VAE 2.0 от Сбера Как сделать диспетчеризацию дома на 1284 квартиры почти бесплатно Как мы разогнали железную дорогу Мы дали агентам рутину. Теперь надо решить — что делать с освободившимся временем Токсичный контент, промпт-хакинг и защита ИИ — всё о Guardrails для LLM Умный город начинается с точного взгляда: как «Фалькон Тех» меняет пространство к лучшему Навайбкодил приложение для анализа графов Почему Дюну так интересно читать? Упрощаем работу с рутиной или как стать Гендальфом Белым Деконструкция Go: CPU, RAM и что там происходит. Go Assembler база. Часть 1.1 Какие профессии исчезнут из-за ИИ, а какие появятся? И что с этим делать Как мы построили IT-отдел, где хочется расти: архитектурные встречи, прозрачные метрики и книжные подарки Rufler: Делаем из Claude Code автономный рой через один YAML-конфиг Sing-box и белый список приложений Как построить надёжный обмен сообщениями в микросервисах: лучшие практики для enterprise OpenAI строит MLM-пирамиду, а McKinsey и Accenture помогают ей в этом Дом, который не построил Фишер (Часть 2) «Сверхзвуковой математик» против «Вдумчивого логиста»: битва алгоритмов 3D-упаковки Мультимодальные модели – грубый и дорогой инструмент Разговоры ничего не стоят. Код тоже Проверки физических лиц: с кого начнет ФНС Топ-10 бесплатных нейросетей для создания видео в 2026 году Первые слои кода: как наши решения сегодня определяют архитектуру ИИ на десятилетия Разработка нового статического анализатора: PVS-Studio JavaScript Поиск уязвимостей ПО: базовый минимум или роскошный максимум Почему оценка персонала не работает как инструмент управления Как мы разработали ИИ-ассистента и сократили рутину продуктовой команды на 50% Как я ушел из найма, нажарил косточек и продал на маркетплейсах на 168 млн в год Когда 1С:ERP уже внедрена, а нормального производственного плана всё ещё нет Как я сделал Claude мультимодальным, подключив к нему Qwen Omni Как приглашение на вакансию мечты превращается в атаку Infrastructure as Code: философия и лучшие практики IaC Тестируем Yandex Code Assistant на задаче, в которой нужно хранить секреты nxs-universal-chart v3.0: новое поколение универсального Helm-чарта Callback Injection: Техника, которая отправила Microsoft Defender в глухой нокаут «Все идеи на стол»: митап как способ вывести проект из тупика Сегодня я узнал нечто новое о GPU благодаря багу в своей игре Как заставить LLM ̶ ̶г̶а̶л̶л̶ю̶ ̶ эволюционировать Карта событий как фундамент аналитики: практический кейс для E-commerce Что выбрать для AI: x86, ARM или RISC-V? Дайджест железа за март Роль соматических мутаций в развитии аутоиммунных заболеваний: путь к избирательной терапии Mythos от Anthropic — тревожный сигнал для всех, а не только для банков Guardrails для LLM на Java: как приручить промпт‑инъекции и токсичные ответы Green-VLA: как мы собрали VLA-модель для реального антропоморфного робота и не потеряли обобщение Финансовая гонка вооружений: почему умные люди добровольно в ней участвуют Эра ИИ-агентов наступила: выбираем лучшего цифрового сотрудника # Практический опыт внедрения WinCC Redundancy на производственном предприятии Сделал MVP за 3 дня, а потом неделю прикручивал оплату. Оно того стоило? Физика против Маска: почему Starship V3 может оказаться ещё одной катастрофой Нефть Венесуэлы: крупнейшие запасы в мире, но не крупнейшая нефтяная держава JPA 4. Переосмысление Hibernate Почему зеркальная фотокамера Nikon D5 десятилетней давности идеально подошла для миссии «Артемида-2» Проект «Уровень-Спутник» или как мы сделали платформу для гидрологов «Замедлиться, чтобы ускориться»: почему ИИ повышает цену ошибок в требованиях и архитектуре Как с нуля поднять трафик IT-компании на 1657% при бюджете 55 тыс. и выжить Pixel-perfect Downsampling — идеальная отрисовка 50 миллионов точек без потерь
Библиотека SNMP на Go, зачем я создал еще одну и чем она может быть интересна
OlegPowerC · 2026-05-08 · via Все публикации подряд на Хабре

Когда-то давно, в 2012–2014 годах, мне и коллегам понадобилось собирать различные данные с большого числа различных коммутаторов и прочего сетевого оборудования.

В то время у нас были в основном коммутаторы Cisco немного Moxa и немного HP.

Мониторилось все это при помощи PRTG и Nagios, а для сбора данных мы использовали ПО switchmap для Cisco и собственные скрипты на PHP для Moxa. ПО в целом выполняло свою функцию, информация собиралась и помогала в работе.

Однако с течением времени количество коммутаторов и маршрутизаторов увеличивалось, появилось оборудование других производителей, которое switchmap опросить уже не мог, так же возникали различные проблемы и с Moxa, плюс ко всему SNMP версии 2C требовалось по возможности больше не использовать, перейдя на версию 3 (актуальная по сей день).

Так же хотелось сохранять данные в базе данных и/или в файлах формата XML/JSON.
И конечно хотелось параллельного опроса множества коммутаторов.

Имея опыт в написании ПО на Go с использованием горутин, я предположил, что Go идеальный кандидат на язык и рантайм для подобного ПО и стал искать подходящую библиотеку для опроса оборудования по протоколу SNMP.

Остановился я на библиотеке WebNMS от Zoho, она коммерческая, но была и бесплатная версия (если что, нвйти по ней информацию чегодня практический невозможно).

На ее базе я довольно быстро смог создать вполне неплохо работающее ПО, которое позволяло собирать данные по SNMP v3 со множества различных коммутаторов параллельно.

Однако довольно быстро обнаружились проблемы: с некоторого оборудования данные получить не удавалось – я получал ошибку парсинга ASN.1 (тут поясню для тех, кто глубоко не знает SNMP. Данные в SNMP кодируются при помощи ASN.1 BER, и именно то, что это BER приводит к проблемам, которые я опишу ниже).

Так же периодически получал различные друге ошибки, которые требовалось исправлять в коде библиотеки.

И можно было бы перейти на коммерческую версию и полагать что Zoho исправит ошибки в рамках поддержки, но Zoho вообще прекратили ее поддерживать, а теперь про нее ничего не может найти даже гугл.

Закопавшись в исходниках библиотеки я стал понимать, что для того, чтоб что-то там исправить, помимо понимания структуры библиотеки, необходимо еще и глубокие знания SNMP и ASN.1. Моих знаний на тот момент было недостаточно и пришлось их форсированно пополнить.

В итоге я принял решение написать свою библиотеку SNMP v3, но начал, разумеется, с версии 2C.

Затем я потратил много времени на погружение в аутентификацию и шифрование (на тот момент у меня были не нулевые знания в этой области, но их было недостаточно) и следующим этапом было добавление поддержки 3 версии SNMP в библиотеку. Я добавил только шифрование AES-128 и аутентификацию SHA и на этом ограничился.

Итогом этого труда стало то, что я с коллегами смог опрашивать все наше оборудование, а там, где возникали проблемы мы их оперативно исправляли.

Очень скоро оказалось что некоторое оборудование поддерживает только DES и MD5, пришлось добавить и их.

В 2025 году мне понадобилось добавить поддержку шифрования AES-256, и я решил так же немного сделать рефакторинг кода, а за одно довести его до состояния, когда им будет не стыдно делиться и в итоге опубликовал его на Github под лицензией MIT.

А в этой статье я попробую раскрыть детали проекта и возможно кому-то моя библиотека пригодится.

Итак приступим.

Любая библиотека SNMP должна уметь кодировать и декодировать данные в формате ASN.1 BER. Нам придется немного погрузиться в этот стандарт.

Минимально что нужно знать – ASN.1 это бинарное кодирование данных, в виде последовательности тип, длина, значение (Type Length Value или TLV), существует множество вариантов такого кодирования, но нас интересует BER – базовое и DER строгое, и то что парсер ASN.1 в Go это парсер именно DER (и именно DER применяется в криптографии).

Но в SNMP применяется BER и напрямую, стандартный парсер Go ASN.1 применить можно, но очень скоро мы столкнемся с оборудованием, декодировать данные которого не сможем.

Тут замечу что в обратную сторону это работает - то есть закодировав запрос DER кодированием, оборудование его поймет.

Очень важное и ключевое отличие между BER и DER это как кодируется длина.
В DER есть правило, ели длина меньше 127 то кодируем одним байтом, в BER же это правило можно не соблюдать и кодировать длину, например в 5 байт двумя байтами, зачем спросите вы? Ну например для того, чтоб структура данных была фиксированной длины, или еще по какой-то причине, это вопросы к производителям оборудования, например к Moxa.

Из этого следует что придется либо использовать сторонний парсер ASN.1, либо писать свой.

Я решил просто модифицировать стандартный парсер, ослабив проверки и введя поддержку еще одного формата длины – Indefinite, это когда в конце данных есть маркер окончания, длину в такой форме в SNMP я никогда не видел, но решил ее поддержку тоже добавить.

Так появился форк стандартного парсера ASN.1, с которым можно ознакомиться по ссылке.

Итак, кодер/декодер есть, теперь нужно реализовать:

Для версии 2c

  • формирование запроса

  • Парсинг ответа

  • Посылку/прием запроса/ответа, повторную посылку в случае неполучения ответа, таймауты

  • Функции Get, Set, GetNext

  • Поддержку Bulk запросов/ответов

  • Высокоуровневый Walk

  • Служебные функции, такие как обнаружение выхода из ветки для завершения Walk, инициализацию и прочее

Для версии 3 в дополнение к тому, что есть у 2c

  • Шифрование/Дешифрование

  • Аутентификация

  • Процедура Discovery

  • Обработка Report сообщений

  • Работа со специфичными для 3 версии функциями – например в версии 3 можно сообщить передатчику сколь данных максимально мы можем принять, и тогда буфер, в который будет прочитана датаграмма, можно ограничить этим объемом сэкономив память.

Так же нужно реализовать прием trap/inform как для версии 2c, так и для 3 версии и еще многое другое.

Если вас утомил текст то вы можете ознакомится с проектом по ссылке и далее не читать, а если же нет, то далее я коротко опишу чем проект интересен, а следующим этапом погрузимся в технические детали, но это будет уже вторая статья (если конечно вы мне не напишите в коментариях - нет, не интересно и не нужно продолжение). А пока опишу чем проект интересен и какие есть альтернативы.

И так сильные (на мой взгляд) стороны библиотеки:

Использование слегка модифицированного парсера ASN.1 из стандартной библиотеки.
Почему это преимущество? Потому что это родной Go парсер, он хорошо оттестирован и широко используется, поскольку модификации минимальные и хорошо описаны, их легко можно внести в более новые версии стандартного парсера. Так же используется традиционный Go способ кодировки/декодировки – marshal/unmarshal.

Синхронизация Boots/Time выполняется при выполнении процедуры Discovery и при получении Report сообщения с ошибкой notInTimeWindows. Почему это как мне кажется хорошо? Давайте немного подробнее разберемся как это возможно реализовать:

И так для того, чтоб ваш запрос к устройству был валидный, вам помимо знания Username, протоколов и ключей аутентификации и шифрования а так же Engine ID, надо еще указать два параметра – Boots (количество перезагрузок устройства) и Time (время в секундах, с момента включения устройства).

Boots должен точно совпадать а Time может отличаться не более чем на ±150 секунд
Если этот параметр однократно инициализировать при Discovery, то спустя 150 секунд мы получим Report с ошибкой notInTimeWindows.

Если же этот параметр инкрементировать при каждом запросе, то если сеть быстрая, а запросов много, то может так случиться, что счетчик, наоборот, убежит вперед и тоже станет не валидным.

Можно, конечно, использовать реальное время, а можно просто при получении Report notInTimeWindows, произвести синхронизацию и повторить запрос.

Обработка Report с ошибкой usmStatsUnknownEngineIDs (выполнение повторного Discovery).

Ошибки разделены на фатальные и не фатальные, так же содержат детальную информацию. Это требует отдельного описания, оно будет ниже.

Компактный код прост для понимания.

Теперь остановимся подробнее на процессе Discovery и обработке Report сообщений, разумеется, это все относится только к версии 3 протокола.

Что же такое Discovery и зачем она нужна? Для того чтоб отправить запрос, например Get, понадобится указать Username, Engine ID, Boots, Time, Auth parameter, Priv parameter. Из всего этого на начальном этапе известен лишь Username, потому что для вычисления Auth/Priv параметров, и создания корректного запроса необходимо знать Engine ID, Boots и Time.

Чтоб получить эти данные пошлем Get запрос с пустыми, всеми этими полями:

В ответ должен придет Report с ошибкой usmStatsUnknownEngineIDs (OID 1.3.6.1.6.3.15.1.1.4.0):

Из этого сообщения мы можем извлечь сразу и Engine ID и Boots/Time.

Данная процедура выполняется до начала обмена данными, однако же, если у вас какой то очень протяженный по времени опрос, и в какой то момент Engine ID на устройстве сменился, нужно обработать эту ситуацию и произвести определение Engine ID заново, а значит и сгенерировать новый ключевой материал, так как он зависит от Engine ID.

К обработке report сообщений мы еще вернемся, а пока перейдем к обработке ошибок при, например отсутствии запрашиваемого OID на оборудовании.

Проблема актуальна только для GET в котором запрашиваются данные сразу по нескольким OID’ам, суть проблемы:

Рассмотрим случай, в котором мы хотим получить данные по нескольким OID’ам:

   "1.3.6.1.2.1.1.5.0",   // sysName
   "1.3.6.1.2.1.1.6.0",   // sysLocation
   "1.3.6.1.2.1.1.99.0",  // Ошибочный OID (вернет noSuchObject)
   "1.3.6.1.2.1.1.100.0",  // Ошибочный OID (вернет noSuchObject)

Вот так выглядит Get запрос:

А так, ответ на него:

То есть формально, SNMP ошибки нет, однако запрашиваемые данные содержат значения noSuchObject. И обычно, это должен обработать пользователь библиотеки.

В PowerSNMPv3 происходит все иначе, Функция SNMP_GetMulti вернет только те данные, которые существуют, и вернет ошибку, ошибка структурирована и есть специальная функция ParseError, которая возвращает информацию, фатальная была ошибка или нет, список ошибочных OID и причину ошибки, и еще некоторую полезную информацию.

Что касается операции Set то тут ситуация абсолютно другая. Set операция атомарная, и если не удалось установить один из OID’ов то вся операция провалена и ничего не будет модифицировано.

Соответственно ошибка будет всегда фатальная.

Так же если мы выполним GET с несколькими OID’ами и все будут с ошибкой, ошибка тоже будет фатальная. Давайте посмотрим на SET:

И ответ на него:

Тут уже поле error-status указывает на тип ошибки, а error-index на тот OID (начиная с 1) по которому и произошла ошибка, если в списке будут другие ошибочные OID мы ничего о них не узнаем, индекс только один.

Однако в данных, которые прислал коммутатор, выглядит все так как будто все применилось.

И некоторые библиотеки отдают эти данные пользователю. PowerSNMPv3 же в этом случае, вернет пустые данные.

В этой статье, чтобы она не распухала, я ограничусь очень примитивным демонстрационным кодом, а подробный разбор сделаю в продолжении если читателю это вообще будет интересно.

Итак, напишем небольшой тест того, что выше описано.

package main

import (
    "flag"
    "fmt"

    PowerSNMP "github.com/OlegPowerC/powersnmpv3"
)

func main() {
    //Параметры командной строки
    Host := flag.String("h", "", "Switch or routers IP")
    SNMPuser := flag.String("u", "", "SNMP v3 USER")
    SNMPauthProtocol := flag.String("a", "", "SNMP auth protocol")
    SNMPauthPassword := flag.String("A", "", "SNMP auth password")
    SNMPprivProtocol := flag.String("x", "", "SNMP priv protocol")
    SNMPprivPassword := flag.String("X", "", "SNMP priv password")
    flag.Parse()

    //Осздаем описание коммутатора
    var dev PowerSNMP.NetworkDevice
    dev.IPaddress = *Host
    dev.SNMPparameters.SNMPversion = 3
    dev.Port = 161
    dev.SNMPparameters.Username = *SNMPuser
    dev.SNMPparameters.AuthProtocol = *SNMPauthProtocol
    dev.SNMPparameters.AuthKey = *SNMPauthPassword
    dev.SNMPparameters.PrivProtocol = *SNMPprivProtocol
    dev.SNMPparameters.PrivKey = *SNMPprivPassword

    //Инициализируем SNMP
    Ssess, InitErr := PowerSNMP.SNMP_Init(dev)
    if InitErr != nil {
       fmt.Println(InitErr)
       return
    }

    fmt.Println("=== выполним GET для единичного OID ===")
    swrongoid := "1.3.6.1.2.1.1.99.0"
    swrongoidiarr, parseerr := PowerSNMP.ParseOID(swrongoid)
    if parseerr != nil {
       fmt.Println(parseerr)
       return
    }
    GetsingleRes, GetsingleErr := Ssess.SNMP_Get(swrongoidiarr)
    if GetsingleErr != nil {
       snmpErr, commonErr := PowerSNMP.ParseError(GetsingleErr)
       if commonErr != nil {
          // Ошибка связанная с недоступностью хоста, шифрованием, еще чем то подобным
          fmt.Println("ошибка Network/system:", GetsingleErr)
          return
       }
       if snmpErr.IsFatal {
          // Фатальная SNMP ошибка
          fmt.Println("Фатальная ошибка SNMP, результат недоступен")
          for _, descr := range snmpErr.Oids {
             fmt.Println("Описание ошибки:", descr.ErrorDescription, descr)
          }
       } else {
          // Ошибка не фатальная
          fmt.Printf("Частичная ошибка, %d ошибочные OID'ы:", len(snmpErr.Oids))
          for _, oidErr := range snmpErr.Oids {
             fmt.Printf("  | %s", oidErr.ErrorDescription)
          }
       }
       fmt.Println("")
    } else {
       fmt.Println("--- Результат одиночного GET ---")
       for _, wl := range GetsingleRes {
          fmt.Println(PowerSNMP.Convert_OID_IntArrayToString_RAW(wl.RSnmpOID), "=", PowerSNMP.Convert_Variable_To_String(wl.RSnmpVar), ":", PowerSNMP.Convert_ClassTag_to_String(wl.RSnmpVar))
       }
    }

    fmt.Println("=== выполним GET с несколькими OID'ами ===")
    OidsStrings := []string{"1.3.6.1.2.1.1.6.0", "1.3.6.1.2.1.1.99.0", "1.3.6.1.2.1.1.5.0", "1.3.6.1.2.1.1.100.0"}
    OidsConverted := []PowerSNMP.SNMP_Packet_V2_Decoded_VarBind{}

    for _, OidSting := range OidsStrings {
       Ioid, IoidErr := PowerSNMP.Convert_OID_StringToIntArray_RAW(OidSting)
       if IoidErr != nil {
          fmt.Println(IoidErr)
          return
       }
       OidsConverted = append(OidsConverted, PowerSNMP.SNMP_Packet_V2_Decoded_VarBind{Ioid, PowerSNMP.SNMPvbNullValue})
    }
    GetRes2, verr2 := Ssess.SNMP_GetMulti(OidsConverted)

    fmt.Println("--- Проверим ошибки ---")
    if verr2 != nil {
       snmpErr, commonErr := PowerSNMP.ParseError(verr2)
       if commonErr != nil {
          // Ошибка связанная с недоступностью хоста, шифрованием, еще чем то подобным
          fmt.Println("ошибки Network/system:", commonErr)
       }

       if snmpErr.IsFatal {
          // Фатальная SNMP ошибка
          fmt.Println("Фатальная ошибка SNMP, результат недоступен")
          for _, descr := range snmpErr.Oids {
             fmt.Println("Описание ошибки:", descr.ErrorDescription, descr)
          }
       }

       // Ошибка не фатальная, результат каой то доступен
       fmt.Printf("Частичная ошибка, %d ошибочные OID'ы:", len(snmpErr.Oids))
       for _, oidErr := range snmpErr.Oids {
          fmt.Printf("  | %s", oidErr.ErrorDescription)
       }
       fmt.Println("")
    } else {
       fmt.Println("- Нет ошибок -")
    }

    fmt.Println("--- Результаты ---")
    for _, wl := range GetRes2 {
       fmt.Println(PowerSNMP.Convert_OID_IntArrayToString_RAW(wl.RSnmpOID), "=", PowerSNMP.Convert_Variable_To_String(wl.RSnmpVar), ":", PowerSNMP.Convert_ClassTag_to_String(wl.RSnmpVar))
    }

    fmt.Println("=== операция SET нескольких OID ===")
    VarData := []PowerSNMP.SNMPVar{PowerSNMP.SetSNMPVar_OctetString("Test 6.0"), PowerSNMP.SetSNMPVar_OctetString("Test 99.0"), PowerSNMP.SetSNMPVar_OctetString("Test 5.0")}
    SetStringOids := []string{"1.3.6.1.2.1.1.6.0", "1.3.6.1.2.1.1.99.0", "1.3.6.1.2.1.1.5.0"}
    SetDataVB := []PowerSNMP.SNMP_Packet_V2_Decoded_VarBind{}
    if len(VarData) == len(SetStringOids) {
       for VdataInd, StoidS := range SetStringOids {
          IoidS, IoidErrS := PowerSNMP.Convert_OID_StringToIntArray_RAW(StoidS)
          if IoidErrS != nil {
             fmt.Println(IoidErrS)
             return
          }
          SetDataVB = append(SetDataVB, PowerSNMP.SNMP_Packet_V2_Decoded_VarBind{IoidS, VarData[VdataInd]})
       }
    } else {
       fmt.Println("Не равно количество oid и данных")
       return
    }

    sdata, verres3 := Ssess.SNMP_SetMulti(SetDataVB)
    fmt.Println("--- Проверим ошибки ---")
    if verres3 != nil {
       snmpErr, commonErr := PowerSNMP.ParseError(verres3)
       if commonErr != nil {
          // Ошибка связанная с недоступностью хоста, шифрованием, еще чем то подобным
          fmt.Println("ошибки Network/system:", commonErr)
       }
       if snmpErr.IsFatal {
          // Фатальная ошибка
          fmt.Println("Фатальная ошибка SNMP, операция провалена")
          for _, descr := range snmpErr.Oids {
             fmt.Println("Описание ошибки:", descr.ErrorDescription)
          }
       } else {
          // Частичная ошибка, но ее не может быть при операйии SET
          fmt.Printf("Частичная ошибка, %d ошибочные OID'ы:", len(snmpErr.Oids))
          for _, oidErr := range snmpErr.Oids {
             fmt.Printf("  | %s", oidErr.ErrorDescription)
          }
       }

       fmt.Println("")
    } else {
       fmt.Println("- Нет ошибки -")
    }

    fmt.Println("--- Результат, который вернул SET ---")
    for _, wl := range sdata {
       fmt.Println(PowerSNMP.Convert_OID_IntArrayToString_RAW(wl.RSnmpOID), "=", PowerSNMP.Convert_Variable_To_String(wl.RSnmpVar), ":", PowerSNMP.Convert_ClassTag_to_String(wl.RSnmpVar))
    }
}

И выполним, указав тестовый коммутатор и все нужные параметры, результат должен быть примерно такой:

   === выполним GET для единичного OID ===
   Фатальная ошибка SNMP, результат недоступен
   Описание ошибки: 1.3.6.1.2.1.1.99.0 (status=128): NoSuchObject {[1 3 6 1 2 1 1 99 0] 128 1.3.6.1.2.1.1.99.0 (status=128): NoSuchObject}

   === выполним GET с несколькими OID'ами ===
   --- Проверим ошибки ---
   Частичная ошибка, 2 ошибочные OID'ы:  | 1.3.6.1.2.1.1.99.0 (status=128): NoSuchObject  | 1.3.6.1.2.1.1.100.0 (status=128): NoSuchObject
   --- Результаты ---
   1.3.6.1.2.1.1.6.0 = Test location : Universal OCTET STRING
   1.3.6.1.2.1.1.5.0 = powercsw01.powerc : Universal OCTET STRING
   === операция SET нескольких OID ===
   --- Проверим ошибки ---
   Фатальная ошибка SNMP, операция провалена
   Описание ошибки: 1.3.6.1.2.1.1.99.0 (status=11): CannotCreateVariable

   --- Результат, который вернул SET ---

Полезно выполнить в режиме отладки, поставить точки останова а так же запустить Wireshark.
В Wireshark можно указать параметры SNMP и он будет дешифровать данные и отображать расшифрованные.

Так же в библиотеку включен интеграционный тест.

Думаю на этом первую чась я закончу и милости просим в коментарии для обсуждения.