Coverage Report

Created: 2025-12-05 06:31

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/frr/lib/netns_linux.c
Line
Count
Source
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/*
3
 * NS functions.
4
 * Copyright (C) 2014 6WIND S.A.
5
 */
6
7
#include <zebra.h>
8
9
#ifdef HAVE_NETNS
10
#undef _GNU_SOURCE
11
#define _GNU_SOURCE
12
13
#include <sched.h>
14
#endif
15
16
/* for basename */
17
#include <libgen.h>
18
19
#include "if.h"
20
#include "ns.h"
21
#include "log.h"
22
#include "memory.h"
23
#include "command.h"
24
#include "vty.h"
25
#include "vrf.h"
26
#include "lib_errors.h"
27
28
8
DEFINE_MTYPE_STATIC(LIB, NS, "NetNS Context");
29
8
DEFINE_MTYPE_STATIC(LIB, NS_NAME, "NetNS Name");
30
8
31
8
static inline int ns_compare(const struct ns *ns, const struct ns *ns2);
32
8
static struct ns *ns_lookup_name_internal(const char *name);
33
8
34
8
RB_GENERATE(ns_head, ns, entry, ns_compare)
35
8
36
8
static struct ns_head ns_tree = RB_INITIALIZER(&ns_tree);
37
8
38
8
static struct ns *default_ns;
39
8
static int ns_current_ns_fd;
40
8
static int ns_default_ns_fd;
41
8
42
8
static int ns_debug;
43
8
44
8
struct ns_map_nsid {
45
8
  RB_ENTRY(ns_map_nsid) id_entry;
46
8
  ns_id_t ns_id_external;
47
8
  ns_id_t ns_id;
48
8
};
49
8
50
8
static inline int ns_map_compare(const struct ns_map_nsid *a,
51
8
           const struct ns_map_nsid *b)
52
8
{
53
0
  return (a->ns_id - b->ns_id);
54
0
}
55
56
RB_HEAD(ns_map_nsid_head, ns_map_nsid);
57
RB_PROTOTYPE(ns_map_nsid_head, ns_map_nsid, id_entry, ns_map_compare);
58
RB_GENERATE(ns_map_nsid_head, ns_map_nsid, id_entry, ns_map_compare);
59
static struct ns_map_nsid_head ns_map_nsid_list =
60
    RB_INITIALIZER(&ns_map_nsid_list);
61
62
static ns_id_t ns_id_external_numbering;
63
64
65
#ifndef CLONE_NEWNET
66
#define CLONE_NEWNET 0x40000000
67
/* New network namespace (lo, device, names sockets, etc) */
68
#endif
69
70
#ifndef HAVE_SETNS
71
static inline int setns(int fd, int nstype)
72
{
73
#ifdef __NR_setns
74
  return syscall(__NR_setns, fd, nstype);
75
#else
76
  errno = EINVAL;
77
  return -1;
78
#endif
79
}
80
#endif /* !HAVE_SETNS */
81
82
#ifdef HAVE_NETNS
83
static int have_netns_enabled = -1;
84
#endif /* HAVE_NETNS */
85
86
static int have_netns(void)
87
7
{
88
7
#ifdef HAVE_NETNS
89
7
  if (have_netns_enabled < 0) {
90
3
    int fd = open(NS_DEFAULT_NAME, O_RDONLY);
91
92
3
    if (fd < 0)
93
0
      have_netns_enabled = 0;
94
3
    else {
95
3
      have_netns_enabled = 1;
96
3
      close(fd);
97
3
    }
98
3
  }
99
7
  return have_netns_enabled;
100
#else
101
  return 0;
102
#endif
103
7
}
104
105
/* Holding NS hooks  */
106
static struct ns_master {
107
  int (*ns_new_hook)(struct ns *ns);
108
  int (*ns_delete_hook)(struct ns *ns);
109
  int (*ns_enable_hook)(struct ns *ns);
110
  int (*ns_disable_hook)(struct ns *ns);
111
} ns_master = {
112
  0,
113
};
114
115
static int ns_is_enabled(struct ns *ns);
116
117
static inline int ns_compare(const struct ns *a, const struct ns *b)
118
2
{
119
2
  return (a->ns_id - b->ns_id);
120
2
}
121
122
/* Look up a NS by identifier. */
123
static struct ns *ns_lookup_internal(ns_id_t ns_id)
124
3
{
125
3
  struct ns ns;
126
127
3
  ns.ns_id = ns_id;
128
3
  return RB_FIND(ns_head, &ns_tree, &ns);
129
3
}
130
131
/* Look up a NS by name */
132
static struct ns *ns_lookup_name_internal(const char *name)
133
0
{
134
0
  struct ns *ns = NULL;
135
136
0
  RB_FOREACH (ns, ns_head, &ns_tree) {
137
0
    if (ns->name != NULL) {
138
0
      if (strcmp(name, ns->name) == 0)
139
0
        return ns;
140
0
    }
141
0
  }
142
0
  return NULL;
143
0
}
144
145
static struct ns *ns_get_created_internal(struct ns *ns, char *name,
146
            ns_id_t ns_id)
