Recibir mensajes de multidifusión en una PC de Windows multihomed
-
24-10-2019 - |
Pregunta
Estoy desarrollando una herramienta de diagnóstico en una PC con varias interfaces de red basadas en multidifusión/UDP. El usuario puede seleccionar una NIC, la aplicación crea sockets, los vincula a esta NIC y los agrega al grupo de multidifusión específico.
El envío de mensajes de multidifusión funciona bien. Sin embargo, la recepción de mensajes solo tiene éxito si encuentro los enchufes a una NIC específica de mi PC. Casi se parece a una NIC 'predeterminada' para recibir mensajes de multidifusión en Windows, que siempre es el primer NIC devuelto por el GetAdapterinfo función.
Monitoricé la red con Wireshark y descubrí que el mensaje "Grupo de unión IGMP" no se envía desde la NIC que ato el socket, pero con esta NIC 'predeterminada'.
Si deshabilo esta NIC (o elimino el cable de red), la siguiente NIC de la lista devuelta por GetAdapterInfo se usa para recibir mensajes de multidifusión.
Tuve éxito al cambiar esta NIC 'predeterminada' agregando una entrada adicional a la tabla de enrutamiento de mi PC, pero no creo que esta sea una buena solución del problema.
El problema también ocurre con el código adjunto a continuación. Los mensajes de unión del grupo no se envían a través de 192.168.52 pero a través de una NIC diferente.
// socket_tst.cpp : Defines the entry point for the console application.
//
\#include tchar.h
\#include winsock2.h
\#include ws2ipdef.h
\#include IpHlpApi.h
\#include IpTypes.h
\#include stdio.h
int _tmain(int argc, _TCHAR* argv[])
{
WSADATA m_wsaData;
SOCKET m_socket;
sockaddr_in m_sockAdr;
UINT16 m_port = 319;
u_long m_interfaceAdr = inet_addr("192.168.1.52");
u_long m_multicastAdr = inet_addr("224.0.0.107");
int returnValue = WSAStartup(MAKEWORD(2,2), &m_wsaData);
if (returnValue != S_OK)
{
return returnValue;
}
// Create sockets
if (INVALID_SOCKET == (m_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) )
{
return WSAGetLastError();
}
int doreuseaddress = TRUE;
if (setsockopt(m_socket,SOL_SOCKET,SO_REUSEADDR,(char*) &doreuseaddress,sizeof(doreuseaddress)) == SOCKET_ERROR)
{
return WSAGetLastError();
}
// Configure socket addresses
memset(&m_sockAdr,0,sizeof(m_sockAdr));
m_sockAdr.sin_family = AF_INET;
m_sockAdr.sin_port = htons(m_port);
m_sockAdr.sin_addr.s_addr = m_interfaceAdr;
//bind sockets
if ( bind( m_socket, (SOCKADDR*) &m_sockAdr, sizeof(m_sockAdr) ) == SOCKET_ERROR )
{
return WSAGetLastError();
}
// join multicast
struct ip_mreq_source imr;
memset(&imr,0,sizeof(imr));
imr.imr_multiaddr.s_addr = m_multicastAdr; // address of multicastgroup
imr.imr_sourceaddr.s_addr = 0; // sourceaddress (not used)
imr.imr_interface.s_addr = m_interfaceAdr; // interface address
/* first join multicast group, then registerer selected interface as
* multicast sending interface */
if( setsockopt( m_socket
,IPPROTO_IP
,IP_ADD_MEMBERSHIP
,(char*) &imr
, sizeof(imr))
== SOCKET_ERROR)
{
return SOCKET_ERROR;
}
else
{
if( setsockopt(m_socket
,IPPROTO_IP
,IP_MULTICAST_IF
,(CHAR*)&imr.imr_interface.s_addr
,sizeof(&imr.imr_interface.s_addr))
== SOCKET_ERROR )
{
return SOCKET_ERROR;
}
}
printf("receiving msgs...\n");
while(1)
{
// get inputbuffer from socket
int sock_return = SOCKET_ERROR;
sockaddr_in socketAddress;
char buffer[1500];
int addressLength = sizeof(socketAddress);
sock_return = recvfrom(m_socket, (char*) &buffer, 1500, 0, (SOCKADDR*)&socketAddress, &addressLength );
if( sock_return == SOCKET_ERROR)
{
int wsa_error = WSAGetLastError();
return wsa_error;
}
else
{
printf("got message!\n");
}
}
return 0;
}
¡Gracias cuatro de tu ayuda!
Solución
El problema era un error tipográfico simple. En lugar de usar la estructura struct ip_mreq_source, la estructura struct ip_mreq debe usarse si se usa la opción IP_MULTICAST_IF. (La otra estructura es necesaria para la opción ip_add_source_membership)
El uso de la estructura incorrecta probablemente tuvo el resultado de que la función setSockeopt encontró un cero donde se esperaba la dirección IP de NIC. Zero también es el valor de la constante inAddr_ alguna, que elige la NIC predeterminada del sistema. :-)
Otros consejos
Es posible que desee verificar/cambiar su tabla de enrutamiento. Habrá una ruta para el tráfico de multicast (224.0.0.0, subred 240.0.0.0) con su métrica apropiada:
C:\Users\Cetra>netstat -rn
*****
IPv4 Route Table
===========================================================================
Active Routes:
Network Destination Netmask Gateway Interface Metric
0.0.0.0 0.0.0.0 192.168.80.254 192.168.80.99 20
127.0.0.0 255.0.0.0 On-link 127.0.0.1 306
127.0.0.1 255.255.255.255 On-link 127.0.0.1 306
127.255.255.255 255.255.255.255 On-link 127.0.0.1 306
192.168.80.0 255.255.255.0 On-link 192.168.80.99 276
192.168.80.99 255.255.255.255 On-link 192.168.80.99 276
192.168.80.255 255.255.255.255 On-link 192.168.80.99 276
224.0.0.0 240.0.0.0 On-link 127.0.0.1 306
224.0.0.0 240.0.0.0 On-link 192.168.80.99 276
255.255.255.255 255.255.255.255 On-link 127.0.0.1 306
255.255.255.255 255.255.255.255 On-link 192.168.80.99 276
******