s4:kdc: Implement KDC plugin hardware authentication policy
[samba.git] / lib / socket / interfaces.c
index 9bdecb243d8ed434ed813031f825eab9280b5f36..2426ce2b52a3d3fd75b89d4296a0c5c2284e86dd 100644 (file)
 #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.
@@ -67,7 +73,7 @@ bool make_netmask(struct sockaddr_storage *pss_out,
 }
 
 /****************************************************************************
- 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.
 ****************************************************************************/
 
@@ -77,7 +83,7 @@ static void make_bcast_or_net(struct sockaddr_storage *pss_out,
                        bool make_bcast_p)
 {
        unsigned int i = 0, len = 0;
-       char *pmask = NULL;
+       const char *pmask = NULL;
        char *p = NULL;
        *pss_out = *pss_in;
 
@@ -85,13 +91,13 @@ static void make_bcast_or_net(struct sockaddr_storage *pss_out,
 #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;
        }
 
@@ -119,6 +125,91 @@ void make_net(struct sockaddr_storage *pss_out,
        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.
@@ -161,6 +252,8 @@ static int _get_interfaces(TALLOC_CTX *mem_ctx, struct iface_struct **pifaces)
 
        /* 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;
@@ -186,6 +279,21 @@ static int _get_interfaces(TALLOC_CTX *mem_ctx, struct iface_struct **pifaces)
                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,
@@ -195,25 +303,32 @@ static int _get_interfaces(TALLOC_CTX *mem_ctx, struct iface_struct **pifaces)
                        memcpy(&ifaces[total].bcast,
                                ifptr->ifa_dstaddr,
                                copy_size);
-#if defined(HAVE_IPV6)
-               } else if (ifptr->ifa_addr->sa_family == AF_INET6) {
-                       const struct sockaddr_in6 *sin6 =
-                               (const struct sockaddr_in6 *)ifptr->ifa_addr;
-                       const struct in6_addr *in6 =
-                               (const 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);
-#endif
                } else {
                        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++;
        }
 
@@ -271,18 +386,18 @@ static int iface_comp(struct iface_struct *i1, struct iface_struct *i2)
        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;
 }
@@ -291,11 +406,15 @@ static int iface_comp(struct iface_struct *i1, struct iface_struct *i2)
    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);