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