Coverage Report

Created: 2026-07-25 07:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/samba/source4/lib/socket/interface.c
Line
Count
Source
1
/* 
2
   Unix SMB/CIFS implementation.
3
4
   multiple interface handling
5
6
   Copyright (C) Andrew Tridgell 1992-2005
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
#include "includes.h"
23
#include "system/network.h"
24
#include "param/param.h"
25
#include "lib/socket/netif.h"
26
#include "../lib/util/util_net.h"
27
#include "../lib/util/dlinklist.h"
28
#include "lib/util/smb_strtox.h"
29
30
/* used for network interfaces */
31
struct interface {
32
  struct interface *next, *prev;
33
  char *name;
34
  int flags;
35
  struct sockaddr_storage ip;
36
  struct sockaddr_storage netmask;
37
  struct sockaddr_storage bcast;
38
  const char *ip_s;
39
  const char *bcast_s;
40
  const char *nmask_s;
41
};
42
43
#define ALLONES  ((uint32_t)0xFFFFFFFF)
44
/*
45
  address construction based on a patch from fred@datalync.com
46
*/
47
#define MKBCADDR(_IP, _NM) ((_IP & _NM) | (_NM ^ ALLONES))
48
#define MKNETADDR(_IP, _NM) (_IP & _NM)
49
50
/****************************************************************************
51
Try and find an interface that matches an ip. If we cannot, return NULL
52
  **************************************************************************/
53
static struct interface *iface_list_find(struct interface *interfaces,
54
           const struct sockaddr *ip,
55
           bool check_mask)
56
0
{
57
0
  struct interface *i;
58
59
0
  if (is_address_any(ip)) {
60
0
    return interfaces;
61
0
  }
62
63
0
  for (i=interfaces;i;i=i->next) {
64
0
    if (check_mask) {
65
0
      if (same_net(ip, (struct sockaddr *)&i->ip, (struct sockaddr *)&i->netmask)) {
66
0
        return i;
67
0
      }
68
0
    } else if (sockaddr_equal((struct sockaddr *)&i->ip, ip)) {
69
0
      return i;
70
0
    }
71
0
  }
72
73
0
  return NULL;
74
0
}
75
76
/****************************************************************************
77
add an interface to the linked list of interfaces
78
****************************************************************************/
79
static void add_interface(TALLOC_CTX *mem_ctx, const struct iface_struct *ifs, struct interface **interfaces)
80
0
{
81
0
  char addr[INET6_ADDRSTRLEN];
82
0
  struct interface *iface;
83
84
0
  if (iface_list_find(*interfaces, (const struct sockaddr *)&ifs->ip, false)) {
85
0
    DEBUG(3,("add_interface: not adding duplicate interface %s\n",
86
0
      print_sockaddr(addr, sizeof(addr), &ifs->ip) ));
87
0
    return;
88
0
  }
89
90
0
  if (ifs->ip.ss_family == AF_INET &&
91
0
    !(ifs->flags & (IFF_BROADCAST|IFF_LOOPBACK))) {
92
0
    DEBUG(3,("not adding non-broadcast interface %s\n",
93
0
          ifs->name ));
94
0
    return;
95
0
  }
96
97
0
  if (*interfaces != NULL) {
98
0
    mem_ctx = *interfaces;
99
0
  }
100
101
0
  iface = talloc_zero(mem_ctx, struct interface);
102
0
  if (iface == NULL) {
103
0
    return;
104
0
  }
105
106
0
  iface->name = talloc_strdup(iface, ifs->name);
107
0
  if (!iface->name) {
108
0
    SAFE_FREE(iface);
109
0
    return;
110
0
  }
111
0
  iface->flags = ifs->flags;
112
0
  iface->ip = ifs->ip;
113
0
  iface->netmask = ifs->netmask;
114
0
  iface->bcast = ifs->bcast;
115
116
  /* keep string versions too, to avoid people tripping over the implied
117
     static in inet_ntoa() */
118
0
  print_sockaddr(addr, sizeof(addr), &iface->ip);
119
0
  DEBUG(4,("added interface %s ip=%s ",
120
0
     iface->name, addr));
121
0
  iface->ip_s = talloc_strdup(iface, addr);
122
123
0
  print_sockaddr(addr, sizeof(addr),
124
0
           &iface->bcast);
125
0
  DEBUG(4,("bcast=%s ", addr));
126
0
  iface->bcast_s = talloc_strdup(iface, addr);
127
128
0
  print_sockaddr(addr, sizeof(addr),
129
0
           &iface->netmask);
130
0
  DEBUG(4,("netmask=%s\n", addr));
131
0
  iface->nmask_s = talloc_strdup(iface, addr);
132
133
  /*
134
     this needs to be a ADD_END, as some tests (such as the
135
     spoolss notify test) depend on the interfaces ordering
136
  */
137
0
  DLIST_ADD_END(*interfaces, iface);
138
0
}
139
140
/**
141
interpret a single element from a interfaces= config line 
142
143
This handles the following different forms:
144
145
1) wildcard interface name
146
2) DNS name
147
3) IP/masklen
148
4) ip/mask
149
5) bcast/mask
150
**/
151
static void interpret_interface(TALLOC_CTX *mem_ctx, 
152
        const char *token, 
153
        struct iface_struct *probed_ifaces, 
154
        int total_probed,
155
        struct interface **local_interfaces)
