某些 FreeBSD 系统上 sendmsg() 的参数无效错误
Invalid argument error for sendmsg() on some FreeBSD systems
我有从特定源 IP 发送 UDP 数据包的代码(见下文)。
这在我迄今为止尝试过的所有系统上都运行良好,包括 FreeBSD。
不幸的是,在客户端系统上 sendmsg() 失败并出现 "invalid argument" 错误,我无法弄清楚原因。
FreeBSD 版本相同,所有系统上的测试都使用相同类型的 IPv4 地址作为源和目标。
我做了一个 ktrace,但只显示了部分使用的参数(sockaddr_in6),但这些看起来不错。 Valgrind 也没有抱怨(在我的系统上)。
我如何找到它?是否有显示 sendmsg() 调用的完整 msghdr 结构的工具?
更新:请关注我可以使用的工具或技术。您可以查看代码片段,但如果没有周围的代码,它将无法编译。
ssize_t UDPSendWithSourceIP(int fd, void * data, size_t len, const sockaddr_in6 & toAddress)
{
struct sockaddr_in6 dest = toAddress;
// set source address
PIPSocket::Address src = RasServer::Instance()->GetLocalAddress(toIP);
struct msghdr msgh = { };
struct cmsghdr *cmsg;
struct iovec iov = { };
char cbuf[256];
memset(&cbuf, 0, sizeof(cbuf));
// Set up iov and msgh structures
memset(&msgh, 0, sizeof(struct msghdr));
iov.iov_base = data;
iov.iov_len = len;
msgh.msg_iov = &iov;
msgh.msg_iovlen = 1;
msgh.msg_name = (struct sockaddr*)&dest;
// must pass short len when sending to IPv4 address on Solaris 11, OpenBSD and NetBSD
// sizeof(dest) is OK on Linux and FreeBSD
size_t addr_len = sizeof(sockaddr_in);
if (toIP.GetVersion() == 6)
addr_len = sizeof(sockaddr_in6);
msgh.msg_namelen = addr_len;
if ((((struct sockaddr*)&dest)->sa_family == AF_INET6)) {
struct in6_pktinfo *pkt;
msgh.msg_control = cbuf;
msgh.msg_controllen = CMSG_SPACE(sizeof(*pkt));
cmsg = CMSG_FIRSTHDR(&msgh);
cmsg->cmsg_level = IPPROTO_IPV6;
cmsg->cmsg_type = IPV6_PKTINFO;
cmsg->cmsg_len = CMSG_LEN(sizeof(*pkt));
pkt = (struct in6_pktinfo *) CMSG_DATA(cmsg);
memset(pkt, 0, sizeof(*pkt));
pkt->ipi6_addr = src;
msgh.msg_controllen = cmsg->cmsg_len;
} else
{
#ifdef IP_SENDSRCADDR // FreeBSD
struct in_addr *in;
msgh.msg_control = cbuf;
msgh.msg_controllen = CMSG_SPACE(sizeof(*in));
cmsg = CMSG_FIRSTHDR(&msgh);
cmsg->cmsg_level = IPPROTO_IP;
cmsg->cmsg_type = IP_SENDSRCADDR;
cmsg->cmsg_len = CMSG_LEN(sizeof(*in));
in = (struct in_addr *) CMSG_DATA(cmsg);
*in = src;
#endif // IP_SENDSRCADDR
}
ssize_t bytesSent = sendmsg(fd, &msgh, 0);
if (bytesSent < 0) {
cerr << "RTP\tSend error " << strerror(errno) << endl;
}
return bytesSent;
}
事实证明,FreeBSD 在允许在 UDP 套接字上使用 IP_SENDSRCADDR 时非常挑剔。如果套接字绑定到 INADDR_ANY 我的代码工作正常。如果套接字绑定到单个 IP,则 sendmsg() returns EINVAL(无效参数)。
我有从特定源 IP 发送 UDP 数据包的代码(见下文)。 这在我迄今为止尝试过的所有系统上都运行良好,包括 FreeBSD。
不幸的是,在客户端系统上 sendmsg() 失败并出现 "invalid argument" 错误,我无法弄清楚原因。
FreeBSD 版本相同,所有系统上的测试都使用相同类型的 IPv4 地址作为源和目标。
我做了一个 ktrace,但只显示了部分使用的参数(sockaddr_in6),但这些看起来不错。 Valgrind 也没有抱怨(在我的系统上)。
我如何找到它?是否有显示 sendmsg() 调用的完整 msghdr 结构的工具?
更新:请关注我可以使用的工具或技术。您可以查看代码片段,但如果没有周围的代码,它将无法编译。
ssize_t UDPSendWithSourceIP(int fd, void * data, size_t len, const sockaddr_in6 & toAddress)
{
struct sockaddr_in6 dest = toAddress;
// set source address
PIPSocket::Address src = RasServer::Instance()->GetLocalAddress(toIP);
struct msghdr msgh = { };
struct cmsghdr *cmsg;
struct iovec iov = { };
char cbuf[256];
memset(&cbuf, 0, sizeof(cbuf));
// Set up iov and msgh structures
memset(&msgh, 0, sizeof(struct msghdr));
iov.iov_base = data;
iov.iov_len = len;
msgh.msg_iov = &iov;
msgh.msg_iovlen = 1;
msgh.msg_name = (struct sockaddr*)&dest;
// must pass short len when sending to IPv4 address on Solaris 11, OpenBSD and NetBSD
// sizeof(dest) is OK on Linux and FreeBSD
size_t addr_len = sizeof(sockaddr_in);
if (toIP.GetVersion() == 6)
addr_len = sizeof(sockaddr_in6);
msgh.msg_namelen = addr_len;
if ((((struct sockaddr*)&dest)->sa_family == AF_INET6)) {
struct in6_pktinfo *pkt;
msgh.msg_control = cbuf;
msgh.msg_controllen = CMSG_SPACE(sizeof(*pkt));
cmsg = CMSG_FIRSTHDR(&msgh);
cmsg->cmsg_level = IPPROTO_IPV6;
cmsg->cmsg_type = IPV6_PKTINFO;
cmsg->cmsg_len = CMSG_LEN(sizeof(*pkt));
pkt = (struct in6_pktinfo *) CMSG_DATA(cmsg);
memset(pkt, 0, sizeof(*pkt));
pkt->ipi6_addr = src;
msgh.msg_controllen = cmsg->cmsg_len;
} else
{
#ifdef IP_SENDSRCADDR // FreeBSD
struct in_addr *in;
msgh.msg_control = cbuf;
msgh.msg_controllen = CMSG_SPACE(sizeof(*in));
cmsg = CMSG_FIRSTHDR(&msgh);
cmsg->cmsg_level = IPPROTO_IP;
cmsg->cmsg_type = IP_SENDSRCADDR;
cmsg->cmsg_len = CMSG_LEN(sizeof(*in));
in = (struct in_addr *) CMSG_DATA(cmsg);
*in = src;
#endif // IP_SENDSRCADDR
}
ssize_t bytesSent = sendmsg(fd, &msgh, 0);
if (bytesSent < 0) {
cerr << "RTP\tSend error " << strerror(errno) << endl;
}
return bytesSent;
}
事实证明,FreeBSD 在允许在 UDP 套接字上使用 IP_SENDSRCADDR 时非常挑剔。如果套接字绑定到 INADDR_ANY 我的代码工作正常。如果套接字绑定到单个 IP,则 sendmsg() returns EINVAL(无效参数)。