1  /* Check recvmsg results for netlink sockets.
       2     Copyright (C) 2015-2023 Free Software Foundation, Inc.
       3     This file is part of the GNU C Library.
       4  
       5     The GNU C Library is free software; you can redistribute it and/or
       6     modify it under the terms of the GNU Lesser General Public
       7     License as published by the Free Software Foundation; either
       8     version 2.1 of the License, or (at your option) any later version.
       9  
      10     The GNU C Library is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13     Lesser General Public License for more details.
      14  
      15     You should have received a copy of the GNU Lesser General Public
      16     License along with the GNU C Library; if not, see
      17     <https://www.gnu.org/licenses/>.  */
      18  
      19  #include <errno.h>
      20  #include <fcntl.h>
      21  #include <stdio.h>
      22  #include <stdbool.h>
      23  #include <sys/socket.h>
      24  
      25  #include "netlinkaccess.h"
      26  
      27  static int
      28  get_address_family (int fd)
      29  {
      30    struct sockaddr_storage sa;
      31    socklen_t sa_len = sizeof (sa);
      32    if (__getsockname (fd, (struct sockaddr *) &sa, &sa_len) < 0)
      33      return -1;
      34    /* Check that the socket family number is preserved despite in-band
      35       signaling.  */
      36    _Static_assert (sizeof (sa.ss_family) < sizeof (int), "address family size");
      37    _Static_assert (0 < (__typeof__ (sa.ss_family)) -1,
      38                    "address family unsigned");
      39    return sa.ss_family;
      40  }
      41  
      42  void
      43  __netlink_assert_response (int fd, ssize_t result)
      44  {
      45    if (result < 0)
      46      {
      47        /* Check if the error is unexpected.  */
      48        bool terminate = false;
      49        int error_code = errno;
      50        int family = get_address_family (fd);
      51        if (family != AF_NETLINK)
      52          /* If the address family does not match (or getsockname
      53             failed), report the original error.  */
      54          terminate = true;
      55        else if (error_code == EBADF
      56            || error_code == ENOTCONN
      57            || error_code == ENOTSOCK
      58            || error_code == ECONNREFUSED)
      59          /* These errors indicate that the descriptor is not a
      60             connected socket.  */
      61          terminate = true;
      62        else if (error_code == EAGAIN || error_code == EWOULDBLOCK)
      63          {
      64            /* The kernel might return EAGAIN for other reasons than a
      65               non-blocking socket.  But if the socket is not blocking,
      66               it is not ours, so report the error.  */
      67            int mode = __fcntl (fd, F_GETFL, 0);
      68            if (mode < 0 || (mode & O_NONBLOCK) != 0)
      69              terminate = true;
      70          }
      71        if (terminate)
      72          {
      73            char message[200];
      74            if (family < 0)
      75              __snprintf (message, sizeof (message),
      76                          "Unexpected error %d on netlink descriptor %d.\n",
      77                          error_code, fd);
      78            else
      79              __snprintf (message, sizeof (message),
      80                          "Unexpected error %d on netlink descriptor %d"
      81                          " (address family %d).\n",
      82                          error_code, fd, family);
      83            __libc_fatal (message);
      84          }
      85        else
      86          /* Restore original errno value.  */
      87          __set_errno (error_code);
      88      }
      89    else if (result < sizeof (struct nlmsghdr))
      90      {
      91        char message[200];
      92        int family = get_address_family (fd);
      93        if (family < 0)
      94            __snprintf (message, sizeof (message),
      95                        "Unexpected netlink response of size %zd"
      96                        " on descriptor %d\n",
      97                        result, fd);
      98        else
      99            __snprintf (message, sizeof (message),
     100                        "Unexpected netlink response of size %zd"
     101                        " on descriptor %d (address family %d)\n",
     102                        result, fd, family);
     103        __libc_fatal (message);
     104      }
     105  }
     106  libc_hidden_def (__netlink_assert_response)