This article will provide a really usable TCP/IP protocol checksum calculation, that is, it is not the theoretical TCPIP checksum calculation method, although I had a little headache about this pit (joke)
This code abstract is purposely named as a small project of "tun/tap" virtual network card layer (link layer) from one of my experimental projects (astar-tun). It implements a super simple tcpip protocol stack (proto net stack) dedicated to "forwarding", complete protocol stack, such as lwIP, uc/IP, TinyTCP, BSD/linux, win32.
Following is the definition of tcpip protocol header. Tcpip header is 20 bytes in general. People always say that TCPIP has at least 20 bytes, otherwise it is not TCPIP protocol. I correct this sentence here, tcpip protocol header is longer, it can be 0 bytes (although it is impossible) depending on the value of the "th_off" field in the protocol header ("th_off * 4 = nTcpH). EaderCount")
#ifndef tcp_seq
#define tcp_seq tcp_seq
typedef volatile uint32_t tcp_seq;
#endif/*
* TCP header.
* Per RFC 793, September, 1981.
*/
typedef struct _tcphdr
{
uint16_t th_sport; /* source port */
uint16_t th_dport; /* destination port */
tcp_seq th_seq; /* sequence number */
tcp_seq th_ack; /* acknowledgement number */
uint8_t th_x2 : 4; /* (unused) */
uint8_t th_off : 4; /* data offset */
uint8_t th_flags;uint16_t th_win; /* window */
uint16_t th_sum; /* checksum */
uint16_t th_urp; /* urgent pointer */
} tcphdr;
The above code is the "tcp ip" standard protocol header defined by "RFC 793", which is 20 bytes ("th_off"= 0x5). So back to the focus of this paper, is the TCP IP protocol checked together with payload (in fact, tcp_segments), or just need to check the IP protocol header as the IP protocol? In order to ensure the correctness and consistency of payload data transmitted in "Internet", the designer of tcpip protocol may make mistakes in the transmission of electro-atomic signals in "cable or optical cable" (the error rate of optical cable will be much lower), or some problems in "switch and router" in the middle of the way, which cause the payload data carried by TCP to fail, and this leads to the failure of payload data carried by tcp. All these problems happen almost all the time.
int tcp_checksum(uint8_t* tcphdr, int tcplen, uint32_t* srcaddr, uint32_t* dstaddr)
{
uint8_t pseudoheader[12];
uint16_t checksum = 0;if (tcphdr != NULL && srcaddr && dstaddr)
{
memcpy(&pseudoheader[0], srcaddr, 4);
memcpy(&pseudoheader[4], dstaddr, 4);
pseudoheader[8] = 0; /* fill zeors */
pseudoheader[9] = IPPROTO_TCP;
memcpy(&pseudoheader[10], &tcplen, 2);uint8_t n = pseudoheader[10];
pseudoheader[10] = pseudoheader[11];
pseudoheader[11] = n;
checksum = ~tcpip_chksum(tcpip_chksum(0, pseudoheader, sizeof(pseudoheader)), tcphdr, tcplen);
}
return checksum;
}
tcplen = tcphdr + payload
In addition to calculating the value of the head and load, tcp_checksum also needs to add a pseudo-header of "tcp_psehdr" which takes up 12 bytes (there is nothing in this header, that is, it identifies the fields of srcaddr, dstaddr, prop, zeros (rsv) and tcplen.
We first calculate the checksum of the pseudoheader, then connect it with the value of "tcphdr + payload" and finally "bit inversion (~)" to get the correct true checksum of the TCPIP package.
int process_tcppacket_datapacket_ack(tcppcb* pcb, uint32_t cseq, uint32_t sseq)
{
if (pcb == NULL)
{
return 0;
}
uint32_t dstaddr = pcb->dstaddr;
uint32_t srcaddr = pcb->srcaddr;
uint16_t dstport = pcb->dstport;
uint16_t srcport = pcb->srcport;tcphdr tcpo;
tcpo.th_ack = __htonl(cseq);
tcpo.th_seq = __htonl(sseq);
tcpo.th_urp = 0;
tcpo.th_sum = 0;
tcpo.th_x2 = 0;
tcpo.th_flags = 0x10; // SETACK(tcpo.th_flags);
tcpo.th_off = 5;
tcpo.th_win = __htons(365);
tcpo.th_dport = __htons(srcport);
tcpo.th_sport = __htons(dstport);int packetlen = 20; // tcpo.th_off * 4
tcpo.th_sum = __htons(tcp_checksum((uint8_t*)&tcpo, packetlen, &dstaddr, &srcaddr));uint8_t* packet = ipv4_build_packet(dstaddr, srcaddr, 0x00, IPPROTO_TCP, (uint8_t*)&tcpo, &packetlen);
if (packet != NULL)
{
tun_write(tuntap, packet, packetlen);
__free(packet);
}
return 0;
}
The above code is a piece of code abstracted from "astar-tun". Its function is mainly used to return ACK tcpip-package to "tun/tap" network card. The above code directly shows how you should call the "tcp_checksum" function correctly. The following lists the "tcpip_chksum" function on which the "tcp_checksum" function depends.
unsigned short tcpip_chksum(unsigned short initcksum, unsigned char* data, int datalen)
{
unsigned int checksum = initcksum;
bool odd = (datalen & 1) != 0 ? 1 : 0;
int index = 0;
if (odd)
{
datalen -= odd;
}
for (index = 0; index < datalen; index += 2)
{
checksum += ((unsigned long)data[index] << 8) + ((unsigned long)data[index + 1]);
}
if (odd)
{
checksum += ((unsigned long)data[index] << 8);
}
while (checksum >> 16)
{
checksum = (checksum & 0xFFFF) + (checksum >> 16);
}
return checksum;
}