]> git.proxmox.com Git - mirror_frr.git/blob - zebra/zebra_netns_notify.c
zebra: adapt the vrf and logical router initialisation
[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;
74
75 if (netnspath == NULL)
76 return;
77
78 if (vrf_handler_create(NULL, name, &vrf) != CMD_SUCCESS) {
79 zlog_warn("NS notify : failed to create VRF %s", name);
80 return;
81 }
82 if (zserv_privs.change(ZPRIVS_RAISE))
83 zlog_err("Can't raise privileges");
84 ns_id = zebra_ns_id_get(netnspath);
85 if (zserv_privs.change(ZPRIVS_LOWER))
86 zlog_err("Can't lower privileges");
87 ret = vrf_netns_handler_create(NULL, vrf, netnspath, ns_id);
88 if (ret != CMD_SUCCESS) {
89 zlog_warn("NS notify : failed to create NS %s", netnspath);
90 return;
91 }
92 zlog_info("NS notify : created VRF %s NS %s",
93 name, netnspath);
94 }
95
96 static int zebra_ns_continue_read(struct zebra_netns_info *zns_info,
97 int stop_retry)
98 {
99 void *ns_path_ptr = (void *)zns_info->netnspath;
100
101 if (stop_retry) {
102 XFREE(MTYPE_NETNS_MISC, ns_path_ptr);
103 XFREE(MTYPE_NETNS_MISC, zns_info);
104 return 0;
105 }
106 thread_add_timer_msec(zebrad.master, zebra_ns_ready_read,
107 (void *)zns_info,
108 ZEBRA_NS_POLLING_INTERVAL_MSEC, NULL);
109 return 0;
110 }
111
112 static int zebra_ns_ready_read(struct thread *t)
113 {
114 struct zebra_netns_info *zns_info = THREAD_ARG(t);
115 const char *netnspath;
116 int err, stop_retry = 0;
117
118 if (!zns_info)
119 return 0;
120 if (!zns_info->netnspath) {
121 XFREE(MTYPE_NETNS_MISC, zns_info);
122 return 0;
123 }
124 netnspath = zns_info->netnspath;
125 if (--zns_info->retries == 0)
126 stop_retry = 1;
127 if (zserv_privs.change(ZPRIVS_RAISE))
128 zlog_err("Can't raise privileges");
129 err = ns_switch_to_netns(netnspath);
130 if (zserv_privs.change(ZPRIVS_LOWER))
131 zlog_err("Can't lower privileges");
132 if (err < 0)
133 return zebra_ns_continue_read(zns_info, stop_retry);
134
135 /* go back to default ns */
136 if (zserv_privs.change(ZPRIVS_RAISE))
137 zlog_err("Can't raise privileges");
138 err = ns_switchback_to_initial();
139 if (zserv_privs.change(ZPRIVS_LOWER))
140 zlog_err("Can't lower privileges");
141 if (err < 0)
142 return zebra_ns_continue_read(zns_info, stop_retry);
143
144 /* success : close fd and create zns context */
145 zebra_ns_notify_create_context_from_entry_name(basename(netnspath));
146 return zebra_ns_continue_read(zns_info, 1);
147 }
148
149 static int zebra_ns_notify_read(struct thread *t)
150 {
151 int fd_monitor = THREAD_FD(t);
152 struct inotify_event *event;
153 char buf[BUFSIZ];
154 ssize_t len;
155
156 zebra_netns_notify_current = thread_add_read(zebrad.master,
157 zebra_ns_notify_read,
158 NULL, fd_monitor, NULL);
159 len = read(fd_monitor, buf, sizeof(buf));
160 if (len < 0) {
161 zlog_warn("NS notify read: failed to read (%s)",
162 safe_strerror(errno));
163 return 0;
164 }
165 for (event = (struct inotify_event *)buf;
166 (char *)event < &buf[len];
167 event = (struct inotify_event *)((char *)event +
168 sizeof(*event) + event->len)) {
169 char *netnspath;
170 struct zebra_netns_info *netnsinfo;
171
172 if (!(event->mask & IN_CREATE))
173 continue;
174 netnspath = ns_netns_pathname(NULL, event->name);
175 if (!netnspath)
176 continue;
177 netnspath = XSTRDUP(MTYPE_NETNS_MISC, netnspath);
178 netnsinfo = XCALLOC(MTYPE_NETNS_MISC,
179 sizeof(struct zebra_netns_info));
180 netnsinfo->retries = ZEBRA_NS_POLLING_MAX_RETRIES;
181 netnsinfo->netnspath = netnspath;
182 thread_add_timer_msec(zebrad.master, zebra_ns_ready_read,
183 (void *)netnsinfo, 0, NULL);
184 }
185 return 0;
186 }
187
188 void zebra_ns_notify_parse(void)
189 {
190 struct dirent *dent;
191 DIR *srcdir = opendir(NS_RUN_DIR);
192
193 if (srcdir == NULL) {
194 zlog_warn("NS parsing init: failed to parse %s", NS_RUN_DIR);
195 return;
196 }
197 while ((dent = readdir(srcdir)) != NULL) {
198 struct stat st;
199
200 if (strcmp(dent->d_name, ".") == 0
201 || strcmp(dent->d_name, "..") == 0)
202 continue;
203 if (fstatat(dirfd(srcdir), dent->d_name, &st, 0) < 0) {
204 zlog_warn("NS parsing init: failed to parse entry %s",
205 dent->d_name);
206 continue;
207 }
208 if (S_ISDIR(st.st_mode)) {
209 zlog_warn("NS parsing init: %s is not a NS",
210 dent->d_name);
211 continue;
212 }
213 zebra_ns_notify_create_context_from_entry_name(dent->d_name);
214 }
215 closedir(srcdir);
216 }
217
218 void zebra_ns_notify_init(void)
219 {
220 int fd_monitor;
221
222 zebra_netns_notify_current = NULL;
223 fd_monitor = inotify_init();
224 if (fd_monitor < 0) {
225 zlog_warn("NS notify init: failed to initialize inotify (%s)",
226 safe_strerror(errno));
227 }
228 if (inotify_add_watch(fd_monitor, NS_RUN_DIR, IN_CREATE) < 0) {
229 zlog_warn("NS notify watch: failed to add watch (%s)",
230 safe_strerror(errno));
231 }
232 zebra_netns_notify_current = thread_add_read(zebrad.master,
233 zebra_ns_notify_read,
234 NULL, fd_monitor, NULL);
235 }
236
237 void zebra_ns_notify_close(void)
238 {
239 if (zebra_netns_notify_current == NULL)
240 return;
241
242 int fd = 0;
243
244 if (zebra_netns_notify_current->u.fd > 0)
245 fd = zebra_netns_notify_current->u.fd;
246 thread_cancel(zebra_netns_notify_current);
247 /* auto-removal of inotify items */
248 if (fd > 0)
249 close(fd);
250 }
251
252 #else
253 void zebra_ns_notify_parse(void)
254 {
255 }
256
257 void zebra_ns_notify_init(void)
258 {
259 }
260
261 void zebra_ns_notify_close(void)
262 {
263 }
264 #endif /* !HAVE_NETLINK */