获取接收数据包的ToS字节的正确(可移植,稳定)方式是什么?我正在使用recvmsg()进行UDP,并且在
linux上我可以获得ToS,如果我setsockopt()IP_RECVTOS / IPV6_RECVTCLASS,但IP_RECVTOS似乎在我的BSD系统上不可用.这样做的正确方法是什么?
我主要希望这可以在BSD和Solaris上运行.
编辑:
澄清:
我目前使用recvmsg(),我在Linux上的msg_control字段中获取TTL和TOS,但为了获得TTL和TOS,我需要setsockopt() – 启用IP_RECVTTL和IP_RECVTOS.而且由于Solaris和BSD(目前与FreeBSD合作)没有我所看到的IP_RECVTOS,因此在循环CMSG数据时我没有获得TOS.
我尝试启用IP_RECVOPTS和IP_RECVRETOPTS,但我仍然没有得到任何IP_TOS类型的CMSG.
编辑2:
我希望ToS能够(尽可能地)验证它在传输过程中没有被覆盖.例如,如果一个VoIP应用程序突然注意到它没有获得EF标记的数据包,那么出现问题并且应该有警报. (不,我不希望EF在公共互联网上受到尊重或保留)
我想要TTL基本上只是因为我可以.假设这可以用来触发“我和另一方之间的网络中的某些变化”警报,这可能有助于了解某些事情是否同时停止工作.
最佳答案 我在想你是否可以创建两个套接字.
> DGRAM类型的一个插座专门用于发送
>一个专门用于接收的Raw套接字.
由于您使用的是UDP,因此可以在Raw Sock Fd上调用绑定recvFrom,然后手动解压缩IP头以确定TOS或TTL.
当你想发送时,使用DGRAM sockFd,所以你不必费心去实际创建UDP&你自己的IP包.
可能存在内核可能将接收到的缓冲区传递到套接字或UDP套接字而不是Raw套接字或仅传递给Raw套接字的问题.如果是这种情况(或者如果它依赖于实现),那么我们将回到原点.但是,您可以尝试在Raw套接字上调用bind并查看它是否有帮助.我知道这可能是一个黑客,但在网上搜索一个用于BSD的setsockopt没有返回任何内容.
编辑:我写了一个示例程序
它实现了目标.
下面的代码创建了两个套接字(一个原始和一个udp). udp套接字绑定在我期望接收数据的实际端口上,而原始套接字绑定在端口0上.我在Linux上进行了测试,就像我预期的那样,两个套接字都接收到端口2905的任何数据.然而,我能够检索TTL& TOS值.不要为了代码的质量而投票.我只是在试验它是否会起作用.
进一步编辑:禁用UDP套接字接收.
我进一步增强了代码以禁用UDP数据包的接收.使用setsockopt,我将UDP的套接字接收缓冲区设置为0.这可确保内核不会将数据包传递给UDP套接字.恕我直言,您现在可以使用UDP套接字专门用于发送和原始套接字进行读取.这应该适用于BSD和Solaris.
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<netinet/ip.h>
#include<arpa/inet.h>
#include<string.h>
#include "protHeaders.x"
#include "gen.h"
int main(void)
{
S32 rawSockFd;
S32 udpSockFd;
struct sockaddr_in rsin;
struct sockaddr_in usin;
S32 one = 1;
const S32* val = &one;
struct timeval tv;
fd_set rfds;
S32 maxFd;
S16 ret;
S8 rawBuffer[2048];
S8 udpBuffer[2048];
struct sockaddr udpFrom,rawFrom;
socklen_t rLen,uLen;
memset(rawBuffer,0,sizeof(rawBuffer));
memset(udpBuffer,0,sizeof(udpBuffer));
memset(udpFrom,0,sizeof(udpFrom));
memset(rawFrom,0,sizeof(rawFrom));
if ((rawSockFd = socket(PF_INET,SOCK_RAW,IPPROTO_UDP)) < 0)
{
perror("socket:create");
RETVALUE(RFAILED);
}
/* doing the IP_HDRINCL call */
if (setsockopt(rawSockFd,IPPROTO_IP,IP_HDRINCL,val,sizeof(one)) < 0)
{
perror("Server:setsockopt");
RETVALUE(RFAILED);
}
rsin.sin_family = AF_INET;
rsin.sin_addr.s_addr = htonl(INADDR_ANY);
rsin.sin_port = htons(0);
usin.sin_family = AF_INET;
usin.sin_addr.s_addr = htons(INADDR_ANY);
usin.sin_port = htons(2905);
if(bind(rawSockFd,(struct sockaddr *)&rsin, sizeof(rsin)) < 0 )
{
perror("Server: bind failed");
RETVALUE(RFAILED);
}
if ((udpSockFd = socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP)) < 0)
{
perror("socket:create");
RETVALUE(RFAILED);
}
if(bind(udpSockFd,(struct sockaddr *)&usin, sizeof(usin)) < 0 )
{
perror("Server: bind failed on udpsocket");
RETVALUE(RFAILED);
}
/*set upd socket receive buffer to 0 */
one = 0;
if (setsockopt(udpSockFd,SOL_SOCKET,SO_RCVBUF,(char *)&one,sizeof(one)) < 0)
{
perror("Server:setsockopt on udpsocket failed");
RETVALUE(RFAILED);
}
tv.tv_sec = 0;
tv.tv_usec = 0;
maxFd = (rawSockFd > udpSockFd)? rawSockFd:udpSockFd;
while(1)
{
FD_ZERO(&rfds);
FD_SET(rawSockFd,&rfds);
FD_SET(udpSockFd,&rfds);
ret = select(maxFd+1,&rfds,0,0,&tv);
if ( ret == -1)
{
perror("Select Failed");
RETVALUE(RFAILED);
}
if(FD_ISSET(rawSockFd,&rfds))
{
printf("Raw Socked Received Message\n");
if(recvfrom(rawSockFd,rawBuffer,sizeof(rawBuffer),0,&rawFrom,&rLen) == -1)
{
perror("Raw socket recvfrom failed");
RETVALUE(RFAILED);
}
/*print the tos */
printf("TOS:%x\n",*(rawBuffer+1));
printf("TTL:%x\n",*(rawBuffer+8));
}
if(FD_ISSET(udpSockFd,&rfds))
{
printf("UDP Socked Received Message\n");
if(recvfrom(udpSockFd,udpBuffer,sizeof(udpBuffer),0,&udpFrom,&uLen) == -1)
{
perror("Udp socket recvfrom failed");
RETVALUE(RFAILED);
}
printf("%s\n",udpBuffer);
}
}
RETVALUE(ROK);
}