147
1
{
148
1
  int created = 0;
149
  /*
150
   * Initialize interfaces.
151
   */
152
1
  if (!ns && !name && ns_id != NS_UNKNOWN)
153
1
    ns = ns_lookup_internal(ns_id);
154
1
  if (!ns && name)
155
0
    ns = ns_lookup_name_internal(name);
156
1
  if (!ns) {
157
1
    ns = XCALLOC(MTYPE_NS, sizeof(struct ns));
158
1
    ns->ns_id = ns_id;
159
1
    if (name)
160
0
      ns->name = XSTRDUP(MTYPE_NS_NAME, name);
161
1
    ns->fd = -1;
162
1
    RB_INSERT(ns_head, &ns_tree, ns);
163
1
    created = 1;
164
1
  }
165
1
  if (ns_id != ns->ns_id) {
166
0
    RB_REMOVE(ns_head, &ns_tree, ns);
167
0
    ns->ns_id = ns_id;
168
0
    RB_INSERT(ns_head, &ns_tree, ns);
169
0
  }
170
1
  if (!created)
171
0
    return ns;
172
1
  if (ns_debug) {
173
0
    if (ns->ns_id != NS_UNKNOWN)
174
0
      zlog_info("NS %u is created.", ns->ns_id);
175
0
    else
176
0
      zlog_info("NS %s is created.", ns->name);
177
0
  }
178
1
  if (ns_master.ns_new_hook)
179
0
    (*ns_master.ns_new_hook)(ns);
180
1
  return ns;
181
1
}
182
183
/*
184
 * Enable a NS - that is, let the NS be ready to use.
185
 * The NS_ENABLE_HOOK callback will be called to inform
186
 * that they can allocate resources in this NS.
187
 *
188
 * RETURN: 1 - enabled successfully; otherwise, 0.
189
 */
190
static int ns_enable_internal(struct ns *ns, void (*func)(ns_id_t, void *))
191
1
{
192
1
  if (!ns_is_enabled(ns)) {
193
0
    if (have_netns()) {
194
0
      ns->fd = open(ns->name, O_RDONLY);
195
0
    } else {
196
0
      ns->fd = -2;
197
      /* Remember ns_enable_hook has been called */
198
0
      errno = -ENOTSUP;
199
0
    }
200
201
0
    if (!ns_is_enabled(ns)) {
202
0
      flog_err_sys(EC_LIB_SYSTEM_CALL,
203
0
             "Can not enable NS %u: %s!", ns->ns_id,
204
0
             safe_strerror(errno));
205
0
      return 0;
206
0
    }
207
208
    /* Non default NS. leave */
209
0
    if (ns->ns_id == NS_UNKNOWN) {
210
0
      flog_err(EC_LIB_NS,
211
0
         "Can not enable NS %s %u: Invalid NSID",
212
0
         ns->name, ns->ns_id);
213
0
      return 0;
214
0
    }
215
0
    if (func)
216
0
      func(ns->ns_id, (void *)ns->vrf_ctxt);
217
0
    if (ns_debug) {
218
0
      if (have_netns())
219
0
        zlog_info("NS %u is associated with NETNS %s.",
220
0
            ns->ns_id, ns->name);
221
0
      zlog_info("NS %u is enabled.", ns->ns_id);
222
0
    }
223
    /* zebra first receives NS enable event,
224
     * then VRF enable event
225
     */
226
0
    if (ns_master.ns_enable_hook)
227
0
      (*ns_master.ns_enable_hook)(ns);
228
0
  }
229
230
1
  return 1;
231
1
}
232
233
/*
234
 * Check whether the NS is enabled - that is, whether the NS
235
 * is ready to allocate resources. Currently there's only one
236
 * type of resource: socket.
237
 */