156
0
{
157
0
  struct sockaddr_storage ss;
158
0
  struct sockaddr_storage ss_mask;
159
0
  struct sockaddr_storage ss_net;
160
0
  struct sockaddr_storage ss_bcast;
161
0
  struct iface_struct ifs;
162
0
  char *p;
163
0
  int i;
164
0
  bool added=false;
165
0
  bool goodaddr = false;
166
167
  /* first check if it is an interface name */
168
0
  for (i=0;i<total_probed;i++) {
169
0
    if (gen_fnmatch(token, probed_ifaces[i].name) == 0) {
170
0
      add_interface(mem_ctx, &probed_ifaces[i],
171
0
              local_interfaces);
172
0
      added = true;
173
0
    }
174
0
  }
175
0
  if (added) {
176
0
    return;
177
0
  }
178
179
0
  p = discard_const_p(char, strchr_m(token, ';'));
180
0
  if (p != NULL) {
181
    /*
182
     * skip smbd-specific extra data:
183
     * link speed, capabilities, and interface index
184
     */
185
0
    *p = 0;
186
0
  }
187
188
  /* maybe it is a DNS name */
189
0
  p = discard_const_p(char, strchr_m(token, '/'));
190
0
  if (p == NULL) {
191
0
    if (!interpret_string_addr(&ss, token, 0)) {
192
0
      DEBUG(2, ("interpret_interface: Can't find address "
193
0
          "for %s\n", token));
194
0
      return;
195
0
    }
196
197
0
    for (i=0;i<total_probed;i++) {
198
0
      if (sockaddr_equal((struct sockaddr *)&ss, (struct sockaddr *)&probed_ifaces[i].ip)) {
199
0
        add_interface(mem_ctx, &probed_ifaces[i],
200
0
                local_interfaces);
201
0
        return;
202
0
      }
203
0
    }
204
0
    DEBUG(2,("interpret_interface: "
205
0
      "can't determine interface for %s\n",
206
0
      token));
207
0
    return;
208
0
  }
209
210
  /* parse it into an IP address/netmasklength pair */
211
0
  *p = 0;
212
0
  goodaddr = interpret_string_addr(&ss, token, 0);
213
0
  *p++ = '/';
214
215
0
  if (!goodaddr) {
216
0
    DEBUG(2,("interpret_interface: "
217
0
      "can't determine interface for %s\n",
218
0
      token));
219
0
    return;
220
0
  }
221
222
0
  if (strlen(p) > 2) {
223
0
    goodaddr = interpret_string_addr(&ss_mask, p, 0);
224
0
    if (!goodaddr) {
225
0
      DEBUG(2,("interpret_interface: "
226
0
        "can't determine netmask from %s\n",
227
0
        p));
228
0
      return;
229
0
    }
230
0
  } else {
231
0
    int error = 0;
232
233
0
    unsigned long val = smb_strtoul(p,
234
0
            NULL,
235
0
            0,
236
0
            &error,
237
0
            SMB_STR_FULL_STR_CONV);
238
0
    if (error != 0) {
239
0
      DEBUG(2,("interpret_interface: "
240
0
        "can't determine netmask value from %s\n",
241
0
        p));
242
0
      return;
243
0
    }
244
0
    if (!make_netmask(&ss_mask, &ss, val)) {
245
0
      DEBUG(2,("interpret_interface: "
246
0
        "can't apply netmask value %lu from %s\n",
247
0
        val,
248
0
        p));
249
0
      return;
250
0
    }
251
0
  }
252
253
0
  make_bcast(&ss_bcast, &ss, &ss_mask);
254
0
  make_net(&ss_net, &ss, &ss_mask);
255
256
  /* Maybe the first component was a broadcast address. */
257
0
  if (sockaddr_equal((struct sockaddr *)&ss_bcast, (struct sockaddr *)&ss) ||
258
0
    sockaddr_equal((struct sockaddr *)&ss_net, (struct sockaddr *)&ss)) {
259
0
    for (i=0;i<total_probed;i++) {
260
0
      if (same_net((struct sockaddr *)&ss,
261
0
             (struct sockaddr *)&probed_ifaces[i].ip,
262
0
             (struct sockaddr *)&ss_mask)) {
263
        /* Temporarily replace netmask on
264
         * the detected interface - user knows
265
         * best.... */
266
0
        struct sockaddr_storage saved_mask =
267
0
          probed_ifaces[i].netmask;
268
0
        probed_ifaces[i].netmask = ss_mask;
269
0
        DEBUG(2,("interpret_interface: "
270
0
          "using netmask value %s from "
271
0
          "config file on interface %s\n",
272
0
          p,
273
0
          probed_ifaces[i].name));
274
0
        add_interface(mem_ctx, &probed_ifaces[i],
275
0
                local_interfaces);
276
0
        probed_ifaces[i].netmask = saved_mask;
277
0
        return;
278
0
      }
279
0
    }
280
0
    DEBUG(2,("interpret_interface: Can't determine ip for "
281
0
      "broadcast address %s\n",
282
0
      token));
283
0
    return;
284
0
  }
285
286
  /* Just fake up the interface definition. User knows best. */
287
288
0
  DEBUG(2,("interpret_interface: Adding interface %s\n",
289
0
    token));
290
291
0
  ZERO_STRUCT(ifs);
292
0
  (void)strlcpy(ifs.name, token, sizeof(ifs.name));
293
0
  ifs.flags = IFF_BROADCAST;
294
0
  ifs.ip = ss;
295
0
  ifs.netmask = ss_mask;
296
0
  ifs.bcast = ss_bcast;
297
0
  add_interface(mem_ctx, &ifs, local_interfaces);
298
0
}
299
300
301
/**
302
load the list of network interfaces
303
**/
304
void load_interface_list(TALLOC_CTX *mem_ctx, struct loadparm_context *lp_ctx, struct interface **local_interfaces)
305
0
{
306
0
  const char **ptr = lpcfg_interfaces(lp_ctx);
307
0
  int i;
308
0
  struct iface_struct *ifaces = NULL;
309
0
  int total_probed;
310
311
0
  *local_interfaces = NULL;
312
313
  /* probe the kernel for interfaces */
314
0
  total_probed = get_interfaces(mem_ctx, &ifaces);
315
316
  /* if we don't have a interfaces line then use all interfaces
317
     except loopback */
318
0
  if (!ptr || !*ptr || !**ptr) {
319
0
    if (total_probed <= 0) {
320
0
      DEBUG(0,("ERROR: Could not determine network interfaces, you must use a interfaces config line\n"));
321
0
    }
322
0
    for (i=0;i<total_probed;i++) {
323
0
      if (!is_loopback_addr((struct sockaddr *)&ifaces[i].ip)) {
324
0
        add_interface(mem_ctx, &ifaces[i], local_interfaces);
325
0
      }
326
0
    }
327
0
  }
328
329
0
  while (ptr && *ptr) {
330
0
    interpret_interface(mem_ctx, *ptr, ifaces, total_probed, local_interfaces);
331
0
    ptr++;
332
0
  }
333
334
0
  if (*local_interfaces == NULL) {
335
0
    const char **first_ptr = lpcfg_interfaces(lp_ctx);
336
0
    if (first_ptr != NULL && *first_ptr != NULL) {
337
0
      DBG_ERR("None of the interfaces listed in smb.conf "
338
0
        "'interfaces = ...' seem to exist\n");
339
0
    }
340
0
    DBG_ERR("WARNING: no network interfaces found\n");
341
0
  }
342
0
  talloc_free(ifaces);
343
0
}
344
345
/**
346
  how many interfaces do we have
347
  **/
