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