Внутри будет немного кода на Си, немного дампов Wireshark'а и чуть-чуть консольных команд.
Дано: несколько железок, которые должны опрашиваться по интерфейсу IPMI из под GNU/Linux, и две из них, которые отказывались это делать.

image

IPMI (от англ. Intelligent Platform Management Interface) — интеллектуальный интерфейс управления платформой, предназначенный для автономного мониторинга и управления функциями, встроенными непосредственно в аппаратное и микропрограммное обеспечения серверных платформ. Ключевые характеристики IPMI — мониторинг, восстановление функций управления, журналирование и инвентаризация, которые доступны независимо от процессора, BIOS'a и операционной системы. Функции управления платформой могут быть доступны, даже если система находится в выключенном состоянии. (Wikipedia)
«Отказывались делать» — при попытке подключиться к ним как через стандартную утилиту ipmitool, так и через мою разработку используя библиотеку libOpenIPMI, соединение завершалось с ошибкой таймаута.
OpenIPMI is an effort to create a full-function IPMI system to allow full access to all IPMI information on a server and to abstract it to a level that will make it easy to use. See the SourceForge page for the source code.
(OpenIPMI)
Первое решение нашлось быстро:

ipmitool -I lanplus -H 192.168.14.5 -U ADMIN -P ADMIN mc info

Здесь, помимо стандартных реквизитов подключения по IPMI, явно указывается необходимость использования протокола RMCP+ (входящего в спецификацию IPMI 2.0).

Казалось бы, с библиотекой OpenIPMI тоже должно быть все просто.

Хоть с документацией у этой библиотеки всё весьма сложно: в качестве документации предлагается большая книга (да, именно книга в PDF-формате) под названием "A Gentle Introduction to IPMI". То есть невозможно прочитать краткий HowTo или Readme, посмотреть примеры и начинать писать код, периодически поглядывая доки для справки, но хуже другое: не смотря на подробное описание архитектуры IPMI и функций библиотеки, в этом самом руководстве пропущены некоторые элементарнейшие вещи. Например, о том, как подключаться используя RMCP+.

Бегло пробежавшись по хидерам библиотеки, находим в define'ах то что нужно и заменяем в ipmi_ip_setup_con()

IPMI_AUTHTYPE_MD5 на IPMI_AUTHTYPE_RMCP_PLUS

И тут нас поджидает следущая проблема: ошибка таймаута действительно исчезла, однако функция подключения стала выдавать ошибку Incorrect argument.

Каких-либо подробностей, что эта ошибка может означать, нет ни в книге-документации, ни в отладочных сообщениях, нигде. Понятно, что кто-то (либо сама библиотечная функция, либо удаленное устройство) ругается на какой-то аргумент, но вот на какой именно, и на какой вообще стадии возникает ошибка выяснить просто так невозможно. Беглое проглядывание исходников говорит о том, что Incorrect argument (константа EINVAL) может возвращаться разными функцями в процессе соединения по весьма разным причинам (ветвей и условий довольно много).

На ум напрашиваются два пути:

  1. сделать debug-сборку библиотеки и пошагово изучать отладчиком, что, где и когда происходит
  2. сначала посмотреть на всё происходящее со стороны, наблюдая за обменом пакетами между клиентом (моим приложением) и сервером (железкой) и сравнить, в чем разница между моей реализацией и ipmitool'ом, а уже потом лезть в код.

Интуиция и желание приключений подтолкнули ко второму варианту и не ошиблись.

Запускаем wireshark, настраиваем фильтр и начинаем изучать.

image

Видно, что клиент и сервер обмениваются запросами-ответами, в случае работы с ipmitool обмен успешно продолжается дальше, а при использовании libOpenIPMI всё затыкается.

Вопрос: событие incorrect argument возникает где-то в недрах библиотеки, или же что-то не нравится самому устройству?

Сравниваем ответы от железки:

image
(Успешный ответ при использовании ipmitool)

image
(Не очень-то успешный ответ при использовании нашего приложения)

Как можно заметить, ответ от устройства действительно различается — в случае ошибки длина блока данных всего 7 байт.

Я попробовал поискать в Сети нормальное описание RMCP+ протокола, чтобы понять, что и как кодируется в этих данных, но это было безрезультатно.

Возник следущий вопрос: чем отличаются запросы, отправленные серверу, что в одном случае он отвечает нормально, а во втором случае что-то идет не так?

image

image

Присмотревшись к посылаемым пакетам, разница была найдена, осталось только выяснить, что означают эти различающиеся байты.

Покопавшись в исходниках функции установления соединения, в send_rmcpp_open_session
был найден алгоритм сборки пакета:

