Coverage Report

Created: 2026-02-14 07:07

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/samba/lib/socket/interfaces.c
Line
Count
Source
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
bool make_netmask(struct sockaddr_storage *pss_out,
39
      const struct sockaddr_storage *pss_in,
40
      unsigned long masklen)
41
0
{
42
0
  *pss_out = *pss_in;
43
  /* Now apply masklen bits of mask. */
44
0
#if defined(HAVE_IPV6)
45
0
  if (pss_in->ss_family == AF_INET6) {
46
0
    char *p = (char *)&((struct sockaddr_in6 *)pss_out)->sin6_addr;
47
0
    unsigned int i;
48
49
0
    if (masklen > 128) {
50
0
      return false;
51
0
    }
52
0
    for (i = 0; masklen >= 8; masklen -= 8, i++) {
53
0
      *p++ = 0xff;
54
0
    }
55
    /* Deal with the partial byte. */
56
0
    *p++ &= (0xff & ~(0xff>>masklen));
57
0
    i++;
58
0
    for (;i < sizeof(struct in6_addr); i++) {
59
0
      *p++ = '\0';
60
0
    }
61
0
    return true;
62
0
  }
63
0
#endif
64
0
  if (pss_in->ss_family == AF_INET) {
65
0
    if (masklen > 32) {
66
0
      return false;
67
0
    }
68
0
    ((struct sockaddr_in *)pss_out)->sin_addr.s_addr =
69
0
      htonl(((0xFFFFFFFFL >> masklen) ^ 0xFFFFFFFFL));
70
0
    return true;
71
0
  }
72
0
  return false;
73
0
}
74
75
/****************************************************************************
76
 Create a struct sockaddr_storage set to the broadcast or network address from
77
 an incoming sockaddr_storage.
78
****************************************************************************/
79
80
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
0
{
85
0
  unsigned int i = 0, len = 0;
86
0
  const char *pmask = NULL;
87
0
  char *p = NULL;
88
0
  *pss_out = *pss_in;
89
90
  /* Set all zero netmask bits to 1. */
91
0
#if defined(HAVE_IPV6)
92
0
  if (pss_in->ss_family == AF_INET6) {
93
0
    p = (char *)&((struct sockaddr_in6 *)pss_out)->sin6_addr;
94
0
    pmask = (const char *)&((const struct sockaddr_in6 *)nmask)->sin6_addr;
95
0
    len = 16;
96
0
  }
97
0
#endif
98
0
  if (pss_in->ss_family == AF_INET) {
99
0
    p = (char *)&((struct sockaddr_in *)pss_out)->sin_addr;
100
0
    pmask = (const char *)&((const struct sockaddr_in *)nmask)->sin_addr;
101
0
    len = 4;
102
0
  }
103
104
0
  for (i = 0; i < len; i++, p++, pmask++) {
105
0
    if (make_bcast_p) {
106
0
      *p = (*p & *pmask) | (*pmask ^ 0xff);
107
0
    } else {
108
      /* make_net */
109
0
      *p = (*p & *pmask);
110
0
    }
111
0
  }
112
0
}
113
114
void make_bcast(struct sockaddr_storage *pss_out,
115
      const struct sockaddr_storage *pss_in,
116
      const struct sockaddr_storage *nmask)
117
0
{
118
0
  make_bcast_or_net(pss_out, pss_in, nmask, true);
119
0
}
120
121
void make_net(struct sockaddr_storage *pss_out,
122
      const struct sockaddr_storage *pss_in,
123
      const struct sockaddr_storage *nmask)
124
0
{
125
0
  make_bcast_or_net(pss_out, pss_in, nmask, false);
126
0
}
127
128
#ifdef HAVE_ETHTOOL
129
static void query_iface_speed_from_name(const char *name, uint64_t *speed)
130
0
{
131
0
  int ret = 0;
132
0
  struct ethtool_cmd ecmd;
133
0
  struct ethtool_value edata;
134
0
  struct ifreq ifr;
135
0
  int fd;
136
137
0
  fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
138
0
  if (fd == -1) {
139
0
    DBG_ERR("Failed to open socket.\n");
140
0
    return;
141
0
  }
142
143
0
  if (strlen(name) >= IF_NAMESIZE) {
144
0
    DBG_ERR("Interface name too long.\n");
145
0
    goto done;
146
0
  }
147
148
0
  ZERO_STRUCT(ifr);
149
0
  strlcpy(ifr.ifr_name, name, IF_NAMESIZE);
150
151
0
  ifr.ifr_data = (void *)&edata;
152
0
  ZERO_STRUCT(edata);
153
0
  edata.cmd = ETHTOOL_GLINK;
154
0
  ret = ioctl(fd, SIOCETHTOOL, &ifr);
155
0
  if (ret == -1) {
156
0
    goto done;
157
0
  }
158
0
  if (edata.data == 0) {
159
    /* no link detected */
160
0
    *speed = 0;
161
0
    goto done;
162
0
  }
163
164
0
  ifr.ifr_data = (void *)&ecmd;
165
0
  ZERO_STRUCT(ecmd);
166
0
  ecmd.cmd = ETHTOOL_GSET;
167
0
  ret = ioctl(fd, SIOCETHTOOL, &ifr);
168
0
  if (ret == -1) {
169
0
    goto done;
170
0
  }
171
0
  *speed = ((uint64_t)ethtool_cmd_speed(&ecmd)) * 1000 * 1000;
172
173
0
done:
174
0
  (void)close(fd);
175
0
}
176
177
static void query_iface_rx_queues_from_name(const char *name,
178
              uint64_t *rx_queues)
