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