【问题标题】:sendmsg() / connect() fails with permission error/EACCES on raw IPv6 socketsendmsg() / connect() 因原始 IPv6 套接字上的权限错误/EACCES 而失败
【发布时间】:2021-01-25 17:42:07
【问题描述】:

我尝试让a program 在实现RFC8157(一种新的隧道协议)的OpenWRT(内核版本:4.14.215,musl libc 版本:1.1.24)上运行。不幸的是,编写它的人似乎不再维护它了。

在某些时候,它会通过sendmsg() 将其第一条消息写入原始 ipv6 套接字。不幸的是 sendmsg() 返回 EACCES。我对系统编程很陌生,并不知道要寻找什么。

我尝试了以下方法:

#> ls -l /proc/[pid]/fd/*
lrwx------    1 root     root            64 Jan 25 17:41 /proc/22727/fd/0 -> /dev/pts/0
lrwx------    1 root     root            64 Jan 25 17:41 /proc/22727/fd/1 -> /dev/pts/0
lrwx------    1 root     root            64 Jan 25 17:41 /proc/22727/fd/2 -> /dev/pts/0
lrwx------    1 root     root            64 Jan 25 17:41 /proc/22727/fd/3 -> socket:[1293688]

#> ls -l /proc/[pid]/fdinfo/*
pos:    0
flags:  02
mnt_id: 8

所以套接字似乎以读/写模式打开。

lsof 也列出了套接字。但由于某种原因,ipv6 地址为 0。

#> lsof | grep [pid]
openhybri 18018                      root    3u     raw6                 0t0      92469 00000000000000000000000000000000:002F->00000000000000000000000000000000:0000 st=07

man page 列出了尝试将 UDP 数据包从广播地址发送到任播地址的可能原因。但这似乎不是这里的情况。原始 IPv6 套接字不是 UDP 套接字(不是吗?),并且 src IP 是公共的。

一切都以root用户身份执行。

#> id
uid=0(root) gid=0(root) groups=0(root)

由于我不太确定要查找什么,这里是整个函数:

sendmsg() 用在最后一个 if 语句中。

bool send_grecpmessage(uint8_t msgtype, uint8_t tuntype, void *attributes, int attributes_size) {
    unsigned char buffer[MAX_PKT_SIZE] = {};
    int size = 0;

    /* GRE header */
    struct grehdr *greh = (struct grehdr *)(buffer + size);
    greh->flags_and_version = htons(GRECP_FLAGSANDVERSION);
    greh->proto = htons(GRECP_PROTO);
    greh->key = htonl(runtime.haap.bonding_key);
    size += sizeof(struct grehdr);

    /* GRECP header */
    struct grecphdr *grecph = (struct grecphdr *)(buffer + size);
    grecph->msgtype_and_tuntype = (msgtype << 4) | tuntype;
    size += sizeof(struct grecphdr);

    /* Add GRECP attributes */
    memcpy(buffer + size, attributes, attributes_size);
    size += attributes_size;

    /* Source & Destination */
    struct sockaddr_in6 src = {};
    src.sin6_family = AF_INET6;
    if (tuntype == GRECP_TUNTYPE_LTE) {
        src.sin6_addr = runtime.lte.interface_ip;
    } else {
        src.sin6_addr = runtime.dsl.interface_ip;
    }
    struct sockaddr_in6 dst = {};
    dst.sin6_family = AF_INET6;
    dst.sin6_addr = runtime.haap.ip;

    /* Construct control information */
    struct msghdr msgh = {};
    struct iovec msgiov = {};
    struct cmsghdr *c;
    struct unp_in_pktinfo {
        struct in6_addr ipi6_addr;
        int ipi6_ifindex;
    } *pi;
    msgh.msg_name = &dst;
    msgh.msg_namelen = sizeof(struct sockaddr_in6);
    msgiov.iov_base = buffer;
    msgiov.iov_len = size;
    msgh.msg_iov = &msgiov;
    msgh.msg_iovlen = 1;
    unsigned char control_buf[CMSG_LEN(sizeof(struct unp_in_pktinfo))] = {};
    msgh.msg_control = &control_buf;
    msgh.msg_controllen = CMSG_LEN(sizeof(struct unp_in_pktinfo));
    c = CMSG_FIRSTHDR(&msgh);
    c->cmsg_level = IPPROTO_IPV6;
    c->cmsg_type = IPV6_PKTINFO;
    c->cmsg_len = CMSG_LEN(sizeof(struct unp_in_pktinfo));
    pi = (struct unp_in_pktinfo *)CMSG_DATA(c);
    pi->ipi6_addr = src.sin6_addr;
    msgh.msg_controllen = c->cmsg_len;

    bool res = true;
    if (memcmp(&src.sin6_addr, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16) != 0) {
        if (sendmsg(sockfd, &msgh, 0) <= 0) {
            logger(LOG_ERROR, "Raw socket send failed: %s\n", strerror(errno));
            res = false;
        }
    } else {
        /* if we don't set a source ip, sendmsg() will use the ip of the outgoing interface
        ** and since the haap doesn't verify source ip's we would still get replies for our hellos
        */
        res = false;
    }

    /* TODO: check if sending failed due to a link failure and call send_grecpnotify_linkfailure if it did */

    return res;
}

【问题讨论】:

    标签: c ipv6 openwrt sendmsg


    【解决方案1】:

    您需要 root(或作为功能的足够子集)来执行原始数据包 io。这是因为构造和发送或捕获任意数据包的能力允许您欺骗或拦截属于另一个用户或系统网络设施的连接的一部分的流量。 EACCES 告诉你你没有足够的权限。

    【讨论】:

    • 我在路由器上以 root 用户身份登录,并以 root 身份运行每个命令。有问题的可执行文件也是如此。但感谢您的回答。问题已编辑。
    【解决方案2】:

    问题是没有为我尝试到达的特定地址设置 ip 路由。

    ip route add [IP] via [gateway] dev [interface]
    

    解决了。

    【讨论】:

      猜你喜欢
      • 2012-07-01
      • 2019-04-03
      • 1970-01-01
      • 2012-03-16
      • 2023-03-09
      • 2018-09-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多