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