]>
Commit | Line | Data |
---|---|---|
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 | 39 | DEFINE_MTYPE_STATIC(LIB, NS, "Logical-Router") |
40 | DEFINE_MTYPE_STATIC(LIB, NS_NAME, "Logical-Router Name") | |
13460c44 | 41 | |
d62a17ae | 42 | static __inline int ns_compare(const struct ns *, const struct ns *); |
43 | static struct ns *ns_lookup(ns_id_t); | |
c7fdd84f | 44 | |
d62a17ae | 45 | RB_GENERATE(ns_head, ns, entry, ns_compare) |
c7fdd84f | 46 | |
d62a17ae | 47 | struct 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 | |
54 | static 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 | 68 | static 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 |
76 | static 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 | 96 | struct 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 | ||
105 | static int ns_is_enabled(struct ns *ns); | |
106 | static int ns_enable(struct ns *ns); | |
107 | static void ns_disable(struct ns *ns); | |
108 | ||
109 | static __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 | 115 | static 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 | 145 | static 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 | 168 | static 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 | 180 | static 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 | 195 | static 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 | 230 | static 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 | 247 | void 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 | 271 | static 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 | 292 | DEFUN_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); | |
315 | return CMD_WARNING_CONFIG_FAILED; | |
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 | ||
330 | DEFUN (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 | 372 | static struct cmd_node ns_node = {NS_NODE, "", /* NS node has no interface. */ |
373 | 1}; | |
13460c44 FL |
374 | |
375 | /* NS configuration write function. */ | |
d62a17ae | 376 | static int ns_config_write(struct vty *vty) |
13460c44 | 377 | { |
d62a17ae | 378 | struct ns *ns; |
379 | int write = 0; | |
13460c44 | 380 | |
d62a17ae | 381 | RB_FOREACH(ns, ns_head, &ns_tree) |
382 | { | |
383 | if (ns->ns_id == NS_DEFAULT || ns->name == NULL) | |
384 | continue; | |
c7fdd84f | 385 | |
d62a17ae | 386 | vty_out(vty, "logical-router %u netns %s\n", ns->ns_id, |
387 | ns->name); | |
388 | write = 1; | |
389 | } | |
13460c44 | 390 | |
d62a17ae | 391 | return write; |
13460c44 FL |
392 | } |
393 | ||
32bcb8b0 | 394 | /* Initialize NS module. */ |
d62a17ae | 395 | void ns_init(void) |
32bcb8b0 | 396 | { |
d62a17ae | 397 | struct ns *default_ns; |
398 | ||
399 | /* The default NS always exists. */ | |
400 | default_ns = ns_get(NS_DEFAULT); | |
401 | if (!default_ns) { | |
402 | zlog_err("ns_init: failed to create the default NS!"); | |
403 | exit(1); | |
404 | } | |
405 | ||
406 | /* Set the default NS name. */ | |
407 | default_ns->name = XSTRDUP(MTYPE_NS_NAME, NS_DEFAULT_NAME); | |
408 | ||
409 | /* Enable the default NS. */ | |
410 | if (!ns_enable(default_ns)) { | |
411 | zlog_err("ns_init: failed to enable the default NS!"); | |
412 | exit(1); | |
413 | } | |
414 | ||
415 | if (have_netns()) { | |
416 | /* Install NS commands. */ | |
417 | install_node(&ns_node, ns_config_write); | |
418 | install_element(CONFIG_NODE, &ns_netns_cmd); | |
419 | install_element(CONFIG_NODE, &no_ns_netns_cmd); | |
420 | } | |
32bcb8b0 DS |
421 | } |
422 | ||
423 | /* Terminate NS module. */ | |
d62a17ae | 424 | void ns_terminate(void) |
32bcb8b0 | 425 | { |
d62a17ae | 426 | struct ns *ns; |
32bcb8b0 | 427 | |
d62a17ae | 428 | while ((ns = RB_ROOT(ns_head, &ns_tree)) != NULL) |
429 | ns_delete(ns); | |
32bcb8b0 DS |
430 | } |
431 | ||
432 | /* Create a socket for the NS. */ | |
d62a17ae | 433 | int ns_socket(int domain, int type, int protocol, ns_id_t ns_id) |
32bcb8b0 | 434 | { |
d62a17ae | 435 | struct ns *ns = ns_lookup(ns_id); |
436 | int ret = -1; | |
437 | ||
438 | if (!ns_is_enabled(ns)) { | |
439 | errno = ENOSYS; | |
440 | return -1; | |
441 | } | |
442 | ||
443 | if (have_netns()) { | |
444 | ret = (ns_id != NS_DEFAULT) ? setns(ns->fd, CLONE_NEWNET) : 0; | |
445 | if (ret >= 0) { | |
446 | ret = socket(domain, type, protocol); | |
447 | if (ns_id != NS_DEFAULT) | |
448 | setns(ns_lookup(NS_DEFAULT)->fd, CLONE_NEWNET); | |
449 | } | |
450 | } else | |
451 | ret = socket(domain, type, protocol); | |
452 | ||
453 | return ret; | |
32bcb8b0 | 454 | } |