LCOV - code coverage report
Current view: top level - lib/socket - interfaces.c (source / functions) Hit Total Coverage
Test: coverage report for master 2f515e9b Lines: 170 214 79.4 %
Date: 2024-04-21 15:09:00 Functions: 9 9 100.0 %

          Line data    Source code
       1             : /*
       2             :    Unix SMB/CIFS implementation.
       3             :    return a list of network interfaces
       4             :    Copyright (C) Andrew Tridgell 1998
       5             :    Copyright (C) Jeremy Allison 2007
       6             :    Copyright (C) Jelmer Vernooij 2007
       7             : 
       8             :    This program is free software; you can redistribute it and/or modify
       9             :    it under the terms of the GNU General Public License as published by
      10             :    the Free Software Foundation; either version 3 of the License, or
      11             :    (at your option) any later version.
      12             : 
      13             :    This program is distributed in the hope that it will be useful,
      14             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      15             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      16             :    GNU General Public License for more details.
      17             : 
      18             :    You should have received a copy of the GNU General Public License
      19             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      20             : */
      21             : 
      22             : 
      23             : #include "includes.h"
      24             : #include "system/network.h"
      25             : #include "interfaces.h"
      26             : #include "lib/util/tsort.h"
      27             : #include "librpc/gen_ndr/ioctl.h"
      28             : 
      29             : #ifdef HAVE_ETHTOOL
      30             : #include "linux/sockios.h"
      31             : #include "linux/ethtool.h"
      32             : #endif
      33             : 
      34             : /****************************************************************************
      35             :  Create a struct sockaddr_storage with the netmask bits set to 1.
      36             : ****************************************************************************/
      37             : 
      38      879112 : bool make_netmask(struct sockaddr_storage *pss_out,
      39             :                         const struct sockaddr_storage *pss_in,
      40             :                         unsigned long masklen)
      41             : {
      42      879112 :         *pss_out = *pss_in;
      43             :         /* Now apply masklen bits of mask. */
      44             : #if defined(HAVE_IPV6)
      45      879112 :         if (pss_in->ss_family == AF_INET6) {
      46      167401 :                 char *p = (char *)&((struct sockaddr_in6 *)pss_out)->sin6_addr;
      47        2851 :                 unsigned int i;
      48             : 
      49      167401 :                 if (masklen > 128) {
      50           0 :                         return false;
      51             :                 }
      52     1506609 :                 for (i = 0; masklen >= 8; masklen -= 8, i++) {
      53     1339208 :                         *p++ = 0xff;
      54             :                 }
      55             :                 /* Deal with the partial byte. */
      56      167401 :                 *p++ &= (0xff & ~(0xff>>masklen));
      57      167401 :                 i++;
      58     1339208 :                 for (;i < sizeof(struct in6_addr); i++) {
      59     1171807 :                         *p++ = '\0';
      60             :                 }
      61      164550 :                 return true;
      62             :         }
      63             : #endif
      64      711711 :         if (pss_in->ss_family == AF_INET) {
      65      711711 :                 if (masklen > 32) {
      66           0 :                         return false;
      67             :                 }
      68      711711 :                 ((struct sockaddr_in *)pss_out)->sin_addr.s_addr =
      69      711711 :                         htonl(((0xFFFFFFFFL >> masklen) ^ 0xFFFFFFFFL));
      70      711711 :                 return true;
      71             :         }
      72           0 :         return false;
      73             : }
      74             : 
      75             : /****************************************************************************
      76             :  Create a struct sockaddr_storage set to the broadcast or network address from
      77             :  an incoming sockaddr_storage.
      78             : ****************************************************************************/
      79             : 
      80     2094754 : static void make_bcast_or_net(struct sockaddr_storage *pss_out,
      81             :                         const struct sockaddr_storage *pss_in,
      82             :                         const struct sockaddr_storage *nmask,
      83             :                         bool make_bcast_p)
      84             : {
      85     2094754 :         unsigned int i = 0, len = 0;
      86     2094754 :         const char *pmask = NULL;
      87     2094754 :         char *p = NULL;
      88     2094754 :         *pss_out = *pss_in;
      89             : 
      90             :         /* Set all zero netmask bits to 1. */
      91             : #if defined(HAVE_IPV6)
      92     2094754 :         if (pss_in->ss_family == AF_INET6) {
      93      334802 :                 p = (char *)&((struct sockaddr_in6 *)pss_out)->sin6_addr;
      94      334802 :                 pmask = (const char *)&((const struct sockaddr_in6 *)nmask)->sin6_addr;
      95      334802 :                 len = 16;
      96             :         }
      97             : #endif
      98     2094754 :         if (pss_in->ss_family == AF_INET) {
      99     1759952 :                 p = (char *)&((struct sockaddr_in *)pss_out)->sin_addr;
     100     1759952 :                 pmask = (const char *)&((const struct sockaddr_in *)nmask)->sin_addr;
     101     1759952 :                 len = 4;
     102             :         }
     103             : 
     104    14491394 :         for (i = 0; i < len; i++, p++, pmask++) {
     105    12396640 :                 if (make_bcast_p) {
     106     6871380 :                         *p = (*p & *pmask) | (*pmask ^ 0xff);
     107             :                 } else {
     108             :                         /* make_net */
     109     5525260 :                         *p = (*p & *pmask);
     110             :                 }
     111             :         }
     112     2094754 : }
     113             : 
     114     1215642 : void make_bcast(struct sockaddr_storage *pss_out,
     115             :                         const struct sockaddr_storage *pss_in,
     116             :                         const struct sockaddr_storage *nmask)
     117             : {
     118     1215642 :         make_bcast_or_net(pss_out, pss_in, nmask, true);
     119     1215642 : }
     120             : 
     121      879112 : void make_net(struct sockaddr_storage *pss_out,
     122             :                         const struct sockaddr_storage *pss_in,
     123             :                         const struct sockaddr_storage *nmask)
     124             : {
     125      879112 :         make_bcast_or_net(pss_out, pss_in, nmask, false);
     126      879112 : }
     127             : 
     128             : #ifdef HAVE_ETHTOOL
     129      673060 : static void query_iface_speed_from_name(const char *name, uint64_t *speed)
     130             : {
     131      673060 :         int ret = 0;
     132       11472 :         struct ethtool_cmd ecmd;
     133       11472 :         struct ethtool_value edata;
     134       11472 :         struct ifreq ifr;
     135       11472 :         int fd;
     136             : 
     137      673060 :         fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
     138      673060 :         if (fd == -1) {
     139           0 :                 DBG_ERR("Failed to open socket.\n");
     140           0 :                 return;
     141             :         }
     142             : 
     143      673060 :         if (strlen(name) >= IF_NAMESIZE) {
     144           0 :                 DBG_ERR("Interface name too long.\n");
     145           0 :                 goto done;
     146             :         }
     147             : 
     148      673060 :         ZERO_STRUCT(ifr);
     149      673060 :         strlcpy(ifr.ifr_name, name, IF_NAMESIZE);
     150             : 
     151      673060 :         ifr.ifr_data = (void *)&edata;
     152      673060 :         ZERO_STRUCT(edata);
     153      673060 :         edata.cmd = ETHTOOL_GLINK;
     154      673060 :         ret = ioctl(fd, SIOCETHTOOL, &ifr);
     155      673060 :         if (ret == -1) {
     156           0 :                 goto done;
     157             :         }
     158      673060 :         if (edata.data == 0) {
     159             :                 /* no link detected */
     160           0 :                 *speed = 0;
     161           0 :                 goto done;
     162             :         }
     163             : 
     164      673060 :         ifr.ifr_data = (void *)&ecmd;
     165      673060 :         ZERO_STRUCT(ecmd);
     166      673060 :         ecmd.cmd = ETHTOOL_GSET;
     167      673060 :         ret = ioctl(fd, SIOCETHTOOL, &ifr);
     168      673060 :         if (ret == -1) {
     169      336530 :                 goto done;
     170             :         }
     171      336530 :         *speed = ((uint64_t)ethtool_cmd_speed(&ecmd)) * 1000 * 1000;
     172             : 
     173      673060 : done:
     174      673060 :         (void)close(fd);
     175             : }
     176             : 
     177      673060 : static void query_iface_rx_queues_from_name(const char *name,
     178             :                                             uint64_t *rx_queues)
     179             : {
     180      673060 :         int ret = 0;
     181       11472 :         struct ethtool_rxnfc rxcmd;
     182       11472 :         struct ifreq ifr;
     183       11472 :         int fd;
     184             : 
     185      673060 :         fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
     186      673060 :         if (fd == -1) {
     187           0 :                 DBG_ERR("Failed to open socket.\n");
     188           0 :                 return;
     189             :         }
     190             : 
     191      673060 :         if (strlen(name) >= IF_NAMESIZE) {
     192           0 :                 DBG_ERR("Interface name too long.\n");
     193           0 :                 goto done;
     194             :         }
     195             : 
     196      673060 :         ZERO_STRUCT(ifr);
     197      673060 :         strlcpy(ifr.ifr_name, name, IF_NAMESIZE);
     198             : 
     199      673060 :         ifr.ifr_data = (void *)&rxcmd;
     200      673060 :         ZERO_STRUCT(rxcmd);
     201      673060 :         rxcmd.cmd = ETHTOOL_GRXRINGS;
     202      673060 :         ret = ioctl(fd, SIOCETHTOOL, &ifr);
     203      673060 :         if (ret == -1) {
     204      673060 :                 goto done;
     205             :         }
     206             : 
     207           0 :         *rx_queues = rxcmd.data;
     208             : 
     209      673060 : done:
     210      673060 :         (void)close(fd);
     211             : }
     212             : #endif
     213             : 
     214             : /****************************************************************************
     215             :  Try the "standard" getifaddrs/freeifaddrs interfaces.
     216             :  Also gets IPv6 interfaces.
     217             : ****************************************************************************/
     218             : 
     219             : /****************************************************************************
     220             :  Get the netmask address for a local interface.
     221             : ****************************************************************************/
     222             : 
     223      168265 : static int _get_interfaces(TALLOC_CTX *mem_ctx, struct iface_struct **pifaces)
     224             : {
     225        2868 :         struct iface_struct *ifaces;
     226      168265 :         struct ifaddrs *iflist = NULL;
     227      168265 :         struct ifaddrs *ifptr = NULL;
     228        2868 :         int count;
     229      168265 :         int total = 0;
     230        2868 :         size_t copy_size;
     231             : 
     232      168265 :         if (getifaddrs(&iflist) < 0) {
     233           0 :                 return -1;
     234             :         }
     235             : 
     236      168265 :         count = 0;
     237     1346120 :         for (ifptr = iflist; ifptr != NULL; ifptr = ifptr->ifa_next) {
     238     1177855 :                 if (!ifptr->ifa_addr || !ifptr->ifa_netmask) {
     239      336530 :                         continue;
     240             :                 }
     241      841325 :                 if (!(ifptr->ifa_flags & IFF_UP)) {
     242           0 :                         continue;
     243             :                 }
     244      841325 :                 count += 1;
     245             :         }
     246             : 
     247      168265 :         ifaces = talloc_array(mem_ctx, struct iface_struct, count);
     248      168265 :         if (ifaces == NULL) {
     249           0 :                 errno = ENOMEM;
     250           0 :                 return -1;
     251             :         }
     252             : 
     253             :         /* Loop through interfaces, looking for given IP address */
     254     1346120 :         for (ifptr = iflist; ifptr != NULL; ifptr = ifptr->ifa_next) {
     255     1177855 :                 uint64_t if_speed = 1000 * 1000 * 1000; /* 1Gbps */
     256     1177855 :                 uint64_t rx_queues = 1;
     257             : 
     258     1177855 :                 if (!ifptr->ifa_addr || !ifptr->ifa_netmask) {
     259      504795 :                         continue;
     260             :                 }
     261             : 
     262             :                 /* Check the interface is up. */
     263      841325 :                 if (!(ifptr->ifa_flags & IFF_UP)) {
     264           0 :                         continue;
     265             :                 }
     266             : 
     267      841325 :                 memset(&ifaces[total], '\0', sizeof(ifaces[total]));
     268             : 
     269      841325 :                 copy_size = sizeof(struct sockaddr_in);
     270             : 
     271      841325 :                 ifaces[total].flags = ifptr->ifa_flags;
     272             : 
     273             : #if defined(HAVE_IPV6)
     274      841325 :                 if (ifptr->ifa_addr->sa_family == AF_INET6) {
     275      504795 :                         copy_size = sizeof(struct sockaddr_in6);
     276             :                 }
     277             : #endif
     278             : 
     279      841325 :                 memcpy(&ifaces[total].ip, ifptr->ifa_addr, copy_size);
     280      841325 :                 memcpy(&ifaces[total].netmask, ifptr->ifa_netmask, copy_size);
     281             : 
     282             :                 /* calculate broadcast address */
     283             : #if defined(HAVE_IPV6)
     284      841325 :                 if (ifptr->ifa_addr->sa_family == AF_INET6) {
     285      504795 :                         struct sockaddr_in6 *sin6 =
     286             :                                 (struct sockaddr_in6 *)ifptr->ifa_addr;
     287      504795 :                         struct in6_addr *in6 =
     288             :                                 (struct in6_addr *)&sin6->sin6_addr;
     289             : 
     290      504795 :                         if (IN6_IS_ADDR_LINKLOCAL(in6) || IN6_IS_ADDR_V4COMPAT(in6)) {
     291      168265 :                                 continue;
     292             :                         }
     293             :                         /* IPv6 does not have broadcast it uses multicast. */
     294      336530 :                         memset(&ifaces[total].bcast, '\0', copy_size);
     295             :                 } else
     296             : #endif
     297      336530 :                 if (ifaces[total].flags & (IFF_BROADCAST|IFF_LOOPBACK)) {
     298      336530 :                         make_bcast(&ifaces[total].bcast,
     299      330794 :                                 &ifaces[total].ip,
     300      330794 :                                 &ifaces[total].netmask);
     301           0 :                 } else if ((ifaces[total].flags & IFF_POINTOPOINT) &&
     302           0 :                                ifptr->ifa_dstaddr ) {
     303           0 :                         memcpy(&ifaces[total].bcast,
     304           0 :                                 ifptr->ifa_dstaddr,
     305             :                                 copy_size);
     306             :                 } else {
     307           0 :                         continue;
     308             :                 }
     309             : 
     310      673060 :                 ifaces[total].if_index = if_nametoindex(ifptr->ifa_name);
     311      673060 :                 if (ifaces[total].if_index == 0) {
     312           0 :                         DBG_ERR("Failed to retrieve interface index for '%s': "
     313             :                                 "%s\n", ifptr->ifa_name, strerror(errno));
     314             :                 }
     315             : 
     316             : #ifdef HAVE_ETHTOOL
     317      673060 :                 query_iface_speed_from_name(ifptr->ifa_name, &if_speed);
     318      673060 :                 query_iface_rx_queues_from_name(ifptr->ifa_name, &rx_queues);
     319             : #endif
     320      673060 :                 ifaces[total].linkspeed = if_speed;
     321      673060 :                 ifaces[total].capability = FSCTL_NET_IFACE_NONE_CAPABLE;
     322      673060 :                 if (rx_queues > 1) {
     323           0 :                         ifaces[total].capability |= FSCTL_NET_IFACE_RSS_CAPABLE;
     324             :                 }
     325             : 
     326      673060 :                 if (strlcpy(ifaces[total].name, ifptr->ifa_name,
     327             :                         sizeof(ifaces[total].name)) >=
     328             :                                 sizeof(ifaces[total].name)) {
     329             :                         /* Truncation ! Ignore. */
     330           0 :                         continue;
     331             :                 }
     332      673060 :                 total++;
     333             :         }
     334             : 
     335      168265 :         freeifaddrs(iflist);
     336             : 
     337      168265 :         *pifaces = ifaces;
     338      168265 :         return total;
     339             : }
     340             : 
     341     1346120 : static int iface_comp(struct iface_struct *i1, struct iface_struct *i2)
     342             : {
     343       22944 :         int r;
     344             : 
     345             : #if defined(HAVE_IPV6)
     346             :         /*
     347             :          * If we have IPv6 - sort these interfaces lower
     348             :          * than any IPv4 ones.
     349             :          */
     350     1346120 :         if (i1->ip.ss_family == AF_INET6 &&
     351      673060 :                         i2->ip.ss_family == AF_INET) {
     352      165397 :                 return -1;
     353     1177855 :         } else if (i1->ip.ss_family == AF_INET &&
     354      673060 :                         i2->ip.ss_family == AF_INET6) {
     355      330794 :                 return 1;
     356             :         }
     357             : 
     358      841325 :         if (i1->ip.ss_family == AF_INET6) {
     359      504795 :                 struct sockaddr_in6 *s1 = (struct sockaddr_in6 *)&i1->ip;
     360      504795 :                 struct sockaddr_in6 *s2 = (struct sockaddr_in6 *)&i2->ip;
     361             : 
     362      504795 :                 r = memcmp(&s1->sin6_addr,
     363      504795 :                                 &s2->sin6_addr,
     364             :                                 sizeof(struct in6_addr));
     365      504795 :                 if (r) {
     366      496191 :                         return r;
     367             :                 }
     368             : 
     369           0 :                 s1 = (struct sockaddr_in6 *)&i1->netmask;
     370           0 :                 s2 = (struct sockaddr_in6 *)&i2->netmask;
     371             : 
     372           0 :                 r = memcmp(&s1->sin6_addr,
     373           0 :                                 &s2->sin6_addr,
     374             :                                 sizeof(struct in6_addr));
     375           0 :                 if (r) {
     376           0 :                         return r;
     377             :                 }
     378             :         }
     379             : #endif
     380             : 
     381             :         /* AIX uses __ss_family instead of ss_family inside of
     382             :            sockaddr_storage. Instead of trying to figure out which field to
     383             :            use, we can just cast it to a sockaddr.
     384             :          */
     385             : 
     386      336530 :         if (((struct sockaddr *)&i1->ip)->sa_family == AF_INET) {
     387      336530 :                 struct sockaddr_in *s1 = (struct sockaddr_in *)&i1->ip;
     388      336530 :                 struct sockaddr_in *s2 = (struct sockaddr_in *)&i2->ip;
     389             : 
     390      336530 :                 r = ntohl(s1->sin_addr.s_addr) -
     391      336530 :                         ntohl(s2->sin_addr.s_addr);
     392      336530 :                 if (r) {
     393      330794 :                         return r;
     394             :                 }
     395             : 
     396           0 :                 s1 = (struct sockaddr_in *)&i1->netmask;
     397           0 :                 s2 = (struct sockaddr_in *)&i2->netmask;
     398             : 
     399           0 :                 return ntohl(s1->sin_addr.s_addr) -
     400           0 :                         ntohl(s2->sin_addr.s_addr);
     401             :         }
     402           0 :         return 0;
     403             : }
     404             : 
     405             : /* this wrapper is used to remove duplicates from the interface list generated
     406             :    above */
     407      168265 : int get_interfaces(TALLOC_CTX *mem_ctx, struct iface_struct **pifaces)
     408             : {
     409      168265 :         struct iface_struct *ifaces = NULL;
     410        2868 :         int total, i, j;
     411             : 
     412      168265 :         total = _get_interfaces(mem_ctx, &ifaces);
     413             :         /* If we have an error, no interface or just one we can leave */
     414      168265 :         if (total <= 1) {
     415           0 :                 *pifaces = ifaces;
     416           0 :                 return total;
     417             :         }
     418             : 
     419             :         /* now we need to remove duplicates */
     420      168265 :         TYPESAFE_QSORT(ifaces, total, iface_comp);
     421             : 
     422      673060 :         for (i=1;i<total;) {
     423      504795 :                 if (iface_comp(&ifaces[i-1], &ifaces[i]) == 0) {
     424           0 :                         for (j=i-1;j<total-1;j++) {
     425           0 :                                 ifaces[j] = ifaces[j+1];
     426             :                         }
     427           0 :                         total--;
     428             :                 } else {
     429      504795 :                         i++;
     430             :                 }
     431             :         }
     432             : 
     433      168265 :         *pifaces = ifaces;
     434      168265 :         return total;
     435             : }

Generated by: LCOV version 1.14