miniupnpc: Fix usage of IP_MULTICAST_IF with struct ip_mreqn

When struct ip_mreqn is passed to IP_MULTICAST_IF setsockopt option it is
always required to set also ipv4 source address. Otherwise Linux kernel
will choose default system multicast ipv4 address which does not have to
belong to chosen interface specified in struct ip_mreqn.

Therefore on system with more multicast interfaces and more ipv4 addresses,
it may happen that interface chosen by upnpc -m option would use ipv4
address which does not belong to this interface.

This change is fixing above issue and ensure that if interface is chosen by
upnpc -m option then source address which belongs to this interface would
be used.

Without this change upnpc -m eth1 can send multicast traffic over interface
eth1 but with source ipv4 address of interface eth0, which obviously would
be rejected by upnp gateway.
This commit is contained in:
Pali Rohár 2020-12-30 13:51:24 +01:00
parent 36a6913970
commit bfbe42d392
1 changed files with 18 additions and 12 deletions

View File

@ -66,7 +66,7 @@ struct sockaddr_un {
#define HAS_IP_MREQN #define HAS_IP_MREQN
#endif #endif
#if !defined(HAS_IP_MREQN) && !defined(_WIN32) #ifndef _WIN32
#include <sys/ioctl.h> #include <sys/ioctl.h>
#if defined(__sun) || defined(__HAIKU__) #if defined(__sun) || defined(__HAIKU__)
#include <sys/sockio.h> #include <sys/sockio.h>
@ -735,10 +735,24 @@ ssdpDiscoverDevices(const char * const deviceTypes[],
PRINT_SOCKET_ERROR("setsockopt IP_MULTICAST_IF"); PRINT_SOCKET_ERROR("setsockopt IP_MULTICAST_IF");
} }
} else { } else {
#ifdef HAS_IP_MREQN
/* was not an ip address, try with an interface name */ /* was not an ip address, try with an interface name */
#ifndef _WIN32
#ifdef HAS_IP_MREQN
struct ip_mreqn reqn; /* only defined with -D_BSD_SOURCE or -D_GNU_SOURCE */ struct ip_mreqn reqn; /* only defined with -D_BSD_SOURCE or -D_GNU_SOURCE */
#endif
struct ifreq ifr;
int ifrlen = sizeof(ifr);
strncpy(ifr.ifr_name, multicastif, IFNAMSIZ);
ifr.ifr_name[IFNAMSIZ-1] = '\0';
if(ioctl(sudp, SIOCGIFADDR, &ifr, &ifrlen) < 0)
{
PRINT_SOCKET_ERROR("ioctl(...SIOCGIFADDR...)");
goto error;
}
mc_if.s_addr = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr;
#ifdef HAS_IP_MREQN
memset(&reqn, 0, sizeof(struct ip_mreqn)); memset(&reqn, 0, sizeof(struct ip_mreqn));
reqn.imr_address.s_addr = mc_if.s_addr;
reqn.imr_ifindex = if_nametoindex(multicastif); reqn.imr_ifindex = if_nametoindex(multicastif);
if(reqn.imr_ifindex == 0) if(reqn.imr_ifindex == 0)
{ {
@ -751,20 +765,12 @@ ssdpDiscoverDevices(const char * const deviceTypes[],
{ {
PRINT_SOCKET_ERROR("setsockopt IP_MULTICAST_IF"); PRINT_SOCKET_ERROR("setsockopt IP_MULTICAST_IF");
} }
#elif !defined(_WIN32) #else
struct ifreq ifr;
int ifrlen = sizeof(ifr);
strncpy(ifr.ifr_name, multicastif, IFNAMSIZ);
ifr.ifr_name[IFNAMSIZ-1] = '\0';
if(ioctl(sudp, SIOCGIFADDR, &ifr, &ifrlen) < 0)
{
PRINT_SOCKET_ERROR("ioctl(...SIOCGIFADDR...)");
}
mc_if.s_addr = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr;
if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0) if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0)
{ {
PRINT_SOCKET_ERROR("setsockopt IP_MULTICAST_IF"); PRINT_SOCKET_ERROR("setsockopt IP_MULTICAST_IF");
} }
#endif
#else /* _WIN32 */ #else /* _WIN32 */
#ifdef DEBUG #ifdef DEBUG
printf("Setting of multicast interface not supported with interface name.\n"); printf("Setting of multicast interface not supported with interface name.\n");