]> git.proxmox.com Git - mirror_frr.git/blame - lib/ns.c
zebra: On shutdown don't count removals
[mirror_frr.git] / lib / ns.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
32bcb8b0
DS
31#include "if.h"
32#include "ns.h"
32bcb8b0
DS
33#include "log.h"
34#include "memory.h"
35
13460c44
FL
36#include "command.h"
37#include "vty.h"
38
d62a17ae 39DEFINE_MTYPE_STATIC(LIB, NS, "Logical-Router")
40DEFINE_MTYPE_STATIC(LIB, NS_NAME, "Logical-Router Name")
13460c44 41
d62a17ae 42static __inline int ns_compare(const struct ns *, const struct ns *);
43static struct ns *ns_lookup(ns_id_t);
c7fdd84f 44
d62a17ae 45RB_GENERATE(ns_head, ns, entry, ns_compare)
c7fdd84f 46
d62a17ae 47struct ns_head ns_tree = RB_INITIALIZER(&ns_tree);
13460c44
FL
48
49#ifndef CLONE_NEWNET
50#define CLONE_NEWNET 0x40000000 /* New network namespace (lo, device, names sockets, etc) */
51#endif
52
53#ifndef HAVE_SETNS
54static inline int setns(int fd, int nstype)
55{
56#ifdef __NR_setns
d62a17ae 57 return syscall(__NR_setns, fd, nstype);
13460c44 58#else
d62a17ae 59 errno = ENOSYS;
60 return -1;
13460c44
FL
61#endif
62}
63#endif /* HAVE_SETNS */
64
c253dcb5
ND
65#ifdef HAVE_NETNS
66
13460c44 67#define NS_DEFAULT_NAME "/proc/self/ns/net"
c253dcb5 68static int have_netns_enabled = -1;
13460c44 69
d62a17ae 70#else /* !HAVE_NETNS */
13460c44 71
32bcb8b0
DS
72#define NS_DEFAULT_NAME "Default-logical-router"
73
13460c44
FL
74#endif /* HAVE_NETNS */
75
c253dcb5
ND
76static int have_netns(void)
77{
78#ifdef HAVE_NETNS
d62a17ae 79 if (have_netns_enabled < 0) {
80 int fd = open(NS_DEFAULT_NAME, O_RDONLY);
81
82 if (fd < 0)
83 have_netns_enabled = 0;
84 else {
85 have_netns_enabled = 1;
86 close(fd);
87 }
88 }
89 return have_netns_enabled;
c253dcb5 90#else
d62a17ae 91 return 0;
c253dcb5
ND
92#endif
93}
94
32bcb8b0 95/* Holding NS hooks */
d62a17ae 96struct ns_master {
97 int (*ns_new_hook)(ns_id_t, void **);
98 int (*ns_delete_hook)(ns_id_t, void **);
99 int (*ns_enable_hook)(ns_id_t, void **);
100 int (*ns_disable_hook)(ns_id_t, void **);
101} ns_master = {
102 0,
103};
104
105static int ns_is_enabled(struct ns *ns);
106static int ns_enable(struct ns *ns);
107static void ns_disable(struct ns *ns);
108
109static __inline int ns_compare(const struct ns *a, const struct ns *b)
32bcb8b0 110{
d62a17ae 111 return (a->ns_id - b->ns_id);
32bcb8b0
DS
112}
113
114/* Get a NS. If not found, create one. */
d62a17ae 115static struct ns *ns_get(ns_id_t ns_id)
32bcb8b0 116{
d62a17ae 117 struct ns *ns;
32bcb8b0 118
d62a17ae 119 ns = ns_lookup(ns_id);
120 if (ns)
121 return (ns);
32bcb8b0 122
d62a17ae 123 ns = XCALLOC(MTYPE_NS, sizeof(struct ns));
124 ns->ns_id = ns_id;
125 ns->fd = -1;
126 RB_INSERT(ns_head, &ns_tree, ns);
32bcb8b0 127
d62a17ae 128 /*
129 * Initialize interfaces.
130 *
131 * I'm not sure if this belongs here or in
132 * the vrf code.
133 */
134 // if_init (&ns->iflist);
32bcb8b0 135
d62a17ae 136 zlog_info("NS %u is created.", ns_id);
32bcb8b0 137
d62a17ae 138 if (ns_master.ns_new_hook)
139 (*ns_master.ns_new_hook)(ns_id, &ns->info);
32bcb8b0 140
d62a17ae 141 return ns;
32bcb8b0
DS
142}
143
144/* Delete a NS. This is called in ns_terminate(). */
d62a17ae 145static void ns_delete(struct ns *ns)
32bcb8b0 146{
d62a17ae 147 zlog_info("NS %u is to be deleted.", ns->ns_id);
32bcb8b0 148
d62a17ae 149 ns_disable(ns);
32bcb8b0 150
d62a17ae 151 if (ns_master.ns_delete_hook)
152 (*ns_master.ns_delete_hook)(ns->ns_id, &ns->info);
32bcb8b0 153
d62a17ae 154 /*
155 * I'm not entirely sure if the vrf->iflist
156 * needs to be moved into here or not.
157 */
158 // if_terminate (&ns->iflist);
32bcb8b0 159
d62a17ae 160 RB_REMOVE(ns_head, &ns_tree, ns);
161 if (ns->name)
162 XFREE(MTYPE_NS_NAME, ns->name);
32bcb8b0 163
d62a17ae 164 XFREE(MTYPE_NS, ns);
32bcb8b0
DS
165}
166
167/* Look up a NS by identifier. */
d62a17ae 168static struct ns *ns_lookup(ns_id_t ns_id)
32bcb8b0 169{
d62a17ae 170 struct ns ns;
171 ns.ns_id = ns_id;
172 return (RB_FIND(ns_head, &ns_tree, &ns));
32bcb8b0
DS
173}
174
175/*
176 * Check whether the NS is enabled - that is, whether the NS
177 * is ready to allocate resources. Currently there's only one
178 * type of resource: socket.
179 */
d62a17ae 180static int ns_is_enabled(struct ns *ns)
32bcb8b0 181{
d62a17ae 182 if (have_netns())
183 return ns && ns->fd >= 0;
184 else
185 return ns && ns->fd == -2 && ns->ns_id == NS_DEFAULT;
32bcb8b0
DS
186}
187
188/*
189 * Enable a NS - that is, let the NS be ready to use.
190 * The NS_ENABLE_HOOK callback will be called to inform
191 * that they can allocate resources in this NS.
192 *
193 * RETURN: 1 - enabled successfully; otherwise, 0.
194 */
d62a17ae 195static int ns_enable(struct ns *ns)
32bcb8b0 196{
13460c44 197
d62a17ae 198 if (!ns_is_enabled(ns)) {
199 if (have_netns()) {
200 ns->fd = open(ns->name, O_RDONLY);
201 } else {
202 ns->fd = -2; /* Remember that ns_enable_hook has been
203 called */
204 errno = -ENOTSUP;
205 }
206
207 if (!ns_is_enabled(ns)) {
208 zlog_err("Can not enable NS %u: %s!", ns->ns_id,
209 safe_strerror(errno));
210 return 0;
211 }
212
213 if (have_netns())
214 zlog_info("NS %u is associated with NETNS %s.",
215 ns->ns_id, ns->name);
216
217 zlog_info("NS %u is enabled.", ns->ns_id);
218 if (ns_master.ns_enable_hook)
219 (*ns_master.ns_enable_hook)(ns->ns_id, &ns->info);
220 }
221
222 return 1;
32bcb8b0
DS
223}
224
225/*
226 * Disable a NS - that is, let the NS be unusable.
227 * The NS_DELETE_HOOK callback will be called to inform
228 * that they must release the resources in the NS.
229 */
d62a17ae 230static void ns_disable(struct ns *ns)
32bcb8b0 231{
d62a17ae 232 if (ns_is_enabled(ns)) {
233 zlog_info("NS %u is to be disabled.", ns->ns_id);
32bcb8b0 234
d62a17ae 235 if (ns_master.ns_disable_hook)
236 (*ns_master.ns_disable_hook)(ns->ns_id, &ns->info);
13460c44 237
d62a17ae 238 if (have_netns())
239 close(ns->fd);
c253dcb5 240
d62a17ae 241 ns->fd = -1;
242 }
32bcb8b0
DS
243}
244
245
246/* Add a NS hook. Please add hooks before calling ns_init(). */
d62a17ae 247void ns_add_hook(int type, int (*func)(ns_id_t, void **))
32bcb8b0 248{
d62a17ae 249 switch (type) {
250 case NS_NEW_HOOK:
251 ns_master.ns_new_hook = func;
252 break;
253 case NS_DELETE_HOOK:
254 ns_master.ns_delete_hook = func;
255 break;
256 case NS_ENABLE_HOOK:
257 ns_master.ns_enable_hook = func;
258 break;
259 case NS_DISABLE_HOOK:
260 ns_master.ns_disable_hook = func;
261 break;
262 default:
263 break;
264 }
32bcb8b0
DS
265}
266
13460c44
FL
267/*
268 * NS realization with NETNS
269 */
270
d62a17ae 271static char *ns_netns_pathname(struct vty *vty, const char *name)
13460c44 272{
d62a17ae 273 static char pathname[PATH_MAX];
274 char *result;
275
276 if (name[0] == '/') /* absolute pathname */
277 result = realpath(name, pathname);
278 else /* relevant pathname */
279 {
280 char tmp_name[PATH_MAX];
281 snprintf(tmp_name, PATH_MAX, "%s/%s", NS_RUN_DIR, name);
282 result = realpath(tmp_name, pathname);
283 }
284
285 if (!result) {
286 vty_out(vty, "Invalid pathname: %s\n", safe_strerror(errno));
287 return NULL;
288 }
289 return pathname;
13460c44
FL
290}
291
505e5056 292DEFUN_NOSH (ns_netns,
13460c44 293 ns_netns_cmd,
6147e2c6 294 "logical-router (1-65535) ns NAME",
13460c44
FL
295 "Enable a logical-router\n"
296 "Specify the logical-router indentifier\n"
297 "The Name Space\n"
298 "The file name in " NS_RUN_DIR ", or a full pathname\n")
299{
d62a17ae 300 int idx_number = 1;
301 int idx_name = 3;
302 ns_id_t ns_id = NS_DEFAULT;
303 struct ns *ns = NULL;
304 char *pathname = ns_netns_pathname(vty, argv[idx_name]->arg);
305
306 if (!pathname)
307 return CMD_WARNING_CONFIG_FAILED;
308
309 ns_id = strtoul(argv[idx_number]->arg, NULL, 10);
310 ns = ns_get(ns_id);
311
312 if (ns->name && strcmp(ns->name, pathname) != 0) {
313 vty_out(vty, "NS %u is already configured with NETNS %s\n",
314 ns->ns_id, ns->name);
851fcbae 315 return CMD_WARNING;
d62a17ae 316 }
317
318 if (!ns->name)
319 ns->name = XSTRDUP(MTYPE_NS_NAME, pathname);
320
321 if (!ns_enable(ns)) {
322 vty_out(vty, "Can not associate NS %u with NETNS %s\n",
323 ns->ns_id, ns->name);
324 return CMD_WARNING_CONFIG_FAILED;
325 }
326
327 return CMD_SUCCESS;
13460c44
FL
328}
329
330DEFUN (no_ns_netns,
331 no_ns_netns_cmd,
6147e2c6 332 "no logical-router (1-65535) ns NAME",
13460c44
FL
333 NO_STR
334 "Enable a Logical-Router\n"
335 "Specify the Logical-Router identifier\n"
336 "The Name Space\n"
337 "The file name in " NS_RUN_DIR ", or a full pathname\n")
338{
d62a17ae 339 int idx_number = 2;
340 int idx_name = 4;
341 ns_id_t ns_id = NS_DEFAULT;
342 struct ns *ns = NULL;
343 char *pathname = ns_netns_pathname(vty, argv[idx_name]->arg);
344
345 if (!pathname)
346 return CMD_WARNING_CONFIG_FAILED;
347
348 ns_id = strtoul(argv[idx_number]->arg, NULL, 10);
349 ns = ns_lookup(ns_id);
350
351 if (!ns) {
352 vty_out(vty, "NS %u is not found\n", ns_id);
353 return CMD_SUCCESS;
354 }
355
356 if (ns->name && strcmp(ns->name, pathname) != 0) {
357 vty_out(vty, "Incorrect NETNS file name\n");
358 return CMD_WARNING_CONFIG_FAILED;
359 }
360
361 ns_disable(ns);
362
363 if (ns->name) {
364 XFREE(MTYPE_NS_NAME, ns->name);
365 ns->name = NULL;
366 }
367
368 return CMD_SUCCESS;
13460c44
FL
369}
370
371/* NS node. */
d62a17ae 372static struct cmd_node ns_node = {NS_NODE, "", /* NS node has no interface. */
373 1};
13460c44
FL
374
375/* NS configuration write function. */
d62a17ae 376static int ns_config_write(struct vty *vty)
13460c44 377{
d62a17ae 378 struct ns *ns;
379 int write = 0;
13460c44 380
a2addae8 381 RB_FOREACH (ns, ns_head, &ns_tree) {
d62a17ae 382 if (ns->ns_id == NS_DEFAULT || ns->name == NULL)
383 continue;
c7fdd84f 384
d62a17ae 385 vty_out(vty, "logical-router %u netns %s\n", ns->ns_id,
386 ns->name);
387 write = 1;
388 }
13460c44 389
d62a17ae 390 return write;
13460c44
FL
391}
392
32bcb8b0 393/* Initialize NS module. */
d62a17ae 394void ns_init(void)
32bcb8b0 395{
d62a17ae 396 struct ns *default_ns;
397
398 /* The default NS always exists. */
399 default_ns = ns_get(NS_DEFAULT);
400 if (!default_ns) {
401 zlog_err("ns_init: failed to create the default NS!");
402 exit(1);
403 }
404
405 /* Set the default NS name. */
406 default_ns->name = XSTRDUP(MTYPE_NS_NAME, NS_DEFAULT_NAME);
407
408 /* Enable the default NS. */
409 if (!ns_enable(default_ns)) {
410 zlog_err("ns_init: failed to enable the default NS!");
411 exit(1);
412 }
413
414 if (have_netns()) {
415 /* Install NS commands. */
416 install_node(&ns_node, ns_config_write);
417 install_element(CONFIG_NODE, &ns_netns_cmd);
418 install_element(CONFIG_NODE, &no_ns_netns_cmd);
419 }
32bcb8b0
DS
420}
421
422/* Terminate NS module. */
d62a17ae 423void ns_terminate(void)
32bcb8b0 424{
d62a17ae 425 struct ns *ns;
32bcb8b0 426
d62a17ae 427 while ((ns = RB_ROOT(ns_head, &ns_tree)) != NULL)
428 ns_delete(ns);
32bcb8b0
DS
429}
430
431/* Create a socket for the NS. */
d62a17ae 432int ns_socket(int domain, int type, int protocol, ns_id_t ns_id)
32bcb8b0 433{
d62a17ae 434 struct ns *ns = ns_lookup(ns_id);
435 int ret = -1;
436
437 if (!ns_is_enabled(ns)) {
438 errno = ENOSYS;
439 return -1;
440 }
441
442 if (have_netns()) {
443 ret = (ns_id != NS_DEFAULT) ? setns(ns->fd, CLONE_NEWNET) : 0;
444 if (ret >= 0) {
445 ret = socket(domain, type, protocol);
446 if (ns_id != NS_DEFAULT)
447 setns(ns_lookup(NS_DEFAULT)->fd, CLONE_NEWNET);
448 }
449 } else
450 ret = socket(domain, type, protocol);
451
452 return ret;
32bcb8b0 453}