]>
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" | |
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 | ||
56 | DEFINE_MTYPE_STATIC(ZEBRA, NETNS_MISC, "ZebraNetNSInfo") | |
57 | static struct thread *zebra_netns_notify_current; | |
58 | ||
59 | struct zebra_netns_info { | |
60 | const char *netnspath; | |
61 | unsigned int retries; | |
62 | }; | |
63 | ||
64 | static int zebra_ns_ready_read(struct thread *t); | |
65 | static void zebra_ns_notify_create_context_from_entry_name(const char *name); | |
66 | static int zebra_ns_continue_read(struct zebra_netns_info *zns_info, | |
67 | int stop_retry); | |
68 | static int zebra_ns_notify_read(struct thread *t); | |
69 | ||
70 | static 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 | ||
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, | |
996c9314 LB |
125 | (void *)zns_info, ZEBRA_NS_POLLING_INTERVAL_MSEC, |
126 | NULL); | |
e27dec3c PG |
127 | return 0; |
128 | } | |
129 | ||
0c902ba5 PG |
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) { | |
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 |
154 | static 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 | ||
171 | static 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 |
190 | static 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 | ||
231 | static 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 | ||
286 | void 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 | ||
326 | void 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 | ||
348 | void 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 | |
367 | void zebra_ns_notify_parse(void) | |
368 | { | |
369 | } | |
370 | ||
371 | void zebra_ns_notify_init(void) | |
372 | { | |
373 | } | |
374 | ||
375 | void zebra_ns_notify_close(void) | |
376 | { | |
377 | } | |
378 | #endif /* !HAVE_NETLINK */ |