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