Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Программирование под Unix/Linux > Создание расширения для net-snmp


Автор: kyzia887 26.8.2011, 14:57
Собственно вопрос возник в следующем:
требуется написать расширение для net-snmp под linux на С++. В этом я новичок и хотел бы попросить у вас совета где и что можно почитать на данную тему. При написании клиента с использование этой библиотеки вопросов особо не возникло, а вот с расширением увы =( Сразу скажу что гуглил и неоднократно (парюсь этим вопросом уже 2й день), но результатов почти ни каких. Варианты с примерами на Перле или Питоне тоже не катят, тк я с ними вообще не работал.

Сама задача заключается в следующем: при запросе по snmp (oid = произвольный набор к примеру задаю) мое расширение опрашивает некую программу и возвращает 1 - если та запущена, и 0 - если нет. Хотелось бы для примера хоть небольшой кусок кода глянуть чтобы понять как все это дело собрать воедино.

Заранее благодарен.

Автор: svlary 29.8.2011, 06:34
Цитата
 В этом я новичок 

  В свое время я написал целиком и менеджера и агента для SNMP, но было это очень давно - больше 10 лет назад, так что - увы, почти ничего не помню...
Цитата
При написании клиента

Не очень понятно, что Вы имеете в виду... В SNMP используется терминология :
  • Менеджер - то, что использует сисадмин для настройки удаленных устройств
  • Агент - то, что работает на удаленных устройствах и обслуживает запросы менеджера

Цитата(kyzia887 @  26.8.2011,  14:57 Найти цитируемый пост)
где и что можно почитать на данную тему

 А на официальном сайте смотрели ? http://www.net-snmp.org/

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


Ну, на http://sourceforge.net/ я, по запросу "snmp"  сразу нашел 376 результатов. smile Пожалуйста - скачивайте и изучайте! Боюсь только, что толку от этого будет немного. Потому,  что SNMP (несмотря на свое название) - штука очень сложная и запутанная.  И никакого "небольшого кусочка кода" просто-напросто не существует.  Насколько я помню, последовательность действий программиста, при необходимости расширения возможностей существующей SNMP системы, выглядит приблизительно так :
  • Выбираете OID для новых сущностей и согласовываете его между агентом и менеджером.
  • Прописываете его в MIB (и там и там...) и компилируете саму MIB для получения ее двоичного варианта.
  • Согласовывает формат ASN.1 кодирования для добавленного OID.
  • В агенте добавляете новую веточку "case <новый OID>:" для обработки запросов GET и GETNEXT
  • В менеджере добавляете возможность работы с новым OID

Автор: kyzia887 29.8.2011, 09:49
Цитата

А на официальном сайте смотрели ? net-snmp.org


Да, перелопатил его. Как раз пример клиента / менеджера там неплохо на С описан и немного повозившись все заработало как часы, а вот расширение агента увы у них тока на перле.

Цитата

Ну, на sourceforge я, по запросу "snmp"  сразу нашел 376 результатов.


Спасибо за инфу, щас буду разбираться что к чему  smile 

Автор: kyzia887 30.8.2011, 14:51
Разобрался с проблемой  smile 

Net-SNMP поддерживает возможность использовать подгружаемый внешний модуль (далее - библиотека). 
Для того чтобы собрать данную библиотеку следует рассмотреть приведенный пример: http://www.net-snmp.org/wiki/index.php/TUT:Writing_a_Dynamically_Loadable_Object

Опишу мои действия вкратце (может кому-нибудь пригодится еще):

1) Для начала написал как в примере 2 файла (заголовочный и исходного текста С):

Заголовочный файл "SNMPExtended.h"
Код

#ifndef SNMPEXTENDED_H
#define SNMPEXTENDED_H

#ifdef __cplusplus
extern "C" {
#endif

/*
 * function declarations
 */
void init_SNMPExtended(void);

#ifdef __cplusplus
}
#endif

#endif // SNMPEXTENDED_H
  

Файл исходного текста С "SNMPExtended.c"
Код

#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/agent/net-snmp-agent-includes.h>
#include "SNMPExtended.h"

#ifdef __cplusplus
extern "C" {
#endif

/*
 * the variable we want to tie an OID to.  The agent will handle all
 * * GET and SET requests to this variable changing it's value as needed.
 */

static int SNMPExtended = 333;
static oid SNMPExtended_oid[] = { 1, 3, 6, 1, 4, 1, 8072, 2, 4, 1, 1, 333, 0 };

/*
 * our initialization routine, automatically called by the agent
 * (to get called, the function name must match init_FILENAME())
 */


void init_SNMPExtended(void)
{
   /*
    * a debugging statement.  Run the agent with -DnstAgentPluginObject to see
    * the output of this debugging statement.
    */
    DEBUGMSGTL(("SNMPExtended",
                "Initializing the SNMPExtended module\n"));


   /*
    * the line below registers our variables defined above as
    * accessible and makes it writable.  A read only version of any
    * of these registration would merely call
    * register_read_only_int_instance() instead.  The functions
    * called below should be consistent with your MIB, however.
    *
    * If we wanted a callback when the value was retrieved or set
    * (even though the details of doing this are handled for you),
    * you could change the NULL pointer below to a valid handler
    * function.
    */
    DEBUGMSGTL(("SNMPExtended",
                "Initalizing SNMPExtended scalar integer.  Default value = %d\n",
                SNMPExtended));

    netsnmp_register_int_instance("SNMPExtended",
                                  SNMPExtended_oid,
                                  OID_LENGTH(SNMPExtended_oid),
                                  &SNMPExtended, NULL);

    DEBUGMSGTL(("SNMPExtended",
                "Done initalizing SNMPExtended module\n"));
}


void deinit_SNMPExtended(void)
{
    unregister_mib(SNMPExtended_oid,OID_LENGTH(SNMPExtended_oid));
}

#ifdef __cplusplus
}
#endif



2) Далее собираю все это дело в нашу библиотеку:
Код

gcc -Wall -fPIC -c SNMPExtended.c
gcc -shared -o SNMPExtended.so SNMPExtended.o -ldl


3) Проверяем конфигурацию net-snmp:
Открываем файл /etc/snmp/snmpd.conf
В моем случае он имеет следующий вид:
Код

syslocation Server Room
syscontact Sysadmin (root@localhost)
rocommunity public 127.0.0.1
rwcommunity private 127.0.0.1


4) Далее создаем свой MIB-файл (отталкивался от примера):
NET-SNM-ITELECT-MIB.txt
Код

NET-SNMP-INTELECT-MIB DEFINITIONS ::= BEGIN

IMPORTS
    netSnmpExamples                FROM NET-SNMP-EXAMPLES-MIB
    OBJECT-TYPE, Integer32,
    MODULE-IDENTITY                FROM SNMPv2-SMI
    MODULE-COMPLIANCE, OBJECT-GROUP        FROM SNMPv2-CONF;

netSnmpIntelectMIB MODULE-IDENTITY
    LAST-UPDATE  "201108300000Z"        --30 august 2011
    ORGANIZATION "zzzz"
    CONTACT-INFO "zzzz"

    DESCRIPTION "Mib for intelect."

    ::= { netSnmpExamples 5 }

nstIntelectMibObjects        OBJECT IDENTIFIER ::= { netSnmpIntelectMIB 1 }

nstIntelectAgentModules        OBJECT IDENTIFIER ::= { nstIntelectMibObjects 1 }

SNMPExtended OBJECT-TYPE
    SYNTAX        Integer32
    MAX-ACCESS    read-write
    STATUS        current
    DESCRIPTION
          "This is test object for intelect"
    DEFVAL { 333 }
    ::= { nstIntelectAgentModules 1 }

END


И закидываем наш новый MIB к остальным MIB-файлам:
/usr/share/snmp/mibs/

После чего перезапускаем агент net-snmp:
cd /etc/init.d/
./snmpd restart


5) Проверяем структуру нашего нового MIB-файла:
cd /usr/bin
./snmptranslate -M+. -mNET-SNMP-INTELECT-MIB -Tp -IR netSnmpIntelectMIB


Получаем что-то вроде этого:
Код

+--netSnmpIntelectMIB(5)
      |
     +-- nstIntelectMibObjects(1)
            |
            +--nstIntelectAgentModules(1)
                  |
                  +-- -RW- Integer32 SNMPExtended(1)


6) После всего выше перечисленного можно пойти двумя путями добавления нашей новой библиотеки в агент:
     - прописать ее в конфигурационном файле snmpd.conf (я выбрал этот вариант) (не нужно каждый раз регать наш 
        внешний модуль)

     - выполнить 7 пунктов, описанных в мануале (ссылка вначале сообщения) (данный вариант работает только до   
        тех пор, пока не перезагрузим агент net-snmp! После перезагрузки все настройки слетают!
)

