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