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