В моем случае отредактированный конфигурационный файл стал выглядеть так:
/etc/snmp/snmpd.conf
Код

syslocation Server Room
syscontact Sysadmin (root@localhost)
rocommunity public 127.0.0.1
rwcommunity private 127.0.0.1
dlmod SNMPExtended /home/intelect/soft/SNMPExtended.so


7) Перезапускаем агент net-snmp и запрашиваем необходимую нам информацию:
cd /usr/bin
./snmpget -c public -v 2c localhost .1.3.6.1.4.1.8072.2.4.1.1.333.0

Получим:
NET-SNMP-EXAMPLES-MIB::netSnmpExamples.4.1.1.333.0 = INTEGER: 333

Автор: svlary 31.8.2011, 05:54
Лично мне Ваше сообщение было очень интересным. Вспомнил - сколько сам мучился с этим делом,  когда никакой net-snmp не было ! smile
Один момент не понял. Вот вы регистрируете функцию-обработчик данного OID:

Цитата(kyzia887 @  30.8.2011,  14:51 Найти цитируемый пост)
   netsnmp_register_int_instance("SNMPExtended",
                                  SNMPExtended_oid,
                                  OID_LENGTH(SNMPExtended_oid),
                                  &SNMPExtended, NULL);


Но где сама функция ? (SNMPExtended)
Т.е. каким образом получается значение объекта { 1, 3, 6, 1, 4, 1, 8072, 2, 4, 1, 1, 333, 0 } и каким образом агент вызывает эту функцию ? При чтении и при записи...

Автор: kyzia887 31.8.2011, 09:59
Цитата(svlary @ 31.8.2011,  05:54)
Но где сама функция ? (SNMPExtended)
Т.е. каким образом получается значение объекта { 1, 3, 6, 1, 4, 1, 8072, 2, 4, 1, 1, 333, 0 } и каким образом агент вызывает эту функцию ? При чтении и при записи...

Как описывается в мануале, мы описываем инициализацию нашей библиотеки по средствам 
Код

void init_SNMPExtended(void)
{
...
}


и должны описать выгрузку нашей библиотеки по окончании работы
Код

void deinit_SNMPExtended(void)
{
...
}


т.е. общая структура получается void init_Название нашей библиотеки (void); и void deinit_Название (void);

При запуске самого net-snmp (если в его конфигурационном файле прописано подключение нашей библиотеки) наша библиотека регистрируется и присваивает новому инстансу заданный в ней OID (естественно MIB файл тоже должен присутствовать как я писал ранее).

После того как net-snmp запуститься можно обращаться к нашему новому элементу двумя способами:
  •  ./snmpget -c public -v 2c localhost .1.3.6.1.4.1.8072.2.4.1.1.333.0
  •  ./snmpget -c public -v 2c localhost NET-SNMP-INTELECT-MIB::SNMPExtended.0
В обоих вариантах получим один и тот же ответ.

Лично я дописал несколько строк кода для вывода конкретного значения по запросу:
SNMPExtebded.cpp
Код

#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/agent/net-snmp-agent-includes.h>
#include "SNMPExtended.h"

#include <QtSql>
#include <QFile>

#ifdef __cplusplus
extern "C" {
#endif

/*
 * the variable we want to tie an OID to.  The agent will handle all
 * * GET and SET requests to this variable changing it's value as needed.
 */

static int SNMPExtended = 0;
static oid SNMPExtended_oid[] = { 1, 3, 6, 1, 4, 1, 8072, 2, 4, 1, 1, 333, 0 };

/*
 * our initialization routine, automatically called by the agent
 * (to get called, the function name must match init_FILENAME())
 */


void init_SNMPExtended(void)
{

    QFile file;
    file.setFileName("SNMPExtended_LOG.txt");
    QTextStream out(&file);
    file.open(QIODevice::WriteOnly | QIODevice::Text);
    out << "-> Init - Ok\nSNMPExtended = " << SNMPExtended << endl;
    file.close();


   /*
    * a debugging statement.  Run the agent with -DnstAgentPluginObject to see
    * the output of this debugging statement.
    */
    DEBUGMSGTL(("SNMPExtended",
                "Initializing the SNMPExtended module\n"));


    file.open(QIODevice::Append | QIODevice::Text);
    out << "-> DEBUGMSGTL - Ok" << endl;
    file.close();

    /** Database connect **/
    int sizeOnDisk = 0;

    QSqlDatabase db = QSqlDatabase::addDatabase("QPSQL");
    db.setDatabaseName("bagira");
    db.setHostName("127.0.0.1");
    db.setUserName("postgres");
    db.setPassword("12345");
    if(!db.open())
    {
        /*
        QSqlError sqlError;
        sqlError = db.lastError();
        qDebug() << "DB connection failed! ERROR: " << sqlError.text();
        */
    }
    else
    {
        file.open(QIODevice::Append | QIODevice::Text);
        out << "-> DB open - Ok" << endl;
        file.close();

        QSqlQuery query;
        if(!query.exec("SELECT * FROM db_size_on_disk;"))
        {
            //qDebug() << "\nNot possible to read the table";
        }
        else
        {
            QSqlRecord rec = query.record();
            while(query.next())
            {
                sizeOnDisk = query.value(rec.indexOf("KB")).toInt();

                file.open(QIODevice::Append | QIODevice::Text);
                out << "-> DB size on disk = " << sizeOnDisk << endl;
                file.close();
            }
            SNMPExtended = sizeOnDisk;
        }
    }
    db.close();
    /** End database connect **/

    file.open(QIODevice::Append | QIODevice::Text);
    out << "-> DB close\nSNMPExtended = " << SNMPExtended << endl;
    file.close();

   /*
    * the line below registers our variables defined above as
    * accessible and makes it writable.  A read only version of any
    * of these registration would merely call
    * register_read_only_int_instance() instead.  The functions
    * called below should be consistent with your MIB, however.
    *
    * If we wanted a callback when the value was retrieved or set
    * (even though the details of doing this are handled for you),
    * you could change the NULL pointer below to a valid handler
    * function.
    */
    DEBUGMSGTL(("SNMPExtended",
                "Initalizing SNMPExtended scalar integer.  Default value = %d\n",
                SNMPExtended));

    file.open(QIODevice::Append | QIODevice::Text);
    out << "-> DEBUGMSGTL - Ok  Default value = " << SNMPExtended << endl;
    file.close();

    netsnmp_register_int_instance("SNMPExtended",
                                  SNMPExtended_oid,
                                  OID_LENGTH(SNMPExtended_oid),
                                  &SNMPExtended, NULL);

    file.open(QIODevice::Append | QIODevice::Text);
    out << "-> netsnmp_register_int_instance - Ok" << endl;
    file.close();

    DEBUGMSGTL(("SNMPExtended",
                "Done initalizing SNMPExtended module\n"));

    file.open(QIODevice::Append | QIODevice::Text);
    out << "-> DEBUGMSGTL - Done" << endl;
    file.close();
}


void deinit_SNMPExtended(void)
{
    unregister_mib(SNMPExtended_oid,OID_LENGTH(SNMPExtended_oid));

    QFile file;
    file.setFileName("SNMPExtended_LOG.txt");
    QTextStream out(&file);
    file.open(QIODevice::Append | QIODevice::Text);
    out << "-> unregister_mib - Ok" << endl;
    file.close();
}

#ifdef __cplusplus
}
#endif



Записывать значение так же как получать  smile 

