LCOV - code coverage report
Current view: top level - lib/socket - interfaces.c (source / functions) Hit Total Coverage
Test: coverage report for master 98b443d9 Lines: 171 216 79.2 %
Date: 2024-05-31 13:13:24 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      884630 : bool make_netmask(struct sockaddr_storage *pss_out,
      39             :                         const struct sockaddr_storage *pss_in,
      40             :                         unsigned long masklen)
      41             : {
      42      884630 :         *pss_out = *pss_in;
      43             :         /* Now apply masklen bits of mask. */
      44             : #if defined(HAVE_IPV6)
      45      884630 :         if (pss_in->ss_family == AF_INET6) {
      46      169190 :                 char *p = (char *)&((struct sockaddr_in6 *)pss_out)->sin6_addr;
      47        2899 :                 unsigned int i;
      48             : 
      49      169190 :                 if (masklen > 128) {
      50           0 :                         return false;
      51             :                 }
      52     1522710 :                 for (i = 0; masklen >= 8; masklen -= 8, i++) {
      53     1353520 :                         *p++ = 0xff;
      54             :                 }
      55             :                 /* Deal with the partial byte. */
      56      169190 :                 *p++ &= (0xff & ~(0xff>>masklen));
      57      169190 :                 i++;
      58     1353520 :                 for (;i < sizeof(struct in6_addr); i++) {
      59     1184330 :                         *p++ = '\0';
      60             :                 }
      61      166291 :                 return true;
      62             :         }
      63             : #endif
      64      715440 :         if (pss_in->ss_family == AF_INET) {
      65      715440 :                 if (masklen > 32) {
      66           0 :                         return false;
      67             :                 }
      68      715440 :                 ((struct sockaddr_in *)pss_out)->sin_addr.s_addr =
      69      715440 :                         htonl(((0xFFFFFFFFL >> masklen) ^ 0xFFFFFFFFL));
      70      715440 :                 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     2109456 : 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     2109456 :         unsigned int i = 0, len = 0;
      86     2109456 :         const char *pmask = NULL;
      87     2109456 :         char *p = NULL;
      88     2109456 :         *pss_out = *pss_in;
      89             : 
      90             :         /* Set all zero netmask bits to 1. */
      91             : #if defined(HAVE_IPV6)
      92     2109456 :         if (pss_in->ss_family == AF_INET6) {
      93      338380 :                 p = (char *)&((struct sockaddr_in6 *)pss_out)->sin6_addr;
      94      338380 :                 pmask = (const char *)&((const struct sockaddr_in6 *)nmask)->sin6_addr;
      95      338380 :                 len = 16;
      96             :         }
      97             : #endif
      98     2109456 :         if (pss_in->ss_family == AF_INET) {
      99     1771076 :                 p = (char *)&((struct sockaddr_in *)pss_out)->sin_addr;
     100     1771076 :                 pmask = (const char *)&((const struct sockaddr_in *)nmask)->sin_addr;
     101     1771076 :                 len = 4;
     102             :         }
     103             : 
     104    14607840 :         for (i = 0; i < len; i++, p++, pmask++) {
     105    12498384 :                 if (make_bcast_p) {
     106     6929584 :                         *p = (*p & *pmask) | (*pmask ^ 0xff);
     107             :                 } else {
     108             :                         /* make_net */
     109     5568800 :                         *p = (*p & *pmask);
     110             :                 }
     111             :         }
     112     2109456 : }
     113             : 
     114     1224826 : void make_bcast(struct sockaddr_storage *pss_out,
     115             :                         const struct sockaddr_storage *pss_in,
     116             :                         const struct sockaddr_storage *nmask)
     117             : {
     118     1224826 :         make_bcast_or_net(pss_out, pss_in, nmask, true);
     119     1224826 : }
     120             : 
     121      884630 : void make_net(struct sockaddr_storage *pss_out,
     122             :                         const struct sockaddr_storage *pss_in,
     123             :                         const struct sockaddr_storage *nmask)
     124             : {
     125      884630 :         make_bcast_or_net(pss_out, pss_in, nmask, false);
     126      884630 : }
     127             : 
     128             : #ifdef HAVE_ETHTOOL
     129      680392 : static void query_iface_speed_from_name(const char *name, uint64_t *speed)
     130             : {
     131      680392 :         int ret = 0;
     132       11664 :         struct ethtool_cmd ecmd;
     133       11664 :         struct ethtool_value edata;
     134       11664 :         struct ifreq ifr;
     135       11664 :         int fd;
     136             : 
     137      680392 :         fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
     138      680392 :         if (fd == -1) {
     139           0 :                 DBG_ERR("Failed to open socket.\n");
     140           0 :                 return;
     141             :         }
     142             : 
     143      680392 :         if (strlen(name) >= IF_NAMESIZE) {
     144           0 :                 DBG_ERR("Interface name too long.\n");
     145           0 :                 goto done;
     146             :         }
     147             : 
     148      680392 :         ZERO_STRUCT(ifr);
     149      680392 :         strlcpy(ifr.ifr_name, name, IF_NAMESIZE);
     150             : 
     151      680392 :         ifr.ifr_data = (void *)&edata;
     152      680392 :         ZERO_STRUCT(edata);
     153      680392 :         edata.cmd = ETHTOOL_GLINK;
     154      680392 :         ret = ioctl(fd, SIOCETHTOOL, &ifr);
     155      680392 :         if (ret == -1) {
     156           0 :                 goto done;
     157             :         }
     158      680392 :         if (edata.data == 0) {
     159             :                 /* no link detected */
     160           0 :                 *speed = 0;
     161           0 :                 goto done;
     162             :         }
     163             : 
     164      680392 :         ifr.ifr_data = (void *)&ecmd;
     165      680392 :         ZERO_STRUCT(ecmd);
     166      680392 :         ecmd.cmd = ETHTOOL_GSET;
     167      680392 :         ret = ioctl(fd, SIOCETHTOOL, &ifr);
     168      680392 :         if (ret == -1) {
     169      340196 :                 goto done;
     170             :         }
     171      340196 :         *speed = ((uint64_t)ethtool_cmd_speed(&ecmd)) * 1000 * 1000;
     172             : 
     173      680392 : done:
     174      680392 :         (void)close(fd);
     175             : }
     176             : 
     177      680392 : static void query_iface_rx_queues_from_name(const char *name,
     178             :                                             uint64_t *rx_queues)
     179             : {
     180      680392 :         int ret = 0;
     181       11664 :         struct ethtool_rxnfc rxcmd;
     182       11664 :         struct ifreq ifr;
     183       11664 :         int fd;
     184             : 
     185      680392 :         fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
     186      680392 :         if (fd == -1) {
     187           0 :                 DBG_ERR("Failed to open socket.\n");
     188           0 :                 return;
     189             :         }
     190             : 
     191      680392 :         if (strlen(name) >= IF_NAMESIZE) {
     192           0 :                 DBG_ERR("Interface name too long.\n");
     193           0 :                 goto done;
     194             :         }
     195             : 
     196      680392 :         ZERO_STRUCT(ifr);
     197      680392 :         strlcpy(ifr.ifr_name, name, IF_NAMESIZE);
     198             : 
     199      680392 :         ifr.ifr_data = (void *)&rxcmd;
     200      680392 :         ZERO_STRUCT(rxcmd);
     201      680392 :         rxcmd.cmd = ETHTOOL_GRXRINGS;
     202      680392 :         ret = ioctl(fd, SIOCETHTOOL, &ifr);
     203      680392 :         if (ret == -1) {
     204      680392 :                 goto done;
     205             :         }
     206             : 
     207           0 :         *rx_queues = rxcmd.data;
     208             : 
     209      680392 : done:
     210      680392 :         (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      170098 : static int _get_interfaces(TALLOC_CTX *mem_ctx, struct iface_struct **pifaces)
     224             : {
     225        2916 :         struct iface_struct *ifaces;
     226      170098 :         struct ifaddrs *iflist = NULL;
     227      170098 :         struct ifaddrs *ifptr = NULL;
     228        2916 :         int count;
     229      170098 :         int total = 0;
     230        2916 :         size_t copy_size;
     231             : 
     232      170098 :         if (getifaddrs(&iflist) < 0) {
     233           0 :                 return -1;
     234             :         }
     235             : 
     236      170098 :         count = 0;
     237     1360784 :         for (ifptr = iflist; ifptr != NULL; ifptr = ifptr->ifa_next) {
     238     1190686 :                 if (!ifptr->ifa_addr || !ifptr->ifa_netmask) {
     239      340196 :                         continue;
     240             :                 }
     241      850490 :                 if (!(ifptr->ifa_flags & IFF_UP)) {
     242           0 :                         continue;
     243             :                 }
     244      850490 :                 count += 1;
     245             :         }
     246             : 
     247      170098 :         ifaces = talloc_array(mem_ctx, struct iface_struct, count);
     248      170098 :         if (ifaces == NULL) {
     249           0 :                 errno = ENOMEM;
     250           0 :                 return -1;
     251             :         }
     252             : 
     253             :         /* Loop through interfaces, looking for given IP address */
     254     1360784 :         for (ifptr = iflist; ifptr != NULL; ifptr = ifptr->ifa_next) {
     255     1190686 :                 uint64_t if_speed = 1000 * 1000 * 1000; /* 1Gbps */
     256     1190686 :                 uint64_t rx_queues = 1;
     257             : 
     258     1190686 :                 if (!ifptr->ifa_addr || !ifptr->ifa_netmask) {
     259      510294 :                         continue;
     260             :                 }
     261             : 
     262             :                 /* Check the interface is up. */
     263      850490 :                 if (!(ifptr->ifa_flags & IFF_UP)) {
     264           0 :                         continue;
     265             :                 }
     266             : 
     267      850490 :                 memset(&ifaces[total], '\0', sizeof(ifaces[total]));
     268             : 
     269      850490 :                 copy_size = sizeof(struct sockaddr_in);
     270             : 
     271      850490 :                 ifaces[total].flags = ifptr->ifa_flags;
     272             : 
     273             : #if defined(HAVE_IPV6)
     274      850490 :                 if (ifptr->ifa_addr->sa_family == AF_INET6) {
     275      510294 :                         copy_size = sizeof(struct sockaddr_in6);
     276             :                 }
     277             : #endif
     278             : 
     279      850490 :                 memcpy(&ifaces[total].ip, ifptr->ifa_addr, copy_size);
     280      850490 :                 memcpy(&ifaces[total].netmask, ifptr->ifa_netmask, copy_size);
     281             : 
     282             :                 /* calculate broadcast address */
     283             : #if defined(HAVE_IPV6)
     284      850490 :                 if (ifptr->ifa_addr->sa_family == AF_INET6) {
     285      510294 :                         struct sockaddr_in6 *sin6 =
     286             :                                 (struct sockaddr_in6 *)ifptr->ifa_addr;
     287      510294 :                         struct in6_addr *in6 =
     288             :                                 (struct in6_addr *)&sin6->sin6_addr;
     289             : 
     290      510294 :                         if (IN6_IS_ADDR_LINKLOCAL(in6) || IN6_IS_ADDR_V4COMPAT(in6)) {
     291      170098 :                                 continue;
     292             :                         }
     293             :                         /* IPv6 does not have broadcast it uses multicast. */
     294      340196 :                         memset(&ifaces[total].bcast, '\0', copy_size);
     295             :                 } else
     296             : #endif
     297      340196 :                 if (ifaces[total].flags & (IFF_BROADCAST|IFF_LOOPBACK)) {
     298      340196 :                         make_bcast(&ifaces[total].bcast,
     299      334364 :                                 &ifaces[total].ip,
     300      334364 :                                 &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      680392 :                 ifaces[total].if_index = if_nametoindex(ifptr->ifa_name);
     311      680392 :                 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      680392 :                 query_iface_speed_from_name(ifptr->ifa_name, &if_speed);
     318      680392 :                 query_iface_rx_queues_from_name(ifptr->ifa_name, &rx_queues);
     319             : #endif
     320      680392 :                 ifaces[total].linkspeed = if_speed;
     321      680392 :                 ifaces[total].capability = FSCTL_NET_IFACE_NONE_CAPABLE;
     322      680392 :                 if (rx_queues > 1) {
     323           0 :                         ifaces[total].capability |= FSCTL_NET_IFACE_RSS_CAPABLE;
     324             :                 }
     325             : 
     326      680392 :                 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      680392 :                 total++;
     333             :         }
     334             : 
     335      170098 :         freeifaddrs(iflist);
     336             : 
     337      170098 :         *pifaces = ifaces;
     338      170098 :         return total;
     339             : }
     340             : 
     341     1360784 : static int iface_comp(struct iface_struct *i1, struct iface_struct *i2)
     342             : {
     343       23328 :         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     1360784 :         if (i1->ip.ss_family == AF_INET6 &&
     351      680392 :                         i2->ip.ss_family == AF_INET) {
     352      167182 :                 return -1;
     353     1190686 :         } else if (i1->ip.ss_family == AF_INET &&
     354      680392 :                         i2->ip.ss_family == AF_INET6) {
     355      334364 :                 return 1;
     356             :         }
     357             : 
     358      850490 :         if (i1->ip.ss_family == AF_INET6) {
     359      510294 :                 struct sockaddr_in6 *s1 = (struct sockaddr_in6 *)&i1->ip;
     360      510294 :                 struct sockaddr_in6 *s2 = (struct sockaddr_in6 *)&i2->ip;
     361             : 
     362      510294 :                 r = memcmp(&s1->sin6_addr,
     363      510294 :                                 &s2->sin6_addr,
     364             :                                 sizeof(struct in6_addr));
     365      510294 :                 if (r) {
     366      501546 :                         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      340196 :         if (((struct sockaddr *)&i1->ip)->sa_family == AF_INET) {
     387      340196 :                 struct sockaddr_in *s1 = (struct sockaddr_in *)&i1->ip;
     388      340196 :                 struct sockaddr_in *s2 = (struct sockaddr_in *)&i2->ip;
     389      340196 :                 uint32_t a1 = ntohl(s1->sin_addr.s_addr);
     390      340196 :                 uint32_t a2 = ntohl(s2->sin_addr.s_addr);
     391      340196 :                 r = NUMERIC_CMP(a1, a2);
     392      340196 :                 if (r == 0) {
     393             :                         /* compare netmasks as a tiebreaker */
     394           0 :                         s1 = (struct sockaddr_in *)&i1->netmask;
     395           0 :                         s2 = (struct sockaddr_in *)&i2->netmask;
     396           0 :                         a1 = ntohl(s1->sin_addr.s_addr);
     397           0 :                         a2 = ntohl(s2->sin_addr.s_addr);
     398           0 :                         r = NUMERIC_CMP(a1, a2);
     399             :                 }
     400      340196 :                 return r;
     401             :         }
     402           0 :         return 0;
     403             : }
     404             : 
     405             : /* this wrapper is used to remove duplicates from the interface list generated
     406             :    above */
     407      170098 : int get_interfaces(TALLOC_CTX *mem_ctx, struct iface_struct **pifaces)
     408             : {
     409      170098 :         struct iface_struct *ifaces = NULL;
     410        2916 :         int total, i, j;
     411             : 
     412      170098 :         total = _get_interfaces(mem_ctx, &ifaces);
     413             :         /* If we have an error, no interface or just one we can leave */
     414      170098 :         if (total <= 1) {
     415           0 :                 *pifaces = ifaces;
     416           0 :                 return total;
     417             :         }
     418             : 
     419             :         /* now we need to remove duplicates */
     420      170098 :         TYPESAFE_QSORT(ifaces, total, iface_comp);
     421             : 
     422      680392 :         for (i=1;i<total;) {
     423      510294 :                 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      510294 :                         i++;
     430             :                 }
     431             :         }
     432             : 
     433      170098 :         *pifaces = ifaces;
     434      170098 :         return total;
     435             : }

Generated by: LCOV version 1.14