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;
191 assert_return(m, -EINVAL);
192 assert_return(m->hdr, -EINVAL);
193 assert_return(NLMSG_ALIGN(m->hdr->nlmsg_len) == m->hdr->nlmsg_len, -EINVAL);
194 assert_return(data, -EINVAL);
195 assert_return(data_length > 0, -EINVAL);
197 /* get the size of the new rta attribute (without padding at the end) */
198 rta_length = RTA_LENGTH(data_length);
199 /* get the new message size (with padding between the old message and the new attrib,
200 * but no padding after)
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 memcpy(RTA_DATA(rta), data, data_length);
226 int sd_rtnl_message_append(sd_rtnl_message *m, unsigned short type, const void *data) {
228 struct ifaddrmsg *ifa;
231 assert_return(m, -EINVAL);
232 assert_return(data, -EINVAL);
234 sd_rtnl_message_get_type(m, &rtm_type);
243 return add_rtattr(m, type, data, strlen(data) + 1);
245 return add_rtattr(m, type, data, sizeof(uint32_t));
247 return add_rtattr(m, type, data, sizeof(uint32_t));
249 return add_rtattr(m, type, data, sizeof(struct rtnl_link_stats));
252 return add_rtattr(m, type, data, ETH_ALEN);
261 return add_rtattr(m, type, data, strlen(data) + 1);
266 ifa = NLMSG_DATA(m->hdr);
267 switch (ifa->ifa_family) {
269 return add_rtattr(m, type, data, sizeof(struct in_addr));
271 return add_rtattr(m, type, data, sizeof(struct in6_addr));
285 rtm = NLMSG_DATA(m->hdr);
286 switch (rtm->rtm_family) {
288 return add_rtattr(m, type, data, sizeof(struct in_addr));
290 return add_rtattr(m, type, data, sizeof(struct in6_addr));
298 return add_rtattr(m, type, data, sizeof(uint32_t));
307 static int message_read(sd_rtnl_message *m, unsigned short *type, void **data) {
308 assert_return(m, -EINVAL);
309 assert_return(data, -EINVAL);
311 if (!RTA_OK(m->next_rta, m->remaining_size))
314 *data = RTA_DATA(m->next_rta);
315 *type = m->next_rta->rta_type;
317 m->next_rta = RTA_NEXT(m->next_rta, m->remaining_size);
322 int sd_rtnl_message_read(sd_rtnl_message *m, unsigned short *type, void **data) {
325 assert_return(m, -EINVAL);
326 assert_return(data, -EINVAL);
328 sd_rtnl_message_get_type(m, &rtm_type);
335 struct ifinfomsg *ifi = NLMSG_DATA(m->hdr);
337 m->next_rta = IFLA_RTA(ifi);
338 m->remaining_size = IFLA_PAYLOAD(m->hdr);
345 struct ifaddrmsg *ifa = NLMSG_DATA(m->hdr);
347 m->next_rta = IFA_RTA(ifa);
348 m->remaining_size = IFA_PAYLOAD(m->hdr);
355 struct rtmesg *rtm = NLMSG_DATA(m->hdr);
357 m->next_rta = RTM_RTA(rtm);
358 m->remaining_size = RTM_PAYLOAD(m->hdr);
365 return message_read(m, type, data);
368 int message_get_serial(sd_rtnl_message *m) {
371 return m->hdr->nlmsg_seq;
374 int message_get_errno(sd_rtnl_message *m) {
375 struct nlmsgerr *err;
379 if (m->hdr->nlmsg_type != NLMSG_ERROR)
382 err = NLMSG_DATA(m->hdr);
387 int message_seal(sd_rtnl *nl, sd_rtnl_message *m) {
391 m->hdr->nlmsg_seq = nl->serial++;
397 static int message_receive_need(sd_rtnl *rtnl, size_t *need) {
398 assert_return(rtnl, -EINVAL);
399 assert_return(need, -EINVAL);
401 /* ioctl(rtnl->fd, FIONREAD, &need)
402 Does not appear to work on netlink sockets. libnl uses
403 MSG_PEEK instead. I don't know if that is worth the
406 For now we simply use the maximum message size the kernel
407 may use (NLMSG_GOODSIZE), and then realloc to the actual
408 size after reading the message (hence avoiding huge memory
409 usage in case many small messages are kept around) */
417 /* returns the number of bytes sent, or a negative error code */
418 int socket_write_message(sd_rtnl *nl, sd_rtnl_message *m) {
421 struct sockaddr_nl nl;
423 .nl.nl_family = AF_NETLINK,
427 assert_return(nl, -EINVAL);
428 assert_return(m, -EINVAL);
430 k = sendto(nl->fd, m->hdr, m->hdr->nlmsg_len,
431 0, &addr.sa, sizeof(addr));
433 return (errno == EAGAIN) ? 0 : -errno;
438 /* On success, the number of bytes received is returned and *ret points to the received message
439 * which has a valid header and the correct size.
440 * If nothing useful was received 0 is returned.
441 * On failure, a negative error code is returned.
443 int socket_read_message(sd_rtnl *nl, sd_rtnl_message **ret) {
447 struct sockaddr_nl nl;
454 assert_return(nl, -EINVAL);
455 assert_return(ret, -EINVAL);
457 r = message_receive_need(nl, &need);
461 r = message_new(&m, need);
465 addr_len = sizeof(addr);
467 k = recvfrom(nl->fd, m->hdr, need,
468 0, &addr.sa, &addr_len);
470 k = (errno == EAGAIN) ? 0 : -errno; /* no data */
472 k = -ECONNRESET; /* connection was closed by the kernel */
473 else if (addr_len != sizeof(addr.nl) ||
474 addr.nl.nl_family != AF_NETLINK)
475 k = -EIO; /* not a netlink message */
476 else if (addr.nl.nl_pid != 0)
477 k = 0; /* not from the kernel */
478 else if ((size_t) k < sizeof(struct nlmsghdr) ||
479 (size_t) k < m->hdr->nlmsg_len)
480 k = -EIO; /* too small (we do accept too big though) */
481 else if (m->hdr->nlmsg_pid != nl->sockaddr.nl.nl_pid)
482 k = 0; /* not for us */
485 switch (m->hdr->nlmsg_type) {
486 /* check that the size matches the message type */
488 if (m->hdr->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr)))
494 if (m->hdr->nlmsg_len < NLMSG_LENGTH(sizeof(struct ifinfomsg)))
500 if (m->hdr->nlmsg_len < NLMSG_LENGTH(sizeof(struct ifaddrmsg)))
507 k = 0; /* ignoring message of unknown type */
511 sd_rtnl_message_unref(m);
513 /* we probably allocated way too much memory, give it back */
514 m->hdr = realloc(m->hdr, m->hdr->nlmsg_len);