#include "system/network.h"
#include "interfaces.h"
#include "lib/util/tsort.h"
+#include "librpc/gen_ndr/ioctl.h"
+
+#ifdef HAVE_ETHTOOL
+#include "linux/sockios.h"
+#include "linux/ethtool.h"
+#endif
/****************************************************************************
Create a struct sockaddr_storage with the netmask bits set to 1.
}
/****************************************************************************
- Create a struct sockaddr_storage set to the broadcast or network adress from
+ Create a struct sockaddr_storage set to the broadcast or network address from
an incoming sockaddr_storage.
****************************************************************************/
bool make_bcast_p)
{
unsigned int i = 0, len = 0;
- char *pmask = NULL;
+ const char *pmask = NULL;
char *p = NULL;
*pss_out = *pss_in;
#if defined(HAVE_IPV6)
if (pss_in->ss_family == AF_INET6) {
p = (char *)&((struct sockaddr_in6 *)pss_out)->sin6_addr;
- pmask = discard_const_p(char, &((struct sockaddr_in6 *)nmask)->sin6_addr);
+ pmask = (const char *)&((const struct sockaddr_in6 *)nmask)->sin6_addr;
len = 16;
}
#endif
if (pss_in->ss_family == AF_INET) {
p = (char *)&((struct sockaddr_in *)pss_out)->sin_addr;
- pmask = discard_const_p(char, &((struct sockaddr_in *)nmask)->sin_addr);
+ pmask = (const char *)&((const struct sockaddr_in *)nmask)->sin_addr;
len = 4;
}
make_bcast_or_net(pss_out, pss_in, nmask, false);
}
+#ifdef HAVE_ETHTOOL
+static void query_iface_speed_from_name(const char *name, uint64_t *speed)
+{
+ int ret = 0;
+ struct ethtool_cmd ecmd;
+ struct ethtool_value edata;
+ struct ifreq ifr;
+ int fd;
+
+ fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
+ if (fd == -1) {
+ DBG_ERR("Failed to open socket.\n");
+ return;
+ }
+
+ if (strlen(name) >= IF_NAMESIZE) {
+ DBG_ERR("Interface name too long.\n");
+ goto done;
+ }
+
+ ZERO_STRUCT(ifr);
+ strlcpy(ifr.ifr_name, name, IF_NAMESIZE);
+
+ ifr.ifr_data = (void *)&edata;
+ ZERO_STRUCT(edata);
+ edata.cmd = ETHTOOL_GLINK;
+ ret = ioctl(fd, SIOCETHTOOL, &ifr);
+ if (ret == -1) {
+ goto done;
+ }
+ if (edata.data == 0) {
+ /* no link detected */
+ *speed = 0;
+ goto done;
+ }
+
+ ifr.ifr_data = (void *)&ecmd;
+ ZERO_STRUCT(ecmd);
+ ecmd.cmd = ETHTOOL_GSET;
+ ret = ioctl(fd, SIOCETHTOOL, &ifr);
+ if (ret == -1) {
+ goto done;
+ }
+ *speed = ((uint64_t)ethtool_cmd_speed(&ecmd)) * 1000 * 1000;
+
+done:
+ (void)close(fd);
+}
+
+static void query_iface_rx_queues_from_name(const char *name,
+ uint64_t *rx_queues)
+{
+ int ret = 0;
+ struct ethtool_rxnfc rxcmd;
+ struct ifreq ifr;
+ int fd;
+
+ fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
+ if (fd == -1) {
+ DBG_ERR("Failed to open socket.\n");
+ return;
+ }
+
+ if (strlen(name) >= IF_NAMESIZE) {
+ DBG_ERR("Interface name too long.\n");
+ goto done;
+ }
+
+ ZERO_STRUCT(ifr);
+ strlcpy(ifr.ifr_name, name, IF_NAMESIZE);
+
+ ifr.ifr_data = (void *)&rxcmd;
+ ZERO_STRUCT(rxcmd);
+ rxcmd.cmd = ETHTOOL_GRXRINGS;
+ ret = ioctl(fd, SIOCETHTOOL, &ifr);
+ if (ret == -1) {
+ goto done;
+ }
+
+ *rx_queues = rxcmd.data;
+
+done:
+ (void)close(fd);
+}
+#endif
/****************************************************************************
Try the "standard" getifaddrs/freeifaddrs interfaces.
/* Loop through interfaces, looking for given IP address */
for (ifptr = iflist; ifptr != NULL; ifptr = ifptr->ifa_next) {
+ uint64_t if_speed = 1000 * 1000 * 1000; /* 1Gbps */
+ uint64_t rx_queues = 1;
if (!ifptr->ifa_addr || !ifptr->ifa_netmask) {
continue;
memcpy(&ifaces[total].ip, ifptr->ifa_addr, copy_size);
memcpy(&ifaces[total].netmask, ifptr->ifa_netmask, copy_size);
+ /* calculate broadcast address */
+#if defined(HAVE_IPV6)
+ if (ifptr->ifa_addr->sa_family == AF_INET6) {
+ struct sockaddr_in6 *sin6 =
+ (struct sockaddr_in6 *)ifptr->ifa_addr;
+ struct in6_addr *in6 =
+ (struct in6_addr *)&sin6->sin6_addr;
+
+ if (IN6_IS_ADDR_LINKLOCAL(in6) || IN6_IS_ADDR_V4COMPAT(in6)) {
+ continue;
+ }
+ /* IPv6 does not have broadcast it uses multicast. */
+ memset(&ifaces[total].bcast, '\0', copy_size);
+ } else
+#endif
if (ifaces[total].flags & (IFF_BROADCAST|IFF_LOOPBACK)) {
make_bcast(&ifaces[total].bcast,
&ifaces[total].ip,
continue;
}
- strlcpy(ifaces[total].name, ifptr->ifa_name,
- sizeof(ifaces[total].name));
+ ifaces[total].if_index = if_nametoindex(ifptr->ifa_name);
+ if (ifaces[total].if_index == 0) {
+ DBG_ERR("Failed to retrieve interface index for '%s': "
+ "%s\n", ifptr->ifa_name, strerror(errno));
+ }
+
+#ifdef HAVE_ETHTOOL
+ query_iface_speed_from_name(ifptr->ifa_name, &if_speed);
+ query_iface_rx_queues_from_name(ifptr->ifa_name, &rx_queues);
+#endif
+ ifaces[total].linkspeed = if_speed;
+ ifaces[total].capability = FSCTL_NET_IFACE_NONE_CAPABLE;
+ if (rx_queues > 1) {
+ ifaces[total].capability |= FSCTL_NET_IFACE_RSS_CAPABLE;
+ }
+
+ if (strlcpy(ifaces[total].name, ifptr->ifa_name,
+ sizeof(ifaces[total].name)) >=
+ sizeof(ifaces[total].name)) {
+ /* Truncation ! Ignore. */
+ continue;
+ }
total++;
}
if (((struct sockaddr *)&i1->ip)->sa_family == AF_INET) {
struct sockaddr_in *s1 = (struct sockaddr_in *)&i1->ip;
struct sockaddr_in *s2 = (struct sockaddr_in *)&i2->ip;
-
- r = ntohl(s1->sin_addr.s_addr) -
- ntohl(s2->sin_addr.s_addr);
- if (r) {
- return r;
+ uint32_t a1 = ntohl(s1->sin_addr.s_addr);
+ uint32_t a2 = ntohl(s2->sin_addr.s_addr);
+ r = NUMERIC_CMP(a1, a2);
+ if (r == 0) {
+ /* compare netmasks as a tiebreaker */
+ s1 = (struct sockaddr_in *)&i1->netmask;
+ s2 = (struct sockaddr_in *)&i2->netmask;
+ a1 = ntohl(s1->sin_addr.s_addr);
+ a2 = ntohl(s2->sin_addr.s_addr);
+ r = NUMERIC_CMP(a1, a2);
}
-
- s1 = (struct sockaddr_in *)&i1->netmask;
- s2 = (struct sockaddr_in *)&i2->netmask;
-
- return ntohl(s1->sin_addr.s_addr) -
- ntohl(s2->sin_addr.s_addr);
+ return r;
}
return 0;
}
above */
int get_interfaces(TALLOC_CTX *mem_ctx, struct iface_struct **pifaces)
{
- struct iface_struct *ifaces;
+ struct iface_struct *ifaces = NULL;
int total, i, j;
total = _get_interfaces(mem_ctx, &ifaces);
- if (total <= 0) return total;
+ /* If we have an error, no interface or just one we can leave */
+ if (total <= 1) {
+ *pifaces = ifaces;
+ return total;
+ }
/* now we need to remove duplicates */
TYPESAFE_QSORT(ifaces, total, iface_comp);