Единственный минус - я пока что не знаю как сделать эти данные обновляемыми, т.е. в моем случае размер БД запишется в переменную только при инициализации библиотеки и все, и если мы к примеру при старте не смогли подключиться к БД то в значении будет висеть 0 и он уже не обновиться даже если подключение вновь появится =( Может конечно нужно настраивать сам net-snmp, пока не знаю... Думаю над этим вопросом. Тут еще возникает необходимость в обработке netsnmpset, т.е. чтобы не ставить это значение в переменную. а производить какие-то манипуляции скажем с БД и не более (значение инстанса при этом должно остаться прежним)...

Автор: fish9370 31.8.2011, 10:39
kyzia887, спасибо, интересная статья.. плюсанул..

Автор: svlary 1.9.2011, 05:49
Цитата(kyzia887 @  31.8.2011,  09:59 Найти цитируемый пост)
размер БД запишется в переменную только при инициализации библиотеки и все

Вот здесь :
Код

SNMPExtended = sizeOnDisk;

Т.е, получается, что Вы регистрируете не функцию доступа, а переменную, хранящую значение для MIB.
Действительно, у Вас она получает значение только при инициализации всей MIB. 
Цитата(kyzia887 @  31.8.2011,  09:59 Найти цитируемый пост)
Думаю над этим вопросом.

Мой вопрос был как раз про это... Насколько я помню,  мне пришлось найти обработчик 
Код
OID=1.3.6.1.4.1.<ID фирмы>

Вставить туда проверку на код нашей фирмы. Если код совпадал, то вызывался мой обработчик оставшейся части OID, который и делал проверку на то, какая именно переменная обрабатывается и что с ней надо делать (код запроса). 
И только после этого реализовывалась семантика запроса.... 

Автор: kyzia887 1.9.2011, 14:35
Цитата(svlary @ 1.9.2011,  05:49)
Мой вопрос был как раз про это... Насколько я помню,  мне пришлось найти обработчик 
Код
OID=1.3.6.1.4.1.<ID фирмы>

Вставить туда проверку на код нашей фирмы. Если код совпадал, то вызывался мой обработчик оставшейся части OID, который и делал проверку...

Разобрался с обработкой (по крайней мере на данном этапе мне этого должно хватить)  smile 
Пришлось переписать немного код. Теперь он выглядит следующим образом:
SNMPExtended.h
Код

#ifndef SNMPEXTENDED_H
#define SNMPEXTENDED_H

#ifdef __cplusplus
extern "C" {
#endif

#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/agent/net-snmp-agent-includes.h>

/*
 * function declarations
 */
void init_SNMPExtended(void);
int database_size();
int handle_node_snmpExtended(netsnmp_mib_handler          *handler,
                             netsnmp_handler_registration *reginfo,
                             netsnmp_agent_request_info   *reqinfo,
                             netsnmp_request_info         *requests);
void deinit_SNMPExtended(void);

#ifdef __cplusplus
}
#endif

#endif // SNMPEXTENDED_H


SNMPExtended.cpp
Код

#include "SNMPExtended.h"

#include <QtSql>
#include <QFile>

#ifdef __cplusplus
extern "C" {
#endif

/*
 * the variable we want to tie an OID to.  The agent will handle all
 * * GET and SET requests to this variable changing it's value as needed.
 */
static int SNMPExtended = 0;
static oid SNMPExtended_oid[] = { 1, 3, 6, 1, 4, 1, 8072, 2, 5, 1, 1, 1, 0 };

/*
 * our initialization routine, automatically called by the agent
 * (to get called, the function name must match init_FILENAME())
 */

void init_SNMPExtended(void)
{
    // - LOG -
    QFile file;
    file.setFileName("SNMPExtended_LOG.txt");
    QTextStream out(&file);
    file.open(QIODevice::WriteOnly | QIODevice::Text);
    out << "-> Init - Ok\n-> SNMPExtended = " << SNMPExtended << endl;
    file.close();
    // - LOG -

    DEBUGMSGTL(("SNMPExtended",
                "Initializing the SNMPExtended module\n"));


    // - LOG -
    file.open(QIODevice::Append | QIODevice::Text);
    out << "-> DEBUGMSGTL: Initializing the SNMPExtended module - Ok" << endl;
    file.close();
    // - LOG -

    //create handler registration
    netsnmp_handler_registration *my_test;

    my_test = netsnmp_create_handler_registration("Test SNMP Extended",
                                                  handle_node_snmpExtended,
                                                  SNMPExtended_oid,
                                                  OID_LENGTH(SNMPExtended_oid),
                                                  HANDLER_CAN_RWRITE);
    //register new instance
    netsnmp_register_instance(my_test);

    DEBUGMSGTL(("SNMPExtended",
                "Done initalizing SNMPExtended module\n"));

    // - LOG -
    file.open(QIODevice::Append | QIODevice::Text);
    out << "-> DEBUGMSGTL: Done initalizing SNMPExtended module." << endl;
    file.close();
    // - LOG -
}

int database_size()
{
    // - LOG -
    QFile file;
    file.setFileName("SNMPExtended_LOG.txt");
    QTextStream out(&file);
    file.open(QIODevice::Append | QIODevice::Text);
    out << "-> Init DataBase size func - Done" << endl;
    file.close();
    // - LOG -

    int sizeOnDisk = -1;

    /** Database connect **/
    QSqlDatabase db = QSqlDatabase::addDatabase("QPSQL");
    db.setDatabaseName("bagira");
    db.setHostName("192.168.16.38");
    db.setUserName("postgres");
    db.setPassword("12345678");
    if(!db.open())
    {
        // - LOG -
        file.open(QIODevice::Append | QIODevice::Text);
        out << "-> DataBase open - Error!" << endl;
        file.close();
        // - LOG -
    }
    else
    {
        // - LOG -
        file.open(QIODevice::Append | QIODevice::Text);
        out << "-> DataBase open - Ok" << endl;
        file.close();
        // - LOG -

        QSqlQuery query;
        if(!query.exec("SELECT * FROM db_size_on_disk;"))
        {
            // - LOG -
            file.open(QIODevice::Append | QIODevice::Text);
            out << "-> DataBase - Not possible to read the table!" << endl;
            file.close();
            // - LOG -
        }
        else
        {
            QSqlRecord rec = query.record();
            while(query.next())
            {
                sizeOnDisk = query.value(rec.indexOf("KB")).toInt();

                // - LOG -
                file.open(QIODevice::Append | QIODevice::Text);
                out << "-> DataBase size on disk = " << sizeOnDisk << endl;
                file.close();
                // - LOG -
            }
        }
    }
    db.close();

    // - LOG -
    file.open(QIODevice::Append | QIODevice::Text);
    out << "-> DataBase close - Ok  Return value = " << sizeOnDisk << endl;
    file.close();
    // - LOG -
    /** End database connect **/

    return sizeOnDisk;
}

int handle_node_snmpExtended(netsnmp_mib_handler          *handler,
                             netsnmp_handler_registration *reginfo,
                             netsnmp_agent_request_info   *reqinfo,
                             netsnmp_request_info         *requests)
{
    struct snmp_pdu *response;
    struct variable_list *vars;

    u_long bytesWaiting = -12345;

    DEBUGMSGTL(("SNMPExtended",
                "Initalizing SNMPExtended scalar integer.  Default value = %d\n",
                SNMPExtended));

    QFile file;
    file.setFileName("SNMPExtended_LOG.txt");
    QTextStream out(&file);

    //       ->> snmpget -c public -v 2c localhost .1.3.6.1.4.1.8072.2.5.1.1.1.0
    //return ->> NET-SNMP-EXAMPLES-MIB::netSnmpExamples.5.1.1.1.0 = INTEGER: 38048
    if (reqinfo->mode == MODE_GET)
    {
        // - LOG -
        file.open(QIODevice::Append | QIODevice::Text);
        out << "\n---> handle_node_snmpExtended Init - Ok\n-> DEBUGMSGTL - Ok  Default value = " << SNMPExtended << endl;
        file.close();
        // - LOG -

        SNMPExtended = database_size(); //get database size

        bytesWaiting = (u_long) SNMPExtended;
        snmp_set_var_typed_value(requests->requestvb, ASN_INTEGER, (u_char *) & bytesWaiting, sizeof(int)); //set value

        // - LOG -
        file.open(QIODevice::Append | QIODevice::Text);
        out << "-> MODE_GET - Ok  Value = " << bytesWaiting << endl;
        file.close();
        // - LOG -

        return SNMP_ERR_NOERROR;
    }

    //       ->> snmpset -c private -v 2c localhost .1.3.6.1.4.1.8072.2.5.1.1.1.0 i 12345
    //return ->> NET-SNMP-EXAMPLES-MIB::netSnmpExamples.5.1.1.1.0 = INTEGER: 12345
    //----------------------------------- ore --------------------------------------------
    //       ->> snmpset -c private -v 2c localhost .1.3.6.1.4.1.8072.2.5.1.1.1.0 i 111
    //return ->> NET-SNMP-EXAMPLES-MIB::netSnmpExamples.5.1.1.1.0 = INTEGER: 2
    if (reqinfo->mode == MODE_SET_COMMIT)
    {
        // - LOG -
        file.open(QIODevice::Append | QIODevice::Text);
        out << "\n---> handle_node_snmpExtended Init - Ok\n-> DEBUGMSGTL - Ok  Default value = " << SNMPExtended << endl;
        file.close();
        // - LOG -

        response = reqinfo->asp->orig_pdu;
        vars = response->variables;

        int varInt = -111;

        if(vars->type == ASN_INTEGER)
        {
            varInt = (int) *vars->val.integer;

            if(111 == varInt)
            {
                bytesWaiting = 2;
            }
            else
            {
                bytesWaiting = (u_long) varInt;
            }
        }
        else
            return SNMP_ERR_GENERR;

        SNMPExtended = (int) bytesWaiting; //save default value
        snmp_set_var_typed_value(requests->requestvb, ASN_INTEGER, (u_char *) & bytesWaiting, sizeof(int)); //set value

        // - LOG -
        file.open(QIODevice::Append | QIODevice::Text);
        out << "-> MODE_SET_COMMIT - Ok  Value = " << bytesWaiting << endl;
        file.close();
        // - LOG -

        return SNMP_ERR_NOERROR;
    }
    else
    {
        //if other MODE
        return SNMP_ERR_NOERROR;
    }
}

void deinit_SNMPExtended(void)
{
    unregister_mib(SNMPExtended_oid,OID_LENGTH(SNMPExtended_oid));

    // - LOG -
    QFile file;
    file.setFileName("SNMPExtended_LOG.txt");
    QTextStream out(&file);
    file.open(QIODevice::Append | QIODevice::Text);
    out << "-> Unregister_mib - Ok" << endl;
    file.close();
    // - LOG -
}

#ifdef __cplusplus
}
#endif


Прокомментировал все что могло бы вызвать вопросы в самом коде.
MIB-файл и настройки net-snmp не трогал.
Данный вариант обрабатывает каждый запрос который поступает от клиента/менеджера:

Код

intelect@linux-bf9n:/usr/bin> ./snmpset -c private -v 2c localhost .1.3.6.1.4.1.8072.2.5.1.1.1.0 i 11123
NET-SNMP-EXAMPLES-MIB::netSnmpExamples.5.1.1.1.0 = INTEGER: 11123

intelect@linux-bf9n:/usr/bin> ./snmpget -c public -v 2c localhost .1.3.6.1.4.1.8072.2.5.1.1.1.0
NET-SNMP-EXAMPLES-MIB::netSnmpExamples.5.1.1.1.0 = INTEGER: 38048

intelect@linux-bf9n:/usr/bin> ./snmpset -c private -v 2c localhost .1.3.6.1.4.1.8072.2.5.1.1.1.0 i 111
NET-SNMP-EXAMPLES-MIB::netSnmpExamples.5.1.1.1.0 = INTEGER: 2
 

Надеюсь не зря разгребал эту штуку   smile  и это еще кому-то пригодиться  smile 

Автор: svlary 2.9.2011, 05:35
Цитата(kyzia887 @  1.9.2011,  14:35 Найти цитируемый пост)
это еще кому-то пригодиться

  Ну, во всяком случае, мне было очень интересно ! smile Спасибо !

Автор: kyzia887 7.9.2011, 13:42
Довинтил во все выше описанное еще создание таблицы. 
MIB-файл теперь имеет следующую структуру:
NET-SNM-ITELECT-MIB.txt
Код

NET-SNMP-INTELECT-MIB DEFINITIONS ::= BEGIN
IMPORTS
    netSnmpExamples                FROM NET-SNMP-EXAMPLES-MIB
    OBJECT-TYPE, Integer32,
    MODULE-IDENTITY                FROM SNMPv2-SMI
    MODULE-COMPLIANCE, OBJECT-GROUP        FROM SNMPv2-CONF;

netSnmpIntelectMIB MODULE-IDENTITY
    LAST-UPDATE  "201108300000Z"        --30 august 2011
    ORGANIZATION "zzzz"
    CONTACT-INFO "zzzz"
    DESCRIPTION "Mib for intelect."
    ::= { netSnmpExamples 5 }

nstIntelectMibObjects        OBJECT IDENTIFIER ::= { netSnmpIntelectMIB 1 }

nstIntelectAgentModules        OBJECT IDENTIFIER ::= { nstIntelectMibObjects 1 }

nstIntelectTables        OBJECT IDENTIFIER ::= { nstIntelectAgentModules 1 }

SNMPExtended OBJECT-TYPE
    SYNTAX        Integer32
    MAX-ACCESS    read-write
    STATUS        current
    DESCRIPTION
          "This is test object for intelect"
    DEFVAL { 333 }
    ::= { nstIntelectAgentModules 1 }

MyTestTables OBJECT-TYPE
    SYNTAX        SEQUENCE OF NSTMyTestTables
    MAX-ACCESS    read-only
    STATUS        current
    DESCRIPTION
        "This test tables"
    ::= { nstIntelectTables 1 }

nstMyTestTables OBJECT-TYPE
    SYNTAX        NSTMyTestTables
    MAX-ACCESS    read-only
    STATUS        current
    DESCRIPTION
        "A row value"
    INDEX { nstTableColumn }
    ::= { MyTestTables 1 }

NSTMyTestTables ::= SEQUENCE {
    nstTableColumn        INTEGER,
    nstTableColumnNext    INTEGER
}

nstTableColumn OBJECT-TYPE
    SYNTAX        INTEGER
    MAX-ACCESS    read-only
    STATUS        current
    DESCRIPTION " "

    ::= { nstMyTestTables 1 }

nstTableColumnNext OBJECT-TYPE
    SYNTAX        INTEGER
    MAX-ACCESS    read-only
    STATUS        current
    DESCRIPTION " "

    ::= { nstMyTestTables 2 }

END


Проверяем структуру нового MIB-файла (см. выше) и должны получить что-то вроде этого:
Код

+--netSnmpIntelectMIB(5)
      |
     +-- nstIntelectMibObjects(1)
            |
            +--nstIntelectAgentModules(1)
                  |
                  +-- -RW- Integer32 SNMPExtended(1)
                  |
                  +--MyTestTables(1)
                  |     |
                  |     +--nstMyTestTables(1)
                  |           |  Index: nstTableColumn
                  |           |
                  |           +-- -R-- INTEGER   nstTableColumn(1)
                  |           +-- -R-- INTEGER   nstTableColumnNext(2)
                  |                  
                  +--nstIntelectTables(1)


Конфигурационный файл Net-SNMP стал выглядеть так (подключаю дополнительный новый модуль, который будет создавать таблицу):
snmpd.conf
Код

syslocation Server Room
syscontact Sysadmin (root@localhost)
rocommunity public 127.0.0.1
rwcommunity private 127.0.0.1
dlmod SNMPExtended /home/intelect/soft/SNMPExtended.so
dlmod MyTestTables     /home/intelect/soft/MyTestTables.so


Сам код имеет следующий вид:
MyTestTables.h
Код

#ifndef MYTESTTABLES_H
#define MYTESTTABLES_H

#ifdef __cplusplus
extern "C" {
#endif

#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/agent/net-snmp-agent-includes.h>

// function declarations
void init_MyTestTables(void);
int handle_node_MyTestTables(netsnmp_mib_handler          *handler,
                             netsnmp_handler_registration *reginfo,
                             netsnmp_agent_request_info   *reqinfo,
                             netsnmp_request_info         *requests);
void deinit_MyTestTables(void);

#ifdef __cplusplus
}
#endif

#endif // MYTESTTABLES_H



MyTestTables.cpp
Код

#include "MyTestTables.h"

#ifdef __cplusplus
extern "C" {
#endif

netsnmp_table_data_set *table_set;
oid MyTestTables_oid[] = { 1, 3, 6, 1, 4, 1, 8072, 2, 5, 1, 1, 1 };

void init_MyTestTables(void)
{
    DEBUGMSGTL(("MyTestTables",
                "Initializing the MyTestTables module\n"));

    table_set = netsnmp_create_table_data_set("MyTestTables");

    // allow the creation of new rows via SNMP SETs
    table_set->allow_creation = 1;

    // set up what a row "should" look like, starting with the index
    netsnmp_table_dataset_add_index(table_set, ASN_INTEGER);

    // define what the columns should look like.
    netsnmp_table_set_add_default_row(table_set,
                                      // column 2 = INTEGER,
                                      // writable = 1,
                                      // default value = NULL,
                                      // default value len = 0
                                      2, ASN_INTEGER, 1, NULL, 0);

    // if we wanted to handle specific data in a specific way, or note
    // when requests came in we could change the NULL below to a valid
    // handler method in which we could over ride the default
    // behaviour of the table_dataset helper
    netsnmp_handler_registration *MyTestTablesModule;
    MyTestTablesModule = netsnmp_create_handler_registration("Module MyTestTables",
                                                             handle_node_MyTestTables,
                                                             MyTestTables_oid,
                                                             OID_LENGTH(MyTestTables_oid),
                                                             HANDLER_CAN_RONLY);


    netsnmp_register_table_data_set(MyTestTablesModule,
                                    table_set,
                                    NULL);

    // register the table
    netsnmp_register_auto_data_table(table_set, NULL);

    DEBUGMSGTL(("MyTestTables",
                "Done initalizing MyTestTables module\n"));
}

int handle_node_MyTestTables(netsnmp_mib_handler *handler,
                             netsnmp_handler_registration *reginfo,
                             netsnmp_agent_request_info *reqinfo,
                             netsnmp_request_info *requests)
{
    int numberProc = 5;
    for(int i = 0; i < numberProc; i++)
    {
        //add row in table
        // create the a row for the table, and add the data
        netsnmp_table_row *row;
        row = netsnmp_create_table_data_row();

        // set the index to the table row
        u_long tableIndex = (u_long) i;
        netsnmp_table_row_add_index(row, ASN_INTEGER, (u_char*) & tableIndex, sizeof(int));

        // set column 2 to be the table
        u_long tableValue = (u_long) (numberProc + i);
        netsnmp_set_row_column(row, 2, ASN_INTEGER, (u_char *) & tableValue, sizeof(int));
        netsnmp_mark_row_column_writable(row, 2, 1);  // make writable via SETs

        // add the row to the table
        netsnmp_table_dataset_add_row(table_set, row);
    }

    return SNMP_ERR_NOERROR;
}

void deinit_MyTestTables(void)
{
    netsnmp_table_dataset_remove_and_delete_row(table_set, NULL);
    netsnmp_delete_table_data_set(table_set);
    table_set = NULL;
}

#ifdef __cplusplus
}
#endif


При запросе информации получим следующее:
Код

intelect@linux-bf9n:/usr/bin> ./snmpwalk -c public -v 2c localhost .1.3.6.1.4.1.8072.2.5.1.1.1.1.2
NET-SNMP-EXAMPLES-MIB::netSnmpExamples.5.1.1.1.1.2.0 = INTEGER: 5
NET-SNMP-EXAMPLES-MIB::netSnmpExamples.5.1.1.1.1.2.1 = INTEGER: 6
NET-SNMP-EXAMPLES-MIB::netSnmpExamples.5.1.1.1.1.2.2 = INTEGER: 7
NET-SNMP-EXAMPLES-MIB::netSnmpExamples.5.1.1.1.1.2.3 = INTEGER: 8
NET-SNMP-EXAMPLES-MIB::netSnmpExamples.5.1.1.1.1.2.4 = INTEGER: 9


Если возникнут вопросы, пишите, попробую объяснить или разбраться что к чему  smile 

Автор: kyzia887 9.9.2011, 10:36
Небольшое дополнение по таблицам  smile 
Для того чтобы таблица каждый раз пересоздавала строки переписал код следующим образом:
MyTestTables.cpp
Код

#include "MyTestTables.h"
#ifdef __cplusplus
extern "C" {
#endif

int numberProcessFound = 0; //defaul number process
netsnmp_table_data_set *table_set;
oid MyTestTables_oid[] = { 1, 3, 6, 1, 4, 1, 8072, 2, 5, 1, 1, 1 };
void init_MyTestTables(void)
{
    DEBUGMSGTL(("MyTestTables",
                "Initializing the MyTestTables module\n"));
    table_set = netsnmp_create_table_data_set("MyTestTables");
    // allow the creation of new rows via SNMP SETs
    table_set->allow_creation = 1;
    // set up what a row "should" look like, starting with the index
    netsnmp_table_dataset_add_index(table_set, ASN_INTEGER);
    // define what the columns should look like.
    netsnmp_table_set_add_default_row(table_set,
                                      // column 2 = INTEGER,
                                      // writable = 1,
                                      // default value = NULL,
                                      // default value len = 0
                                      2, ASN_INTEGER, 1, NULL, 0);
    // if we wanted to handle specific data in a specific way, or note
    // when requests came in we could change the NULL below to a valid
    // handler method in which we could over ride the default
    // behaviour of the table_dataset helper
    netsnmp_handler_registration *MyTestTablesModule;
    MyTestTablesModule = netsnmp_create_handler_registration("Module MyTestTables",
                                                             handle_node_MyTestTables,
                                                             MyTestTables_oid,
                                                             OID_LENGTH(MyTestTables_oid),
                                                             HANDLER_CAN_RONLY);
    netsnmp_register_table_data_set(MyTestTablesModule,
                                    table_set,
                                    NULL);
    // register the table
    netsnmp_register_auto_data_table(table_set, NULL);
    DEBUGMSGTL(("MyTestTables",
                "Done initalizing MyTestTables module\n"));
}
int handle_node_MyTestTables(netsnmp_mib_handler *handler,
                             netsnmp_handler_registration *reginfo,
                             netsnmp_agent_request_info *reqinfo,
                             netsnmp_request_info *requests)
{
    /** delete all row **/
    if(numberProcessFound != 0)
    {
        netsnmp_table_row *firstRow = netsnmp_table_data_set_get_first_row(table_set);
        while(firstRow != NULL)
        {
            netsnmp_table_row *nextRow = netsnmp_table_data_set_get_next_row(table_set, firstRow);
            netsnmp_table_dataset_remove_row(table_set, firstRow);
            firstRow = nextRow;
        }
    }
    /** -------------- **/

    int numberProc = 5;
    numberProcessFound = numberProc; //set new defaul number process

    for(int i = 0; i < numberProc; i++)
    {
        //add row in table
        // create the a row for the table, and add the data
        netsnmp_table_row *row;
        row = netsnmp_create_table_data_row();
        // set the index to the table row
        u_long tableIndex = (u_long) i;
        netsnmp_table_row_add_index(row, ASN_INTEGER, (u_char*) & tableIndex, sizeof(int));
        // set column 2 to be the table
        u_long tableValue = (u_long) (numberProc + i);
        netsnmp_set_row_column(row, 2, ASN_INTEGER, (u_char *) & tableValue, sizeof(int));
        netsnmp_mark_row_column_writable(row, 2, 1);  // make writable via SETs
        // add the row to the table
        netsnmp_table_dataset_add_row(table_set, row);
    }
    return SNMP_ERR_NOERROR;
}
void deinit_MyTestTables(void)
{
    netsnmp_table_dataset_remove_and_delete_row(table_set, NULL);
    netsnmp_delete_table_data_set(table_set);
    table_set = NULL;
}
#ifdef __cplusplus
}
#endif


Все остальное осталось прежним. Всем успехов  smile 

Автор: azote 30.10.2012, 12:50
Спасибо за статью. 
Есть такой вопрос. Могу ли я как-то создать обработчик всех OID'ов начиная с определенного, например, .1.3.6.1.4.1.8072...., и уже в своем обработчике перебирать дочерние номера и решать какому из них какое значение устанавливать?
Ведь, как я понял, у Вас в примере показывается, как обрабатывать определенные значения OID'ов.
И еще вопрос. Могу ли я создать подобное расширение агента без внедрение соответствующего MIB-модуля?

Автор: kyzia887 21.12.2012, 15:40
Цитата

Спасибо за статью. 
Есть такой вопрос. Могу ли я как-то создать обработчик всех OID'ов начиная с определенного, например, .1.3.6.1.4.1.8072...., и уже в своем обработчике перебирать дочерние номера и решать какому из них какое значение устанавливать?
Ведь, как я понял, у Вас в примере показывается, как обрабатывать определенные значения OID'ов.
И еще вопрос. Могу ли я создать подобное расширение агента без внедрение соответствующего MIB-модуля? 


Вот на счет первого вопроса к сожалению не подскажу, т.к. сам это задачу не решал, а по поводу внедрения MIB-файла, то вроде бы можно и без него.  smile 

Автор: igormat 17.9.2013, 11:33
Добрый день, а никто не может посоветовать литературу по Net-SNMP с точки зрения девелоперов? А то туториалы на официальном сайте очень скудные.

Автор: kyzia887 17.9.2013, 11:37
Цитата(igormat @ 17.9.2013,  11:33)
Добрый день, а никто не может посоветовать литературу по Net-SNMP с точки зрения девелоперов? А то туториалы на официальном сайте очень скудные.

Лично я, когда занимался этим вопросом, так толком ни чего и не нашел, кроме различных статей и офф. сайта.

Автор: igormat 17.9.2013, 14:09
Тогда вопрос. Как из запроса вычленить нужную ячейку таблицы и  ответить? Из предыдущего примера не видно где именно задается значение в ОТВЕТЕ. Или же мы просто задаем значение в таблице, а уже сам Net-SNMP достает значение?

Автор: igormat 17.9.2013, 17:46
И не могли бы привести статьи касаемо обработки таблиц в Net-SNMP?

Автор: Vorlon 16.10.2013, 11:01
kyzia887,  здравствуй.
Если при общении с роутером на основе примера 
Код
net-snmp.org
 при подаче OID snmpget возвращает:
No such Object available on this agent at this OID - с чем это связано?  Что необходимо сделать на уровне кода, чтобы роутер всё-таки ответил?
(Только учусь и страдаю)

Добавлено через 9 минут и 31 секунду
Пример - если необходимо - сразу готов сюда выложить. )

