2 * Zebra NS collector and notifier for Network NameSpaces
3 * Copyright (C) 2017 6WIND
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)
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
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
30 #include <sys/inotify.h>
39 #include "zebra_memory.h"
40 #endif /* defined(HAVE_NETLINK) */
42 #include "zebra_netns_notify.h"
43 #include "zebra_netns_id.h"
47 /* upon creation of folder under /var/run/netns,
48 * wait that netns context is bound to
49 * that folder 10 seconds
51 #define ZEBRA_NS_POLLING_INTERVAL_MSEC 1000
52 #define ZEBRA_NS_POLLING_MAX_RETRIES 200
54 DEFINE_MTYPE_STATIC(ZEBRA
, NETNS_MISC
, "ZebraNetNSInfo")
55 static struct thread
*zebra_netns_notify_current
;
57 struct zebra_netns_info
{
58 const char *netnspath
;
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
,
66 static int zebra_ns_notify_read(struct thread
*t
);
68 static void zebra_ns_notify_create_context_from_entry_name(const char *name
)
70 char *netnspath
= ns_netns_pathname(NULL
, name
);
75 if (netnspath
== NULL
)
78 if (vrf_handler_create(NULL
, name
, &vrf
) != CMD_SUCCESS
) {
79 zlog_warn("NS notify : failed to create VRF %s", name
);
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
);
92 zlog_info("NS notify : created VRF %s NS %s",
96 static int zebra_ns_continue_read(struct zebra_netns_info
*zns_info
,
99 void *ns_path_ptr
= (void *)zns_info
->netnspath
;
102 XFREE(MTYPE_NETNS_MISC
, ns_path_ptr
);
103 XFREE(MTYPE_NETNS_MISC
, zns_info
);
106 thread_add_timer_msec(zebrad
.master
, zebra_ns_ready_read
,
108 ZEBRA_NS_POLLING_INTERVAL_MSEC
, NULL
);
112 static int zebra_ns_ready_read(struct thread
*t
)
114 struct zebra_netns_info
*zns_info
= THREAD_ARG(t
);
115 const char *netnspath
;
116 int err
, stop_retry
= 0;
120 if (!zns_info
->netnspath
) {
121 XFREE(MTYPE_NETNS_MISC
, zns_info
);
124 netnspath
= zns_info
->netnspath
;
125 if (--zns_info
->retries
== 0)
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");
133 return zebra_ns_continue_read(zns_info
, stop_retry
);
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");
142 return zebra_ns_continue_read(zns_info
, stop_retry
);
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);
149 static int zebra_ns_notify_read(struct thread
*t
)
151 int fd_monitor
= THREAD_FD(t
);
152 struct inotify_event
*event
;
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
));
161 zlog_warn("NS notify read: failed to read (%s)",
162 safe_strerror(errno
));
165 for (event
= (struct inotify_event
*)buf
;
166 (char *)event
< &buf
[len
];
167 event
= (struct inotify_event
*)((char *)event
+
168 sizeof(*event
) + event
->len
)) {
170 struct zebra_netns_info
*netnsinfo
;
172 if (!(event
->mask
& IN_CREATE
))
174 netnspath
= ns_netns_pathname(NULL
, event
->name
);
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
);
188 void zebra_ns_notify_parse(void)
191 DIR *srcdir
= opendir(NS_RUN_DIR
);
193 if (srcdir
== NULL
) {
194 zlog_warn("NS parsing init: failed to parse %s", NS_RUN_DIR
);
197 while ((dent
= readdir(srcdir
)) != NULL
) {
200 if (strcmp(dent
->d_name
, ".") == 0
201 || strcmp(dent
->d_name
, "..") == 0)
203 if (fstatat(dirfd(srcdir
), dent
->d_name
, &st
, 0) < 0) {
204 zlog_warn("NS parsing init: failed to parse entry %s",
208 if (S_ISDIR(st
.st_mode
)) {
209 zlog_warn("NS parsing init: %s is not a NS",
213 zebra_ns_notify_create_context_from_entry_name(dent
->d_name
);
218 void zebra_ns_notify_init(void)
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
));
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
));
232 zebra_netns_notify_current
= thread_add_read(zebrad
.master
,
233 zebra_ns_notify_read
,
234 NULL
, fd_monitor
, NULL
);
237 void zebra_ns_notify_close(void)
239 if (zebra_netns_notify_current
== NULL
)
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 */
253 void zebra_ns_notify_parse(void)
257 void zebra_ns_notify_init(void)
261 void zebra_ns_notify_close(void)
264 #endif /* !HAVE_NETLINK */