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