]>
Commit | Line | Data |
---|---|---|
acddc0ed | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
e27dec3c PG |
2 | /* |
3 | * Zebra NS collector and notifier for Network NameSpaces | |
4 | * Copyright (C) 2017 6WIND | |
e27dec3c PG |
5 | */ |
6 | ||
7 | #include <zebra.h> | |
8 | ||
9 | #ifdef HAVE_NETLINK | |
10 | #ifdef HAVE_NETNS | |
11 | #undef _GNU_SOURCE | |
12 | #define _GNU_SOURCE | |
13 | ||
14 | #include <sched.h> | |
15 | #endif | |
16 | #include <dirent.h> | |
17 | #include <sys/inotify.h> | |
18 | #include <sys/stat.h> | |
19 | ||
24a58196 | 20 | #include "frrevent.h" |
e27dec3c PG |
21 | #include "ns.h" |
22 | #include "command.h" | |
23 | #include "memory.h" | |
174482ef | 24 | #include "lib_errors.h" |
e27dec3c | 25 | |
3801e764 | 26 | #include "zebra_router.h" |
e27dec3c PG |
27 | #endif /* defined(HAVE_NETLINK) */ |
28 | ||
29 | #include "zebra_netns_notify.h" | |
30 | #include "zebra_netns_id.h" | |
9df414fe | 31 | #include "zebra_errors.h" |
6502e5f1 | 32 | #include "interface.h" |
e27dec3c PG |
33 | |
34 | #ifdef HAVE_NETLINK | |
35 | ||
36 | /* upon creation of folder under /var/run/netns, | |
37 | * wait that netns context is bound to | |
38 | * that folder 10 seconds | |
39 | */ | |
40 | #define ZEBRA_NS_POLLING_INTERVAL_MSEC 1000 | |
41 | #define ZEBRA_NS_POLLING_MAX_RETRIES 200 | |
42 | ||
bf8d3d6a | 43 | DEFINE_MTYPE_STATIC(ZEBRA, NETNS_MISC, "ZebraNetNSInfo"); |
e6685141 | 44 | static struct event *zebra_netns_notify_current; |
e27dec3c PG |
45 | |
46 | struct zebra_netns_info { | |
47 | const char *netnspath; | |
48 | unsigned int retries; | |
49 | }; | |
50 | ||
e6685141 | 51 | static void zebra_ns_ready_read(struct event *t); |
e27dec3c PG |
52 | static void zebra_ns_notify_create_context_from_entry_name(const char *name); |
53 | static int zebra_ns_continue_read(struct zebra_netns_info *zns_info, | |
54 | int stop_retry); | |
e6685141 | 55 | static void zebra_ns_notify_read(struct event *t); |
e27dec3c | 56 | |
62d89c64 IR |
57 | static struct vrf *vrf_handler_create(struct vty *vty, const char *vrfname) |
58 | { | |
59 | if (strlen(vrfname) > VRF_NAMSIZ) { | |
60 | flog_warn(EC_LIB_VRF_LENGTH, | |
61 | "%% VRF name %s invalid: length exceeds %d bytes", | |
62 | vrfname, VRF_NAMSIZ); | |
63 | return NULL; | |
64 | } | |
65 | ||
66 | return vrf_get(VRF_UNKNOWN, vrfname); | |
67 | } | |
68 | ||
e27dec3c PG |
69 | static void zebra_ns_notify_create_context_from_entry_name(const char *name) |
70 | { | |
71 | char *netnspath = ns_netns_pathname(NULL, name); | |
72 | struct vrf *vrf; | |
73 | int ret; | |
2d4e4d39 PG |
74 | ns_id_t ns_id, ns_id_external, ns_id_relative = NS_UNKNOWN; |
75 | struct ns *default_ns; | |
e27dec3c PG |
76 | |
77 | if (netnspath == NULL) | |
78 | return; | |
79 | ||
0cf6db21 | 80 | frr_with_privs(&zserv_privs) { |
289b0f0d | 81 | ns_id = zebra_ns_id_get(netnspath, -1); |
01b9e3fd | 82 | } |
85a0edca PG |
83 | if (ns_id == NS_UNKNOWN) |
84 | return; | |
03aff2d8 | 85 | ns_id_external = ns_map_nsid_with_external(ns_id, true); |
b7b816df | 86 | /* if VRF with NS ID already present */ |
03aff2d8 | 87 | vrf = vrf_lookup_by_id((vrf_id_t)ns_id_external); |
b7b816df | 88 | if (vrf) { |
9df414fe | 89 | zlog_debug( |
996c9314 LB |
90 | "NS notify : same NSID used by VRF %s. Ignore NS %s creation", |
91 | vrf->name, netnspath); | |
b7b816df PG |
92 | return; |
93 | } | |
62d89c64 IR |
94 | vrf = vrf_handler_create(NULL, name); |
95 | if (!vrf) { | |
e914ccbe | 96 | flog_warn(EC_ZEBRA_NS_VRF_CREATION_FAILED, |
9df414fe | 97 | "NS notify : failed to create VRF %s", name); |
03aff2d8 | 98 | ns_map_nsid_with_external(ns_id, false); |
b7b816df PG |
99 | return; |
100 | } | |
289b0f0d PG |
101 | |
102 | default_ns = ns_get_default(); | |
103 | ||
104 | /* force kernel ns_id creation in that new vrf */ | |
105 | frr_with_privs(&zserv_privs) { | |
106 | ns_switch_to_netns(netnspath); | |
107 | ns_id_relative = zebra_ns_id_get(NULL, default_ns->fd); | |
108 | ns_switchback_to_initial(); | |
109 | } | |
110 | ||
0cf6db21 | 111 | frr_with_privs(&zserv_privs) { |
62d89c64 IR |
112 | ret = zebra_vrf_netns_handler_create(NULL, vrf, netnspath, |
113 | ns_id_external, ns_id, | |
114 | ns_id_relative); | |
01b9e3fd | 115 | } |
e27dec3c | 116 | if (ret != CMD_SUCCESS) { |
e914ccbe | 117 | flog_warn(EC_ZEBRA_NS_VRF_CREATION_FAILED, |
9df414fe | 118 | "NS notify : failed to create NS %s", netnspath); |
03aff2d8 | 119 | ns_map_nsid_with_external(ns_id, false); |
73899a2f | 120 | vrf_delete(vrf); |
e27dec3c PG |
121 | return; |
122 | } | |
996c9314 | 123 | zlog_info("NS notify : created VRF %s NS %s", name, netnspath); |
e27dec3c PG |
124 | } |
125 | ||
126 | static int zebra_ns_continue_read(struct zebra_netns_info *zns_info, | |
127 | int stop_retry) | |
128 | { | |
129 | void *ns_path_ptr = (void *)zns_info->netnspath; | |
130 | ||
131 | if (stop_retry) { | |
132 | XFREE(MTYPE_NETNS_MISC, ns_path_ptr); | |
133 | XFREE(MTYPE_NETNS_MISC, zns_info); | |
134 | return 0; | |
135 | } | |
907a2395 DS |
136 | event_add_timer_msec(zrouter.master, zebra_ns_ready_read, |
137 | (void *)zns_info, ZEBRA_NS_POLLING_INTERVAL_MSEC, | |
138 | NULL); | |
e27dec3c PG |
139 | return 0; |
140 | } | |
141 | ||
0c902ba5 PG |
142 | static int zebra_ns_delete(char *name) |
143 | { | |
144 | struct vrf *vrf = vrf_lookup_by_name(name); | |
6502e5f1 | 145 | struct interface *ifp, *tmp; |
0c902ba5 PG |
146 | struct ns *ns; |
147 | ||
148 | if (!vrf) { | |
e914ccbe | 149 | flog_warn(EC_ZEBRA_NS_DELETION_FAILED_NO_VRF, |
9df414fe | 150 | "NS notify : no VRF found using NS %s", name); |
0c902ba5 PG |
151 | return 0; |
152 | } | |
6502e5f1 IR |
153 | |
154 | /* | |
155 | * We don't receive interface down/delete notifications from kernel | |
156 | * when a netns is deleted. Therefore we have to manually replicate | |
157 | * the necessary actions here. | |
158 | */ | |
159 | RB_FOREACH_SAFE (ifp, if_name_head, &vrf->ifaces_by_name, tmp) { | |
160 | if (!CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_ACTIVE)) | |
161 | continue; | |
162 | ||
163 | if (if_is_no_ptm_operative(ifp)) { | |
164 | UNSET_FLAG(ifp->flags, IFF_RUNNING); | |
165 | if_down(ifp); | |
166 | } | |
167 | ||
7eefea98 PJD |
168 | if (IS_ZEBRA_IF_BOND(ifp)) |
169 | zebra_l2if_update_bond(ifp, false); | |
170 | if (IS_ZEBRA_IF_BOND_SLAVE(ifp)) | |
171 | zebra_l2if_update_bond_slave(ifp, IFINDEX_INTERNAL, | |
172 | false); | |
173 | /* Special handling for bridge or VxLAN interfaces. */ | |
174 | if (IS_ZEBRA_IF_BRIDGE(ifp)) | |
175 | zebra_l2_bridge_del(ifp); | |
176 | else if (IS_ZEBRA_IF_VXLAN(ifp)) | |
177 | zebra_l2_vxlanif_del(ifp); | |
178 | ||
6502e5f1 | 179 | UNSET_FLAG(ifp->flags, IFF_UP); |
d0438da6 | 180 | if_delete_update(&ifp); |
6502e5f1 IR |
181 | } |
182 | ||
0c902ba5 PG |
183 | ns = (struct ns *)vrf->ns_ctxt; |
184 | /* the deletion order is the same | |
185 | * as the one used when siging signal is received | |
186 | */ | |
9acc98c8 | 187 | vrf->ns_ctxt = NULL; |
0c902ba5 PG |
188 | vrf_delete(vrf); |
189 | if (ns) | |
190 | ns_delete(ns); | |
191 | ||
192 | zlog_info("NS notify : deleted VRF %s", name); | |
193 | return 0; | |
194 | } | |
195 | ||
3ed78e8c PG |
196 | static int zebra_ns_notify_self_identify(struct stat *netst) |
197 | { | |
2b7165e7 | 198 | char net_path[PATH_MAX]; |
3ed78e8c PG |
199 | int netns; |
200 | ||
772270f3 | 201 | snprintf(net_path, sizeof(net_path), "/proc/self/ns/net"); |
3ed78e8c PG |
202 | netns = open(net_path, O_RDONLY); |
203 | if (netns < 0) | |
204 | return -1; | |
205 | if (fstat(netns, netst) < 0) { | |
206 | close(netns); | |
207 | return -1; | |
208 | } | |
209 | close(netns); | |
210 | return 0; | |
211 | } | |
212 | ||
213 | static bool zebra_ns_notify_is_default_netns(const char *name) | |
214 | { | |
215 | struct stat default_netns_stat; | |
216 | struct stat st; | |
f177a83e | 217 | char netnspath[PATH_MAX]; |
3ed78e8c PG |
218 | |
219 | if (zebra_ns_notify_self_identify(&default_netns_stat)) | |
220 | return false; | |
221 | ||
6006b807 | 222 | memset(&st, 0, sizeof(st)); |
772270f3 | 223 | snprintf(netnspath, sizeof(netnspath), "%s/%s", NS_RUN_DIR, name); |
3ed78e8c PG |
224 | /* compare with local stat */ |
225 | if (stat(netnspath, &st) == 0 && | |
226 | (st.st_dev == default_netns_stat.st_dev) && | |
227 | (st.st_ino == default_netns_stat.st_ino)) | |
228 | return true; | |
229 | return false; | |
230 | } | |
0c902ba5 | 231 | |
e6685141 | 232 | static void zebra_ns_ready_read(struct event *t) |
e27dec3c | 233 | { |
e16d030c | 234 | struct zebra_netns_info *zns_info = EVENT_ARG(t); |
e27dec3c PG |
235 | const char *netnspath; |
236 | int err, stop_retry = 0; | |
237 | ||
238 | if (!zns_info) | |
cc9f21da | 239 | return; |
e27dec3c PG |
240 | if (!zns_info->netnspath) { |
241 | XFREE(MTYPE_NETNS_MISC, zns_info); | |
cc9f21da | 242 | return; |
e27dec3c PG |
243 | } |
244 | netnspath = zns_info->netnspath; | |
245 | if (--zns_info->retries == 0) | |
246 | stop_retry = 1; | |
0cf6db21 | 247 | frr_with_privs(&zserv_privs) { |
01b9e3fd DL |
248 | err = ns_switch_to_netns(netnspath); |
249 | } | |
cc9f21da DS |
250 | if (err < 0) { |
251 | zebra_ns_continue_read(zns_info, stop_retry); | |
252 | return; | |
253 | } | |
e27dec3c PG |
254 | |
255 | /* go back to default ns */ | |
0cf6db21 | 256 | frr_with_privs(&zserv_privs) { |
01b9e3fd DL |
257 | err = ns_switchback_to_initial(); |
258 | } | |
cc9f21da DS |
259 | if (err < 0) { |
260 | zebra_ns_continue_read(zns_info, stop_retry); | |
261 | return; | |
262 | } | |
e27dec3c | 263 | |
167b0889 PG |
264 | /* check default name is not already set */ |
265 | if (strmatch(VRF_DEFAULT_NAME, basename(netnspath))) { | |
3efd0893 | 266 | zlog_warn("NS notify : NS %s is already default VRF.Cancel VRF Creation", basename(netnspath)); |
cc9f21da DS |
267 | zebra_ns_continue_read(zns_info, 1); |
268 | return; | |
167b0889 | 269 | } |
3ed78e8c PG |
270 | if (zebra_ns_notify_is_default_netns(basename(netnspath))) { |
271 | zlog_warn( | |
ac2cb9bf IR |
272 | "NS notify : NS %s is default VRF. Ignore VRF creation", |
273 | basename(netnspath)); | |
cc9f21da DS |
274 | zebra_ns_continue_read(zns_info, 1); |
275 | return; | |
3ed78e8c PG |
276 | } |
277 | ||
e27dec3c PG |
278 | /* success : close fd and create zns context */ |
279 | zebra_ns_notify_create_context_from_entry_name(basename(netnspath)); | |
cc9f21da | 280 | zebra_ns_continue_read(zns_info, 1); |
e27dec3c PG |
281 | } |
282 | ||
e6685141 | 283 | static void zebra_ns_notify_read(struct event *t) |
e27dec3c | 284 | { |
e16d030c | 285 | int fd_monitor = EVENT_FD(t); |
e27dec3c PG |
286 | struct inotify_event *event; |
287 | char buf[BUFSIZ]; | |
288 | ssize_t len; | |
da5bd13c | 289 | char event_name[NAME_MAX + 1]; |
e27dec3c | 290 | |
907a2395 DS |
291 | event_add_read(zrouter.master, zebra_ns_notify_read, NULL, fd_monitor, |
292 | &zebra_netns_notify_current); | |
e27dec3c PG |
293 | len = read(fd_monitor, buf, sizeof(buf)); |
294 | if (len < 0) { | |
e914ccbe | 295 | flog_err_sys(EC_ZEBRA_NS_NOTIFY_READ, |
9df414fe QY |
296 | "NS notify read: failed to read (%s)", |
297 | safe_strerror(errno)); | |
cc9f21da | 298 | return; |
e27dec3c | 299 | } |
996c9314 LB |
300 | for (event = (struct inotify_event *)buf; (char *)event < &buf[len]; |
301 | event = (struct inotify_event *)((char *)event + sizeof(*event) | |
302 | + event->len)) { | |
e27dec3c PG |
303 | char *netnspath; |
304 | struct zebra_netns_info *netnsinfo; | |
305 | ||
0c902ba5 | 306 | if (!(event->mask & (IN_CREATE | IN_DELETE))) |
e27dec3c | 307 | continue; |
45981fda | 308 | |
309 | if (offsetof(struct inotify_event, name) + event->len | |
310 | >= sizeof(buf)) { | |
e914ccbe | 311 | flog_err(EC_ZEBRA_NS_NOTIFY_READ, |
9df414fe | 312 | "NS notify read: buffer underflow"); |
32ac96b2 | 313 | break; |
314 | } | |
b6312ad1 | 315 | |
316 | if (strnlen(event->name, event->len) == event->len) { | |
e914ccbe | 317 | flog_err(EC_ZEBRA_NS_NOTIFY_READ, |
9df414fe | 318 | "NS notify error: bad event name"); |
b6312ad1 | 319 | break; |
320 | } | |
321 | ||
da5bd13c RZ |
322 | /* |
323 | * Coverity Scan extra steps to satisfy `STRING_NULL` warning: | |
324 | * - Make sure event name is present by checking `len != 0` | |
325 | * - Event name length must be at most `NAME_MAX + 1` | |
326 | * (null byte inclusive) | |
327 | * - Copy event name to a stack buffer to make sure it | |
328 | * includes the null byte. `event->name` includes at least | |
329 | * one null byte and `event->len` accounts the null bytes, | |
330 | * so the operation after `memcpy` will look like a | |
331 | * truncation to satisfy Coverity Scan null byte ending. | |
332 | * | |
333 | * Example: | |
334 | * if `event->name` is `abc\0` and `event->len` is 4, | |
335 | * `memcpy` will copy the 4 bytes and then we set the | |
336 | * null byte again at the position 4. | |
337 | * | |
338 | * For more information please read inotify(7) man page. | |
339 | */ | |
340 | if (event->len == 0) | |
341 | continue; | |
342 | ||
343 | if (event->len > sizeof(event_name)) { | |
344 | flog_err(EC_ZEBRA_NS_NOTIFY_READ, | |
345 | "NS notify error: unexpected big event name"); | |
346 | break; | |
347 | } | |
348 | ||
349 | memcpy(event_name, event->name, event->len); | |
350 | event_name[event->len - 1] = 0; | |
351 | ||
f245bcae | 352 | if (event->mask & IN_DELETE) { |
da5bd13c | 353 | zebra_ns_delete(event_name); |
f245bcae PG |
354 | continue; |
355 | } | |
da5bd13c | 356 | netnspath = ns_netns_pathname(NULL, event_name); |
e27dec3c PG |
357 | if (!netnspath) |
358 | continue; | |
359 | netnspath = XSTRDUP(MTYPE_NETNS_MISC, netnspath); | |
360 | netnsinfo = XCALLOC(MTYPE_NETNS_MISC, | |
361 | sizeof(struct zebra_netns_info)); | |
362 | netnsinfo->retries = ZEBRA_NS_POLLING_MAX_RETRIES; | |
363 | netnsinfo->netnspath = netnspath; | |
907a2395 DS |
364 | event_add_timer_msec(zrouter.master, zebra_ns_ready_read, |
365 | (void *)netnsinfo, 0, NULL); | |
e27dec3c | 366 | } |
e27dec3c PG |
367 | } |
368 | ||
369 | void zebra_ns_notify_parse(void) | |
370 | { | |
371 | struct dirent *dent; | |
372 | DIR *srcdir = opendir(NS_RUN_DIR); | |
373 | ||
374 | if (srcdir == NULL) { | |
450971aa | 375 | flog_err_sys(EC_LIB_SYSTEM_CALL, |
9df414fe | 376 | "NS parsing init: failed to parse %s", NS_RUN_DIR); |
e27dec3c PG |
377 | return; |
378 | } | |
379 | while ((dent = readdir(srcdir)) != NULL) { | |
380 | struct stat st; | |
381 | ||
382 | if (strcmp(dent->d_name, ".") == 0 | |
996c9314 | 383 | || strcmp(dent->d_name, "..") == 0) |
e27dec3c PG |
384 | continue; |
385 | if (fstatat(dirfd(srcdir), dent->d_name, &st, 0) < 0) { | |
9df414fe | 386 | flog_err_sys( |
450971aa | 387 | EC_LIB_SYSTEM_CALL, |
9df414fe QY |
388 | "NS parsing init: failed to parse entry %s", |
389 | dent->d_name); | |
e27dec3c PG |
390 | continue; |
391 | } | |
392 | if (S_ISDIR(st.st_mode)) { | |
9df414fe QY |
393 | zlog_debug("NS parsing init: %s is not a NS", |
394 | dent->d_name); | |
e27dec3c PG |
395 | continue; |
396 | } | |
167b0889 PG |
397 | /* check default name is not already set */ |
398 | if (strmatch(VRF_DEFAULT_NAME, basename(dent->d_name))) { | |
3efd0893 | 399 | zlog_warn("NS notify : NS %s is already default VRF.Cancel VRF Creation", dent->d_name); |
167b0889 PG |
400 | continue; |
401 | } | |
3ed78e8c PG |
402 | if (zebra_ns_notify_is_default_netns(dent->d_name)) { |
403 | zlog_warn( | |
ac2cb9bf IR |
404 | "NS notify : NS %s is default VRF. Ignore VRF creation", |
405 | dent->d_name); | |
3ed78e8c PG |
406 | continue; |
407 | } | |
e27dec3c PG |
408 | zebra_ns_notify_create_context_from_entry_name(dent->d_name); |
409 | } | |
410 | closedir(srcdir); | |
411 | } | |
412 | ||
413 | void zebra_ns_notify_init(void) | |
414 | { | |
415 | int fd_monitor; | |
416 | ||
e27dec3c PG |
417 | fd_monitor = inotify_init(); |
418 | if (fd_monitor < 0) { | |
9df414fe | 419 | flog_err_sys( |
450971aa | 420 | EC_LIB_SYSTEM_CALL, |
9df414fe QY |
421 | "NS notify init: failed to initialize inotify (%s)", |
422 | safe_strerror(errno)); | |
e27dec3c | 423 | } |
0c902ba5 PG |
424 | if (inotify_add_watch(fd_monitor, NS_RUN_DIR, |
425 | IN_CREATE | IN_DELETE) < 0) { | |
450971aa | 426 | flog_err_sys(EC_LIB_SYSTEM_CALL, |
9df414fe QY |
427 | "NS notify watch: failed to add watch (%s)", |
428 | safe_strerror(errno)); | |
e27dec3c | 429 | } |
907a2395 DS |
430 | event_add_read(zrouter.master, zebra_ns_notify_read, NULL, fd_monitor, |
431 | &zebra_netns_notify_current); | |
e27dec3c PG |
432 | } |
433 | ||
434 | void zebra_ns_notify_close(void) | |
435 | { | |
436 | if (zebra_netns_notify_current == NULL) | |
437 | return; | |
438 | ||
439 | int fd = 0; | |
440 | ||
441 | if (zebra_netns_notify_current->u.fd > 0) | |
442 | fd = zebra_netns_notify_current->u.fd; | |
71077c48 MS |
443 | |
444 | if (zebra_netns_notify_current->master != NULL) | |
e16d030c | 445 | EVENT_OFF(zebra_netns_notify_current); |
71077c48 MS |
446 | |
447 | /* auto-removal of notify items */ | |
e27dec3c PG |
448 | if (fd > 0) |
449 | close(fd); | |
450 | } | |
451 | ||
452 | #else | |
453 | void zebra_ns_notify_parse(void) | |
454 | { | |
455 | } | |
456 | ||
457 | void zebra_ns_notify_init(void) | |
458 | { | |
459 | } | |
460 | ||
461 | void zebra_ns_notify_close(void) | |
462 | { | |
463 | } | |
464 | #endif /* !HAVE_NETLINK */ |