support multiple ip addresses on same interface

This commit is contained in:
nickkelsey 2019-06-07 17:19:08 -07:00 committed by Pavel Koshevoy
commit 3c60a84adb

View file

@ -20,8 +20,13 @@
#include "hdhomerun.h" #include "hdhomerun.h"
#include <net/if.h> #if defined(LIBHDHOMERUN_USE_LEGACY_SIOCGIFCONF)
#include <sys/ioctl.h> #include <sys/ioctl.h>
#else
#include <ifaddrs.h>
#endif
#include <net/if.h>
#ifndef MSG_NOSIGNAL #ifndef MSG_NOSIGNAL
#define MSG_NOSIGNAL 0 #define MSG_NOSIGNAL 0
@ -31,48 +36,6 @@ struct hdhomerun_sock_t {
int sock; int sock;
}; };
static bool hdhomerun_local_ip_info_add(struct hdhomerun_local_ip_info_t *ip_info, int sock, struct ifreq *ifr)
{
/* Flags. */
if (ioctl(sock, SIOCGIFFLAGS, ifr) != 0) {
return false;
}
if ((ifr->ifr_flags & IFF_UP) == 0) {
return false;
}
if ((ifr->ifr_flags & IFF_RUNNING) == 0) {
return false;
}
/* Local IP address. */
if (ioctl(sock, SIOCGIFADDR, ifr) != 0) {
return false;
}
struct sockaddr_in *ip_addr_in = (struct sockaddr_in *)&ifr->ifr_addr;
uint32_t ip_addr = ntohl(ip_addr_in->sin_addr.s_addr);
if (ip_addr == 0) {
return false;
}
/* Subnet mask. */
if (ioctl(sock, SIOCGIFNETMASK, ifr) != 0) {
return false;
}
struct sockaddr_in *subnet_mask_in = (struct sockaddr_in *)&ifr->ifr_addr;
uint32_t subnet_mask = ntohl(subnet_mask_in->sin_addr.s_addr);
/* Result. */
if (ip_info) {
ip_info->ip_addr = ip_addr;
ip_info->subnet_mask = subnet_mask;
}
return true;
}
#if defined(LIBHDHOMERUN_USE_LEGACY_SIOCGIFCONF) #if defined(LIBHDHOMERUN_USE_LEGACY_SIOCGIFCONF)
int hdhomerun_local_ip_info(struct hdhomerun_local_ip_info_t ip_info_list[], int max_count) int hdhomerun_local_ip_info(struct hdhomerun_local_ip_info_t ip_info_list[], int max_count)
{ {
@ -113,7 +76,7 @@ int hdhomerun_local_ip_info(struct hdhomerun_local_ip_info_t ip_info_list[], int
} }
} }
struct hdhomerun_local_ip_info_t *ip_info = (max_count > 0) ? ip_info_list : NULL; struct hdhomerun_local_ip_info_t *ip_info = ip_info_list;
char *ptr = ifc.ifc_buf; char *ptr = ifc.ifc_buf;
char *end = ifc.ifc_buf + ifc.ifc_len; char *end = ifc.ifc_buf + ifc.ifc_len;
int count = 0; int count = 0;
@ -122,17 +85,43 @@ int hdhomerun_local_ip_info(struct hdhomerun_local_ip_info_t ip_info_list[], int
struct ifreq *ifr = (struct ifreq *)ptr; struct ifreq *ifr = (struct ifreq *)ptr;
ptr += sizeof(struct ifreq); ptr += sizeof(struct ifreq);
if (!hdhomerun_local_ip_info_add(ip_info, sock, ifr)) { /* Flags. */
if (ioctl(sock, SIOCGIFFLAGS, ifr) != 0) {
continue; continue;
} }
count++; unsigned int flags = ifr->ifr_flags & (IFF_LOOPBACK | IFF_POINTOPOINT | IFF_UP | IFF_RUNNING);
if (flags != (IFF_UP | IFF_RUNNING)) {
continue;
}
if (count >= max_count) { /* Local IP address. */
ip_info = NULL; if (ioctl(sock, SIOCGIFADDR, ifr) != 0) {
} else { continue;
}
struct sockaddr_in *ip_addr_in = (struct sockaddr_in *)&ifr->ifr_addr;
uint32_t ip_addr = ntohl(ip_addr_in->sin_addr.s_addr);
if (ip_addr == 0) {
continue;
}
/* Subnet mask. */
if (ioctl(sock, SIOCGIFNETMASK, ifr) != 0) {
continue;
}
struct sockaddr_in *subnet_mask_in = (struct sockaddr_in *)&ifr->ifr_addr;
uint32_t subnet_mask = ntohl(subnet_mask_in->sin_addr.s_addr);
/* Result. */
if (count < max_count) {
ip_info->ip_addr = ip_addr;
ip_info->subnet_mask = subnet_mask;
ip_info++; ip_info++;
} }
count++;
} }
free(ifreq_buffer); free(ifreq_buffer);
@ -142,51 +131,50 @@ int hdhomerun_local_ip_info(struct hdhomerun_local_ip_info_t ip_info_list[], int
#else #else
int hdhomerun_local_ip_info(struct hdhomerun_local_ip_info_t ip_info_list[], int max_count) int hdhomerun_local_ip_info(struct hdhomerun_local_ip_info_t ip_info_list[], int max_count)
{ {
int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); struct ifaddrs *ifaddrs;
if (sock == -1) { if (getifaddrs(&ifaddrs) != 0) {
return -1; return -1;
} }
struct if_nameindex *ni = if_nameindex(); struct hdhomerun_local_ip_info_t *ip_info = ip_info_list;
if (!ni) { struct ifaddrs *ifa = ifaddrs;
close(sock);
return -1;
}
struct hdhomerun_local_ip_info_t *ip_info = (max_count > 0) ? ip_info_list : NULL;
struct if_nameindex *iter = ni;
int count = 0; int count = 0;
while (1) { while (ifa) {
if (!iter->if_name) { if (ifa->ifa_addr == NULL) {
if (iter->if_index == 0) { ifa = ifa->ifa_next;
break;
}
iter++;
continue; continue;
} }
struct ifreq ifr; if (ifa->ifa_addr->sa_family != AF_INET) {
memset(&ifr, 0, sizeof(ifr)); ifa = ifa->ifa_next;
strncpy(ifr.ifr_name, iter->if_name, IF_NAMESIZE);
iter++;
if (!hdhomerun_local_ip_info_add(ip_info, sock, &ifr)) {
continue; continue;
} }
unsigned int flags = ifa->ifa_flags & (IFF_LOOPBACK | IFF_POINTOPOINT | IFF_UP | IFF_RUNNING);
if (flags != (IFF_UP | IFF_RUNNING)) {
ifa = ifa->ifa_next;
continue;
}
struct sockaddr_in *addr_in = (struct sockaddr_in *)ifa->ifa_addr;
uint32_t ip_addr = ntohl(addr_in->sin_addr.s_addr);
struct sockaddr_in *netmask_in = (struct sockaddr_in *)ifa->ifa_netmask;
uint32_t subnet_mask = ntohl(netmask_in->sin_addr.s_addr);
ifa = ifa->ifa_next;
if (count < max_count) {
ip_info->ip_addr = ip_addr;
ip_info->subnet_mask = subnet_mask;
ip_info++;
}
count++; count++;
if (count >= max_count) {
ip_info = NULL;
} else {
ip_info++;
}
} }
if_freenameindex(ni); freeifaddrs(ifaddrs);
close(sock);
return count; return count;
} }
#endif #endif