How to calculate TCP checksum

I am writing a kernel module that uses Netfilter bindings to change some of the TCP header information, and obviously, before sending, I want to recalculate the checksum.
I also edit the header on the receiving side, so I need to calculate it again there.

Searching the Internet, I found that some people say that I can just set it to 0, and it will be calculated for me, apparently this did not work.
I also found this feature

tcp_v4_send_check(struct sock *sk, struct sk_buff *skb); 

Although no one has explained how this is used, and whether I can actually use it when receiving / sending in the same way.
My own attempt was to set the checksum to 0, then call this function, passing skb, which I have, and skb-> sk, I have, nothing else.

So please, what is a simple way to calculate the checksum of TCP datagrams?

+7
source share
3 answers

To recalculate the checksum, you can calculate the incremental checksum - just change the existing checksum based on the fields you changed, instead of reading the entire package.

This must be done when you change the package, when you know both the old values ​​and the new values ​​that you store.

The main idea is tcp->check += (new_val - old_val) .
It is a little more complicated than that because:
1. old_val and new_val should be 16-bit values ​​that are aligned by 2 bytes (for example, changing the port number).
2. The checksum uses one addition to arithmetic, so you need to do a “feedback transfer”. This basically means that if tcp->check + new_val - old_val negative, you need to subtract 1 from the result.

+3
source

Here is an example that combines the netfilter API + checksum for TCP (not IP):

http://www.linuxvirtualserver.org/software/tcpsp/index.html

Check out the tcpsp_core.c file.

  th->check = 0; th->check = csum_tcpudp_magic(iph->saddr, iph->daddr, datalen, iph->protocol, csum_partial((char *)th, datalen, 0)); skb->ip_summed = CHECKSUM_UNNECESSARY; 

(note that zero is first assigned, the checksum is calculated, then the IP checksum is indicated as not needed).

Depending on which netfilter u module is being loaded (there are a lot of them !!!), they will work at different levels, for example, iptable working at the IP level is shown below (image):

http://ars.sciencedirect.com/content/image/1-s2.0-S1389128608004040-gr3.jpg

+1
source

@Ugoren's answer is not accurate. According to RFC1624 https://tools.ietf.org/html/rfc1624 , this sometimes causes -0 (0xFFFF), which is unacceptable.

The correct way to calculate the checksum should be: new_tcp_check = ~(~old_tcp_check + ~old_val + new_val)

+1
source

All Articles