chiark / gitweb /
sd-rtnl: add sd_rtnl_message_append_u16()
[elogind.git] / src / libsystemd / sd-rtnl / rtnl-message.c
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
22 #include <linux/rtnetlink.h>
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
34 struct sd_rtnl_message {
35         RefCount n_ref;
36
37         struct nlmsghdr *hdr;
38         size_t container_offset; /* offset from hdr to container start */
39         size_t next_rta_offset; /* offset from hdr to next rta */
40
41         bool sealed:1;
42 };
43
44 #define CURRENT_CONTAINER(m) ((m)->container_offset ? (struct rtattr*)((uint8_t*)(m)->hdr + (m)->container_offset) : NULL)
45 #define NEXT_RTA(m) ((struct rtattr*)((uint8_t*)(m)->hdr + (m)->next_rta_offset))
46 #define UPDATE_RTA(m, new) (m)->next_rta_offset = (uint8_t*)(new) - (uint8_t*)(m)->hdr;
47
48 static int message_new(sd_rtnl_message **ret, size_t initial_size) {
49         sd_rtnl_message *m;
50
51         assert_return(ret, -EINVAL);
52         assert_return(initial_size >= sizeof(struct nlmsghdr), -EINVAL);
53
54         m = new0(sd_rtnl_message, 1);
55         if (!m)
56                 return -ENOMEM;
57
58         m->hdr = malloc0(initial_size);
59         if (!m->hdr) {
60                 free(m);
61                 return -ENOMEM;
62         }
63
64         m->n_ref = REFCNT_INIT;
65
66         m->hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
67         m->sealed = false;
68
69         *ret = m;
70
71         return 0;
72 }
73
74 int message_new_synthetic_error(int error, uint32_t serial, sd_rtnl_message **ret) {
75         struct nlmsgerr *err;
76         int r;
77
78         assert(error <= 0);
79
80         r = message_new(ret, NLMSG_SPACE(sizeof(struct nlmsgerr)));
81         if (r < 0)
82                 return r;
83
84         (*ret)->hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct nlmsgerr));
85         (*ret)->hdr->nlmsg_type = NLMSG_ERROR;
86         (*ret)->hdr->nlmsg_seq = serial;
87
88         err = NLMSG_DATA((*ret)->hdr);
89
90         err->error = error;
91
92         return 0;
93 }
94
95 bool message_type_is_route(uint16_t type) {
96         switch (type) {
97                 case RTM_NEWROUTE:
98                 case RTM_GETROUTE:
99                 case RTM_DELROUTE:
100                         return true;
101                 default:
102                         return false;
103         }
104 }
105
106 bool message_type_is_link(uint16_t type) {
107         switch (type) {
108                 case RTM_NEWLINK:
109                 case RTM_SETLINK:
110                 case RTM_GETLINK:
111                 case RTM_DELLINK:
112                         return true;
113                 default:
114                         return false;
115         }
116 }
117
118 bool message_type_is_addr(uint16_t type) {
119         switch (type) {
120                 case RTM_NEWADDR:
121                 case RTM_GETADDR:
122                 case RTM_DELADDR:
123                         return true;
124                 default:
125                         return false;
126         }
127 }
128
129 int sd_rtnl_message_route_set_dst_prefixlen(sd_rtnl_message *m, unsigned char prefixlen) {
130         struct rtmsg *rtm;
131
132         rtm = NLMSG_DATA(m->hdr);
133
134         rtm->rtm_dst_len = prefixlen;
135
136         return 0;
137 }
138
139 int sd_rtnl_message_route_new(uint16_t nlmsg_type, unsigned char rtm_family,
140                               sd_rtnl_message **ret) {
141         struct rtmsg *rtm;
142         int r;
143
144         assert_return(message_type_is_route(nlmsg_type), -EINVAL);
145         assert_return(rtm_family == AF_INET || rtm_family == AF_INET6, -EINVAL);
146         assert_return(ret, -EINVAL);
147
148         r = message_new(ret, NLMSG_SPACE(sizeof(struct rtmsg)));
149         if (r < 0)
150                 return r;
151
152         (*ret)->hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
153         (*ret)->hdr->nlmsg_type = nlmsg_type;
154         if (nlmsg_type == RTM_NEWROUTE)
155                 (*ret)->hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_EXCL;
156
157         rtm = NLMSG_DATA((*ret)->hdr);
158
159         UPDATE_RTA(*ret, RTM_RTA(rtm));
160
161         rtm->rtm_family = rtm_family;
162         rtm->rtm_scope = RT_SCOPE_UNIVERSE;
163         rtm->rtm_type = RTN_UNICAST;
164         rtm->rtm_table = RT_TABLE_MAIN;
165         rtm->rtm_protocol = RTPROT_BOOT;
166
167         return 0;
168 }
169
170 int sd_rtnl_message_link_set_flags(sd_rtnl_message *m, unsigned flags, unsigned change) {
171         struct ifinfomsg *ifi;
172
173         ifi = NLMSG_DATA(m->hdr);
174
175         ifi->ifi_flags = flags;
176         if (change)
177                 ifi->ifi_change = change;
178         else
179                 ifi->ifi_change = 0xffffffff;
180
181         return 0;
182 }
183
184 int sd_rtnl_message_link_set_type(sd_rtnl_message *m, unsigned type) {
185         struct ifinfomsg *ifi;
186
187         ifi = NLMSG_DATA(m->hdr);
188
189         ifi->ifi_type = type;
190
191         return 0;
192 }
193
194 int sd_rtnl_message_link_new(uint16_t nlmsg_type, int index, sd_rtnl_message **ret) {
195         struct ifinfomsg *ifi;
196         int r;
197
198         assert_return(message_type_is_link(nlmsg_type), -EINVAL);
199         assert_return(nlmsg_type == RTM_NEWLINK || index > 0, -EINVAL);
200         assert_return(ret, -EINVAL);
201
202         r = message_new(ret, NLMSG_SPACE(sizeof(struct ifinfomsg)));
203         if (r < 0)
204                 return r;
205
206         (*ret)->hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
207         (*ret)->hdr->nlmsg_type = nlmsg_type;
208         if (nlmsg_type == RTM_NEWLINK)
209                 (*ret)->hdr->nlmsg_flags |= NLM_F_CREATE;
210
211         ifi = NLMSG_DATA((*ret)->hdr);
212
213         ifi->ifi_family = AF_UNSPEC;
214         ifi->ifi_index = index;
215
216         UPDATE_RTA(*ret, IFLA_RTA(ifi));
217
218         return 0;
219 }
220
221 int 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) {
222         struct ifaddrmsg *ifa;
223         int r;
224
225         assert_return(message_type_is_addr(nlmsg_type), -EINVAL);
226         assert_return(index > 0, -EINVAL);
227         assert_return(ret, -EINVAL);
228
229         r = message_new(ret, NLMSG_SPACE(sizeof(struct ifaddrmsg)));
230         if (r < 0)
231                 return r;
232
233         (*ret)->hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
234         (*ret)->hdr->nlmsg_type = nlmsg_type;
235
236         ifa = NLMSG_DATA((*ret)->hdr);
237
238         ifa->ifa_family = family;
239         ifa->ifa_prefixlen = prefixlen;
240         ifa->ifa_flags = flags;
241         ifa->ifa_scope = scope;
242         ifa->ifa_index = index;
243
244         UPDATE_RTA(*ret, IFA_RTA(ifa));
245
246         return 0;
247 }
248
249 sd_rtnl_message *sd_rtnl_message_ref(sd_rtnl_message *m) {
250         if (m)
251                 assert_se(REFCNT_INC(m->n_ref) >= 2);
252
253         return m;
254 }
255
256 sd_rtnl_message *sd_rtnl_message_unref(sd_rtnl_message *m) {
257         if (m && REFCNT_DEC(m->n_ref) <= 0) {
258                 free(m->hdr);
259                 free(m);
260         }
261
262         return NULL;
263 }
264
265 int sd_rtnl_message_get_type(sd_rtnl_message *m, uint16_t *type) {
266         assert_return(m, -EINVAL);
267         assert_return(type, -EINVAL);
268
269         *type = m->hdr->nlmsg_type;
270
271         return 0;
272 }
273
274 int sd_rtnl_message_link_get_ifindex(sd_rtnl_message *m, int *ifindex) {
275         struct ifinfomsg *ifi;
276
277         assert_return(m, -EINVAL);
278         assert_return(m->hdr, -EINVAL);
279         assert_return(message_type_is_link(m->hdr->nlmsg_type), -EINVAL);
280         assert_return(ifindex, -EINVAL);
281
282         ifi = NLMSG_DATA(m->hdr);
283
284         *ifindex = ifi->ifi_index;
285
286         return 0;
287 }
288
289 int sd_rtnl_message_link_get_flags(sd_rtnl_message *m, unsigned *flags) {
290         struct ifinfomsg *ifi;
291
292         assert_return(m, -EINVAL);
293         assert_return(m->hdr, -EINVAL);
294         assert_return(message_type_is_link(m->hdr->nlmsg_type), -EINVAL);
295         assert_return(flags, -EINVAL);
296
297         ifi = NLMSG_DATA(m->hdr);
298
299         *flags = ifi->ifi_flags;
300
301         return 0;
302 }
303
304 /* If successful the updated message will be correctly aligned, if
305    unsuccessful the old message is untouched. */
306 static int add_rtattr(sd_rtnl_message *m, unsigned short type, const void *data, size_t data_length) {
307         uint32_t rta_length, message_length;
308         struct nlmsghdr *new_hdr;
309         struct rtattr *rta;
310         char *padding;
311
312         assert(m);
313         assert(m->hdr);
314         assert(NLMSG_ALIGN(m->hdr->nlmsg_len) == m->hdr->nlmsg_len);
315         assert(!data || data_length > 0);
316
317         /* get the size of the new rta attribute (with padding at the end) */
318         rta_length = RTA_LENGTH(data_length);
319
320         /* get the new message size (with padding at the end) */
321         message_length = m->hdr->nlmsg_len + RTA_ALIGN(rta_length);
322
323         /* realloc to fit the new attribute */
324         new_hdr = realloc(m->hdr, message_length);
325         if (!new_hdr)
326                 return -ENOMEM;
327         m->hdr = new_hdr;
328
329         /* get pointer to the attribute we are about to add */
330         rta = (struct rtattr *) ((uint8_t *) m->hdr + m->hdr->nlmsg_len);
331
332         /* if we are inside a container, extend it */
333         if (CURRENT_CONTAINER(m))
334                 CURRENT_CONTAINER(m)->rta_len += message_length - m->hdr->nlmsg_len;
335
336         /* fill in the attribute */
337         rta->rta_type = type;
338         rta->rta_len = rta_length;
339         if (!data) {
340                 /* this is the start of a new container */
341                 m->container_offset = m->hdr->nlmsg_len;
342         } else {
343                 /* we don't deal with the case where the user lies about the type
344                  * and gives us too little data (so don't do that)
345                 */
346                 padding = mempcpy(RTA_DATA(rta), data, data_length);
347                 /* make sure also the padding at the end of the message is initialized */
348                 memzero(padding,
349                         (uint8_t *) m->hdr + message_length - (uint8_t *) padding);
350         }
351
352         /* update message size */
353         m->hdr->nlmsg_len = message_length;
354
355         return 0;
356 }
357
358 int sd_rtnl_message_append_string(sd_rtnl_message *m, unsigned short type, const char *data) {
359         uint16_t rtm_type;
360         int r;
361
362         assert_return(m, -EINVAL);
363         assert_return(data, -EINVAL);
364
365         r = sd_rtnl_message_get_type(m, &rtm_type);
366         if (r < 0)
367                 return r;
368
369         /* check that the type is correct */
370         switch (rtm_type) {
371                 case RTM_NEWLINK:
372                 case RTM_SETLINK:
373                 case RTM_GETLINK:
374                 case RTM_DELLINK:
375                         if (CURRENT_CONTAINER(m)) {
376                                 if (CURRENT_CONTAINER(m)->rta_type != IFLA_LINKINFO ||
377                                     type != IFLA_INFO_KIND)
378                                         return -ENOTSUP;
379                         } else {
380                                 switch (type) {
381                                         case IFLA_IFNAME:
382                                         case IFLA_IFALIAS:
383                                         case IFLA_QDISC:
384                                                 break;
385                                         default:
386                                                 return -ENOTSUP;
387                                 }
388                         }
389                         break;
390                 case RTM_NEWADDR:
391                 case RTM_GETADDR:
392                 case RTM_DELADDR:
393                         if (type != IFA_LABEL)
394                                 return -ENOTSUP;
395                         break;
396                 default:
397                         return -ENOTSUP;
398         }
399
400         r = add_rtattr(m, type, data, strlen(data) + 1);
401         if (r < 0)
402                 return r;
403
404         return 0;
405 }
406
407 int sd_rtnl_message_append_u16(sd_rtnl_message *m, unsigned short type, uint16_t data) {
408         uint16_t rtm_type;
409         int r;
410
411         assert_return(m, -EINVAL);
412
413         r = sd_rtnl_message_get_type(m, &rtm_type);
414         if (r < 0)
415                 return r;
416
417         /* check that the type is correct */
418         switch (rtm_type) {
419                 case RTM_NEWLINK:
420                 case RTM_SETLINK:
421                 case RTM_GETLINK:
422                 case RTM_DELLINK:
423                         switch (type) {
424                                 case IFLA_VLAN_ID:
425                                         break;
426                                 default:
427                                         return -ENOTSUP;
428                         }
429                         break;
430                 default:
431                         return -ENOTSUP;
432         }
433
434         r = add_rtattr(m, type, &data, sizeof(uint16_t));
435         if (r < 0)
436                 return r;
437
438         return 0;
439 }
440
441 int sd_rtnl_message_append_u32(sd_rtnl_message *m, unsigned short type, uint32_t data) {
442         uint16_t rtm_type;
443         int r;
444
445         assert_return(m, -EINVAL);
446
447         r = sd_rtnl_message_get_type(m, &rtm_type);
448         if (r < 0)
449                 return r;
450
451         /* check that the type is correct */
452         switch (rtm_type) {
453                 case RTM_NEWLINK:
454                 case RTM_SETLINK:
455                 case RTM_GETLINK:
456                 case RTM_DELLINK:
457                         switch (type) {
458                                 case IFLA_MASTER:
459                                 case IFLA_MTU:
460                                 case IFLA_LINK:
461                                         break;
462                                 default:
463                                         return -ENOTSUP;
464                         }
465                         break;
466                 case RTM_NEWROUTE:
467                 case RTM_GETROUTE:
468                 case RTM_DELROUTE:
469                         switch (type) {
470                                 case RTA_TABLE:
471                                 case RTA_PRIORITY:
472                                 case RTA_IIF:
473                                 case RTA_OIF:
474                                         break;
475                                 default:
476                                         return -ENOTSUP;
477                         }
478                         break;
479                 default:
480                         return -ENOTSUP;
481         }
482
483         r = add_rtattr(m, type, &data, sizeof(uint32_t));
484         if (r < 0)
485                 return r;
486
487         return 0;
488 }
489
490 int sd_rtnl_message_append_in_addr(sd_rtnl_message *m, unsigned short type, const struct in_addr *data) {
491         struct ifaddrmsg *ifa;
492         struct rtmsg *rtm;
493         uint16_t rtm_type;
494         int r;
495
496         assert_return(m, -EINVAL);
497         assert_return(data, -EINVAL);
498
499         r = sd_rtnl_message_get_type(m, &rtm_type);
500         if (r < 0)
501                 return r;
502
503         /* check that the type is correct */
504         switch (rtm_type) {
505                 case RTM_NEWADDR:
506                 case RTM_GETADDR:
507                 case RTM_DELADDR:
508                         switch (type) {
509                                 case IFA_ADDRESS:
510                                 case IFA_LOCAL:
511                                 case IFA_BROADCAST:
512                                 case IFA_ANYCAST:
513                                         ifa = NLMSG_DATA(m->hdr);
514
515                                         if (ifa->ifa_family != AF_INET)
516                                                 return -EINVAL;
517
518                                         break;
519                                 default:
520                                         return -ENOTSUP;
521                         }
522                         break;
523                 case RTM_NEWROUTE:
524                 case RTM_GETROUTE:
525                 case RTM_DELROUTE:
526                         switch (type) {
527                                 case RTA_DST:
528                                 case RTA_SRC:
529                                 case RTA_GATEWAY:
530                                         rtm = NLMSG_DATA(m->hdr);
531
532                                         if (rtm->rtm_family != AF_INET)
533                                                 return -EINVAL;
534
535                                         break;
536                                 default:
537                                         return -ENOTSUP;
538                         }
539                         break;
540                 default:
541                         return -ENOTSUP;
542         }
543
544         r = add_rtattr(m, type, data, sizeof(struct in_addr));
545         if (r < 0)
546                 return r;
547
548         return 0;
549 }
550
551 int sd_rtnl_message_append_in6_addr(sd_rtnl_message *m, unsigned short type, const struct in6_addr *data) {
552         struct ifaddrmsg *ifa;
553         struct rtmsg *rtm;
554         uint16_t rtm_type;
555         int r;
556
557         assert_return(m, -EINVAL);
558         assert_return(data, -EINVAL);
559
560         r = sd_rtnl_message_get_type(m, &rtm_type);
561         if (r < 0)
562                 return r;
563
564         /* check that the type is correct */
565         switch (rtm_type) {
566                 case RTM_NEWADDR:
567                 case RTM_GETADDR:
568                 case RTM_DELADDR:
569                         switch (type) {
570                                 case IFA_ADDRESS:
571                                 case IFA_LOCAL:
572                                 case IFA_BROADCAST:
573                                 case IFA_ANYCAST:
574                                         ifa = NLMSG_DATA(m->hdr);
575
576                                         if (ifa->ifa_family != AF_INET6)
577                                                 return -EINVAL;
578
579                                         break;
580                                 default:
581                                         return -ENOTSUP;
582                         }
583                         break;
584                 case RTM_NEWROUTE:
585                 case RTM_GETROUTE:
586                 case RTM_DELROUTE:
587                         switch (type) {
588                                 case RTA_DST:
589                                 case RTA_SRC:
590                                 case RTA_GATEWAY:
591                                         rtm = NLMSG_DATA(m->hdr);
592
593                                         if (rtm->rtm_family != AF_INET6)
594                                                 return -EINVAL;
595
596                                         break;
597                                 default:
598                                         return -ENOTSUP;
599                         }
600                 default:
601                         return -ENOTSUP;
602         }
603
604         r = add_rtattr(m, type, data, sizeof(struct in6_addr));
605         if (r < 0)
606                 return r;
607
608         return 0;
609 }
610
611 int sd_rtnl_message_append_ether_addr(sd_rtnl_message *m, unsigned short type, const struct ether_addr *data) {
612         uint16_t rtm_type;
613         int r;
614
615         assert_return(m, -EINVAL);
616         assert_return(data, -EINVAL);
617
618         sd_rtnl_message_get_type(m, &rtm_type);
619
620         switch (rtm_type) {
621                 case RTM_NEWLINK:
622                 case RTM_SETLINK:
623                 case RTM_DELLINK:
624                 case RTM_GETLINK:
625                         switch (type) {
626                                 case IFLA_ADDRESS:
627                                 case IFLA_BROADCAST:
628                                         break;
629                                 default:
630                                         return -ENOTSUP;
631                         }
632                         break;
633                 default:
634                         return -ENOTSUP;
635         }
636
637         r = add_rtattr(m, type, data, ETH_ALEN);
638         if (r < 0)
639                 return r;
640
641         return 0;
642 }
643
644 int sd_rtnl_message_open_container(sd_rtnl_message *m, unsigned short type) {
645         uint16_t rtm_type;
646
647         assert_return(m, -EINVAL);
648         assert_return(!CURRENT_CONTAINER(m), -EINVAL);
649
650         sd_rtnl_message_get_type(m, &rtm_type);
651
652         if (message_type_is_link(rtm_type)) {
653                 if (type == IFLA_LINKINFO)
654                         return add_rtattr(m, type, NULL, 0);
655                 else
656                         return -ENOTSUP;
657         } else
658                 return -ENOTSUP;
659
660         return 0;
661 }
662
663 int sd_rtnl_message_close_container(sd_rtnl_message *m) {
664         assert_return(m, -EINVAL);
665         assert_return(CURRENT_CONTAINER(m), -EINVAL);
666
667         m->container_offset = 0;
668
669         return 0;
670 }
671
672 int sd_rtnl_message_read(sd_rtnl_message *m, unsigned short *type, void **data) {
673         size_t remaining_size;
674         uint16_t rtm_type;
675         int r;
676
677         assert(m);
678         assert(m->next_rta_offset);
679         assert(type);
680         assert(data);
681
682         remaining_size = m->hdr->nlmsg_len - m->next_rta_offset;
683
684         if (!RTA_OK(NEXT_RTA(m), remaining_size))
685                 return 0;
686
687         /* make sure we don't try to read a container
688          * TODO: add support for entering containers for reading */
689         r = sd_rtnl_message_get_type(m, &rtm_type);
690         if (r < 0)
691                 return r;
692
693         if (message_type_is_link(rtm_type) &&
694             NEXT_RTA(m)->rta_type == IFLA_LINKINFO)
695                return -EINVAL;
696
697         *data = RTA_DATA(NEXT_RTA(m));
698         *type = NEXT_RTA(m)->rta_type;
699
700         UPDATE_RTA(m, RTA_NEXT(NEXT_RTA(m), remaining_size));
701
702         return 1;
703 }
704
705 uint32_t message_get_serial(sd_rtnl_message *m) {
706         assert(m);
707         assert(m->hdr);
708
709         return m->hdr->nlmsg_seq;
710 }
711
712 int sd_rtnl_message_get_errno(sd_rtnl_message *m) {
713         struct nlmsgerr *err;
714
715         assert_return(m, -EINVAL);
716         assert_return(m->hdr, -EINVAL);
717
718         if (m->hdr->nlmsg_type != NLMSG_ERROR)
719                 return 0;
720
721         err = NLMSG_DATA(m->hdr);
722
723         return err->error;
724 }
725
726 int message_seal(sd_rtnl *nl, sd_rtnl_message *m) {
727         assert(nl);
728         assert(m);
729         assert(m->hdr);
730
731         if (m->sealed)
732                 return -EPERM;
733
734         m->hdr->nlmsg_seq = nl->serial++;
735         m->sealed = true;
736
737         return 0;
738 }
739
740 static int message_receive_need(sd_rtnl *rtnl, size_t *need) {
741         assert(rtnl);
742         assert(need);
743
744         /* ioctl(rtnl->fd, FIONREAD, &need)
745            Does not appear to work on netlink sockets. libnl uses
746            MSG_PEEK instead. I don't know if that is worth the
747            extra roundtrip.
748
749            For now we simply use the maximum message size the kernel
750            may use (NLMSG_GOODSIZE), and then realloc to the actual
751            size after reading the message (hence avoiding huge memory
752            usage in case many small messages are kept around) */
753         *need = page_size();
754         if (*need > 8192UL)
755                 *need = 8192UL;
756
757         return 0;
758 }
759
760 /* returns the number of bytes sent, or a negative error code */
761 int socket_write_message(sd_rtnl *nl, sd_rtnl_message *m) {
762         union {
763                 struct sockaddr sa;
764                 struct sockaddr_nl nl;
765         } addr = {
766                 .nl.nl_family = AF_NETLINK,
767         };
768         ssize_t k;
769
770         assert(nl);
771         assert(m);
772         assert(m->hdr);
773
774         k = sendto(nl->fd, m->hdr, m->hdr->nlmsg_len,
775                         0, &addr.sa, sizeof(addr));
776         if (k < 0)
777                 return (errno == EAGAIN) ? 0 : -errno;
778
779         return k;
780 }
781
782 /* On success, the number of bytes received is returned and *ret points to the received message
783  * which has a valid header and the correct size.
784  * If nothing useful was received 0 is returned.
785  * On failure, a negative error code is returned.
786  */
787 int socket_read_message(sd_rtnl *nl, sd_rtnl_message **ret) {
788         sd_rtnl_message *m;
789         union {
790                 struct sockaddr sa;
791                 struct sockaddr_nl nl;
792         } addr;
793         socklen_t addr_len;
794         int r;
795         ssize_t k;
796         size_t need;
797
798         assert(nl);
799         assert(ret);
800
801         r = message_receive_need(nl, &need);
802         if (r < 0)
803                 return r;
804
805         r = message_new(&m, need);
806         if (r < 0)
807                 return r;
808
809         addr_len = sizeof(addr);
810
811         k = recvfrom(nl->fd, m->hdr, need,
812                         0, &addr.sa, &addr_len);
813         if (k < 0)
814                 k = (errno == EAGAIN) ? 0 : -errno; /* no data */
815         else if (k == 0)
816                 k = -ECONNRESET; /* connection was closed by the kernel */
817         else if (addr_len != sizeof(addr.nl) ||
818                         addr.nl.nl_family != AF_NETLINK)
819                 k = -EIO; /* not a netlink message */
820         else if (addr.nl.nl_pid != 0)
821                 k = 0; /* not from the kernel */
822         else if ((size_t) k < sizeof(struct nlmsghdr) ||
823                         (size_t) k < m->hdr->nlmsg_len)
824                 k = -EIO; /* too small (we do accept too big though) */
825         else if (m->hdr->nlmsg_pid && m->hdr->nlmsg_pid != nl->sockaddr.nl.nl_pid)
826                 k = 0; /* not broadcast and not for us */
827
828         if (k > 0)
829                 switch (m->hdr->nlmsg_type) {
830                         struct ifinfomsg *ifi;
831                         struct ifaddrmsg *ifa;
832                         struct rtmsg *rtm;
833
834                         /* check that the size matches the message type */
835                         case NLMSG_ERROR:
836                                 if (m->hdr->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr)))
837                                         k = -EIO;
838                                 break;
839                         case RTM_NEWLINK:
840                         case RTM_SETLINK:
841                         case RTM_DELLINK:
842                         case RTM_GETLINK:
843                                 if (m->hdr->nlmsg_len < NLMSG_LENGTH(sizeof(struct ifinfomsg)))
844                                         k = -EIO;
845                                 else {
846                                         ifi = NLMSG_DATA(m->hdr);
847                                         UPDATE_RTA(m, IFLA_RTA(ifi));
848                                 }
849                                 break;
850                         case RTM_NEWADDR:
851                         case RTM_DELADDR:
852                         case RTM_GETADDR:
853                                 if (m->hdr->nlmsg_len < NLMSG_LENGTH(sizeof(struct ifaddrmsg)))
854                                         k = -EIO;
855                                 else {
856                                         ifa = NLMSG_DATA(m->hdr);
857                                         UPDATE_RTA(m, IFA_RTA(ifa));
858                                 }
859                                 break;
860                         case RTM_NEWROUTE:
861                         case RTM_DELROUTE:
862                         case RTM_GETROUTE:
863                                 if (m->hdr->nlmsg_len < NLMSG_LENGTH(sizeof(struct rtmsg)))
864                                         k = -EIO;
865                                 else {
866                                         rtm = NLMSG_DATA(m->hdr);
867                                         UPDATE_RTA(m, RTM_RTA(rtm));
868                                 }
869                                 break;
870                         case NLMSG_NOOP:
871                                 k = 0;
872                                 break;
873                         default:
874                                 k = 0; /* ignoring message of unknown type */
875                 }
876
877         if (k <= 0)
878                 sd_rtnl_message_unref(m);
879         else {
880                 /* we probably allocated way too much memory, give it back */
881                 m->hdr = realloc(m->hdr, m->hdr->nlmsg_len);
882                 *ret = m;
883         }
884
885         return k;
886 }
887
888 int sd_rtnl_message_rewind(sd_rtnl_message *m) {
889         struct ifinfomsg *ifi;
890         struct ifaddrmsg *ifa;
891         struct rtmsg *rtm;
892
893         assert_return(m, -EINVAL);
894         assert_return(m->hdr, -EINVAL);
895
896         switch(m->hdr->nlmsg_type) {
897                 case RTM_NEWLINK:
898                 case RTM_SETLINK:
899                 case RTM_GETLINK:
900                 case RTM_DELLINK:
901                         ifi = NLMSG_DATA(m->hdr);
902                         UPDATE_RTA(m, IFLA_RTA(ifi));
903
904                         break;
905                 case RTM_NEWADDR:
906                 case RTM_GETADDR:
907                 case RTM_DELADDR:
908                         ifa = NLMSG_DATA(m->hdr);
909                         UPDATE_RTA(m, IFA_RTA(ifa));
910
911                         break;
912                 case RTM_NEWROUTE:
913                 case RTM_GETROUTE:
914                 case RTM_DELROUTE:
915                         rtm = NLMSG_DATA(m->hdr);
916                         UPDATE_RTA(m, RTM_RTA(rtm));
917
918                         break;
919                 default:
920                         return -ENOTSUP;
921         }
922
923         return 0;
924 }