179
0
{
180
0
  int ret = 0;
181
0
  struct ethtool_rxnfc rxcmd;
182
0
  struct ifreq ifr;
183
0
  int fd;
184
185
0
  fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
186
0
  if (fd == -1) {
187
0
    DBG_ERR("Failed to open socket.\n");
188
0
    return;
189
0
  }
190
191
0
  if (strlen(name) >= IF_NAMESIZE) {
192
0
    DBG_ERR("Interface name too long.\n");
193
0
    goto done;
194
0
  }
195
196
0
  ZERO_STRUCT(ifr);
197
0
  strlcpy(ifr.ifr_name, name, IF_NAMESIZE);
198
199
0
  ifr.ifr_data = (void *)&rxcmd;
200
0
  ZERO_STRUCT(rxcmd);
201
0
  rxcmd.cmd = ETHTOOL_GRXRINGS;
202
0
  ret = ioctl(fd, SIOCETHTOOL, &ifr);
203
0
  if (ret == -1) {
204
0
    goto done;
205
0
  }
206
207
0
  *rx_queues = rxcmd.data;
208
209
0
done:
210
0
  (void)close(fd);
211
0
}
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
static int _get_interfaces(TALLOC_CTX *mem_ctx, struct iface_struct **pifaces)
224
0
{
225
0
  struct iface_struct *ifaces;
226
0
  struct ifaddrs *iflist = NULL;
227
0
  struct ifaddrs *ifptr = NULL;
228
0
  int count;
229
0
  int total = 0;
230
0
  size_t copy_size;
231
232
0
  if (getifaddrs(&iflist) < 0) {
233
0
    return -1;
234
0
  }
235
236
0
  count = 0;
237
0
  for (ifptr = iflist; ifptr != NULL; ifptr = ifptr->ifa_next) {
238
0
    if (!ifptr->ifa_addr || !ifptr->ifa_netmask) {
239
0
      continue;
240
0
    }
241
0
    if (!(ifptr->ifa_flags & IFF_UP)) {
242
0
      continue;
243
0
    }
244
0
    count += 1;
245
0
  }
246
247
0
  ifaces = talloc_array(mem_ctx, struct iface_struct, count);
248
0
  if (ifaces == NULL) {
249
0
    errno = ENOMEM;
250
0
    return -1;
251
0
  }
252
253
  /* Loop through interfaces, looking for given IP address */
254
0
  for (ifptr = iflist; ifptr != NULL; ifptr = ifptr->ifa_next) {
255
0
    uint64_t if_speed = 1000 * 1000 * 1000; /* 1Gbps */
256
0
    uint64_t rx_queues = 1;
257
258
0
    if (!ifptr->ifa_addr || !ifptr->ifa_netmask) {
259
0
      continue;
260
0
    }
261
262
    /* Check the interface is up. */
263
0
    if (!(ifptr->ifa_flags & IFF_UP)) {
264
0
      continue;
265
0
    }
266
267
0
    memset(&ifaces[total], '\0', sizeof(ifaces[total]));
268
269
0
    copy_size = sizeof(struct sockaddr_in);
270
271
0
    ifaces[total].flags = ifptr->ifa_flags;
272
273
0
#if defined(HAVE_IPV6)
274
0
    if (ifptr->ifa_addr->sa_family == AF_INET6) {
275
0
      copy_size = sizeof(struct sockaddr_in6);
276
0
    }
277
0
#endif
278
279
0
    memcpy(&ifaces[total].ip, ifptr->ifa_addr, copy_size);
280
0
    memcpy(&ifaces[total].netmask, ifptr->ifa_netmask, copy_size);
281
282
    /* calculate broadcast address */
283
0
#if defined(HAVE_IPV6)
284
0
    if (ifptr->ifa_addr->sa_family == AF_INET6) {
285
0
      struct sockaddr_in6 *sin6 =
286
0
        (struct sockaddr_in6 *)ifptr->ifa_addr;
287
0
      struct in6_addr *in6 =
288
0
        (struct in6_addr *)&sin6->sin6_addr;
289
290
0
      if (IN6_IS_ADDR_LINKLOCAL(in6) || IN6_IS_ADDR_V4COMPAT(in6)) {
291
0
        continue;
292
0
      }
293
      /* IPv6 does not have broadcast it uses multicast. */
294
0
      memset(&ifaces[total].bcast, '\0', copy_size);
295
0
    } else
296
0
#endif
297
0
    if (ifaces[total].flags & (IFF_BROADCAST|IFF_LOOPBACK)) {
298
0
      make_bcast(&ifaces[total].bcast,
299
0
        &ifaces[total].ip,
300
0
        &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
0
        copy_size);
306
0
    } else {
307
0
      continue;
308
0
    }
309
310
0
    ifaces[total].if_index = if_nametoindex(ifptr->ifa_name);
311
0
    if (ifaces[total].if_index == 0) {
312
0
      DBG_ERR("Failed to retrieve interface index for '%s': "
313
0
        "%s\n", ifptr->ifa_name, strerror(errno));
314
0
    }
315
316
0
#ifdef HAVE_ETHTOOL
317
0
    query_iface_speed_from_name(ifptr->ifa_name, &if_speed);
318
0
    query_iface_rx_queues_from_name(ifptr->ifa_name, &rx_queues);
319
0
#endif
320
0
    ifaces[total].linkspeed = if_speed;
321
0
    ifaces[total].capability = FSCTL_NET_IFACE_NONE_CAPABLE;
322
0
    if (rx_queues > 1) {
323
0
      ifaces[total].capability |= FSCTL_NET_IFACE_RSS_CAPABLE;
324
0
    }
325
326
0
    if (strlcpy(ifaces[total].name, ifptr->ifa_name,
327
0
      sizeof(ifaces[total].name)) >=
328
0
        sizeof(ifaces[total].name)) {
329
      /* Truncation ! Ignore. */
330
0
      continue;
331
0
    }
332
0
    total++;
333
0
  }
334
335
0
  freeifaddrs(iflist);
336
337
0
  *pifaces = ifaces;
338
0
  return total;
339
0
}
340
341
static int iface_comp(struct iface_struct *i1, struct iface_struct *i2)
342
0
{
343
0
  int r;
344
345
0
#if defined(HAVE_IPV6)
346
  /*
347
   * If we have IPv6 - sort these interfaces lower
348
   * than any IPv4 ones.
349
   */
350
0
  if (i1->ip.ss_family == AF_INET6 &&
351
0
      i2->ip.ss_family == AF_INET) {
352
0
    return -1;
353
0
  } else if (i1->ip.ss_family == AF_INET &&
354
0
      i2->ip.ss_family == AF_INET6) {
355
0
    return 1;
356
0
  }
357
358
0
  if (i1->ip.ss_family == AF_INET6) {
359
0
    struct sockaddr_in6 *s1 = (struct sockaddr_in6 *)&i1->ip;
360
0
    struct sockaddr_in6 *s2 = (struct sockaddr_in6 *)&i2->ip;
361
362
0
    r = memcmp(&s1->sin6_addr,
363
0
        &s2->sin6_addr,
364
0
        sizeof(struct in6_addr));
365
0
    if (r) {
366
0
      return r;
367
0
    }
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
0
        sizeof(struct in6_addr));
375
0
    if (r) {
376
0
      return r;
377
0
    }
378
0
  }
