Я использую библиотеку net-snmp (версия 5.7.1) в программе c ++ под Linux. У меня есть веб-интерфейс, где пользователь может выбрать SNMP-версию и настроить ее. SNMPv1 и SNMPv2 работают просто отлично, но у меня возникли некоторые проблемы с SNMPv3.
Вот картина внешнего интерфейса: Снимок экрана веб-интерфейса (Извините, что не загрузил его прямо здесь, но мне нужно как минимум 10 репутации, чтобы сделать это)
Когда я запускаю бэкэнд c ++ и правильно ввожу все необходимые учетные данные SNMPv3, все работает нормально и устройство становится доступным. Если я изменю, например, протокол аутентификации с MD5 на SHA, но оставлю остальные учетные данные такими же, я ожидаю, что устройство больше недоступно. В действительности это остается достижимым. После перезапуска бэкэнда устройство (как и ожидалось) больше не доступно с теми же настройками.
После обнаружения этой проблемы я провел несколько тестов. Для теста я использовал разных пользователей и разные настройки. Они работали с тремя разными устройствами разных производителей, и я получал каждый раз один и тот же результат. Таким образом, это не может быть проблема, связанная с устройством. Результаты можно увидеть здесь: Результаты теста
После тестирования я пришел к выводу, что net-snmp, похоже, кэширует выбранные протоколы auth и priv для одного имени пользователя. Это очень хорошо видно на Тесте 2. Когда я впервые использую имя пользователя с определенным протоколом, я получаю ожидаемый результат. После смены протокола
ожидается другой результат, но я получаю тот же результат, что и раньше.
В заключение немного информации о том, как производятся SNMP-звонки:
SNMPWrapper
, который обрабатывает всю SNMP-связьinit_snmp()
инициировать net-snmpget()
, set()
а также walk()
, Каждый раз, когда вызывается один из этих методов, создается новый SNMP-сеанс (Сначала я создаю новый сеанс с snmp_sess_init()
Затем я настраиваю необходимые вещи и, наконец, открываю сеанс с snmp_sess_open()
snmp_sess_close()
Вопрос: Нужно ли выполнять какую-либо другую очистку перед изменением протокола, чтобы он работал правильно?
редактировать: Я добавил некоторый код, который показывает описанное поведение
int main(int argc, char** argv) {
struct snmp_session session, session1, *ss, *ss1;
struct snmp_pdu *pdu, *pdu1;
struct snmp_pdu *response, *response1;
oid anOID[MAX_OID_LEN];
size_t anOID_len = MAX_OID_LEN;
struct variable_list *vars;
int status, status1;
init_snmp("snmpapp");
const char* user = "md5";
string authpw = "123123123";
string privpw = "";
string ipString = "192.168.15.32";
char ip[16];
memset(&ip, 0, sizeof (ip));
ipString.copy(ip, sizeof (ip) - 1, 0);
/*
* First request: AuthProto is MD5, no PrivProto is used. The snmp-get
* request is successful
*/
snmp_sess_init(&session); /* set up defaults */
session.peername = ip;
session.version = SNMP_VERSION_3;
/* set the SNMPv3 user name */
session.securityName = strdup(user);
session.securityNameLen = strlen(session.securityName);
// set the authentication method to MD5
session.securityLevel = SNMP_SEC_LEVEL_AUTHNOPRIV;
session.securityAuthProto = usmHMACMD5AuthProtocol;
session.securityAuthProtoLen = USM_AUTH_PROTO_MD5_LEN;
session.securityAuthKeyLen = USM_AUTH_KU_LEN;;
if (generate_Ku(session.securityAuthProto,
session.securityAuthProtoLen,
(u_char *) authpw.c_str(), strlen(authpw.c_str()),
session.securityAuthKey,
&session.securityAuthKeyLen) != SNMPERR_SUCCESS) {
//if code reaches here, the creation of the security key was not successful
}
cout << "SecurityAuthProto - session: " << session.securityAuthProto[9] << " / SecurityAuthKey - session: " << session.securityAuthKey << endl;
ss = snmp_open(&session); /* establish the session */
if (!ss) {
cout << "Couldn't open session1 correctly";
exit(2);
}
cout << "SecurityAuthProto - ss: " << ss->securityAuthProto[9] << " / SecurityAuthKey - ss: " << ss->securityAuthKey << endl;
//send message
pdu = snmp_pdu_create(SNMP_MSG_GET);
read_objid(".1.3.6.1.2.1.1.1.0", anOID, &anOID_len);
snmp_add_null_var(pdu, anOID, anOID_len);
status = snmp_synch_response(ss, pdu, &response);
/*
* Process the response.
*/
if (status == STAT_SUCCESS && response->errstat == SNMP_ERR_NOERROR) {
cout << "SNMP-read success" << endl;
} else {
cout << "SNMP-read fail" << endl;
}
if (response)
snmp_free_pdu(response);
if (!snmp_close(ss))
cout << "Snmp closing failed" << endl;
/*
* Second request: Only the authProto is changed from MD5 to SHA1. I expect,
* that the snmp-get fails, but it still succeeds.
*/
snmp_sess_init(&session1);
session1.peername = ip;
session1.version = SNMP_VERSION_3;
/* set the SNMPv3 user name */
session1.securityName = strdup(user);
session1.securityNameLen = strlen(session1.securityName);
// set the authentication method to SHA1
session1.securityLevel = SNMP_SEC_LEVEL_AUTHNOPRIV;
session1.securityAuthProto = usmHMACSHA1AuthProtocol;
session1.securityAuthProtoLen = USM_AUTH_PROTO_SHA_LEN;
session1.securityAuthKeyLen = USM_AUTH_KU_LEN;
if (generate_Ku(session1.securityAuthProto,
session1.securityAuthProtoLen,
(u_char *) authpw.c_str(), strlen(authpw.c_str()),
session1.securityAuthKey,
&session1.securityAuthKeyLen) != SNMPERR_SUCCESS) {
//if code reaches here, the creation of the security key was not successful
}
cout << "SecurityAuthProto - session1: " << session1.securityAuthProto[9] << " / SecurityAuthKey - session1: " << session1.securityAuthKey << endl;
ss1 = snmp_open(&session1); /* establish the session */
if (!ss1) {
cout << "Couldn't open session1 correctly";
exit(2);
}
cout << "SecurityAuthProto - ss1: " << ss1->securityAuthProto[9] << " / SecurityAuthKey - ss1: " << ss1->securityAuthKey << endl;
//send message
pdu1 = snmp_pdu_create(SNMP_MSG_GET);
read_objid(".1.3.6.1.2.1.1.1.0", anOID, &anOID_len);
snmp_add_null_var(pdu1, anOID, anOID_len);
status1 = snmp_synch_response(ss1, pdu1, &response1);
/*
* Process the response.
*/
if (status1 == STAT_SUCCESS && response1->errstat == SNMP_ERR_NOERROR) {
cout << "SNMP-read success" << endl;
} else {
cout << "SNMP-read fail" << endl;
}
if (response1)
snmp_free_pdu(response1);
snmp_close(ss1);
return 0;
}
Я нашел решение самостоятельно:
net-snmp кеширует для каждого EngineId (устройства) пользователей. Если для engineID существует существующий пользователь, и вы пытаетесь открыть новый сеанс с этим пользователем, net-snmp будет использовать кэшированный. Таким образом, решение состояло в том, чтобы очистить список с кэшированными пользователями.
С помощью этого фрагмента кода я смог решить мою проблему:
usmUser* actUser = usm_get_userList();
while (actUser != NULL) {
usmUser* dummy = actUser;
usm_remove_user(actUser);
actUser = dummy->next;
}
Я надеюсь, что могу помочь кому-то еще с этим.
Других решений пока нет …