]>
Commit | Line | Data |
---|---|---|
acddc0ed | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
05895ad0 PG |
2 | /* zebra NETNS ID handling routines |
3 | * those routines are implemented locally to avoid having external dependencies. | |
4 | * Copyright (C) 2018 6WIND | |
05895ad0 PG |
5 | */ |
6 | ||
7 | #include <zebra.h> | |
8 | ||
9 | #include "ns.h" | |
ec31f30d | 10 | #include "vrf.h" |
05895ad0 | 11 | #include "log.h" |
43e52561 | 12 | #include "lib_errors.h" |
fce7f209 | 13 | #include "network.h" |
05895ad0 | 14 | |
85a75f1e MS |
15 | #include "zebra/rib.h" |
16 | #include "zebra/zebra_dplane.h" | |
05895ad0 PG |
17 | #if defined(HAVE_NETLINK) |
18 | ||
19 | #include <linux/net_namespace.h> | |
20 | #include <linux/netlink.h> | |
21 | #include <linux/rtnetlink.h> | |
22 | ||
05895ad0 PG |
23 | #include "zebra_ns.h" |
24 | #include "kernel_netlink.h" | |
25 | #endif /* defined(HAVE_NETLINK) */ | |
26 | ||
43e52561 QY |
27 | #include "zebra/zebra_netns_id.h" |
28 | #include "zebra/zebra_errors.h" | |
05895ad0 PG |
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 | |
996c9314 LB |
38 | #define NETLINK_ALIGN(len) \ |
39 | (((len) + NETLINK_ALIGNTO - 1) & ~(NETLINK_ALIGNTO - 1)) | |
05895ad0 PG |
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; | |
fce7f209 | 64 | nlh->nlmsg_seq = *seq = frr_sequence32_next(); |
05895ad0 PG |
65 | return nlh; |
66 | } | |
67 | ||
996c9314 LB |
68 | static int send_receive(int sock, struct nlmsghdr *nlh, unsigned int seq, |
69 | char *buf) | |
05895ad0 PG |
70 | { |
71 | int ret; | |
996c9314 | 72 | static const struct sockaddr_nl snl = {.nl_family = AF_NETLINK}; |
05895ad0 PG |
73 | |
74 | ret = sendto(sock, (const void *)nlh, (size_t)nlh->nlmsg_len, 0, | |
996c9314 | 75 | (struct sockaddr *)&snl, (socklen_t)sizeof(snl)); |
05895ad0 | 76 | if (ret < 0) { |
450971aa | 77 | flog_err_sys(EC_LIB_SOCKET, "netlink( %u) sendmsg() error: %s", |
09c866e3 | 78 | sock, safe_strerror(errno)); |
05895ad0 PG |
79 | return -1; |
80 | } | |
81 | ||
82 | /* reception */ | |
83 | struct sockaddr_nl addr; | |
84 | struct iovec iov = { | |
996c9314 | 85 | .iov_base = buf, .iov_len = NETLINK_SOCKET_BUFFER_SIZE, |
05895ad0 PG |
86 | }; |
87 | struct msghdr msg = { | |
996c9314 LB |
88 | .msg_name = &addr, |
89 | .msg_namelen = sizeof(struct sockaddr_nl), | |
90 | .msg_iov = &iov, | |
91 | .msg_iovlen = 1, | |
92 | .msg_control = NULL, | |
05895ad0 | 93 | .msg_controllen = 0, |
996c9314 | 94 | .msg_flags = 0, |
05895ad0 PG |
95 | }; |
96 | ret = recvmsg(sock, &msg, 0); | |
97 | if (ret < 0) { | |
450971aa | 98 | flog_err_sys(EC_LIB_SOCKET, |
09c866e3 QY |
99 | "netlink recvmsg: error %d (errno %u)", ret, |
100 | errno); | |
05895ad0 PG |
101 | return -1; |
102 | } | |
103 | if (msg.msg_flags & MSG_TRUNC) { | |
e914ccbe | 104 | flog_err(EC_ZEBRA_NETLINK_LENGTH_ERROR, |
1c50c1c0 | 105 | "netlink recvmsg : error message truncated"); |
05895ad0 PG |
106 | return -1; |
107 | } | |
108 | /* nlh already points to buf */ | |
109 | if (nlh->nlmsg_seq != seq) { | |
af4c2728 | 110 | flog_err( |
e914ccbe | 111 | EC_ZEBRA_NETLINK_BAD_SEQUENCE, |
996c9314 LB |
112 | "netlink recvmsg: bad sequence number %x (expected %x)", |
113 | seq, nlh->nlmsg_seq); | |
05895ad0 PG |
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; | |
996c9314 LB |
125 | int offset = NETLINK_ALIGN(sizeof(struct nlmsghdr)) |
126 | + NETLINK_ALIGN(sizeof(struct rtgenmsg)); | |
05895ad0 PG |
127 | void *tail = (void *)((char *)nlh + NETLINK_ALIGN(nlh->nlmsg_len)); |
128 | struct nlattr *attr; | |
129 | ||
c4efd0f4 | 130 | for (attr = (struct nlattr *)(buf + offset); |
996c9314 LB |
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); | |
05895ad0 | 134 | attr += NETLINK_ALIGN(attr->nla_len)) { |
05895ad0 | 135 | if ((attr->nla_type & NLA_TYPE_MASK) == NETNSA_NSID) { |
d7c0a89a | 136 | uint32_t *ptr = (uint32_t *)(attr); |
05895ad0 PG |
137 | |
138 | ns_id = ptr[1]; | |
139 | break; | |
140 | } | |
141 | } | |
142 | return ns_id; | |
143 | } | |
144 | ||
289b0f0d PG |
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) | |
05895ad0 PG |
150 | { |
151 | int ns_id = -1; | |
152 | struct sockaddr_nl snl; | |
289b0f0d | 153 | int fd = -1, sock, ret; |
05895ad0 PG |
154 | unsigned int seq; |
155 | ns_id_t return_nsid = NS_UNKNOWN; | |
156 | ||
157 | /* netns path check */ | |
289b0f0d | 158 | if (!netnspath && fd_param == -1) |
bd23c840 | 159 | return NS_UNKNOWN; |
289b0f0d PG |
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; | |
05895ad0 PG |
166 | /* netlink socket */ |
167 | sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); | |
168 | if (sock < 0) { | |
450971aa | 169 | flog_err_sys(EC_LIB_SOCKET, "netlink( %u) socket() error: %s", |
09c866e3 | 170 | sock, safe_strerror(errno)); |
7529bf8f | 171 | if (netnspath) |
289b0f0d | 172 | close(fd); |
05895ad0 PG |
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) { | |
450971aa | 181 | flog_err_sys(EC_LIB_SOCKET, |
09c866e3 QY |
182 | "netlink( %u) socket() bind error: %s", sock, |
183 | safe_strerror(errno)); | |
05895ad0 | 184 | close(sock); |
7529bf8f | 185 | if (netnspath) |
289b0f0d | 186 | close(fd); |
05895ad0 PG |
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 | ||
312a6bee JU |
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); | |
05895ad0 PG |
204 | |
205 | ret = send_receive(sock, nlh, seq, buf); | |
206 | if (ret < 0) { | |
207 | close(sock); | |
7529bf8f | 208 | if (netnspath) |
289b0f0d | 209 | close(fd); |
05895ad0 PG |
210 | return NS_UNKNOWN; |
211 | } | |
212 | nlh = (struct nlmsghdr *)buf; | |
213 | ||
214 | /* message to analyse : NEWNSID response */ | |
05895ad0 | 215 | ret = 0; |
b5891e15 A |
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 | |
05895ad0 | 234 | */ |
05895ad0 | 235 | errno = EEXIST; |
05895ad0 | 236 | } |
b5891e15 A |
237 | } else { |
238 | /* other errors ignored | |
239 | * attempt to get nsid | |
240 | */ | |
241 | ret = -1; | |
242 | errno = EEXIST; | |
05895ad0 | 243 | } |
b5891e15 | 244 | } |
05895ad0 | 245 | |
87472c69 DS |
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); | |
05895ad0 | 256 | } |
87472c69 | 257 | return NS_UNKNOWN; |
05895ad0 | 258 | } |
87472c69 DS |
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); | |
05895ad0 | 299 | |
7529bf8f | 300 | if (netnspath) |
289b0f0d | 301 | close(fd); |
05895ad0 PG |
302 | close(sock); |
303 | return return_nsid; | |
304 | } | |
305 | ||
306 | #else | |
289b0f0d | 307 | ns_id_t zebra_ns_id_get(const char *netnspath, int fd __attribute__ ((unused))) |
05895ad0 PG |
308 | { |
309 | return zebra_ns_id_get_fallback(netnspath); | |
310 | } | |
2d4e4d39 | 311 | |
05895ad0 | 312 | #endif /* ! defined(HAVE_NETLINK) */ |
ec31f30d PG |
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) { | |
e914ccbe | 321 | flog_warn(EC_ZEBRA_NAMESPACE_DIR_INACCESSIBLE, |
9df414fe | 322 | "NS check: failed to access %s", NS_RUN_DIR); |
ec31f30d PG |
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) | |
1eb92f06 | 341 | return NS_DEFAULT; |
4307629f DS |
342 | if (!vrf_is_backend_netns()) { |
343 | close(fd); | |
1eb92f06 | 344 | return NS_DEFAULT; |
4307629f | 345 | } |
ec31f30d | 346 | close(fd); |
289b0f0d | 347 | return zebra_ns_id_get((char *)NS_DEFAULT_NAME, -1); |
996c9314 | 348 | #else /* HAVE_NETNS */ |
1eb92f06 | 349 | return NS_DEFAULT; |
ec31f30d PG |
350 | #endif /* !HAVE_NETNS */ |
351 | } |