]> git.proxmox.com Git - mirror_frr.git/blob - zebra/zebra_netns_id.c
zebra: upon NS creation, collect the NSID via netlink
[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 "log.h"
24
25 #if defined(HAVE_NETLINK)
26
27 #include <linux/net_namespace.h>
28 #include <linux/netlink.h>
29 #include <linux/rtnetlink.h>
30
31 #include "rib.h"
32 #include "zebra_ns.h"
33 #include "kernel_netlink.h"
34 #endif /* defined(HAVE_NETLINK) */
35
36 #include "zebra_netns_id.h"
37
38 /* in case NEWNSID not available, the NSID will be locally obtained
39 */
40 #define NS_BASE_NSID 0
41
42 #if defined(HAVE_NETLINK)
43
44 #define NETLINK_SOCKET_BUFFER_SIZE 512
45 #define NETLINK_ALIGNTO 4
46 #define NETLINK_ALIGN(len) (((len)+NETLINK_ALIGNTO-1) \
47 & ~(NETLINK_ALIGNTO-1))
48 #define NETLINK_NLATTR_LEN(_a, _b) (unsigned int)((char *)_a - (char *)_b)
49
50 #endif /* defined(HAVE_NETLINK) */
51
52 static ns_id_t zebra_ns_id_get_fallback(const char *netnspath)
53 {
54 static int zebra_ns_id_local;
55
56 return zebra_ns_id_local++;
57 }
58
59 #if defined(HAVE_NETLINK)
60
61 static struct nlmsghdr *initiate_nlh(char *buf, unsigned int *seq, int type)
62 {
63 struct nlmsghdr *nlh;
64
65 nlh = (struct nlmsghdr *)buf;
66 nlh->nlmsg_len = NETLINK_ALIGN(sizeof(struct nlmsghdr));
67
68 nlh->nlmsg_type = type;
69 nlh->nlmsg_flags = NLM_F_REQUEST;
70 if (type == RTM_NEWNSID)
71 nlh->nlmsg_flags |= NLM_F_ACK;
72 nlh->nlmsg_seq = *seq = time(NULL);
73 return nlh;
74 }
75
76 static int send_receive(int sock, struct nlmsghdr *nlh,
77 unsigned int seq, char *buf)
78 {
79 int ret;
80 static const struct sockaddr_nl snl = {
81 .nl_family = AF_NETLINK
82 };
83
84 ret = sendto(sock, (const void *)nlh, (size_t)nlh->nlmsg_len, 0,
85 (struct sockaddr *) &snl, (socklen_t)sizeof(snl));
86 if (ret < 0) {
87 zlog_err("netlink( %u) sendmsg() error: %s",
88 sock, safe_strerror(errno));
89 return -1;
90 }
91
92 /* reception */
93 struct sockaddr_nl addr;
94 struct iovec iov = {
95 .iov_base = buf,
96 .iov_len = NETLINK_SOCKET_BUFFER_SIZE,
97 };
98 struct msghdr msg = {
99 .msg_name = &addr,
100 .msg_namelen = sizeof(struct sockaddr_nl),
101 .msg_iov = &iov,
102 .msg_iovlen = 1,
103 .msg_control = NULL,
104 .msg_controllen = 0,
105 .msg_flags = 0,
106 };
107 ret = recvmsg(sock, &msg, 0);
108 if (ret < 0) {
109 zlog_err("netlink recvmsg: error %d (errno %u)", ret, errno);
110 return -1;
111 }
112 if (msg.msg_flags & MSG_TRUNC) {
113 zlog_err("netlink recvmsg : error message truncated");
114 return -1;
115 }
116 /* nlh already points to buf */
117 if (nlh->nlmsg_seq != seq) {
118 zlog_err("netlink recvmsg: bad sequence number %x (expected %x)",
119 seq, nlh->nlmsg_seq);
120 return -1;
121 }
122 return ret;
123 }
124
125 /* extract on a valid nlmsg the nsid
126 * valid nlmsghdr - not a nlmsgerr
127 */
128 static ns_id_t extract_nsid(struct nlmsghdr *nlh, char *buf)
129 {
130 ns_id_t ns_id = NS_UNKNOWN;
131 int offset = NETLINK_ALIGN(sizeof(struct nlmsghdr)) +
132 NETLINK_ALIGN(sizeof(struct rtgenmsg));
133 int curr_length = offset;
134 void *tail = (void *)((char *)nlh + NETLINK_ALIGN(nlh->nlmsg_len));
135 struct nlattr *attr;
136
137 for (attr = (struct nlattr *)((char *)buf + offset);
138 NETLINK_NLATTR_LEN(tail, attr) >= sizeof(struct nlattr) &&
139 attr->nla_len >= sizeof(struct nlattr) &&
140 attr->nla_len <= NETLINK_NLATTR_LEN(tail, attr);
141 attr += NETLINK_ALIGN(attr->nla_len)) {
142 curr_length += attr->nla_len;
143 if ((attr->nla_type & NLA_TYPE_MASK) == NETNSA_NSID) {
144 u_int32_t *ptr = (u_int32_t *)(attr);
145
146 ns_id = ptr[1];
147 break;
148 }
149 }
150 return ns_id;
151 }
152
153 ns_id_t zebra_ns_id_get(const char *netnspath)
154 {
155 int ns_id = -1;
156 struct sockaddr_nl snl;
157 int fd, sock, ret;
158 unsigned int seq;
159 ns_id_t return_nsid = NS_UNKNOWN;
160
161 /* netns path check */
162 if (!netnspath)
163 return NS_UNKNOWN;
164 fd = open(netnspath, O_RDONLY);
165 if (fd == -1)
166 return NS_UNKNOWN;
167
168 /* netlink socket */
169 sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
170 if (sock < 0) {
171 zlog_err("netlink( %u) socket() error: %s",
172 sock, safe_strerror(errno));
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 zlog_err("netlink( %u) socket() bind error: %s",
182 sock, safe_strerror(errno));
183 close(sock);
184 close(fd);
185 return NS_UNKNOWN;
186 }
187
188 /* message to send to netlink,and response : NEWNSID */
189 char buf[NETLINK_SOCKET_BUFFER_SIZE];
190 struct nlmsghdr *nlh;
191 struct rtgenmsg *rt;
192 int len;
193
194 memset(buf, 0, NETLINK_SOCKET_BUFFER_SIZE);
195 nlh = initiate_nlh(buf, &seq, RTM_NEWNSID);
196 rt = (struct rtgenmsg *)(buf + nlh->nlmsg_len);
197 nlh->nlmsg_len += NETLINK_ALIGN(sizeof(struct rtgenmsg));
198 rt->rtgen_family = AF_UNSPEC;
199
200 addattr32(nlh, NETLINK_SOCKET_BUFFER_SIZE, NETNSA_FD, fd);
201 addattr32(nlh, NETLINK_SOCKET_BUFFER_SIZE, NETNSA_NSID, ns_id);
202
203 ret = send_receive(sock, nlh, seq, buf);
204 if (ret < 0) {
205 close(sock);
206 close(fd);
207 return NS_UNKNOWN;
208 }
209 nlh = (struct nlmsghdr *)buf;
210
211 /* message to analyse : NEWNSID response */
212 len = ret;
213 ret = 0;
214 do {
215 if (nlh->nlmsg_type >= NLMSG_MIN_TYPE) {
216 return_nsid = extract_nsid(nlh, buf);
217 if (return_nsid != NS_UNKNOWN)
218 break;
219 } else {
220 if (nlh->nlmsg_type == NLMSG_ERROR) {
221 struct nlmsgerr *err = (struct nlmsgerr *)
222 ((char *)nlh +
223 NETLINK_ALIGN(sizeof(
224 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 break;
244 }
245 }
246 len = len - NETLINK_ALIGN(nlh->nlmsg_len);
247 nlh = (struct nlmsghdr *)((char *)nlh +
248 NETLINK_ALIGN(nlh->nlmsg_len));
249 } while (len != 0 && return_nsid != NS_UNKNOWN && ret == 0);
250
251 if (ret <= 0) {
252 if (errno != EEXIST && ret != 0) {
253 zlog_err("netlink( %u) recvfrom() error 2 when reading: %s",
254 fd, safe_strerror(errno));
255 close(sock);
256 close(fd);
257 if (errno == ENOTSUP) {
258 zlog_warn("NEWNSID locally generated");
259 return zebra_ns_id_get_fallback(netnspath);
260 }
261 return NS_UNKNOWN;
262 }
263 /* message to send to netlink : GETNSID */
264 memset(buf, 0, NETLINK_SOCKET_BUFFER_SIZE);
265 nlh = initiate_nlh(buf, &seq, RTM_GETNSID);
266 rt = (struct rtgenmsg *)(buf + nlh->nlmsg_len);
267 nlh->nlmsg_len += NETLINK_ALIGN(sizeof(struct rtgenmsg));
268 rt->rtgen_family = AF_UNSPEC;
269
270 addattr32(nlh, NETLINK_SOCKET_BUFFER_SIZE, NETNSA_FD, fd);
271 addattr32(nlh, NETLINK_SOCKET_BUFFER_SIZE, NETNSA_NSID, ns_id);
272
273 ret = send_receive(sock, nlh, seq, buf);
274 if (ret < 0) {
275 close(sock);
276 close(fd);
277 return NS_UNKNOWN;
278 }
279 nlh = (struct nlmsghdr *)buf;
280 len = ret;
281 ret = 0;
282 do {
283 if (nlh->nlmsg_type >= NLMSG_MIN_TYPE) {
284 return_nsid = extract_nsid(nlh, buf);
285 if (return_nsid != NS_UNKNOWN)
286 break;
287 } else if (nlh->nlmsg_type == NLMSG_ERROR) {
288 struct nlmsgerr *err = (struct nlmsgerr *)
289 ((char *)nlh +
290 NETLINK_ALIGN(sizeof(
291 struct nlmsghdr)));
292 if (err->error < 0)
293 errno = -err->error;
294 else
295 errno = err->error;
296 break;
297 }
298 len = len - NETLINK_ALIGN(nlh->nlmsg_len);
299 nlh = (struct nlmsghdr *)((char *)nlh +
300 NETLINK_ALIGN(nlh->nlmsg_len));
301 } while (len != 0 && return_nsid != NS_UNKNOWN && ret == 0);
302 }
303
304 close(fd);
305 close(sock);
306 return return_nsid;
307 }
308
309 #else
310 ns_id_t zebra_ns_id_get(const char *netnspath)
311 {
312 return zebra_ns_id_get_fallback(netnspath);
313 }
314 #endif /* ! defined(HAVE_NETLINK) */