如何解决sendmsg() / connect() 因原始 IPv6 套接字上的权限错误/EACCES 失败
我尝试让 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 是公共 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;
}
解决方法
您需要 root(或足够的子集作为功能)来执行原始数据包 io。这是因为构建和发送或捕获任意数据包的能力允许您欺骗或拦截属于另一个用户或系统网络设施的连接的一部分的流量。 EACCES
告诉您您没有足够的权限。
问题是没有为我尝试访问的特定地址设置 ip 路由。
ip route add [IP] via [gateway] dev [interface]
解决了。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。