Ошибка привязки сокета, когда для адаптера Ethernet установлен собственный IP-адрес

Эта проблема

В нашей системе с RTEMS 4.9.2 мы столкнулись с очень странной проблемой связи через сокеты. Мы настраиваем сокет и используем следующую команду для привязки:

// Bind the socket to set the local port
sockaddr_in localSocketAddress = {0};
localSocketAddress.sin_family = AF_INET;
localSocketAddress.sin_port = (u_short)localPort;
localSocketAddress.sin_addr.s_addr = localAddress;

if (bind( mSocket, (sockaddr *)&localSocketAddress, sizeof(sockaddr_in)) == SOCKET_ERROR)
{
    int errorOut = errno;
    ...

И это работает для связи UDP, за исключением странного конкретного сценария, который объясняется ниже. Проблема, с которой мы сталкиваемся, заключается в сбое этого вызова bind, даже если установка правильная. Мы получаем ошибку 125, которая для RTEMS равна EADDRNOTAVAIL:

Был запрошен несуществующий интерфейс или запрошенный адрес не был локальным.

Видимая причина

При загрузке устройства мы можем настроить нашу сеть одним из двух способов:

  1. Сетевой IP-адрес и подсеть настраиваются автоматически на основе того, что находится в загрузчике по умолчанию (UBOOT), и настраивается через ОС RTEMS.

  2. Функция RTEMS rtems_bsdnet_ifconfig вызывается для изменения IP-адреса единственного интерфейса Ethernet после загрузки.

Для уточнения вариант 2 называется так:

rtems_bsdnet_ifconfig(eth_interface, SIOCSIFADDR, &ipaddr);

Если сеть настроена в соответствии с параметром 1, все работает, как и ожидалось, но если используется вариант 2 (даже в случае, когда настройка соответствует параметрам сети, определенным параметром 1), то привязка сокета завершается ошибкой.

Есть ли известная причина или ошибка для RTEMS, которая указывает на то, что эта привязка не будет работать, если вы перенастроите свой IP-адрес?

Дополнительная информация

  • Мы настраиваем новый IP-адрес (вариант 2), используя метод, который по существу использует ioctl("eht1", SIOCSIFADDR, ...).

  • Если мы привязываем наш сокет без указания локального АДРЕСА (т.е. используем INADDR_ANY), то он работает в любом случае.

  • rtems_bsdnet_ifconfig — это простой интерфейс для функции ioctl. Это от rtems_glue.c и имеет функциональную сигнатуру int rtems_bsdnet_ifconfig(const char *ifname, uint32_t cmd, void *param)

  • Все нормальные сетевые функции работают, кроме этой привязки.

  • Посмотрев на это, я подумал, что, возможно, мне нужно сделать больше для сброса моего IP адрес. Но это не работает, использование первого ответа или даже какие-либо действия с SIOCSIFFLAGS приводят к прекращению работы всей сети.


person Fantastic Mr Fox    schedule 08.06.2016    source источник
comment
возможно ли, что ваше bind происходит до завершения настройки интерфейса? будет продолжаться сбой, если вы продолжите повторять попытку bind?   -  person mark    schedule 08.06.2016
comment
@mark Связывание происходит намного позже установки, оба параметра установки выполняются при загрузке до того, как можно будет попытаться установить сокет.   -  person Fantastic Mr Fox    schedule 08.06.2016
comment
Возможно, что-то изменилось с моей версии rtems (4.6.1), но rtems_bsdnet_ifconfig это структура, а не функция...   -  person LPs    schedule 14.06.2016
comment
@LPs Это и то, и другое: структура существует, как и функция, у функции, похоже, нет никакой документации, иначе я бы связал ее, ее можно найти в rtems_glue.c.   -  person Fantastic Mr Fox    schedule 14.06.2016
comment
Отказ от ответственности: здесь нет эксперта, просто немного изучил код. Вы явно устанавливаете sa_family в AF_INET? Похоже, что это не так t требуется, но его следует установить, поскольку он используется в сравнение. Еще одна подсказка, так как структуры sockaddr сравниваются по байтам: вы memset свою структуру sockaddr устанавливали в 0 перед настройкой ее значений и переходом к bind?   -  person grasbueschel    schedule 16.06.2016
comment
Пробовали ли вы выполнить вызов bind() с помощью отладчика и посмотреть, где именно происходит return EADDRNOTAVAIL?   -  person exilit    schedule 17.06.2016
comment
@exilit К сожалению, у нас нет активного отладчика, это происходит только на реальном встроенном оборудовании, все, что я могу использовать, это операторы печати.   -  person Fantastic Mr Fox    schedule 17.06.2016
comment
Вы сами собираете библиотеку?   -  person exilit    schedule 17.06.2016
comment
@exilit Мы сами компилируем всю ОС. Но нет интерфейса для встроенного оборудования, который мог бы использовать отладчик, даже если бы RTEMS реализовал его.   -  person Fantastic Mr Fox    schedule 17.06.2016
comment
@grasbueschel Мы делаем и то, и другое, я отредактировал вопрос, чтобы отразить это. sockaddr_in localSocketAddress = {0};   -  person Fantastic Mr Fox    schedule 17.06.2016
comment
Я не поэтому спрашиваю. Есть ли шанс, что вы включите некоторый отладочный вывод в соответствующие места и отлаживаете его таким образом? Нам придется приложить некоторые мысли и усилия, чтобы выяснить эти важные места, но это, возможно, стоит попробовать.   -  person exilit    schedule 17.06.2016
comment
@exilit Да, я могу поместить отладочные отпечатки в операционную систему, она использует библиотеки BSDNet для всего, что связано с сетью. Тогда я смогу перекомпилировать операционную систему и увидеть отпечатки, но обычно мне приходится быть очень осторожным, слишком много печати может испортить время. Я ненавижу это делать...   -  person Fantastic Mr Fox    schedule 17.06.2016
comment
Я тоже это ненавижу. Но пока никто не отвечает, я думаю, у вас не так много выбора.   -  person exilit    schedule 17.06.2016
comment
@Бен, понятно. Еще одна вещь, которую нужно проверить, хотя я не думаю, что это основная проблема здесь: есть sin_len член sockaddr_in, который вы должны инициализировать как sizeof struct sockaddr_in.   -  person grasbueschel    schedule 17.06.2016
comment
@grasbueschel Итак, я попробовал, к сожалению, это не имеет никакого значения.   -  person Fantastic Mr Fox    schedule 23.06.2016


Ответы (1)


Вы не упомянули архитектуру, на которой вы работаете, но вы устанавливаете порт и адрес в локальном порядке байтов, который может не совпадать с порядком байтов в сети. Самое первое, что я бы попробовал, это:

localSocketAddress.sin_port = htons (localPort); localSocketAddress.sin_addr.s_addr = htonl (localAddress);

Это также сделает ваш код более переносимым, если это НЕ ваша проблема (т. е. вы работаете на хосте с обратным порядком байтов), и однажды вы попытаетесь скомпилировать его в другой системе с прямым порядком байтов.

person Kean    schedule 11.07.2016
comment
Почему это должен быть комментарий? Просто спрашиваю для ясности в будущем. Также вы даже ПОПРОБОВАЛИ мое предложение? Эти поля должны быть в сетевом порядке байтов, и использование htons() и htonl() является правильным. - person Kean; 11.07.2016
comment
@Ben Решение - это ответ, а не комментарий. Ты ошибаешься, а Кин нет. - person user207421; 11.07.2016
comment
@Kean, приношу свои извинения за комментарий, я попробую ваше решение. Однако ожидаем ли мы, что это сработает? Если вы прочитали вопрос, привязка сокета работает нормально в любом случае, если я не запускаю rtems_bsdnet_ifconfig( для перенастройки моего сетевого адреса. Почему это могло бы работать, если бы сетевой адрес был в неправильном порядке? - person Fantastic Mr Fox; 11.07.2016
comment
@Ben Я думаю, я предположил, что фактический адрес, к которому вы подключались, изменился после этого вызова rtems. Я не должен был так предполагать, но, поскольку я это сделал, я пришел к выводу, что в первом случае это могла быть какая-то константа, которую вы присвоили localAddress, тогда как после внесения изменения вы присвоили ей что-то другое. Предположение — мать всех неудач. Извини за это. - person Kean; 12.07.2016
comment
Просто чтобы мы поняли это, к сожалению, это решение не сработало. - person Fantastic Mr Fox; 30.08.2016