LCOV - code coverage report
Current view: top level - libsystemd/sd-netlink - netlink-socket.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 161 231 69.7 %
Date: 2019-08-22 15:41:25 Functions: 12 12 100.0 %

          Line data    Source code
       1             : /* SPDX-License-Identifier: LGPL-2.1+ */
       2             : 
       3             : #include <netinet/in.h>
       4             : #include <stdbool.h>
       5             : #include <unistd.h>
       6             : 
       7             : #include "sd-netlink.h"
       8             : 
       9             : #include "alloc-util.h"
      10             : #include "fd-util.h"
      11             : #include "format-util.h"
      12             : #include "io-util.h"
      13             : #include "missing.h"
      14             : #include "netlink-internal.h"
      15             : #include "netlink-types.h"
      16             : #include "netlink-util.h"
      17             : #include "socket-util.h"
      18             : #include "util.h"
      19             : 
      20          15 : int socket_open(int family) {
      21             :         int fd;
      22             : 
      23          15 :         fd = socket(PF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, family);
      24          15 :         if (fd < 0)
      25           0 :                 return -errno;
      26             : 
      27          15 :         return fd_move_above_stdio(fd);
      28             : }
      29             : 
      30          15 : static int broadcast_groups_get(sd_netlink *nl) {
      31          15 :         _cleanup_free_ uint32_t *groups = NULL;
      32          15 :         socklen_t len = 0, old_len;
      33             :         unsigned i, j;
      34             :         int r;
      35             : 
      36          15 :         assert(nl);
      37          15 :         assert(nl->fd >= 0);
      38             : 
      39          15 :         r = getsockopt(nl->fd, SOL_NETLINK, NETLINK_LIST_MEMBERSHIPS, NULL, &len);
      40          15 :         if (r < 0) {
      41           0 :                 if (errno == ENOPROTOOPT) {
      42           0 :                         nl->broadcast_group_dont_leave = true;
      43           0 :                         return 0;
      44             :                 } else
      45           0 :                         return -errno;
      46             :         }
      47             : 
      48          15 :         if (len == 0)
      49          15 :                 return 0;
      50             : 
      51           0 :         groups = new0(uint32_t, len);
      52           0 :         if (!groups)
      53           0 :                 return -ENOMEM;
      54             : 
      55           0 :         old_len = len;
      56             : 
      57           0 :         r = getsockopt(nl->fd, SOL_NETLINK, NETLINK_LIST_MEMBERSHIPS, groups, &len);
      58           0 :         if (r < 0)
      59           0 :                 return -errno;
      60             : 
      61           0 :         if (old_len != len)
      62           0 :                 return -EIO;
      63             : 
      64           0 :         r = hashmap_ensure_allocated(&nl->broadcast_group_refs, NULL);
      65           0 :         if (r < 0)
      66           0 :                 return r;
      67             : 
      68           0 :         for (i = 0; i < len; i++) {
      69           0 :                 for (j = 0; j < sizeof(uint32_t) * 8; j++) {
      70             :                         uint32_t offset;
      71             :                         unsigned group;
      72             : 
      73           0 :                         offset = 1U << j;
      74             : 
      75           0 :                         if (!(groups[i] & offset))
      76           0 :                                 continue;
      77             : 
      78           0 :                         group = i * sizeof(uint32_t) * 8 + j + 1;
      79             : 
      80           0 :                         r = hashmap_put(nl->broadcast_group_refs, UINT_TO_PTR(group), UINT_TO_PTR(1));
      81           0 :                         if (r < 0)
      82           0 :                                 return r;
      83             :                 }
      84             :         }
      85             : 
      86           0 :         return 0;
      87             : }
      88             : 
      89          15 : int socket_bind(sd_netlink *nl) {
      90             :         socklen_t addrlen;
      91             :         int r;
      92             : 
      93          15 :         r = setsockopt_int(nl->fd, SOL_NETLINK, NETLINK_PKTINFO, true);
      94          15 :         if (r < 0)
      95           0 :                 return r;
      96             : 
      97          15 :         addrlen = sizeof(nl->sockaddr);
      98             : 
      99          15 :         r = bind(nl->fd, &nl->sockaddr.sa, addrlen);
     100             :         /* ignore EINVAL to allow opening an already bound socket */
     101          15 :         if (r < 0 && errno != EINVAL)
     102           0 :                 return -errno;
     103             : 
     104          15 :         r = getsockname(nl->fd, &nl->sockaddr.sa, &addrlen);
     105          15 :         if (r < 0)
     106           0 :                 return -errno;
     107             : 
     108          15 :         r = broadcast_groups_get(nl);
     109          15 :         if (r < 0)
     110           0 :                 return r;
     111             : 
     112          15 :         return 0;
     113             : }
     114             : 
     115          32 : static unsigned broadcast_group_get_ref(sd_netlink *nl, unsigned group) {
     116          32 :         assert(nl);
     117             : 
     118          32 :         return PTR_TO_UINT(hashmap_get(nl->broadcast_group_refs, UINT_TO_PTR(group)));
     119             : }
     120             : 
     121          32 : static int broadcast_group_set_ref(sd_netlink *nl, unsigned group, unsigned n_ref) {
     122             :         int r;
     123             : 
     124          32 :         assert(nl);
     125             : 
     126          32 :         r = hashmap_replace(nl->broadcast_group_refs, UINT_TO_PTR(group), UINT_TO_PTR(n_ref));
     127          32 :         if (r < 0)
     128           0 :                 return r;
     129             : 
     130          32 :         return 0;
     131             : }
     132             : 
     133           9 : static int broadcast_group_join(sd_netlink *nl, unsigned group) {
     134             :         int r;
     135             : 
     136           9 :         assert(nl);
     137           9 :         assert(nl->fd >= 0);
     138           9 :         assert(group > 0);
     139             : 
     140           9 :         r = setsockopt(nl->fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &group, sizeof(group));
     141           9 :         if (r < 0)
     142           0 :                 return -errno;
     143             : 
     144           9 :         return 0;
     145             : }
     146             : 
     147          19 : int socket_broadcast_group_ref(sd_netlink *nl, unsigned group) {
     148             :         unsigned n_ref;
     149             :         int r;
     150             : 
     151          19 :         assert(nl);
     152             : 
     153          19 :         n_ref = broadcast_group_get_ref(nl, group);
     154             : 
     155          19 :         n_ref++;
     156             : 
     157          19 :         r = hashmap_ensure_allocated(&nl->broadcast_group_refs, NULL);
     158          19 :         if (r < 0)
     159           0 :                 return r;
     160             : 
     161          19 :         r = broadcast_group_set_ref(nl, group, n_ref);
     162          19 :         if (r < 0)
     163           0 :                 return r;
     164             : 
     165          19 :         if (n_ref > 1)
     166             :                 /* not yet in the group */
     167          10 :                 return 0;
     168             : 
     169           9 :         r = broadcast_group_join(nl, group);
     170           9 :         if (r < 0)
     171           0 :                 return r;
     172             : 
     173           9 :         return 0;
     174             : }
     175             : 
     176           6 : static int broadcast_group_leave(sd_netlink *nl, unsigned group) {
     177             :         int r;
     178             : 
     179           6 :         assert(nl);
     180           6 :         assert(nl->fd >= 0);
     181           6 :         assert(group > 0);
     182             : 
     183           6 :         if (nl->broadcast_group_dont_leave)
     184           0 :                 return 0;
     185             : 
     186           6 :         r = setsockopt(nl->fd, SOL_NETLINK, NETLINK_DROP_MEMBERSHIP, &group, sizeof(group));
     187           6 :         if (r < 0)
     188           0 :                 return -errno;
     189             : 
     190           6 :         return 0;
     191             : }
     192             : 
     193          13 : int socket_broadcast_group_unref(sd_netlink *nl, unsigned group) {
     194             :         unsigned n_ref;
     195             :         int r;
     196             : 
     197          13 :         assert(nl);
     198             : 
     199          13 :         n_ref = broadcast_group_get_ref(nl, group);
     200             : 
     201          13 :         assert(n_ref > 0);
     202             : 
     203          13 :         n_ref--;
     204             : 
     205          13 :         r = broadcast_group_set_ref(nl, group, n_ref);
     206          13 :         if (r < 0)
     207           0 :                 return r;
     208             : 
     209          13 :         if (n_ref > 0)
     210             :                 /* still refs left */
     211           7 :                 return 0;
     212             : 
     213           6 :         r = broadcast_group_leave(nl, group);
     214           6 :         if (r < 0)
     215           0 :                 return r;
     216             : 
     217           6 :         return 0;
     218             : }
     219             : 
     220             : /* returns the number of bytes sent, or a negative error code */
     221          26 : int socket_write_message(sd_netlink *nl, sd_netlink_message *m) {
     222             :         union {
     223             :                 struct sockaddr sa;
     224             :                 struct sockaddr_nl nl;
     225          26 :         } addr = {
     226             :                 .nl.nl_family = AF_NETLINK,
     227             :         };
     228             :         ssize_t k;
     229             : 
     230          26 :         assert(nl);
     231          26 :         assert(m);
     232          26 :         assert(m->hdr);
     233             : 
     234          26 :         k = sendto(nl->fd, m->hdr, m->hdr->nlmsg_len,
     235             :                         0, &addr.sa, sizeof(addr));
     236          26 :         if (k < 0)
     237           0 :                 return -errno;
     238             : 
     239          26 :         return k;
     240             : }
     241             : 
     242          64 : static int socket_recv_message(int fd, struct iovec *iov, uint32_t *_group, bool peek) {
     243             :         union sockaddr_union sender;
     244             :         uint8_t cmsg_buffer[CMSG_SPACE(sizeof(struct nl_pktinfo))];
     245          64 :         struct msghdr msg = {
     246             :                 .msg_iov = iov,
     247             :                 .msg_iovlen = 1,
     248             :                 .msg_name = &sender,
     249             :                 .msg_namelen = sizeof(sender),
     250             :                 .msg_control = cmsg_buffer,
     251             :                 .msg_controllen = sizeof(cmsg_buffer),
     252             :         };
     253             :         struct cmsghdr *cmsg;
     254          64 :         uint32_t group = 0;
     255             :         ssize_t n;
     256             : 
     257          64 :         assert(fd >= 0);
     258          64 :         assert(iov);
     259             : 
     260          64 :         n = recvmsg(fd, &msg, MSG_TRUNC | (peek ? MSG_PEEK : 0));
     261          64 :         if (n < 0) {
     262             :                 /* no data */
     263           0 :                 if (errno == ENOBUFS)
     264           0 :                         log_debug("rtnl: kernel receive buffer overrun");
     265           0 :                 else if (errno == EAGAIN)
     266           0 :                         log_debug("rtnl: no data in socket");
     267             : 
     268           0 :                 return IN_SET(errno, EAGAIN, EINTR) ? 0 : -errno;
     269             :         }
     270             : 
     271          64 :         if (sender.nl.nl_pid != 0) {
     272             :                 /* not from the kernel, ignore */
     273           0 :                 log_debug("rtnl: ignoring message from portid %"PRIu32, sender.nl.nl_pid);
     274             : 
     275           0 :                 if (peek) {
     276             :                         /* drop the message */
     277           0 :                         n = recvmsg(fd, &msg, 0);
     278           0 :                         if (n < 0)
     279           0 :                                 return IN_SET(errno, EAGAIN, EINTR) ? 0 : -errno;
     280             :                 }
     281             : 
     282           0 :                 return 0;
     283             :         }
     284             : 
     285         128 :         CMSG_FOREACH(cmsg, &msg) {
     286          64 :                 if (cmsg->cmsg_level == SOL_NETLINK &&
     287          64 :                     cmsg->cmsg_type == NETLINK_PKTINFO &&
     288          64 :                     cmsg->cmsg_len == CMSG_LEN(sizeof(struct nl_pktinfo))) {
     289          64 :                         struct nl_pktinfo *pktinfo = (void *)CMSG_DATA(cmsg);
     290             : 
     291             :                         /* multi-cast group */
     292          64 :                         group = pktinfo->group;
     293             :                 }
     294             :         }
     295             : 
     296          64 :         if (_group)
     297          32 :                 *_group = group;
     298             : 
     299          64 :         return (int) n;
     300             : }
     301             : 
     302             : /* On success, the number of bytes received is returned and *ret points to the received message
     303             :  * which has a valid header and the correct size.
     304             :  * If nothing useful was received 0 is returned.
     305             :  * On failure, a negative error code is returned.
     306             :  */
     307          32 : int socket_read_message(sd_netlink *rtnl) {
     308          32 :         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *first = NULL;
     309          32 :         struct iovec iov = {};
     310          32 :         uint32_t group = 0;
     311          32 :         bool multi_part = false, done = false;
     312             :         struct nlmsghdr *new_msg;
     313             :         size_t len;
     314             :         int r;
     315          32 :         unsigned i = 0;
     316             :         const NLTypeSystem *type_system_root;
     317             : 
     318          32 :         assert(rtnl);
     319          32 :         assert(rtnl->rbuffer);
     320          32 :         assert(rtnl->rbuffer_allocated >= sizeof(struct nlmsghdr));
     321             : 
     322          32 :         type_system_root = type_system_get_root(rtnl->protocol);
     323             : 
     324             :         /* read nothing, just get the pending message size */
     325          32 :         r = socket_recv_message(rtnl->fd, &iov, NULL, true);
     326          32 :         if (r <= 0)
     327           0 :                 return r;
     328             :         else
     329          32 :                 len = (size_t) r;
     330             : 
     331             :         /* make room for the pending message */
     332          32 :         if (!greedy_realloc((void **)&rtnl->rbuffer,
     333             :                             &rtnl->rbuffer_allocated,
     334             :                             len, sizeof(uint8_t)))
     335           0 :                 return -ENOMEM;
     336             : 
     337          32 :         iov = IOVEC_MAKE(rtnl->rbuffer, rtnl->rbuffer_allocated);
     338             : 
     339             :         /* read the pending message */
     340          32 :         r = socket_recv_message(rtnl->fd, &iov, &group, false);
     341          32 :         if (r <= 0)
     342           0 :                 return r;
     343             :         else
     344          32 :                 len = (size_t) r;
     345             : 
     346          32 :         if (len > rtnl->rbuffer_allocated)
     347             :                 /* message did not fit in read buffer */
     348           0 :                 return -EIO;
     349             : 
     350          32 :         if (NLMSG_OK(rtnl->rbuffer, len) && rtnl->rbuffer->nlmsg_flags & NLM_F_MULTI) {
     351          15 :                 multi_part = true;
     352             : 
     353          15 :                 for (i = 0; i < rtnl->rqueue_partial_size; i++) {
     354          10 :                         if (rtnl_message_get_serial(rtnl->rqueue_partial[i]) ==
     355          10 :                             rtnl->rbuffer->nlmsg_seq) {
     356          10 :                                 first = rtnl->rqueue_partial[i];
     357          10 :                                 break;
     358             :                         }
     359             :                 }
     360             :         }
     361             : 
     362         108 :         for (new_msg = rtnl->rbuffer; NLMSG_OK(new_msg, len) && !done; new_msg = NLMSG_NEXT(new_msg, len)) {
     363          76 :                 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
     364             :                 const NLType *nl_type;
     365             : 
     366          76 :                 if (!group && new_msg->nlmsg_pid != rtnl->sockaddr.nl.nl_pid)
     367             :                         /* not broadcast and not for us */
     368           0 :                         continue;
     369             : 
     370          76 :                 if (new_msg->nlmsg_type == NLMSG_NOOP)
     371             :                         /* silently drop noop messages */
     372           0 :                         continue;
     373             : 
     374          76 :                 if (new_msg->nlmsg_type == NLMSG_DONE) {
     375             :                         /* finished reading multi-part message */
     376           5 :                         done = true;
     377             : 
     378             :                         /* if first is not defined, put NLMSG_DONE into the receive queue. */
     379           5 :                         if (first)
     380           5 :                                 continue;
     381             :                 }
     382             : 
     383             :                 /* check that we support this message type */
     384          71 :                 r = type_system_get_type(type_system_root, &nl_type, new_msg->nlmsg_type);
     385          71 :                 if (r < 0) {
     386           0 :                         if (r == -EOPNOTSUPP)
     387           0 :                                 log_debug("sd-netlink: ignored message with unknown type: %i",
     388             :                                           new_msg->nlmsg_type);
     389             : 
     390           0 :                         continue;
     391             :                 }
     392             : 
     393             :                 /* check that the size matches the message type */
     394          71 :                 if (new_msg->nlmsg_len < NLMSG_LENGTH(type_get_size(nl_type))) {
     395           0 :                         log_debug("sd-netlink: message is shorter than expected, dropping");
     396           0 :                         continue;
     397             :                 }
     398             : 
     399          71 :                 r = message_new_empty(rtnl, &m);
     400          71 :                 if (r < 0)
     401           0 :                         return r;
     402             : 
     403          71 :                 m->broadcast = !!group;
     404             : 
     405          71 :                 m->hdr = memdup(new_msg, new_msg->nlmsg_len);
     406          71 :                 if (!m->hdr)
     407           0 :                         return -ENOMEM;
     408             : 
     409             :                 /* seal and parse the top-level message */
     410          71 :                 r = sd_netlink_message_rewind(m);
     411          71 :                 if (r < 0)
     412           0 :                         return r;
     413             : 
     414             :                 /* push the message onto the multi-part message stack */
     415          71 :                 if (first)
     416          49 :                         m->next = first;
     417          71 :                 first = TAKE_PTR(m);
     418             :         }
     419             : 
     420          32 :         if (len > 0)
     421           0 :                 log_debug("sd-netlink: discarding %zu bytes of incoming message", len);
     422             : 
     423          32 :         if (!first)
     424           0 :                 return 0;
     425             : 
     426          32 :         if (!multi_part || done) {
     427             :                 /* we got a complete message, push it on the read queue */
     428          22 :                 r = rtnl_rqueue_make_room(rtnl);
     429          22 :                 if (r < 0)
     430           0 :                         return r;
     431             : 
     432          22 :                 rtnl->rqueue[rtnl->rqueue_size++] = TAKE_PTR(first);
     433             : 
     434          22 :                 if (multi_part && (i < rtnl->rqueue_partial_size)) {
     435             :                         /* remove the message form the partial read queue */
     436           5 :                         memmove(rtnl->rqueue_partial + i,rtnl->rqueue_partial + i + 1,
     437           5 :                                 sizeof(sd_netlink_message*) * (rtnl->rqueue_partial_size - i - 1));
     438           5 :                         rtnl->rqueue_partial_size--;
     439             :                 }
     440             : 
     441          22 :                 return 1;
     442             :         } else {
     443             :                 /* we only got a partial multi-part message, push it on the
     444             :                    partial read queue */
     445          10 :                 if (i < rtnl->rqueue_partial_size)
     446           5 :                         rtnl->rqueue_partial[i] = TAKE_PTR(first);
     447             :                 else {
     448           5 :                         r = rtnl_rqueue_partial_make_room(rtnl);
     449           5 :                         if (r < 0)
     450           0 :                                 return r;
     451             : 
     452           5 :                         rtnl->rqueue_partial[rtnl->rqueue_partial_size++] = TAKE_PTR(first);
     453             :                 }
     454             : 
     455          10 :                 return 0;
     456             :         }
     457             : }

Generated by: LCOV version 1.14