1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2013 Tom Gundersen <teg@jklm.no>
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.
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.
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/>.
22 #include <linux/rtnetlink.h>
23 #include <netinet/in.h>
24 #include <netinet/ether.h>
32 #include "rtnl-internal.h"
34 struct sd_rtnl_message {
39 struct rtattr *current_container;
41 struct rtattr *next_rta;
42 size_t remaining_size;
47 static int message_new(sd_rtnl_message **ret, size_t initial_size) {
50 assert_return(ret, -EINVAL);
51 assert_return(initial_size >= sizeof(struct nlmsghdr), -EINVAL);
53 m = new0(sd_rtnl_message, 1);
57 m->hdr = malloc0(initial_size);
63 m->n_ref = REFCNT_INIT;
65 m->hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
73 int message_new_synthetic_error(int error, uint32_t serial, sd_rtnl_message **ret) {
79 r = message_new(ret, NLMSG_SPACE(sizeof(struct nlmsgerr)));
83 (*ret)->hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct nlmsgerr));
84 (*ret)->hdr->nlmsg_type = NLMSG_ERROR;
85 (*ret)->hdr->nlmsg_seq = serial;
87 err = NLMSG_DATA((*ret)->hdr);
94 bool message_type_is_route(uint16_t type) {
105 bool message_type_is_link(uint16_t type) {
117 bool message_type_is_addr(uint16_t type) {
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) {
136 assert_return(message_type_is_route(nlmsg_type), -EINVAL);
137 assert_return(ret, -EINVAL);
139 r = message_new(ret, NLMSG_SPACE(sizeof(struct rtmsg)));
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;
148 rtm = NLMSG_DATA((*ret)->hdr);
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;
163 int sd_rtnl_message_link_set_flags(sd_rtnl_message *m, unsigned flags) {
164 struct ifinfomsg *ifi;
166 ifi = NLMSG_DATA(m->hdr);
168 ifi->ifi_flags = flags;
173 int sd_rtnl_message_link_set_type(sd_rtnl_message *m, unsigned type) {
174 struct ifinfomsg *ifi;
176 ifi = NLMSG_DATA(m->hdr);
178 ifi->ifi_type = type;
183 int sd_rtnl_message_link_new(uint16_t nlmsg_type, int index, sd_rtnl_message **ret) {
184 struct ifinfomsg *ifi;
187 assert_return(message_type_is_link(nlmsg_type), -EINVAL);
188 assert_return(nlmsg_type == RTM_NEWLINK || index > 0, -EINVAL);
189 assert_return(ret, -EINVAL);
191 r = message_new(ret, NLMSG_SPACE(sizeof(struct ifinfomsg)));
195 (*ret)->hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
196 (*ret)->hdr->nlmsg_type = nlmsg_type;
197 if (nlmsg_type == RTM_NEWLINK)
198 (*ret)->hdr->nlmsg_flags |= NLM_F_CREATE;
200 ifi = NLMSG_DATA((*ret)->hdr);
202 ifi->ifi_family = AF_UNSPEC;
203 ifi->ifi_index = index;
204 ifi->ifi_change = 0xffffffff;
209 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) {
210 struct ifaddrmsg *ifa;
213 assert_return(message_type_is_addr(nlmsg_type), -EINVAL);
214 assert_return(index > 0, -EINVAL);
215 assert_return(ret, -EINVAL);
217 r = message_new(ret, NLMSG_SPACE(sizeof(struct ifaddrmsg)));
221 (*ret)->hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
222 (*ret)->hdr->nlmsg_type = nlmsg_type;
224 ifa = NLMSG_DATA((*ret)->hdr);
226 ifa->ifa_family = family;
227 ifa->ifa_prefixlen = prefixlen;
228 ifa->ifa_flags = flags;
229 ifa->ifa_scope = scope;
230 ifa->ifa_index = index;
235 sd_rtnl_message *sd_rtnl_message_ref(sd_rtnl_message *m) {
237 assert_se(REFCNT_INC(m->n_ref) >= 2);
242 sd_rtnl_message *sd_rtnl_message_unref(sd_rtnl_message *m) {
243 if (m && REFCNT_DEC(m->n_ref) <= 0) {
251 int sd_rtnl_message_get_type(sd_rtnl_message *m, uint16_t *type) {
252 assert_return(m, -EINVAL);
253 assert_return(type, -EINVAL);
255 *type = m->hdr->nlmsg_type;
260 int sd_rtnl_message_link_get_ifindex(sd_rtnl_message *m, int *ifindex) {
261 struct ifinfomsg *ifi;
263 assert_return(m, -EINVAL);
264 assert_return(m->hdr, -EINVAL);
265 assert_return(message_type_is_link(m->hdr->nlmsg_type), -EINVAL);
266 assert_return(ifindex, -EINVAL);
268 ifi = NLMSG_DATA(m->hdr);
270 *ifindex = ifi->ifi_index;
275 int sd_rtnl_message_link_get_flags(sd_rtnl_message *m, unsigned *flags) {
276 struct ifinfomsg *ifi;
278 assert_return(m, -EINVAL);
279 assert_return(m->hdr, -EINVAL);
280 assert_return(message_type_is_link(m->hdr->nlmsg_type), -EINVAL);
281 assert_return(flags, -EINVAL);
283 ifi = NLMSG_DATA(m->hdr);
285 *flags = ifi->ifi_flags;
290 /* If successful the updated message will be correctly aligned, if unsuccessful the old message is
292 static int add_rtattr(sd_rtnl_message *m, unsigned short type, const void *data, size_t data_length) {
293 uint32_t rta_length, message_length;
294 struct nlmsghdr *new_hdr;
300 assert(NLMSG_ALIGN(m->hdr->nlmsg_len) == m->hdr->nlmsg_len);
301 assert(!data || data_length > 0);
303 /* get the size of the new rta attribute (with padding at the end) */
304 rta_length = RTA_LENGTH(data_length);
305 /* get the new message size (with padding at the end)
307 message_length = m->hdr->nlmsg_len + RTA_ALIGN(rta_length);
309 /* realloc to fit the new attribute */
310 new_hdr = realloc(m->hdr, message_length);
315 /* get pointer to the attribute we are about to add */
316 rta = (struct rtattr *) ((uint8_t *) m->hdr + m->hdr->nlmsg_len);
317 /* update message size */
318 m->hdr->nlmsg_len = message_length;
320 /* we are inside a container, extend it */
321 if (m->current_container)
322 m->current_container->rta_len = (unsigned char *) m->hdr +
324 (unsigned char *) m->current_container;
326 /* fill in the attribute */
327 rta->rta_type = type;
328 rta->rta_len = rta_length;
330 /* this is a container, set pointer */
331 m->current_container = rta;
333 /* we don't deal with the case where the user lies about the type
334 * and gives us too little data (so don't do that)
336 padding = mempcpy(RTA_DATA(rta), data, data_length);
337 /* make sure also the padding at the end of the message is initialized */
338 memset(padding, '\0', (unsigned char *) m->hdr +
340 (unsigned char *) padding);
346 int sd_rtnl_message_append(sd_rtnl_message *m, unsigned short type, const void *data) {
348 struct ifaddrmsg *ifa;
351 assert_return(m, -EINVAL);
352 assert_return(data, -EINVAL);
354 sd_rtnl_message_get_type(m, &rtm_type);
356 if (m->current_container) {
362 switch (m->current_container->rta_type) {
366 return add_rtattr(m, type, data, strlen(data) + 1);
387 return add_rtattr(m, type, data, strlen(data) + 1);
391 return add_rtattr(m, type, data, sizeof(uint32_t));
393 return add_rtattr(m, type, data, sizeof(struct rtnl_link_stats));
396 return add_rtattr(m, type, data, ETH_ALEN);
405 return add_rtattr(m, type, data, strlen(data) + 1);
410 ifa = NLMSG_DATA(m->hdr);
411 switch (ifa->ifa_family) {
413 return add_rtattr(m, type, data, sizeof(struct in_addr));
415 return add_rtattr(m, type, data, sizeof(struct in6_addr));
429 rtm = NLMSG_DATA(m->hdr);
430 switch (rtm->rtm_family) {
432 return add_rtattr(m, type, data, sizeof(struct in_addr));
434 return add_rtattr(m, type, data, sizeof(struct in6_addr));
442 return add_rtattr(m, type, data, sizeof(uint32_t));
451 int sd_rtnl_message_open_container(sd_rtnl_message *m, unsigned short type) {
454 assert_return(m, -EINVAL);
455 assert_return(!m->current_container, -EINVAL);
457 sd_rtnl_message_get_type(m, &rtm_type);
459 if (message_type_is_link(rtm_type)) {
460 if (type == IFLA_LINKINFO)
461 return add_rtattr(m, type, NULL, 0);
470 int sd_rtnl_message_close_container(sd_rtnl_message *m) {
471 assert_return(m, -EINVAL);
472 assert_return(m->current_container, -EINVAL);
474 m->current_container = NULL;
479 static int message_read(sd_rtnl_message *m, unsigned short *type, void **data) {
488 if (!RTA_OK(m->next_rta, m->remaining_size))
491 /* make sure we don't try to read a container
492 * TODO: add support for entering containers for reading */
493 r = sd_rtnl_message_get_type(m, &rtm_type);
502 if (m->next_rta->rta_type == IFLA_LINKINFO) {
507 *data = RTA_DATA(m->next_rta);
508 *type = m->next_rta->rta_type;
510 m->next_rta = RTA_NEXT(m->next_rta, m->remaining_size);
515 int sd_rtnl_message_read(sd_rtnl_message *m, unsigned short *type, void **data) {
519 assert_return(m, -EINVAL);
520 assert_return(data, -EINVAL);
522 r = sd_rtnl_message_get_type(m, &rtm_type);
532 struct ifinfomsg *ifi = NLMSG_DATA(m->hdr);
534 m->next_rta = IFLA_RTA(ifi);
535 m->remaining_size = IFLA_PAYLOAD(m->hdr);
542 struct ifaddrmsg *ifa = NLMSG_DATA(m->hdr);
544 m->next_rta = IFA_RTA(ifa);
545 m->remaining_size = IFA_PAYLOAD(m->hdr);
552 struct rtmesg *rtm = NLMSG_DATA(m->hdr);
554 m->next_rta = RTM_RTA(rtm);
555 m->remaining_size = RTM_PAYLOAD(m->hdr);
562 return message_read(m, type, data);
565 uint32_t message_get_serial(sd_rtnl_message *m) {
569 return m->hdr->nlmsg_seq;
572 int sd_rtnl_message_get_errno(sd_rtnl_message *m) {
573 struct nlmsgerr *err;
575 assert_return(m, -EINVAL);
576 assert_return(m->hdr, -EINVAL);
578 if (m->hdr->nlmsg_type != NLMSG_ERROR)
581 err = NLMSG_DATA(m->hdr);
586 int message_seal(sd_rtnl *nl, sd_rtnl_message *m) {
594 m->hdr->nlmsg_seq = nl->serial++;
600 static int message_receive_need(sd_rtnl *rtnl, size_t *need) {
604 /* ioctl(rtnl->fd, FIONREAD, &need)
605 Does not appear to work on netlink sockets. libnl uses
606 MSG_PEEK instead. I don't know if that is worth the
609 For now we simply use the maximum message size the kernel
610 may use (NLMSG_GOODSIZE), and then realloc to the actual
611 size after reading the message (hence avoiding huge memory
612 usage in case many small messages are kept around) */
620 /* returns the number of bytes sent, or a negative error code */
621 int socket_write_message(sd_rtnl *nl, sd_rtnl_message *m) {
624 struct sockaddr_nl nl;
626 .nl.nl_family = AF_NETLINK,
634 k = sendto(nl->fd, m->hdr, m->hdr->nlmsg_len,
635 0, &addr.sa, sizeof(addr));
637 return (errno == EAGAIN) ? 0 : -errno;
642 /* On success, the number of bytes received is returned and *ret points to the received message
643 * which has a valid header and the correct size.
644 * If nothing useful was received 0 is returned.
645 * On failure, a negative error code is returned.
647 int socket_read_message(sd_rtnl *nl, sd_rtnl_message **ret) {
651 struct sockaddr_nl nl;
661 r = message_receive_need(nl, &need);
665 r = message_new(&m, need);
669 addr_len = sizeof(addr);
671 k = recvfrom(nl->fd, m->hdr, need,
672 0, &addr.sa, &addr_len);
674 k = (errno == EAGAIN) ? 0 : -errno; /* no data */
676 k = -ECONNRESET; /* connection was closed by the kernel */
677 else if (addr_len != sizeof(addr.nl) ||
678 addr.nl.nl_family != AF_NETLINK)
679 k = -EIO; /* not a netlink message */
680 else if (addr.nl.nl_pid != 0)
681 k = 0; /* not from the kernel */
682 else if ((size_t) k < sizeof(struct nlmsghdr) ||
683 (size_t) k < m->hdr->nlmsg_len)
684 k = -EIO; /* too small (we do accept too big though) */
685 else if (m->hdr->nlmsg_pid && m->hdr->nlmsg_pid != nl->sockaddr.nl.nl_pid)
686 k = 0; /* not broadcast and not for us */
689 switch (m->hdr->nlmsg_type) {
690 /* check that the size matches the message type */
692 if (m->hdr->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr)))
699 if (m->hdr->nlmsg_len < NLMSG_LENGTH(sizeof(struct ifinfomsg)))
705 if (m->hdr->nlmsg_len < NLMSG_LENGTH(sizeof(struct ifaddrmsg)))
711 if (m->hdr->nlmsg_len < NLMSG_LENGTH(sizeof(struct rtmsg)))
718 k = 0; /* ignoring message of unknown type */
722 sd_rtnl_message_unref(m);
724 /* we probably allocated way too much memory, give it back */
725 m->hdr = realloc(m->hdr, m->hdr->nlmsg_len);