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