docs: def nl_recv(sk, nla, buf, creds=None):
      """Receive data from Netlink socket.
  
      https://github.com/thom311/libnl/blob/libnl3_2_25/lib/nl.c#L625
  
      Receives data from a connected netlink socket using recvmsg() and returns the number of bytes read. The read data is
      stored in a newly allocated buffer that is assigned to `buf`. The peer's netlink address will be stored in `nla`.
  
      This function blocks until data is available to be read unless the socket has been put into non-blocking mode using
      nl_socket_set_nonblocking() in which case this function will return immediately with a return value of 0.
  
      The buffer size used when reading from the netlink socket and thus limiting the maximum size of a netlink message
      that can be read defaults to the size of a memory page (getpagesize()). The buffer size can be modified on a per
      socket level using the function `nl_socket_set_msg_buf_size()`.
  
      If message peeking is enabled using nl_socket_enable_msg_peek() the size of the message to be read will be
      determined using the MSG_PEEK flag prior to performing the actual read. This leads to an additional recvmsg() call
      for every read operation which has performance implications and is not recommended for high throughput protocols.
  
      An eventual interruption of the recvmsg() system call is automatically handled by retrying the operation.
  
      If receiving of credentials has been enabled using the function `nl_socket_set_passcred()`, this function will
      allocate a new struct `ucred` filled with the received credentials and assign it to `creds`.
  
      Positional arguments:
      sk -- Netlink socket (nl_sock class instance) (input).
      nla -- Netlink socket structure to hold address of peer (sockaddr_nl class instance) (output).
      buf -- destination bytearray() for message content (output).
      creds -- destination class instance for credentials (ucred class instance) (output).
  
      Returns:
      Two-item tuple. First item is number of bytes read, 0 on EOF, 0 on no data event (non-blocking mode), or a negative
      error code. Second item is the message content from the socket or None.
      """
      flags = 0
      page_size = resource.getpagesize() * 4
      if sk.s_flags & NL_MSG_PEEK:
          flags |= socket.MSG_PEEK | socket.MSG_TRUNC
      iov_len = sk.s_bufsize or page_size
  
      if creds and sk.s_flags & NL_SOCK_PASSCRED:
          raise NotImplementedError  # TODO https://github.com/Robpol86/libnl/issues/2
  
      while True:  # This is the `goto retry` implementation.
          try:
              if hasattr(sk.socket_instance, 'recvmsg'):
                  iov, _, msg_flags, address = sk.socket_instance.recvmsg(iov_len, 0, flags)
              else:
                  iov, address = sk.socket_instance.recvfrom(iov_len, flags)
                  msg_flags = 0
          except OSError as exc:
              if exc.errno == errno.EINTR:
                  continue  # recvmsg() returned EINTR, retrying.
              return -nl_syserr2nlerr(exc.errno)
          nla.nl_family = sk.socket_instance.family  # recvmsg() in C does this, but not Python's.
          if not iov:
              return 0
  
          if msg_flags & socket.MSG_CTRUNC:
              raise NotImplementedError  # TODO https://github.com/Robpol86/libnl/issues/2
  
          if iov_len < len(iov) or msg_flags & socket.MSG_TRUNC:
              # Provided buffer is not long enough.
              # Enlarge it to size of n (which should be total length of the message) and try again.
              iov_len = len(iov)
              continue
  
          if flags:
              # Buffer is big enough, do the actual reading.
              flags = 0
              continue
  
          nla.nl_pid = address[0]
          nla.nl_groups = address[1]
  
          if creds and sk.s_flags * NL_SOCK_PASSCRED:
              raise NotImplementedError  # TODO https://github.com/Robpol86/libnl/issues/2
  
          if iov:
              buf += iov
  
          return len(buf)