Libnet TCP校验和错误

时间:2011-10-24 04:24:18

标签: c libpcap tcpdump libnet

我正在尝试使用libnet库构建一个TCP数据包。我使用'0'来自动计算libnet_build_tcp函数中的校验和值。但是,由于校验和错误,校验和似乎在计算时忽略pseudo-header,导致无用的数据包。有谁知道如何解决这个问题?

1 个答案:

答案 0 :(得分:1)

就我在代码中看到的情况而言,只要您未使用libnet_toggle_checksum(l, ptag, 1);0 sum参数libnet_build_tcp()就应该libnet_pblock_setflags(p, LIBNET_PBLOCK_DO_CHECKSUM)通过调用int bytesRead; int pseudoHeaderLength; int payloadLength; int tcpHdrLength; int tcpSegmentLength; int ipHdrLength; int headerLengths; int ethIPHdrLengths; struct ethhdr *ethHdr; struct iphdr *ipHdr; struct tcphdr *tcpHdr; u_int8_t *pkt_data; u_int8_t pseudoHeader[12]; u_int8_t packetBuffer[1600]; u_int16_t newChecksum; 使其为您自动计算校验和。

我无法告诉你如何解决它,但既然你是构建数据包的人,你可以选择创建自己的校验和吗?

TCP伪头是一个12字节的字段,包含源和目标IP地址,每个都是4个字节,保留字段是2个字节,总是设置为零,4字节TCP段长度等于TCP标头的大小+有效负载长度。

你可以创建这样的东西:

首先创建变量:

ethHdr = (struct ethhdr*)pkt_data;
ipHdr = (struct iphdr*)(pkt_data + ETH_HLEN);

ipHdrLength = ipHdr->ihl * 4;

ethIPHdrLengths = ETH_HLEN + ipHdrLength;

tcpHdr = (struct tcphdr*)(pkt_data + ethIPHdrLengths);

tcpHdrLength = tcpHdr->doff * 4;

headerLengths = ethIPHdrLengths + tcpHdrLength;

payloadLength = b->len - headerLengths;

tcpSegmentLength = tcpHdrLength + payloadLength;

pseudoHeaderLength = tcpSegmentLength + 12;

pseudoHeader[0] = pkt_data[30];
pseudoHeader[1] = pkt_data[31];
pseudoHeader[2] = pkt_data[32];
pseudoHeader[3] = pkt_data[33];
pseudoHeader[4] = pkt_data[26];
pseudoHeader[5] = pkt_data[27];
pseudoHeader[6] = pkt_data[28];
pseudoHeader[7] = pkt_data[29];
pseudoHeader[8] = 0x00;
pseudoHeader[9] = 0x06;
pseudoHeader[10] = (tcpSegmentLength >> 8) & 0xFF;
pseudoHeader[11] = tcpSegmentLength & 0xFF;

bytesRead = 0;

for(i = 0; i < 12; i++) {
    packetBuffer[bytesRead] = pseudoHeader[i];
    bytesRead++;
}

for(i = ethIPHdrLengths; i < headerLengths; i++) {
    packetBuffer[bytesRead] = pkt_data[i];
    bytesRead++;
}

for(i = b->len - payloadLength; i < b->len; i++) {
    packetBuffer[bytesRead] = pkt_data[i];
    bytesRead++;
}

newChecksum = checksum((uint16_t *)packetBuffer, pseudoHeaderLength);

然后创建实际函数,其中b-&gt; len是数据包的总大小(只需添加所有标题+有效负载并获得总大小),您只需将标题和数据memcpy到pkt_data:

uint16_t checksum(uint16_t *addr, int len)
{

int count = len;
register uint32_t sum = 0;
uint16_t answer = 0;

while (count > 1) {
    sum += *(addr++);
    count -= 2;
}

if (count > 0) {
    sum += *(uint8_t *) addr;
}

while (sum >> 16) {
    sum = (sum & 0xffff) + (sum >> 16);
}

answer = ~sum;

return (answer);
}

只需使用https://tools.ietf.org/html/rfc1071提供的校验和函数来计算缓冲区的校验和:

SELECT RIGHT(MarketIdentifier, LEN(MarketIdentifier) - 5) AS MyTrimmedColumn  AS "MarketID" from Markets

我在现实环境中使用它来计算500万PPS的校验和。