socket() 设置的 Errno 在联机帮助页中未记录

Errno set by socket() is undocumented in the manpage

本文档 socket(3p) documents the signature to this function and a basic introduction on how to use it. The error section 的联机帮助页列出了几个 errno 值,并解释了错误发生的原因。不幸的是,我在读取 EPERM 时收到一个未记录的错误,其中数字 1 表示 Operation not permitted。未记录的 errno 最初是在 glibc-2.33clang-12.0.1 的 ArchLinux 环境下发现的。我希望以下概念验证 (PoC) 可以在其他 Linux 环境中重现:

#include <errno.h>
#include <sys/socket.h>
#include <netinet/ip.h>

int main() {
    socket(PF_INET, SOCK_RAW, IPPROTO_TCP);
    return errno;
}

以下命令可能有助于重现,其中 PoC 代码存储在文件 raw-socket-poc.c:

$ clang -O0 -g raw-socket-poc.c -o raw-socket-poc
$ ./raw-socket-poc; errno $?
EPERM 1 Operation not permitted

其他观察表明,将 IPPROTO_TCP 更改为 IPPROTO_UDP 没有任何帮助。但是,通过将 IPROTO_TCP 替换为建议的 0,程序以记录错误 EPROTONOSUPPORT 93 Protocol not supported 结束。进一步的实验注意到 运行 作为 root 的原始 PoC yold 没有错误。不幸的是,如果 PoC 运行 作为具有 CAP_NET_RAW 能力的非特权用户,就会发生未记录的错误。与 # setcap CAP_NET_RAW=+eip ./raw-socket-poc 关联的 PoC 的能力提升,并用 $ getcap ./raw-socket-poc 进行了双重检查,其中 ./raw-socket-poc cap_net_raw=eip 显示。

由于信息有限,很难得出缺少文档是故意为之还是设计使然的结论。因此,如果有关于如何使用联机帮助页和原始套接字相关功能的解释,我们将不胜感激。

如果您查看“错误”部分底部的 man 2 socket。你会看到它提到可能会产生其他错误。

ERRORS
       EACCES Permission to create a socket of the specified type and/or  pro‐
              tocol is denied.

       EAFNOSUPPORT
              The  implementation  does not support the specified address fam‐
              ily.

       EINVAL Unknown protocol, or protocol family not available.

       EINVAL Invalid flags in type.

       EMFILE Process file table overflow.

       ENFILE The system limit on the total number  of  open  files  has  been
              reached.

       ENOBUFS or ENOMEM
              Insufficient  memory is available.  The socket cannot be created
              until sufficient resources are freed.

       EPROTONOSUPPORT
              The protocol type or the specified  protocol  is  not  supported
              within this domain.

       Other errors may be generated by the underlying protocol modules.

如果我们再检查 man 7 raw:

SYNOPSIS

       #include <sys/socket.h>
       #include <netinet/in.h>
       raw_socket = socket(AF_INET, SOCK_RAW, int protocol);

...

ERRORS

       EACCES User tried to send to a broadcast address without having
              the broadcast flag set on the socket.

       EFAULT An invalid memory address was supplied.

       EINVAL Invalid argument.

       EMSGSIZE
              Packet too big.  Either Path MTU Discovery is enabled (the
              IP_MTU_DISCOVER socket flag) or the packet size exceeds
              the maximum allowed IPv4 packet size of 64 kB.

       EOPNOTSUPP
              Invalid flag has been passed to a socket call (like
              MSG_OOB).

       EPERM  The user doesn't have permission to open raw sockets.
              Only processes with an effective user ID of 0 or the
              CAP_NET_RAW attribute may do that.

       EPROTO An ICMP error has arrived reporting a parameter problem.

我们看到可以生成EPERM