]> git.proxmox.com Git - mirror_frr.git/blob - zebra/zebra_netns_id.c
Merge pull request #2992 from opensourcerouting/large_as_path_fix
[mirror_frr.git] / zebra / zebra_netns_id.c
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"
23 #include "vrf.h"
24 #include "log.h"
25 #include "lib_errors.h"
26
27 #if defined(HAVE_NETLINK)
28
29 #include <linux/net_namespace.h>
30 #include <linux/netlink.h>
31 #include <linux/rtnetlink.h>
32
33 #include "rib.h"
34 #include "zebra_ns.h"
35 #include "kernel_netlink.h"
36 #endif /* defined(HAVE_NETLINK) */
37
38 #include "zebra/zebra_netns_id.h"
39 #include "zebra/zebra_errors.h"
40
41 /* default NS ID value used when VRF backend is not NETNS */
42 #define NS_DEFAULT_INTERNAL 0
43
44 /* in case NEWNSID not available, the NSID will be locally obtained
45 */
46 #define NS_BASE_NSID 0
47
48 #if defined(HAVE_NETLINK)
49
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)
55
56 #endif /* defined(HAVE_NETLINK) */
57
58 static ns_id_t zebra_ns_id_get_fallback(const char *netnspath)
59 {
60 static int zebra_ns_id_local;
61
62 return zebra_ns_id_local++;
63 }
64
65 #if defined(HAVE_NETLINK)
66
67 static struct nlmsghdr *initiate_nlh(char *buf, unsigned int *seq, int type)
68 {
69 struct nlmsghdr *nlh;
70
71 nlh = (struct nlmsghdr *)buf;
72 nlh->nlmsg_len = NETLINK_ALIGN(sizeof(struct nlmsghdr));
73
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);
79 return nlh;
80 }
81
82 static int send_receive(int sock, struct nlmsghdr *nlh, unsigned int seq,
83 char *buf)
84 {
85 int ret;
86 static const struct sockaddr_nl snl = {.nl_family = AF_NETLINK};
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 flog_err_sys(EC_LIB_SOCKET, "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, .iov_len = NETLINK_SOCKET_BUFFER_SIZE,
100 };
101 struct msghdr msg = {
102 .msg_name = &addr,
103 .msg_namelen = sizeof(struct sockaddr_nl),
104 .msg_iov = &iov,
105 .msg_iovlen = 1,
106 .msg_control = NULL,
107 .msg_controllen = 0,
108 .msg_flags = 0,
109 };
110 ret = recvmsg(sock, &msg, 0);
111 if (ret < 0) {
112 flog_err_sys(EC_LIB_SOCKET,
113 "netlink recvmsg: error %d (errno %u)", ret,
114 errno);
115 return -1;
116 }
117 if (msg.msg_flags & MSG_TRUNC) {
118 flog_err(EC_ZEBRA_NETLINK_LENGTH_ERROR,
119 "netlink recvmsg : error message truncated");
120 return -1;
121 }
122 /* nlh already points to buf */
123 if (nlh->nlmsg_seq != seq) {
124 flog_err(
125 EC_ZEBRA_NETLINK_BAD_SEQUENCE,
126 "netlink recvmsg: bad sequence number %x (expected %x)",
127 seq, nlh->nlmsg_seq);
128 return -1;
129 }
130 return ret;
131 }
132
133 /* extract on a valid nlmsg the nsid
134 * valid nlmsghdr - not a nlmsgerr
135 */
136 static ns_id_t extract_nsid(struct nlmsghdr *nlh, char *buf)
137 {
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));
143 struct nlattr *attr;
144
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);
153
154 ns_id = ptr[1];
155 break;
156 }
157 }
158 return ns_id;
159 }
160
161 ns_id_t zebra_ns_id_get(const char *netnspath)
162 {
163 int ns_id = -1;
164 struct sockaddr_nl snl;
165 int fd, sock, ret;
166 unsigned int seq;
167 ns_id_t return_nsid = NS_UNKNOWN;
168
169 /* netns path check */
170 if (!netnspath)
171 return NS_UNKNOWN;
172 fd = open(netnspath, O_RDONLY);
173 if (fd == -1)
174 return NS_UNKNOWN;
175
176 /* netlink socket */
177 sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
178 if (sock < 0) {
179 flog_err_sys(EC_LIB_SOCKET, "netlink( %u) socket() error: %s",
180 sock, safe_strerror(errno));
181 close(fd);
182 return NS_UNKNOWN;
183 }
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));
189 if (ret < 0) {
190 flog_err_sys(EC_LIB_SOCKET,
191 "netlink( %u) socket() bind error: %s", sock,
192 safe_strerror(errno));
193 close(sock);
194 close(fd);
195 return NS_UNKNOWN;
196 }
197
198 /* message to send to netlink,and response : NEWNSID */
199 char buf[NETLINK_SOCKET_BUFFER_SIZE];
200 struct nlmsghdr *nlh;
201 struct rtgenmsg *rt;
202 int len;
203
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;
209
210 addattr32(nlh, NETLINK_SOCKET_BUFFER_SIZE, NETNSA_FD, fd);
211 addattr32(nlh, NETLINK_SOCKET_BUFFER_SIZE, NETNSA_NSID, ns_id);
212
213 ret = send_receive(sock, nlh, seq, buf);
214 if (ret < 0) {
215 close(sock);
216 close(fd);
217 return NS_UNKNOWN;
218 }
219 nlh = (struct nlmsghdr *)buf;
220
221 /* message to analyse : NEWNSID response */
222 len = ret;
223 ret = 0;
224 do {
225 if (nlh->nlmsg_type >= NLMSG_MIN_TYPE) {
226 return_nsid = extract_nsid(nlh, buf);
227 if (return_nsid != NS_UNKNOWN)
228 break;
229 } else {
230 if (nlh->nlmsg_type == NLMSG_ERROR) {
231 struct nlmsgerr *err =
232 (struct nlmsgerr
233 *)((char *)nlh
234 + NETLINK_ALIGN(sizeof(
235 struct
236 nlmsghdr)));
237
238 ret = -1;
239 if (err->error < 0)
240 errno = -err->error;
241 else
242 errno = err->error;
243 if (errno == 0) {
244 /* request NEWNSID was successfull
245 * return EEXIST error to get GETNSID
246 */
247 errno = EEXIST;
248 }
249 } else {
250 /* other errors ignored
251 * attempt to get nsid
252 */
253 ret = -1;
254 errno = EEXIST;
255 break;
256 }
257 }
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);
262
263 if (ret <= 0) {
264 if (errno != EEXIST && ret != 0) {
265 flog_err(
266 EC_LIB_SOCKET,
267 "netlink( %u) recvfrom() error 2 when reading: %s",
268 fd, safe_strerror(errno));
269 close(sock);
270 close(fd);
271 if (errno == ENOTSUP) {
272 zlog_debug("NEWNSID locally generated");
273 return zebra_ns_id_get_fallback(netnspath);
274 }
275 return NS_UNKNOWN;
276 }
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;
283
284 addattr32(nlh, NETLINK_SOCKET_BUFFER_SIZE, NETNSA_FD, fd);
285 addattr32(nlh, NETLINK_SOCKET_BUFFER_SIZE, NETNSA_NSID, ns_id);
286
287 ret = send_receive(sock, nlh, seq, buf);
288 if (ret < 0) {
289 close(sock);
290 close(fd);
291 return NS_UNKNOWN;
292 }
293 nlh = (struct nlmsghdr *)buf;
294 len = ret;
295 ret = 0;
296 do {
297 if (nlh->nlmsg_type >= NLMSG_MIN_TYPE) {
298 return_nsid = extract_nsid(nlh, buf);
299 if (return_nsid != NS_UNKNOWN)
300 break;
301 } else if (nlh->nlmsg_type == NLMSG_ERROR) {
302 struct nlmsgerr *err =
303 (struct nlmsgerr
304 *)((char *)nlh
305 + NETLINK_ALIGN(sizeof(
306 struct
307 nlmsghdr)));
308 if (err->error < 0)
309 errno = -err->error;
310 else
311 errno = err->error;
312 break;
313 }
314 len = len - NETLINK_ALIGN(nlh->nlmsg_len);
315 nlh = (struct nlmsghdr *)((char *)nlh
316 + NETLINK_ALIGN(
317 nlh->nlmsg_len));
318 } while (len != 0 && ret == 0);
319 }
320
321 close(fd);
322 close(sock);
323 return return_nsid;
324 }
325
326 #else
327 ns_id_t zebra_ns_id_get(const char *netnspath)
328 {
329 return zebra_ns_id_get_fallback(netnspath);
330 }
331 #endif /* ! defined(HAVE_NETLINK) */
332
333 #ifdef HAVE_NETNS
334 static void zebra_ns_create_netns_directory(void)
335 {
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);
342 return;
343 }
344 }
345 }
346 #endif
347
348 ns_id_t zebra_ns_id_get_default(void)
349 {
350 #ifdef HAVE_NETNS
351 int fd;
352 #endif /* !HAVE_NETNS */
353
354 #ifdef HAVE_NETNS
355 if (vrf_is_backend_netns())
356 zebra_ns_create_netns_directory();
357 fd = open(NS_DEFAULT_NAME, O_RDONLY);
358
359 if (fd == -1)
360 return NS_DEFAULT_INTERNAL;
361 if (!vrf_is_backend_netns()) {
362 close(fd);
363 return NS_DEFAULT_INTERNAL;
364 }
365 close(fd);
366 return zebra_ns_id_get((char *)NS_DEFAULT_NAME);
367 #else /* HAVE_NETNS */
368 return NS_DEFAULT_INTERNAL;
369 #endif /* !HAVE_NETNS */
370 }