Автор: kyzia887 16.10.2013, 11:11
Цитата

Vorlon:
No such Object available on this agent at this OID - с чем это связано?  Что необходимо сделать на уровне кода, чтобы роутер всё-таки ответил?


Скорее всего у вас не подключен MIB файл с OID-ом по которому вы обращаетесь. Посмотрите в начале темы, я там описывал как что делал. Но это все проверялось на локальных машинах. Железки я вообще не трогал, т.к. это не требовалось.

Автор: Vorlon 16.10.2013, 11:16
MIB-файл есть.

Для его подключения надо обязательно создавать SNMPExtended.c и h и собирать в so?

Можно MIB-файл напрямую привязать в snmpd.conf?

Добавлено через 8 минут и 9 секунд
Или по имени и пути MIB-файла привязать его в самом коде, например, в конструкторе?

Автор: kyzia887 16.10.2013, 12:53
MIB файл только описывает что и как запрашивать. Файл библиотеки (*.so - linux, *.dll - windows) собственно и выдает результат.

Автор: kyzia887 11.2.2014, 12:46
Внесу небольшие поправки во все выше описанное. 
Прошло чуть меньше 3-х лет и мне снова пришлось залезть в свои исходники и наклепать очередной модуль для Net-SNMP.
Увы. Он не завелся вот так сразу, что было для меня весьма удивительно, ведь делал все в точности как сам же и писал ранее.

