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 <netinet/in.h>
23 #include <netinet/ether.h>
31 #include "rtnl-internal.h"
33 struct sd_rtnl_message {
38 struct rtattr *next_rta;
39 size_t remaining_size;
44 static int message_new(sd_rtnl_message **ret, size_t initial_size) {
47 assert_return(ret, -EINVAL);
48 assert_return(initial_size >= sizeof(struct nlmsghdr), -EINVAL);
50 m = new0(sd_rtnl_message, 1);
54 m->hdr = malloc0(initial_size);
60 m->n_ref = REFCNT_INIT;
62 m->hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
70 int sd_rtnl_message_route_new(uint16_t nlmsg_type, unsigned char rtm_family,
71 unsigned char rtm_dst_len, unsigned char rtm_src_len,
72 unsigned char rtm_tos, unsigned char rtm_table,
73 unsigned char rtm_scope, unsigned char rtm_protocol,
74 unsigned char rtm_type, unsigned rtm_flags, sd_rtnl_message **ret) {
78 assert_return(nlmsg_type == RTM_NEWROUTE || nlmsg_type == RTM_DELROUTE ||
79 nlmsg_type == RTM_GETROUTE, -EINVAL);
80 assert_return(ret, -EINVAL);
82 r = message_new(ret, NLMSG_SPACE(sizeof(struct rtmsg)));
86 (*ret)->hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
87 (*ret)->hdr->nlmsg_type = nlmsg_type;
88 if (nlmsg_type == RTM_NEWROUTE)
89 (*ret)->hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_EXCL;
91 rtm = NLMSG_DATA((*ret)->hdr);
93 rtm->rtm_family = rtm_family;
94 rtm->rtm_dst_len = rtm_dst_len;
95 rtm->rtm_src_len = rtm_src_len;
96 rtm->rtm_tos = rtm_tos;
97 rtm->rtm_table = rtm_table;
98 rtm->rtm_protocol = rtm_protocol;
99 rtm->rtm_scope = rtm_scope;
100 rtm->rtm_type = rtm_type;
101 rtm->rtm_flags = rtm_flags;
106 int sd_rtnl_message_link_new(uint16_t nlmsg_type, int index, unsigned int type, unsigned int flags, sd_rtnl_message **ret) {
107 struct ifinfomsg *ifi;
110 assert_return(nlmsg_type == RTM_NEWLINK || nlmsg_type == RTM_DELLINK || nlmsg_type == RTM_GETLINK, -EINVAL);
111 assert_return(index > 0, -EINVAL);
112 assert_return(ret, -EINVAL);
114 r = message_new(ret, NLMSG_SPACE(sizeof(struct ifinfomsg)));
118 (*ret)->hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
119 (*ret)->hdr->nlmsg_type = nlmsg_type;
121 ifi = NLMSG_DATA((*ret)->hdr);
123 ifi->ifi_family = AF_UNSPEC;
124 ifi->ifi_index = index;
125 ifi->ifi_type = type;
126 ifi->ifi_flags = flags;
127 ifi->ifi_change = 0xffffffff;
132 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) {
133 struct ifaddrmsg *ifa;
136 assert_return(nlmsg_type == RTM_NEWADDR || nlmsg_type == RTM_DELADDR || nlmsg_type == RTM_GETADDR, -EINVAL);
137 assert_return(index > 0, -EINVAL);
138 assert_return(ret, -EINVAL);
140 r = message_new(ret, NLMSG_SPACE(sizeof(struct ifaddrmsg)));
144 (*ret)->hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
145 (*ret)->hdr->nlmsg_type = nlmsg_type;
147 ifa = NLMSG_DATA((*ret)->hdr);
149 ifa->ifa_family = family;
150 ifa->ifa_prefixlen = prefixlen;
151 ifa->ifa_flags = flags;
152 ifa->ifa_scope = scope;
153 ifa->ifa_index = index;
158 sd_rtnl_message *sd_rtnl_message_ref(sd_rtnl_message *m) {
160 assert_se(REFCNT_INC(m->n_ref) >= 2);
165 sd_rtnl_message *sd_rtnl_message_unref(sd_rtnl_message *m) {
166 if (m && REFCNT_DEC(m->n_ref) <= 0) {
174 int sd_rtnl_message_get_type(sd_rtnl_message *m, uint16_t *type) {
175 assert_return(m, -EINVAL);
176 assert_return(type, -EINVAL);
178 *type = m->hdr->nlmsg_type;
183 /* If successful the updated message will be correctly aligned, if unsuccessful the old message is
185 static int add_rtattr(sd_rtnl_message *m, unsigned short type, const void *data, size_t data_length) {
186 uint32_t rta_length, message_length;
187 struct nlmsghdr *new_hdr;
190 assert_return(m, -EINVAL);
191 assert_return(m->hdr, -EINVAL);
192 assert_return(NLMSG_ALIGN(m->hdr->nlmsg_len) == m->hdr->nlmsg_len, -EINVAL);
193 assert_return(data, -EINVAL);
194 assert_return(data_length > 0, -EINVAL);
196 /* get the size of the new rta attribute (without padding at the end) */
197 rta_length = RTA_LENGTH(data_length);
198 /* get the new message size (with padding between the old message and the new attrib,
199 * but no padding after)
201 message_length = m->hdr->nlmsg_len + RTA_ALIGN(rta_length);
203 /* realloc to fit the new attribute */
204 new_hdr = realloc(m->hdr, message_length);
209 /* get pointer to the attribute we are about to add */
210 rta = (struct rtattr *) ((uint8_t *) m->hdr + m->hdr->nlmsg_len);
211 /* update message size */
212 m->hdr->nlmsg_len = message_length;
214 /* fill in the attribute */
215 rta->rta_type = type;
216 rta->rta_len = rta_length;
217 /* we don't deal with the case where the user lies about the type and gives us
218 * too little data (so don't do that)
220 memcpy(RTA_DATA(rta), data, data_length);
225 int sd_rtnl_message_append(sd_rtnl_message *m, unsigned short type, const void *data) {
227 struct ifaddrmsg *ifa;
230 assert_return(m, -EINVAL);
231 assert_return(data, -EINVAL);
233 sd_rtnl_message_get_type(m, &rtm_type);
242 return add_rtattr(m, type, data, strlen(data) + 1);
244 return add_rtattr(m, type, data, sizeof(uint32_t));
246 return add_rtattr(m, type, data, sizeof(uint32_t));
248 return add_rtattr(m, type, data, sizeof(struct rtnl_link_stats));
251 return add_rtattr(m, type, data, ETH_ALEN);
260 return add_rtattr(m, type, data, strlen(data) + 1);
265 ifa = NLMSG_DATA(m->hdr);
266 switch (ifa->ifa_family) {
268 return add_rtattr(m, type, data, sizeof(struct in_addr));
270 return add_rtattr(m, type, data, sizeof(struct in6_addr));
284 rtm = NLMSG_DATA(m->hdr);
285 switch (rtm->rtm_family) {
287 return add_rtattr(m, type, data, sizeof(struct in_addr));
289 return add_rtattr(m, type, data, sizeof(struct in6_addr));
297 return add_rtattr(m, type, data, sizeof(uint32_t));
306 static int message_read(sd_rtnl_message *m, unsigned short *type, void **data) {
307 assert_return(m, -EINVAL);
308 assert_return(data, -EINVAL);
310 if (!RTA_OK(m->next_rta, m->remaining_size))
313 *data = RTA_DATA(m->next_rta);
314 *type = m->next_rta->rta_type;
316 m->next_rta = RTA_NEXT(m->next_rta, m->remaining_size);
321 int sd_rtnl_message_read(sd_rtnl_message *m, unsigned short *type, void **data) {
324 assert_return(m, -EINVAL);
325 assert_return(data, -EINVAL);
327 sd_rtnl_message_get_type(m, &rtm_type);
334 struct ifinfomsg *ifi = NLMSG_DATA(m->hdr);
336 m->next_rta = IFLA_RTA(ifi);
337 m->remaining_size = IFLA_PAYLOAD(m->hdr);
344 struct ifaddrmsg *ifa = NLMSG_DATA(m->hdr);
346 m->next_rta = IFA_RTA(ifa);
347 m->remaining_size = IFA_PAYLOAD(m->hdr);
354 struct rtmesg *rtm = NLMSG_DATA(m->hdr);
356 m->next_rta = RTM_RTA(rtm);
357 m->remaining_size = RTM_PAYLOAD(m->hdr);
364 return message_read(m, type, data);
367 int message_get_serial(sd_rtnl_message *m) {
370 return m->hdr->nlmsg_seq;
373 int message_get_errno(sd_rtnl_message *m) {
374 struct nlmsgerr *err;
378 if (m->hdr->nlmsg_type != NLMSG_ERROR)
381 err = NLMSG_DATA(m->hdr);
386 int message_seal(sd_rtnl *nl, sd_rtnl_message *m) {
390 m->hdr->nlmsg_seq = nl->serial++;
396 static int message_receive_need(sd_rtnl *rtnl, size_t *need) {
397 assert_return(rtnl, -EINVAL);
398 assert_return(need, -EINVAL);
400 /* ioctl(rtnl->fd, FIONREAD, &need)
401 Does not appear to work on netlink sockets. libnl uses
402 MSG_PEEK instead. I don't know if that is worth the
405 For now we simply use the maximum message size the kernel
406 may use (NLMSG_GOODSIZE), and then realloc to the actual
407 size after reading the message (hence avoiding huge memory
408 usage in case many small messages are kept around) */
416 /* returns the number of bytes sent, or a negative error code */
417 int socket_write_message(sd_rtnl *nl, sd_rtnl_message *m) {
420 struct sockaddr_nl nl;
422 .nl.nl_family = AF_NETLINK,
426 assert_return(nl, -EINVAL);
427 assert_return(m, -EINVAL);
429 k = sendto(nl->fd, m->hdr, m->hdr->nlmsg_len,
430 0, &addr.sa, sizeof(addr));
432 return (errno == EAGAIN) ? 0 : -errno;
437 /* On success, the number of bytes received is returned and *ret points to the received message
438 * which has a valid header and the correct size.
439 * If nothing useful was received 0 is returned.
440 * On failure, a negative error code is returned.
442 int socket_read_message(sd_rtnl *nl, sd_rtnl_message **ret) {
446 struct sockaddr_nl nl;
453 assert_return(nl, -EINVAL);
454 assert_return(ret, -EINVAL);
456 r = message_receive_need(nl, &need);
460 r = message_new(&m, need);
464 addr_len = sizeof(addr);
466 k = recvfrom(nl->fd, m->hdr, need,
467 0, &addr.sa, &addr_len);
469 k = (errno == EAGAIN) ? 0 : -errno; /* no data */
471 k = -ECONNRESET; /* connection was closed by the kernel */
472 else if (addr_len != sizeof(addr.nl) ||
473 addr.nl.nl_family != AF_NETLINK)
474 k = -EIO; /* not a netlink message */
475 else if (addr.nl.nl_pid != 0)
476 k = 0; /* not from the kernel */
477 else if ((size_t) k < sizeof(struct nlmsghdr) ||
478 (size_t) k < m->hdr->nlmsg_len)
479 k = -EIO; /* too small (we do accept too big though) */
480 else if (m->hdr->nlmsg_pid != nl->sockaddr.nl.nl_pid)
481 k = 0; /* not for us */
484 switch (m->hdr->nlmsg_type) {
485 /* check that the size matches the message type */
487 if (m->hdr->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr)))
493 if (m->hdr->nlmsg_len < NLMSG_LENGTH(sizeof(struct ifinfomsg)))
499 if (m->hdr->nlmsg_len < NLMSG_LENGTH(sizeof(struct ifaddrmsg)))
506 k = 0; /* ignoring message of unknown type */
510 sd_rtnl_message_unref(m);
512 /* we probably allocated way too much memory, give it back */
513 m->hdr = realloc(m->hdr, m->hdr->nlmsg_len);