379
0
#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
0
  if (((struct sockaddr *)&i1->ip)->sa_family == AF_INET) {
387
0
    struct sockaddr_in *s1 = (struct sockaddr_in *)&i1->ip;
388
0
    struct sockaddr_in *s2 = (struct sockaddr_in *)&i2->ip;
389
0
    uint32_t a1 = ntohl(s1->sin_addr.s_addr);
390
0
    uint32_t a2 = ntohl(s2->sin_addr.s_addr);
391
0
    r = NUMERIC_CMP(a1, a2);
392
0
    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
0
    }
400
0
    return r;
401
0
  }
402
0
  return 0;
403
0
}
404
405
/* this wrapper is used to remove duplicates from the interface list generated
406
   above */
407
int get_interfaces(TALLOC_CTX *mem_ctx, struct iface_struct **pifaces)
408
0
{
409
0
  struct iface_struct *ifaces = NULL;
410
0
  int total, i, j;
411
412
0
  total = _get_interfaces(mem_ctx, &ifaces);
413
  /* If we have an error, no interface or just one we can leave */
414
0
  if (total <= 1) {
415
0
    *pifaces = ifaces;
416
0
    return total;
417
0
  }
418
419
  /* now we need to remove duplicates */
420
0
  TYPESAFE_QSORT(ifaces, total, iface_comp);
421
422
0
  for (i=1;i<total;) {
423
0
    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
0
      }
427
0
      total--;
428
0
    } else {
429
0
      i++;
430
0
    }
431
0
  }
432
433
0
  *pifaces = ifaces;
434
0
  return total;
435
0
}