C IP数据包校验和值不正确且可变

C IP数据包校验和值不正确且可变,c,networking,wireshark,checksum,packet,C,Networking,Wireshark,Checksum,Packet,目标:从零开始制作数据包并发送 问题:在wireshark上启用“验证校验和”后,我注意到预期的校验和值应为0xa85d,但我使用的校验和函数每次计算的值不同。如果数据包始终相同,为什么校验和会发生变化?我计算的校验和与wireshark期望的校验和不匹配的原因是什么?谢谢 #include <sys/socket.h> #include <linux/if_packet.h> #include <net/ethernet.h> /* the L2 proto

目标:从零开始制作数据包并发送

问题:在wireshark上启用“验证校验和”后,我注意到预期的校验和值应为0xa85d,但我使用的校验和函数每次计算的值不同。如果数据包始终相同,为什么校验和会发生变化?我计算的校验和与wireshark期望的校验和不匹配的原因是什么?谢谢

#include <sys/socket.h>
#include <linux/if_packet.h>
#include <net/ethernet.h> /* the L2 protocols */
#include <netinet/ip.h> 
#include <netinet/udp.h> 
#include <stdio.h>
#include <arpa/inet.h> /* htons */
#include <sys/ioctl.h> 
#include <net/if.h> /* ifreq */
#include <string.h> 
#include <stdlib.h>

#include <linux/seccomp.h> /* seccomp_data */

#define BUF_SIZE 1024 



// https://gist.github.com/leonid-ed/909a883c114eb58ed49f
unsigned short csum(unsigned short *buf, int nwords)
{
  unsigned long sum;
  for(sum=0; nwords>0; nwords--)
    sum += *buf++;
  sum = (sum >> 16) + (sum &0xffff);
  sum += (sum >> 16);
  return (unsigned short)(~sum);
}

