]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd-rtnl/rtnl-message.c
rtnl: simplify route_new()
[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
33125ac5
TG
39 struct rtattr *current_container;
40
65f568bb
TG
41 struct rtattr *next_rta;
42 size_t remaining_size;
43
44 bool sealed:1;
45};
46
47static int message_new(sd_rtnl_message **ret, size_t initial_size) {
48 sd_rtnl_message *m;
49
50 assert_return(ret, -EINVAL);
dabfa9d1 51 assert_return(initial_size >= sizeof(struct nlmsghdr), -EINVAL);
65f568bb
TG
52
53 m = new0(sd_rtnl_message, 1);
54 if (!m)
55 return -ENOMEM;
56
dc7d8997 57 m->hdr = malloc0(initial_size);
65f568bb
TG
58 if (!m->hdr) {
59 free(m);
60 return -ENOMEM;
61 }
62
63 m->n_ref = REFCNT_INIT;
64
65 m->hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
66 m->sealed = false;
67
68 *ret = m;
69
70 return 0;
71}
72
e16bcf98
TG
73int message_new_synthetic_error(int error, uint32_t serial, sd_rtnl_message **ret) {
74 struct nlmsgerr *err;
75 int r;
76
77 assert(error <= 0);
78
79 r = message_new(ret, NLMSG_SPACE(sizeof(struct nlmsgerr)));
80 if (r < 0)
81 return r;
82
83 (*ret)->hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct nlmsgerr));
84 (*ret)->hdr->nlmsg_type = NLMSG_ERROR;
85 (*ret)->hdr->nlmsg_seq = serial;
86
87 err = NLMSG_DATA((*ret)->hdr);
88
89 err->error = error;
90
91 return 0;
92}
93
9d0db178
TG
94bool message_type_is_route(uint16_t type) {
95 switch (type) {
96 case RTM_NEWROUTE:
97 case RTM_GETROUTE:
98 case RTM_DELROUTE:
99 return true;
100 default:
101 return false;
102 }
103}
104
105bool message_type_is_link(uint16_t type) {
106 switch (type) {
107 case RTM_NEWLINK:
108 case RTM_SETLINK:
109 case RTM_GETLINK:
110 case RTM_DELLINK:
111 return true;
112 default:
113 return false;
114 }
115}
116
117bool message_type_is_addr(uint16_t type) {
118 switch (type) {
119 case RTM_NEWADDR:
120 case RTM_GETADDR:
121 case RTM_DELADDR:
122 return true;
123 default:
124 return false;
125 }
126}
127
1f01fb4f
TG
128int sd_rtnl_message_route_set_dst_prefixlen(sd_rtnl_message *m, unsigned char prefixlen) {
129 struct rtmsg *rtm;
130
131 rtm = NLMSG_DATA(m->hdr);
132
133 rtm->rtm_dst_len = prefixlen;
134
135 return 0;
136}
137
03d7e632 138int sd_rtnl_message_route_new(uint16_t nlmsg_type, unsigned char rtm_family,
1f01fb4f 139 sd_rtnl_message **ret) {
03d7e632
TG
140 struct rtmsg *rtm;
141 int r;
142
9d0db178 143 assert_return(message_type_is_route(nlmsg_type), -EINVAL);
1f01fb4f 144 assert_return(rtm_family == AF_INET || rtm_family == AF_INET6, -EINVAL);
03d7e632
TG
145 assert_return(ret, -EINVAL);
146
147 r = message_new(ret, NLMSG_SPACE(sizeof(struct rtmsg)));
148 if (r < 0)
149 return r;
150
151 (*ret)->hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
152 (*ret)->hdr->nlmsg_type = nlmsg_type;
153 if (nlmsg_type == RTM_NEWROUTE)
154 (*ret)->hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_EXCL;
155
156 rtm = NLMSG_DATA((*ret)->hdr);
157
158 rtm->rtm_family = rtm_family;
1f01fb4f
TG
159 rtm->rtm_scope = RT_SCOPE_UNIVERSE;
160 rtm->rtm_type = RTN_UNICAST;
161 rtm->rtm_table = RT_TABLE_MAIN;
162 rtm->rtm_protocol = RTPROT_BOOT;
03d7e632
TG
163
164 return 0;
165}
166
fc25d7f8
TG
167int sd_rtnl_message_link_set_flags(sd_rtnl_message *m, unsigned flags) {
168 struct ifinfomsg *ifi;
169
170 ifi = NLMSG_DATA(m->hdr);
171
172 ifi->ifi_flags = flags;
173
174 return 0;
175}
176
177int sd_rtnl_message_link_set_type(sd_rtnl_message *m, unsigned type) {
178 struct ifinfomsg *ifi;
179
180 ifi = NLMSG_DATA(m->hdr);
181
182 ifi->ifi_type = type;
183
184 return 0;
185}
186
187int sd_rtnl_message_link_new(uint16_t nlmsg_type, int index, sd_rtnl_message **ret) {
65f568bb
TG
188 struct ifinfomsg *ifi;
189 int r;
190
9d0db178 191 assert_return(message_type_is_link(nlmsg_type), -EINVAL);
33125ac5 192 assert_return(nlmsg_type == RTM_NEWLINK || index > 0, -EINVAL);
65f568bb
TG
193 assert_return(ret, -EINVAL);
194
195 r = message_new(ret, NLMSG_SPACE(sizeof(struct ifinfomsg)));
196 if (r < 0)
197 return r;
198
199 (*ret)->hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
200 (*ret)->hdr->nlmsg_type = nlmsg_type;
33125ac5
TG
201 if (nlmsg_type == RTM_NEWLINK)
202 (*ret)->hdr->nlmsg_flags |= NLM_F_CREATE;
65f568bb 203
dabfa9d1 204 ifi = NLMSG_DATA((*ret)->hdr);
65f568bb
TG
205
206 ifi->ifi_family = AF_UNSPEC;
207 ifi->ifi_index = index;
65f568bb
TG
208 ifi->ifi_change = 0xffffffff;
209
210 return 0;
211}
212
dabfa9d1 213int 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
214 struct ifaddrmsg *ifa;
215 int r;
216
9d0db178 217 assert_return(message_type_is_addr(nlmsg_type), -EINVAL);
65f568bb
TG
218 assert_return(index > 0, -EINVAL);
219 assert_return(ret, -EINVAL);
220
221 r = message_new(ret, NLMSG_SPACE(sizeof(struct ifaddrmsg)));
222 if (r < 0)
223 return r;
224
225 (*ret)->hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
226 (*ret)->hdr->nlmsg_type = nlmsg_type;
227
dabfa9d1 228 ifa = NLMSG_DATA((*ret)->hdr);
65f568bb
TG
229
230 ifa->ifa_family = family;
231 ifa->ifa_prefixlen = prefixlen;
232 ifa->ifa_flags = flags;
233 ifa->ifa_scope = scope;
234 ifa->ifa_index = index;
235
236 return 0;
237}
238
239sd_rtnl_message *sd_rtnl_message_ref(sd_rtnl_message *m) {
240 if (m)
241 assert_se(REFCNT_INC(m->n_ref) >= 2);
242
243 return m;
244}
245
246sd_rtnl_message *sd_rtnl_message_unref(sd_rtnl_message *m) {
247 if (m && REFCNT_DEC(m->n_ref) <= 0) {
248 free(m->hdr);
249 free(m);
250 }
251
252 return NULL;
253}
254
dabfa9d1 255int sd_rtnl_message_get_type(sd_rtnl_message *m, uint16_t *type) {
65f568bb
TG
256 assert_return(m, -EINVAL);
257 assert_return(type, -EINVAL);
258
259 *type = m->hdr->nlmsg_type;
260
261 return 0;
262}
263
33125ac5
TG
264int sd_rtnl_message_link_get_ifindex(sd_rtnl_message *m, int *ifindex) {
265 struct ifinfomsg *ifi;
266
267 assert_return(m, -EINVAL);
9d0db178
TG
268 assert_return(m->hdr, -EINVAL);
269 assert_return(message_type_is_link(m->hdr->nlmsg_type), -EINVAL);
33125ac5 270 assert_return(ifindex, -EINVAL);
33125ac5
TG
271
272 ifi = NLMSG_DATA(m->hdr);
273
274 *ifindex = ifi->ifi_index;
275
276 return 0;
277}
278
50b3c42f
TG
279int sd_rtnl_message_link_get_flags(sd_rtnl_message *m, unsigned *flags) {
280 struct ifinfomsg *ifi;
281
282 assert_return(m, -EINVAL);
9d0db178
TG
283 assert_return(m->hdr, -EINVAL);
284 assert_return(message_type_is_link(m->hdr->nlmsg_type), -EINVAL);
50b3c42f 285 assert_return(flags, -EINVAL);
50b3c42f
TG
286
287 ifi = NLMSG_DATA(m->hdr);
288
289 *flags = ifi->ifi_flags;
290
291 return 0;
292}
293
65f568bb
TG
294/* If successful the updated message will be correctly aligned, if unsuccessful the old message is
295 untouched */
296static int add_rtattr(sd_rtnl_message *m, unsigned short type, const void *data, size_t data_length) {
dabfa9d1 297 uint32_t rta_length, message_length;
65f568bb
TG
298 struct nlmsghdr *new_hdr;
299 struct rtattr *rta;
8e337e64 300 char *padding;
65f568bb 301
33125ac5
TG
302 assert(m);
303 assert(m->hdr);
304 assert(NLMSG_ALIGN(m->hdr->nlmsg_len) == m->hdr->nlmsg_len);
305 assert(!data || data_length > 0);
65f568bb 306
8e337e64 307 /* get the size of the new rta attribute (with padding at the end) */
65f568bb 308 rta_length = RTA_LENGTH(data_length);
8e337e64 309 /* get the new message size (with padding at the end)
65f568bb
TG
310 */
311 message_length = m->hdr->nlmsg_len + RTA_ALIGN(rta_length);
312
313 /* realloc to fit the new attribute */
314 new_hdr = realloc(m->hdr, message_length);
315 if (!new_hdr)
316 return -ENOMEM;
317 m->hdr = new_hdr;
318
319 /* get pointer to the attribute we are about to add */
320 rta = (struct rtattr *) ((uint8_t *) m->hdr + m->hdr->nlmsg_len);
321 /* update message size */
322 m->hdr->nlmsg_len = message_length;
323
33125ac5
TG
324 /* we are inside a container, extend it */
325 if (m->current_container)
326 m->current_container->rta_len = (unsigned char *) m->hdr +
327 m->hdr->nlmsg_len -
328 (unsigned char *) m->current_container;
329
65f568bb
TG
330 /* fill in the attribute */
331 rta->rta_type = type;
332 rta->rta_len = rta_length;
33125ac5
TG
333 if (!data) {
334 /* this is a container, set pointer */
335 m->current_container = rta;
336 } else {
337 /* we don't deal with the case where the user lies about the type
338 * and gives us too little data (so don't do that)
339 */
340 padding = mempcpy(RTA_DATA(rta), data, data_length);
341 /* make sure also the padding at the end of the message is initialized */
342 memset(padding, '\0', (unsigned char *) m->hdr +
343 m->hdr->nlmsg_len -
344 (unsigned char *) padding);
345 }
65f568bb
TG
346
347 return 0;
348}
349
350int sd_rtnl_message_append(sd_rtnl_message *m, unsigned short type, const void *data) {
dabfa9d1 351 uint16_t rtm_type;
65f568bb 352 struct ifaddrmsg *ifa;
03d7e632 353 struct rtmsg *rtm;
65f568bb
TG
354
355 assert_return(m, -EINVAL);
356 assert_return(data, -EINVAL);
357
358 sd_rtnl_message_get_type(m, &rtm_type);
359
33125ac5
TG
360 if (m->current_container) {
361 switch (rtm_type) {
362 case RTM_NEWLINK:
363 case RTM_SETLINK:
364 case RTM_GETLINK:
365 case RTM_DELLINK:
366 switch (m->current_container->rta_type) {
367 case IFLA_LINKINFO:
368 switch (type) {
369 case IFLA_INFO_KIND:
370 return add_rtattr(m, type, data, strlen(data) + 1);
371 default:
372 return -ENOTSUP;
373 }
374 default:
375 return -ENOTSUP;
376 }
377 default:
378 return -ENOTSUP;
379 }
380 }
381
65f568bb
TG
382 switch (rtm_type) {
383 case RTM_NEWLINK:
d2df0d0e 384 case RTM_SETLINK:
65f568bb
TG
385 case RTM_DELLINK:
386 case RTM_GETLINK:
387 switch (type) {
388 case IFLA_IFNAME:
d2df0d0e 389 case IFLA_IFALIAS:
65f568bb
TG
390 case IFLA_QDISC:
391 return add_rtattr(m, type, data, strlen(data) + 1);
46fabae6 392 case IFLA_MASTER:
65f568bb 393 case IFLA_MTU:
65f568bb 394 case IFLA_LINK:
03d7e632 395 return add_rtattr(m, type, data, sizeof(uint32_t));
65f568bb
TG
396 case IFLA_STATS:
397 return add_rtattr(m, type, data, sizeof(struct rtnl_link_stats));
398 case IFLA_ADDRESS:
399 case IFLA_BROADCAST:
400 return add_rtattr(m, type, data, ETH_ALEN);
401 default:
402 return -ENOTSUP;
403 }
404 case RTM_NEWADDR:
405 case RTM_DELADDR:
406 case RTM_GETADDR:
407 switch (type) {
408 case IFA_LABEL:
409 return add_rtattr(m, type, data, strlen(data) + 1);
410 case IFA_ADDRESS:
411 case IFA_LOCAL:
412 case IFA_BROADCAST:
413 case IFA_ANYCAST:
414 ifa = NLMSG_DATA(m->hdr);
415 switch (ifa->ifa_family) {
416 case AF_INET:
417 return add_rtattr(m, type, data, sizeof(struct in_addr));
418 case AF_INET6:
419 return add_rtattr(m, type, data, sizeof(struct in6_addr));
420 default:
421 return -EINVAL;
422 }
423 default:
424 return -ENOTSUP;
425 }
03d7e632
TG
426 case RTM_NEWROUTE:
427 case RTM_DELROUTE:
428 case RTM_GETROUTE:
429 switch (type) {
430 case RTA_DST:
431 case RTA_SRC:
432 case RTA_GATEWAY:
433 rtm = NLMSG_DATA(m->hdr);
434 switch (rtm->rtm_family) {
435 case AF_INET:
436 return add_rtattr(m, type, data, sizeof(struct in_addr));
437 case AF_INET6:
438 return add_rtattr(m, type, data, sizeof(struct in6_addr));
439 default:
440 return -EINVAL;
441 }
442 case RTA_TABLE:
443 case RTA_PRIORITY:
444 case RTA_IIF:
445 case RTA_OIF:
446 return add_rtattr(m, type, data, sizeof(uint32_t));
447 default:
448 return -ENOTSUP;
449 }
65f568bb
TG
450 default:
451 return -ENOTSUP;
452 }
453}
454
33125ac5
TG
455int sd_rtnl_message_open_container(sd_rtnl_message *m, unsigned short type) {
456 uint16_t rtm_type;
457
65f568bb 458 assert_return(m, -EINVAL);
33125ac5
TG
459 assert_return(!m->current_container, -EINVAL);
460
461 sd_rtnl_message_get_type(m, &rtm_type);
462
9d0db178
TG
463 if (message_type_is_link(rtm_type)) {
464 if (type == IFLA_LINKINFO)
465 return add_rtattr(m, type, NULL, 0);
466 else
33125ac5 467 return -ENOTSUP;
9d0db178
TG
468 } else
469 return -ENOTSUP;
33125ac5
TG
470
471 return 0;
472}
473
474int sd_rtnl_message_close_container(sd_rtnl_message *m) {
475 assert_return(m, -EINVAL);
476 assert_return(m->current_container, -EINVAL);
477
478 m->current_container = NULL;
479
480 return 0;
481}
482
483static int message_read(sd_rtnl_message *m, unsigned short *type, void **data) {
484 uint16_t rtm_type;
485 int r;
486
487 assert(m);
488 assert(m->next_rta);
489 assert(type);
490 assert(data);
65f568bb
TG
491
492 if (!RTA_OK(m->next_rta, m->remaining_size))
493 return 0;
494
33125ac5
TG
495 /* make sure we don't try to read a container
496 * TODO: add support for entering containers for reading */
497 r = sd_rtnl_message_get_type(m, &rtm_type);
498 if (r < 0)
499 return r;
500
501 switch (rtm_type) {
502 case RTM_NEWLINK:
503 case RTM_GETLINK:
504 case RTM_SETLINK:
505 case RTM_DELLINK:
506 if (m->next_rta->rta_type == IFLA_LINKINFO) {
507 return -EINVAL;
508 }
509 }
510
65f568bb
TG
511 *data = RTA_DATA(m->next_rta);
512 *type = m->next_rta->rta_type;
513
514 m->next_rta = RTA_NEXT(m->next_rta, m->remaining_size);
515
516 return 1;
517}
518
519int sd_rtnl_message_read(sd_rtnl_message *m, unsigned short *type, void **data) {
dabfa9d1 520 uint16_t rtm_type;
33125ac5 521 int r;
65f568bb
TG
522
523 assert_return(m, -EINVAL);
524 assert_return(data, -EINVAL);
525
33125ac5
TG
526 r = sd_rtnl_message_get_type(m, &rtm_type);
527 if (r < 0)
528 return r;
65f568bb
TG
529
530 switch (rtm_type) {
531 case RTM_NEWLINK:
d2df0d0e 532 case RTM_SETLINK:
65f568bb
TG
533 case RTM_DELLINK:
534 case RTM_GETLINK:
535 if (!m->next_rta) {
dabfa9d1 536 struct ifinfomsg *ifi = NLMSG_DATA(m->hdr);
65f568bb
TG
537
538 m->next_rta = IFLA_RTA(ifi);
539 m->remaining_size = IFLA_PAYLOAD(m->hdr);
540 }
03d7e632 541 break;
65f568bb
TG
542 case RTM_NEWADDR:
543 case RTM_DELADDR:
544 case RTM_GETADDR:
545 if (!m->next_rta) {
dabfa9d1 546 struct ifaddrmsg *ifa = NLMSG_DATA(m->hdr);
65f568bb
TG
547
548 m->next_rta = IFA_RTA(ifa);
03d7e632
TG
549 m->remaining_size = IFA_PAYLOAD(m->hdr);
550 }
551 break;
552 case RTM_NEWROUTE:
553 case RTM_DELROUTE:
554 case RTM_GETROUTE:
555 if (!m->next_rta) {
556 struct rtmesg *rtm = NLMSG_DATA(m->hdr);
557
558 m->next_rta = RTM_RTA(rtm);
559 m->remaining_size = RTM_PAYLOAD(m->hdr);
65f568bb 560 }
03d7e632 561 break;
65f568bb
TG
562 default:
563 return -ENOTSUP;
564 }
565
566 return message_read(m, type, data);
567}
568
4555ec72 569uint32_t message_get_serial(sd_rtnl_message *m) {
65f568bb 570 assert(m);
9d0db178 571 assert(m->hdr);
65f568bb
TG
572
573 return m->hdr->nlmsg_seq;
574}
575
e16bcf98 576int sd_rtnl_message_get_errno(sd_rtnl_message *m) {
65f568bb
TG
577 struct nlmsgerr *err;
578
e16bcf98 579 assert_return(m, -EINVAL);
9d0db178 580 assert_return(m->hdr, -EINVAL);
65f568bb
TG
581
582 if (m->hdr->nlmsg_type != NLMSG_ERROR)
583 return 0;
584
585 err = NLMSG_DATA(m->hdr);
586
587 return err->error;
588}
589
590int message_seal(sd_rtnl *nl, sd_rtnl_message *m) {
9d0db178
TG
591 assert(nl);
592 assert(m);
593 assert(m->hdr);
594
65f568bb
TG
595 if (m->sealed)
596 return -EPERM;
597
598 m->hdr->nlmsg_seq = nl->serial++;
599 m->sealed = true;
600
601 return 0;
602}
603
604static int message_receive_need(sd_rtnl *rtnl, size_t *need) {
9d0db178
TG
605 assert(rtnl);
606 assert(need);
65f568bb
TG
607
608 /* ioctl(rtnl->fd, FIONREAD, &need)
dabfa9d1
TG
609 Does not appear to work on netlink sockets. libnl uses
610 MSG_PEEK instead. I don't know if that is worth the
611 extra roundtrip.
612
613 For now we simply use the maximum message size the kernel
614 may use (NLMSG_GOODSIZE), and then realloc to the actual
615 size after reading the message (hence avoiding huge memory
616 usage in case many small messages are kept around) */
617 *need = page_size();
65f568bb
TG
618 if (*need > 8192UL)
619 *need = 8192UL;
620
621 return 0;
622}
623
624/* returns the number of bytes sent, or a negative error code */
625int socket_write_message(sd_rtnl *nl, sd_rtnl_message *m) {
d4bbdb77
TG
626 union {
627 struct sockaddr sa;
628 struct sockaddr_nl nl;
629 } addr = {
630 .nl.nl_family = AF_NETLINK,
631 };
65f568bb
TG
632 ssize_t k;
633
9d0db178
TG
634 assert(nl);
635 assert(m);
636 assert(m->hdr);
65f568bb
TG
637
638 k = sendto(nl->fd, m->hdr, m->hdr->nlmsg_len,
d4bbdb77 639 0, &addr.sa, sizeof(addr));
65f568bb
TG
640 if (k < 0)
641 return (errno == EAGAIN) ? 0 : -errno;
642
643 return k;
644}
645
646/* On success, the number of bytes received is returned and *ret points to the received message
647 * which has a valid header and the correct size.
648 * If nothing useful was received 0 is returned.
649 * On failure, a negative error code is returned.
dabfa9d1 650 */
65f568bb
TG
651int socket_read_message(sd_rtnl *nl, sd_rtnl_message **ret) {
652 sd_rtnl_message *m;
d4bbdb77
TG
653 union {
654 struct sockaddr sa;
655 struct sockaddr_nl nl;
656 } addr;
657 socklen_t addr_len;
65f568bb
TG
658 int r;
659 ssize_t k;
660 size_t need;
661
9d0db178
TG
662 assert(nl);
663 assert(ret);
65f568bb
TG
664
665 r = message_receive_need(nl, &need);
666 if (r < 0)
667 return r;
668
669 r = message_new(&m, need);
670 if (r < 0)
671 return r;
672
d4bbdb77
TG
673 addr_len = sizeof(addr);
674
65f568bb 675 k = recvfrom(nl->fd, m->hdr, need,
d4bbdb77 676 0, &addr.sa, &addr_len);
65f568bb 677 if (k < 0)
276fc066 678 k = (errno == EAGAIN) ? 0 : -errno; /* no data */
65f568bb
TG
679 else if (k == 0)
680 k = -ECONNRESET; /* connection was closed by the kernel */
d4bbdb77
TG
681 else if (addr_len != sizeof(addr.nl) ||
682 addr.nl.nl_family != AF_NETLINK)
65f568bb 683 k = -EIO; /* not a netlink message */
d4bbdb77 684 else if (addr.nl.nl_pid != 0)
65f568bb
TG
685 k = 0; /* not from the kernel */
686 else if ((size_t) k < sizeof(struct nlmsghdr) ||
dabfa9d1 687 (size_t) k < m->hdr->nlmsg_len)
65f568bb 688 k = -EIO; /* too small (we do accept too big though) */
a02113d2
TG
689 else if (m->hdr->nlmsg_pid && m->hdr->nlmsg_pid != nl->sockaddr.nl.nl_pid)
690 k = 0; /* not broadcast and not for us */
65f568bb
TG
691
692 if (k > 0)
693 switch (m->hdr->nlmsg_type) {
694 /* check that the size matches the message type */
695 case NLMSG_ERROR:
696 if (m->hdr->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr)))
697 k = -EIO;
03d7e632 698 break;
65f568bb 699 case RTM_NEWLINK:
d2df0d0e 700 case RTM_SETLINK:
65f568bb
TG
701 case RTM_DELLINK:
702 case RTM_GETLINK:
703 if (m->hdr->nlmsg_len < NLMSG_LENGTH(sizeof(struct ifinfomsg)))
704 k = -EIO;
03d7e632 705 break;
65f568bb
TG
706 case RTM_NEWADDR:
707 case RTM_DELADDR:
708 case RTM_GETADDR:
709 if (m->hdr->nlmsg_len < NLMSG_LENGTH(sizeof(struct ifaddrmsg)))
710 k = -EIO;
03d7e632 711 break;
3e10a9f4
TG
712 case RTM_NEWROUTE:
713 case RTM_DELROUTE:
714 case RTM_GETROUTE:
715 if (m->hdr->nlmsg_len < NLMSG_LENGTH(sizeof(struct rtmsg)))
716 k = -EIO;
717 break;
65f568bb
TG
718 case NLMSG_NOOP:
719 k = 0;
03d7e632 720 break;
65f568bb
TG
721 default:
722 k = 0; /* ignoring message of unknown type */
723 }
724
725 if (k <= 0)
726 sd_rtnl_message_unref(m);
727 else {
728 /* we probably allocated way too much memory, give it back */
729 m->hdr = realloc(m->hdr, m->hdr->nlmsg_len);
730 *ret = m;
731 }
732
733 return k;
734}