Опишу две основные ошибки:
  • Не корректный MIB файл
  • Не корректный путь к библиотеке в snmpd.conf
Много писать не буду, а выложу исходники и примеры запросов.
PS: Использовал Qt 4.8.4

PhoenixNetSnmpTableModule.pro
Код

QT      -= gui

TARGET   = PhoenixServicesTable
TEMPLATE = lib
DESTDIR  = dist

SOURCES += PhoenixNetSnmpTableModule.cpp

HEADERS += PhoenixNetSnmpTableModule.h


PhoenixNetSnmpTableModule.h
Код

#ifndef PHOENIXNETSNMPTABLEMODULE_H
#define PHOENIXNETSNMPTABLEMODULE_H

#ifdef __cplusplus
extern "C" {
#endif

#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/agent/net-snmp-agent-includes.h>

// function declarations
void init_PhoenixServicesTable(void);

int handle_node_PhoenixNetSnmpTableModule(netsnmp_mib_handler          *handler,
                                                                                         netsnmp_handler_registration *reginfo,
                                                                                         netsnmp_agent_request_info   *reqinfo,
                                                                                         netsnmp_request_info         *requests);

void deinit_PhoenixServicesTable(void);

#ifdef __cplusplus
}
#endif

#endif // PHOENIXNETSNMPTABLEMODULE_H


