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_link_new(uint16_t nlmsg_type, int index, unsigned int type, unsigned int flags, sd_rtnl_message **ret) {
71 struct ifinfomsg *ifi;
74 assert_return(nlmsg_type == RTM_NEWLINK || nlmsg_type == RTM_DELLINK || nlmsg_type == RTM_GETLINK, -EINVAL);
75 assert_return(index > 0, -EINVAL);
76 assert_return(ret, -EINVAL);
78 r = message_new(ret, NLMSG_SPACE(sizeof(struct ifinfomsg)));
82 (*ret)->hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
83 (*ret)->hdr->nlmsg_type = nlmsg_type;
85 ifi = NLMSG_DATA((*ret)->hdr);
87 ifi->ifi_family = AF_UNSPEC;
88 ifi->ifi_index = index;
90 ifi->ifi_flags = flags;
91 ifi->ifi_change = 0xffffffff;
96 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) {
97 struct ifaddrmsg *ifa;
100 assert_return(nlmsg_type == RTM_NEWADDR || nlmsg_type == RTM_DELADDR || nlmsg_type == RTM_GETADDR, -EINVAL);
101 assert_return(index > 0, -EINVAL);
102 assert_return(ret, -EINVAL);
104 r = message_new(ret, NLMSG_SPACE(sizeof(struct ifaddrmsg)));
108 (*ret)->hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
109 (*ret)->hdr->nlmsg_type = nlmsg_type;
111 ifa = NLMSG_DATA((*ret)->hdr);
113 ifa->ifa_family = family;
114 ifa->ifa_prefixlen = prefixlen;
115 ifa->ifa_flags = flags;
116 ifa->ifa_scope = scope;
117 ifa->ifa_index = index;
122 sd_rtnl_message *sd_rtnl_message_ref(sd_rtnl_message *m) {
124 assert_se(REFCNT_INC(m->n_ref) >= 2);
129 sd_rtnl_message *sd_rtnl_message_unref(sd_rtnl_message *m) {
130 if (m && REFCNT_DEC(m->n_ref) <= 0) {
138 int sd_rtnl_message_get_type(sd_rtnl_message *m, uint16_t *type) {
139 assert_return(m, -EINVAL);
140 assert_return(type, -EINVAL);
142 *type = m->hdr->nlmsg_type;
147 /* If successful the updated message will be correctly aligned, if unsuccessful the old message is
149 static int add_rtattr(sd_rtnl_message *m, unsigned short type, const void *data, size_t data_length) {
150 uint32_t rta_length, message_length;
151 struct nlmsghdr *new_hdr;
154 assert_return(m, -EINVAL);
155 assert_return(m->hdr, -EINVAL);
156 assert_return(NLMSG_ALIGN(m->hdr->nlmsg_len) == m->hdr->nlmsg_len, -EINVAL);
157 assert_return(data, -EINVAL);
158 assert_return(data_length > 0, -EINVAL);
160 /* get the size of the new rta attribute (without padding at the end) */
161 rta_length = RTA_LENGTH(data_length);
162 /* get the new message size (with padding between the old message and the new attrib,
163 * but no padding after)
165 message_length = m->hdr->nlmsg_len + RTA_ALIGN(rta_length);
167 /* realloc to fit the new attribute */
168 new_hdr = realloc(m->hdr, message_length);
173 /* get pointer to the attribute we are about to add */
174 rta = (struct rtattr *) ((uint8_t *) m->hdr + m->hdr->nlmsg_len);
175 /* update message size */
176 m->hdr->nlmsg_len = message_length;
178 /* fill in the attribute */
179 rta->rta_type = type;
180 rta->rta_len = rta_length;
181 /* we don't deal with the case where the user lies about the type and gives us
182 * too little data (so don't do that)
184 memcpy(RTA_DATA(rta), data, data_length);
189 int sd_rtnl_message_append(sd_rtnl_message *m, unsigned short type, const void *data) {
191 struct ifaddrmsg *ifa;
193 assert_return(m, -EINVAL);
194 assert_return(data, -EINVAL);
196 sd_rtnl_message_get_type(m, &rtm_type);
205 return add_rtattr(m, type, data, strlen(data) + 1);
207 return add_rtattr(m, type, data, sizeof(unsigned int));
209 return add_rtattr(m, type, data, sizeof(int));
211 return add_rtattr(m, type, data, sizeof(struct rtnl_link_stats));
214 return add_rtattr(m, type, data, ETH_ALEN);
223 return add_rtattr(m, type, data, strlen(data) + 1);
228 ifa = NLMSG_DATA(m->hdr);
229 switch (ifa->ifa_family) {
231 return add_rtattr(m, type, data, sizeof(struct in_addr));
233 return add_rtattr(m, type, data, sizeof(struct in6_addr));
245 static int message_read(sd_rtnl_message *m, unsigned short *type, void **data) {
246 assert_return(m, -EINVAL);
247 assert_return(data, -EINVAL);
249 if (!RTA_OK(m->next_rta, m->remaining_size))
252 *data = RTA_DATA(m->next_rta);
253 *type = m->next_rta->rta_type;
255 m->next_rta = RTA_NEXT(m->next_rta, m->remaining_size);
260 int sd_rtnl_message_read(sd_rtnl_message *m, unsigned short *type, void **data) {
263 assert_return(m, -EINVAL);
264 assert_return(data, -EINVAL);
266 sd_rtnl_message_get_type(m, &rtm_type);
273 struct ifinfomsg *ifi = NLMSG_DATA(m->hdr);
275 m->next_rta = IFLA_RTA(ifi);
276 m->remaining_size = IFLA_PAYLOAD(m->hdr);
283 struct ifaddrmsg *ifa = NLMSG_DATA(m->hdr);
285 m->next_rta = IFA_RTA(ifa);
286 m->remaining_size = IFLA_PAYLOAD(m->hdr);
293 return message_read(m, type, data);
296 int message_get_serial(sd_rtnl_message *m) {
299 return m->hdr->nlmsg_seq;
302 int message_get_errno(sd_rtnl_message *m) {
303 struct nlmsgerr *err;
307 if (m->hdr->nlmsg_type != NLMSG_ERROR)
310 err = NLMSG_DATA(m->hdr);
315 int message_seal(sd_rtnl *nl, sd_rtnl_message *m) {
319 m->hdr->nlmsg_seq = nl->serial++;
325 static int message_receive_need(sd_rtnl *rtnl, size_t *need) {
326 assert_return(rtnl, -EINVAL);
327 assert_return(need, -EINVAL);
329 /* ioctl(rtnl->fd, FIONREAD, &need)
330 Does not appear to work on netlink sockets. libnl uses
331 MSG_PEEK instead. I don't know if that is worth the
334 For now we simply use the maximum message size the kernel
335 may use (NLMSG_GOODSIZE), and then realloc to the actual
336 size after reading the message (hence avoiding huge memory
337 usage in case many small messages are kept around) */
345 /* returns the number of bytes sent, or a negative error code */
346 int socket_write_message(sd_rtnl *nl, sd_rtnl_message *m) {
349 struct sockaddr_nl nl;
351 .nl.nl_family = AF_NETLINK,
355 assert_return(nl, -EINVAL);
356 assert_return(m, -EINVAL);
358 k = sendto(nl->fd, m->hdr, m->hdr->nlmsg_len,
359 0, &addr.sa, sizeof(addr));
361 return (errno == EAGAIN) ? 0 : -errno;
366 /* On success, the number of bytes received is returned and *ret points to the received message
367 * which has a valid header and the correct size.
368 * If nothing useful was received 0 is returned.
369 * On failure, a negative error code is returned.
371 int socket_read_message(sd_rtnl *nl, sd_rtnl_message **ret) {
375 struct sockaddr_nl nl;
382 assert_return(nl, -EINVAL);
383 assert_return(ret, -EINVAL);
385 r = message_receive_need(nl, &need);
389 r = message_new(&m, need);
393 addr_len = sizeof(addr);
395 k = recvfrom(nl->fd, m->hdr, need,
396 0, &addr.sa, &addr_len);
398 k = (errno == EAGAIN) ? 0 : -errno; /* no data */
400 k = -ECONNRESET; /* connection was closed by the kernel */
401 else if (addr_len != sizeof(addr.nl) ||
402 addr.nl.nl_family != AF_NETLINK)
403 k = -EIO; /* not a netlink message */
404 else if (addr.nl.nl_pid != 0)
405 k = 0; /* not from the kernel */
406 else if ((size_t) k < sizeof(struct nlmsghdr) ||
407 (size_t) k < m->hdr->nlmsg_len)
408 k = -EIO; /* too small (we do accept too big though) */
409 else if (m->hdr->nlmsg_pid != nl->sockaddr.nl.nl_pid)
410 k = 0; /* not for us */
413 switch (m->hdr->nlmsg_type) {
414 /* check that the size matches the message type */
416 if (m->hdr->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr)))
422 if (m->hdr->nlmsg_len < NLMSG_LENGTH(sizeof(struct ifinfomsg)))
428 if (m->hdr->nlmsg_len < NLMSG_LENGTH(sizeof(struct ifaddrmsg)))
435 k = 0; /* ignoring message of unknown type */
439 sd_rtnl_message_unref(m);
441 /* we probably allocated way too much memory, give it back */
442 m->hdr = realloc(m->hdr, m->hdr->nlmsg_len);