1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /* zebra NETNS ID handling routines
3 * those routines are implemented locally to avoid having external dependencies.
4 * Copyright (C) 2018 6WIND
12 #include "lib_errors.h"
15 #include "zebra/rib.h"
16 #include "zebra/zebra_dplane.h"
17 #if defined(HAVE_NETLINK)
19 #include <linux/net_namespace.h>
20 #include <linux/netlink.h>
21 #include <linux/rtnetlink.h>
24 #include "kernel_netlink.h"
25 #endif /* defined(HAVE_NETLINK) */
27 #include "zebra/zebra_netns_id.h"
28 #include "zebra/zebra_errors.h"
30 /* in case NEWNSID not available, the NSID will be locally obtained
32 #define NS_BASE_NSID 0
34 #if defined(HAVE_NETLINK)
36 #define NETLINK_SOCKET_BUFFER_SIZE 512
37 #define NETLINK_ALIGNTO 4
38 #define NETLINK_ALIGN(len) \
39 (((len) + NETLINK_ALIGNTO - 1) & ~(NETLINK_ALIGNTO - 1))
40 #define NETLINK_NLATTR_LEN(_a, _b) (unsigned int)((char *)_a - (char *)_b)
42 #endif /* defined(HAVE_NETLINK) */
44 static ns_id_t
zebra_ns_id_get_fallback(const char *netnspath
)
46 static int zebra_ns_id_local
;
48 return zebra_ns_id_local
++;
51 #if defined(HAVE_NETLINK)
53 static struct nlmsghdr
*initiate_nlh(char *buf
, unsigned int *seq
, int type
)
57 nlh
= (struct nlmsghdr
*)buf
;
58 nlh
->nlmsg_len
= NETLINK_ALIGN(sizeof(struct nlmsghdr
));
60 nlh
->nlmsg_type
= type
;
61 nlh
->nlmsg_flags
= NLM_F_REQUEST
;
62 if (type
== RTM_NEWNSID
)
63 nlh
->nlmsg_flags
|= NLM_F_ACK
;
64 nlh
->nlmsg_seq
= *seq
= frr_sequence32_next();
68 static int send_receive(int sock
, struct nlmsghdr
*nlh
, unsigned int seq
,
72 static const struct sockaddr_nl snl
= {.nl_family
= AF_NETLINK
};
74 ret
= sendto(sock
, (const void *)nlh
, (size_t)nlh
->nlmsg_len
, 0,
75 (struct sockaddr
*)&snl
, (socklen_t
)sizeof(snl
));
77 flog_err_sys(EC_LIB_SOCKET
, "netlink( %u) sendmsg() error: %s",
78 sock
, safe_strerror(errno
));
83 struct sockaddr_nl addr
;
85 .iov_base
= buf
, .iov_len
= NETLINK_SOCKET_BUFFER_SIZE
,
89 .msg_namelen
= sizeof(struct sockaddr_nl
),
96 ret
= recvmsg(sock
, &msg
, 0);
98 flog_err_sys(EC_LIB_SOCKET
,
99 "netlink recvmsg: error %d (errno %u)", ret
,
103 if (msg
.msg_flags
& MSG_TRUNC
) {
104 flog_err(EC_ZEBRA_NETLINK_LENGTH_ERROR
,
105 "netlink recvmsg : error message truncated");
108 /* nlh already points to buf */
109 if (nlh
->nlmsg_seq
!= seq
) {
111 EC_ZEBRA_NETLINK_BAD_SEQUENCE
,
112 "netlink recvmsg: bad sequence number %x (expected %x)",
113 seq
, nlh
->nlmsg_seq
);
119 /* extract on a valid nlmsg the nsid
120 * valid nlmsghdr - not a nlmsgerr
122 static ns_id_t
extract_nsid(struct nlmsghdr
*nlh
, char *buf
)
124 ns_id_t ns_id
= NS_UNKNOWN
;
125 int offset
= NETLINK_ALIGN(sizeof(struct nlmsghdr
))
126 + NETLINK_ALIGN(sizeof(struct rtgenmsg
));
127 void *tail
= (void *)((char *)nlh
+ NETLINK_ALIGN(nlh
->nlmsg_len
));
130 for (attr
= (struct nlattr
*)(buf
+ offset
);
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
);
134 attr
+= NETLINK_ALIGN(attr
->nla_len
)) {
135 if ((attr
->nla_type
& NLA_TYPE_MASK
) == NETNSA_NSID
) {
136 uint32_t *ptr
= (uint32_t *)(attr
);
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
149 ns_id_t
zebra_ns_id_get(const char *netnspath
, int fd_param
)
152 struct sockaddr_nl snl
;
153 int fd
= -1, sock
, ret
;
155 ns_id_t return_nsid
= NS_UNKNOWN
;
157 /* netns path check */
158 if (!netnspath
&& fd_param
== -1)
161 fd
= open(netnspath
, O_RDONLY
);
164 } else if (fd_param
!= -1)
167 sock
= socket(AF_NETLINK
, SOCK_RAW
, NETLINK_ROUTE
);
169 flog_err_sys(EC_LIB_SOCKET
, "netlink( %u) socket() error: %s",
170 sock
, safe_strerror(errno
));
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
));
181 flog_err_sys(EC_LIB_SOCKET
,
182 "netlink( %u) socket() bind error: %s", sock
,
183 safe_strerror(errno
));
190 /* message to send to netlink,and response : NEWNSID */
191 char buf
[NETLINK_SOCKET_BUFFER_SIZE
];
192 struct nlmsghdr
*nlh
;
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
;
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
);
205 ret
= send_receive(sock
, nlh
, seq
, buf
);
212 nlh
= (struct nlmsghdr
*)buf
;
214 /* message to analyse : NEWNSID response */
216 if (nlh
->nlmsg_type
>= NLMSG_MIN_TYPE
) {
217 return_nsid
= extract_nsid(nlh
, buf
);
219 if (nlh
->nlmsg_type
== NLMSG_ERROR
) {
220 struct nlmsgerr
*err
=
224 sizeof(struct nlmsghdr
)));
232 /* request NEWNSID was successfull
233 * return EEXIST error to get GETNSID
238 /* other errors ignored
239 * attempt to get nsid
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
));
253 if (errno
== ENOTSUP
) {
254 zlog_debug("NEWNSID locally generated");
255 return zebra_ns_id_get_fallback(netnspath
);
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
;
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
);
269 ret
= send_receive(sock
, nlh
, seq
, buf
);
276 nlh
= (struct nlmsghdr
*)buf
;
280 if (nlh
->nlmsg_type
>= NLMSG_MIN_TYPE
) {
281 return_nsid
= extract_nsid(nlh
, buf
);
282 if (return_nsid
!= NS_UNKNOWN
)
284 } else if (nlh
->nlmsg_type
== NLMSG_ERROR
) {
285 struct nlmsgerr
*err
=
286 (struct nlmsgerr
*)((char *)nlh
+
287 NETLINK_ALIGN(sizeof(
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);
307 ns_id_t
zebra_ns_id_get(const char *netnspath
, int fd
__attribute__ ((unused
)))
309 return zebra_ns_id_get_fallback(netnspath
);
312 #endif /* ! defined(HAVE_NETLINK) */
315 static void zebra_ns_create_netns_directory(void)
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
) {
321 flog_warn(EC_ZEBRA_NAMESPACE_DIR_INACCESSIBLE
,
322 "NS check: failed to access %s", NS_RUN_DIR
);
329 ns_id_t
zebra_ns_id_get_default(void)
333 #endif /* !HAVE_NETNS */
336 if (vrf_is_backend_netns())
337 zebra_ns_create_netns_directory();
338 fd
= open(NS_DEFAULT_NAME
, O_RDONLY
);
342 if (!vrf_is_backend_netns()) {
347 return zebra_ns_id_get((char *)NS_DEFAULT_NAME
, -1);
348 #else /* HAVE_NETNS */
350 #endif /* !HAVE_NETNS */