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