]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd-rtnl/rtnl-message.c
test-rtnl: fix typo
[thirdparty/systemd.git] / src / libsystemd-rtnl / rtnl-message.c
CommitLineData
65f568bb
TG
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2013 Tom Gundersen <teg@jklm.no>
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
a33dece5 22#include <linux/rtnetlink.h>
65f568bb
TG
23#include <netinet/in.h>
24#include <netinet/ether.h>
25#include <stdbool.h>
26#include <unistd.h>
27
28#include "util.h"
29#include "refcnt.h"
30
31#include "sd-rtnl.h"
32#include "rtnl-internal.h"
33
34struct sd_rtnl_message {
35 RefCount n_ref;
36
37 struct nlmsghdr *hdr;
38
39 struct rtattr *next_rta;
40 size_t remaining_size;
41
42 bool sealed:1;
43};
44
45static int message_new(sd_rtnl_message **ret, size_t initial_size) {
46 sd_rtnl_message *m;
47
48 assert_return(ret, -EINVAL);
dabfa9d1 49 assert_return(initial_size >= sizeof(struct nlmsghdr), -EINVAL);
65f568bb
TG
50
51 m = new0(sd_rtnl_message, 1);
52 if (!m)
53 return -ENOMEM;
54
dc7d8997 55 m->hdr = malloc0(initial_size);
65f568bb
TG
56 if (!m->hdr) {
57 free(m);
58 return -ENOMEM;
59 }
60
61 m->n_ref = REFCNT_INIT;
62
63 m->hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
64 m->sealed = false;
65
66 *ret = m;
67
68 return 0;
69}
70
03d7e632
TG
71int sd_rtnl_message_route_new(uint16_t nlmsg_type, unsigned char rtm_family,
72 unsigned char rtm_dst_len, unsigned char rtm_src_len,
73 unsigned char rtm_tos, unsigned char rtm_table,
74 unsigned char rtm_scope, unsigned char rtm_protocol,
75 unsigned char rtm_type, unsigned rtm_flags, sd_rtnl_message **ret) {
76 struct rtmsg *rtm;
77 int r;
78
79 assert_return(nlmsg_type == RTM_NEWROUTE || nlmsg_type == RTM_DELROUTE ||
80 nlmsg_type == RTM_GETROUTE, -EINVAL);
81 assert_return(ret, -EINVAL);
82
83 r = message_new(ret, NLMSG_SPACE(sizeof(struct rtmsg)));
84 if (r < 0)
85 return r;
86
87 (*ret)->hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
88 (*ret)->hdr->nlmsg_type = nlmsg_type;
89 if (nlmsg_type == RTM_NEWROUTE)
90 (*ret)->hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_EXCL;
91
92 rtm = NLMSG_DATA((*ret)->hdr);
93
94 rtm->rtm_family = rtm_family;
95 rtm->rtm_dst_len = rtm_dst_len;
96 rtm->rtm_src_len = rtm_src_len;
97 rtm->rtm_tos = rtm_tos;
98 rtm->rtm_table = rtm_table;
99 rtm->rtm_protocol = rtm_protocol;
100 rtm->rtm_scope = rtm_scope;
101 rtm->rtm_type = rtm_type;
102 rtm->rtm_flags = rtm_flags;
103
104 return 0;
105}
106
dabfa9d1 107int sd_rtnl_message_link_new(uint16_t nlmsg_type, int index, unsigned int type, unsigned int flags, sd_rtnl_message **ret) {
65f568bb
TG
108 struct ifinfomsg *ifi;
109 int r;
110
111 assert_return(nlmsg_type == RTM_NEWLINK || nlmsg_type == RTM_DELLINK || nlmsg_type == RTM_GETLINK, -EINVAL);
112 assert_return(index > 0, -EINVAL);
113 assert_return(ret, -EINVAL);
114
115 r = message_new(ret, NLMSG_SPACE(sizeof(struct ifinfomsg)));
116 if (r < 0)
117 return r;
118
119 (*ret)->hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
120 (*ret)->hdr->nlmsg_type = nlmsg_type;
121
dabfa9d1 122 ifi = NLMSG_DATA((*ret)->hdr);
65f568bb
TG
123
124 ifi->ifi_family = AF_UNSPEC;
125 ifi->ifi_index = index;
126 ifi->ifi_type = type;
127 ifi->ifi_flags = flags;
128 ifi->ifi_change = 0xffffffff;
129
130 return 0;
131}
132
dabfa9d1 133int sd_rtnl_message_addr_new(uint16_t nlmsg_type, int index, unsigned char family, unsigned char prefixlen, unsigned char flags, unsigned char scope, sd_rtnl_message **ret) {
65f568bb
TG
134 struct ifaddrmsg *ifa;
135 int r;
136
137 assert_return(nlmsg_type == RTM_NEWADDR || nlmsg_type == RTM_DELADDR || nlmsg_type == RTM_GETADDR, -EINVAL);
138 assert_return(index > 0, -EINVAL);
139 assert_return(ret, -EINVAL);
140
141 r = message_new(ret, NLMSG_SPACE(sizeof(struct ifaddrmsg)));
142 if (r < 0)
143 return r;
144
145 (*ret)->hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
146 (*ret)->hdr->nlmsg_type = nlmsg_type;
147
dabfa9d1 148 ifa = NLMSG_DATA((*ret)->hdr);
65f568bb
TG
149
150 ifa->ifa_family = family;
151 ifa->ifa_prefixlen = prefixlen;
152 ifa->ifa_flags = flags;
153 ifa->ifa_scope = scope;
154 ifa->ifa_index = index;
155
156 return 0;
157}
158
159sd_rtnl_message *sd_rtnl_message_ref(sd_rtnl_message *m) {
160 if (m)
161 assert_se(REFCNT_INC(m->n_ref) >= 2);
162
163 return m;
164}
165
166sd_rtnl_message *sd_rtnl_message_unref(sd_rtnl_message *m) {
167 if (m && REFCNT_DEC(m->n_ref) <= 0) {
168 free(m->hdr);
169 free(m);
170 }
171
172 return NULL;
173}
174
dabfa9d1 175int sd_rtnl_message_get_type(sd_rtnl_message *m, uint16_t *type) {
65f568bb
TG
176 assert_return(m, -EINVAL);
177 assert_return(type, -EINVAL);
178
179 *type = m->hdr->nlmsg_type;
180
181 return 0;
182}
183
184/* If successful the updated message will be correctly aligned, if unsuccessful the old message is
185 untouched */
186static int add_rtattr(sd_rtnl_message *m, unsigned short type, const void *data, size_t data_length) {
dabfa9d1 187 uint32_t rta_length, message_length;
65f568bb
TG
188 struct nlmsghdr *new_hdr;
189 struct rtattr *rta;
190
191 assert_return(m, -EINVAL);
192 assert_return(m->hdr, -EINVAL);
193 assert_return(NLMSG_ALIGN(m->hdr->nlmsg_len) == m->hdr->nlmsg_len, -EINVAL);
194 assert_return(data, -EINVAL);
195 assert_return(data_length > 0, -EINVAL);
196
197 /* get the size of the new rta attribute (without padding at the end) */
198 rta_length = RTA_LENGTH(data_length);
199 /* get the new message size (with padding between the old message and the new attrib,
200 * but no padding after)
201 */
202 message_length = m->hdr->nlmsg_len + RTA_ALIGN(rta_length);
203
204 /* realloc to fit the new attribute */
205 new_hdr = realloc(m->hdr, message_length);
206 if (!new_hdr)
207 return -ENOMEM;
208 m->hdr = new_hdr;
209
210 /* get pointer to the attribute we are about to add */
211 rta = (struct rtattr *) ((uint8_t *) m->hdr + m->hdr->nlmsg_len);
212 /* update message size */
213 m->hdr->nlmsg_len = message_length;
214
215 /* fill in the attribute */
216 rta->rta_type = type;
217 rta->rta_len = rta_length;
218 /* we don't deal with the case where the user lies about the type and gives us
219 * too little data (so don't do that)
220 */
221 memcpy(RTA_DATA(rta), data, data_length);
222
223 return 0;
224}
225
226int sd_rtnl_message_append(sd_rtnl_message *m, unsigned short type, const void *data) {
dabfa9d1 227 uint16_t rtm_type;
65f568bb 228 struct ifaddrmsg *ifa;
03d7e632 229 struct rtmsg *rtm;
65f568bb
TG
230
231 assert_return(m, -EINVAL);
232 assert_return(data, -EINVAL);
233
234 sd_rtnl_message_get_type(m, &rtm_type);
235
236 switch (rtm_type) {
237 case RTM_NEWLINK:
238 case RTM_DELLINK:
239 case RTM_GETLINK:
240 switch (type) {
241 case IFLA_IFNAME:
242 case IFLA_QDISC:
243 return add_rtattr(m, type, data, strlen(data) + 1);
244 case IFLA_MTU:
03d7e632 245 return add_rtattr(m, type, data, sizeof(uint32_t));
65f568bb 246 case IFLA_LINK:
03d7e632 247 return add_rtattr(m, type, data, sizeof(uint32_t));
65f568bb
TG
248 case IFLA_STATS:
249 return add_rtattr(m, type, data, sizeof(struct rtnl_link_stats));
250 case IFLA_ADDRESS:
251 case IFLA_BROADCAST:
252 return add_rtattr(m, type, data, ETH_ALEN);
253 default:
254 return -ENOTSUP;
255 }
256 case RTM_NEWADDR:
257 case RTM_DELADDR:
258 case RTM_GETADDR:
259 switch (type) {
260 case IFA_LABEL:
261 return add_rtattr(m, type, data, strlen(data) + 1);
262 case IFA_ADDRESS:
263 case IFA_LOCAL:
264 case IFA_BROADCAST:
265 case IFA_ANYCAST:
266 ifa = NLMSG_DATA(m->hdr);
267 switch (ifa->ifa_family) {
268 case AF_INET:
269 return add_rtattr(m, type, data, sizeof(struct in_addr));
270 case AF_INET6:
271 return add_rtattr(m, type, data, sizeof(struct in6_addr));
272 default:
273 return -EINVAL;
274 }
275 default:
276 return -ENOTSUP;
277 }
03d7e632
TG
278 case RTM_NEWROUTE:
279 case RTM_DELROUTE:
280 case RTM_GETROUTE:
281 switch (type) {
282 case RTA_DST:
283 case RTA_SRC:
284 case RTA_GATEWAY:
285 rtm = NLMSG_DATA(m->hdr);
286 switch (rtm->rtm_family) {
287 case AF_INET:
288 return add_rtattr(m, type, data, sizeof(struct in_addr));
289 case AF_INET6:
290 return add_rtattr(m, type, data, sizeof(struct in6_addr));
291 default:
292 return -EINVAL;
293 }
294 case RTA_TABLE:
295 case RTA_PRIORITY:
296 case RTA_IIF:
297 case RTA_OIF:
298 return add_rtattr(m, type, data, sizeof(uint32_t));
299 default:
300 return -ENOTSUP;
301 }
65f568bb
TG
302 default:
303 return -ENOTSUP;
304 }
305}
306
307static int message_read(sd_rtnl_message *m, unsigned short *type, void **data) {
308 assert_return(m, -EINVAL);
309 assert_return(data, -EINVAL);
310
311 if (!RTA_OK(m->next_rta, m->remaining_size))
312 return 0;
313
314 *data = RTA_DATA(m->next_rta);
315 *type = m->next_rta->rta_type;
316
317 m->next_rta = RTA_NEXT(m->next_rta, m->remaining_size);
318
319 return 1;
320}
321
322int sd_rtnl_message_read(sd_rtnl_message *m, unsigned short *type, void **data) {
dabfa9d1 323 uint16_t rtm_type;
65f568bb
TG
324
325 assert_return(m, -EINVAL);
326 assert_return(data, -EINVAL);
327
328 sd_rtnl_message_get_type(m, &rtm_type);
329
330 switch (rtm_type) {
331 case RTM_NEWLINK:
332 case RTM_DELLINK:
333 case RTM_GETLINK:
334 if (!m->next_rta) {
dabfa9d1 335 struct ifinfomsg *ifi = NLMSG_DATA(m->hdr);
65f568bb
TG
336
337 m->next_rta = IFLA_RTA(ifi);
338 m->remaining_size = IFLA_PAYLOAD(m->hdr);
339 }
03d7e632 340 break;
65f568bb
TG
341 case RTM_NEWADDR:
342 case RTM_DELADDR:
343 case RTM_GETADDR:
344 if (!m->next_rta) {
dabfa9d1 345 struct ifaddrmsg *ifa = NLMSG_DATA(m->hdr);
65f568bb
TG
346
347 m->next_rta = IFA_RTA(ifa);
03d7e632
TG
348 m->remaining_size = IFA_PAYLOAD(m->hdr);
349 }
350 break;
351 case RTM_NEWROUTE:
352 case RTM_DELROUTE:
353 case RTM_GETROUTE:
354 if (!m->next_rta) {
355 struct rtmesg *rtm = NLMSG_DATA(m->hdr);
356
357 m->next_rta = RTM_RTA(rtm);
358 m->remaining_size = RTM_PAYLOAD(m->hdr);
65f568bb 359 }
03d7e632 360 break;
65f568bb
TG
361 default:
362 return -ENOTSUP;
363 }
364
365 return message_read(m, type, data);
366}
367
368int message_get_serial(sd_rtnl_message *m) {
369 assert(m);
370
371 return m->hdr->nlmsg_seq;
372}
373
374int message_get_errno(sd_rtnl_message *m) {
375 struct nlmsgerr *err;
376
377 assert(m);
378
379 if (m->hdr->nlmsg_type != NLMSG_ERROR)
380 return 0;
381
382 err = NLMSG_DATA(m->hdr);
383
384 return err->error;
385}
386
387int message_seal(sd_rtnl *nl, sd_rtnl_message *m) {
388 if (m->sealed)
389 return -EPERM;
390
391 m->hdr->nlmsg_seq = nl->serial++;
392 m->sealed = true;
393
394 return 0;
395}
396
397static int message_receive_need(sd_rtnl *rtnl, size_t *need) {
398 assert_return(rtnl, -EINVAL);
399 assert_return(need, -EINVAL);
400
401 /* ioctl(rtnl->fd, FIONREAD, &need)
dabfa9d1
TG
402 Does not appear to work on netlink sockets. libnl uses
403 MSG_PEEK instead. I don't know if that is worth the
404 extra roundtrip.
405
406 For now we simply use the maximum message size the kernel
407 may use (NLMSG_GOODSIZE), and then realloc to the actual
408 size after reading the message (hence avoiding huge memory
409 usage in case many small messages are kept around) */
410 *need = page_size();
65f568bb
TG
411 if (*need > 8192UL)
412 *need = 8192UL;
413
414 return 0;
415}
416
417/* returns the number of bytes sent, or a negative error code */
418int socket_write_message(sd_rtnl *nl, sd_rtnl_message *m) {
d4bbdb77
TG
419 union {
420 struct sockaddr sa;
421 struct sockaddr_nl nl;
422 } addr = {
423 .nl.nl_family = AF_NETLINK,
424 };
65f568bb
TG
425 ssize_t k;
426
427 assert_return(nl, -EINVAL);
428 assert_return(m, -EINVAL);
429
430 k = sendto(nl->fd, m->hdr, m->hdr->nlmsg_len,
d4bbdb77 431 0, &addr.sa, sizeof(addr));
65f568bb
TG
432 if (k < 0)
433 return (errno == EAGAIN) ? 0 : -errno;
434
435 return k;
436}
437
438/* On success, the number of bytes received is returned and *ret points to the received message
439 * which has a valid header and the correct size.
440 * If nothing useful was received 0 is returned.
441 * On failure, a negative error code is returned.
dabfa9d1 442 */
65f568bb
TG
443int socket_read_message(sd_rtnl *nl, sd_rtnl_message **ret) {
444 sd_rtnl_message *m;
d4bbdb77
TG
445 union {
446 struct sockaddr sa;
447 struct sockaddr_nl nl;
448 } addr;
449 socklen_t addr_len;
65f568bb
TG
450 int r;
451 ssize_t k;
452 size_t need;
453
454 assert_return(nl, -EINVAL);
455 assert_return(ret, -EINVAL);
456
457 r = message_receive_need(nl, &need);
458 if (r < 0)
459 return r;
460
461 r = message_new(&m, need);
462 if (r < 0)
463 return r;
464
d4bbdb77
TG
465 addr_len = sizeof(addr);
466
65f568bb 467 k = recvfrom(nl->fd, m->hdr, need,
d4bbdb77 468 0, &addr.sa, &addr_len);
65f568bb 469 if (k < 0)
276fc066 470 k = (errno == EAGAIN) ? 0 : -errno; /* no data */
65f568bb
TG
471 else if (k == 0)
472 k = -ECONNRESET; /* connection was closed by the kernel */
d4bbdb77
TG
473 else if (addr_len != sizeof(addr.nl) ||
474 addr.nl.nl_family != AF_NETLINK)
65f568bb 475 k = -EIO; /* not a netlink message */
d4bbdb77 476 else if (addr.nl.nl_pid != 0)
65f568bb
TG
477 k = 0; /* not from the kernel */
478 else if ((size_t) k < sizeof(struct nlmsghdr) ||
dabfa9d1 479 (size_t) k < m->hdr->nlmsg_len)
65f568bb 480 k = -EIO; /* too small (we do accept too big though) */
d4bbdb77 481 else if (m->hdr->nlmsg_pid != nl->sockaddr.nl.nl_pid)
65f568bb
TG
482 k = 0; /* not for us */
483
484 if (k > 0)
485 switch (m->hdr->nlmsg_type) {
486 /* check that the size matches the message type */
487 case NLMSG_ERROR:
488 if (m->hdr->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr)))
489 k = -EIO;
03d7e632 490 break;
65f568bb
TG
491 case RTM_NEWLINK:
492 case RTM_DELLINK:
493 case RTM_GETLINK:
494 if (m->hdr->nlmsg_len < NLMSG_LENGTH(sizeof(struct ifinfomsg)))
495 k = -EIO;
03d7e632 496 break;
65f568bb
TG
497 case RTM_NEWADDR:
498 case RTM_DELADDR:
499 case RTM_GETADDR:
500 if (m->hdr->nlmsg_len < NLMSG_LENGTH(sizeof(struct ifaddrmsg)))
501 k = -EIO;
03d7e632 502 break;
65f568bb
TG
503 case NLMSG_NOOP:
504 k = 0;
03d7e632 505 break;
65f568bb
TG
506 default:
507 k = 0; /* ignoring message of unknown type */
508 }
509
510 if (k <= 0)
511 sd_rtnl_message_unref(m);
512 else {
513 /* we probably allocated way too much memory, give it back */
514 m->hdr = realloc(m->hdr, m->hdr->nlmsg_len);
515 *ret = m;
516 }
517
518 return k;
519}