]> git.proxmox.com Git - mirror_lxc.git/blob - src/lxc/nl.c
nl: improve how we surface errors
[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 size_t nlmsg_len(const struct nlmsg *nlmsg)
23 {
24 return nlmsg->nlmsghdr->nlmsg_len - NLMSG_HDRLEN;
25 }
26
27 void *nlmsg_data(struct nlmsg *nlmsg)
28 {
29 char *data;
30
31 data = ((char *)nlmsg) + NLMSG_HDRLEN;
32 if (!nlmsg_len(nlmsg))
33 return ret_set_errno(NULL, EINVAL);
34
35 return data;
36 }
37
38 static int nla_put(struct nlmsg *nlmsg, int attr,
39 const void *data, size_t len)
40 {
41 struct rtattr *rta;
42 size_t rtalen = RTA_LENGTH(len);
43 size_t tlen = NLMSG_ALIGN(nlmsg->nlmsghdr->nlmsg_len) + RTA_ALIGN(rtalen);
44
45 if (tlen > nlmsg->cap)
46 return ret_errno(ENOMEM);
47
48 rta = NLMSG_TAIL(nlmsg->nlmsghdr);
49 rta->rta_type = attr;
50 rta->rta_len = rtalen;
51 if (data && len)
52 memcpy(RTA_DATA(rta), data, len);
53 nlmsg->nlmsghdr->nlmsg_len = tlen;
54
55 return 0;
56 }
57
58 int nla_put_buffer(struct nlmsg *nlmsg, int attr, const void *data, size_t size)
59 {
60 return nla_put(nlmsg, attr, data, size);
61 }
62
63 int nla_put_string(struct nlmsg *nlmsg, int attr, const char *string)
64 {
65 return nla_put(nlmsg, attr, string, strlen(string) + 1);
66 }
67
68 int nla_put_u32(struct nlmsg *nlmsg, int attr, int value)
69 {
70 return nla_put(nlmsg, attr, &value, sizeof(value));
71 }
72
73 int nla_put_u16(struct nlmsg *nlmsg, int attr, unsigned short value)
74 {
75 return nla_put(nlmsg, attr, &value, 2);
76 }
77
78 int nla_put_attr(struct nlmsg *nlmsg, int attr)
79 {
80 return nla_put(nlmsg, attr, NULL, 0);
81 }
82
83 struct rtattr *nla_begin_nested(struct nlmsg *nlmsg, int attr)
84 {
85 struct rtattr *rtattr;
86
87 rtattr = NLMSG_TAIL(nlmsg->nlmsghdr);
88 if (nla_put_attr(nlmsg, attr))
89 return ret_set_errno(NULL, ENOMEM);
90
91 return rtattr;
92 }
93
94 void nla_end_nested(struct nlmsg *nlmsg, struct rtattr *attr)
95 {
96 attr->rta_len = (void *)NLMSG_TAIL(nlmsg->nlmsghdr) - (void *)attr;
97 }
98
99 struct nlmsg *nlmsg_alloc(size_t size)
100 {
101 __do_free struct nlmsg *nlmsg = NULL;
102 size_t len = NLMSG_HDRLEN + NLMSG_ALIGN(size);
103
104 nlmsg = malloc(sizeof(struct nlmsg));
105 if (!nlmsg)
106 return ret_set_errno(NULL, ENOMEM);
107
108 nlmsg->nlmsghdr = malloc(len);
109 if (!nlmsg->nlmsghdr)
110 return ret_set_errno(NULL, ENOMEM);
111
112 memset(nlmsg->nlmsghdr, 0, len);
113 nlmsg->cap = len;
114 nlmsg->nlmsghdr->nlmsg_len = NLMSG_HDRLEN;
115
116 return move_ptr(nlmsg);
117 }
118
119 void *nlmsg_reserve(struct nlmsg *nlmsg, size_t len)
120 {
121 void *buf;
122 size_t nlmsg_len = nlmsg->nlmsghdr->nlmsg_len;
123 size_t tlen = NLMSG_ALIGN(len);
124
125 if (nlmsg_len + tlen > nlmsg->cap)
126 return ret_set_errno(NULL, ENOMEM);
127
128 buf = ((char *)(nlmsg->nlmsghdr)) + nlmsg_len;
129 nlmsg->nlmsghdr->nlmsg_len += tlen;
130
131 if (tlen > len)
132 memset(buf + len, 0, tlen - len);
133
134 return buf;
135 }
136
137 struct nlmsg *nlmsg_alloc_reserve(size_t size)
138 {
139 struct nlmsg *nlmsg;
140
141 nlmsg = nlmsg_alloc(size);
142 if (!nlmsg)
143 return ret_set_errno(NULL, ENOMEM);
144
145 /* Just set message length to cap directly. */
146 nlmsg->nlmsghdr->nlmsg_len = nlmsg->cap;
147 return nlmsg;
148 }
149
150 void nlmsg_free(struct nlmsg *nlmsg)
151 {
152 if (nlmsg) {
153 free(nlmsg->nlmsghdr);
154 free(nlmsg);
155 }
156 }
157
158 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 ret_errno(errno);
186 }
187
188 if (!ret)
189 return 0;
190
191 if (msg.msg_flags & MSG_TRUNC && (ret == nlmsghdr->nlmsg_len))
192 return ret_errno(EMSGSIZE);
193
194 return ret;
195 }
196
197 int netlink_rcv(struct nl_handler *handler, struct nlmsg *answer)
198 {
199 return __netlink_recv(handler, answer->nlmsghdr);
200 }
201
202 int __netlink_send(struct nl_handler *handler, struct nlmsghdr *nlmsghdr)
203 {
204 int ret;
205 struct sockaddr_nl nladdr;
206 struct iovec iov = {
207 .iov_base = nlmsghdr,
208 .iov_len = nlmsghdr->nlmsg_len,
209 };
210 struct msghdr msg = {
211 .msg_name = &nladdr,
212 .msg_namelen = sizeof(nladdr),
213 .msg_iov = &iov,
214 .msg_iovlen = 1,
215 };
216
217 memset(&nladdr, 0, sizeof(nladdr));
218 nladdr.nl_family = AF_NETLINK;
219 nladdr.nl_pid = 0;
220 nladdr.nl_groups = 0;
221
222 ret = sendmsg(handler->fd, &msg, MSG_NOSIGNAL);
223 if (ret < 0)
224 return ret_errno(errno);
225
226 return ret;
227 }
228
229 extern int netlink_send(struct nl_handler *handler, struct nlmsg *nlmsg)
230 {
231 return __netlink_send(handler, nlmsg->nlmsghdr);
232 }
233
234 extern int __netlink_transaction(struct nl_handler *handler,
235 struct nlmsghdr *request,
236 struct nlmsghdr *answer)
237 {
238 int ret;
239
240 ret = __netlink_send(handler, request);
241 if (ret < 0)
242 return ret;
243
244 ret = __netlink_recv(handler, answer);
245 if (ret < 0)
246 return ret;
247
248 if (answer->nlmsg_type == NLMSG_ERROR) {
249 struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(answer);
250 if (err->error < 0)
251 return ret_errno(-err->error);
252 }
253
254 return 0;
255 }
256
257 extern int netlink_transaction(struct nl_handler *handler,
258 struct nlmsg *request, struct nlmsg *answer)
259 {
260 return __netlink_transaction(handler, request->nlmsghdr,
261 answer->nlmsghdr);
262 }
263
264 extern int netlink_open(struct nl_handler *handler, int protocol)
265 {
266 __do_close int fd = -EBADF;
267 socklen_t socklen;
268 int sndbuf = 32768;
269 int rcvbuf = 32768;
270
271 memset(handler, 0, sizeof(*handler));
272 handler->fd = -EBADF;
273
274 fd = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, protocol);
275 if (fd < 0)
276 return ret_errno(errno);
277
278 if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf)) < 0)
279 return ret_errno(errno);
280
281 if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf,sizeof(rcvbuf)) < 0)
282 return ret_errno(errno);
283
284 memset(&handler->local, 0, sizeof(handler->local));
285 handler->local.nl_family = AF_NETLINK;
286 handler->local.nl_groups = 0;
287
288 if (bind(fd, (struct sockaddr*)&handler->local, sizeof(handler->local)) < 0)
289 return ret_errno(errno);
290
291 socklen = sizeof(handler->local);
292 if (getsockname(fd, (struct sockaddr*)&handler->local, &socklen) < 0)
293 return ret_errno(errno);
294
295 if (socklen != sizeof(handler->local))
296 return ret_errno(EINVAL);
297
298 if (handler->local.nl_family != AF_NETLINK)
299 return ret_errno(EINVAL);
300
301 handler->seq = time(NULL);
302 handler->fd = move_fd(fd);
303 return 0;
304 }
305
306 extern void netlink_close(struct nl_handler *handler)
307 {
308 close_prot_errno_disarm(handler->fd);
309 }
310
311 int addattr(struct nlmsghdr *n, size_t maxlen, int type, const void *data,
312 size_t alen)
313 {
314 int len = RTA_LENGTH(alen);
315 struct rtattr *rta;
316
317 if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len) > maxlen)
318 return ret_errno(EMSGSIZE);
319
320 rta = NLMSG_TAIL(n);
321 rta->rta_type = type;
322 rta->rta_len = len;
323 if (alen)
324 memcpy(RTA_DATA(rta), data, alen);
325 n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len);
326
327 return 0;
328 }