LCOV - code coverage report
Current view: top level - libsystemd/sd-netlink - netlink-socket.c (source / functions) Hit Total Coverage
Test: systemd_full.info Lines: 161 231 69.7 %
Date: 2019-08-23 13:36:53 Functions: 12 12 100.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 99 221 44.8 %

           Branch data     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                 :         60 : int socket_open(int family) {
      21                 :            :         int fd;
      22                 :            : 
      23                 :         60 :         fd = socket(PF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, family);
      24         [ -  + ]:         60 :         if (fd < 0)
      25                 :          0 :                 return -errno;
      26                 :            : 
      27                 :         60 :         return fd_move_above_stdio(fd);
      28                 :            : }
      29                 :            : 
      30                 :         60 : static int broadcast_groups_get(sd_netlink *nl) {
      31                 :         60 :         _cleanup_free_ uint32_t *groups = NULL;
      32                 :         60 :         socklen_t len = 0, old_len;
      33                 :            :         unsigned i, j;
      34                 :            :         int r;
      35                 :            : 
      36         [ -  + ]:         60 :         assert(nl);
      37         [ -  + ]:         60 :         assert(nl->fd >= 0);
      38                 :            : 
      39                 :         60 :         r = getsockopt(nl->fd, SOL_NETLINK, NETLINK_LIST_MEMBERSHIPS, NULL, &len);
      40         [ -  + ]:         60 :         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         [ +  - ]:         60 :         if (len == 0)
      49                 :         60 :                 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                 :         60 : int socket_bind(sd_netlink *nl) {
      90                 :            :         socklen_t addrlen;
      91                 :            :         int r;
      92                 :            : 
      93                 :         60 :         r = setsockopt_int(nl->fd, SOL_NETLINK, NETLINK_PKTINFO, true);
      94         [ -  + ]:         60 :         if (r < 0)
      95                 :          0 :                 return r;
      96                 :            : 
      97                 :         60 :         addrlen = sizeof(nl->sockaddr);
      98                 :            : 
      99                 :         60 :         r = bind(nl->fd, &nl->sockaddr.sa, addrlen);
     100                 :            :         /* ignore EINVAL to allow opening an already bound socket */
     101   [ -  +  #  # ]:         60 :         if (r < 0 && errno != EINVAL)
     102                 :          0 :                 return -errno;
     103                 :            : 
     104                 :         60 :         r = getsockname(nl->fd, &nl->sockaddr.sa, &addrlen);
     105         [ -  + ]:         60 :         if (r < 0)
     106                 :          0 :                 return -errno;
     107                 :            : 
     108                 :         60 :         r = broadcast_groups_get(nl);
     109         [ -  + ]:         60 :         if (r < 0)
     110                 :          0 :                 return r;
     111                 :            : 
     112                 :         60 :         return 0;
     113                 :            : }
     114                 :            : 
     115                 :        128 : static unsigned broadcast_group_get_ref(sd_netlink *nl, unsigned group) {
     116         [ -  + ]:        128 :         assert(nl);
     117                 :            : 
     118                 :        128 :         return PTR_TO_UINT(hashmap_get(nl->broadcast_group_refs, UINT_TO_PTR(group)));
     119                 :            : }
     120                 :            : 
     121                 :        128 : static int broadcast_group_set_ref(sd_netlink *nl, unsigned group, unsigned n_ref) {
     122                 :            :         int r;
     123                 :            : 
     124         [ -  + ]:        128 :         assert(nl);
     125                 :            : 
     126                 :        128 :         r = hashmap_replace(nl->broadcast_group_refs, UINT_TO_PTR(group), UINT_TO_PTR(n_ref));
     127         [ -  + ]:        128 :         if (r < 0)
     128                 :          0 :                 return r;
     129                 :            : 
     130                 :        128 :         return 0;
     131                 :            : }
     132                 :            : 
     133                 :         36 : static int broadcast_group_join(sd_netlink *nl, unsigned group) {
     134                 :            :         int r;
     135                 :            : 
     136         [ -  + ]:         36 :         assert(nl);
     137         [ -  + ]:         36 :         assert(nl->fd >= 0);
     138         [ -  + ]:         36 :         assert(group > 0);
     139                 :            : 
     140                 :         36 :         r = setsockopt(nl->fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &group, sizeof(group));
     141         [ -  + ]:         36 :         if (r < 0)
     142                 :          0 :                 return -errno;
     143                 :            : 
     144                 :         36 :         return 0;
     145                 :            : }
     146                 :            : 
     147                 :         76 : int socket_broadcast_group_ref(sd_netlink *nl, unsigned group) {
     148                 :            :         unsigned n_ref;
     149                 :            :         int r;
     150                 :            : 
     151         [ -  + ]:         76 :         assert(nl);
     152                 :            : 
     153                 :         76 :         n_ref = broadcast_group_get_ref(nl, group);
     154                 :            : 
     155                 :         76 :         n_ref++;
     156                 :            : 
     157                 :         76 :         r = hashmap_ensure_allocated(&nl->broadcast_group_refs, NULL);
     158         [ -  + ]:         76 :         if (r < 0)
     159                 :          0 :                 return r;
     160                 :            : 
     161                 :         76 :         r = broadcast_group_set_ref(nl, group, n_ref);
     162         [ -  + ]:         76 :         if (r < 0)
     163                 :          0 :                 return r;
     164                 :            : 
     165         [ +  + ]:         76 :         if (n_ref > 1)
     166                 :            :                 /* not yet in the group */
     167                 :         40 :                 return 0;
     168                 :            : 
     169                 :         36 :         r = broadcast_group_join(nl, group);
     170         [ -  + ]:         36 :         if (r < 0)
     171                 :          0 :                 return r;
     172                 :            : 
     173                 :         36 :         return 0;
     174                 :            : }
     175                 :            : 
     176                 :         24 : static int broadcast_group_leave(sd_netlink *nl, unsigned group) {
     177                 :            :         int r;
     178                 :            : 
     179         [ -  + ]:         24 :         assert(nl);
     180         [ -  + ]:         24 :         assert(nl->fd >= 0);
     181         [ -  + ]:         24 :         assert(group > 0);
     182                 :            : 
     183         [ -  + ]:         24 :         if (nl->broadcast_group_dont_leave)
     184                 :          0 :                 return 0;
     185                 :            : 
     186                 :         24 :         r = setsockopt(nl->fd, SOL_NETLINK, NETLINK_DROP_MEMBERSHIP, &group, sizeof(group));
     187         [ -  + ]:         24 :         if (r < 0)
     188                 :          0 :                 return -errno;
     189                 :            : 
     190                 :         24 :         return 0;
     191                 :            : }
     192                 :            : 
     193                 :         52 : int socket_broadcast_group_unref(sd_netlink *nl, unsigned group) {
     194                 :            :         unsigned n_ref;
     195                 :            :         int r;
     196                 :            : 
     197         [ -  + ]:         52 :         assert(nl);
     198                 :            : 
     199                 :         52 :         n_ref = broadcast_group_get_ref(nl, group);
     200                 :            : 
     201         [ -  + ]:         52 :         assert(n_ref > 0);
     202                 :            : 
     203                 :         52 :         n_ref--;
     204                 :            : 
     205                 :         52 :         r = broadcast_group_set_ref(nl, group, n_ref);
     206         [ -  + ]:         52 :         if (r < 0)
     207                 :          0 :                 return r;
     208                 :            : 
     209         [ +  + ]:         52 :         if (n_ref > 0)
     210                 :            :                 /* still refs left */
     211                 :         28 :                 return 0;
     212                 :            : 
     213                 :         24 :         r = broadcast_group_leave(nl, group);
     214         [ -  + ]:         24 :         if (r < 0)
     215                 :          0 :                 return r;
     216                 :            : 
     217                 :         24 :         return 0;
     218                 :            : }
     219                 :            : 
     220                 :            : /* returns the number of bytes sent, or a negative error code */
     221                 :        108 : int socket_write_message(sd_netlink *nl, sd_netlink_message *m) {
     222                 :            :         union {
     223                 :            :                 struct sockaddr sa;
     224                 :            :                 struct sockaddr_nl nl;
     225                 :        108 :         } addr = {
     226                 :            :                 .nl.nl_family = AF_NETLINK,
     227                 :            :         };
     228                 :            :         ssize_t k;
     229                 :            : 
     230         [ -  + ]:        108 :         assert(nl);
     231         [ -  + ]:        108 :         assert(m);
     232         [ -  + ]:        108 :         assert(m->hdr);
     233                 :            : 
     234                 :        108 :         k = sendto(nl->fd, m->hdr, m->hdr->nlmsg_len,
     235                 :            :                         0, &addr.sa, sizeof(addr));
     236         [ -  + ]:        108 :         if (k < 0)
     237                 :          0 :                 return -errno;
     238                 :            : 
     239                 :        108 :         return k;
     240                 :            : }
     241                 :            : 
     242                 :        256 : 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                 :        256 :         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                 :        256 :         uint32_t group = 0;
     255                 :            :         ssize_t n;
     256                 :            : 
     257         [ -  + ]:        256 :         assert(fd >= 0);
     258         [ -  + ]:        256 :         assert(iov);
     259                 :            : 
     260         [ +  + ]:        256 :         n = recvmsg(fd, &msg, MSG_TRUNC | (peek ? MSG_PEEK : 0));
     261         [ -  + ]:        256 :         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         [ -  + ]:        256 :         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   [ +  -  +  + ]:        512 :         CMSG_FOREACH(cmsg, &msg) {
     286         [ +  - ]:        256 :                 if (cmsg->cmsg_level == SOL_NETLINK &&
     287         [ +  - ]:        256 :                     cmsg->cmsg_type == NETLINK_PKTINFO &&
     288         [ +  - ]:        256 :                     cmsg->cmsg_len == CMSG_LEN(sizeof(struct nl_pktinfo))) {
     289                 :        256 :                         struct nl_pktinfo *pktinfo = (void *)CMSG_DATA(cmsg);
     290                 :            : 
     291                 :            :                         /* multi-cast group */
     292                 :        256 :                         group = pktinfo->group;
     293                 :            :                 }
     294                 :            :         }
     295                 :            : 
     296         [ +  + ]:        256 :         if (_group)
     297                 :        128 :                 *_group = group;
     298                 :            : 
     299                 :        256 :         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                 :        128 : int socket_read_message(sd_netlink *rtnl) {
     308                 :        128 :         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *first = NULL;
     309                 :        128 :         struct iovec iov = {};
     310                 :        128 :         uint32_t group = 0;
     311                 :        128 :         bool multi_part = false, done = false;
     312                 :            :         struct nlmsghdr *new_msg;
     313                 :            :         size_t len;
     314                 :            :         int r;
     315                 :        128 :         unsigned i = 0;
     316                 :            :         const NLTypeSystem *type_system_root;
     317                 :            : 
     318         [ -  + ]:        128 :         assert(rtnl);
     319         [ -  + ]:        128 :         assert(rtnl->rbuffer);
     320         [ -  + ]:        128 :         assert(rtnl->rbuffer_allocated >= sizeof(struct nlmsghdr));
     321                 :            : 
     322                 :        128 :         type_system_root = type_system_get_root(rtnl->protocol);
     323                 :            : 
     324                 :            :         /* read nothing, just get the pending message size */
     325                 :        128 :         r = socket_recv_message(rtnl->fd, &iov, NULL, true);
     326         [ -  + ]:        128 :         if (r <= 0)
     327                 :          0 :                 return r;
     328                 :            :         else
     329                 :        128 :                 len = (size_t) r;
     330                 :            : 
     331                 :            :         /* make room for the pending message */
     332         [ -  + ]:        128 :         if (!greedy_realloc((void **)&rtnl->rbuffer,
     333                 :            :                             &rtnl->rbuffer_allocated,
     334                 :            :                             len, sizeof(uint8_t)))
     335                 :          0 :                 return -ENOMEM;
     336                 :            : 
     337                 :        128 :         iov = IOVEC_MAKE(rtnl->rbuffer, rtnl->rbuffer_allocated);
     338                 :            : 
     339                 :            :         /* read the pending message */
     340                 :        128 :         r = socket_recv_message(rtnl->fd, &iov, &group, false);
     341         [ -  + ]:        128 :         if (r <= 0)
     342                 :          0 :                 return r;
     343                 :            :         else
     344                 :        128 :                 len = (size_t) r;
     345                 :            : 
     346         [ -  + ]:        128 :         if (len > rtnl->rbuffer_allocated)
     347                 :            :                 /* message did not fit in read buffer */
     348                 :          0 :                 return -EIO;
     349                 :            : 
     350   [ +  -  +  -  :        128 :         if (NLMSG_OK(rtnl->rbuffer, len) && rtnl->rbuffer->nlmsg_flags & NLM_F_MULTI) {
             +  -  +  + ]
     351                 :         60 :                 multi_part = true;
     352                 :            : 
     353         [ +  + ]:         60 :                 for (i = 0; i < rtnl->rqueue_partial_size; i++) {
     354                 :         40 :                         if (rtnl_message_get_serial(rtnl->rqueue_partial[i]) ==
     355         [ +  - ]:         40 :                             rtnl->rbuffer->nlmsg_seq) {
     356                 :         40 :                                 first = rtnl->rqueue_partial[i];
     357                 :         40 :                                 break;
     358                 :            :                         }
     359                 :            :                 }
     360                 :            :         }
     361                 :            : 
     362   [ +  +  +  -  :        472 :         for (new_msg = rtnl->rbuffer; NLMSG_OK(new_msg, len) && !done; new_msg = NLMSG_NEXT(new_msg, len)) {
             +  -  +  - ]
     363      [ +  +  - ]:        344 :                 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
     364                 :            :                 const NLType *nl_type;
     365                 :            : 
     366   [ +  -  -  + ]:        344 :                 if (!group && new_msg->nlmsg_pid != rtnl->sockaddr.nl.nl_pid)
     367                 :            :                         /* not broadcast and not for us */
     368                 :          0 :                         continue;
     369                 :            : 
     370         [ -  + ]:        344 :                 if (new_msg->nlmsg_type == NLMSG_NOOP)
     371                 :            :                         /* silently drop noop messages */
     372                 :          0 :                         continue;
     373                 :            : 
     374         [ +  + ]:        344 :                 if (new_msg->nlmsg_type == NLMSG_DONE) {
     375                 :            :                         /* finished reading multi-part message */
     376                 :         20 :                         done = true;
     377                 :            : 
     378                 :            :                         /* if first is not defined, put NLMSG_DONE into the receive queue. */
     379         [ +  - ]:         20 :                         if (first)
     380                 :         20 :                                 continue;
     381                 :            :                 }
     382                 :            : 
     383                 :            :                 /* check that we support this message type */
     384                 :        324 :                 r = type_system_get_type(type_system_root, &nl_type, new_msg->nlmsg_type);
     385         [ -  + ]:        324 :                 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         [ -  + ]:        324 :                 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                 :        324 :                 r = message_new_empty(rtnl, &m);
     400         [ -  + ]:        324 :                 if (r < 0)
     401                 :          0 :                         return r;
     402                 :            : 
     403                 :        324 :                 m->broadcast = !!group;
     404                 :            : 
     405                 :        324 :                 m->hdr = memdup(new_msg, new_msg->nlmsg_len);
     406         [ -  + ]:        324 :                 if (!m->hdr)
     407                 :          0 :                         return -ENOMEM;
     408                 :            : 
     409                 :            :                 /* seal and parse the top-level message */
     410                 :        324 :                 r = sd_netlink_message_rewind(m);
     411         [ -  + ]:        324 :                 if (r < 0)
     412                 :          0 :                         return r;
     413                 :            : 
     414                 :            :                 /* push the message onto the multi-part message stack */
     415         [ +  + ]:        324 :                 if (first)
     416                 :        236 :                         m->next = first;
     417                 :        324 :                 first = TAKE_PTR(m);
     418                 :            :         }
     419                 :            : 
     420         [ -  + ]:        128 :         if (len > 0)
     421         [ #  # ]:          0 :                 log_debug("sd-netlink: discarding %zu bytes of incoming message", len);
     422                 :            : 
     423         [ -  + ]:        128 :         if (!first)
     424                 :          0 :                 return 0;
     425                 :            : 
     426   [ +  +  +  + ]:        128 :         if (!multi_part || done) {
     427                 :            :                 /* we got a complete message, push it on the read queue */
     428                 :         88 :                 r = rtnl_rqueue_make_room(rtnl);
     429         [ -  + ]:         88 :                 if (r < 0)
     430                 :          0 :                         return r;
     431                 :            : 
     432                 :         88 :                 rtnl->rqueue[rtnl->rqueue_size++] = TAKE_PTR(first);
     433                 :            : 
     434   [ +  +  +  - ]:         88 :                 if (multi_part && (i < rtnl->rqueue_partial_size)) {
     435                 :            :                         /* remove the message form the partial read queue */
     436                 :         20 :                         memmove(rtnl->rqueue_partial + i,rtnl->rqueue_partial + i + 1,
     437                 :         20 :                                 sizeof(sd_netlink_message*) * (rtnl->rqueue_partial_size - i - 1));
     438                 :         20 :                         rtnl->rqueue_partial_size--;
     439                 :            :                 }
     440                 :            : 
     441                 :         88 :                 return 1;
     442                 :            :         } else {
     443                 :            :                 /* we only got a partial multi-part message, push it on the
     444                 :            :                    partial read queue */
     445         [ +  + ]:         40 :                 if (i < rtnl->rqueue_partial_size)
     446                 :         20 :                         rtnl->rqueue_partial[i] = TAKE_PTR(first);
     447                 :            :                 else {
     448                 :         20 :                         r = rtnl_rqueue_partial_make_room(rtnl);
     449         [ -  + ]:         20 :                         if (r < 0)
     450                 :          0 :                                 return r;
     451                 :            : 
     452                 :         20 :                         rtnl->rqueue_partial[rtnl->rqueue_partial_size++] = TAKE_PTR(first);
     453                 :            :                 }
     454                 :            : 
     455                 :         40 :                 return 0;
     456                 :            :         }
     457                 :            : }

Generated by: LCOV version 1.14