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