]> git.proxmox.com Git - mirror_frr.git/blame - zebra/zebra_netns_notify.c
Merge pull request #3502 from donaldsharp/socket_to_me_baby
[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"
174482ef 37#include "lib_errors.h"
e27dec3c
PG
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"
9df414fe 45#include "zebra_errors.h"
e27dec3c
PG
46
47#ifdef HAVE_NETLINK
48
49/* upon creation of folder under /var/run/netns,
50 * wait that netns context is bound to
51 * that folder 10 seconds
52 */
53#define ZEBRA_NS_POLLING_INTERVAL_MSEC 1000
54#define ZEBRA_NS_POLLING_MAX_RETRIES 200
55
56DEFINE_MTYPE_STATIC(ZEBRA, NETNS_MISC, "ZebraNetNSInfo")
57static struct thread *zebra_netns_notify_current;
58
59struct zebra_netns_info {
60 const char *netnspath;
61 unsigned int retries;
62};
63
64static int zebra_ns_ready_read(struct thread *t);
65static void zebra_ns_notify_create_context_from_entry_name(const char *name);
66static int zebra_ns_continue_read(struct zebra_netns_info *zns_info,
67 int stop_retry);
68static int zebra_ns_notify_read(struct thread *t);
69
70static void zebra_ns_notify_create_context_from_entry_name(const char *name)
71{
72 char *netnspath = ns_netns_pathname(NULL, name);
73 struct vrf *vrf;
74 int ret;
03aff2d8 75 ns_id_t ns_id, ns_id_external;
e27dec3c
PG
76
77 if (netnspath == NULL)
78 return;
79
01b9e3fd
DL
80 frr_elevate_privs(&zserv_privs) {
81 ns_id = zebra_ns_id_get(netnspath);
82 }
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) {
9df414fe 89 zlog_debug(
996c9314
LB
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) {
e914ccbe 95 flog_warn(EC_ZEBRA_NS_VRF_CREATION_FAILED,
9df414fe 96 "NS notify : failed to create VRF %s", name);
03aff2d8 97 ns_map_nsid_with_external(ns_id, false);
b7b816df
PG
98 return;
99 }
01b9e3fd
DL
100 frr_elevate_privs(&zserv_privs) {
101 ret = vrf_netns_handler_create(NULL, vrf, netnspath,
102 ns_id_external, ns_id);
103 }
e27dec3c 104 if (ret != CMD_SUCCESS) {
e914ccbe 105 flog_warn(EC_ZEBRA_NS_VRF_CREATION_FAILED,
9df414fe 106 "NS notify : failed to create NS %s", netnspath);
03aff2d8 107 ns_map_nsid_with_external(ns_id, false);
73899a2f 108 vrf_delete(vrf);
e27dec3c
PG
109 return;
110 }
996c9314 111 zlog_info("NS notify : created VRF %s NS %s", name, netnspath);
e27dec3c
PG
112}
113
114static 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,
996c9314
LB
125 (void *)zns_info, ZEBRA_NS_POLLING_INTERVAL_MSEC,
126 NULL);
e27dec3c
PG
127 return 0;
128}
129
0c902ba5
PG
130static int zebra_ns_delete(char *name)
131{
132 struct vrf *vrf = vrf_lookup_by_name(name);
133 struct ns *ns;
134
135 if (!vrf) {
e914ccbe 136 flog_warn(EC_ZEBRA_NS_DELETION_FAILED_NO_VRF,
9df414fe 137 "NS notify : no VRF found using NS %s", name);
0c902ba5
PG
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
3ed78e8c
PG
154static int zebra_ns_notify_self_identify(struct stat *netst)
155{
156 char net_path[64];
157 int netns;
158
159 sprintf(net_path, "/proc/self/ns/net");
160 netns = open(net_path, O_RDONLY);
161 if (netns < 0)
162 return -1;
163 if (fstat(netns, netst) < 0) {
164 close(netns);
165 return -1;
166 }
167 close(netns);
168 return 0;
169}
170
171static bool zebra_ns_notify_is_default_netns(const char *name)
172{
173 struct stat default_netns_stat;
174 struct stat st;
175 char netnspath[64];
176
177 if (zebra_ns_notify_self_identify(&default_netns_stat))
178 return false;
179
180 memset(&st, 0, sizeof(struct stat));
181 snprintf(netnspath, 64, "%s/%s", NS_RUN_DIR, name);
182 /* compare with local stat */
183 if (stat(netnspath, &st) == 0 &&
184 (st.st_dev == default_netns_stat.st_dev) &&
185 (st.st_ino == default_netns_stat.st_ino))
186 return true;
187 return false;
188}
0c902ba5 189
e27dec3c
PG
190static int zebra_ns_ready_read(struct thread *t)
191{
192 struct zebra_netns_info *zns_info = THREAD_ARG(t);
193 const char *netnspath;
194 int err, stop_retry = 0;
195
196 if (!zns_info)
197 return 0;
198 if (!zns_info->netnspath) {
199 XFREE(MTYPE_NETNS_MISC, zns_info);
200 return 0;
201 }
202 netnspath = zns_info->netnspath;
203 if (--zns_info->retries == 0)
204 stop_retry = 1;
01b9e3fd
DL
205 frr_elevate_privs(&zserv_privs) {
206 err = ns_switch_to_netns(netnspath);
207 }
e27dec3c
PG
208 if (err < 0)
209 return zebra_ns_continue_read(zns_info, stop_retry);
210
211 /* go back to default ns */
01b9e3fd
DL
212 frr_elevate_privs(&zserv_privs) {
213 err = ns_switchback_to_initial();
214 }
e27dec3c
PG
215 if (err < 0)
216 return zebra_ns_continue_read(zns_info, stop_retry);
217
3ed78e8c
PG
218 if (zebra_ns_notify_is_default_netns(basename(netnspath))) {
219 zlog_warn(
220 "NS notify : NS %s is default VRF."
221 " Updating VRF Name", basename(netnspath));
4fe52e76 222 vrf_set_default_name(basename(netnspath), false);
3ed78e8c
PG
223 return zebra_ns_continue_read(zns_info, 1);
224 }
225
e27dec3c
PG
226 /* success : close fd and create zns context */
227 zebra_ns_notify_create_context_from_entry_name(basename(netnspath));
228 return zebra_ns_continue_read(zns_info, 1);
229}
230
231static int zebra_ns_notify_read(struct thread *t)
232{
233 int fd_monitor = THREAD_FD(t);
234 struct inotify_event *event;
235 char buf[BUFSIZ];
236 ssize_t len;
237
996c9314
LB
238 zebra_netns_notify_current = thread_add_read(
239 zebrad.master, zebra_ns_notify_read, NULL, fd_monitor, NULL);
e27dec3c
PG
240 len = read(fd_monitor, buf, sizeof(buf));
241 if (len < 0) {
e914ccbe 242 flog_err_sys(EC_ZEBRA_NS_NOTIFY_READ,
9df414fe
QY
243 "NS notify read: failed to read (%s)",
244 safe_strerror(errno));
e27dec3c
PG
245 return 0;
246 }
996c9314
LB
247 for (event = (struct inotify_event *)buf; (char *)event < &buf[len];
248 event = (struct inotify_event *)((char *)event + sizeof(*event)
249 + event->len)) {
e27dec3c
PG
250 char *netnspath;
251 struct zebra_netns_info *netnsinfo;
252
0c902ba5 253 if (!(event->mask & (IN_CREATE | IN_DELETE)))
e27dec3c 254 continue;
45981fda 255
256 if (offsetof(struct inotify_event, name) + event->len
257 >= sizeof(buf)) {
e914ccbe 258 flog_err(EC_ZEBRA_NS_NOTIFY_READ,
9df414fe 259 "NS notify read: buffer underflow");
32ac96b2 260 break;
261 }
b6312ad1 262
263 if (strnlen(event->name, event->len) == event->len) {
e914ccbe 264 flog_err(EC_ZEBRA_NS_NOTIFY_READ,
9df414fe 265 "NS notify error: bad event name");
b6312ad1 266 break;
267 }
268
01b4cb3e
DS
269 if (event->mask & IN_DELETE)
270 return zebra_ns_delete(event->name);
271
e27dec3c
PG
272 netnspath = ns_netns_pathname(NULL, event->name);
273 if (!netnspath)
274 continue;
275 netnspath = XSTRDUP(MTYPE_NETNS_MISC, netnspath);
276 netnsinfo = XCALLOC(MTYPE_NETNS_MISC,
277 sizeof(struct zebra_netns_info));
278 netnsinfo->retries = ZEBRA_NS_POLLING_MAX_RETRIES;
279 netnsinfo->netnspath = netnspath;
280 thread_add_timer_msec(zebrad.master, zebra_ns_ready_read,
996c9314 281 (void *)netnsinfo, 0, NULL);
e27dec3c
PG
282 }
283 return 0;
284}
285
286void zebra_ns_notify_parse(void)
287{
288 struct dirent *dent;
289 DIR *srcdir = opendir(NS_RUN_DIR);
290
291 if (srcdir == NULL) {
450971aa 292 flog_err_sys(EC_LIB_SYSTEM_CALL,
9df414fe 293 "NS parsing init: failed to parse %s", NS_RUN_DIR);
e27dec3c
PG
294 return;
295 }
296 while ((dent = readdir(srcdir)) != NULL) {
297 struct stat st;
298
299 if (strcmp(dent->d_name, ".") == 0
996c9314 300 || strcmp(dent->d_name, "..") == 0)
e27dec3c
PG
301 continue;
302 if (fstatat(dirfd(srcdir), dent->d_name, &st, 0) < 0) {
9df414fe 303 flog_err_sys(
450971aa 304 EC_LIB_SYSTEM_CALL,
9df414fe
QY
305 "NS parsing init: failed to parse entry %s",
306 dent->d_name);
e27dec3c
PG
307 continue;
308 }
309 if (S_ISDIR(st.st_mode)) {
9df414fe
QY
310 zlog_debug("NS parsing init: %s is not a NS",
311 dent->d_name);
e27dec3c
PG
312 continue;
313 }
3ed78e8c
PG
314 if (zebra_ns_notify_is_default_netns(dent->d_name)) {
315 zlog_warn(
316 "NS notify : NS %s is default VRF."
317 " Updating VRF Name", dent->d_name);
4fe52e76 318 vrf_set_default_name(dent->d_name, false);
3ed78e8c
PG
319 continue;
320 }
e27dec3c
PG
321 zebra_ns_notify_create_context_from_entry_name(dent->d_name);
322 }
323 closedir(srcdir);
324}
325
326void zebra_ns_notify_init(void)
327{
328 int fd_monitor;
329
330 zebra_netns_notify_current = NULL;
331 fd_monitor = inotify_init();
332 if (fd_monitor < 0) {
9df414fe 333 flog_err_sys(
450971aa 334 EC_LIB_SYSTEM_CALL,
9df414fe
QY
335 "NS notify init: failed to initialize inotify (%s)",
336 safe_strerror(errno));
e27dec3c 337 }
0c902ba5
PG
338 if (inotify_add_watch(fd_monitor, NS_RUN_DIR,
339 IN_CREATE | IN_DELETE) < 0) {
450971aa 340 flog_err_sys(EC_LIB_SYSTEM_CALL,
9df414fe
QY
341 "NS notify watch: failed to add watch (%s)",
342 safe_strerror(errno));
e27dec3c 343 }
996c9314
LB
344 zebra_netns_notify_current = thread_add_read(
345 zebrad.master, zebra_ns_notify_read, NULL, fd_monitor, NULL);
e27dec3c
PG
346}
347
348void zebra_ns_notify_close(void)
349{
350 if (zebra_netns_notify_current == NULL)
351 return;
352
353 int fd = 0;
354
355 if (zebra_netns_notify_current->u.fd > 0)
356 fd = zebra_netns_notify_current->u.fd;
71077c48
MS
357
358 if (zebra_netns_notify_current->master != NULL)
359 thread_cancel(zebra_netns_notify_current);
360
361 /* auto-removal of notify items */
e27dec3c
PG
362 if (fd > 0)
363 close(fd);
364}
365
366#else
367void zebra_ns_notify_parse(void)
368{
369}
370
371void zebra_ns_notify_init(void)
372{
373}
374
375void zebra_ns_notify_close(void)
376{
377}
378#endif /* !HAVE_NETLINK */