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