348
int iface_list_count(struct interface *ifaces)
349
0
{
350
0
  int ret = 0;
351
0
  struct interface *i;
352
353
0
  for (i=ifaces;i;i=i->next)
354
0
    ret++;
355
0
  return ret;
356
0
}
357
358
/**
359
  return IP of the Nth interface
360
  **/
361
const char *iface_list_n_ip(struct interface *ifaces, int n)
362
0
{
363
0
  struct interface *i;
364
365
0
  for (i=ifaces;i && n;i=i->next)
366
0
    n--;
367
368
0
  if (i) {
369
0
    return i->ip_s;
370
0
  }
371
0
  return NULL;
372
0
}
373
374
375
/**
376
  return the first IPv4 interface address we have registered
377
  **/
378
const char *iface_list_first_v4(struct interface *ifaces)
379
0
{
380
0
  struct interface *i;
381
382
0
  for (i=ifaces; i; i=i->next) {
383
0
    if (i->ip.ss_family == AF_INET) {
384
0
      return i->ip_s;
385
0
    }
386
0
  }
387
0
  return NULL;
388
0
}
389
390
/**
391
  return the first IPv6 interface address we have registered
392
  **/
393
static const char *iface_list_first_v6(struct interface *ifaces)
394
0
{
395
0
  struct interface *i;
396
397
0
#ifdef HAVE_IPV6
398
0
  for (i=ifaces; i; i=i->next) {
399
0
    if (i->ip.ss_family == AF_INET6) {
400
0
      return i->ip_s;
401
0
    }
402
0
  }
403
0
#endif
404
0
  return NULL;
405
0
}
406
407
/**
408
   check if an interface is IPv4
409
  **/
