]>
Commit | Line | Data |
---|---|---|
e27dec3c PG |
1 | /* |
2 | * Zebra NS collector and notifier for Network NameSpaces | |
3 | * Copyright (C) 2017 6WIND | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify it | |
6 | * under the terms of the GNU General Public License as published by the Free | |
7 | * Software Foundation; either version 2 of the License, or (at your option) | |
8 | * any later version. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, but WITHOUT | |
11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
13 | * more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License along | |
16 | * with this program; see the file COPYING; if not, write to the Free Software | |
17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | |
18 | */ | |
19 | ||
20 | #include <zebra.h> | |
21 | ||
22 | #ifdef HAVE_NETLINK | |
23 | #ifdef HAVE_NETNS | |
24 | #undef _GNU_SOURCE | |
25 | #define _GNU_SOURCE | |
26 | ||
27 | #include <sched.h> | |
28 | #endif | |
29 | #include <dirent.h> | |
30 | #include <sys/inotify.h> | |
31 | #include <sys/stat.h> | |
32 | ||
33 | #include "thread.h" | |
34 | #include "ns.h" | |
35 | #include "command.h" | |
36 | #include "memory.h" | |
174482ef | 37 | #include "lib_errors.h" |
e27dec3c | 38 | |
3801e764 | 39 | #include "zebra_router.h" |
e27dec3c PG |
40 | #endif /* defined(HAVE_NETLINK) */ |
41 | ||
42 | #include "zebra_netns_notify.h" | |
43 | #include "zebra_netns_id.h" | |
9df414fe | 44 | #include "zebra_errors.h" |
e27dec3c PG |
45 | |
46 | #ifdef HAVE_NETLINK | |
47 | ||
48 | /* upon creation of folder under /var/run/netns, | |
49 | * wait that netns context is bound to | |
50 | * that folder 10 seconds | |
51 | */ | |
52 | #define ZEBRA_NS_POLLING_INTERVAL_MSEC 1000 | |
53 | #define ZEBRA_NS_POLLING_MAX_RETRIES 200 | |
54 | ||
bf8d3d6a | 55 | DEFINE_MTYPE_STATIC(ZEBRA, NETNS_MISC, "ZebraNetNSInfo"); |
e27dec3c PG |
56 | static struct thread *zebra_netns_notify_current; |
57 | ||
58 | struct zebra_netns_info { | |
59 | const char *netnspath; | |
60 | unsigned int retries; | |
61 | }; | |
62 | ||
63 | static int zebra_ns_ready_read(struct thread *t); | |
64 | static void zebra_ns_notify_create_context_from_entry_name(const char *name); | |
65 | static int zebra_ns_continue_read(struct zebra_netns_info *zns_info, | |
66 | int stop_retry); | |
67 | static int zebra_ns_notify_read(struct thread *t); | |
68 | ||
62d89c64 IR |
69 | static struct vrf *vrf_handler_create(struct vty *vty, const char *vrfname) |
70 | { | |
71 | if (strlen(vrfname) > VRF_NAMSIZ) { | |
72 | flog_warn(EC_LIB_VRF_LENGTH, | |
73 | "%% VRF name %s invalid: length exceeds %d bytes", | |
74 | vrfname, VRF_NAMSIZ); | |
75 | return NULL; | |
76 | } | |
77 | ||
78 | return vrf_get(VRF_UNKNOWN, vrfname); | |
79 | } | |
80 | ||
e27dec3c PG |
81 | static void zebra_ns_notify_create_context_from_entry_name(const char *name) |
82 | { | |
83 | char *netnspath = ns_netns_pathname(NULL, name); | |
84 | struct vrf *vrf; | |
85 | int ret; | |
2d4e4d39 PG |
86 | ns_id_t ns_id, ns_id_external, ns_id_relative = NS_UNKNOWN; |
87 | struct ns *default_ns; | |
e27dec3c PG |
88 | |
89 | if (netnspath == NULL) | |
90 | return; | |
91 | ||
0cf6db21 | 92 | frr_with_privs(&zserv_privs) { |
289b0f0d | 93 | ns_id = zebra_ns_id_get(netnspath, -1); |
01b9e3fd | 94 | } |
85a0edca PG |
95 | if (ns_id == NS_UNKNOWN) |
96 | return; | |
03aff2d8 | 97 | ns_id_external = ns_map_nsid_with_external(ns_id, true); |
b7b816df | 98 | /* if VRF with NS ID already present */ |
03aff2d8 | 99 | vrf = vrf_lookup_by_id((vrf_id_t)ns_id_external); |
b7b816df | 100 | if (vrf) { |
9df414fe | 101 | zlog_debug( |
996c9314 LB |
102 | "NS notify : same NSID used by VRF %s. Ignore NS %s creation", |
103 | vrf->name, netnspath); | |
b7b816df PG |
104 | return; |
105 | } | |
62d89c64 IR |
106 | vrf = vrf_handler_create(NULL, name); |
107 | if (!vrf) { | |
e914ccbe | 108 | flog_warn(EC_ZEBRA_NS_VRF_CREATION_FAILED, |
9df414fe | 109 | "NS notify : failed to create VRF %s", name); |
03aff2d8 | 110 | ns_map_nsid_with_external(ns_id, false); |
b7b816df PG |
111 | return; |
112 | } | |
289b0f0d PG |
113 | |
114 | default_ns = ns_get_default(); | |
115 | ||
116 | /* force kernel ns_id creation in that new vrf */ | |
117 | frr_with_privs(&zserv_privs) { | |
118 | ns_switch_to_netns(netnspath); | |
119 | ns_id_relative = zebra_ns_id_get(NULL, default_ns->fd); | |
120 | ns_switchback_to_initial(); | |
121 | } | |
122 | ||
0cf6db21 | 123 | frr_with_privs(&zserv_privs) { |
62d89c64 IR |
124 | ret = zebra_vrf_netns_handler_create(NULL, vrf, netnspath, |
125 | ns_id_external, ns_id, | |
126 | ns_id_relative); | |
01b9e3fd | 127 | } |
e27dec3c | 128 | if (ret != CMD_SUCCESS) { |
e914ccbe | 129 | flog_warn(EC_ZEBRA_NS_VRF_CREATION_FAILED, |
9df414fe | 130 | "NS notify : failed to create NS %s", netnspath); |
03aff2d8 | 131 | ns_map_nsid_with_external(ns_id, false); |
73899a2f | 132 | vrf_delete(vrf); |
e27dec3c PG |
133 | return; |
134 | } | |
996c9314 | 135 | zlog_info("NS notify : created VRF %s NS %s", name, netnspath); |
e27dec3c PG |
136 | } |
137 | ||
138 | static int zebra_ns_continue_read(struct zebra_netns_info *zns_info, | |
139 | int stop_retry) | |
140 | { | |
141 | void *ns_path_ptr = (void *)zns_info->netnspath; | |
142 | ||
143 | if (stop_retry) { | |
144 | XFREE(MTYPE_NETNS_MISC, ns_path_ptr); | |
145 | XFREE(MTYPE_NETNS_MISC, zns_info); | |
146 | return 0; | |
147 | } | |
3801e764 | 148 | thread_add_timer_msec(zrouter.master, zebra_ns_ready_read, |
996c9314 LB |
149 | (void *)zns_info, ZEBRA_NS_POLLING_INTERVAL_MSEC, |
150 | NULL); | |
e27dec3c PG |
151 | return 0; |
152 | } | |
153 | ||
0c902ba5 PG |
154 | static int zebra_ns_delete(char *name) |
155 | { | |
156 | struct vrf *vrf = vrf_lookup_by_name(name); | |
157 | struct ns *ns; | |
158 | ||
159 | if (!vrf) { | |
e914ccbe | 160 | flog_warn(EC_ZEBRA_NS_DELETION_FAILED_NO_VRF, |
9df414fe | 161 | "NS notify : no VRF found using NS %s", name); |
0c902ba5 PG |
162 | return 0; |
163 | } | |
0c902ba5 PG |
164 | ns = (struct ns *)vrf->ns_ctxt; |
165 | /* the deletion order is the same | |
166 | * as the one used when siging signal is received | |
167 | */ | |
168 | vrf_delete(vrf); | |
169 | if (ns) | |
170 | ns_delete(ns); | |
171 | ||
172 | zlog_info("NS notify : deleted VRF %s", name); | |
173 | return 0; | |
174 | } | |
175 | ||
3ed78e8c PG |
176 | static int zebra_ns_notify_self_identify(struct stat *netst) |
177 | { | |
2b7165e7 | 178 | char net_path[PATH_MAX]; |
3ed78e8c PG |
179 | int netns; |
180 | ||
772270f3 | 181 | snprintf(net_path, sizeof(net_path), "/proc/self/ns/net"); |
3ed78e8c PG |
182 | netns = open(net_path, O_RDONLY); |
183 | if (netns < 0) | |
184 | return -1; | |
185 | if (fstat(netns, netst) < 0) { | |
186 | close(netns); | |
187 | return -1; | |
188 | } | |
189 | close(netns); | |
190 | return 0; | |
191 | } | |
192 | ||
193 | static bool zebra_ns_notify_is_default_netns(const char *name) | |
194 | { | |
195 | struct stat default_netns_stat; | |
196 | struct stat st; | |
f177a83e | 197 | char netnspath[PATH_MAX]; |
3ed78e8c PG |
198 | |
199 | if (zebra_ns_notify_self_identify(&default_netns_stat)) | |
200 | return false; | |
201 | ||
202 | memset(&st, 0, sizeof(struct stat)); | |
772270f3 | 203 | snprintf(netnspath, sizeof(netnspath), "%s/%s", NS_RUN_DIR, name); |
3ed78e8c PG |
204 | /* compare with local stat */ |
205 | if (stat(netnspath, &st) == 0 && | |
206 | (st.st_dev == default_netns_stat.st_dev) && | |
207 | (st.st_ino == default_netns_stat.st_ino)) | |
208 | return true; | |
209 | return false; | |
210 | } | |
0c902ba5 | 211 | |
e27dec3c PG |
212 | static int zebra_ns_ready_read(struct thread *t) |
213 | { | |
214 | struct zebra_netns_info *zns_info = THREAD_ARG(t); | |
215 | const char *netnspath; | |
216 | int err, stop_retry = 0; | |
217 | ||
218 | if (!zns_info) | |
219 | return 0; | |
220 | if (!zns_info->netnspath) { | |
221 | XFREE(MTYPE_NETNS_MISC, zns_info); | |
222 | return 0; | |
223 | } | |
224 | netnspath = zns_info->netnspath; | |
225 | if (--zns_info->retries == 0) | |
226 | stop_retry = 1; | |
0cf6db21 | 227 | frr_with_privs(&zserv_privs) { |
01b9e3fd DL |
228 | err = ns_switch_to_netns(netnspath); |
229 | } | |
e27dec3c PG |
230 | if (err < 0) |
231 | return zebra_ns_continue_read(zns_info, stop_retry); | |
232 | ||
233 | /* go back to default ns */ | |
0cf6db21 | 234 | frr_with_privs(&zserv_privs) { |
01b9e3fd DL |
235 | err = ns_switchback_to_initial(); |
236 | } | |
e27dec3c PG |
237 | if (err < 0) |
238 | return zebra_ns_continue_read(zns_info, stop_retry); | |
239 | ||
167b0889 PG |
240 | /* check default name is not already set */ |
241 | if (strmatch(VRF_DEFAULT_NAME, basename(netnspath))) { | |
3efd0893 | 242 | zlog_warn("NS notify : NS %s is already default VRF.Cancel VRF Creation", basename(netnspath)); |
167b0889 PG |
243 | return zebra_ns_continue_read(zns_info, 1); |
244 | } | |
3ed78e8c PG |
245 | if (zebra_ns_notify_is_default_netns(basename(netnspath))) { |
246 | zlog_warn( | |
3efd0893 | 247 | "NS notify : NS %s is default VRF. Updating VRF Name", basename(netnspath)); |
4fe52e76 | 248 | vrf_set_default_name(basename(netnspath), false); |
3ed78e8c PG |
249 | return zebra_ns_continue_read(zns_info, 1); |
250 | } | |
251 | ||
e27dec3c PG |
252 | /* success : close fd and create zns context */ |
253 | zebra_ns_notify_create_context_from_entry_name(basename(netnspath)); | |
254 | return zebra_ns_continue_read(zns_info, 1); | |
255 | } | |
256 | ||
257 | static int zebra_ns_notify_read(struct thread *t) | |
258 | { | |
259 | int fd_monitor = THREAD_FD(t); | |
260 | struct inotify_event *event; | |
261 | char buf[BUFSIZ]; | |
262 | ssize_t len; | |
263 | ||
ee1455dd IR |
264 | thread_add_read(zrouter.master, zebra_ns_notify_read, NULL, fd_monitor, |
265 | &zebra_netns_notify_current); | |
e27dec3c PG |
266 | len = read(fd_monitor, buf, sizeof(buf)); |
267 | if (len < 0) { | |
e914ccbe | 268 | flog_err_sys(EC_ZEBRA_NS_NOTIFY_READ, |
9df414fe QY |
269 | "NS notify read: failed to read (%s)", |
270 | safe_strerror(errno)); | |
e27dec3c PG |
271 | return 0; |
272 | } | |
996c9314 LB |
273 | for (event = (struct inotify_event *)buf; (char *)event < &buf[len]; |
274 | event = (struct inotify_event *)((char *)event + sizeof(*event) | |
275 | + event->len)) { | |
e27dec3c PG |
276 | char *netnspath; |
277 | struct zebra_netns_info *netnsinfo; | |
278 | ||
0c902ba5 | 279 | if (!(event->mask & (IN_CREATE | IN_DELETE))) |
e27dec3c | 280 | continue; |
45981fda | 281 | |
282 | if (offsetof(struct inotify_event, name) + event->len | |
283 | >= sizeof(buf)) { | |
e914ccbe | 284 | flog_err(EC_ZEBRA_NS_NOTIFY_READ, |
9df414fe | 285 | "NS notify read: buffer underflow"); |
32ac96b2 | 286 | break; |
287 | } | |
b6312ad1 | 288 | |
289 | if (strnlen(event->name, event->len) == event->len) { | |
e914ccbe | 290 | flog_err(EC_ZEBRA_NS_NOTIFY_READ, |
9df414fe | 291 | "NS notify error: bad event name"); |
b6312ad1 | 292 | break; |
293 | } | |
294 | ||
f245bcae PG |
295 | if (event->mask & IN_DELETE) { |
296 | zebra_ns_delete(event->name); | |
297 | continue; | |
298 | } | |
e27dec3c PG |
299 | netnspath = ns_netns_pathname(NULL, event->name); |
300 | if (!netnspath) | |
301 | continue; | |
302 | netnspath = XSTRDUP(MTYPE_NETNS_MISC, netnspath); | |
303 | netnsinfo = XCALLOC(MTYPE_NETNS_MISC, | |
304 | sizeof(struct zebra_netns_info)); | |
305 | netnsinfo->retries = ZEBRA_NS_POLLING_MAX_RETRIES; | |
306 | netnsinfo->netnspath = netnspath; | |
3801e764 | 307 | thread_add_timer_msec(zrouter.master, zebra_ns_ready_read, |
996c9314 | 308 | (void *)netnsinfo, 0, NULL); |
e27dec3c PG |
309 | } |
310 | return 0; | |
311 | } | |
312 | ||
313 | void zebra_ns_notify_parse(void) | |
314 | { | |
315 | struct dirent *dent; | |
316 | DIR *srcdir = opendir(NS_RUN_DIR); | |
317 | ||
318 | if (srcdir == NULL) { | |
450971aa | 319 | flog_err_sys(EC_LIB_SYSTEM_CALL, |
9df414fe | 320 | "NS parsing init: failed to parse %s", NS_RUN_DIR); |
e27dec3c PG |
321 | return; |
322 | } | |
323 | while ((dent = readdir(srcdir)) != NULL) { | |
324 | struct stat st; | |
325 | ||
326 | if (strcmp(dent->d_name, ".") == 0 | |
996c9314 | 327 | || strcmp(dent->d_name, "..") == 0) |
e27dec3c PG |
328 | continue; |
329 | if (fstatat(dirfd(srcdir), dent->d_name, &st, 0) < 0) { | |
9df414fe | 330 | flog_err_sys( |
450971aa | 331 | EC_LIB_SYSTEM_CALL, |
9df414fe QY |
332 | "NS parsing init: failed to parse entry %s", |
333 | dent->d_name); | |
e27dec3c PG |
334 | continue; |
335 | } | |
336 | if (S_ISDIR(st.st_mode)) { | |
9df414fe QY |
337 | zlog_debug("NS parsing init: %s is not a NS", |
338 | dent->d_name); | |
e27dec3c PG |
339 | continue; |
340 | } | |
167b0889 PG |
341 | /* check default name is not already set */ |
342 | if (strmatch(VRF_DEFAULT_NAME, basename(dent->d_name))) { | |
3efd0893 | 343 | zlog_warn("NS notify : NS %s is already default VRF.Cancel VRF Creation", dent->d_name); |
167b0889 PG |
344 | continue; |
345 | } | |
3ed78e8c PG |
346 | if (zebra_ns_notify_is_default_netns(dent->d_name)) { |
347 | zlog_warn( | |
3efd0893 | 348 | "NS notify : NS %s is default VRF. Updating VRF Name", dent->d_name); |
4fe52e76 | 349 | vrf_set_default_name(dent->d_name, false); |
3ed78e8c PG |
350 | continue; |
351 | } | |
e27dec3c PG |
352 | zebra_ns_notify_create_context_from_entry_name(dent->d_name); |
353 | } | |
354 | closedir(srcdir); | |
355 | } | |
356 | ||
357 | void zebra_ns_notify_init(void) | |
358 | { | |
359 | int fd_monitor; | |
360 | ||
e27dec3c PG |
361 | fd_monitor = inotify_init(); |
362 | if (fd_monitor < 0) { | |
9df414fe | 363 | flog_err_sys( |
450971aa | 364 | EC_LIB_SYSTEM_CALL, |
9df414fe QY |
365 | "NS notify init: failed to initialize inotify (%s)", |
366 | safe_strerror(errno)); | |
e27dec3c | 367 | } |
0c902ba5 PG |
368 | if (inotify_add_watch(fd_monitor, NS_RUN_DIR, |
369 | IN_CREATE | IN_DELETE) < 0) { | |
450971aa | 370 | flog_err_sys(EC_LIB_SYSTEM_CALL, |
9df414fe QY |
371 | "NS notify watch: failed to add watch (%s)", |
372 | safe_strerror(errno)); | |
e27dec3c | 373 | } |
ee1455dd IR |
374 | thread_add_read(zrouter.master, zebra_ns_notify_read, NULL, fd_monitor, |
375 | &zebra_netns_notify_current); | |
e27dec3c PG |
376 | } |
377 | ||
378 | void zebra_ns_notify_close(void) | |
379 | { | |
380 | if (zebra_netns_notify_current == NULL) | |
381 | return; | |
382 | ||
383 | int fd = 0; | |
384 | ||
385 | if (zebra_netns_notify_current->u.fd > 0) | |
386 | fd = zebra_netns_notify_current->u.fd; | |
71077c48 MS |
387 | |
388 | if (zebra_netns_notify_current->master != NULL) | |
b3d6bc6e | 389 | thread_cancel(&zebra_netns_notify_current); |
71077c48 MS |
390 | |
391 | /* auto-removal of notify items */ | |
e27dec3c PG |
392 | if (fd > 0) |
393 | close(fd); | |
394 | } | |
395 | ||
396 | #else | |
397 | void zebra_ns_notify_parse(void) | |
398 | { | |
399 | } | |
400 | ||
401 | void zebra_ns_notify_init(void) | |
402 | { | |
403 | } | |
404 | ||
405 | void zebra_ns_notify_close(void) | |
406 | { | |
407 | } | |
408 | #endif /* !HAVE_NETLINK */ |