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