getifaddr.c: prefer non-reserved over reserved addresses in `getifaddr()`

When iterating interface addresses obtained via `getifaddrs()`, don't
stop at the first found IPv4 address but continue checking all IPv4
addresses and prefer to use a non-reserved one in case an interface
has both reserved (private) and non-reserved (public) addresses
assigned.

After this fix, miniupnpd on OpenWrt is able to properly detect the
external IP address of an external interface with both a private
RFC1918 and a public IP assigned regardless of whether `getifaddrs()`
happens to return the private or the public IPv4 address first.

Signed-off-by: Jo-Philipp Wich <jo@mein.io>
This commit is contained in:
Jo-Philipp Wich 2022-02-16 17:58:42 +01:00
parent a28dec3c2d
commit 95002535b3
1 changed files with 16 additions and 8 deletions

View File

@ -101,6 +101,7 @@ getifaddr(const char * ifname, char * buf, int len,
/* Works for all address families (both ip v4 and ip v6) */ /* Works for all address families (both ip v4 and ip v6) */
struct ifaddrs * ifap; struct ifaddrs * ifap;
struct ifaddrs * ife; struct ifaddrs * ife;
struct ifaddrs * candidate = NULL;
if(!ifname || ifname[0]=='\0') if(!ifname || ifname[0]=='\0')
return -1; return -1;
@ -119,14 +120,10 @@ getifaddr(const char * ifname, char * buf, int len,
switch(ife->ifa_addr->sa_family) switch(ife->ifa_addr->sa_family)
{ {
case AF_INET: case AF_INET:
if(buf) if(!candidate ||
{ (addr_is_reserved(&((struct sockaddr_in *)candidate->ifa_addr)->sin_addr) &&
inet_ntop(ife->ifa_addr->sa_family, !addr_is_reserved(&((struct sockaddr_in *)ife->ifa_addr)->sin_addr)))
&((struct sockaddr_in *)ife->ifa_addr)->sin_addr, candidate = ife;
buf, len);
}
if(addr) *addr = ((struct sockaddr_in *)ife->ifa_addr)->sin_addr;
if(mask) *mask = ((struct sockaddr_in *)ife->ifa_netmask)->sin_addr;
break; break;
/* /*
case AF_INET6: case AF_INET6:
@ -136,6 +133,17 @@ getifaddr(const char * ifname, char * buf, int len,
*/ */
} }
} }
if(candidate)
{
if(buf)
{
inet_ntop(candidate->ifa_addr->sa_family,
&((struct sockaddr_in *)candidate->ifa_addr)->sin_addr,
buf, len);
}
if(addr) *addr = ((struct sockaddr_in *)candidate->ifa_addr)->sin_addr;
if(mask) *mask = ((struct sockaddr_in *)candidate->ifa_netmask)->sin_addr;
}
freeifaddrs(ifap); freeifaddrs(ifap);
#endif #endif
return 0; return 0;