if ((int) lan->cparm.auth == IPMI_LANP_AUTHENTICATION_ALGORITHM_BMCPICK)
    data[11] = 0; /* Let the BMC pick */
else {
    data[11] = 8;
    data[12] = lan->cparm.auth;
}

data[16] = 1; /* integrity algorithm */
if ((int) lan->cparm.integ == IPMI_LANP_INTEGRITY_ALGORITHM_BMCPICK)
    data[19] = 0; /* Let the BMC pick */
else {
    data[19] = 8;
    data[20] = lan->cparm.integ;
}

data[24] = 2; /* confidentiality algorithm */
if ((int) lan->cparm.conf == IPMI_LANP_CONFIDENTIALITY_ALGORITHM_BMCPICK)
    data[27] = 0; /* Let the BMC pick */
else {
    data[27] = 8;
    data[28] = lan->cparm.conf;
}

Это уже становилось интересно. Байты 0x08 в посылке явно бросались в глаза и говорили что «это вот оно самое».

Там же в исходниках нашлись define'ы различных вариантов аутентификации и подобного:

#define     IPMI_LANP_AUTHENTICATION_ALGORITHM_BMCPICK   (~0)
#define     IPMI_LANP_AUTHENTICATION_ALGORITHM_RAKP_NONE   0
#define     IPMI_LANP_AUTHENTICATION_ALGORITHM_RAKP_HMAC_SHA1   1
#define     IPMI_LANP_AUTHENTICATION_ALGORITHM_RAKP_HMAC_MD5   2

Из этого следовал явный вывод, что ipmitool инициировало подключение с аутентификацией SHA-1, а наше приложение с libOpenIPMI почему-то пыталось подключиться вообще без защиты, получая в ответ игнор (видимо, устройству ну очень не нравились незащищенные подключения).

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

Дальнейшее изучение исходников показало, что варианты аутентификации берутся из глобального массива auths[], куда они добавляются процедурой ipmi_rmcpp_register_authentication(), а вот сама процедура… где она вызывается-то? Ищем и находим:

#ifdef HAVE_OPENSSL
ipmi_rmcpp_register_authentication
    (IPMI_LANP_AUTHENTICATION_ALGORITHM_RAKP_HMAC_MD5, NULL);
ipmi_rmcpp_register_authentication
    (IPMI_LANP_AUTHENTICATION_ALGORITHM_RAKP_HMAC_SHA1, NULL);
#endif

вон он и ответ.

Стандартный пакет libopenipmi0 в Ubuntu и Debian собран без поддержки OpenSSL, необходимой для этих функций. И хоть кто-нибудь бы слово написал об этом нюансе в документации!

Убедиться в этом можно, выполнив

apt-get source libopenipmi0

и заглянув в файл debian/rules встретив там вполне четкую строчку --without-openssl

Решение — пересобрать пакет как надо.

sudo apt-get install devscripts build-essential fakeroot
sudo apt-get build-dep libopenipmi0
apt-get source libopenipmi0
# заменяем в debian/rules:
# --without-openssl  =>  --with-openssl
sudo dpkg-buildpackage
debuild -us -uc

Устанавливаем, и проверяем, что все заработало как надо.

Чуть позже нашелся ppa, где добрый человек пересобирает эту либу для Ubuntu с поддержкой OpenSSL.

Ура. Хэппи-энд.

Комментарии (5)


  1. AEP
    05.03.2018 21:46

    Технически, пересборка является решением, а юридически — есть мнение, что это недопустимо.

    Ключ --without-openssl добавлен по лицензионным соображениям, так как существенные части библиотеки лицензированы под GPLv2, которая требует, чтобы при распространении весь код был под GPLv2. Итог: по мнению дебианщиков, результат сборки с OpenSSL запрещено распространять в бинарном виде. RedHat пыталась обойти эту юридическую проблему (GPL + OpenSSL) путем объявления OpenSSL частью операционной системы.


    1. Whuthering Автор
      06.03.2018 10:17

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

      Интересно, но при этом ipmitool, поставленная из тех же реп в бинарном виде работает корректно.


      1. AEP
        06.03.2018 10:58

        Ipmitool распространяется не под GPL, а под BSD 3-clause license, поэтому проблема совместимости с лицензией OpenSSL не стоит.


  1. TaHKucT
    06.03.2018 00:31

    Об этом много написано, только в документации сторонних продуктов, например в zabbix: https://www.zabbix.com/documentation/3.4/ru/manual/installation/known_issues


    Форум заббикса по ключевым словам выдает много тем с одним и тем же решением, так что не вы первый и не вы последний :)


    1. Whuthering Автор
      06.03.2018 10:19

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