]> git.proxmox.com Git - mirror_lxc.git/blob - src/lxc/nl.c
Merge pull request #3235 from xinhua9569/master
[mirror_lxc.git] / src / lxc / nl.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #ifndef _GNU_SOURCE
4 #define _GNU_SOURCE 1
5 #endif
6 #include <errno.h>
7 #include <linux/netlink.h>
8 #include <linux/rtnetlink.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <sys/socket.h>
13 #include <time.h>
14 #include <unistd.h>
15
16 #include "config.h"
17 #include "log.h"
18 #include "nl.h"
19
20 lxc_log_define(nl, lxc);
21
22 extern size_t nlmsg_len(const struct nlmsg *nlmsg)
23 {
24 return nlmsg->nlmsghdr->nlmsg_len - NLMSG_HDRLEN;
25 }
26
27 extern void *nlmsg_data(struct nlmsg *nlmsg)
28 {
29 char *data = ((char *)nlmsg) + NLMSG_HDRLEN;
30 if (!nlmsg_len(nlmsg))
31 return NULL;
32 return data;
33 }
34
35 static int nla_put(struct nlmsg *nlmsg, int attr,
36 const void *data, size_t len)
37 {
38 struct rtattr *rta;
39 size_t rtalen = RTA_LENGTH(len);
40 size_t tlen = NLMSG_ALIGN(nlmsg->nlmsghdr->nlmsg_len) + RTA_ALIGN(rtalen);
41
42 if (tlen > nlmsg->cap)
43 return -ENOMEM;
44
45 rta = NLMSG_TAIL(nlmsg->nlmsghdr);
46 rta->rta_type = attr;
47 rta->rta_len = rtalen;
48 if (data && len)
49 memcpy(RTA_DATA(rta), data, len);
50 nlmsg->nlmsghdr->nlmsg_len = tlen;
51 return 0;
52 }
53
54 extern int nla_put_buffer(struct nlmsg *nlmsg, int attr,
55 const void *data, size_t size)
56 {
57 return nla_put(nlmsg, attr, data, size);
58 }
59
60 extern int nla_put_string(struct nlmsg *nlmsg, int attr, const char *string)
61 {
62 return nla_put(nlmsg, attr, string, strlen(string) + 1);
63 }
64
65 extern int nla_put_u32(struct nlmsg *nlmsg, int attr, int value)
66 {
67 return nla_put(nlmsg, attr, &value, sizeof(value));
68 }
69
70 extern int nla_put_u16(struct nlmsg *nlmsg, int attr, unsigned short value)
71 {
72 return nla_put(nlmsg, attr, &value, 2);
73 }
74
75 extern int nla_put_attr(struct nlmsg *nlmsg, int attr)
76 {
77 return nla_put(nlmsg, attr, NULL, 0);
78 }
79
80 struct rtattr *nla_begin_nested(struct nlmsg *nlmsg, int attr)
81 {
82 struct rtattr *rtattr = NLMSG_TAIL(nlmsg->nlmsghdr);
83
84 if (nla_put_attr(nlmsg, attr))
85 return NULL;
86
87 return rtattr;
88 }
89
90 void nla_end_nested(struct nlmsg *nlmsg, struct rtattr *attr)
91 {
92 attr->rta_len = (void *)NLMSG_TAIL(nlmsg->nlmsghdr) - (void *)attr;
93 }
94
95 extern struct nlmsg *nlmsg_alloc(size_t size)
96 {
97 struct nlmsg *nlmsg;
98 size_t len = NLMSG_HDRLEN + NLMSG_ALIGN(size);
99
100 nlmsg = (struct nlmsg *)malloc(sizeof(struct nlmsg));
101 if (!nlmsg)
102 return NULL;
103
104 nlmsg->nlmsghdr = (struct nlmsghdr *)malloc(len);
105 if (!nlmsg->nlmsghdr)
106 goto errout;
107
108 memset(nlmsg->nlmsghdr, 0, len);
109 nlmsg->cap = len;
110 nlmsg->nlmsghdr->nlmsg_len = NLMSG_HDRLEN;
111
112 return nlmsg;
113 errout:
114 free(nlmsg);
115 return NULL;
116 }
117
118 extern void *nlmsg_reserve(struct nlmsg *nlmsg, size_t len)
119 {
120 void *buf;
121 size_t nlmsg_len = nlmsg->nlmsghdr->nlmsg_len;
122 size_t tlen = NLMSG_ALIGN(len);
123
124 if (nlmsg_len + tlen > nlmsg->cap)
125 return NULL;
126
127 buf = ((char *)(nlmsg->nlmsghdr)) + nlmsg_len;
128 nlmsg->nlmsghdr->nlmsg_len += tlen;
129
130 if (tlen > len)
131 memset(buf + len, 0, tlen - len);
132
133 return buf;
134 }
135
136 extern struct nlmsg *nlmsg_alloc_reserve(size_t size)
137 {
138 struct nlmsg *nlmsg;
139
140 nlmsg = nlmsg_alloc(size);
141 if (!nlmsg)
142 return NULL;
143
144 /* Just set message length to cap directly. */
145 nlmsg->nlmsghdr->nlmsg_len = nlmsg->cap;
146 return nlmsg;
147 }
148
149 extern void nlmsg_free(struct nlmsg *nlmsg)
150 {
151 if (!nlmsg)
152 return;
153
154 free(nlmsg->nlmsghdr);
155 free(nlmsg);
156 }
157
158 extern int __netlink_recv(struct nl_handler *handler, struct nlmsghdr *nlmsghdr)
159 {
160 int ret;
161 struct sockaddr_nl nladdr;
162 struct iovec iov = {
163 .iov_base = nlmsghdr,
164 .iov_len = nlmsghdr->nlmsg_len,
165 };
166
167 struct msghdr msg = {
168 .msg_name = &nladdr,
169 .msg_namelen = sizeof(nladdr),
170 .msg_iov = &iov,
171 .msg_iovlen = 1,
172 };
173
174 memset(&nladdr, 0, sizeof(nladdr));
175 nladdr.nl_family = AF_NETLINK;
176 nladdr.nl_pid = 0;
177 nladdr.nl_groups = 0;
178
179 again:
180 ret = recvmsg(handler->fd, &msg, 0);
181 if (ret < 0) {
182 if (errno == EINTR)
183 goto again;
184
185 return -1;
186 }
187
188 if (!ret)
189 return 0;
190
191 if (msg.msg_flags & MSG_TRUNC && (ret == nlmsghdr->nlmsg_len)) {
192 errno = EMSGSIZE;
193 ret = -1;
194 }
195
196 return ret;
197 }
198
199 extern int netlink_rcv(struct nl_handler *handler, struct nlmsg *answer)
200 {
201 return __netlink_recv(handler, answer->nlmsghdr);
202 }
203
204 extern int __netlink_send(struct nl_handler *handler, struct nlmsghdr *nlmsghdr)
205 {
206 int ret;
207 struct sockaddr_nl nladdr;
208 struct iovec iov = {
209 .iov_base = nlmsghdr,
210 .iov_len = nlmsghdr->nlmsg_len,
211 };
212 struct msghdr msg = {
213 .msg_name = &nladdr,
214 .msg_namelen = sizeof(nladdr),
215 .msg_iov = &iov,
216 .msg_iovlen = 1,
217 };
218
219 memset(&nladdr, 0, sizeof(nladdr));
220 nladdr.nl_family = AF_NETLINK;
221 nladdr.nl_pid = 0;
222 nladdr.nl_groups = 0;
223
224 ret = sendmsg(handler->fd, &msg, MSG_NOSIGNAL);
225 if (ret < 0)
226 return -1;
227
228 return ret;
229 }
230
231 extern int netlink_send(struct nl_handler *handler, struct nlmsg *nlmsg)
232 {
233 return __netlink_send(handler, nlmsg->nlmsghdr);
234 }
235
236 extern int __netlink_transaction(struct nl_handler *handler,
237 struct nlmsghdr *request,
238 struct nlmsghdr *answer)
239 {
240 int ret;
241
242 ret = __netlink_send(handler, request);
243 if (ret < 0)
244 return -1;
245
246 ret = __netlink_recv(handler, answer);
247 if (ret < 0)
248 return -1;
249
250 ret = 0;
251 if (answer->nlmsg_type == NLMSG_ERROR) {
252 struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(answer);
253 errno = -err->error;
254 if (err->error < 0)
255 ret = -1;
256 }
257
258 return ret;
259 }
260
261 extern int netlink_transaction(struct nl_handler *handler,
262 struct nlmsg *request, struct nlmsg *answer)
263 {
264 return __netlink_transaction(handler, request->nlmsghdr,
265 answer->nlmsghdr);
266 }
267
268 extern int netlink_open(struct nl_handler *handler, int protocol)
269 {
270 socklen_t socklen;
271 int sndbuf = 32768;
272 int rcvbuf = 32768;
273 int err;
274
275 memset(handler, 0, sizeof(*handler));
276
277 handler->fd = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, protocol);
278 if (handler->fd < 0)
279 return -errno;
280
281 if (setsockopt(handler->fd, SOL_SOCKET, SO_SNDBUF,
282 &sndbuf, sizeof(sndbuf)) < 0)
283 goto err_with_errno;
284
285 if (setsockopt(handler->fd, SOL_SOCKET, SO_RCVBUF,
286 &rcvbuf,sizeof(rcvbuf)) < 0)
287 goto err_with_errno;
288
289 memset(&handler->local, 0, sizeof(handler->local));
290 handler->local.nl_family = AF_NETLINK;
291 handler->local.nl_groups = 0;
292
293 if (bind(handler->fd, (struct sockaddr*)&handler->local,
294 sizeof(handler->local)) < 0)
295 goto err_with_errno;
296
297 socklen = sizeof(handler->local);
298 if (getsockname(handler->fd, (struct sockaddr*)&handler->local,
299 &socklen) < 0)
300 goto err_with_errno;
301
302 if (socklen != sizeof(handler->local)) {
303 err = -EINVAL;
304 goto errclose;
305 }
306
307 if (handler->local.nl_family != AF_NETLINK) {
308 err = -EINVAL;
309 goto errclose;
310 }
311
312 handler->seq = time(NULL);
313
314 return 0;
315 err_with_errno:
316 err = -errno;
317 errclose:
318 close(handler->fd);
319 return err;
320 }
321
322 extern int netlink_close(struct nl_handler *handler)
323 {
324 close(handler->fd);
325 handler->fd = -1;
326 return 0;
327 }
328
329 int addattr(struct nlmsghdr *n, size_t maxlen, int type, const void *data,
330 size_t alen)
331 {
332 int len = RTA_LENGTH(alen);
333 struct rtattr *rta;
334
335 errno = EMSGSIZE;
336 if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len) > maxlen)
337 return -1;
338
339 rta = NLMSG_TAIL(n);
340 rta->rta_type = type;
341 rta->rta_len = len;
342 if (alen)
343 memcpy(RTA_DATA(rta), data, alen);
344 n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len);
345
346 return 0;
347 }