]>
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" | |
32bcb8b0 DS |
34 | #include "log.h" |
35 | #include "memory.h" | |
36 | ||
13460c44 FL |
37 | #include "command.h" |
38 | #include "vty.h" | |
39 | ||
4a1ab8e4 DL |
40 | DEFINE_MTYPE_STATIC(LIB, NS, "Logical-Router") |
41 | DEFINE_MTYPE_STATIC(LIB, NS_NAME, "Logical-Router Name") | |
13460c44 | 42 | |
c7fdd84f RW |
43 | static __inline int ns_compare (struct ns *, struct ns *); |
44 | static struct ns *ns_lookup (ns_id_t); | |
45 | ||
46 | RB_GENERATE (ns_head, ns, entry, ns_compare) | |
47 | ||
48 | struct ns_head ns_tree = RB_INITIALIZER (&ns_tree); | |
13460c44 FL |
49 | |
50 | #ifndef CLONE_NEWNET | |
51 | #define CLONE_NEWNET 0x40000000 /* New network namespace (lo, device, names sockets, etc) */ | |
52 | #endif | |
53 | ||
54 | #ifndef HAVE_SETNS | |
55 | static inline int setns(int fd, int nstype) | |
56 | { | |
57 | #ifdef __NR_setns | |
58 | return syscall(__NR_setns, fd, nstype); | |
59 | #else | |
60 | errno = ENOSYS; | |
61 | return -1; | |
62 | #endif | |
63 | } | |
64 | #endif /* HAVE_SETNS */ | |
65 | ||
c253dcb5 ND |
66 | #ifdef HAVE_NETNS |
67 | ||
13460c44 | 68 | #define NS_DEFAULT_NAME "/proc/self/ns/net" |
c253dcb5 | 69 | static int have_netns_enabled = -1; |
13460c44 FL |
70 | |
71 | #else /* !HAVE_NETNS */ | |
72 | ||
32bcb8b0 DS |
73 | #define NS_DEFAULT_NAME "Default-logical-router" |
74 | ||
13460c44 FL |
75 | #endif /* HAVE_NETNS */ |
76 | ||
c253dcb5 ND |
77 | static int have_netns(void) |
78 | { | |
79 | #ifdef HAVE_NETNS | |
80 | if (have_netns_enabled < 0) | |
81 | { | |
82 | int fd = open (NS_DEFAULT_NAME, O_RDONLY); | |
83 | ||
84 | if (fd < 0) | |
85 | have_netns_enabled = 0; | |
86 | else | |
87 | { | |
88 | have_netns_enabled = 1; | |
89 | close(fd); | |
90 | } | |
91 | } | |
92 | return have_netns_enabled; | |
93 | #else | |
94 | return 0; | |
95 | #endif | |
96 | } | |
97 | ||
32bcb8b0 DS |
98 | /* Holding NS hooks */ |
99 | struct ns_master | |
100 | { | |
101 | int (*ns_new_hook) (ns_id_t, void **); | |
102 | int (*ns_delete_hook) (ns_id_t, void **); | |
103 | int (*ns_enable_hook) (ns_id_t, void **); | |
104 | int (*ns_disable_hook) (ns_id_t, void **); | |
105 | } ns_master = {0,}; | |
106 | ||
32bcb8b0 DS |
107 | static int ns_is_enabled (struct ns *ns); |
108 | static int ns_enable (struct ns *ns); | |
109 | static void ns_disable (struct ns *ns); | |
110 | ||
c7fdd84f RW |
111 | static __inline int |
112 | ns_compare(struct ns *a, struct ns *b) | |
32bcb8b0 | 113 | { |
c7fdd84f | 114 | return (a->ns_id - b->ns_id); |
32bcb8b0 DS |
115 | } |
116 | ||
117 | /* Get a NS. If not found, create one. */ | |
118 | static struct ns * | |
119 | ns_get (ns_id_t ns_id) | |
120 | { | |
32bcb8b0 DS |
121 | struct ns *ns; |
122 | ||
c7fdd84f RW |
123 | ns = ns_lookup (ns_id); |
124 | if (ns) | |
125 | return (ns); | |
32bcb8b0 DS |
126 | |
127 | ns = XCALLOC (MTYPE_NS, sizeof (struct ns)); | |
128 | ns->ns_id = ns_id; | |
13460c44 | 129 | ns->fd = -1; |
c7fdd84f | 130 | RB_INSERT (ns_head, &ns_tree, ns); |
32bcb8b0 DS |
131 | |
132 | /* | |
133 | * Initialize interfaces. | |
134 | * | |
135 | * I'm not sure if this belongs here or in | |
136 | * the vrf code. | |
137 | */ | |
138 | // if_init (&ns->iflist); | |
139 | ||
140 | zlog_info ("NS %u is created.", ns_id); | |
141 | ||
142 | if (ns_master.ns_new_hook) | |
143 | (*ns_master.ns_new_hook) (ns_id, &ns->info); | |
144 | ||
145 | return ns; | |
146 | } | |
147 | ||
148 | /* Delete a NS. This is called in ns_terminate(). */ | |
149 | static void | |
150 | ns_delete (struct ns *ns) | |
151 | { | |
152 | zlog_info ("NS %u is to be deleted.", ns->ns_id); | |
153 | ||
13460c44 | 154 | ns_disable (ns); |
32bcb8b0 DS |
155 | |
156 | if (ns_master.ns_delete_hook) | |
157 | (*ns_master.ns_delete_hook) (ns->ns_id, &ns->info); | |
158 | ||
159 | /* | |
160 | * I'm not entirely sure if the vrf->iflist | |
161 | * needs to be moved into here or not. | |
162 | */ | |
163 | //if_terminate (&ns->iflist); | |
164 | ||
c7fdd84f | 165 | RB_REMOVE (ns_head, &ns_tree, ns); |
32bcb8b0 DS |
166 | if (ns->name) |
167 | XFREE (MTYPE_NS_NAME, ns->name); | |
168 | ||
169 | XFREE (MTYPE_NS, ns); | |
170 | } | |
171 | ||
172 | /* Look up a NS by identifier. */ | |
173 | static struct ns * | |
174 | ns_lookup (ns_id_t ns_id) | |
175 | { | |
c7fdd84f RW |
176 | struct ns ns; |
177 | ns.ns_id = ns_id; | |
178 | return (RB_FIND (ns_head, &ns_tree, &ns)); | |
32bcb8b0 DS |
179 | } |
180 | ||
181 | /* | |
182 | * Check whether the NS is enabled - that is, whether the NS | |
183 | * is ready to allocate resources. Currently there's only one | |
184 | * type of resource: socket. | |
185 | */ | |
186 | static int | |
187 | ns_is_enabled (struct ns *ns) | |
188 | { | |
c253dcb5 ND |
189 | if (have_netns()) |
190 | return ns && ns->fd >= 0; | |
191 | else | |
192 | return ns && ns->fd == -2 && ns->ns_id == NS_DEFAULT; | |
32bcb8b0 DS |
193 | } |
194 | ||
195 | /* | |
196 | * Enable a NS - that is, let the NS be ready to use. | |
197 | * The NS_ENABLE_HOOK callback will be called to inform | |
198 | * that they can allocate resources in this NS. | |
199 | * | |
200 | * RETURN: 1 - enabled successfully; otherwise, 0. | |
201 | */ | |
202 | static int | |
203 | ns_enable (struct ns *ns) | |
204 | { | |
13460c44 FL |
205 | |
206 | if (!ns_is_enabled (ns)) | |
32bcb8b0 | 207 | { |
c253dcb5 ND |
208 | if (have_netns()) { |
209 | ns->fd = open (ns->name, O_RDONLY); | |
210 | } else { | |
211 | ns->fd = -2; /* Remember that ns_enable_hook has been called */ | |
212 | errno = -ENOTSUP; | |
213 | } | |
13460c44 FL |
214 | |
215 | if (!ns_is_enabled (ns)) | |
216 | { | |
217 | zlog_err ("Can not enable NS %u: %s!", | |
218 | ns->ns_id, safe_strerror (errno)); | |
219 | return 0; | |
220 | } | |
221 | ||
c253dcb5 ND |
222 | if (have_netns()) |
223 | zlog_info ("NS %u is associated with NETNS %s.", | |
224 | ns->ns_id, ns->name); | |
32bcb8b0 | 225 | |
13460c44 | 226 | zlog_info ("NS %u is enabled.", ns->ns_id); |
32bcb8b0 DS |
227 | if (ns_master.ns_enable_hook) |
228 | (*ns_master.ns_enable_hook) (ns->ns_id, &ns->info); | |
32bcb8b0 DS |
229 | } |
230 | ||
13460c44 | 231 | return 1; |
32bcb8b0 DS |
232 | } |
233 | ||
234 | /* | |
235 | * Disable a NS - that is, let the NS be unusable. | |
236 | * The NS_DELETE_HOOK callback will be called to inform | |
237 | * that they must release the resources in the NS. | |
238 | */ | |
239 | static void | |
240 | ns_disable (struct ns *ns) | |
241 | { | |
242 | if (ns_is_enabled (ns)) | |
243 | { | |
244 | zlog_info ("NS %u is to be disabled.", ns->ns_id); | |
245 | ||
32bcb8b0 DS |
246 | if (ns_master.ns_disable_hook) |
247 | (*ns_master.ns_disable_hook) (ns->ns_id, &ns->info); | |
13460c44 | 248 | |
c253dcb5 ND |
249 | if (have_netns()) |
250 | close (ns->fd); | |
251 | ||
13460c44 | 252 | ns->fd = -1; |
32bcb8b0 DS |
253 | } |
254 | } | |
255 | ||
256 | ||
257 | /* Add a NS hook. Please add hooks before calling ns_init(). */ | |
258 | void | |
259 | ns_add_hook (int type, int (*func)(ns_id_t, void **)) | |
260 | { | |
261 | switch (type) { | |
262 | case NS_NEW_HOOK: | |
263 | ns_master.ns_new_hook = func; | |
264 | break; | |
265 | case NS_DELETE_HOOK: | |
266 | ns_master.ns_delete_hook = func; | |
267 | break; | |
268 | case NS_ENABLE_HOOK: | |
269 | ns_master.ns_enable_hook = func; | |
270 | break; | |
271 | case NS_DISABLE_HOOK: | |
272 | ns_master.ns_disable_hook = func; | |
273 | break; | |
274 | default: | |
275 | break; | |
276 | } | |
277 | } | |
278 | ||
13460c44 FL |
279 | /* |
280 | * NS realization with NETNS | |
281 | */ | |
282 | ||
283 | static char * | |
284 | ns_netns_pathname (struct vty *vty, const char *name) | |
285 | { | |
286 | static char pathname[PATH_MAX]; | |
287 | char *result; | |
288 | ||
289 | if (name[0] == '/') /* absolute pathname */ | |
290 | result = realpath (name, pathname); | |
291 | else /* relevant pathname */ | |
292 | { | |
293 | char tmp_name[PATH_MAX]; | |
294 | snprintf (tmp_name, PATH_MAX, "%s/%s", NS_RUN_DIR, name); | |
295 | result = realpath (tmp_name, pathname); | |
296 | } | |
297 | ||
298 | if (! result) | |
299 | { | |
300 | vty_out (vty, "Invalid pathname: %s%s", safe_strerror (errno), | |
301 | VTY_NEWLINE); | |
302 | return NULL; | |
303 | } | |
304 | return pathname; | |
305 | } | |
306 | ||
505e5056 | 307 | DEFUN_NOSH (ns_netns, |
13460c44 | 308 | ns_netns_cmd, |
6147e2c6 | 309 | "logical-router (1-65535) ns NAME", |
13460c44 FL |
310 | "Enable a logical-router\n" |
311 | "Specify the logical-router indentifier\n" | |
312 | "The Name Space\n" | |
313 | "The file name in " NS_RUN_DIR ", or a full pathname\n") | |
314 | { | |
c349116d DW |
315 | int idx_number = 1; |
316 | int idx_name = 3; | |
13460c44 FL |
317 | ns_id_t ns_id = NS_DEFAULT; |
318 | struct ns *ns = NULL; | |
c349116d | 319 | char *pathname = ns_netns_pathname (vty, argv[idx_name]->arg); |
13460c44 FL |
320 | |
321 | if (!pathname) | |
322 | return CMD_WARNING; | |
323 | ||
c349116d | 324 | VTY_GET_INTEGER ("NS ID", ns_id, argv[idx_number]->arg); |
13460c44 FL |
325 | ns = ns_get (ns_id); |
326 | ||
327 | if (ns->name && strcmp (ns->name, pathname) != 0) | |
328 | { | |
329 | vty_out (vty, "NS %u is already configured with NETNS %s%s", | |
330 | ns->ns_id, ns->name, VTY_NEWLINE); | |
331 | return CMD_WARNING; | |
332 | } | |
333 | ||
334 | if (!ns->name) | |
335 | ns->name = XSTRDUP (MTYPE_NS_NAME, pathname); | |
336 | ||
337 | if (!ns_enable (ns)) | |
338 | { | |
339 | vty_out (vty, "Can not associate NS %u with NETNS %s%s", | |
340 | ns->ns_id, ns->name, VTY_NEWLINE); | |
341 | return CMD_WARNING; | |
342 | } | |
343 | ||
344 | return CMD_SUCCESS; | |
345 | } | |
346 | ||
347 | DEFUN (no_ns_netns, | |
348 | no_ns_netns_cmd, | |
6147e2c6 | 349 | "no logical-router (1-65535) ns NAME", |
13460c44 FL |
350 | NO_STR |
351 | "Enable a Logical-Router\n" | |
352 | "Specify the Logical-Router identifier\n" | |
353 | "The Name Space\n" | |
354 | "The file name in " NS_RUN_DIR ", or a full pathname\n") | |
355 | { | |
c349116d DW |
356 | int idx_number = 2; |
357 | int idx_name = 4; | |
13460c44 FL |
358 | ns_id_t ns_id = NS_DEFAULT; |
359 | struct ns *ns = NULL; | |
c349116d | 360 | char *pathname = ns_netns_pathname (vty, argv[idx_name]->arg); |
13460c44 FL |
361 | |
362 | if (!pathname) | |
363 | return CMD_WARNING; | |
364 | ||
c349116d | 365 | VTY_GET_INTEGER ("NS ID", ns_id, argv[idx_number]->arg); |
13460c44 FL |
366 | ns = ns_lookup (ns_id); |
367 | ||
368 | if (!ns) | |
369 | { | |
370 | vty_out (vty, "NS %u is not found%s", ns_id, VTY_NEWLINE); | |
371 | return CMD_SUCCESS; | |
372 | } | |
373 | ||
374 | if (ns->name && strcmp (ns->name, pathname) != 0) | |
375 | { | |
376 | vty_out (vty, "Incorrect NETNS file name%s", VTY_NEWLINE); | |
377 | return CMD_WARNING; | |
378 | } | |
379 | ||
380 | ns_disable (ns); | |
381 | ||
382 | if (ns->name) | |
383 | { | |
384 | XFREE (MTYPE_NS_NAME, ns->name); | |
385 | ns->name = NULL; | |
386 | } | |
387 | ||
388 | return CMD_SUCCESS; | |
389 | } | |
390 | ||
391 | /* NS node. */ | |
392 | static struct cmd_node ns_node = | |
393 | { | |
394 | NS_NODE, | |
395 | "", /* NS node has no interface. */ | |
396 | 1 | |
397 | }; | |
398 | ||
399 | /* NS configuration write function. */ | |
400 | static int | |
401 | ns_config_write (struct vty *vty) | |
402 | { | |
13460c44 FL |
403 | struct ns *ns; |
404 | int write = 0; | |
405 | ||
c7fdd84f RW |
406 | RB_FOREACH (ns, ns_head, &ns_tree) { |
407 | if (ns->ns_id == NS_DEFAULT || ns->name == NULL) | |
408 | continue; | |
409 | ||
410 | vty_out (vty, "logical-router %u netns %s%s", ns->ns_id, ns->name, | |
411 | VTY_NEWLINE); | |
412 | write = 1; | |
413 | } | |
13460c44 FL |
414 | |
415 | return write; | |
416 | } | |
417 | ||
32bcb8b0 DS |
418 | /* Initialize NS module. */ |
419 | void | |
420 | ns_init (void) | |
421 | { | |
422 | struct ns *default_ns; | |
423 | ||
32bcb8b0 DS |
424 | /* The default NS always exists. */ |
425 | default_ns = ns_get (NS_DEFAULT); | |
426 | if (!default_ns) | |
427 | { | |
428 | zlog_err ("ns_init: failed to create the default NS!"); | |
429 | exit (1); | |
430 | } | |
431 | ||
432 | /* Set the default NS name. */ | |
433 | default_ns->name = XSTRDUP (MTYPE_NS_NAME, NS_DEFAULT_NAME); | |
434 | ||
435 | /* Enable the default NS. */ | |
436 | if (!ns_enable (default_ns)) | |
437 | { | |
438 | zlog_err ("ns_init: failed to enable the default NS!"); | |
439 | exit (1); | |
440 | } | |
13460c44 | 441 | |
c253dcb5 ND |
442 | if (have_netns()) |
443 | { | |
444 | /* Install NS commands. */ | |
445 | install_node (&ns_node, ns_config_write); | |
446 | install_element (CONFIG_NODE, &ns_netns_cmd); | |
447 | install_element (CONFIG_NODE, &no_ns_netns_cmd); | |
448 | } | |
32bcb8b0 DS |
449 | } |
450 | ||
451 | /* Terminate NS module. */ | |
452 | void | |
453 | ns_terminate (void) | |
454 | { | |
32bcb8b0 DS |
455 | struct ns *ns; |
456 | ||
c7fdd84f RW |
457 | while ((ns = RB_ROOT (&ns_tree)) != NULL) |
458 | ns_delete (ns); | |
32bcb8b0 DS |
459 | } |
460 | ||
461 | /* Create a socket for the NS. */ | |
462 | int | |
463 | ns_socket (int domain, int type, int protocol, ns_id_t ns_id) | |
464 | { | |
13460c44 | 465 | struct ns *ns = ns_lookup (ns_id); |
32bcb8b0 DS |
466 | int ret = -1; |
467 | ||
13460c44 | 468 | if (!ns_is_enabled (ns)) |
32bcb8b0 DS |
469 | { |
470 | errno = ENOSYS; | |
471 | return -1; | |
472 | } | |
473 | ||
c253dcb5 | 474 | if (have_netns()) |
13460c44 | 475 | { |
c253dcb5 ND |
476 | ret = (ns_id != NS_DEFAULT) ? setns (ns->fd, CLONE_NEWNET) : 0; |
477 | if (ret >= 0) | |
478 | { | |
479 | ret = socket (domain, type, protocol); | |
480 | if (ns_id != NS_DEFAULT) | |
481 | setns (ns_lookup (NS_DEFAULT)->fd, CLONE_NEWNET); | |
482 | } | |
13460c44 | 483 | } |
c253dcb5 ND |
484 | else |
485 | ret = socket (domain, type, protocol); | |
32bcb8b0 DS |
486 | |
487 | return ret; | |
488 | } |