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