原始套接字帮助:为什么使用原始套接字创建的UDP数据包无法被内核UDP接收?

5
我正在学习原始套接字。我使用了IP_HDRINCL选项来构建自己的IP头部。在IP头部之后,我正在构建一个UDP头部。然后,我将数据包发送到系统的回环地址。我有另一个正在运行的程序,它会捕获UDP数据包。为了检查数据包是否被正确地形成和接收,我还有另一个进程正在读取原始IP数据报。我的问题是,虽然第二个进程(读取原始数据报)工作正常(所有IP和UDP字段似乎都没问题),但第一个进程(接收UDP)却没有接收到我创建的任何数据包。IP头部中的协议字段和端口也匹配...我正在使用Linux 2.6.35-22。我想知道这在新内核中是否正常?请检查下面的代码是否有错误。应该接收数据包的UDP进程正在监听绑定到同一台机器上的端口50000的套接字...
unsigned short in_cksum(unsigned short *addr, int len)
{
    int nleft = len;
    int sum = 0;
    unsigned short *w = addr;
    unsigned short answer = 0;

    while (nleft > 1) {
        sum += *w++;
        nleft -= 2;
    }

    if (nleft == 1) {
        *(unsigned char *) (&answer) = *(unsigned char *) w;
        sum += answer;
    }

    sum = (sum >> 16) + (sum & 0xFFFF);
    sum += (sum >> 16);
    answer = ~sum;
    return (answer);
}


main()
{
    int fd=socket(AF_INET,SOCK_RAW,IPPROTO_UDP);

    int val=1;

    int ret=setsockopt(fd,IPPROTO_IP,IP_HDRINCL,&val,sizeof(val));

    char buf[8192];

    /* create a IP header */

    struct iphdr* ip=(struct iphdr*)buf;//(struct iphdr*) malloc(sizeof(struct iphdr));

    ip->version=4;
    ip->ihl=5;
    ip->tos=0;
    ip->id=0;
    ip->frag_off=0;
    ip->ttl=255;
    ip->protocol=IPPROTO_UDP;
    ip->check=0;
    ip->saddr=inet_addr("1.2.3.4");
    ip->daddr=inet_addr("127.0.0.1");


    struct udphdr* udp=(struct udphdr*)(buf+sizeof(struct iphdr));//(struct udphdr*) malloc(sizeof(struct udphdr));
    udp->source=htons(40000);   
    udp->dest=htons(50000);
    udp->check=0;
    char* data=(char*)buf+sizeof(struct iphdr)+sizeof(struct udphdr);strcpy(data,"Harry Potter and the Philosopher's Stone");
    udp->len=htons(sizeof(struct udphdr)+strlen(data));
    udp->check=in_cksum((unsigned short*) udp,8+strlen(data));

    ip->tot_len=htons(sizeof(struct iphdr)+sizeof(struct udphdr)+strlen(data));

    struct sockaddr_in d;
    bzero(&d,sizeof(d));
    d.sin_family=AF_INET;
    d.sin_port=htons(50000);
    inet_pton(AF_INET,"localhost",&d.sin_addr.s_addr);
    while(1)
     sendto(fd,buf,sizeof(struct iphdr)+sizeof(struct udphdr)+strlen(data),0,(struct sockaddr*) &d,sizeof(d));
}   

1
启动Wireshark。他们是否正在连接网络? - rook
是的,它们是正确的...并且它正确地显示协议为UDP,目标端口为50000。但是,源端口被标记为“saftynetp”。它具有正确的值40000,但我不知道这意味着什么。 - pflz
2
捕获由socat或其他工具传输的真实UDP数据包,然后尝试伪造一个完全相同的数据包。确保检查所有内容,包括校验和。在数据包转储上运行类似meld的差异程序效果很好。 - rook
2个回答

3

UDP校验和计算似乎存在问题。

udp->check=in_cksum((unsigned short*) udp,8+strlen(data));

UDP校验和在UDP头之前必须包括一个称为“伪头”的东西。该代码仅在UDP头和有效载荷上计算校验和。UDP接收过程可能由于错误的校验和而无法接收数据包。

在Wireshark中启用校验和验证,并检查UDP数据包的校验和字段是否正确。

请参阅以下内容:


0

我尝试过类似的方法。问题在于套接字API及其使用的任何程序不会返回正在被接口写入的数据,而嗅探器(如Wireshark / tcpdump)使用的原始套接字则会返回。因此,即使您的数据包正确形成,UDP应用程序也无法读取它们。

如果您在网络上有另一台计算机,请使用其中一台计算机生成流量,另一台计算机读取流量。或者,如果您有两个接口,可以分别打开一个原始套接字...一个用于写入,另一个用于读取。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接