]>
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 | * | |
17 | * You should have received a copy of the GNU General Public License | |
18 | * along with GNU Zebra; see the file COPYING. If not, write to the | |
19 | * Free Software Foundation, Inc., 59 Temple Place - Suite 330, | |
20 | * Boston, MA 02111-1307, USA. | |
21 | */ | |
22 | ||
23 | #include <zebra.h> | |
24 | ||
13460c44 FL |
25 | #ifdef HAVE_NETNS |
26 | #undef _GNU_SOURCE | |
27 | #define _GNU_SOURCE | |
28 | ||
29 | #include <sched.h> | |
30 | #endif | |
31 | ||
32bcb8b0 DS |
32 | #include "if.h" |
33 | #include "ns.h" | |
34 | #include "prefix.h" | |
35 | #include "table.h" | |
36 | #include "log.h" | |
37 | #include "memory.h" | |
38 | ||
13460c44 FL |
39 | #include "command.h" |
40 | #include "vty.h" | |
41 | ||
4a1ab8e4 DL |
42 | DEFINE_MTYPE_STATIC(LIB, NS, "Logical-Router") |
43 | DEFINE_MTYPE_STATIC(LIB, NS_NAME, "Logical-Router Name") | |
44 | DEFINE_MTYPE_STATIC(LIB, NS_BITMAP, "Logical-Router bit-map") | |
13460c44 FL |
45 | |
46 | #ifndef CLONE_NEWNET | |
47 | #define CLONE_NEWNET 0x40000000 /* New network namespace (lo, device, names sockets, etc) */ | |
48 | #endif | |
49 | ||
50 | #ifndef HAVE_SETNS | |
51 | static inline int setns(int fd, int nstype) | |
52 | { | |
53 | #ifdef __NR_setns | |
54 | return syscall(__NR_setns, fd, nstype); | |
55 | #else | |
56 | errno = ENOSYS; | |
57 | return -1; | |
58 | #endif | |
59 | } | |
60 | #endif /* HAVE_SETNS */ | |
61 | ||
c253dcb5 ND |
62 | #ifdef HAVE_NETNS |
63 | ||
13460c44 | 64 | #define NS_DEFAULT_NAME "/proc/self/ns/net" |
c253dcb5 | 65 | static int have_netns_enabled = -1; |
13460c44 FL |
66 | |
67 | #else /* !HAVE_NETNS */ | |
68 | ||
32bcb8b0 DS |
69 | #define NS_DEFAULT_NAME "Default-logical-router" |
70 | ||
13460c44 FL |
71 | #endif /* HAVE_NETNS */ |
72 | ||
c253dcb5 ND |
73 | static int have_netns(void) |
74 | { | |
75 | #ifdef HAVE_NETNS | |
76 | if (have_netns_enabled < 0) | |
77 | { | |
78 | int fd = open (NS_DEFAULT_NAME, O_RDONLY); | |
79 | ||
80 | if (fd < 0) | |
81 | have_netns_enabled = 0; | |
82 | else | |
83 | { | |
84 | have_netns_enabled = 1; | |
85 | close(fd); | |
86 | } | |
87 | } | |
88 | return have_netns_enabled; | |
89 | #else | |
90 | return 0; | |
91 | #endif | |
92 | } | |
93 | ||
32bcb8b0 DS |
94 | struct ns |
95 | { | |
96 | /* Identifier, same as the vector index */ | |
97 | ns_id_t ns_id; | |
98 | /* Name */ | |
99 | char *name; | |
13460c44 FL |
100 | /* File descriptor */ |
101 | int fd; | |
32bcb8b0 DS |
102 | |
103 | /* Master list of interfaces belonging to this NS */ | |
104 | struct list *iflist; | |
105 | ||
106 | /* User data */ | |
107 | void *info; | |
108 | }; | |
109 | ||
110 | /* Holding NS hooks */ | |
111 | struct ns_master | |
112 | { | |
113 | int (*ns_new_hook) (ns_id_t, void **); | |
114 | int (*ns_delete_hook) (ns_id_t, void **); | |
115 | int (*ns_enable_hook) (ns_id_t, void **); | |
116 | int (*ns_disable_hook) (ns_id_t, void **); | |
117 | } ns_master = {0,}; | |
118 | ||
119 | /* NS table */ | |
120 | struct route_table *ns_table = NULL; | |
121 | ||
122 | static int ns_is_enabled (struct ns *ns); | |
123 | static int ns_enable (struct ns *ns); | |
124 | static void ns_disable (struct ns *ns); | |
125 | ||
126 | ||
127 | /* Build the table key */ | |
128 | static void | |
129 | ns_build_key (ns_id_t ns_id, struct prefix *p) | |
130 | { | |
131 | p->family = AF_INET; | |
132 | p->prefixlen = IPV4_MAX_BITLEN; | |
133 | p->u.prefix4.s_addr = ns_id; | |
134 | } | |
135 | ||
136 | /* Get a NS. If not found, create one. */ | |
137 | static struct ns * | |
138 | ns_get (ns_id_t ns_id) | |
139 | { | |
140 | struct prefix p; | |
141 | struct route_node *rn; | |
142 | struct ns *ns; | |
143 | ||
144 | ns_build_key (ns_id, &p); | |
145 | rn = route_node_get (ns_table, &p); | |
146 | if (rn->info) | |
147 | { | |
148 | ns = (struct ns *)rn->info; | |
149 | route_unlock_node (rn); /* get */ | |
150 | return ns; | |
151 | } | |
152 | ||
153 | ns = XCALLOC (MTYPE_NS, sizeof (struct ns)); | |
154 | ns->ns_id = ns_id; | |
13460c44 | 155 | ns->fd = -1; |
32bcb8b0 DS |
156 | rn->info = ns; |
157 | ||
158 | /* | |
159 | * Initialize interfaces. | |
160 | * | |
161 | * I'm not sure if this belongs here or in | |
162 | * the vrf code. | |
163 | */ | |
164 | // if_init (&ns->iflist); | |
165 | ||
166 | zlog_info ("NS %u is created.", ns_id); | |
167 | ||
168 | if (ns_master.ns_new_hook) | |
169 | (*ns_master.ns_new_hook) (ns_id, &ns->info); | |
170 | ||
171 | return ns; | |
172 | } | |
173 | ||
174 | /* Delete a NS. This is called in ns_terminate(). */ | |
175 | static void | |
176 | ns_delete (struct ns *ns) | |
177 | { | |
178 | zlog_info ("NS %u is to be deleted.", ns->ns_id); | |
179 | ||
13460c44 | 180 | ns_disable (ns); |
32bcb8b0 DS |
181 | |
182 | if (ns_master.ns_delete_hook) | |
183 | (*ns_master.ns_delete_hook) (ns->ns_id, &ns->info); | |
184 | ||
185 | /* | |
186 | * I'm not entirely sure if the vrf->iflist | |
187 | * needs to be moved into here or not. | |
188 | */ | |
189 | //if_terminate (&ns->iflist); | |
190 | ||
191 | if (ns->name) | |
192 | XFREE (MTYPE_NS_NAME, ns->name); | |
193 | ||
194 | XFREE (MTYPE_NS, ns); | |
195 | } | |
196 | ||
197 | /* Look up a NS by identifier. */ | |
198 | static struct ns * | |
199 | ns_lookup (ns_id_t ns_id) | |
200 | { | |
201 | struct prefix p; | |
202 | struct route_node *rn; | |
203 | struct ns *ns = NULL; | |
204 | ||
205 | ns_build_key (ns_id, &p); | |
206 | rn = route_node_lookup (ns_table, &p); | |
207 | if (rn) | |
208 | { | |
209 | ns = (struct ns *)rn->info; | |
210 | route_unlock_node (rn); /* lookup */ | |
211 | } | |
212 | return ns; | |
213 | } | |
214 | ||
215 | /* | |
216 | * Check whether the NS is enabled - that is, whether the NS | |
217 | * is ready to allocate resources. Currently there's only one | |
218 | * type of resource: socket. | |
219 | */ | |
220 | static int | |
221 | ns_is_enabled (struct ns *ns) | |
222 | { | |
c253dcb5 ND |
223 | if (have_netns()) |
224 | return ns && ns->fd >= 0; | |
225 | else | |
226 | return ns && ns->fd == -2 && ns->ns_id == NS_DEFAULT; | |
32bcb8b0 DS |
227 | } |
228 | ||
229 | /* | |
230 | * Enable a NS - that is, let the NS be ready to use. | |
231 | * The NS_ENABLE_HOOK callback will be called to inform | |
232 | * that they can allocate resources in this NS. | |
233 | * | |
234 | * RETURN: 1 - enabled successfully; otherwise, 0. | |
235 | */ | |
236 | static int | |
237 | ns_enable (struct ns *ns) | |
238 | { | |
13460c44 FL |
239 | |
240 | if (!ns_is_enabled (ns)) | |
32bcb8b0 | 241 | { |
c253dcb5 ND |
242 | if (have_netns()) { |
243 | ns->fd = open (ns->name, O_RDONLY); | |
244 | } else { | |
245 | ns->fd = -2; /* Remember that ns_enable_hook has been called */ | |
246 | errno = -ENOTSUP; | |
247 | } | |
13460c44 FL |
248 | |
249 | if (!ns_is_enabled (ns)) | |
250 | { | |
251 | zlog_err ("Can not enable NS %u: %s!", | |
252 | ns->ns_id, safe_strerror (errno)); | |
253 | return 0; | |
254 | } | |
255 | ||
c253dcb5 ND |
256 | if (have_netns()) |
257 | zlog_info ("NS %u is associated with NETNS %s.", | |
258 | ns->ns_id, ns->name); | |
32bcb8b0 | 259 | |
13460c44 | 260 | zlog_info ("NS %u is enabled.", ns->ns_id); |
32bcb8b0 DS |
261 | if (ns_master.ns_enable_hook) |
262 | (*ns_master.ns_enable_hook) (ns->ns_id, &ns->info); | |
32bcb8b0 DS |
263 | } |
264 | ||
13460c44 | 265 | return 1; |
32bcb8b0 DS |
266 | } |
267 | ||
268 | /* | |
269 | * Disable a NS - that is, let the NS be unusable. | |
270 | * The NS_DELETE_HOOK callback will be called to inform | |
271 | * that they must release the resources in the NS. | |
272 | */ | |
273 | static void | |
274 | ns_disable (struct ns *ns) | |
275 | { | |
276 | if (ns_is_enabled (ns)) | |
277 | { | |
278 | zlog_info ("NS %u is to be disabled.", ns->ns_id); | |
279 | ||
32bcb8b0 DS |
280 | if (ns_master.ns_disable_hook) |
281 | (*ns_master.ns_disable_hook) (ns->ns_id, &ns->info); | |
13460c44 | 282 | |
c253dcb5 ND |
283 | if (have_netns()) |
284 | close (ns->fd); | |
285 | ||
13460c44 | 286 | ns->fd = -1; |
32bcb8b0 DS |
287 | } |
288 | } | |
289 | ||
290 | ||
291 | /* Add a NS hook. Please add hooks before calling ns_init(). */ | |
292 | void | |
293 | ns_add_hook (int type, int (*func)(ns_id_t, void **)) | |
294 | { | |
295 | switch (type) { | |
296 | case NS_NEW_HOOK: | |
297 | ns_master.ns_new_hook = func; | |
298 | break; | |
299 | case NS_DELETE_HOOK: | |
300 | ns_master.ns_delete_hook = func; | |
301 | break; | |
302 | case NS_ENABLE_HOOK: | |
303 | ns_master.ns_enable_hook = func; | |
304 | break; | |
305 | case NS_DISABLE_HOOK: | |
306 | ns_master.ns_disable_hook = func; | |
307 | break; | |
308 | default: | |
309 | break; | |
310 | } | |
311 | } | |
312 | ||
313 | /* Return the iterator of the first NS. */ | |
314 | ns_iter_t | |
315 | ns_first (void) | |
316 | { | |
317 | struct route_node *rn; | |
318 | ||
319 | for (rn = route_top (ns_table); rn; rn = route_next (rn)) | |
320 | if (rn->info) | |
321 | { | |
322 | route_unlock_node (rn); /* top/next */ | |
323 | return (ns_iter_t)rn; | |
324 | } | |
325 | return NS_ITER_INVALID; | |
326 | } | |
327 | ||
328 | /* Return the next NS iterator to the given iterator. */ | |
329 | ns_iter_t | |
330 | ns_next (ns_iter_t iter) | |
331 | { | |
332 | struct route_node *rn = NULL; | |
333 | ||
334 | /* Lock it first because route_next() will unlock it. */ | |
335 | if (iter != NS_ITER_INVALID) | |
336 | rn = route_next (route_lock_node ((struct route_node *)iter)); | |
337 | ||
338 | for (; rn; rn = route_next (rn)) | |
339 | if (rn->info) | |
340 | { | |
341 | route_unlock_node (rn); /* next */ | |
342 | return (ns_iter_t)rn; | |
343 | } | |
344 | return NS_ITER_INVALID; | |
345 | } | |
346 | ||
347 | /* Return the NS iterator of the given NS ID. If it does not exist, | |
348 | * the iterator of the next existing NS is returned. */ | |
349 | ns_iter_t | |
350 | ns_iterator (ns_id_t ns_id) | |
351 | { | |
352 | struct prefix p; | |
353 | struct route_node *rn; | |
354 | ||
355 | ns_build_key (ns_id, &p); | |
356 | rn = route_node_get (ns_table, &p); | |
357 | if (rn->info) | |
358 | { | |
359 | /* OK, the NS exists. */ | |
360 | route_unlock_node (rn); /* get */ | |
361 | return (ns_iter_t)rn; | |
362 | } | |
363 | ||
364 | /* Find the next NS. */ | |
365 | for (rn = route_next (rn); rn; rn = route_next (rn)) | |
366 | if (rn->info) | |
367 | { | |
368 | route_unlock_node (rn); /* next */ | |
369 | return (ns_iter_t)rn; | |
370 | } | |
371 | ||
372 | return NS_ITER_INVALID; | |
373 | } | |
374 | ||
375 | /* Obtain the NS ID from the given NS iterator. */ | |
376 | ns_id_t | |
377 | ns_iter2id (ns_iter_t iter) | |
378 | { | |
379 | struct route_node *rn = (struct route_node *) iter; | |
380 | return (rn && rn->info) ? ((struct ns *)rn->info)->ns_id : NS_DEFAULT; | |
381 | } | |
382 | ||
383 | /* Obtain the data pointer from the given NS iterator. */ | |
384 | void * | |
385 | ns_iter2info (ns_iter_t iter) | |
386 | { | |
387 | struct route_node *rn = (struct route_node *) iter; | |
388 | return (rn && rn->info) ? ((struct ns *)rn->info)->info : NULL; | |
389 | } | |
390 | ||
391 | /* Obtain the interface list from the given NS iterator. */ | |
392 | struct list * | |
393 | ns_iter2iflist (ns_iter_t iter) | |
394 | { | |
395 | struct route_node *rn = (struct route_node *) iter; | |
396 | return (rn && rn->info) ? ((struct ns *)rn->info)->iflist : NULL; | |
397 | } | |
398 | ||
399 | /* Get the data pointer of the specified NS. If not found, create one. */ | |
400 | void * | |
401 | ns_info_get (ns_id_t ns_id) | |
402 | { | |
403 | struct ns *ns = ns_get (ns_id); | |
404 | return ns->info; | |
405 | } | |
406 | ||
407 | /* Look up the data pointer of the specified NS. */ | |
408 | void * | |
409 | ns_info_lookup (ns_id_t ns_id) | |
410 | { | |
411 | struct ns *ns = ns_lookup (ns_id); | |
412 | return ns ? ns->info : NULL; | |
413 | } | |
414 | ||
415 | /* Look up the interface list in a NS. */ | |
416 | struct list * | |
417 | ns_iflist (ns_id_t ns_id) | |
418 | { | |
419 | struct ns * ns = ns_lookup (ns_id); | |
420 | return ns ? ns->iflist : NULL; | |
421 | } | |
422 | ||
423 | /* Get the interface list of the specified NS. Create one if not find. */ | |
424 | struct list * | |
425 | ns_iflist_get (ns_id_t ns_id) | |
426 | { | |
427 | struct ns * ns = ns_get (ns_id); | |
428 | return ns->iflist; | |
429 | } | |
430 | ||
431 | /* | |
432 | * NS bit-map | |
433 | */ | |
434 | ||
435 | #define NS_BITMAP_NUM_OF_GROUPS 8 | |
436 | #define NS_BITMAP_NUM_OF_BITS_IN_GROUP \ | |
437 | (UINT16_MAX / NS_BITMAP_NUM_OF_GROUPS) | |
438 | #define NS_BITMAP_NUM_OF_BYTES_IN_GROUP \ | |
439 | (NS_BITMAP_NUM_OF_BITS_IN_GROUP / CHAR_BIT + 1) /* +1 for ensure */ | |
440 | ||
441 | #define NS_BITMAP_GROUP(_id) \ | |
442 | ((_id) / NS_BITMAP_NUM_OF_BITS_IN_GROUP) | |
443 | #define NS_BITMAP_BIT_OFFSET(_id) \ | |
444 | ((_id) % NS_BITMAP_NUM_OF_BITS_IN_GROUP) | |
445 | ||
446 | #define NS_BITMAP_INDEX_IN_GROUP(_bit_offset) \ | |
447 | ((_bit_offset) / CHAR_BIT) | |
448 | #define NS_BITMAP_FLAG(_bit_offset) \ | |
449 | (((u_char)1) << ((_bit_offset) % CHAR_BIT)) | |
450 | ||
451 | struct ns_bitmap | |
452 | { | |
453 | u_char *groups[NS_BITMAP_NUM_OF_GROUPS]; | |
454 | }; | |
455 | ||
456 | ns_bitmap_t | |
457 | ns_bitmap_init (void) | |
458 | { | |
459 | return (ns_bitmap_t) XCALLOC (MTYPE_NS_BITMAP, sizeof (struct ns_bitmap)); | |
460 | } | |
461 | ||
462 | void | |
463 | ns_bitmap_free (ns_bitmap_t bmap) | |
464 | { | |
465 | struct ns_bitmap *bm = (struct ns_bitmap *) bmap; | |
466 | int i; | |
467 | ||
468 | if (bmap == NS_BITMAP_NULL) | |
469 | return; | |
470 | ||
471 | for (i = 0; i < NS_BITMAP_NUM_OF_GROUPS; i++) | |
472 | if (bm->groups[i]) | |
473 | XFREE (MTYPE_NS_BITMAP, bm->groups[i]); | |
474 | ||
475 | XFREE (MTYPE_NS_BITMAP, bm); | |
476 | } | |
477 | ||
478 | void | |
479 | ns_bitmap_set (ns_bitmap_t bmap, ns_id_t ns_id) | |
480 | { | |
481 | struct ns_bitmap *bm = (struct ns_bitmap *) bmap; | |
482 | u_char group = NS_BITMAP_GROUP (ns_id); | |
483 | u_char offset = NS_BITMAP_BIT_OFFSET (ns_id); | |
484 | ||
485 | if (bmap == NS_BITMAP_NULL) | |
486 | return; | |
487 | ||
488 | if (bm->groups[group] == NULL) | |
489 | bm->groups[group] = XCALLOC (MTYPE_NS_BITMAP, | |
490 | NS_BITMAP_NUM_OF_BYTES_IN_GROUP); | |
491 | ||
492 | SET_FLAG (bm->groups[group][NS_BITMAP_INDEX_IN_GROUP (offset)], | |
493 | NS_BITMAP_FLAG (offset)); | |
494 | } | |
495 | ||
496 | void | |
497 | ns_bitmap_unset (ns_bitmap_t bmap, ns_id_t ns_id) | |
498 | { | |
499 | struct ns_bitmap *bm = (struct ns_bitmap *) bmap; | |
500 | u_char group = NS_BITMAP_GROUP (ns_id); | |
501 | u_char offset = NS_BITMAP_BIT_OFFSET (ns_id); | |
502 | ||
503 | if (bmap == NS_BITMAP_NULL || bm->groups[group] == NULL) | |
504 | return; | |
505 | ||
506 | UNSET_FLAG (bm->groups[group][NS_BITMAP_INDEX_IN_GROUP (offset)], | |
507 | NS_BITMAP_FLAG (offset)); | |
508 | } | |
509 | ||
510 | int | |
511 | ns_bitmap_check (ns_bitmap_t bmap, ns_id_t ns_id) | |
512 | { | |
513 | struct ns_bitmap *bm = (struct ns_bitmap *) bmap; | |
514 | u_char group = NS_BITMAP_GROUP (ns_id); | |
515 | u_char offset = NS_BITMAP_BIT_OFFSET (ns_id); | |
516 | ||
517 | if (bmap == NS_BITMAP_NULL || bm->groups[group] == NULL) | |
518 | return 0; | |
519 | ||
520 | return CHECK_FLAG (bm->groups[group][NS_BITMAP_INDEX_IN_GROUP (offset)], | |
521 | NS_BITMAP_FLAG (offset)) ? 1 : 0; | |
522 | } | |
523 | ||
13460c44 FL |
524 | /* |
525 | * NS realization with NETNS | |
526 | */ | |
527 | ||
528 | static char * | |
529 | ns_netns_pathname (struct vty *vty, const char *name) | |
530 | { | |
531 | static char pathname[PATH_MAX]; | |
532 | char *result; | |
533 | ||
534 | if (name[0] == '/') /* absolute pathname */ | |
535 | result = realpath (name, pathname); | |
536 | else /* relevant pathname */ | |
537 | { | |
538 | char tmp_name[PATH_MAX]; | |
539 | snprintf (tmp_name, PATH_MAX, "%s/%s", NS_RUN_DIR, name); | |
540 | result = realpath (tmp_name, pathname); | |
541 | } | |
542 | ||
543 | if (! result) | |
544 | { | |
545 | vty_out (vty, "Invalid pathname: %s%s", safe_strerror (errno), | |
546 | VTY_NEWLINE); | |
547 | return NULL; | |
548 | } | |
549 | return pathname; | |
550 | } | |
551 | ||
552 | DEFUN (ns_netns, | |
553 | ns_netns_cmd, | |
554 | "logical-router <1-65535> ns NAME", | |
555 | "Enable a logical-router\n" | |
556 | "Specify the logical-router indentifier\n" | |
557 | "The Name Space\n" | |
558 | "The file name in " NS_RUN_DIR ", or a full pathname\n") | |
559 | { | |
560 | ns_id_t ns_id = NS_DEFAULT; | |
561 | struct ns *ns = NULL; | |
562 | char *pathname = ns_netns_pathname (vty, argv[1]); | |
563 | ||
564 | if (!pathname) | |
565 | return CMD_WARNING; | |
566 | ||
567 | VTY_GET_INTEGER ("NS ID", ns_id, argv[0]); | |
568 | ns = ns_get (ns_id); | |
569 | ||
570 | if (ns->name && strcmp (ns->name, pathname) != 0) | |
571 | { | |
572 | vty_out (vty, "NS %u is already configured with NETNS %s%s", | |
573 | ns->ns_id, ns->name, VTY_NEWLINE); | |
574 | return CMD_WARNING; | |
575 | } | |
576 | ||
577 | if (!ns->name) | |
578 | ns->name = XSTRDUP (MTYPE_NS_NAME, pathname); | |
579 | ||
580 | if (!ns_enable (ns)) | |
581 | { | |
582 | vty_out (vty, "Can not associate NS %u with NETNS %s%s", | |
583 | ns->ns_id, ns->name, VTY_NEWLINE); | |
584 | return CMD_WARNING; | |
585 | } | |
586 | ||
587 | return CMD_SUCCESS; | |
588 | } | |
589 | ||
590 | DEFUN (no_ns_netns, | |
591 | no_ns_netns_cmd, | |
592 | "no logical-router <1-65535> ns NAME", | |
593 | NO_STR | |
594 | "Enable a Logical-Router\n" | |
595 | "Specify the Logical-Router identifier\n" | |
596 | "The Name Space\n" | |
597 | "The file name in " NS_RUN_DIR ", or a full pathname\n") | |
598 | { | |
599 | ns_id_t ns_id = NS_DEFAULT; | |
600 | struct ns *ns = NULL; | |
601 | char *pathname = ns_netns_pathname (vty, argv[1]); | |
602 | ||
603 | if (!pathname) | |
604 | return CMD_WARNING; | |
605 | ||
606 | VTY_GET_INTEGER ("NS ID", ns_id, argv[0]); | |
607 | ns = ns_lookup (ns_id); | |
608 | ||
609 | if (!ns) | |
610 | { | |
611 | vty_out (vty, "NS %u is not found%s", ns_id, VTY_NEWLINE); | |
612 | return CMD_SUCCESS; | |
613 | } | |
614 | ||
615 | if (ns->name && strcmp (ns->name, pathname) != 0) | |
616 | { | |
617 | vty_out (vty, "Incorrect NETNS file name%s", VTY_NEWLINE); | |
618 | return CMD_WARNING; | |
619 | } | |
620 | ||
621 | ns_disable (ns); | |
622 | ||
623 | if (ns->name) | |
624 | { | |
625 | XFREE (MTYPE_NS_NAME, ns->name); | |
626 | ns->name = NULL; | |
627 | } | |
628 | ||
629 | return CMD_SUCCESS; | |
630 | } | |
631 | ||
632 | /* NS node. */ | |
633 | static struct cmd_node ns_node = | |
634 | { | |
635 | NS_NODE, | |
636 | "", /* NS node has no interface. */ | |
637 | 1 | |
638 | }; | |
639 | ||
640 | /* NS configuration write function. */ | |
641 | static int | |
642 | ns_config_write (struct vty *vty) | |
643 | { | |
644 | struct route_node *rn; | |
645 | struct ns *ns; | |
646 | int write = 0; | |
647 | ||
648 | for (rn = route_top (ns_table); rn; rn = route_next (rn)) | |
649 | if ((ns = rn->info) != NULL && | |
650 | ns->ns_id != NS_DEFAULT && ns->name) | |
651 | { | |
652 | vty_out (vty, "logical-router %u netns %s%s", ns->ns_id, ns->name, VTY_NEWLINE); | |
653 | write++; | |
654 | } | |
655 | ||
656 | return write; | |
657 | } | |
658 | ||
32bcb8b0 DS |
659 | /* Initialize NS module. */ |
660 | void | |
661 | ns_init (void) | |
662 | { | |
663 | struct ns *default_ns; | |
664 | ||
665 | /* Allocate NS table. */ | |
666 | ns_table = route_table_init (); | |
667 | ||
668 | /* The default NS always exists. */ | |
669 | default_ns = ns_get (NS_DEFAULT); | |
670 | if (!default_ns) | |
671 | { | |
672 | zlog_err ("ns_init: failed to create the default NS!"); | |
673 | exit (1); | |
674 | } | |
675 | ||
676 | /* Set the default NS name. */ | |
677 | default_ns->name = XSTRDUP (MTYPE_NS_NAME, NS_DEFAULT_NAME); | |
678 | ||
679 | /* Enable the default NS. */ | |
680 | if (!ns_enable (default_ns)) | |
681 | { | |
682 | zlog_err ("ns_init: failed to enable the default NS!"); | |
683 | exit (1); | |
684 | } | |
13460c44 | 685 | |
c253dcb5 ND |
686 | if (have_netns()) |
687 | { | |
688 | /* Install NS commands. */ | |
689 | install_node (&ns_node, ns_config_write); | |
690 | install_element (CONFIG_NODE, &ns_netns_cmd); | |
691 | install_element (CONFIG_NODE, &no_ns_netns_cmd); | |
692 | } | |
32bcb8b0 DS |
693 | } |
694 | ||
695 | /* Terminate NS module. */ | |
696 | void | |
697 | ns_terminate (void) | |
698 | { | |
699 | struct route_node *rn; | |
700 | struct ns *ns; | |
701 | ||
702 | for (rn = route_top (ns_table); rn; rn = route_next (rn)) | |
703 | if ((ns = rn->info) != NULL) | |
704 | ns_delete (ns); | |
705 | ||
706 | route_table_finish (ns_table); | |
707 | ns_table = NULL; | |
708 | } | |
709 | ||
710 | /* Create a socket for the NS. */ | |
711 | int | |
712 | ns_socket (int domain, int type, int protocol, ns_id_t ns_id) | |
713 | { | |
13460c44 | 714 | struct ns *ns = ns_lookup (ns_id); |
32bcb8b0 DS |
715 | int ret = -1; |
716 | ||
13460c44 | 717 | if (!ns_is_enabled (ns)) |
32bcb8b0 DS |
718 | { |
719 | errno = ENOSYS; | |
720 | return -1; | |
721 | } | |
722 | ||
c253dcb5 | 723 | if (have_netns()) |
13460c44 | 724 | { |
c253dcb5 ND |
725 | ret = (ns_id != NS_DEFAULT) ? setns (ns->fd, CLONE_NEWNET) : 0; |
726 | if (ret >= 0) | |
727 | { | |
728 | ret = socket (domain, type, protocol); | |
729 | if (ns_id != NS_DEFAULT) | |
730 | setns (ns_lookup (NS_DEFAULT)->fd, CLONE_NEWNET); | |
731 | } | |
13460c44 | 732 | } |
c253dcb5 ND |
733 | else |
734 | ret = socket (domain, type, protocol); | |
32bcb8b0 DS |
735 | |
736 | return ret; | |
737 | } |