410
bool iface_list_n_is_v4(struct interface *ifaces, int n)
411
0
{
412
0
  struct interface *i;
413
414
0
  for (i=ifaces;i && n;i=i->next)
415
0
    n--;
416
417
0
  if (i) {
418
0
    return i->ip.ss_family == AF_INET;
419
0
  }
420
0
  return false;
421
0
}
422
423
/**
424
  return bcast of the Nth interface
425
  **/
426
const char *iface_list_n_bcast(struct interface *ifaces, int n)
427
0
{
428
0
  struct interface *i;
429
  
430
0
  for (i=ifaces;i && n;i=i->next)
431
0
    n--;
432
433
0
  if (i) {
434
0
    return i->bcast_s;
435
0
  }
436
0
  return NULL;
437
0
}
438
439
/**
440
  return netmask of the Nth interface
441
  **/
442
const char *iface_list_n_netmask(struct interface *ifaces, int n)
443
0
{
444
0
  struct interface *i;
445
  
446
0
  for (i=ifaces;i && n;i=i->next)
447
0
    n--;
448
449
0
  if (i) {
450
0
    return i->nmask_s;
451
0
  }
452
0
  return NULL;
453
0
}
454
455
/**
456
  return the local IP address that best matches a destination IP, or
457
  our first interface if none match
458
*/
459
const char *iface_list_best_ip(struct interface *ifaces, const char *dest)
460
0
{
461
0
  struct interface *iface;
462
0
  struct sockaddr_storage ss;
463
464
0
  if (!interpret_string_addr(&ss, dest, AI_NUMERICHOST)) {
465
0
    return iface_list_n_ip(ifaces, 0);
466
0
  }
467
0
  iface = iface_list_find(ifaces, (const struct sockaddr *)&ss, true);
468
0
  if (iface) {
469
0
    return iface->ip_s;
470
0
  }
471
0
#ifdef HAVE_IPV6
472
0
  if (ss.ss_family == AF_INET6) {
473
0
    return iface_list_first_v6(ifaces);
474
0
  }
475
0
#endif
476
0
  return iface_list_first_v4(ifaces);
477
0
}
478
479
/**
480
  return true if an IP is one one of our local networks
481
*/
482
bool iface_list_is_local(struct interface *ifaces, const char *dest)
483
0
{
484
0
  struct sockaddr_storage ss;
485
486
0
  if (!interpret_string_addr(&ss, dest, AI_NUMERICHOST)) {
487
0
    return false;
488
0
  }
489
0
  if (iface_list_find(ifaces, (const struct sockaddr *)&ss, true)) {
490
0
    return true;
491
0
  }
492
0
  return false;
493
0
}
494
495
/**
496
  return true if a IP matches a IP/netmask pair
497
*/
498
bool iface_list_same_net(const char *ip1, const char *ip2, const char *netmask)
499
0
{
500
0
  struct sockaddr_storage ip1_ss, ip2_ss, nm_ss;
501
502
0
  if (!interpret_string_addr(&ip1_ss, ip1, AI_NUMERICHOST)) {
503
0
    return false;
504
0
  }
505
0
  if (!interpret_string_addr(&ip2_ss, ip2, AI_NUMERICHOST)) {
506
0
    return false;
507
0
  }
508
0
  if (!interpret_string_addr(&nm_ss, netmask, AI_NUMERICHOST)) {
509
0
    return false;
510
0
  }
511
512
0
  return same_net((struct sockaddr *)&ip1_ss,
513
0
      (struct sockaddr *)&ip2_ss,
514
0
      (struct sockaddr *)&nm_ss);
515
0
}
516
517
/**
518
   return the list of wildcard interfaces
519
   this will include the IPv4 0.0.0.0, and may include IPv6 ::
520
*/
521
char **iface_list_wildcard(TALLOC_CTX *mem_ctx)
522
0
{
523
0
  char **ret;
524
0
#ifdef HAVE_IPV6
525
0
  ret = str_list_make(mem_ctx, "::,0.0.0.0", NULL);
526
#else
527
  ret = str_list_make(mem_ctx, "0.0.0.0", NULL);
528
#endif
529
0
  return ret;
530
0
}