PhoenixNetSnmpTableModule.cpp
Код

#include "PhoenixNetSnmpTableModule.h"
#include <QProcess>
#include <QTextStream>
#include <QVector>

#ifdef __cplusplus
extern "C" {
#endif

int numberProcessFound = 0;
netsnmp_table_data_set *table_set;
oid PhoenixNetSnmpTableModule_oid[] = { 1, 3, 6, 1, 4, 1, 8072, 2, 5, 1, 1, 1, 1, 2 };

void init_PhoenixServicesTable(void)
{
    DEBUGMSGTL(("PhoenixServicesTable",
                                  "Initializing the PhoenixServicesTable module\n"));

    table_set = netsnmp_create_table_data_set("PhoenixServicesTable");

    // allow the creation of new rows via SNMP SETs
    table_set->allow_creation = 1;

    // set up what a row "should" look like, starting with the index
    netsnmp_table_dataset_add_index(table_set, ASN_OCTET_STR);

    // define what the columns should look like.
    netsnmp_table_set_add_default_row(table_set,
                                                                         // column 2 = ASN_OCTET_STR,
                                                                         // writable = 1,
                                                                         // default value = NULL,
                                                                         // default value len = 0
                                                                         2, ASN_OCTET_STR, 1, NULL, 0);

    // if we wanted to handle specific data in a specific way, or note
    // when requests came in we could change the NULL below to a valid
    // handler method in which we could over ride the default
    // behaviour of the table_dataset helper
    netsnmp_handler_registration *PhoenixNetSnmpTableModule;
    PhoenixNetSnmpTableModule = netsnmp_create_handler_registration("Module PhoenixServicesTable",
                                                                                                                                      handle_node_PhoenixNetSnmpTableModule,
                                                                                                                                      PhoenixNetSnmpTableModule_oid,
                                                                                                                                      OID_LENGTH(PhoenixNetSnmpTableModule_oid),
                                                                                                                                      HANDLER_CAN_RONLY);


    netsnmp_register_table_data_set(PhoenixNetSnmpTableModule,
                                                                    table_set,
                                                                    NULL);

    // register the table
    netsnmp_register_auto_data_table(table_set, NULL);

    DEBUGMSGTL(("PhoenixServicesTable",
                                  "Done initalizing PhoenixServicesTable module\n"));
}

int handle_node_PhoenixNetSnmpTableModule(netsnmp_mib_handler *handler,
                                                                                         netsnmp_handler_registration *reginfo,
                                                                                         netsnmp_agent_request_info *reqinfo,
                                                                                         netsnmp_request_info *requests)
{
    /** delete all row **/
    if(numberProcessFound != 0) {
        netsnmp_table_row *firstRow = netsnmp_table_data_set_get_first_row(table_set);
        while(firstRow != NULL) {
            netsnmp_table_row *nextRow = netsnmp_table_data_set_get_next_row(table_set, firstRow);
            netsnmp_table_dataset_remove_row(table_set, firstRow);
            firstRow = nextRow;
        }
    }
    /** -------------- **/

    QString allProcessMap = "";
    QProcess process;
    QStringList commandProcess;
    commandProcess << "ax";
    process.start("ps", commandProcess);
    if(!process.waitForStarted(30000)) {
        return SNMP_ERR_GENERR;
    }

    QVector <QString> processValue;

    int readStop = 0;
    while(readStop != 1) {
        if(process.waitForReadyRead(30000)) {
            QByteArray processLine = process.readAllStandardOutput();
            QTextStream processOutLocal(processLine);
            allProcessMap = allProcessMap + processOutLocal.readAll();
        } else {
            //qDebug() << "Error Read!";
            readStop = 1;
        }
    }
    process.close();

    QTextStream processOut(&allProcessMap);
    while(!processOut.atEnd()) {
        QString readLine = processOut.readLine();
        QStringList splitReadLine = readLine.split(" ");
        for(int i = 0; i < splitReadLine.size(); i++) {
            QString readLineNext = splitReadLine.operator [](i);
            QStringList splitReadLineNext = readLineNext.split("/");
            for(int j = 0; j < splitReadLineNext.size(); j++) {
                if(splitReadLineNext.operator [](j) == "qtcreator") { //find qtcreator
                    processValue.push_back("qtcreator|ok");
                }
            }
        }
    }

    numberProcessFound = processValue.size(); //save default number process
    if(processValue.size() != 0) {
        for(int i = 0; i < processValue.size(); i++) {
            // add row in table
            // create the a row for the table, and add the data
            netsnmp_table_row *row;
            row = netsnmp_create_table_data_row();

            // set the index to the table row
            u_long tableIndex = (u_long) i;
            netsnmp_table_row_add_index(row, ASN_INTEGER, (u_char*) & tableIndex, sizeof(int));

            // set column 2 to be the table
            QString tableValue = processValue.operator [](i);
            netsnmp_set_row_column(row, 2, ASN_OCTET_STR, (u_char *) tableValue.toStdString().c_str(), tableValue.size());
            netsnmp_mark_row_column_writable(row, 2, 1);  // make writable via SETs

            // add the row to the table
            netsnmp_table_dataset_add_row(table_set, row);
        }
    } else {
        //return SNMP_ERR_GENERR;
    }

    return SNMP_ERR_NOERROR;
}

void deinit_PhoenixServicesTable(void)
{
    netsnmp_table_dataset_remove_and_delete_row(table_set, NULL);
    netsnmp_delete_table_data_set(table_set);
    table_set = NULL;
}

#ifdef __cplusplus
}
#endif


