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