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