NET-SNMP-PHOENIX-MIB.txt (копируем в /usr/share/snmp/mibs/)
Код

NET-SNMP-PHOENIX-MIB DEFINITIONS ::= BEGIN

IMPORTS
    netSnmpExamples                FROM NET-SNMP-EXAMPLES-MIB
    OBJECT-TYPE, Integer32,
    MODULE-IDENTITY                FROM SNMPv2-SMI
    MODULE-COMPLIANCE, OBJECT-GROUP        FROM SNMPv2-CONF;

netSnmpPhoenixMIB MODULE-IDENTITY
    LAST-UPDATE  "201402110000Z"        --11 february 2014
    ORGANIZATION " "
    CONTACT-INFO "SPb"

    DESCRIPTION "Mib for phoenix modules."

    ::= { netSnmpExamples 5 }

PhoenixMibObjects        OBJECT IDENTIFIER ::= { netSnmpPhoenixMIB 1 }

PhoenixAgentModules        OBJECT IDENTIFIER ::= { PhoenixMibObjects 1 }

PhoenixTables        OBJECT IDENTIFIER ::= { PhoenixAgentModules 1 }

PhoenixServicesTable OBJECT-TYPE
    SYNTAX        SEQUENCE OF PhoenixServicesDataTable
    MAX-ACCESS    read-only
    STATUS        current
    DESCRIPTION
        "This Phoenix Services tables"
    ::= { PhoenixTables 1 }

PhoenixServicesDataTable OBJECT-TYPE
    SYNTAX        PhoenixServicesDataTable
    MAX-ACCESS    read-only
    STATUS        current
    DESCRIPTION
        "A row value"
    INDEX { PhoenixServicesData }
    ::= { PhoenixServicesTable 1 }

PhoenixServicesDataTable ::= SEQUENCE {
    PhoenixServicesData        OCTET STRING,
    PhoenixServicesDataNumber    INTEGER
}

PhoenixServicesData OBJECT-TYPE
    SYNTAX        OCTET STRING
    MAX-ACCESS    read-only
    STATUS        current
    DESCRIPTION
        "Service Data for Phoenix"

    ::= { PhoenixServicesDataTable 1 }

PhoenixServicesDataNumber OBJECT-TYPE
    SYNTAX        INTEGER
    MAX-ACCESS    read-only
    STATUS        current
    DESCRIPTION
        "Number Service Data for Phoenix"

    ::= { PhoenixServicesDataTable 2 }

END


snmpd.conf (/etc/snmp/snmpd.conf)
Код

syslocation Server Room
syscontact Sysadmin (root@localhost)

rocommunity public 127.0.0.1

dlmod PhoenixServicesTable /home/anton/SnmpDist/libPhoenixServicesTable.so


Теперь главные моменты:
  • Собирал в QtCreator-е (то что он еще до кучи пару тройку символических ссылок делает ни на что не влияет; копипастим куда нужно и не думаем ни о чем)
  • Функции void init_PhoenixServicesTable(void) и void deinit_PhoenixServicesTable(void); init_ИМЯ, КОТОРОЕ УКАЗАЛИ В SNMPD.CONF! deinit_ИМЯ, КОТОРОЕ УКАЗАЛИ В SNMPD.CONF! (если иначе, то просто ни чего не будет работать, т.к. Net-SNMP вряд ли сможет корректно распознать расширение)
  • Так как QtCreator на выходе генерирует библиотеку с префиксом lib, то и его указываем в snmpd.conf (ну нужно ему полное имя и все тут), т.е.: dlmod PhoenixServicesTable /home/anton/SnmpDist/libPhoenixServicesTable.so
В остальном все как и раньше, читайте посты выше.

PS: Если у вас не работает ваш модуль (ошибка из серии: No such Object available on this agent at this OID), то проверить из-за чего можно командой:
Код

./snmptable -c public -v 2c localhost UCD-DLMOD-MIB::dlmodTable

SNMP table: UCD-DLMOD-MIB::dlmodTable

            dlmodName                                       dlmodPath dlmodError dlmodStatus
 PhoenixServicesTable /home/anton/SnmpDist/libPhoenixServicesTable.so                 loaded


Если расширение не загрузилось, то будет хоть как-то описана причина. Например:
Код

./snmptable -c public -v 2c localhost UCD-DLMOD-MIB::dlmodTable

SNMP table: UCD-DLMOD-MIB::dlmodTable

            dlmodName                                        dlmodPath                                                                                                                 dlmodError dlmodStatus
 PhoenixServicesTable /home/anton/SnmpDist/libPhoenixServicesTable1.so dlopen failed: /home/anton/SnmpDist/libPhoenixServicesTable1.so: cannot open shared object file: No such file or directory       error


Может возникнуть косяк из-за того что Net-SNMP не подгрузит MIB. Выполняем следующее:
Код

./snmptranslate -M+. -mNET-SNMP-PHOENIX-MIB -Tp -IR netSnmpPhoenixMIB                                                                                                                                                                                                                   
                                                                                                                                                                                                                                                                                                                                                                                                                                                              Expected LAST-UPDATED (LAST-UPDATE): At line 10 in /usr/share/snmp/mibs/NET-SNMP-PHOENIX-MIB.txt                                                                                                                                                   
+--netSnmpPhoenixMIB(5)                                                                                                                                                                                                                            
   |                                                                                                                                                                                                                                               
   +--PhoenixMibObjects(1)
      |
      +--PhoenixAgentModules(1)
         |
         +--PhoenixTables(1)
            |
            +--PhoenixServicesTable(1)
               |
               +--PhoenixServicesDataTable(1)
                  |  Index: PhoenixServicesData
                  |
                  +-- -R-- String    PhoenixServicesData(1)
                  +-- -R-- INTEGER   PhoenixServicesDataNumber(2)


Ниже прикладываю исходники (мало ли что)  smile 

Автор: mchertz 23.6.2014, 16:25
Спасибо автору=)

Автор: WOLFY17 26.4.2017, 13:34
kyzia887, спасибо за подробное описание.
Не могу понять некоторых вещей: 
1. Для чего мы создаём пункты таблицы в MIB, если мы её создаём на уровне кода?
2. В каком случае должен вызываться обработчик, к которому привязана таблица (т. е. после какой команды из консоли). я думал что после snmpset или snmpget того OIDа, на котором висит таблица, но это не работает. Вообще не очень понимаю по какому принципу работает таблица
Поясни, если не тяжело)

Автор: kyzia887 27.4.2017, 13:46
Цитата(WOLFY17 @ 26.4.2017,  13:34)
kyzia887, спасибо за подробное описание.
Не могу понять некоторых вещей: 
1. Для чего мы создаём пункты таблицы в MIB, если мы её создаём на уровне кода?
2. В каком случае должен вызываться обработчик, к которому привязана таблица (т. е. после какой команды из консоли). я думал что после snmpset или snmpget того OIDа, на котором висит таблица, но это не работает. Вообще не очень понимаю по какому принципу работает таблица
Поясни, если не тяжело)

1. Для чего мы создаём пункты таблицы в MIB - для себя и для сторонних разработчиков, если они захотят запросить ваши данные (ведь MIB содержит описание возвращаемых данных)
2. В каком случае должен вызываться обработчик, к которому привязана таблица - после запроса snmpwalk или snmpgetnext

В том примере, который я описал, таблица не имеет постоянного размера (может быть 1 или N строк в зависимости от конфигурационного файла самого модуля), поэтому в своем коде я запрашивал эти данные командой snmpwalk, 
т.к. она выгрузит вам все дочерние элементы этой таблицы.

Например oid таблицы = .1.2.3.4.5, тогда все строки которые вы создадите будут имет oid-ы:
1 -  .1.2.3.4.5.1
2 -  .1.2.3.4.5.2
N - .1.2.3.4.5.N

Пример запроса таблицы:
Код

snmpwalk -v 2c -c public 127.0.0.1 .1.2.3.4.5 


А вот уже единичное значение (например sysname oid = 1.3.6.1.2.1.1.5 ) нужно запрашивать через snmpget.
Пример запроса sysname:
Код

snmpget -v 2c -c public 127.0.0.1 .1.3.6.1.2.1.1.5.0


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

Автор: WOLFY17 2.5.2017, 11:37
kyzia887
Спасибо за ответ, не думал, что дождусь ответа через 3 года после активности в теме))

А почему мы в коде пишем oid таблицы 1.3.6.1.4.1.8072.2.5.1.1.1.1.2, если, судя по MIB, она будет иметь oid 1.3.6.1.4.1.8072.2.5.1.1.1.1, что означает последняя двойка в записи oid в коде?

