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