]> git.proxmox.com Git - mirror_frr.git/blame - zebra/zebra_netns_id.c
Merge pull request #3561 from opensourcerouting/northbound-freebsd-fix
[mirror_frr.git] / zebra / zebra_netns_id.c
CommitLineData
05895ad0
PG
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"
ec31f30d 23#include "vrf.h"
05895ad0 24#include "log.h"
43e52561 25#include "lib_errors.h"
05895ad0 26
85a75f1e
MS
27#include "zebra/rib.h"
28#include "zebra/zebra_dplane.h"
05895ad0
PG
29#if defined(HAVE_NETLINK)
30
31#include <linux/net_namespace.h>
32#include <linux/netlink.h>
33#include <linux/rtnetlink.h>
34
05895ad0
PG
35#include "zebra_ns.h"
36#include "kernel_netlink.h"
37#endif /* defined(HAVE_NETLINK) */
38
43e52561
QY
39#include "zebra/zebra_netns_id.h"
40#include "zebra/zebra_errors.h"
05895ad0 41
ec31f30d
PG
42/* default NS ID value used when VRF backend is not NETNS */
43#define NS_DEFAULT_INTERNAL 0
44
05895ad0
PG
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
996c9314
LB
53#define NETLINK_ALIGN(len) \
54 (((len) + NETLINK_ALIGNTO - 1) & ~(NETLINK_ALIGNTO - 1))
05895ad0
PG
55#define NETLINK_NLATTR_LEN(_a, _b) (unsigned int)((char *)_a - (char *)_b)
56
57#endif /* defined(HAVE_NETLINK) */
58
59static 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
68static 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
996c9314
LB
83static int send_receive(int sock, struct nlmsghdr *nlh, unsigned int seq,
84 char *buf)
05895ad0
PG
85{
86 int ret;
996c9314 87 static const struct sockaddr_nl snl = {.nl_family = AF_NETLINK};
05895ad0
PG
88
89 ret = sendto(sock, (const void *)nlh, (size_t)nlh->nlmsg_len, 0,
996c9314 90 (struct sockaddr *)&snl, (socklen_t)sizeof(snl));
05895ad0 91 if (ret < 0) {
450971aa 92 flog_err_sys(EC_LIB_SOCKET, "netlink( %u) sendmsg() error: %s",
09c866e3 93 sock, safe_strerror(errno));
05895ad0
PG
94 return -1;
95 }
96
97 /* reception */
98 struct sockaddr_nl addr;
99 struct iovec iov = {
996c9314 100 .iov_base = buf, .iov_len = NETLINK_SOCKET_BUFFER_SIZE,
05895ad0
PG
101 };
102 struct msghdr msg = {
996c9314
LB
103 .msg_name = &addr,
104 .msg_namelen = sizeof(struct sockaddr_nl),
105 .msg_iov = &iov,
106 .msg_iovlen = 1,
107 .msg_control = NULL,
05895ad0 108 .msg_controllen = 0,
996c9314 109 .msg_flags = 0,
05895ad0
PG
110 };
111 ret = recvmsg(sock, &msg, 0);
112 if (ret < 0) {
450971aa 113 flog_err_sys(EC_LIB_SOCKET,
09c866e3
QY
114 "netlink recvmsg: error %d (errno %u)", ret,
115 errno);
05895ad0
PG
116 return -1;
117 }
118 if (msg.msg_flags & MSG_TRUNC) {
e914ccbe 119 flog_err(EC_ZEBRA_NETLINK_LENGTH_ERROR,
1c50c1c0 120 "netlink recvmsg : error message truncated");
05895ad0
PG
121 return -1;
122 }
123 /* nlh already points to buf */
124 if (nlh->nlmsg_seq != seq) {
af4c2728 125 flog_err(
e914ccbe 126 EC_ZEBRA_NETLINK_BAD_SEQUENCE,
996c9314
LB
127 "netlink recvmsg: bad sequence number %x (expected %x)",
128 seq, nlh->nlmsg_seq);
05895ad0
PG
129 return -1;
130 }
131 return ret;
132}
133
134/* extract on a valid nlmsg the nsid
135 * valid nlmsghdr - not a nlmsgerr
136 */
137static ns_id_t extract_nsid(struct nlmsghdr *nlh, char *buf)
138{
139 ns_id_t ns_id = NS_UNKNOWN;
996c9314
LB
140 int offset = NETLINK_ALIGN(sizeof(struct nlmsghdr))
141 + NETLINK_ALIGN(sizeof(struct rtgenmsg));
05895ad0
PG
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);
996c9314
LB
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);
05895ad0
PG
150 attr += NETLINK_ALIGN(attr->nla_len)) {
151 curr_length += attr->nla_len;
152 if ((attr->nla_type & NLA_TYPE_MASK) == NETNSA_NSID) {
d7c0a89a 153 uint32_t *ptr = (uint32_t *)(attr);
05895ad0
PG
154
155 ns_id = ptr[1];
156 break;
157 }
158 }
159 return ns_id;
160}
161
162ns_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) {
450971aa 180 flog_err_sys(EC_LIB_SOCKET, "netlink( %u) socket() error: %s",
09c866e3 181 sock, safe_strerror(errno));
af734bc7 182 close(fd);
05895ad0
PG
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) {
450971aa 191 flog_err_sys(EC_LIB_SOCKET,
09c866e3
QY
192 "netlink( %u) socket() bind error: %s", sock,
193 safe_strerror(errno));
05895ad0
PG
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 */
05895ad0 223 ret = 0;
b5891e15
A
224 if (nlh->nlmsg_type >= NLMSG_MIN_TYPE) {
225 return_nsid = extract_nsid(nlh, buf);
226 } else {
227 if (nlh->nlmsg_type == NLMSG_ERROR) {
228 struct nlmsgerr *err =
229 (struct nlmsgerr
230 *)((char *)nlh
231 + NETLINK_ALIGN(
232 sizeof(struct nlmsghdr)));
233
234 ret = -1;
235 if (err->error < 0)
236 errno = -err->error;
237 else
238 errno = err->error;
239 if (errno == 0) {
240 /* request NEWNSID was successfull
241 * return EEXIST error to get GETNSID
05895ad0 242 */
05895ad0 243 errno = EEXIST;
05895ad0 244 }
b5891e15
A
245 } else {
246 /* other errors ignored
247 * attempt to get nsid
248 */
249 ret = -1;
250 errno = EEXIST;
05895ad0 251 }
b5891e15 252 }
05895ad0
PG
253
254 if (ret <= 0) {
255 if (errno != EEXIST && ret != 0) {
af4c2728 256 flog_err(
450971aa 257 EC_LIB_SOCKET,
996c9314
LB
258 "netlink( %u) recvfrom() error 2 when reading: %s",
259 fd, safe_strerror(errno));
05895ad0
PG
260 close(sock);
261 close(fd);
262 if (errno == ENOTSUP) {
9df414fe 263 zlog_debug("NEWNSID locally generated");
05895ad0
PG
264 return zebra_ns_id_get_fallback(netnspath);
265 }
266 return NS_UNKNOWN;
267 }
268 /* message to send to netlink : GETNSID */
269 memset(buf, 0, NETLINK_SOCKET_BUFFER_SIZE);
270 nlh = initiate_nlh(buf, &seq, RTM_GETNSID);
271 rt = (struct rtgenmsg *)(buf + nlh->nlmsg_len);
272 nlh->nlmsg_len += NETLINK_ALIGN(sizeof(struct rtgenmsg));
273 rt->rtgen_family = AF_UNSPEC;
274
275 addattr32(nlh, NETLINK_SOCKET_BUFFER_SIZE, NETNSA_FD, fd);
276 addattr32(nlh, NETLINK_SOCKET_BUFFER_SIZE, NETNSA_NSID, ns_id);
277
278 ret = send_receive(sock, nlh, seq, buf);
279 if (ret < 0) {
280 close(sock);
281 close(fd);
282 return NS_UNKNOWN;
283 }
284 nlh = (struct nlmsghdr *)buf;
285 len = ret;
286 ret = 0;
287 do {
288 if (nlh->nlmsg_type >= NLMSG_MIN_TYPE) {
289 return_nsid = extract_nsid(nlh, buf);
290 if (return_nsid != NS_UNKNOWN)
291 break;
292 } else if (nlh->nlmsg_type == NLMSG_ERROR) {
996c9314
LB
293 struct nlmsgerr *err =
294 (struct nlmsgerr
295 *)((char *)nlh
296 + NETLINK_ALIGN(sizeof(
297 struct
298 nlmsghdr)));
05895ad0
PG
299 if (err->error < 0)
300 errno = -err->error;
301 else
302 errno = err->error;
303 break;
304 }
305 len = len - NETLINK_ALIGN(nlh->nlmsg_len);
996c9314
LB
306 nlh = (struct nlmsghdr *)((char *)nlh
307 + NETLINK_ALIGN(
308 nlh->nlmsg_len));
0cfbff74 309 } while (len != 0 && ret == 0);
05895ad0
PG
310 }
311
312 close(fd);
313 close(sock);
314 return return_nsid;
315}
316
317#else
318ns_id_t zebra_ns_id_get(const char *netnspath)
319{
320 return zebra_ns_id_get_fallback(netnspath);
321}
322#endif /* ! defined(HAVE_NETLINK) */
ec31f30d
PG
323
324#ifdef HAVE_NETNS
325static void zebra_ns_create_netns_directory(void)
326{
327 /* check that /var/run/netns is created */
328 /* S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH */
329 if (mkdir(NS_RUN_DIR, 0755)) {
330 if (errno != EEXIST) {
e914ccbe 331 flog_warn(EC_ZEBRA_NAMESPACE_DIR_INACCESSIBLE,
9df414fe 332 "NS check: failed to access %s", NS_RUN_DIR);
ec31f30d
PG
333 return;
334 }
335 }
336}
337#endif
338
339ns_id_t zebra_ns_id_get_default(void)
340{
341#ifdef HAVE_NETNS
342 int fd;
343#endif /* !HAVE_NETNS */
344
345#ifdef HAVE_NETNS
346 if (vrf_is_backend_netns())
347 zebra_ns_create_netns_directory();
348 fd = open(NS_DEFAULT_NAME, O_RDONLY);
349
350 if (fd == -1)
351 return NS_DEFAULT_INTERNAL;
4307629f
DS
352 if (!vrf_is_backend_netns()) {
353 close(fd);
ec31f30d 354 return NS_DEFAULT_INTERNAL;
4307629f 355 }
ec31f30d
PG
356 close(fd);
357 return zebra_ns_id_get((char *)NS_DEFAULT_NAME);
996c9314 358#else /* HAVE_NETNS */
ec31f30d
PG
359 return NS_DEFAULT_INTERNAL;
360#endif /* !HAVE_NETNS */
361}