Кстати, при запросе получаю следующие значения
Код

snmpwalk -v 2c -c public 127.0.0.1 .1.3.6.1.4.1.8072.2
iso.3.6.1.4.1.8072.2.5.1.1.1.1.2.1.2.0 = INTEGER: 5
iso.3.6.1.4.1.8072.2.5.1.1.1.1.2.1.2.1 = INTEGER: 6
iso.3.6.1.4.1.8072.2.5.1.1.1.1.2.1.2.2 = INTEGER: 7
iso.3.6.1.4.1.8072.2.5.1.1.1.1.2.1.2.3 = INTEGER: 8
iso.3.6.1.4.1.8072.2.5.1.1.1.1.2.1.2.4 = INTEGER: 9

iso.3.6.1.4.1.8072.2.5.1.1.1.1.2 - это oid таблицы из кода;
предпоследняя цифра 2 -это, я так понимаю, столбец, который мы заполняем в строке;
последние цифры 0-4 это, наверное, индексы (кстати, вы не могли бы пояснить что они означают? ведь мы указали таблицу, создали запись в ней(строку), указали поле (столбец), что же тогда обозначает этот индекс?)
А вот откуда появилась единица между oid'ом таблицы и номером столбца (iso.3.6.1.4.1.8072.2.5.1.1.1.1.2.1.2.0) я понять никак не могу)) Может это порядковый номер записи? Если так, то как тогда создать вторую запись в таблице?

А как в данном случае запрашивать конкретное значение из таблицы? Команда snmpget по oid не работает:
Код

snmpget -v 2c -c public 127.0.0.1 iso.3.6.1.4.1.8072.2.5.1.1.1.1.2.1.2.2
iso.3.6.1.4.1.8072.2.5.1.1.1.1.2.1.2.2 = No Such Instance currently exists at this OID


Заранее спасибо за ответ)

Автор: katenka141 19.5.2017, 00:13
Есть такой вопрос. Могу ли я как-то создать обработчик всех OID'ов начиная с определенного, например, .1.3.6.1.4.1.8072...., и уже в своем обработчике перебирать дочерние номера и решать какому из них какое значение устанавливать?
Ведь, как я понял, у Вас в примере показывается, как обрабатывать определенные значения OID'ов.
И еще вопрос. Могу ли я создать подобное расширение агента без внедрение соответствующего MIB-модуля? 

ege.org.ru
ege.net.ru

Автор: kyzia887 19.5.2017, 16:01
Цитата(WOLFY17 @  2.5.2017,  11:37 Найти цитируемый пост)
WOLFY17


Боюсь что конкретное значение врядли получиться запросить, либо пробовать команду snmpgetnext.

А для более точного понимания что и как происходит попробуйте воспользоваться каким-нибудь MIB-браузером (qtmib, oid view и т.д.), он вам более детально все должен показать.

Автор: kyzia887 19.5.2017, 16:18
Цитата(katenka141 @ 19.5.2017,  00:13)
Есть такой вопрос. Могу ли я как-то создать обработчик всех OID'ов начиная с определенного, например, .1.3.6.1.4.1.8072...., и уже в своем обработчике перебирать дочерние номера и решать какому из них какое значение устанавливать?
Ведь, как я понял, у Вас в примере показывается, как обрабатывать определенные значения OID'ов.
И еще вопрос. Могу ли я создать подобное расширение агента без внедрение соответствующего MIB-модуля? 

ege.org.ru
ege.net.ru

1. Могу ли я как-то создать обработчик всех OID'ов начиная с определенного - могу ошибаться, но вроде бы нет. Тут более точно можно сказать, если детально изучить интерфейсы, которые предоставляет net-snmp.
2. Могу ли я создать подобное расширение агента без внедрение соответствующего MIB-модуля? - если я не ошибаюсь, то да, можете. Вроде бы net-snmp служба (snmpd) не лезет в MIB-файл, а зачитывает OID из модуля при его регистрации.

Автор: WOLFY17 16.8.2017, 15:52
kyzia887, У меня почему-то оба примера (MyTestTables и PhoenixServicesTable) выдают данные только начиная со второго запроса snmpwalk. На первом выдают ошибку "No Such Object available on this agent at this OID". С первого запроса данные выдаются только если таблица заполнялась в функции инициализации, сразу при создании таблицы. И то такое ощущение, что выдаются те данные, которые были записаны при инициализации, а не те, которые были записаны уже в обработчике запроса. У тебя не возникало такой проблемы?

Автор: kyzia887 17.8.2017, 11:05
Цитата(WOLFY17 @ 16.8.2017,  15:52)
kyzia887, У меня почему-то оба примера (MyTestTables и PhoenixServicesTable) выдают данные только начиная со второго запроса snmpwalk. 
На первом выдают ошибку "No Such Object available on this agent at this OID". С первого запроса данные выдаются только если таблица заполнялась в функции инициализации, 
сразу при создании таблицы. И то такое ощущение, что выдаются те данные, которые были записаны при инициализации, а не те, которые были записаны уже в обработчике запроса. 
У тебя не возникало такой проблемы?


У меня была аналогичная ситуация, и если честно, то я ее не исправил, т.к. не было времени разбираться. Буду признателен, если ты найдешь решение и опишешь его тут.

Автор: WOLFY17 21.8.2017, 09:19
Цитата(kyzia887 @  17.8.2017,  11:05 Найти цитируемый пост)
У меня была аналогичная ситуация, и если честно, то я ее не исправил, т.к. не было времени разбираться. Буду признателен, если ты найдешь решение и опишешь его тут. 

Я пока понял, что таблице нужна первоначальная, если можно так выразиться, "инициализация", то есть до запроса. Если её не сделать (то есть на заполнить таблицу чем либо), то будет происходить то, что происходит. Я пока делаю так - в функции init_<> создаю таблицу, заполняю её какими-либо данными и после этого в обработчике запросов всё работает корректно.
Но появилась другая проблема: команда snmpwalk просто выполняет последовательные запросы командами GET и GETNEXT для каждого отдельного поля: есть 10  полей-будет 10 запросов разных OIDов. То есть сама по себе таблица как таковая не запрашивается, просто все запросы сводятся к одному обработчику. Отсюда возникает небольшая сложность, касающаяся того как же заполнять таблицу? Строка (row) формируется целиком и уже после формирования добавляется в таблицу. А растягивать её формирование на несколько запросов, в каждом их которых будет заполняться одно поле-не очень хорошая идея. С другой стороны, и перезаписывать всю таблицу с одними и теми же данными 10 раз в каждом из запросов тоже не есть хорошо + в этом случае нужно следить за актуальностью данных. 

Автор: kyzia887 22.12.2017, 11:25
Цитата(WOLFY17 @  21.8.2017,  09:19 Найти цитируемый пост)
Но появилась другая проблема: команда snmpwalk просто выполняет последовательные запросы командами GET и GETNEXT для каждого отдельного поля: есть 10  полей-будет 10 запросов разных OIDов. То есть сама по себе таблица как таковая не запрашивается, просто все запросы сводятся к одному обработчику.


Это нормальное поведение, ведь snmpwalk выпоняет последовательно GETNEXT запросы, пока не выберет все данные из таблицы.

Автор: WOLFY17 13.2.2019, 14:16
kyzia887, попробую ещё раз возродить тему)) Вы не пробовали не отвечать на запросы с помощью net-snmp, а наоборот - самому запрашивать данные у устройства в своём приложении? То есть, может быть есть у net-snmp какой-то API для пользовательского кода, аналог snmpget. Если вы сталкивались с такой задачей буду очень благодарен, если поделитесь информацией))

Автор: kyzia887 24.11.2020, 14:22
Цитата(WOLFY17 @ 13.2.2019,  14:16)
kyzia887, попробую ещё раз возродить тему)) Вы не пробовали не отвечать на запросы с помощью net-snmp, а наоборот - самому запрашивать данные у устройства в своём приложении? То есть, может быть есть у net-snmp какой-то API для пользовательского кода, аналог snmpget. Если вы сталкивались с такой задачей буду очень благодарен, если поделитесь информацией))

Добрый день. Прошло много времени, но я отвечу)) Библиотека net-snmp позволяет выполнять весь спектр клиентских запросов. Пример использования проще всего посмотреть в исходниках где реализованы клиенты (snmpget, snmpwalk и тд). Могу сказать сразу, что библиотека net-snmp не поддерживаем многопоточность в рамках клиентских запросов. Эти факторы нужно учитывать заранее.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)