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 *next_rta;
40 size_t remaining_size;
45 static int message_new(sd_rtnl_message **ret, size_t initial_size) {
48 assert_return(ret, -EINVAL);
49 assert_return(initial_size >= sizeof(struct nlmsghdr), -EINVAL);
51 m = new0(sd_rtnl_message, 1);
55 m->hdr = malloc0(initial_size);
61 m->n_ref = REFCNT_INIT;
63 m->hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
71 int 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) {
79 assert_return(nlmsg_type == RTM_NEWROUTE || nlmsg_type == RTM_DELROUTE ||
80 nlmsg_type == RTM_GETROUTE, -EINVAL);
81 assert_return(ret, -EINVAL);
83 r = message_new(ret, NLMSG_SPACE(sizeof(struct rtmsg)));
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;
92 rtm = NLMSG_DATA((*ret)->hdr);
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;
107 int sd_rtnl_message_link_new(uint16_t nlmsg_type, int index, unsigned int type, unsigned int flags, sd_rtnl_message **ret) {
108 struct ifinfomsg *ifi;
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);
115 r = message_new(ret, NLMSG_SPACE(sizeof(struct ifinfomsg)));
119 (*ret)->hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
120 (*ret)->hdr->nlmsg_type = nlmsg_type;
122 ifi = NLMSG_DATA((*ret)->hdr);
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;
133 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) {
134 struct ifaddrmsg *ifa;
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);
141 r = message_new(ret, NLMSG_SPACE(sizeof(struct ifaddrmsg)));
145 (*ret)->hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
146 (*ret)->hdr->nlmsg_type = nlmsg_type;
148 ifa = NLMSG_DATA((*ret)->hdr);
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;
159 sd_rtnl_message *sd_rtnl_message_ref(sd_rtnl_message *m) {
161 assert_se(REFCNT_INC(m->n_ref) >= 2);
166 sd_rtnl_message *sd_rtnl_message_unref(sd_rtnl_message *m) {
167 if (m && REFCNT_DEC(m->n_ref) <= 0) {
175 int sd_rtnl_message_get_type(sd_rtnl_message *m, uint16_t *type) {
176 assert_return(m, -EINVAL);
177 assert_return(type, -EINVAL);
179 *type = m->hdr->nlmsg_type;
184 /* If successful the updated message will be correctly aligned, if unsuccessful the old message is
186 static int add_rtattr(sd_rtnl_message *m, unsigned short type, const void *data, size_t data_length) {
187 uint32_t rta_length, message_length;
188 struct nlmsghdr *new_hdr;
192 assert_return(m, -EINVAL);
193 assert_return(m->hdr, -EINVAL);
194 assert_return(NLMSG_ALIGN(m->hdr->nlmsg_len) == m->hdr->nlmsg_len, -EINVAL);
195 assert_return(data, -EINVAL);
196 assert_return(data_length > 0, -EINVAL);
198 /* get the size of the new rta attribute (with padding at the end) */
199 rta_length = RTA_LENGTH(data_length);
200 /* get the new message size (with padding at the end)
202 message_length = m->hdr->nlmsg_len + RTA_ALIGN(rta_length);
204 /* realloc to fit the new attribute */
205 new_hdr = realloc(m->hdr, message_length);
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;
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)
221 padding = mempcpy(RTA_DATA(rta), data, data_length);
222 /* make sure also the padding at the end of the message is initialized */
223 memset(padding, '\0', (unsigned char *) m->hdr + m->hdr->nlmsg_len - (unsigned char *) padding);
228 int sd_rtnl_message_append(sd_rtnl_message *m, unsigned short type, const void *data) {
230 struct ifaddrmsg *ifa;
233 assert_return(m, -EINVAL);
234 assert_return(data, -EINVAL);
236 sd_rtnl_message_get_type(m, &rtm_type);
245 return add_rtattr(m, type, data, strlen(data) + 1);
247 return add_rtattr(m, type, data, sizeof(uint32_t));
249 return add_rtattr(m, type, data, sizeof(uint32_t));
251 return add_rtattr(m, type, data, sizeof(struct rtnl_link_stats));
254 return add_rtattr(m, type, data, ETH_ALEN);
263 return add_rtattr(m, type, data, strlen(data) + 1);
268 ifa = NLMSG_DATA(m->hdr);
269 switch (ifa->ifa_family) {
271 return add_rtattr(m, type, data, sizeof(struct in_addr));
273 return add_rtattr(m, type, data, sizeof(struct in6_addr));
287 rtm = NLMSG_DATA(m->hdr);
288 switch (rtm->rtm_family) {
290 return add_rtattr(m, type, data, sizeof(struct in_addr));
292 return add_rtattr(m, type, data, sizeof(struct in6_addr));
300 return add_rtattr(m, type, data, sizeof(uint32_t));
309 static int message_read(sd_rtnl_message *m, unsigned short *type, void **data) {
310 assert_return(m, -EINVAL);
311 assert_return(data, -EINVAL);
313 if (!RTA_OK(m->next_rta, m->remaining_size))
316 *data = RTA_DATA(m->next_rta);
317 *type = m->next_rta->rta_type;
319 m->next_rta = RTA_NEXT(m->next_rta, m->remaining_size);
324 int sd_rtnl_message_read(sd_rtnl_message *m, unsigned short *type, void **data) {
327 assert_return(m, -EINVAL);
328 assert_return(data, -EINVAL);
330 sd_rtnl_message_get_type(m, &rtm_type);
337 struct ifinfomsg *ifi = NLMSG_DATA(m->hdr);
339 m->next_rta = IFLA_RTA(ifi);
340 m->remaining_size = IFLA_PAYLOAD(m->hdr);
347 struct ifaddrmsg *ifa = NLMSG_DATA(m->hdr);
349 m->next_rta = IFA_RTA(ifa);
350 m->remaining_size = IFA_PAYLOAD(m->hdr);
357 struct rtmesg *rtm = NLMSG_DATA(m->hdr);
359 m->next_rta = RTM_RTA(rtm);
360 m->remaining_size = RTM_PAYLOAD(m->hdr);
367 return message_read(m, type, data);
370 int message_get_serial(sd_rtnl_message *m) {
373 return m->hdr->nlmsg_seq;
376 int message_get_errno(sd_rtnl_message *m) {
377 struct nlmsgerr *err;
381 if (m->hdr->nlmsg_type != NLMSG_ERROR)
384 err = NLMSG_DATA(m->hdr);
389 int message_seal(sd_rtnl *nl, sd_rtnl_message *m) {
393 m->hdr->nlmsg_seq = nl->serial++;
399 static int message_receive_need(sd_rtnl *rtnl, size_t *need) {
400 assert_return(rtnl, -EINVAL);
401 assert_return(need, -EINVAL);
403 /* ioctl(rtnl->fd, FIONREAD, &need)
404 Does not appear to work on netlink sockets. libnl uses
405 MSG_PEEK instead. I don't know if that is worth the
408 For now we simply use the maximum message size the kernel
409 may use (NLMSG_GOODSIZE), and then realloc to the actual
410 size after reading the message (hence avoiding huge memory
411 usage in case many small messages are kept around) */
419 /* returns the number of bytes sent, or a negative error code */
420 int socket_write_message(sd_rtnl *nl, sd_rtnl_message *m) {
423 struct sockaddr_nl nl;
425 .nl.nl_family = AF_NETLINK,
429 assert_return(nl, -EINVAL);
430 assert_return(m, -EINVAL);
432 k = sendto(nl->fd, m->hdr, m->hdr->nlmsg_len,
433 0, &addr.sa, sizeof(addr));
435 return (errno == EAGAIN) ? 0 : -errno;
440 /* On success, the number of bytes received is returned and *ret points to the received message
441 * which has a valid header and the correct size.
442 * If nothing useful was received 0 is returned.
443 * On failure, a negative error code is returned.
445 int socket_read_message(sd_rtnl *nl, sd_rtnl_message **ret) {
449 struct sockaddr_nl nl;
456 assert_return(nl, -EINVAL);
457 assert_return(ret, -EINVAL);
459 r = message_receive_need(nl, &need);
463 r = message_new(&m, need);
467 addr_len = sizeof(addr);
469 k = recvfrom(nl->fd, m->hdr, need,
470 0, &addr.sa, &addr_len);
472 k = (errno == EAGAIN) ? 0 : -errno; /* no data */
474 k = -ECONNRESET; /* connection was closed by the kernel */
475 else if (addr_len != sizeof(addr.nl) ||
476 addr.nl.nl_family != AF_NETLINK)
477 k = -EIO; /* not a netlink message */
478 else if (addr.nl.nl_pid != 0)
479 k = 0; /* not from the kernel */
480 else if ((size_t) k < sizeof(struct nlmsghdr) ||
481 (size_t) k < m->hdr->nlmsg_len)
482 k = -EIO; /* too small (we do accept too big though) */
483 else if (m->hdr->nlmsg_pid != nl->sockaddr.nl.nl_pid)
484 k = 0; /* not for us */
487 switch (m->hdr->nlmsg_type) {
488 /* check that the size matches the message type */
490 if (m->hdr->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr)))
496 if (m->hdr->nlmsg_len < NLMSG_LENGTH(sizeof(struct ifinfomsg)))
502 if (m->hdr->nlmsg_len < NLMSG_LENGTH(sizeof(struct ifaddrmsg)))
509 k = 0; /* ignoring message of unknown type */
513 sd_rtnl_message_unref(m);
515 /* we probably allocated way too much memory, give it back */
516 m->hdr = realloc(m->hdr, m->hdr->nlmsg_len);