int main(){

    const char IF[] = "lo"; // modify to change interface
    int sockfd, ifindex, tx_len=sizeof(struct ether_header)+sizeof(struct iphdr)+sizeof(struct udphdr);
    struct ifreq ifr;
    size_t if_name_len;
    char buf[BUF_SIZE];
    struct ether_header *eh = (struct ether_header *) buf;
    struct iphdr *iph = (struct iphdr *) (buf + sizeof(struct ether_header));
    struct udphdr *udph = (struct udphdr *) (buf + sizeof(struct ether_header) + sizeof(struct iphdr));
    unsigned char *data = buf + sizeof(struct ether_header) + sizeof(struct iphdr) + sizeof(struct udphdr);
    u_int16_t src_port, dst_port;
    struct sockaddr_ll dst_addr;
    struct seccomp_data sec_payload;
    const char dmac[] = {0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff};
    const char smac[] = {0x00, 0xd0, 0x56, 0xf2, 0xb5, 0x12};

    // create raw socket to send/receive ethernet frames that can transport all protocols
    if ((sockfd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL))) == -1) {
        perror("socket");
    }

    // get interface name length
    if_name_len = strlen(IF);
    if(if_name_len < IF_NAMESIZE) {
        strncpy(ifr.ifr_name, IF, strlen(IF));
        ifr.ifr_name[if_name_len]=0;
    }

    // get the interface index number
    if(ioctl(sockfd, SIOCGIFINDEX, &ifr) == -1){
        perror("ioctl");
    }
        ifindex = ifr.ifr_ifindex;

    // build ethernet header    
        memcpy(eh->ether_dhost, dmac, ETHER_ADDR_LEN);  
        memcpy(eh->ether_shost, smac, ETHER_ADDR_LEN);  
    eh->ether_type = htons(ETH_P_IP);

    // add a struct seccomp_data as data 
        memset(&sec_payload,5,sizeof(struct seccomp_data)); 
    data = (char*)malloc(sizeof(struct seccomp_data));
    memcpy(data, (const unsigned char *)&sec_payload, sizeof(struct seccomp_data));

    int i;
    for(i=0;i<sizeof(sec_payload);i++){
        buf[tx_len++] = data[i];
        printf("%02X ",data[i]);
    }

    // build ip header
    iph->ihl = 5;
    iph->version = 4;
    iph->tos = 0;
    iph->tot_len = htons(sizeof(struct iphdr) + sizeof(struct udphdr) + sizeof(sec_payload));
    iph->id = htons(54321);
    iph->frag_off = 0;
    iph->ttl = 64;
    iph->protocol = IPPROTO_UDP;
    iph->check = 0;
    iph->saddr = inet_addr("127.0.0.1");
    iph->daddr = inet_addr("127.0.0.1");

    // build udp header 
    udph->source = htons(8090);
    udph->dest = htons(8091);
    udph->len = htons(sizeof(struct udphdr) + sizeof(sec_payload));

    iph->check = csum((unsigned short *)buf,  sizeof(struct iphdr) + sizeof(struct udphdr) + sizeof(sec_payload));

    memset(&dst_addr,0,sizeof(struct sockaddr_ll)); 
    dst_addr.sll_ifindex = ifr.ifr_ifindex; 
    dst_addr.sll_halen = ETH_ALEN;
    memcpy(dst_addr.sll_addr, dmac, ETH_ALEN);

        if (sendto(sockfd, buf, tx_len, 0, (struct sockaddr*)&dst_addr, sizeof(struct sockaddr_ll)) < 0)
        printf("Send failed\n");
    return 0;
}
#包括
#包括
#包括/*L2协议*/
#包括
#包括
#包括
#包括/*HTON*/
#包括
#包括/*ifreq*/
#包括
#包括
#包括/*seccomp_数据*/
#定义BUF_大小1024
// https://gist.github.com/leonid-ed/909a883c114eb58ed49f
无符号短csum(无符号短*buf,整数nwords)
{
无符号长和;
对于(总和=0;nwords>0;nwords--)
总和+=*buf++;
总和=(总和>>16)+(总和&0xffff);
总和+=(总和>>16);
返回值(无符号短)(~sum);
}
int main(){
const char IF[]=“lo”;//修改以更改接口
int sockfd,ifindex,tx_len=sizeof(结构以太网头)+sizeof(结构iphdr)+sizeof(结构udphdr);
结构ifreq-ifr;
尺寸(如果名称);
字符大小[buf_SIZE];
结构以太单元头*eh=(结构以太单元头*)buf;
结构iphdr*iph=(结构iphdr*)(buf+sizeof(结构ether_头));
结构udphdr*udph=(结构udphdr*)(buf+sizeof(结构ether_头)+sizeof(结构iphdr));
unsigned char*data=buf+sizeof(struct ether_header)+sizeof(struct iphdr)+sizeof(struct udphdr);
u_int16_t src_端口、dst_端口;
结构sockaddr\u ll dst\u addr;
结构seccomp_数据secu_有效载荷;
常量字符dmac[]={0xaa,0xbb,0xcc,0xdd,0xee,0xff};
常量字符smac[]={0x00,0xd0,0x56,0xf2,0xb5,0x12};
//创建原始套接字以发送/接收可传输所有协议的以太网帧
if((sockfd=socket(AF_数据包,SOCK_原始,htons(ETH_P_ALL)))=-1){
佩罗(“插座”);
}
//获取接口名称长度
if_name_len=strlen(if);
if(if_name_len以太主机、dmac、以太地址);
memcpy(eh->Ethernet\u shost、smac、Ethernet\u ADDR\u LEN);
eh->乙醚类型=htons(乙醚类型);
//将结构seccomp_数据添加为数据
memset(&sec_payload,5,sizeof(struct seccomp_data));
数据=(char*)malloc(sizeof(struct seccomp_data));
memcpy(数据,(常量无符号字符*)和sec_有效负载,sizeof(结构seccomp_数据));
int i;
对于(i=0;iihl=5;
iph->version=4;
iph->tos=0;
iph->tot_len=htons(sizeof(struct iphdr)+sizeof(struct udphdr)+sizeof(sec_payload));
iph->id=htons(54321);
iph->frag_off=0;
iph->ttl=64;
iph->protocol=IPPROTO_UDP;
iph->check=0;
iph->saddr=inet_addr(“127.0.0.1”);
iph->daddr=inet_addr(“127.0.0.1”);
//生成udp报头
udph->source=htons(8090);
udph->dest=htons(8091);
udph->len=htons(sizeof(struct udphdr)+sizeof(sec_payload));
iph->check=csum((无符号短*)buf,sizeof(struct iphdr)+sizeof(struct udphdr)+sizeof(sec_有效载荷));
memset(&dst_addr,0,sizeof(struct sockaddr_ll));
dst_addr.sll_ifindex=ifr.ifr_ifindex;
dst_addr.sll_halen=ETH_ALEN;
memcpy(dst地址、sll地址、dmac地址、ETH地址);
如果(发送到(sockfd,buf,tx_len,0,(struct sockaddr*)和dst_addr,sizeof(struct sockaddr_ll))<0)
printf(“发送失败\n”);
返回0;
}

说明如何计算校验和,包括第4.1节中的示例C代码(IPv4校验和位于IPv4标头上,如中所述)。我是否应该使用10作为16位(2字节)的数字考虑到ip报头的长度为20字节这一事实的字数?我的想法是,字数总是10个,或者这个假设是错误的?您应该真正计算IPv4报头的长度,因为它是一个可变长度。IPv4报头将至少有5个32位字长,多达15个32位字长。报头长度以32位为单位字,包含在IPv4标头的IHL字段中,您可以将该值乘以16位字的标头长度。只有在设置了选项字段时,标头大小才与5个字不同。在示例中,我没有设置字段选项。这会改变标头长度,从而改变校验和吗?您可以使用magic number
10
如果您愿意,但我要说的是,这是一种糟糕的编程实践。正确的代码将从报头字段中导出报头中的16位字数,该字段告诉您报头的长度。这就是该字段的全部用途。此外,请记住,您还必须计算传入数据包的校验和,其中您有n个o关于报头长度的概念,因此有一种方法可以导出发送和接收例程都可以调用的校验和。记住,应该避免使用幻数。