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