четверг, 12 мая 2016 г.

Странное поведение клиента SNMP при работе с неадекватным сервером.

История следующая.


Пользователь клентской программы указал для некоторого устройства, опрашиваемого (чтение данных по некоторым OID)  по SNMP - IP адрес, несовпадающий с IP адресом опрашиваемого устройства. В результате - клиентская программа с грохотом упала. В ходе "разбора полётов" было выяснено следующее:

  •      В локальной сети находилось SNMP устройство другого типа ( и другого производителя) , опрашивать которое клиент не планировал и информацией об OID которого не располагал, но IP адрес которого был ошибочно введён.
  •     "Community" для чтения этих устройств совпали (да оно и понятно - 'public' -   по умолчанию).
  •     Обработка ошибок от функции snmp_sess_synch_response в клиентской программе была организована из рук вон плохо - ну и понятно, возникло нарушение доступа к памяти и т.д. и т.п.

Казалось бы, всё ясно - не используйте одинаковые 'community' для разных устройств, обрабатывайте правильно ответы на неправильные запросы  и проблем не будет... Но.

В ходе более глубокого копания выяснилась некоторая неоднозначность возврата из функции snmp_sess_synch_response.

Берём устройство (или эмулятор) IP, OIDы и community которого мы знаем и обращаемся к нему с запросом данных по несуществующим у него OID (IP и commutity совпадают). Примерно так (часть кода пропущена):

struct snmp_session *sess;

struct snmp_pdu **response;
void *ptr=NULL;
snmp_sess_init(sess);
  
sess->version=SNMP_VERSION_2c;
sess->peername=ip;
sess->community=(unsigned char*)community;
sess->community_len = strlen(community);

ptr = snmp_sess_open(sess);

pdu=snmp_pdu_create(SNMP_MSG_GET);

snmp_parse_oid(buff, anOID, &anOID_len);
snmp_add_null_var(pdu, anOID, anOID_len);

int Ret = snmp_sess_synch_response(ptr, pdu, response);



Варианты возврата из последней функции (для разных, но несуществующих OID) следующие:

  •     Ret==STAT_SUCCESS и (*response)->errstat==SNMP_ERR_NOSUCHNAME
  •     Ret==STAT_TIMEOUT и (*response)==NULL;  
  •     Ret==STAT_SUCCESS и (*response)->errstat==SNMP_ERR_NOERROR;

 ПОЧЕМУ? Ведь проблема одна и та же - агент получил запрос на отсутствующую у него информацию. А потому что ответственность за ответ на запрос c неизвестным OID лежит на агенте SNMP, то есть на том, кто пишет софт для устройства или эмулятора. Ответственность эта - понимается по-разному.

Но не всё потеряно - даже в последнем варианте можно пойти дальше - и обработать возникшую ошибку. Попробуем проанализировать содержимое полученного ответа

 netsnmp_variable_list *vars = pdu_receive->variables;

Список у нас содержит только одну переменную, поэтому посмотрим содержимое её поля type. В нашем случае имеем тип SNMP_NOSUCHINSTANCE.

И конечно, не следует забывать о функции snmp_sess_error. Она не всегда возвращает пустые данные.