]>
Commit | Line | Data |
---|---|---|
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 | 24 | #include "log.h" |
43e52561 | 25 | #include "lib_errors.h" |
05895ad0 PG |
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 | ||
43e52561 QY |
38 | #include "zebra/zebra_netns_id.h" |
39 | #include "zebra/zebra_errors.h" | |
05895ad0 | 40 | |
ec31f30d PG |
41 | /* default NS ID value used when VRF backend is not NETNS */ |
42 | #define NS_DEFAULT_INTERNAL 0 | |
43 | ||
05895ad0 PG |
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 | |
996c9314 LB |
52 | #define NETLINK_ALIGN(len) \ |
53 | (((len) + NETLINK_ALIGNTO - 1) & ~(NETLINK_ALIGNTO - 1)) | |
05895ad0 PG |
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 | ||
996c9314 LB |
82 | static int send_receive(int sock, struct nlmsghdr *nlh, unsigned int seq, |
83 | char *buf) | |
05895ad0 PG |
84 | { |
85 | int ret; | |
996c9314 | 86 | static const struct sockaddr_nl snl = {.nl_family = AF_NETLINK}; |
05895ad0 PG |
87 | |
88 | ret = sendto(sock, (const void *)nlh, (size_t)nlh->nlmsg_len, 0, | |
996c9314 | 89 | (struct sockaddr *)&snl, (socklen_t)sizeof(snl)); |
05895ad0 | 90 | if (ret < 0) { |
450971aa | 91 | flog_err_sys(EC_LIB_SOCKET, "netlink( %u) sendmsg() error: %s", |
09c866e3 | 92 | sock, safe_strerror(errno)); |
05895ad0 PG |
93 | return -1; |
94 | } | |
95 | ||
96 | /* reception */ | |
97 | struct sockaddr_nl addr; | |
98 | struct iovec iov = { | |
996c9314 | 99 | .iov_base = buf, .iov_len = NETLINK_SOCKET_BUFFER_SIZE, |
05895ad0 PG |
100 | }; |
101 | struct msghdr msg = { | |
996c9314 LB |
102 | .msg_name = &addr, |
103 | .msg_namelen = sizeof(struct sockaddr_nl), | |
104 | .msg_iov = &iov, | |
105 | .msg_iovlen = 1, | |
106 | .msg_control = NULL, | |
05895ad0 | 107 | .msg_controllen = 0, |
996c9314 | 108 | .msg_flags = 0, |
05895ad0 PG |
109 | }; |
110 | ret = recvmsg(sock, &msg, 0); | |
111 | if (ret < 0) { | |
450971aa | 112 | flog_err_sys(EC_LIB_SOCKET, |
09c866e3 QY |
113 | "netlink recvmsg: error %d (errno %u)", ret, |
114 | errno); | |
05895ad0 PG |
115 | return -1; |
116 | } | |
117 | if (msg.msg_flags & MSG_TRUNC) { | |
e914ccbe | 118 | flog_err(EC_ZEBRA_NETLINK_LENGTH_ERROR, |
1c50c1c0 | 119 | "netlink recvmsg : error message truncated"); |
05895ad0 PG |
120 | return -1; |
121 | } | |
122 | /* nlh already points to buf */ | |
123 | if (nlh->nlmsg_seq != seq) { | |
af4c2728 | 124 | flog_err( |
e914ccbe | 125 | EC_ZEBRA_NETLINK_BAD_SEQUENCE, |
996c9314 LB |
126 | "netlink recvmsg: bad sequence number %x (expected %x)", |
127 | seq, nlh->nlmsg_seq); | |
05895ad0 PG |
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; | |
996c9314 LB |
139 | int offset = NETLINK_ALIGN(sizeof(struct nlmsghdr)) |
140 | + NETLINK_ALIGN(sizeof(struct rtgenmsg)); | |
05895ad0 PG |
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); | |
996c9314 LB |
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); | |
05895ad0 PG |
149 | attr += NETLINK_ALIGN(attr->nla_len)) { |
150 | curr_length += attr->nla_len; | |
151 | if ((attr->nla_type & NLA_TYPE_MASK) == NETNSA_NSID) { | |
d7c0a89a | 152 | uint32_t *ptr = (uint32_t *)(attr); |
05895ad0 PG |
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) { | |
450971aa | 179 | flog_err_sys(EC_LIB_SOCKET, "netlink( %u) socket() error: %s", |
09c866e3 | 180 | sock, safe_strerror(errno)); |
af734bc7 | 181 | close(fd); |
05895ad0 PG |
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) { | |
450971aa | 190 | flog_err_sys(EC_LIB_SOCKET, |
09c866e3 QY |
191 | "netlink( %u) socket() bind error: %s", sock, |
192 | safe_strerror(errno)); | |
05895ad0 PG |
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) { | |
996c9314 LB |
231 | struct nlmsgerr *err = |
232 | (struct nlmsgerr | |
233 | *)((char *)nlh | |
234 | + NETLINK_ALIGN(sizeof( | |
235 | struct | |
236 | nlmsghdr))); | |
05895ad0 PG |
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); | |
996c9314 LB |
259 | nlh = (struct nlmsghdr *)((char *)nlh |
260 | + NETLINK_ALIGN(nlh->nlmsg_len)); | |
05895ad0 PG |
261 | } while (len != 0 && return_nsid != NS_UNKNOWN && ret == 0); |
262 | ||
263 | if (ret <= 0) { | |
264 | if (errno != EEXIST && ret != 0) { | |
af4c2728 | 265 | flog_err( |
450971aa | 266 | EC_LIB_SOCKET, |
996c9314 LB |
267 | "netlink( %u) recvfrom() error 2 when reading: %s", |
268 | fd, safe_strerror(errno)); | |
05895ad0 PG |
269 | close(sock); |
270 | close(fd); | |
271 | if (errno == ENOTSUP) { | |
9df414fe | 272 | zlog_debug("NEWNSID locally generated"); |
05895ad0 PG |
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) { | |
996c9314 LB |
302 | struct nlmsgerr *err = |
303 | (struct nlmsgerr | |
304 | *)((char *)nlh | |
305 | + NETLINK_ALIGN(sizeof( | |
306 | struct | |
307 | nlmsghdr))); | |
05895ad0 PG |
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); | |
996c9314 LB |
315 | nlh = (struct nlmsghdr *)((char *)nlh |
316 | + NETLINK_ALIGN( | |
317 | nlh->nlmsg_len)); | |
0cfbff74 | 318 | } while (len != 0 && ret == 0); |
05895ad0 PG |
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) */ | |
ec31f30d PG |
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) { | |
e914ccbe | 340 | flog_warn(EC_ZEBRA_NAMESPACE_DIR_INACCESSIBLE, |
9df414fe | 341 | "NS check: failed to access %s", NS_RUN_DIR); |
ec31f30d PG |
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; | |
4307629f DS |
361 | if (!vrf_is_backend_netns()) { |
362 | close(fd); | |
ec31f30d | 363 | return NS_DEFAULT_INTERNAL; |
4307629f | 364 | } |
ec31f30d PG |
365 | close(fd); |
366 | return zebra_ns_id_get((char *)NS_DEFAULT_NAME); | |
996c9314 | 367 | #else /* HAVE_NETNS */ |
ec31f30d PG |
368 | return NS_DEFAULT_INTERNAL; |
369 | #endif /* !HAVE_NETNS */ | |
370 | } |