]> git.proxmox.com Git - mirror_frr.git/blob - zebra/zebra_netns_id.c
Merge pull request #12798 from donaldsharp/rib_match_multicast
[mirror_frr.git] / zebra / zebra_netns_id.c
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
5 */
6
7 #include <zebra.h>
8
9 #include "ns.h"
10 #include "vrf.h"
11 #include "log.h"
12 #include "lib_errors.h"
13 #include "network.h"
14
15 #include "zebra/rib.h"
16 #include "zebra/zebra_dplane.h"
17 #if defined(HAVE_NETLINK)
18
19 #include <linux/net_namespace.h>
20 #include <linux/netlink.h>
21 #include <linux/rtnetlink.h>
22
23 #include "zebra_ns.h"
24 #include "kernel_netlink.h"
25 #endif /* defined(HAVE_NETLINK) */
26
27 #include "zebra/zebra_netns_id.h"
28 #include "zebra/zebra_errors.h"
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
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)
41
42 #endif /* defined(HAVE_NETLINK) */
43
44 static 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
53 static 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;
64 nlh->nlmsg_seq = *seq = frr_sequence32_next();
65 return nlh;
66 }
67
68 static int send_receive(int sock, struct nlmsghdr *nlh, unsigned int seq,
69 char *buf)
70 {
71 int ret;
72 static const struct sockaddr_nl snl = {.nl_family = AF_NETLINK};
73
74 ret = sendto(sock, (const void *)nlh, (size_t)nlh->nlmsg_len, 0,
75 (struct sockaddr *)&snl, (socklen_t)sizeof(snl));
76 if (ret < 0) {
77 flog_err_sys(EC_LIB_SOCKET, "netlink( %u) sendmsg() error: %s",
78 sock, safe_strerror(errno));
79 return -1;
80 }
81
82 /* reception */
83 struct sockaddr_nl addr;
84 struct iovec iov = {
85 .iov_base = buf, .iov_len = NETLINK_SOCKET_BUFFER_SIZE,
86 };
87 struct msghdr msg = {
88 .msg_name = &addr,
89 .msg_namelen = sizeof(struct sockaddr_nl),
90 .msg_iov = &iov,
91 .msg_iovlen = 1,
92 .msg_control = NULL,
93 .msg_controllen = 0,
94 .msg_flags = 0,
95 };
96 ret = recvmsg(sock, &msg, 0);
97 if (ret < 0) {
98 flog_err_sys(EC_LIB_SOCKET,
99 "netlink recvmsg: error %d (errno %u)", ret,
100 errno);
101 return -1;
102 }
103 if (msg.msg_flags & MSG_TRUNC) {
104 flog_err(EC_ZEBRA_NETLINK_LENGTH_ERROR,
105 "netlink recvmsg : error message truncated");
106 return -1;
107 }
108 /* nlh already points to buf */
109 if (nlh->nlmsg_seq != seq) {
110 flog_err(
111 EC_ZEBRA_NETLINK_BAD_SEQUENCE,
112 "netlink recvmsg: bad sequence number %x (expected %x)",
113 seq, nlh->nlmsg_seq);
114 return -1;
115 }
116 return ret;
117 }
118
119 /* extract on a valid nlmsg the nsid
120 * valid nlmsghdr - not a nlmsgerr
121 */
122 static ns_id_t extract_nsid(struct nlmsghdr *nlh, char *buf)
123 {
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));
128 struct nlattr *attr;
129
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);
137
138 ns_id = ptr[1];
139 break;
140 }
141 }
142 return ns_id;
143 }
144
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 */
149 ns_id_t zebra_ns_id_get(const char *netnspath, int fd_param)
150 {
151 int ns_id = -1;
152 struct sockaddr_nl snl;
153 int fd = -1, sock, ret;
154 unsigned int seq;
155 ns_id_t return_nsid = NS_UNKNOWN;
156
157 /* netns path check */
158 if (!netnspath && fd_param == -1)
159 return NS_UNKNOWN;
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;
166 /* netlink socket */
167 sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
168 if (sock < 0) {
169 flog_err_sys(EC_LIB_SOCKET, "netlink( %u) socket() error: %s",
170 sock, safe_strerror(errno));
171 if (netnspath)
172 close(fd);
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) {
181 flog_err_sys(EC_LIB_SOCKET,
182 "netlink( %u) socket() bind error: %s", sock,
183 safe_strerror(errno));
184 close(sock);
185 if (netnspath)
186 close(fd);
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
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);
204
205 ret = send_receive(sock, nlh, seq, buf);
206 if (ret < 0) {
207 close(sock);
208 if (netnspath)
209 close(fd);
210 return NS_UNKNOWN;
211 }
212 nlh = (struct nlmsghdr *)buf;
213
214 /* message to analyse : NEWNSID response */
215 ret = 0;
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
234 */
235 errno = EEXIST;
236 }
237 } else {
238 /* other errors ignored
239 * attempt to get nsid
240 */
241 ret = -1;
242 errno = EEXIST;
243 }
244 }
245
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);
256 }
257 return NS_UNKNOWN;
258 }
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);
299
300 if (netnspath)
301 close(fd);
302 close(sock);
303 return return_nsid;
304 }
305
306 #else
307 ns_id_t zebra_ns_id_get(const char *netnspath, int fd __attribute__ ((unused)))
308 {
309 return zebra_ns_id_get_fallback(netnspath);
310 }
311
312 #endif /* ! defined(HAVE_NETLINK) */
313
314 #ifdef HAVE_NETNS
315 static 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) {
321 flog_warn(EC_ZEBRA_NAMESPACE_DIR_INACCESSIBLE,
322 "NS check: failed to access %s", NS_RUN_DIR);
323 return;
324 }
325 }
326 }
327 #endif
328
329 ns_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)
341 return NS_DEFAULT;
342 if (!vrf_is_backend_netns()) {
343 close(fd);
344 return NS_DEFAULT;
345 }
346 close(fd);
347 return zebra_ns_id_get((char *)NS_DEFAULT_NAME, -1);
348 #else /* HAVE_NETNS */
349 return NS_DEFAULT;
350 #endif /* !HAVE_NETNS */
351 }