]>
Commit | Line | Data |
---|---|---|
acddc0ed | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
32bcb8b0 DS |
2 | /* |
3 | * NS functions. | |
4 | * Copyright (C) 2014 6WIND S.A. | |
32bcb8b0 DS |
5 | */ |
6 | ||
7 | #include <zebra.h> | |
8 | ||
13460c44 | 9 | #ifdef HAVE_NETNS |
d62a17ae | 10 | #undef _GNU_SOURCE |
13460c44 FL |
11 | #define _GNU_SOURCE |
12 | ||
13 | #include <sched.h> | |
14 | #endif | |
15 | ||
4691b65a PG |
16 | /* for basename */ |
17 | #include <libgen.h> | |
18 | ||
32bcb8b0 DS |
19 | #include "if.h" |
20 | #include "ns.h" | |
32bcb8b0 DS |
21 | #include "log.h" |
22 | #include "memory.h" | |
13460c44 FL |
23 | #include "command.h" |
24 | #include "vty.h" | |
b95c1883 | 25 | #include "vrf.h" |
481bc15f | 26 | #include "lib_errors.h" |
13460c44 | 27 | |
bf8d3d6a DL |
28 | DEFINE_MTYPE_STATIC(LIB, NS, "NetNS Context"); |
29 | DEFINE_MTYPE_STATIC(LIB, NS_NAME, "NetNS Name"); | |
13460c44 | 30 | |
e26aedbe PG |
31 | static inline int ns_compare(const struct ns *ns, const struct ns *ns2); |
32 | static struct ns *ns_lookup_name_internal(const char *name); | |
c7fdd84f | 33 | |
d62a17ae | 34 | RB_GENERATE(ns_head, ns, entry, ns_compare) |
c7fdd84f | 35 | |
2961d060 | 36 | static struct ns_head ns_tree = RB_INITIALIZER(&ns_tree); |
13460c44 | 37 | |
ec31f30d | 38 | static struct ns *default_ns; |
ce1be369 PG |
39 | static int ns_current_ns_fd; |
40 | static int ns_default_ns_fd; | |
41 | ||
a2c999f2 PG |
42 | static int ns_debug; |
43 | ||
03aff2d8 PG |
44 | struct ns_map_nsid { |
45 | RB_ENTRY(ns_map_nsid) id_entry; | |
46 | ns_id_t ns_id_external; | |
47 | ns_id_t ns_id; | |
48 | }; | |
49 | ||
1ea16e09 | 50 | static inline int ns_map_compare(const struct ns_map_nsid *a, |
03aff2d8 PG |
51 | const struct ns_map_nsid *b) |
52 | { | |
53 | return (a->ns_id - b->ns_id); | |
54 | } | |
55 | ||
56 | RB_HEAD(ns_map_nsid_head, ns_map_nsid); | |
57 | RB_PROTOTYPE(ns_map_nsid_head, ns_map_nsid, id_entry, ns_map_compare); | |
58 | RB_GENERATE(ns_map_nsid_head, ns_map_nsid, id_entry, ns_map_compare); | |
1b3e9a21 DL |
59 | static struct ns_map_nsid_head ns_map_nsid_list = |
60 | RB_INITIALIZER(&ns_map_nsid_list); | |
03aff2d8 PG |
61 | |
62 | static ns_id_t ns_id_external_numbering; | |
63 | ||
64 | ||
13460c44 | 65 | #ifndef CLONE_NEWNET |
e26aedbe PG |
66 | #define CLONE_NEWNET 0x40000000 |
67 | /* New network namespace (lo, device, names sockets, etc) */ | |
13460c44 FL |
68 | #endif |
69 | ||
70 | #ifndef HAVE_SETNS | |
71 | static inline int setns(int fd, int nstype) | |
72 | { | |
73 | #ifdef __NR_setns | |
d62a17ae | 74 | return syscall(__NR_setns, fd, nstype); |
13460c44 | 75 | #else |
281da0a9 | 76 | errno = EINVAL; |
d62a17ae | 77 | return -1; |
13460c44 FL |
78 | #endif |
79 | } | |
e26aedbe | 80 | #endif /* !HAVE_SETNS */ |
13460c44 | 81 | |
c253dcb5 | 82 | #ifdef HAVE_NETNS |
c253dcb5 | 83 | static int have_netns_enabled = -1; |
13460c44 FL |
84 | #endif /* HAVE_NETNS */ |
85 | ||
c253dcb5 ND |
86 | static int have_netns(void) |
87 | { | |
88 | #ifdef HAVE_NETNS | |
d62a17ae | 89 | if (have_netns_enabled < 0) { |
90 | int fd = open(NS_DEFAULT_NAME, O_RDONLY); | |
91 | ||
92 | if (fd < 0) | |
93 | have_netns_enabled = 0; | |
94 | else { | |
95 | have_netns_enabled = 1; | |
96 | close(fd); | |
97 | } | |
98 | } | |
99 | return have_netns_enabled; | |
c253dcb5 | 100 | #else |
d62a17ae | 101 | return 0; |
c253dcb5 ND |
102 | #endif |
103 | } | |
104 | ||
32bcb8b0 | 105 | /* Holding NS hooks */ |
1b3e9a21 | 106 | static struct ns_master { |
3347430b PG |
107 | int (*ns_new_hook)(struct ns *ns); |
108 | int (*ns_delete_hook)(struct ns *ns); | |
109 | int (*ns_enable_hook)(struct ns *ns); | |
110 | int (*ns_disable_hook)(struct ns *ns); | |
d62a17ae | 111 | } ns_master = { |
112 | 0, | |
113 | }; | |
114 | ||
115 | static int ns_is_enabled(struct ns *ns); | |
d62a17ae | 116 | |
e26aedbe | 117 | static inline int ns_compare(const struct ns *a, const struct ns *b) |
32bcb8b0 | 118 | { |
d62a17ae | 119 | return (a->ns_id - b->ns_id); |
32bcb8b0 DS |
120 | } |
121 | ||
32bcb8b0 | 122 | /* Look up a NS by identifier. */ |
e26aedbe | 123 | static struct ns *ns_lookup_internal(ns_id_t ns_id) |
32bcb8b0 | 124 | { |
d62a17ae | 125 | struct ns ns; |
32bcb8b0 | 126 | |
e26aedbe PG |
127 | ns.ns_id = ns_id; |
128 | return RB_FIND(ns_head, &ns_tree, &ns); | |
81c9005f PG |
129 | } |
130 | ||
b95c1883 | 131 | /* Look up a NS by name */ |
e26aedbe | 132 | static struct ns *ns_lookup_name_internal(const char *name) |
b95c1883 PG |
133 | { |
134 | struct ns *ns = NULL; | |
135 | ||
c485b14b | 136 | RB_FOREACH (ns, ns_head, &ns_tree) { |
b95c1883 PG |
137 | if (ns->name != NULL) { |
138 | if (strcmp(name, ns->name) == 0) | |
139 | return ns; | |
140 | } | |
141 | } | |
142 | return NULL; | |
143 | } | |
144 | ||
e26aedbe PG |
145 | static struct ns *ns_get_created_internal(struct ns *ns, char *name, |
146 | ns_id_t ns_id) | |
32bcb8b0 | 147 | { |
e26aedbe PG |
148 | int created = 0; |
149 | /* | |
150 | * Initialize interfaces. | |
151 | */ | |
152 | if (!ns && !name && ns_id != NS_UNKNOWN) | |
153 | ns = ns_lookup_internal(ns_id); | |
154 | if (!ns && name) | |
155 | ns = ns_lookup_name_internal(name); | |
156 | if (!ns) { | |
157 | ns = XCALLOC(MTYPE_NS, sizeof(struct ns)); | |
158 | ns->ns_id = ns_id; | |
159 | if (name) | |
160 | ns->name = XSTRDUP(MTYPE_NS_NAME, name); | |
161 | ns->fd = -1; | |
162 | RB_INSERT(ns_head, &ns_tree, ns); | |
163 | created = 1; | |
164 | } | |
165 | if (ns_id != ns->ns_id) { | |
166 | RB_REMOVE(ns_head, &ns_tree, ns); | |
167 | ns->ns_id = ns_id; | |
168 | RB_INSERT(ns_head, &ns_tree, ns); | |
169 | } | |
170 | if (!created) | |
171 | return ns; | |
a2c999f2 PG |
172 | if (ns_debug) { |
173 | if (ns->ns_id != NS_UNKNOWN) | |
174 | zlog_info("NS %u is created.", ns->ns_id); | |
175 | else | |
176 | zlog_info("NS %s is created.", ns->name); | |
177 | } | |
e26aedbe | 178 | if (ns_master.ns_new_hook) |
996c9314 | 179 | (*ns_master.ns_new_hook)(ns); |
e26aedbe | 180 | return ns; |
32bcb8b0 DS |
181 | } |
182 | ||
183 | /* | |
184 | * Enable a NS - that is, let the NS be ready to use. | |
185 | * The NS_ENABLE_HOOK callback will be called to inform | |
186 | * that they can allocate resources in this NS. | |
187 | * | |
188 | * RETURN: 1 - enabled successfully; otherwise, 0. | |
189 | */ | |
e26aedbe | 190 | static int ns_enable_internal(struct ns *ns, void (*func)(ns_id_t, void *)) |
32bcb8b0 | 191 | { |
d62a17ae | 192 | if (!ns_is_enabled(ns)) { |
193 | if (have_netns()) { | |
194 | ns->fd = open(ns->name, O_RDONLY); | |
195 | } else { | |
e26aedbe PG |
196 | ns->fd = -2; |
197 | /* Remember ns_enable_hook has been called */ | |
d62a17ae | 198 | errno = -ENOTSUP; |
199 | } | |
200 | ||
201 | if (!ns_is_enabled(ns)) { | |
450971aa | 202 | flog_err_sys(EC_LIB_SYSTEM_CALL, |
09c866e3 QY |
203 | "Can not enable NS %u: %s!", ns->ns_id, |
204 | safe_strerror(errno)); | |
d62a17ae | 205 | return 0; |
206 | } | |
207 | ||
697d3ec7 PG |
208 | /* Non default NS. leave */ |
209 | if (ns->ns_id == NS_UNKNOWN) { | |
450971aa | 210 | flog_err(EC_LIB_NS, |
1c50c1c0 QY |
211 | "Can not enable NS %s %u: Invalid NSID", |
212 | ns->name, ns->ns_id); | |
697d3ec7 PG |
213 | return 0; |
214 | } | |
e26aedbe PG |
215 | if (func) |
216 | func(ns->ns_id, (void *)ns->vrf_ctxt); | |
a2c999f2 PG |
217 | if (ns_debug) { |
218 | if (have_netns()) | |
219 | zlog_info("NS %u is associated with NETNS %s.", | |
220 | ns->ns_id, ns->name); | |
221 | zlog_info("NS %u is enabled.", ns->ns_id); | |
222 | } | |
697d3ec7 PG |
223 | /* zebra first receives NS enable event, |
224 | * then VRF enable event | |
225 | */ | |
d62a17ae | 226 | if (ns_master.ns_enable_hook) |
3347430b | 227 | (*ns_master.ns_enable_hook)(ns); |
d62a17ae | 228 | } |
229 | ||
230 | return 1; | |
32bcb8b0 DS |
231 | } |
232 | ||
e26aedbe PG |
233 | /* |
234 | * Check whether the NS is enabled - that is, whether the NS | |
235 | * is ready to allocate resources. Currently there's only one | |
236 | * type of resource: socket. | |
237 | */ | |
238 | static int ns_is_enabled(struct ns *ns) | |
239 | { | |
240 | if (have_netns()) | |
241 | return ns && ns->fd >= 0; | |
242 | else | |
243 | return ns && ns->fd == -2 && ns->ns_id == NS_DEFAULT; | |
244 | } | |
245 | ||
32bcb8b0 DS |
246 | /* |
247 | * Disable a NS - that is, let the NS be unusable. | |
248 | * The NS_DELETE_HOOK callback will be called to inform | |
249 | * that they must release the resources in the NS. | |
250 | */ | |
e26aedbe | 251 | static void ns_disable_internal(struct ns *ns) |
32bcb8b0 | 252 | { |
d62a17ae | 253 | if (ns_is_enabled(ns)) { |
a2c999f2 | 254 | if (ns_debug) |
996c9314 | 255 | zlog_info("NS %u is to be disabled.", ns->ns_id); |
32bcb8b0 | 256 | |
d62a17ae | 257 | if (ns_master.ns_disable_hook) |
3347430b | 258 | (*ns_master.ns_disable_hook)(ns); |
13460c44 | 259 | |
d62a17ae | 260 | if (have_netns()) |
261 | close(ns->fd); | |
c253dcb5 | 262 | |
d62a17ae | 263 | ns->fd = -1; |
264 | } | |
32bcb8b0 DS |
265 | } |
266 | ||
214d8a60 | 267 | /* VRF list existence check by name. */ |
03aff2d8 PG |
268 | static struct ns_map_nsid *ns_map_nsid_lookup_by_nsid(ns_id_t ns_id) |
269 | { | |
270 | struct ns_map_nsid ns_map; | |
271 | ||
272 | ns_map.ns_id = ns_id; | |
1ea16e09 | 273 | return RB_FIND(ns_map_nsid_head, &ns_map_nsid_list, &ns_map); |
03aff2d8 PG |
274 | } |
275 | ||
f0295a54 | 276 | ns_id_t ns_map_nsid_with_external(ns_id_t ns_id, bool map) |
03aff2d8 PG |
277 | { |
278 | struct ns_map_nsid *ns_map; | |
279 | vrf_id_t ns_id_external; | |
280 | ||
281 | ns_map = ns_map_nsid_lookup_by_nsid(ns_id); | |
f0295a54 | 282 | if (ns_map && !map) { |
03aff2d8 PG |
283 | ns_id_external = ns_map->ns_id_external; |
284 | RB_REMOVE(ns_map_nsid_head, &ns_map_nsid_list, ns_map); | |
285 | return ns_id_external; | |
286 | } | |
287 | if (ns_map) | |
288 | return ns_map->ns_id_external; | |
289 | ns_map = XCALLOC(MTYPE_NS, sizeof(struct ns_map_nsid)); | |
290 | /* increase vrf_id | |
291 | * default vrf is the first one : 0 | |
292 | */ | |
293 | ns_map->ns_id_external = ns_id_external_numbering++; | |
294 | ns_map->ns_id = ns_id; | |
295 | RB_INSERT(ns_map_nsid_head, &ns_map_nsid_list, ns_map); | |
296 | return ns_map->ns_id_external; | |
297 | } | |
298 | ||
e26aedbe PG |
299 | struct ns *ns_get_created(struct ns *ns, char *name, ns_id_t ns_id) |
300 | { | |
301 | return ns_get_created_internal(ns, name, ns_id); | |
302 | } | |
303 | ||
304 | int ns_have_netns(void) | |
305 | { | |
306 | return have_netns(); | |
307 | } | |
308 | ||
309 | /* Delete a NS. This is called in ns_terminate(). */ | |
310 | void ns_delete(struct ns *ns) | |
311 | { | |
a2c999f2 PG |
312 | if (ns_debug) |
313 | zlog_info("NS %u is to be deleted.", ns->ns_id); | |
e26aedbe PG |
314 | |
315 | ns_disable(ns); | |
316 | ||
317 | if (ns_master.ns_delete_hook) | |
318 | (*ns_master.ns_delete_hook)(ns); | |
319 | ||
320 | /* | |
321 | * I'm not entirely sure if the vrf->iflist | |
322 | * needs to be moved into here or not. | |
323 | */ | |
324 | // if_terminate (&ns->iflist); | |
325 | ||
326 | RB_REMOVE(ns_head, &ns_tree, ns); | |
0a22ddfb | 327 | XFREE(MTYPE_NS_NAME, ns->name); |
e26aedbe PG |
328 | |
329 | XFREE(MTYPE_NS, ns); | |
330 | } | |
331 | ||
332 | /* Look up the data pointer of the specified VRF. */ | |
996c9314 | 333 | void *ns_info_lookup(ns_id_t ns_id) |
e26aedbe PG |
334 | { |
335 | struct ns *ns = ns_lookup_internal(ns_id); | |
336 | ||
337 | return ns ? ns->info : NULL; | |
338 | } | |
339 | ||
340 | /* Look up a NS by name */ | |
341 | struct ns *ns_lookup_name(const char *name) | |
342 | { | |
343 | return ns_lookup_name_internal(name); | |
344 | } | |
345 | ||
346 | int ns_enable(struct ns *ns, void (*func)(ns_id_t, void *)) | |
347 | { | |
348 | return ns_enable_internal(ns, func); | |
349 | } | |
350 | ||
351 | void ns_disable(struct ns *ns) | |
352 | { | |
5ee080f0 | 353 | ns_disable_internal(ns); |
e26aedbe PG |
354 | } |
355 | ||
356 | struct ns *ns_lookup(ns_id_t ns_id) | |
357 | { | |
358 | return ns_lookup_internal(ns_id); | |
359 | } | |
360 | ||
2961d060 PG |
361 | void ns_walk_func(int (*func)(struct ns *, |
362 | void *param_in, | |
363 | void **param_out), | |
364 | void *param_in, | |
365 | void **param_out) | |
e26aedbe PG |
366 | { |
367 | struct ns *ns = NULL; | |
2961d060 | 368 | int ret; |
e26aedbe | 369 | |
2961d060 PG |
370 | RB_FOREACH (ns, ns_head, &ns_tree) { |
371 | ret = func(ns, param_in, param_out); | |
372 | if (ret == NS_WALK_STOP) | |
373 | return; | |
374 | } | |
e26aedbe PG |
375 | } |
376 | ||
377 | const char *ns_get_name(struct ns *ns) | |
378 | { | |
379 | if (!ns) | |
380 | return NULL; | |
381 | return ns->name; | |
382 | } | |
32bcb8b0 DS |
383 | |
384 | /* Add a NS hook. Please add hooks before calling ns_init(). */ | |
3347430b | 385 | void ns_add_hook(int type, int (*func)(struct ns *)) |
32bcb8b0 | 386 | { |
d62a17ae | 387 | switch (type) { |
388 | case NS_NEW_HOOK: | |
389 | ns_master.ns_new_hook = func; | |
390 | break; | |
391 | case NS_DELETE_HOOK: | |
392 | ns_master.ns_delete_hook = func; | |
393 | break; | |
394 | case NS_ENABLE_HOOK: | |
395 | ns_master.ns_enable_hook = func; | |
396 | break; | |
397 | case NS_DISABLE_HOOK: | |
398 | ns_master.ns_disable_hook = func; | |
399 | break; | |
400 | default: | |
401 | break; | |
402 | } | |
32bcb8b0 DS |
403 | } |
404 | ||
13460c44 FL |
405 | /* |
406 | * NS realization with NETNS | |
407 | */ | |
408 | ||
697d3ec7 | 409 | char *ns_netns_pathname(struct vty *vty, const char *name) |
13460c44 | 410 | { |
d62a17ae | 411 | static char pathname[PATH_MAX]; |
412 | char *result; | |
4691b65a | 413 | char *check_base; |
d62a17ae | 414 | |
415 | if (name[0] == '/') /* absolute pathname */ | |
416 | result = realpath(name, pathname); | |
e26aedbe PG |
417 | else { |
418 | /* relevant pathname */ | |
d62a17ae | 419 | char tmp_name[PATH_MAX]; |
e26aedbe | 420 | |
772270f3 | 421 | snprintf(tmp_name, sizeof(tmp_name), "%s/%s", NS_RUN_DIR, name); |
d62a17ae | 422 | result = realpath(tmp_name, pathname); |
423 | } | |
424 | ||
425 | if (!result) { | |
697d3ec7 | 426 | if (vty) |
0faeba26 PG |
427 | vty_out(vty, "Invalid pathname for %s: %s\n", |
428 | pathname, | |
697d3ec7 | 429 | safe_strerror(errno)); |
4691b65a | 430 | else |
450971aa | 431 | flog_warn(EC_LIB_LINUX_NS, |
8b895cd3 | 432 | "Invalid pathname for %s: %s", pathname, |
0faeba26 | 433 | safe_strerror(errno)); |
4691b65a PG |
434 | return NULL; |
435 | } | |
436 | check_base = basename(pathname); | |
437 | if (check_base != NULL && strlen(check_base) + 1 > NS_NAMSIZ) { | |
438 | if (vty) | |
e26aedbe | 439 | vty_out(vty, "NS name (%s) invalid: too long (>%d)\n", |
996c9314 | 440 | check_base, NS_NAMSIZ - 1); |
4691b65a | 441 | else |
450971aa | 442 | flog_warn(EC_LIB_LINUX_NS, |
8b895cd3 | 443 | "NS name (%s) invalid: too long (>%d)", |
996c9314 | 444 | check_base, NS_NAMSIZ - 1); |
d62a17ae | 445 | return NULL; |
446 | } | |
447 | return pathname; | |
13460c44 FL |
448 | } |
449 | ||
ce1be369 PG |
450 | void ns_init(void) |
451 | { | |
e26aedbe PG |
452 | static int ns_initialised; |
453 | ||
a2c999f2 | 454 | ns_debug = 0; |
e26aedbe PG |
455 | /* silently return as initialisation done */ |
456 | if (ns_initialised == 1) | |
457 | return; | |
458 | errno = 0; | |
2ed3953c | 459 | if (have_netns()) |
ce1be369 | 460 | ns_default_ns_fd = open(NS_DEFAULT_NAME, O_RDONLY); |
2ed3953c | 461 | else { |
e26aedbe | 462 | ns_default_ns_fd = -1; |
c214a6e9 PG |
463 | default_ns = NULL; |
464 | } | |
e26aedbe PG |
465 | ns_current_ns_fd = -1; |
466 | ns_initialised = 1; | |
ce1be369 PG |
467 | } |
468 | ||
32bcb8b0 | 469 | /* Initialize NS module. */ |
03aff2d8 | 470 | void ns_init_management(ns_id_t default_ns_id, ns_id_t internal_ns) |
32bcb8b0 | 471 | { |
ec31f30d | 472 | int fd; |
d62a17ae | 473 | |
ce1be369 | 474 | ns_init(); |
e26aedbe | 475 | default_ns = ns_get_created_internal(NULL, NULL, default_ns_id); |
d62a17ae | 476 | if (!default_ns) { |
450971aa | 477 | flog_err(EC_LIB_NS, "%s: failed to create the default NS!", |
1c50c1c0 | 478 | __func__); |
d62a17ae | 479 | exit(1); |
480 | } | |
ec31f30d PG |
481 | if (have_netns()) { |
482 | fd = open(NS_DEFAULT_NAME, O_RDONLY); | |
483 | default_ns->fd = fd; | |
484 | } | |
03aff2d8 PG |
485 | default_ns->internal_ns_id = internal_ns; |
486 | ||
d62a17ae | 487 | /* Set the default NS name. */ |
488 | default_ns->name = XSTRDUP(MTYPE_NS_NAME, NS_DEFAULT_NAME); | |
a2c999f2 | 489 | if (ns_debug) |
996c9314 LB |
490 | zlog_info("%s: default NSID is %u", __func__, |
491 | default_ns->ns_id); | |
d62a17ae | 492 | |
493 | /* Enable the default NS. */ | |
e26aedbe | 494 | if (!ns_enable(default_ns, NULL)) { |
450971aa | 495 | flog_err(EC_LIB_NS, "%s: failed to enable the default NS!", |
1c50c1c0 | 496 | __func__); |
d62a17ae | 497 | exit(1); |
498 | } | |
32bcb8b0 DS |
499 | } |
500 | ||
501 | /* Terminate NS module. */ | |
d62a17ae | 502 | void ns_terminate(void) |
32bcb8b0 | 503 | { |
d62a17ae | 504 | struct ns *ns; |
32bcb8b0 | 505 | |
55cd0f61 DS |
506 | while (!RB_EMPTY(ns_head, &ns_tree)) { |
507 | ns = RB_ROOT(ns_head, &ns_tree); | |
508 | ||
d62a17ae | 509 | ns_delete(ns); |
55cd0f61 | 510 | } |
32bcb8b0 DS |
511 | } |
512 | ||
ce1be369 PG |
513 | int ns_switch_to_netns(const char *name) |
514 | { | |
515 | int ret; | |
516 | int fd; | |
517 | ||
518 | if (name == NULL) | |
519 | return -1; | |
e26aedbe PG |
520 | if (ns_default_ns_fd == -1) |
521 | return -1; | |
ce1be369 PG |
522 | fd = open(name, O_RDONLY); |
523 | if (fd == -1) { | |
281da0a9 | 524 | errno = EINVAL; |
ce1be369 PG |
525 | return -1; |
526 | } | |
527 | ret = setns(fd, CLONE_NEWNET); | |
528 | ns_current_ns_fd = fd; | |
529 | close(fd); | |
530 | return ret; | |
531 | } | |
532 | ||
533 | /* returns 1 if switch() was not called before | |
534 | * return status of setns() otherwise | |
535 | */ | |
536 | int ns_switchback_to_initial(void) | |
537 | { | |
e26aedbe | 538 | if (ns_current_ns_fd != -1 && ns_default_ns_fd != -1) { |
ce1be369 PG |
539 | int ret; |
540 | ||
541 | ret = setns(ns_default_ns_fd, CLONE_NEWNET); | |
542 | ns_current_ns_fd = -1; | |
543 | return ret; | |
544 | } | |
545 | /* silently ignore if setns() is not called */ | |
546 | return 1; | |
547 | } | |
548 | ||
32bcb8b0 | 549 | /* Create a socket for the NS. */ |
d62a17ae | 550 | int ns_socket(int domain, int type, int protocol, ns_id_t ns_id) |
32bcb8b0 | 551 | { |
d62a17ae | 552 | struct ns *ns = ns_lookup(ns_id); |
fe533c56 | 553 | int ret; |
d62a17ae | 554 | |
fe533c56 | 555 | if (!ns || !ns_is_enabled(ns)) { |
281da0a9 | 556 | errno = EINVAL; |
d62a17ae | 557 | return -1; |
558 | } | |
d62a17ae | 559 | if (have_netns()) { |
560 | ret = (ns_id != NS_DEFAULT) ? setns(ns->fd, CLONE_NEWNET) : 0; | |
561 | if (ret >= 0) { | |
562 | ret = socket(domain, type, protocol); | |
ce1be369 | 563 | if (ns_id != NS_DEFAULT) { |
d62a17ae | 564 | setns(ns_lookup(NS_DEFAULT)->fd, CLONE_NEWNET); |
ce1be369 PG |
565 | ns_current_ns_fd = ns_id; |
566 | } | |
d62a17ae | 567 | } |
568 | } else | |
569 | ret = socket(domain, type, protocol); | |
570 | ||
571 | return ret; | |
32bcb8b0 | 572 | } |
ec31f30d | 573 | |
b1cc23b2 PG |
574 | /* if relative link_nsid matches default netns, |
575 | * then return default absolute netns value | |
576 | * otherwise, return NS_UNKNOWN | |
577 | */ | |
578 | ns_id_t ns_id_get_absolute(ns_id_t ns_id_reference, ns_id_t link_nsid) | |
579 | { | |
580 | struct ns *ns; | |
581 | ||
582 | ns = ns_lookup(ns_id_reference); | |
583 | if (!ns) | |
584 | return NS_UNKNOWN; | |
585 | ||
586 | if (ns->relative_default_ns != link_nsid) | |
587 | return NS_UNKNOWN; | |
588 | ||
589 | ns = ns_get_default(); | |
590 | assert(ns); | |
591 | return ns->ns_id; | |
592 | } | |
593 | ||
2d4e4d39 PG |
594 | struct ns *ns_get_default(void) |
595 | { | |
596 | return default_ns; | |
597 | } |