238
static int ns_is_enabled(struct ns *ns)
239
2
{
240
2
  if (have_netns())
241
2
    return ns && ns->fd >= 0;
242
0
  else
243
0
    return ns && ns->fd == -2 && ns->ns_id == NS_DEFAULT;
244
2
}
245
246
/*
247
 * Disable a NS - that is, let the NS be unusable.
248
 * The NS_DELETE_HOOK callback will be called to inform
249
 * that they must release the resources in the NS.
250
 */
251
static void ns_disable_internal(struct ns *ns)
252
0
{
253
0
  if (ns_is_enabled(ns)) {
254
0
    if (ns_debug)
255
0
      zlog_info("NS %u is to be disabled.", ns->ns_id);
256
257
0
    if (ns_master.ns_disable_hook)
258
0
      (*ns_master.ns_disable_hook)(ns);
259
260
0
    if (have_netns())
261
0
      close(ns->fd);
262
263
0
    ns->fd = -1;
264
0
  }
265
0
}
266
267
/* VRF list existence check by name. */
268
static struct ns_map_nsid *ns_map_nsid_lookup_by_nsid(ns_id_t ns_id)
269
1
{
270
1
  struct ns_map_nsid ns_map;
271
272
1
  ns_map.ns_id = ns_id;
273
1
  return RB_FIND(ns_map_nsid_head, &ns_map_nsid_list, &ns_map);
274
1
}
275
276
ns_id_t ns_map_nsid_with_external(ns_id_t ns_id, bool map)
277
1
{
278
1
  struct ns_map_nsid *ns_map;
279
1
  vrf_id_t ns_id_external;
280
281
1
  ns_map = ns_map_nsid_lookup_by_nsid(ns_id);
282
1
  if (ns_map && !map) {
283
0
    ns_id_external = ns_map->ns_id_external;
284
0
    RB_REMOVE(ns_map_nsid_head, &ns_map_nsid_list, ns_map);
285
0
    return ns_id_external;
286
0
  }
287
1
  if (ns_map)
288
0
    return ns_map->ns_id_external;
289
1
  ns_map = XCALLOC(MTYPE_NS, sizeof(struct ns_map_nsid));
290
  /* increase vrf_id
291
   * default vrf is the first one : 0
292
   */
293
1
  ns_map->ns_id_external = ns_id_external_numbering++;
294
1
  ns_map->ns_id = ns_id;
295
1
  RB_INSERT(ns_map_nsid_head, &ns_map_nsid_list, ns_map);
296
1
  return ns_map->ns_id_external;
297
1
}
298
299
struct ns *ns_get_created(struct ns *ns, char *name, ns_id_t ns_id)
300
0
{
301
0
  return ns_get_created_internal(ns, name, ns_id);
302
0
}
303
304
int ns_have_netns(void)
305
0
{
306
0
  return have_netns();
307
0
}
308
309
/* Delete a NS. This is called in ns_terminate(). */
310
void ns_delete(struct ns *ns)
311
0
{
312
0
  if (ns_debug)
313
0
    zlog_info("NS %u is to be deleted.", ns->ns_id);
314
315
0
  ns_disable(ns);
316
317
0
  if (ns_master.ns_delete_hook)
318
0
    (*ns_master.ns_delete_hook)(ns);
319
320
  /*
321
   * I'm not entirely sure if the vrf->iflist
322
   * needs to be moved into here or not.
323
   */
324
  // if_terminate (&ns->iflist);
325
326
0
  RB_REMOVE(ns_head, &ns_tree, ns);
327
0
  XFREE(MTYPE_NS_NAME, ns->name);
328
329
0
  XFREE(MTYPE_NS, ns);
330
0
}
331
332
/* Look up the data pointer of the specified VRF. */
333
void *ns_info_lookup(ns_id_t ns_id)
334
0
{
335
0
  struct ns *ns = ns_lookup_internal(ns_id);
336
337
0
  return ns ? ns->info : NULL;
338
0
}
339
340
/* Look up a NS by name */
341
struct ns *ns_lookup_name(const char *name)
342
0
{
343
0
  return ns_lookup_name_internal(name);
344
0
}
345
346
int ns_enable(struct ns *ns, void (*func)(ns_id_t, void *))
347
1
{
348
1
  return ns_enable_internal(ns, func);
349
1
}
350
351
void ns_disable(struct ns *ns)
352
0
{
353
0
  ns_disable_internal(ns);
354
0
}
355
356
struct ns *ns_lookup(ns_id_t ns_id)
357
2
{
358
2
  return ns_lookup_internal(ns_id);
359
2
}
360
361
void ns_walk_func(int (*func)(struct ns *,
362
            void *param_in,
363
            void **param_out),
364
      void *param_in,
365
      void **param_out)
