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