Ошибка «Нет такого устройства» в вызове setockopt при присоединении к многоадресной группе
-
02-10-2019 - |
Вопрос
У меня есть код, в котором отправляются многоадресные датаграммы.Критический фрагмент кода:
uint32_t port;
int sockfd, err_ip;
const uint32_t sizebuff = 65535 - (20 + 8);
unsigned char *buff = (unsigned char *) malloc(sizebuff);
struct sockaddr_in servaddr, cliaddr;
struct in_addr serv_in_addr;
struct ip_mreq req;
port = str2uint16(cmdsrv->ipport);
bzero(buff, (size_t)sizebuff);
bzero(&servaddr, sizeof(servaddr));
bzero(&serv_in_addr, sizeof(serv_in_addr));
err_ip = inet_aton(cmdsrv->ipaddr, &serv_in_addr);
if(( err_ip != 0 ) && ( port != 0 )) {
servaddr.sin_family = AF_INET;
servaddr.sin_addr = serv_in_addr;
servaddr.sin_port = htons(port);
memcpy(&req.imr_multiaddr,&serv_in_addr,sizeof(req.imr_multiaddr));
req.imr_interface.s_addr = INADDR_ANY;
sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if( sockfd == -1 ) {
int outerror = errno;
char *retstr = "Couldn't open socket\n";
pthread_exit(retstr);
}
else {
struct in_addr ifaddr;
ifaddr.s_addr = INADDR_ANY;
int optres3 =
setsockopt( sockfd, IPPROTO_IP, IP_MULTICAST_IF, &ifaddr,
sizeof( ifaddr ));
if( optres3 == -1 ) {
int perrno = errno;
char *retstr = "Can't set IP_MULTICAST_IF for socket\n";
printf( "Error setsockopt: ERRNO = %s\n", strerror( perrno ));
printf( "%s",retstr );
pthread_exit(retstr);
}
unsigned char ttl = 32;
int optres2 =
setsockopt( sockfd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl,
sizeof( ttl ));
if( optres2 == -1 ) {
int perrno = errno;
char *retstr = "Can't set IP_MULTICAST_TTL for socket\n";
printf("Error setsockopt: ERRNO = %s\n",strerror(perrno));
printf("%s",retstr);
pthread_exit(retstr);
}
int optres =
setsockopt( sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &req,
sizeof( req ));
if( optres == -1 ) {
int perrno = errno;
char *retstr = "Can't join to multicast-group\n";
printf("Error setsockopt: ERRNO = %s\n",strerror(perrno));
printf("%s",retstr);
pthread_exit(retstr);
}
// Bind port with socket
uint16_t cliport;
cliaddr.sin_family = AF_INET;
cliaddr.sin_addr.s_addr = INADDR_ANY;
if( strcmp( cmdsrv->ipport, "16011" ) == 0 ) {
cliport = str2uint16("16003");
cliaddr.sin_port = htons(cliport);
}
else if( strcmp( cmdsrv->ipport, "16012" ) == 0 ) {
cliport = str2uint16("16004");
cliaddr.sin_port = htons(cliport);
}
else {
printf("Device hasn't such port");
pthread_exit(NULL);
}
int bindres =
bind( sockfd, (struct sockaddr*)&cliaddr, sizeof( cliaddr ));
if( bindres == -1 ) {
int perrno = errno;
perror("Error in bind\n");
}
// ADD 1 BYTE
data rawdata;
rawdata.desc = 23;
printf( "SIZEOF = %d\n", sizeof( *( cmdsrv->cmd )));
memcpy( &rawdata.cmd, cmdsrv->cmd, sizeof( *( cmdsrv->cmd )));
printf( "RAWDATA: desc = %d, cmd = %d\n", rawdata.desc, rawdata.cmd );
int outerror = 0;
printf( "Send command to IP:\n addr = %s, port = %d\n",
inet_ntoa( servaddr.sin_addr ), ntohs( servaddr.sin_port ));
int size = sendto( sockfd, &rawdata, sizeof( rawdata ), 0,
(struct sockaddr*)&servaddr, sizeof( servaddr ));
if( size == -1 ) {
perror("Can't send command to socket");
}
...
Иногда программа выполняется успешно (в данный момент у меня IP - 192.168.80.122
).Я могу захватить свою многоадресную датаграмму с помощью Wireshark.Все в порядке.
Но если я изменю свой IP на 192.168.1.2
, я получаю сообщение об ошибке при вызове
int optres =
setsockopt( sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &req,
sizeof( req ));
И я даже не могу перехватить свой многоадресный пакет.Ничего не отправляется.Где ошибка?
Решение
Если это работает для одного IP, но не для другого, может быть это может помочь.
Что означает "ip_add_membership: нет такого устройства"?
Это означает, что инструмент пытается использовать многоадресную расстояние, но сетевой интерфейс не поддерживает его, есть две вероятные причины:
Ваша машина не включена поддержка многоадресной рассылки. Например, на Linux и FreeBSD можно собрать ядро, которое не поддерживает многоадресную расстояние.
У вас нет маршрута для многоадресного трафика. Некоторые системы не добавляют это по умолчанию, и вам нужно запустить.
route add -net 224.0.0.0 netmask 224.0.0.0 eth0
(или похожие). Если вы хотите использовать крыс только в режиме одноадресной системы, можно добавить маршрут многоадресной рассылки на интерфейс Loopback.
Другие советы
IP_ADD_MEMBERSHIP
и bind()
необходимы только для приема многоадресной рассылки, используйте IP_MULTICAST_IF
вместо этого для эффективного членства в группе многоадресной рассылки только для отправки.
IP_MULTICAST_IF
настраивает ядро на отправку многоадресных пакетов для данной группы на заданном интерфейсе, это фактически «только отправка», поскольку после настройки вы не сможете получать трафик в этой группе.Это зависит от платформы:Платформы Posix обычно функционируют таким образом как оптимизация, в то время как Win32 выполняет маршрутизацию на программном уровне для распространения локально сгенерированных пакетов.