366
0
{
367
0
  struct ns *ns = NULL;
368
0
  int ret;
369
370
0
  RB_FOREACH (ns, ns_head, &ns_tree) {
371
0
    ret = func(ns, param_in, param_out);
372
0
    if (ret == NS_WALK_STOP)
373
0
      return;
374
0
  }
375
0
}
376
377
const char *ns_get_name(struct ns *ns)
378
0
{
379
0
  if (!ns)
380
0
    return NULL;
381
0
  return ns->name;
382
0
}
383
384
/* Add a NS hook. Please add hooks before calling ns_init(). */
385
void ns_add_hook(int type, int (*func)(struct ns *))
386
0
{
387
0
  switch (type) {
388
0
  case NS_NEW_HOOK:
389
0
    ns_master.ns_new_hook = func;
390
0
    break;
391
0
  case NS_DELETE_HOOK:
392
0
    ns_master.ns_delete_hook = func;
393
0
    break;
394
0
  case NS_ENABLE_HOOK:
395
0
    ns_master.ns_enable_hook = func;
396
0
    break;
397
0
  case NS_DISABLE_HOOK:
398
0
    ns_master.ns_disable_hook = func;
399
0
    break;
400
0
  default:
401
0
    break;
402
0
  }
403
0
}
404
405
/*
406
 * NS realization with NETNS
407
 */
408
409
char *ns_netns_pathname(struct vty *vty, const char *name)
410
0
{
411
0
  static char pathname[PATH_MAX];
412
0
  char *result;
413
0
  char *check_base;
414
415
0
  if (name[0] == '/') /* absolute pathname */
416
0
    result = realpath(name, pathname);
417
0
  else {
418
    /* relevant pathname */
419
0
    char tmp_name[PATH_MAX];
420
421
0
    snprintf(tmp_name, sizeof(tmp_name), "%s/%s", NS_RUN_DIR, name);
422
0
    result = realpath(tmp_name, pathname);
423
0
  }
424
425
0
  if (!result) {
426
0
    if (vty)
427
0
      vty_out(vty, "Invalid pathname for %s: %s\n",
428
0
        pathname,
429
0
        safe_strerror(errno));
430
0
    else
431
0
      flog_warn(EC_LIB_LINUX_NS,
432
0
          "Invalid pathname for %s: %s", pathname,
433
0
          safe_strerror(errno));
434
0
    return NULL;
435
0
  }
436
0
  check_base = basename(pathname);
437
0
  if (check_base != NULL && strlen(check_base) + 1 > NS_NAMSIZ) {
438
0
    if (vty)
439
0
      vty_out(vty, "NS name (%s) invalid: too long (>%d)\n",
440
0
        check_base, NS_NAMSIZ - 1);
441
0
    else
442
0
      flog_warn(EC_LIB_LINUX_NS,
443
0
          "NS name (%s) invalid: too long (>%d)",
444
0
          check_base, NS_NAMSIZ - 1);
445
0
    return NULL;
446
0
  }
447
0
  return pathname;
448
0
}
449
450
void ns_init(void)
451
4
{
452
4
  static int ns_initialised;
453
454
4
  ns_debug = 0;
455
  /* silently return as initialisation done */
456
4
  if (ns_initialised == 1)
457
1
    return;
458
4
  errno = 0;
459
3
  if (have_netns())
460
3
    ns_default_ns_fd = open(NS_DEFAULT_NAME, O_RDONLY);
461
0
  else {
462
0
    ns_default_ns_fd = -1;
463
0
    default_ns = NULL;
464
0
  }
465
3
  ns_current_ns_fd = -1;
466
3
  ns_initialised = 1;
467
3
}
468
469
/* Initialize NS module. */
470
void ns_init_management(ns_id_t default_ns_id, ns_id_t internal_ns)
471
1
{
472
1
  int fd;
473
474
1
  ns_init();
475
1
  default_ns = ns_get_created_internal(NULL, NULL, default_ns_id);
476
1
  if (!default_ns) {
477
0
    flog_err(EC_LIB_NS, "%s: failed to create the default NS!",
478
0
       __func__);
479
0
    exit(1);
480
0
  }
481
1
  if (have_netns()) {
482
1
    fd = open(NS_DEFAULT_NAME, O_RDONLY);
483
1
    default_ns->fd = fd;
484
1
  }
485
1
  default_ns->internal_ns_id = internal_ns;
486
487
  /* Set the default NS name. */
488
1
  default_ns->name = XSTRDUP(MTYPE_NS_NAME, NS_DEFAULT_NAME);
489
1
  if (ns_debug)
490
0
    zlog_info("%s: default NSID is %u", __func__,
491
1
        default_ns->ns_id);
492
493
  /* Enable the default NS. */
494
1
  if (!ns_enable(default_ns, NULL)) {
495
0
    flog_err(EC_LIB_NS, "%s: failed to enable the default NS!",
496
0
       __func__);
497
0
    exit(1);
498
0
  }
499
1
}
500
501
/* Terminate NS module. */
502
void ns_terminate(void)
503
0
{
504
0
  struct ns *ns;
505
506
0
  while (!RB_EMPTY(ns_head, &ns_tree)) {
507
0
    ns = RB_ROOT(ns_head, &ns_tree);
508
509
0
    ns_delete(ns);
510
0
  }
511
0
}
512
513
int ns_switch_to_netns(const char *name)
514
0
{
515
0
  int ret;
516
0
  int fd;
517
518
0
  if (name == NULL)
519
0
    return -1;
520
0
  if (ns_default_ns_fd == -1)
521
0
    return -1;
522
0
  fd = open(name, O_RDONLY);
523
0
  if (fd == -1) {
524
0
    errno = EINVAL;
525
0
    return -1;
526
0
  }
527
0
  ret = setns(fd, CLONE_NEWNET);
528
0
  ns_current_ns_fd = fd;
529
0
  close(fd);
530
0
  return ret;
531
0
}
532
533
/* returns 1 if switch() was not called before
534
 * return status of setns() otherwise
535
 */
