]> git.proxmox.com Git - mirror_frr.git/blob - zebra/zebra_netns_notify.c
*: use frr_elevate_privs() (1/2: coccinelle)
[mirror_frr.git] / zebra / zebra_netns_notify.c
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"
37 #include "lib_errors.h"
38
39 #include "zserv.h"
40 #include "zebra_memory.h"
41 #endif /* defined(HAVE_NETLINK) */
42
43 #include "zebra_netns_notify.h"
44 #include "zebra_netns_id.h"
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
55 DEFINE_MTYPE_STATIC(ZEBRA, NETNS_MISC, "ZebraNetNSInfo")
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
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;
74 ns_id_t ns_id, ns_id_external;
75
76 if (netnspath == NULL)
77 return;
78
79 frr_elevate_privs(&zserv_privs) {
80 ns_id = zebra_ns_id_get(netnspath);
81 }
82 if (ns_id == NS_UNKNOWN)
83 return;
84 ns_id_external = ns_map_nsid_with_external(ns_id, true);
85 /* if VRF with NS ID already present */
86 vrf = vrf_lookup_by_id((vrf_id_t)ns_id_external);
87 if (vrf) {
88 zlog_warn(
89 "NS notify : same NSID used by VRF %s. Ignore NS %s creation",
90 vrf->name, netnspath);
91 return;
92 }
93 if (vrf_handler_create(NULL, name, &vrf) != CMD_SUCCESS) {
94 zlog_warn("NS notify : failed to create VRF %s", name);
95 ns_map_nsid_with_external(ns_id, false);
96 return;
97 }
98 frr_elevate_privs(&zserv_privs) {
99 ret = vrf_netns_handler_create(NULL, vrf, netnspath,
100 ns_id_external, ns_id);
101 }
102 if (ret != CMD_SUCCESS) {
103 zlog_warn("NS notify : failed to create NS %s", netnspath);
104 ns_map_nsid_with_external(ns_id, false);
105 vrf_delete(vrf);
106 return;
107 }
108 zlog_info("NS notify : created VRF %s NS %s", name, netnspath);
109 }
110
111 static int zebra_ns_continue_read(struct zebra_netns_info *zns_info,
112 int stop_retry)
113 {
114 void *ns_path_ptr = (void *)zns_info->netnspath;
115
116 if (stop_retry) {
117 XFREE(MTYPE_NETNS_MISC, ns_path_ptr);
118 XFREE(MTYPE_NETNS_MISC, zns_info);
119 return 0;
120 }
121 thread_add_timer_msec(zebrad.master, zebra_ns_ready_read,
122 (void *)zns_info, ZEBRA_NS_POLLING_INTERVAL_MSEC,
123 NULL);
124 return 0;
125 }
126
127 static int zebra_ns_delete(char *name)
128 {
129 struct vrf *vrf = vrf_lookup_by_name(name);
130 struct ns *ns;
131
132 if (!vrf) {
133 zlog_warn(
134 "NS notify : no VRF found using NS %s",
135 name);
136 return 0;
137 }
138 /* Clear configured flag and invoke delete. */
139 UNSET_FLAG(vrf->status, VRF_CONFIGURED);
140 ns = (struct ns *)vrf->ns_ctxt;
141 /* the deletion order is the same
142 * as the one used when siging signal is received
143 */
144 vrf_delete(vrf);
145 if (ns)
146 ns_delete(ns);
147
148 zlog_info("NS notify : deleted VRF %s", name);
149 return 0;
150 }
151
152
153 static int zebra_ns_ready_read(struct thread *t)
154 {
155 struct zebra_netns_info *zns_info = THREAD_ARG(t);
156 const char *netnspath;
157 int err, stop_retry = 0;
158
159 if (!zns_info)
160 return 0;
161 if (!zns_info->netnspath) {
162 XFREE(MTYPE_NETNS_MISC, zns_info);
163 return 0;
164 }
165 netnspath = zns_info->netnspath;
166 if (--zns_info->retries == 0)
167 stop_retry = 1;
168 frr_elevate_privs(&zserv_privs) {
169 err = ns_switch_to_netns(netnspath);
170 }
171 if (err < 0)
172 return zebra_ns_continue_read(zns_info, stop_retry);
173
174 /* go back to default ns */
175 frr_elevate_privs(&zserv_privs) {
176 err = ns_switchback_to_initial();
177 }
178 if (err < 0)
179 return zebra_ns_continue_read(zns_info, stop_retry);
180
181 /* success : close fd and create zns context */
182 zebra_ns_notify_create_context_from_entry_name(basename(netnspath));
183 return zebra_ns_continue_read(zns_info, 1);
184 }
185
186 static int zebra_ns_notify_read(struct thread *t)
187 {
188 int fd_monitor = THREAD_FD(t);
189 struct inotify_event *event;
190 char buf[BUFSIZ];
191 ssize_t len;
192
193 zebra_netns_notify_current = thread_add_read(
194 zebrad.master, zebra_ns_notify_read, NULL, fd_monitor, NULL);
195 len = read(fd_monitor, buf, sizeof(buf));
196 if (len < 0) {
197 zlog_warn("NS notify read: failed to read (%s)",
198 safe_strerror(errno));
199 return 0;
200 }
201 for (event = (struct inotify_event *)buf; (char *)event < &buf[len];
202 event = (struct inotify_event *)((char *)event + sizeof(*event)
203 + event->len)) {
204 char *netnspath;
205 struct zebra_netns_info *netnsinfo;
206
207 if (!(event->mask & (IN_CREATE | IN_DELETE)))
208 continue;
209 if (event->mask & IN_DELETE)
210 return zebra_ns_delete(event->name);
211
212 if (offsetof(struct inotify_event, name) + event->len
213 >= sizeof(buf)) {
214 zlog_err("NS notify read: buffer underflow");
215 break;
216 }
217
218 if (strnlen(event->name, event->len) == event->len) {
219 zlog_err("NS notify error: bad event name");
220 break;
221 }
222
223 netnspath = ns_netns_pathname(NULL, event->name);
224 if (!netnspath)
225 continue;
226 netnspath = XSTRDUP(MTYPE_NETNS_MISC, netnspath);
227 netnsinfo = XCALLOC(MTYPE_NETNS_MISC,
228 sizeof(struct zebra_netns_info));
229 netnsinfo->retries = ZEBRA_NS_POLLING_MAX_RETRIES;
230 netnsinfo->netnspath = netnspath;
231 thread_add_timer_msec(zebrad.master, zebra_ns_ready_read,
232 (void *)netnsinfo, 0, NULL);
233 }
234 return 0;
235 }
236
237 void zebra_ns_notify_parse(void)
238 {
239 struct dirent *dent;
240 DIR *srcdir = opendir(NS_RUN_DIR);
241
242 if (srcdir == NULL) {
243 zlog_warn("NS parsing init: failed to parse %s", NS_RUN_DIR);
244 return;
245 }
246 while ((dent = readdir(srcdir)) != NULL) {
247 struct stat st;
248
249 if (strcmp(dent->d_name, ".") == 0
250 || strcmp(dent->d_name, "..") == 0)
251 continue;
252 if (fstatat(dirfd(srcdir), dent->d_name, &st, 0) < 0) {
253 zlog_warn("NS parsing init: failed to parse entry %s",
254 dent->d_name);
255 continue;
256 }
257 if (S_ISDIR(st.st_mode)) {
258 zlog_warn("NS parsing init: %s is not a NS",
259 dent->d_name);
260 continue;
261 }
262 zebra_ns_notify_create_context_from_entry_name(dent->d_name);
263 }
264 closedir(srcdir);
265 }
266
267 void zebra_ns_notify_init(void)
268 {
269 int fd_monitor;
270
271 zebra_netns_notify_current = NULL;
272 fd_monitor = inotify_init();
273 if (fd_monitor < 0) {
274 zlog_warn("NS notify init: failed to initialize inotify (%s)",
275 safe_strerror(errno));
276 }
277 if (inotify_add_watch(fd_monitor, NS_RUN_DIR,
278 IN_CREATE | IN_DELETE) < 0) {
279 zlog_warn("NS notify watch: failed to add watch (%s)",
280 safe_strerror(errno));
281 }
282 zebra_netns_notify_current = thread_add_read(
283 zebrad.master, zebra_ns_notify_read, NULL, fd_monitor, NULL);
284 }
285
286 void zebra_ns_notify_close(void)
287 {
288 if (zebra_netns_notify_current == NULL)
289 return;
290
291 int fd = 0;
292
293 if (zebra_netns_notify_current->u.fd > 0)
294 fd = zebra_netns_notify_current->u.fd;
295 thread_cancel(zebra_netns_notify_current);
296 /* auto-removal of inotify items */
297 if (fd > 0)
298 close(fd);
299 }
300
301 #else
302 void zebra_ns_notify_parse(void)
303 {
304 }
305
306 void zebra_ns_notify_init(void)
307 {
308 }
309
310 void zebra_ns_notify_close(void)
311 {
312 }
313 #endif /* !HAVE_NETLINK */