1 /* zebra NETNS ID handling routines
2 * those routines are implemented locally to avoid having external dependencies.
3 * Copyright (C) 2018 6WIND
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)
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
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
25 #include "lib_errors.h"
27 #if defined(HAVE_NETLINK)
29 #include <linux/net_namespace.h>
30 #include <linux/netlink.h>
31 #include <linux/rtnetlink.h>
35 #include "kernel_netlink.h"
36 #endif /* defined(HAVE_NETLINK) */
38 #include "zebra/zebra_netns_id.h"
39 #include "zebra/zebra_errors.h"
41 /* default NS ID value used when VRF backend is not NETNS */
42 #define NS_DEFAULT_INTERNAL 0
44 /* in case NEWNSID not available, the NSID will be locally obtained
46 #define NS_BASE_NSID 0
48 #if defined(HAVE_NETLINK)
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)
56 #endif /* defined(HAVE_NETLINK) */
58 static ns_id_t
zebra_ns_id_get_fallback(const char *netnspath
)
60 static int zebra_ns_id_local
;
62 return zebra_ns_id_local
++;
65 #if defined(HAVE_NETLINK)
67 static struct nlmsghdr
*initiate_nlh(char *buf
, unsigned int *seq
, int type
)
71 nlh
= (struct nlmsghdr
*)buf
;
72 nlh
->nlmsg_len
= NETLINK_ALIGN(sizeof(struct nlmsghdr
));
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
);
82 static int send_receive(int sock
, struct nlmsghdr
*nlh
, unsigned int seq
,
86 static const struct sockaddr_nl snl
= {.nl_family
= AF_NETLINK
};
88 ret
= sendto(sock
, (const void *)nlh
, (size_t)nlh
->nlmsg_len
, 0,
89 (struct sockaddr
*)&snl
, (socklen_t
)sizeof(snl
));
91 flog_err_sys(EC_LIB_SOCKET
, "netlink( %u) sendmsg() error: %s",
92 sock
, safe_strerror(errno
));
97 struct sockaddr_nl addr
;
99 .iov_base
= buf
, .iov_len
= NETLINK_SOCKET_BUFFER_SIZE
,
101 struct msghdr msg
= {
103 .msg_namelen
= sizeof(struct sockaddr_nl
),
110 ret
= recvmsg(sock
, &msg
, 0);
112 flog_err_sys(EC_LIB_SOCKET
,
113 "netlink recvmsg: error %d (errno %u)", ret
,
117 if (msg
.msg_flags
& MSG_TRUNC
) {
118 flog_err(EC_ZEBRA_NETLINK_LENGTH_ERROR
,
119 "netlink recvmsg : error message truncated");
122 /* nlh already points to buf */
123 if (nlh
->nlmsg_seq
!= seq
) {
125 EC_ZEBRA_NETLINK_BAD_SEQUENCE
,
126 "netlink recvmsg: bad sequence number %x (expected %x)",
127 seq
, nlh
->nlmsg_seq
);
133 /* extract on a valid nlmsg the nsid
134 * valid nlmsghdr - not a nlmsgerr
136 static ns_id_t
extract_nsid(struct nlmsghdr
*nlh
, char *buf
)
138 ns_id_t ns_id
= NS_UNKNOWN
;
139 int offset
= NETLINK_ALIGN(sizeof(struct nlmsghdr
))
140 + NETLINK_ALIGN(sizeof(struct rtgenmsg
));
141 int curr_length
= offset
;
142 void *tail
= (void *)((char *)nlh
+ NETLINK_ALIGN(nlh
->nlmsg_len
));
145 for (attr
= (struct nlattr
*)((char *)buf
+ offset
);
146 NETLINK_NLATTR_LEN(tail
, attr
) >= sizeof(struct nlattr
)
147 && attr
->nla_len
>= sizeof(struct nlattr
)
148 && attr
->nla_len
<= NETLINK_NLATTR_LEN(tail
, attr
);
149 attr
+= NETLINK_ALIGN(attr
->nla_len
)) {
150 curr_length
+= attr
->nla_len
;
151 if ((attr
->nla_type
& NLA_TYPE_MASK
) == NETNSA_NSID
) {
152 uint32_t *ptr
= (uint32_t *)(attr
);
161 ns_id_t
zebra_ns_id_get(const char *netnspath
)
164 struct sockaddr_nl snl
;
167 ns_id_t return_nsid
= NS_UNKNOWN
;
169 /* netns path check */
172 fd
= open(netnspath
, O_RDONLY
);
177 sock
= socket(AF_NETLINK
, SOCK_RAW
, NETLINK_ROUTE
);
179 flog_err_sys(EC_LIB_SOCKET
, "netlink( %u) socket() error: %s",
180 sock
, safe_strerror(errno
));
184 memset(&snl
, 0, sizeof(snl
));
185 snl
.nl_family
= AF_NETLINK
;
186 snl
.nl_groups
= RTNLGRP_NSID
;
187 snl
.nl_pid
= 0; /* AUTO PID */
188 ret
= bind(sock
, (struct sockaddr
*)&snl
, sizeof(snl
));
190 flog_err_sys(EC_LIB_SOCKET
,
191 "netlink( %u) socket() bind error: %s", sock
,
192 safe_strerror(errno
));
198 /* message to send to netlink,and response : NEWNSID */
199 char buf
[NETLINK_SOCKET_BUFFER_SIZE
];
200 struct nlmsghdr
*nlh
;
204 memset(buf
, 0, NETLINK_SOCKET_BUFFER_SIZE
);
205 nlh
= initiate_nlh(buf
, &seq
, RTM_NEWNSID
);
206 rt
= (struct rtgenmsg
*)(buf
+ nlh
->nlmsg_len
);
207 nlh
->nlmsg_len
+= NETLINK_ALIGN(sizeof(struct rtgenmsg
));
208 rt
->rtgen_family
= AF_UNSPEC
;
210 addattr32(nlh
, NETLINK_SOCKET_BUFFER_SIZE
, NETNSA_FD
, fd
);
211 addattr32(nlh
, NETLINK_SOCKET_BUFFER_SIZE
, NETNSA_NSID
, ns_id
);
213 ret
= send_receive(sock
, nlh
, seq
, buf
);
219 nlh
= (struct nlmsghdr
*)buf
;
221 /* message to analyse : NEWNSID response */
225 if (nlh
->nlmsg_type
>= NLMSG_MIN_TYPE
) {
226 return_nsid
= extract_nsid(nlh
, buf
);
227 if (return_nsid
!= NS_UNKNOWN
)
230 if (nlh
->nlmsg_type
== NLMSG_ERROR
) {
231 struct nlmsgerr
*err
=
234 + NETLINK_ALIGN(sizeof(
244 /* request NEWNSID was successfull
245 * return EEXIST error to get GETNSID
250 /* other errors ignored
251 * attempt to get nsid
258 len
= len
- NETLINK_ALIGN(nlh
->nlmsg_len
);
259 nlh
= (struct nlmsghdr
*)((char *)nlh
260 + NETLINK_ALIGN(nlh
->nlmsg_len
));
261 } while (len
!= 0 && return_nsid
!= NS_UNKNOWN
&& ret
== 0);
264 if (errno
!= EEXIST
&& ret
!= 0) {
267 "netlink( %u) recvfrom() error 2 when reading: %s",
268 fd
, safe_strerror(errno
));
271 if (errno
== ENOTSUP
) {
272 zlog_debug("NEWNSID locally generated");
273 return zebra_ns_id_get_fallback(netnspath
);
277 /* message to send to netlink : GETNSID */
278 memset(buf
, 0, NETLINK_SOCKET_BUFFER_SIZE
);
279 nlh
= initiate_nlh(buf
, &seq
, RTM_GETNSID
);
280 rt
= (struct rtgenmsg
*)(buf
+ nlh
->nlmsg_len
);
281 nlh
->nlmsg_len
+= NETLINK_ALIGN(sizeof(struct rtgenmsg
));
282 rt
->rtgen_family
= AF_UNSPEC
;
284 addattr32(nlh
, NETLINK_SOCKET_BUFFER_SIZE
, NETNSA_FD
, fd
);
285 addattr32(nlh
, NETLINK_SOCKET_BUFFER_SIZE
, NETNSA_NSID
, ns_id
);
287 ret
= send_receive(sock
, nlh
, seq
, buf
);
293 nlh
= (struct nlmsghdr
*)buf
;
297 if (nlh
->nlmsg_type
>= NLMSG_MIN_TYPE
) {
298 return_nsid
= extract_nsid(nlh
, buf
);
299 if (return_nsid
!= NS_UNKNOWN
)
301 } else if (nlh
->nlmsg_type
== NLMSG_ERROR
) {
302 struct nlmsgerr
*err
=
305 + NETLINK_ALIGN(sizeof(
314 len
= len
- NETLINK_ALIGN(nlh
->nlmsg_len
);
315 nlh
= (struct nlmsghdr
*)((char *)nlh
318 } while (len
!= 0 && ret
== 0);
327 ns_id_t
zebra_ns_id_get(const char *netnspath
)
329 return zebra_ns_id_get_fallback(netnspath
);
331 #endif /* ! defined(HAVE_NETLINK) */
334 static void zebra_ns_create_netns_directory(void)
336 /* check that /var/run/netns is created */
337 /* S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH */
338 if (mkdir(NS_RUN_DIR
, 0755)) {
339 if (errno
!= EEXIST
) {
340 flog_warn(EC_ZEBRA_NAMESPACE_DIR_INACCESSIBLE
,
341 "NS check: failed to access %s", NS_RUN_DIR
);
348 ns_id_t
zebra_ns_id_get_default(void)
352 #endif /* !HAVE_NETNS */
355 if (vrf_is_backend_netns())
356 zebra_ns_create_netns_directory();
357 fd
= open(NS_DEFAULT_NAME
, O_RDONLY
);
360 return NS_DEFAULT_INTERNAL
;
361 if (!vrf_is_backend_netns()) {
363 return NS_DEFAULT_INTERNAL
;
366 return zebra_ns_id_get((char *)NS_DEFAULT_NAME
);
367 #else /* HAVE_NETNS */
368 return NS_DEFAULT_INTERNAL
;
369 #endif /* !HAVE_NETNS */