【问题标题】:Calculated tcp checksum and original tcp checksum don't match计算出的 tcp 校验和与原始 tcp 校验和不匹配
【发布时间】:2012-10-08 00:01:31
【问题描述】:

我正在尝试修改进入我的电脑的 ip 数据包。我正在使用 iptables 对数据包进行排队,并使用 libnetfilter_queue 库来修改数据包,实际上是一些 http-header 内容。即使我更改了单个字符,数据包也会被拒绝,很可能是由于 tcp 校验和的更改。所以,我正在尝试重新计算修改数据包的 tcp 校验和。最初,为了测试校验和功能,我没有修改数据包,而只是重新计算校验和。但是计算出来的校验和,与原来的不匹配。这是校验和函数:`

unsigned short tcp_sum_calc(unsigned short len_tcp, unsigned short src_addr[],unsigned short dest_addr[], unsigned short buff[])
{
unsigned char prot_tcp=6;
unsigned long sum;
int nleft;
unsigned short *w;

sum = 0;
nleft = len_tcp;

w=buff;

/* calculate the checksum for the tcp header and payload */
while(nleft > 1)
{
sum = sum + ntohs(*w);
w++;
nleft = nleft - 2;
}

/* if nleft is 1 there ist still on byte left. We add a padding byte (0xFF) to build a 16bit word */
if(nleft>0)
{
    cout<<"check out";
// sum += *w&0xFF; 
sum += ntohs(*w&0xFF00);            // is this the correct way of doing
}

/* add the pseudo header */
sum += ntohs(src_addr[0]);
sum += ntohs(src_addr[1]);
sum += ntohs(dest_addr[0]);
sum += ntohs(dest_addr[1]);
sum += len_tcp;
sum += prot_tcp;

// keep only the last 16 bits of the 32 bit calculated sum and add the carries
sum = (sum >> 16) + (sum & 0xFFFF);
sum += (sum >> 16);

// Take the one's complement of sum
sum = ~sum;

return ((unsigned short) sum);
}

这里是libnetfilter_queue模块的回调函数:

static int analyzeResponse(struct nfq_q_handle *qh, struct nfgenmsg *nfmsg,struct nfq_data *nfa, void *data)
{
int len=0,id=0;
struct iphdr *ip;
struct tcphdr *tcp;
    char *pktData;
string tempPkt;
unsigned short chksum=0,ip_hdr_len,tcp_len;
unsigned int src_ip, des_ip;
struct nfqnl_msg_packet_hdr *pktHeader;
pktHeader = nfq_get_msg_packet_hdr(nfa);

if (pktHeader) 
{
    id = ntohl(pktHeader->packet_id);
}
len = nfq_get_payload ( ( struct nfq_data * ) nfa, (char**)&ip );

if(len) 
{
    pktData=(char*)ip;
    ip_hdr_len=(unsigned short)(pktData[0]&0x7);
    src_ip=ip->saddr;
    des_ip=ip->daddr;
    int pos;
    tempPkt.assign(pktData,len);
    pos=tempPkt.find("teststring",0);
        tempPkt.replace("teststring");
    pktData=(char*)tempPkt.c_str();  
            tcp = (struct tcphdr*) (pktData + (4*ip_hdr_len));

    cout<<"*********************************************************************************";
    cout<<ip_hdr_len;
    cout<<"\ntcp checksum: "<<tcp->check;
    cout<<"\nip packet length: "<<ip->tot_len;
    cout<<"\nip packet length_calc: "<<len;
    cout<<"\nip source address: "<<src_ip;
    cout<<"\nip destination address: "<<des_ip;
    cout<<"*********************************************************************************";
    tcp->check=0; 
tcp_len=len - (4*ip_hdr_len);
    chksum = tcp_sum_calc(tcp_len, (unsigned short *) &src_ip, (unsigned short *) &des_ip, (unsigned short *) &tcp);
    tcp->check=chksum;
    cout<<"\nnew checkksum: "<<chksum;
return nfq_set_verdict(qh, id, NF_ACCEPT, len, (unsigned char*)pktData);

}   
return nfq_set_verdict(qh, id, NF_ACCEPT, len, (unsigned char*)pktData);
}

这是输出:

5
tcp checksum: 11687
ip packet length: 54017
ip packet length_calc: 467
ip source address: 1719453657
ip destination address: 2569775296
new checkksum: 36507

`

【问题讨论】:

    标签: c linux tcp ip checksum


    【解决方案1】:

    您似乎没有更正数据的字节顺序,也没有更正伪标题。原始数据是大端的,但英特尔架构是小端的。因此,在直接添加之前,您需要交换单词的字节——您可以使用ntohs()

    看到这个example

    【讨论】:

    • @adnankamili 它仍然无法正常工作......但行为是否改变了?
    • 是的,行为改变了,也许我把事情搞砸了。任何人都可以发布更正的代码。
    • 您的代码似乎已更改,但您仍未交换 TCP 数据字或 IP 地址字。 所有字必须在进行加法之前进行字节交换。 (另外,htons(0xFFFF) 是空操作,因为两个字节是相同的。)
    • 有点混乱。在上面的代码中,所有的加法都是以大端方式完成的。我已经以小端方式通过了 tcp_len,但是在求和时,它正在使用 htons() 转换为大端。现在,我该怎么办。我应该在添加时将 *w(即 tcp 数据)以及伪标头转换为小端还是仅将伪标头转换为小端。此外,我是否应该在替换旧校验和时将生成的校验和转换回大端。
    • Endian-ness 是一个多字节字的概念。字符串是字节序列,因此没有什么可以交换的。但是,由于您将字符串解释为一系列 2 字节的单词,那么 yes,您必须在将这些单词中的每个单词进行字节交换之前将它们加在一起以形成校验和。
    猜你喜欢
    • 2014-05-08
    • 1970-01-01
    • 2016-03-09
    • 2015-01-04
    • 2021-03-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-30
    相关资源
    最近更新 更多