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