1 /* zebra NETNS ID handling routines
2  * those routines are implemented locally to avoid having external dependencies.
3  * Copyright (C) 2018 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 #include "ns.h"
23 #include "vrf.h"
24 #include "log.h"
25 #include "lib_errors.h"
26 
27 #include "zebra/rib.h"
28 #include "zebra/zebra_dplane.h"
29 #if defined(HAVE_NETLINK)
30 
31 #include <linux/net_namespace.h>
32 #include <linux/netlink.h>
33 #include <linux/rtnetlink.h>
34 
35 #include "zebra_ns.h"
36 #include "kernel_netlink.h"
37 #endif /* defined(HAVE_NETLINK) */
38 
39 #include "zebra/zebra_netns_id.h"
40 #include "zebra/zebra_errors.h"
41 
42 /* in case NEWNSID not available, the NSID will be locally obtained
43  */
44 #define NS_BASE_NSID 0
45 
46 #if defined(HAVE_NETLINK)
47 
48 #define NETLINK_SOCKET_BUFFER_SIZE 512
49 #define NETLINK_ALIGNTO             4
50 #define NETLINK_ALIGN(len)                                                     \
51 	(((len) + NETLINK_ALIGNTO - 1) & ~(NETLINK_ALIGNTO - 1))
52 #define NETLINK_NLATTR_LEN(_a, _b)   (unsigned int)((char *)_a - (char *)_b)
53 
54 #endif /* defined(HAVE_NETLINK) */
55 
zebra_ns_id_get_fallback(const char * netnspath)56 static ns_id_t zebra_ns_id_get_fallback(const char *netnspath)
57 {
58 	static int zebra_ns_id_local;
59 
60 	return zebra_ns_id_local++;
61 }
62 
63 #if defined(HAVE_NETLINK)
64 
initiate_nlh(char * buf,unsigned int * seq,int type)65 static struct nlmsghdr *initiate_nlh(char *buf, unsigned int *seq, int type)
66 {
67 	struct nlmsghdr *nlh;
68 
69 	nlh = (struct nlmsghdr *)buf;
70 	nlh->nlmsg_len = NETLINK_ALIGN(sizeof(struct nlmsghdr));
71 
72 	nlh->nlmsg_type = type;
73 	nlh->nlmsg_flags = NLM_F_REQUEST;
74 	if (type == RTM_NEWNSID)
75 		nlh->nlmsg_flags |= NLM_F_ACK;
76 	nlh->nlmsg_seq = *seq = time(NULL);
77 	return nlh;
78 }
79 
send_receive(int sock,struct nlmsghdr * nlh,unsigned int seq,char * buf)80 static int send_receive(int sock, struct nlmsghdr *nlh, unsigned int seq,
81 			char *buf)
82 {
83 	int ret;
84 	static const struct sockaddr_nl snl = {.nl_family = AF_NETLINK};
85 
86 	ret = sendto(sock, (const void *)nlh, (size_t)nlh->nlmsg_len, 0,
87 		     (struct sockaddr *)&snl, (socklen_t)sizeof(snl));
88 	if (ret < 0) {
89 		flog_err_sys(EC_LIB_SOCKET, "netlink( %u) sendmsg() error: %s",
90 			     sock, safe_strerror(errno));
91 		return -1;
92 	}
93 
94 	/* reception */
95 	struct sockaddr_nl addr;
96 	struct iovec iov = {
97 		.iov_base = buf, .iov_len = NETLINK_SOCKET_BUFFER_SIZE,
98 	};
99 	struct msghdr msg = {
100 		.msg_name = &addr,
101 		.msg_namelen = sizeof(struct sockaddr_nl),
102 		.msg_iov = &iov,
103 		.msg_iovlen = 1,
104 		.msg_control = NULL,
105 		.msg_controllen = 0,
106 		.msg_flags = 0,
107 	};
108 	ret = recvmsg(sock, &msg, 0);
109 	if (ret < 0) {
110 		flog_err_sys(EC_LIB_SOCKET,
111 			     "netlink recvmsg: error %d (errno %u)", ret,
112 			     errno);
113 		return -1;
114 	}
115 	if (msg.msg_flags & MSG_TRUNC) {
116 		flog_err(EC_ZEBRA_NETLINK_LENGTH_ERROR,
117 			 "netlink recvmsg : error message truncated");
118 		return -1;
119 	}
120 	/* nlh already points to buf */
121 	if (nlh->nlmsg_seq != seq) {
122 		flog_err(
123 			EC_ZEBRA_NETLINK_BAD_SEQUENCE,
124 			"netlink recvmsg: bad sequence number %x (expected %x)",
125 			seq, nlh->nlmsg_seq);
126 		return -1;
127 	}
128 	return ret;
129 }
130 
131 /* extract on a valid nlmsg the nsid
132  * valid nlmsghdr - not a nlmsgerr
133  */
extract_nsid(struct nlmsghdr * nlh,char * buf)134 static ns_id_t extract_nsid(struct nlmsghdr *nlh, char *buf)
135 {
136 	ns_id_t ns_id = NS_UNKNOWN;
137 	int offset = NETLINK_ALIGN(sizeof(struct nlmsghdr))
138 		     + NETLINK_ALIGN(sizeof(struct rtgenmsg));
139 	int curr_length = offset;
140 	void *tail = (void *)((char *)nlh + NETLINK_ALIGN(nlh->nlmsg_len));
141 	struct nlattr *attr;
142 
143 	for (attr = (struct nlattr *)(buf + offset);
144 	     NETLINK_NLATTR_LEN(tail, attr) >= sizeof(struct nlattr)
145 	     && attr->nla_len >= sizeof(struct nlattr)
146 	     && attr->nla_len <= NETLINK_NLATTR_LEN(tail, attr);
147 	     attr += NETLINK_ALIGN(attr->nla_len)) {
148 		curr_length += attr->nla_len;
149 		if ((attr->nla_type & NLA_TYPE_MASK) == NETNSA_NSID) {
150 			uint32_t *ptr = (uint32_t *)(attr);
151 
152 			ns_id = ptr[1];
153 			break;
154 		}
155 	}
156 	return ns_id;
157 }
158 
159 /* fd_param = -1 is ignored.
160  * netnspath set to null is ignored.
161  * one of the 2 params is mandatory. netnspath is looked in priority
162  */
zebra_ns_id_get(const char * netnspath,int fd_param)163 ns_id_t zebra_ns_id_get(const char *netnspath, int fd_param)
164 {
165 	int ns_id = -1;
166 	struct sockaddr_nl snl;
167 	int fd = -1, sock, ret;
168 	unsigned int seq;
169 	ns_id_t return_nsid = NS_UNKNOWN;
170 
171 	/* netns path check */
172 	if (!netnspath && fd_param == -1)
173 		return NS_UNKNOWN;
174 	if (netnspath)  {
175 		fd = open(netnspath, O_RDONLY);
176 		if (fd == -1)
177 			return NS_UNKNOWN;
178 	} else if (fd_param != -1)
179 		fd = fd_param;
180 	/* netlink socket */
181 	sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
182 	if (sock < 0) {
183 		flog_err_sys(EC_LIB_SOCKET, "netlink( %u) socket() error: %s",
184 			     sock, safe_strerror(errno));
185 		if (netnspath)
186 			close(fd);
187 		return NS_UNKNOWN;
188 	}
189 	memset(&snl, 0, sizeof(snl));
190 	snl.nl_family = AF_NETLINK;
191 	snl.nl_groups = RTNLGRP_NSID;
192 	snl.nl_pid = 0; /* AUTO PID */
193 	ret = bind(sock, (struct sockaddr *)&snl, sizeof(snl));
194 	if (ret < 0) {
195 		flog_err_sys(EC_LIB_SOCKET,
196 			     "netlink( %u) socket() bind error: %s", sock,
197 			     safe_strerror(errno));
198 		close(sock);
199 		if (netnspath)
200 			close(fd);
201 		return NS_UNKNOWN;
202 	}
203 
204 	/* message to send to netlink,and response : NEWNSID */
205 	char buf[NETLINK_SOCKET_BUFFER_SIZE];
206 	struct nlmsghdr *nlh;
207 	struct rtgenmsg *rt;
208 	int len;
209 
210 	memset(buf, 0, NETLINK_SOCKET_BUFFER_SIZE);
211 	nlh = initiate_nlh(buf, &seq, RTM_NEWNSID);
212 	rt = (struct rtgenmsg *)(buf + nlh->nlmsg_len);
213 	nlh->nlmsg_len += NETLINK_ALIGN(sizeof(struct rtgenmsg));
214 	rt->rtgen_family = AF_UNSPEC;
215 
216 	nl_attr_put32(nlh, NETLINK_SOCKET_BUFFER_SIZE, NETNSA_FD, fd);
217 	nl_attr_put32(nlh, NETLINK_SOCKET_BUFFER_SIZE, NETNSA_NSID, ns_id);
218 
219 	ret = send_receive(sock, nlh, seq, buf);
220 	if (ret < 0) {
221 		close(sock);
222 		if (netnspath)
223 			close(fd);
224 		return NS_UNKNOWN;
225 	}
226 	nlh = (struct nlmsghdr *)buf;
227 
228 	/* message to analyse : NEWNSID response */
229 	ret = 0;
230 	if (nlh->nlmsg_type >= NLMSG_MIN_TYPE) {
231 		return_nsid = extract_nsid(nlh, buf);
232 	} else {
233 		if (nlh->nlmsg_type == NLMSG_ERROR) {
234 			struct nlmsgerr *err =
235 				(struct nlmsgerr
236 					 *)((char *)nlh
237 					    + NETLINK_ALIGN(
238 						      sizeof(struct nlmsghdr)));
239 
240 			ret = -1;
241 			if (err->error < 0)
242 				errno = -err->error;
243 			else
244 				errno = err->error;
245 			if (errno == 0) {
246 				/* request NEWNSID was successfull
247 				 * return EEXIST error to get GETNSID
248 				 */
249 				errno = EEXIST;
250 			}
251 		} else {
252 			/* other errors ignored
253 			 * attempt to get nsid
254 			 */
255 			ret = -1;
256 			errno = EEXIST;
257 		}
258 	}
259 
260 	if (ret <= 0) {
261 		if (errno != EEXIST && ret != 0) {
262 			flog_err(
263 				EC_LIB_SOCKET,
264 				"netlink( %u) recvfrom() error 2 when reading: %s",
265 				fd, safe_strerror(errno));
266 			close(sock);
267 			if (netnspath)
268 				close(fd);
269 			if (errno == ENOTSUP) {
270 				zlog_debug("NEWNSID locally generated");
271 				return zebra_ns_id_get_fallback(netnspath);
272 			}
273 			return NS_UNKNOWN;
274 		}
275 		/* message to send to netlink : GETNSID */
276 		memset(buf, 0, NETLINK_SOCKET_BUFFER_SIZE);
277 		nlh = initiate_nlh(buf, &seq, RTM_GETNSID);
278 		rt = (struct rtgenmsg *)(buf + nlh->nlmsg_len);
279 		nlh->nlmsg_len += NETLINK_ALIGN(sizeof(struct rtgenmsg));
280 		rt->rtgen_family = AF_UNSPEC;
281 
282 		nl_attr_put32(nlh, NETLINK_SOCKET_BUFFER_SIZE, NETNSA_FD, fd);
283 		nl_attr_put32(nlh, NETLINK_SOCKET_BUFFER_SIZE, NETNSA_NSID,
284 			      ns_id);
285 
286 		ret = send_receive(sock, nlh, seq, buf);
287 		if (ret < 0) {
288 			close(sock);
289 			if (netnspath)
290 				close(fd);
291 			return NS_UNKNOWN;
292 		}
293 		nlh = (struct nlmsghdr *)buf;
294 		len = ret;
295 		ret = 0;
296 		do {
297 			if (nlh->nlmsg_type >= NLMSG_MIN_TYPE) {
298 				return_nsid = extract_nsid(nlh, buf);
299 				if (return_nsid != NS_UNKNOWN)
300 					break;
301 			} else if (nlh->nlmsg_type == NLMSG_ERROR) {
302 				struct nlmsgerr *err =
303 					(struct nlmsgerr
304 						 *)((char *)nlh
305 						    + NETLINK_ALIGN(sizeof(
306 							      struct
307 							      nlmsghdr)));
308 				if (err->error < 0)
309 					errno = -err->error;
310 				else
311 					errno = err->error;
312 				break;
313 			}
314 			len = len - NETLINK_ALIGN(nlh->nlmsg_len);
315 			nlh = (struct nlmsghdr *)((char *)nlh
316 						  + NETLINK_ALIGN(
317 							    nlh->nlmsg_len));
318 		} while (len != 0 && ret == 0);
319 	}
320 
321 	if (netnspath)
322 		close(fd);
323 	close(sock);
324 	return return_nsid;
325 }
326 
327 #else
zebra_ns_id_get(const char * netnspath,int fd)328 ns_id_t zebra_ns_id_get(const char *netnspath, int fd __attribute__ ((unused)))
329 {
330 	return zebra_ns_id_get_fallback(netnspath);
331 }
332 
333 #endif /* ! defined(HAVE_NETLINK) */
334 
335 #ifdef HAVE_NETNS
zebra_ns_create_netns_directory(void)336 static void zebra_ns_create_netns_directory(void)
337 {
338 	/* check that /var/run/netns is created */
339 	/* S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH */
340 	if (mkdir(NS_RUN_DIR, 0755)) {
341 		if (errno != EEXIST) {
342 			flog_warn(EC_ZEBRA_NAMESPACE_DIR_INACCESSIBLE,
343 				  "NS check: failed to access %s", NS_RUN_DIR);
344 			return;
345 		}
346 	}
347 }
348 #endif
349 
zebra_ns_id_get_default(void)350 ns_id_t zebra_ns_id_get_default(void)
351 {
352 #ifdef HAVE_NETNS
353 	int fd;
354 #endif /* !HAVE_NETNS */
355 
356 #ifdef HAVE_NETNS
357 	if (vrf_is_backend_netns())
358 		zebra_ns_create_netns_directory();
359 	fd = open(NS_DEFAULT_NAME, O_RDONLY);
360 
361 	if (fd == -1)
362 		return NS_DEFAULT;
363 	if (!vrf_is_backend_netns()) {
364 		close(fd);
365 		return NS_DEFAULT;
366 	}
367 	close(fd);
368 	return zebra_ns_id_get((char *)NS_DEFAULT_NAME, -1);
369 #else  /* HAVE_NETNS */
370 	return NS_DEFAULT;
371 #endif /* !HAVE_NETNS */
372 }
373