LCOV - code coverage report
Current view: top level - libsystemd/sd-netlink - netlink-socket.c (source / functions) Hit Total Coverage
Test: systemd test coverage Lines: 113 148 76.4 %
Date: 2015-07-29 18:47:03 Functions: 6 6 100.0 %

          Line data    Source code
       1             : /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
       2             : 
       3             : /***
       4             :   This file is part of systemd.
       5             : 
       6             :   Copyright 2013 Tom Gundersen <teg@jklm.no>
       7             : 
       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.
      12             : 
      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.
      17             : 
      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/>.
      20             : ***/
      21             : 
      22             : #include <netinet/in.h>
      23             : #include <stdbool.h>
      24             : #include <unistd.h>
      25             : 
      26             : #include "util.h"
      27             : #include "socket-util.h"
      28             : #include "formats-util.h"
      29             : #include "refcnt.h"
      30             : #include "missing.h"
      31             : 
      32             : #include "sd-netlink.h"
      33             : #include "netlink-util.h"
      34             : #include "netlink-internal.h"
      35             : #include "netlink-types.h"
      36             : 
      37          10 : int socket_open(int family) {
      38             :         int fd;
      39             : 
      40          10 :         fd = socket(PF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, family);
      41          10 :         if (fd < 0)
      42           0 :                 return -errno;
      43             : 
      44          10 :         return fd;
      45             : }
      46             : 
      47          10 : int socket_bind(sd_netlink *nl) {
      48             :         socklen_t addrlen;
      49          10 :         int r, one = 1;
      50             : 
      51          10 :         r = setsockopt(nl->fd, SOL_NETLINK, NETLINK_PKTINFO, &one, sizeof(one));
      52          10 :         if (r < 0)
      53           0 :                 return -errno;
      54             : 
      55          10 :         addrlen = sizeof(nl->sockaddr);
      56             : 
      57          10 :         r = bind(nl->fd, &nl->sockaddr.sa, addrlen);
      58             :         /* ignore EINVAL to allow opening an already bound socket */
      59          10 :         if (r < 0 && errno != EINVAL)
      60           0 :                 return -errno;
      61             : 
      62          10 :         r = getsockname(nl->fd, &nl->sockaddr.sa, &addrlen);
      63          10 :         if (r < 0)
      64           0 :                 return -errno;
      65             : 
      66          10 :         return 0;
      67             : }
      68             : 
      69             : 
      70           8 : int socket_join_broadcast_group(sd_netlink *nl, unsigned group) {
      71             :         int r;
      72             : 
      73           8 :         assert(nl);
      74           8 :         assert(nl->fd >= 0);
      75           8 :         assert(group > 0);
      76             : 
      77           8 :         r = setsockopt(nl->fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &group, sizeof(group));
      78           8 :         if (r < 0)
      79           0 :                 return -errno;
      80             : 
      81           8 :         return 0;
      82             : }
      83             : 
      84             : /* returns the number of bytes sent, or a negative error code */
      85          16 : int socket_write_message(sd_netlink *nl, sd_netlink_message *m) {
      86             :         union {
      87             :                 struct sockaddr sa;
      88             :                 struct sockaddr_nl nl;
      89          16 :         } addr = {
      90             :                 .nl.nl_family = AF_NETLINK,
      91             :         };
      92             :         ssize_t k;
      93             : 
      94          16 :         assert(nl);
      95          16 :         assert(m);
      96          16 :         assert(m->hdr);
      97             : 
      98          16 :         k = sendto(nl->fd, m->hdr, m->hdr->nlmsg_len,
      99             :                         0, &addr.sa, sizeof(addr));
     100          16 :         if (k < 0)
     101           0 :                 return -errno;
     102             : 
     103          16 :         return k;
     104             : }
     105             : 
     106          50 : static int socket_recv_message(int fd, struct iovec *iov, uint32_t *_group, bool peek) {
     107             :         union sockaddr_union sender;
     108             :         uint8_t cmsg_buffer[CMSG_SPACE(sizeof(struct nl_pktinfo))];
     109          50 :         struct msghdr msg = {
     110             :                 .msg_iov = iov,
     111             :                 .msg_iovlen = 1,
     112             :                 .msg_name = &sender,
     113             :                 .msg_namelen = sizeof(sender),
     114             :                 .msg_control = cmsg_buffer,
     115             :                 .msg_controllen = sizeof(cmsg_buffer),
     116             :         };
     117             :         struct cmsghdr *cmsg;
     118          50 :         uint32_t group = 0;
     119             :         int r;
     120             : 
     121          50 :         assert(fd >= 0);
     122          50 :         assert(iov);
     123             : 
     124          50 :         r = recvmsg(fd, &msg, MSG_TRUNC | (peek ? MSG_PEEK : 0));
     125          50 :         if (r < 0) {
     126             :                 /* no data */
     127           0 :                 if (errno == ENOBUFS)
     128           0 :                         log_debug("rtnl: kernel receive buffer overrun");
     129           0 :                 else if (errno == EAGAIN)
     130           0 :                         log_debug("rtnl: no data in socket");
     131             : 
     132           0 :                 return (errno == EAGAIN || errno == EINTR) ? 0 : -errno;
     133             :         }
     134             : 
     135          50 :         if (sender.nl.nl_pid != 0) {
     136             :                 /* not from the kernel, ignore */
     137           0 :                 log_debug("rtnl: ignoring message from portid %"PRIu32, sender.nl.nl_pid);
     138             : 
     139           0 :                 if (peek) {
     140             :                         /* drop the message */
     141           0 :                         r = recvmsg(fd, &msg, 0);
     142           0 :                         if (r < 0)
     143           0 :                                 return (errno == EAGAIN || errno == EINTR) ? 0 : -errno;
     144             :                 }
     145             : 
     146           0 :                 return 0;
     147             :         }
     148             : 
     149         100 :         CMSG_FOREACH(cmsg, &msg) {
     150         100 :                 if (cmsg->cmsg_level == SOL_NETLINK &&
     151         100 :                     cmsg->cmsg_type == NETLINK_PKTINFO &&
     152          50 :                     cmsg->cmsg_len == CMSG_LEN(sizeof(struct nl_pktinfo))) {
     153          50 :                         struct nl_pktinfo *pktinfo = (void *)CMSG_DATA(cmsg);
     154             : 
     155             :                         /* multi-cast group */
     156          50 :                         group = pktinfo->group;
     157             :                 }
     158             :         }
     159             : 
     160          50 :         if (_group)
     161          25 :                 *_group = group;
     162             : 
     163          50 :         return r;
     164             : }
     165             : 
     166             : /* On success, the number of bytes received is returned and *ret points to the received message
     167             :  * which has a valid header and the correct size.
     168             :  * If nothing useful was received 0 is returned.
     169             :  * On failure, a negative error code is returned.
     170             :  */
     171          25 : int socket_read_message(sd_netlink *rtnl) {
     172          50 :         _cleanup_netlink_message_unref_ sd_netlink_message *first = NULL;
     173          25 :         struct iovec iov = {};
     174          25 :         uint32_t group = 0;
     175          25 :         bool multi_part = false, done = false;
     176             :         struct nlmsghdr *new_msg;
     177             :         size_t len;
     178             :         int r;
     179          25 :         unsigned i = 0;
     180             : 
     181          25 :         assert(rtnl);
     182          25 :         assert(rtnl->rbuffer);
     183          25 :         assert(rtnl->rbuffer_allocated >= sizeof(struct nlmsghdr));
     184             : 
     185             :         /* read nothing, just get the pending message size */
     186          25 :         r = socket_recv_message(rtnl->fd, &iov, NULL, true);
     187          25 :         if (r <= 0)
     188           0 :                 return r;
     189             :         else
     190          25 :                 len = (size_t)r;
     191             : 
     192             :         /* make room for the pending message */
     193          25 :         if (!greedy_realloc((void **)&rtnl->rbuffer,
     194             :                             &rtnl->rbuffer_allocated,
     195             :                             len, sizeof(uint8_t)))
     196           0 :                 return -ENOMEM;
     197             : 
     198          25 :         iov.iov_base = rtnl->rbuffer;
     199          25 :         iov.iov_len = rtnl->rbuffer_allocated;
     200             : 
     201             :         /* read the pending message */
     202          25 :         r = socket_recv_message(rtnl->fd, &iov, &group, false);
     203          25 :         if (r <= 0)
     204           0 :                 return r;
     205             :         else
     206          25 :                 len = (size_t)r;
     207             : 
     208          25 :         if (len > rtnl->rbuffer_allocated)
     209             :                 /* message did not fit in read buffer */
     210           0 :                 return -EIO;
     211             : 
     212          25 :         if (NLMSG_OK(rtnl->rbuffer, len) && rtnl->rbuffer->nlmsg_flags & NLM_F_MULTI) {
     213          14 :                 multi_part = true;
     214             : 
     215          14 :                 for (i = 0; i < rtnl->rqueue_partial_size; i++) {
     216          18 :                         if (rtnl_message_get_serial(rtnl->rqueue_partial[i]) ==
     217           9 :                             rtnl->rbuffer->nlmsg_seq) {
     218           9 :                                 first = rtnl->rqueue_partial[i];
     219           9 :                                 break;
     220             :                         }
     221             :                 }
     222             :         }
     223             : 
     224          82 :         for (new_msg = rtnl->rbuffer; NLMSG_OK(new_msg, len) && !done; new_msg = NLMSG_NEXT(new_msg, len)) {
     225         114 :                 _cleanup_netlink_message_unref_ sd_netlink_message *m = NULL;
     226             :                 const NLType *nl_type;
     227             : 
     228          57 :                 if (!group && new_msg->nlmsg_pid != rtnl->sockaddr.nl.nl_pid)
     229             :                         /* not broadcast and not for us */
     230           0 :                         continue;
     231             : 
     232          57 :                 if (new_msg->nlmsg_type == NLMSG_NOOP)
     233             :                         /* silently drop noop messages */
     234           0 :                         continue;
     235             : 
     236          57 :                 if (new_msg->nlmsg_type == NLMSG_DONE) {
     237             :                         /* finished reading multi-part message */
     238           5 :                         done = true;
     239             : 
     240             :                         /* if first is not defined, put NLMSG_DONE into the receive queue. */
     241           5 :                         if (first)
     242           5 :                                 continue;
     243             :                 }
     244             : 
     245             :                 /* check that we support this message type */
     246          52 :                 r = type_system_get_type(&type_system_root, &nl_type, new_msg->nlmsg_type);
     247          52 :                 if (r < 0) {
     248           0 :                         if (r == -EOPNOTSUPP)
     249           0 :                                 log_debug("sd-netlink: ignored message with unknown type: %i",
     250             :                                           new_msg->nlmsg_type);
     251             : 
     252           0 :                         continue;
     253             :                 }
     254             : 
     255             :                 /* check that the size matches the message type */
     256          52 :                 if (new_msg->nlmsg_len < NLMSG_LENGTH(type_get_size(nl_type))) {
     257           0 :                         log_debug("sd-netlink: message larger than expected, dropping");
     258           0 :                         continue;
     259             :                 }
     260             : 
     261          52 :                 r = message_new_empty(rtnl, &m);
     262          52 :                 if (r < 0)
     263           0 :                         return r;
     264             : 
     265          52 :                 m->broadcast = !!group;
     266             : 
     267          52 :                 m->hdr = memdup(new_msg, new_msg->nlmsg_len);
     268          52 :                 if (!m->hdr)
     269           0 :                         return -ENOMEM;
     270             : 
     271             :                 /* seal and parse the top-level message */
     272          52 :                 r = sd_netlink_message_rewind(m);
     273          52 :                 if (r < 0)
     274           0 :                         return r;
     275             : 
     276             :                 /* push the message onto the multi-part message stack */
     277          52 :                 if (first)
     278          36 :                         m->next = first;
     279          52 :                 first = m;
     280          52 :                 m = NULL;
     281             :         }
     282             : 
     283          25 :         if (len)
     284           0 :                 log_debug("sd-netlink: discarding %zu bytes of incoming message", len);
     285             : 
     286          25 :         if (!first)
     287           0 :                 return 0;
     288             : 
     289          25 :         if (!multi_part || done) {
     290             :                 /* we got a complete message, push it on the read queue */
     291          16 :                 r = rtnl_rqueue_make_room(rtnl);
     292          16 :                 if (r < 0)
     293           0 :                         return r;
     294             : 
     295          16 :                 rtnl->rqueue[rtnl->rqueue_size ++] = first;
     296          16 :                 first = NULL;
     297             : 
     298          16 :                 if (multi_part && (i < rtnl->rqueue_partial_size)) {
     299             :                         /* remove the message form the partial read queue */
     300           5 :                         memmove(rtnl->rqueue_partial + i,rtnl->rqueue_partial + i + 1,
     301           5 :                                 sizeof(sd_netlink_message*) * (rtnl->rqueue_partial_size - i - 1));
     302           5 :                         rtnl->rqueue_partial_size --;
     303             :                 }
     304             : 
     305          16 :                 return 1;
     306             :         } else {
     307             :                 /* we only got a partial multi-part message, push it on the
     308             :                    partial read queue */
     309           9 :                 if (i < rtnl->rqueue_partial_size) {
     310           4 :                         rtnl->rqueue_partial[i] = first;
     311             :                 } else {
     312           5 :                         r = rtnl_rqueue_partial_make_room(rtnl);
     313           5 :                         if (r < 0)
     314           0 :                                 return r;
     315             : 
     316           5 :                         rtnl->rqueue_partial[rtnl->rqueue_partial_size ++] = first;
     317             :                 }
     318           9 :                 first = NULL;
     319             : 
     320           9 :                 return 0;
     321             :         }
     322             : }

Generated by: LCOV version 1.11