]> git.proxmox.com Git - mirror_lxc.git/blame - src/lxc/nl.c
Merge pull request #3956 from brauner/2021-08-27.list
[mirror_lxc.git] / src / lxc / nl.c
CommitLineData
cc73685d 1/* SPDX-License-Identifier: LGPL-2.1+ */
bfcedc7e 2
d38dd64a
CB
3#ifndef _GNU_SOURCE
4#define _GNU_SOURCE 1
5#endif
0ad19a3f 6#include <errno.h>
0ad19a3f 7#include <linux/netlink.h>
8#include <linux/rtnetlink.h>
d38dd64a
CB
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>
f549edcc 15
d38dd64a 16#include "config.h"
bfcedc7e 17#include "log.h"
f549edcc 18#include "nl.h"
0ad19a3f 19
bfcedc7e
CB
20lxc_log_define(nl, lxc);
21
6822ba9b 22static size_t nlmsg_len(const struct nlmsg *nlmsg)
0ad19a3f 23{
06f976ca 24 return nlmsg->nlmsghdr->nlmsg_len - NLMSG_HDRLEN;
0ad19a3f 25}
26
19bfd55a 27void *nlmsg_data(struct nlmsg *nlmsg)
0ad19a3f 28{
19bfd55a
CB
29 char *data;
30
31 data = ((char *)nlmsg) + NLMSG_HDRLEN;
0ad19a3f 32 if (!nlmsg_len(nlmsg))
19bfd55a
CB
33 return ret_set_errno(NULL, EINVAL);
34
0ad19a3f 35 return data;
36}
37
f79d43bb 38static int nla_put(struct nlmsg *nlmsg, int attr,
0ad19a3f 39 const void *data, size_t len)
40{
41 struct rtattr *rta;
42 size_t rtalen = RTA_LENGTH(len);
06f976ca 43 size_t tlen = NLMSG_ALIGN(nlmsg->nlmsghdr->nlmsg_len) + RTA_ALIGN(rtalen);
84760c11 44
06f976ca 45 if (tlen > nlmsg->cap)
19bfd55a 46 return ret_errno(ENOMEM);
06f976ca
SZ
47
48 rta = NLMSG_TAIL(nlmsg->nlmsghdr);
49 rta->rta_type = attr;
50 rta->rta_len = rtalen;
c8f05589
RM
51 if (data && len)
52 memcpy(RTA_DATA(rta), data, len);
06f976ca 53 nlmsg->nlmsghdr->nlmsg_len = tlen;
19bfd55a 54
0ad19a3f 55 return 0;
56}
57
19bfd55a 58int nla_put_buffer(struct nlmsg *nlmsg, int attr, const void *data, size_t size)
0ad19a3f 59{
60 return nla_put(nlmsg, attr, data, size);
61}
62
19bfd55a 63int nla_put_string(struct nlmsg *nlmsg, int attr, const char *string)
0ad19a3f 64{
d028235d 65 return nla_put(nlmsg, attr, string, strlen(string) + 1);
0ad19a3f 66}
67
19bfd55a 68int nla_put_u32(struct nlmsg *nlmsg, int attr, int value)
0ad19a3f 69{
70 return nla_put(nlmsg, attr, &value, sizeof(value));
71}
72
19bfd55a 73int nla_put_u16(struct nlmsg *nlmsg, int attr, unsigned short value)
9ddaf3bf
JHS
74{
75 return nla_put(nlmsg, attr, &value, 2);
76}
77
19bfd55a 78int nla_put_attr(struct nlmsg *nlmsg, int attr)
0ad19a3f 79{
80 return nla_put(nlmsg, attr, NULL, 0);
81}
82
83struct rtattr *nla_begin_nested(struct nlmsg *nlmsg, int attr)
84{
19bfd55a 85 struct rtattr *rtattr;
0ad19a3f 86
19bfd55a 87 rtattr = NLMSG_TAIL(nlmsg->nlmsghdr);
0ad19a3f 88 if (nla_put_attr(nlmsg, attr))
19bfd55a 89 return ret_set_errno(NULL, ENOMEM);
0ad19a3f 90
91 return rtattr;
92}
93
94void nla_end_nested(struct nlmsg *nlmsg, struct rtattr *attr)
95{
06f976ca 96 attr->rta_len = (void *)NLMSG_TAIL(nlmsg->nlmsghdr) - (void *)attr;
0ad19a3f 97}
98
19bfd55a 99struct nlmsg *nlmsg_alloc(size_t size)
0ad19a3f 100{
19bfd55a 101 __do_free struct nlmsg *nlmsg = NULL;
b5887164 102 size_t len = NLMSG_HDRLEN + NLMSG_ALIGN(size);
0ad19a3f 103
19bfd55a 104 nlmsg = malloc(sizeof(struct nlmsg));
0ad19a3f 105 if (!nlmsg)
19bfd55a 106 return ret_set_errno(NULL, ENOMEM);
0ad19a3f 107
19bfd55a 108 nlmsg->nlmsghdr = malloc(len);
3d88831c 109 if (!nlmsg->nlmsghdr)
19bfd55a 110 return ret_set_errno(NULL, ENOMEM);
06f976ca
SZ
111
112 memset(nlmsg->nlmsghdr, 0, len);
113 nlmsg->cap = len;
114 nlmsg->nlmsghdr->nlmsg_len = NLMSG_HDRLEN;
0ad19a3f 115
19bfd55a 116 return move_ptr(nlmsg);
06f976ca
SZ
117}
118
19bfd55a 119void *nlmsg_reserve(struct nlmsg *nlmsg, size_t len)
06f976ca
SZ
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)
19bfd55a 126 return ret_set_errno(NULL, ENOMEM);
06f976ca
SZ
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
19bfd55a 137struct nlmsg *nlmsg_alloc_reserve(size_t size)
06f976ca
SZ
138{
139 struct nlmsg *nlmsg;
140
141 nlmsg = nlmsg_alloc(size);
142 if (!nlmsg)
19bfd55a 143 return ret_set_errno(NULL, ENOMEM);
06f976ca 144
1a0e70ac 145 /* Just set message length to cap directly. */
06f976ca 146 nlmsg->nlmsghdr->nlmsg_len = nlmsg->cap;
0ad19a3f 147 return nlmsg;
148}
149
19bfd55a 150void nlmsg_free(struct nlmsg *nlmsg)
0ad19a3f 151{
19bfd55a
CB
152 if (nlmsg) {
153 free(nlmsg->nlmsghdr);
154 free(nlmsg);
155 }
0ad19a3f 156}
157
19bfd55a 158int __netlink_recv(struct nl_handler *handler, struct nlmsghdr *nlmsghdr)
0ad19a3f 159{
160 int ret;
d028235d
SG
161 struct sockaddr_nl nladdr;
162 struct iovec iov = {
9fbbc427
CB
163 .iov_base = nlmsghdr,
164 .iov_len = nlmsghdr->nlmsg_len,
d028235d 165 };
1a0e70ac 166
0ad19a3f 167 struct msghdr msg = {
9fbbc427
CB
168 .msg_name = &nladdr,
169 .msg_namelen = sizeof(nladdr),
170 .msg_iov = &iov,
171 .msg_iovlen = 1,
d028235d 172 };
1a0e70ac 173
d028235d
SG
174 memset(&nladdr, 0, sizeof(nladdr));
175 nladdr.nl_family = AF_NETLINK;
176 nladdr.nl_pid = 0;
177 nladdr.nl_groups = 0;
0ad19a3f 178
179again:
180 ret = recvmsg(handler->fd, &msg, 0);
181 if (ret < 0) {
182 if (errno == EINTR)
183 goto again;
9fbbc427 184
19bfd55a 185 return ret_errno(errno);
0ad19a3f 186 }
187
188 if (!ret)
189 return 0;
190
19bfd55a
CB
191 if (msg.msg_flags & MSG_TRUNC && (ret == nlmsghdr->nlmsg_len))
192 return ret_errno(EMSGSIZE);
0ad19a3f 193
194 return ret;
195}
196
19bfd55a 197int netlink_rcv(struct nl_handler *handler, struct nlmsg *answer)
9fbbc427
CB
198{
199 return __netlink_recv(handler, answer->nlmsghdr);
200}
201
19bfd55a 202int __netlink_send(struct nl_handler *handler, struct nlmsghdr *nlmsghdr)
0ad19a3f 203{
9fbbc427 204 int ret;
d028235d
SG
205 struct sockaddr_nl nladdr;
206 struct iovec iov = {
9fbbc427
CB
207 .iov_base = nlmsghdr,
208 .iov_len = nlmsghdr->nlmsg_len,
d028235d 209 };
0ad19a3f 210 struct msghdr msg = {
9fbbc427
CB
211 .msg_name = &nladdr,
212 .msg_namelen = sizeof(nladdr),
213 .msg_iov = &iov,
214 .msg_iovlen = 1,
d028235d 215 };
1a0e70ac 216
d028235d
SG
217 memset(&nladdr, 0, sizeof(nladdr));
218 nladdr.nl_family = AF_NETLINK;
219 nladdr.nl_pid = 0;
220 nladdr.nl_groups = 0;
0ad19a3f 221
57b1ab60 222 ret = sendmsg(handler->fd, &msg, MSG_NOSIGNAL);
e239ff31 223 if (ret < 0)
19bfd55a 224 return ret_errno(errno);
0ad19a3f 225
226 return ret;
227}
228
9fbbc427
CB
229extern int netlink_send(struct nl_handler *handler, struct nlmsg *nlmsg)
230{
231 return __netlink_send(handler, nlmsg->nlmsghdr);
232}
233
234extern int __netlink_transaction(struct nl_handler *handler,
235 struct nlmsghdr *request,
236 struct nlmsghdr *answer)
0ad19a3f 237{
0ad19a3f 238 int ret;
239
9fbbc427 240 ret = __netlink_send(handler, request);
0ad19a3f 241 if (ret < 0)
19bfd55a 242 return ret;
0ad19a3f 243
9fbbc427 244 ret = __netlink_recv(handler, answer);
0ad19a3f 245 if (ret < 0)
19bfd55a 246 return ret;
0ad19a3f 247
9fbbc427
CB
248 if (answer->nlmsg_type == NLMSG_ERROR) {
249 struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(answer);
bfcedc7e 250 if (err->error < 0)
19bfd55a 251 return ret_errno(-err->error);
0ad19a3f 252 }
e239ff31 253
19bfd55a 254 return 0;
0ad19a3f 255}
256
9fbbc427
CB
257extern 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
0ad19a3f 264extern int netlink_open(struct nl_handler *handler, int protocol)
265{
19bfd55a 266 __do_close int fd = -EBADF;
0ad19a3f 267 socklen_t socklen;
d028235d
SG
268 int sndbuf = 32768;
269 int rcvbuf = 32768;
0ad19a3f 270
d028235d 271 memset(handler, 0, sizeof(*handler));
19bfd55a 272 handler->fd = -EBADF;
0ad19a3f 273
19bfd55a
CB
274 fd = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, protocol);
275 if (fd < 0)
276 return ret_errno(errno);
0ad19a3f 277
19bfd55a
CB
278 if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf)) < 0)
279 return ret_errno(errno);
0ad19a3f 280
19bfd55a
CB
281 if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf,sizeof(rcvbuf)) < 0)
282 return ret_errno(errno);
0ad19a3f 283
d028235d
SG
284 memset(&handler->local, 0, sizeof(handler->local));
285 handler->local.nl_family = AF_NETLINK;
286 handler->local.nl_groups = 0;
0ad19a3f 287
19bfd55a
CB
288 if (bind(fd, (struct sockaddr*)&handler->local, sizeof(handler->local)) < 0)
289 return ret_errno(errno);
0ad19a3f 290
d028235d 291 socklen = sizeof(handler->local);
19bfd55a
CB
292 if (getsockname(fd, (struct sockaddr*)&handler->local, &socklen) < 0)
293 return ret_errno(errno);
0ad19a3f 294
19bfd55a
CB
295 if (socklen != sizeof(handler->local))
296 return ret_errno(EINVAL);
0ad19a3f 297
19bfd55a
CB
298 if (handler->local.nl_family != AF_NETLINK)
299 return ret_errno(EINVAL);
0ad19a3f 300
301 handler->seq = time(NULL);
19bfd55a 302 handler->fd = move_fd(fd);
d028235d 303 return 0;
0ad19a3f 304}
305
d16bda44 306extern void netlink_close(struct nl_handler *handler)
0ad19a3f 307{
d16bda44 308 close_prot_errno_disarm(handler->fd);
0ad19a3f 309}
310
cc6119a0
CB
311int 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
cc6119a0 317 if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len) > maxlen)
19bfd55a 318 return ret_errno(EMSGSIZE);
cc6119a0
CB
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}