536
int ns_switchback_to_initial(void)
537
0
{
538
0
  if (ns_current_ns_fd != -1 && ns_default_ns_fd != -1) {
539
0
    int ret;
540
541
0
    ret = setns(ns_default_ns_fd, CLONE_NEWNET);
542
0
    ns_current_ns_fd = -1;
543
0
    return ret;
544
0
  }
545
  /* silently ignore if setns() is not called */
546
0
  return 1;
547
0
}
548
549
/* Create a socket for the NS. */
550
int ns_socket(int domain, int type, int protocol, ns_id_t ns_id)
551
1
{
552
1
  struct ns *ns = ns_lookup(ns_id);
553
1
  int ret;
554
555
1
  if (!ns || !ns_is_enabled(ns)) {
556
0
    errno = EINVAL;
557
0
    return -1;
558
0
  }
559
1
  if (have_netns()) {
560
1
    ret = (ns_id != NS_DEFAULT) ? setns(ns->fd, CLONE_NEWNET) : 0;
561
1
    if (ret >= 0) {
562
1
      ret = socket(domain, type, protocol);
563
1
      if (ns_id != NS_DEFAULT) {
564
0
        setns(ns_lookup(NS_DEFAULT)->fd, CLONE_NEWNET);
565
0
        ns_current_ns_fd = ns_id;
566
0
      }
567
1
    }
568
1
  } else
569
0
    ret = socket(domain, type, protocol);
570
571
1
  return ret;
572
1
}
573
574
/* if relative link_nsid matches default netns,
575
 * then return default absolute netns value
576
 * otherwise, return NS_UNKNOWN
577
 */
578
ns_id_t ns_id_get_absolute(ns_id_t ns_id_reference, ns_id_t link_nsid)
579
0
{
580
0
  struct ns *ns;
581
582
0
  ns = ns_lookup(ns_id_reference);
583
0
  if (!ns)
584
0
    return NS_UNKNOWN;
585
586
0
  if (ns->relative_default_ns != link_nsid)
587
0
    return NS_UNKNOWN;
588
589
0
  ns = ns_get_default();
590
0
  assert(ns);
591
0
  return ns->ns_id;
592
0
}
593
594
struct ns *ns_get_default(void)
595
1
{
596
1
  return default_ns;
597
1
}