Re: Bad TCP checksum error

From: Gaurav Aggarwal
Date: Fri Oct 26 2007 - 12:03:16 EST


Hi All,

I have dome some changes in the code. Now instead of calculating the
TCP checksum from scratch, I was just recalculating it by incremental
update as mentioned in RFC 1624. Good thing is that now I was able to
get the correct IP header checksum but still TCP checksum value is
corrupted. Interesting thing is, this time the difference in checksum
is exactly the same as that of difference in original and modified
packet header. What I mean to say is that I was changing the
destination IP from "10.102.35.22" to "10.102.35.24" and the
difference in tcp checksum (expected and computed) is coming as 0x02.
Attached is the modified source code and tcpdump. Any help will be
really appreciated.

On 10/26/07, Gaurav Aggarwal <grv.aggarwal@xxxxxxxxx> wrote:
> Hi,
>
> I wrote a program where I am using the nfnetlink and netfilter_queue
> model to capture the packet. After that I just change the destination
> address of the packet and insert it back into the ip stack. But after
> inserting the packet I am getting a bad TCP checksum error. Even I am
> getting the same error for IP header checksum. Attached is the source
> code and tcpdump on host machine.
>
> /* Source Code */
> /* Compile with gcc -lnfnetlink -lnetfilter_queue */
> #include <stdio.h>
> #include <stdlib.h>
> #include <string.h>
> #include <unistd.h>
> #include <netinet/in.h>
> #include <netinet/ip.h>
> #include <netinet/tcp.h>
> #include <linux/netfilter.h> /* for NF_ACCEPT */
> #include <arpa/inet.h>
>
> #include <libnetfilter_queue/libnetfilter_queue.h>
>
> #define BUFSIZE 2048
>
> struct in_addr foreign;
> struct in_addr local;
>
> struct queued_pckt {
> char *payload;
> int payload_len;
> };
>
> struct pseudohdr
> {
> unsigned long ip_src ;
> unsigned long ip_dst ;
> unsigned char reserve ;
> unsigned char type ;
> unsigned short length;
> } ;
>
> unsigned short checksum(unsigned short *addr, unsigned int count) {
> /* Compute Internet Checksum for "count" bytes beginning at location "addr".
> * Algorithm is simple, using a 32-bit accumulator (sum),
> * we add sequential 16-bit words to it, and at the end, fold back
> * all the carry bits from the top 16 bits into the lower 16 bits.
> */
> register long sum = 0;
> unsigned short result;
>
> while (count > 1) {
> /* This is the inner loop */
> sum += * addr++;
> count -= 2;
> }
> /* Add left-over byte, if any */
> if (count == 1)
> {
> result = 0; //make sure top half is zero
> * (unsigned char *) (&result) = *(unsigned char *)addr;
> sum += result;
> }
>
> /*
> * Add back carry outs from top 16 bits to low 16 bits.
> * Fold 32-bit sum to 16 bits
> */
>
> sum = (sum >> 16) + (sum & 0xffff); /* add high-16 to low-16 */
> sum += (sum >> 16); /* add carry */
>
> result = ~sum; /* ones-complement, then truncate to 16 bits */
> return (result);
> }
>
>
> unsigned short get_tcp_chksum (struct tcphdr *orig_tcphdr, struct
> iphdr *orig_iphdr )
> {
> struct pseudohdr pseudoh ;
>
> unsigned int total_len = ntohs(orig_iphdr->tot_len);
> int tcpopt_len = (orig_tcphdr->doff * 4) - 20;
> int tcpdata_len = total_len - (orig_tcphdr->doff * 4) - (orig_iphdr->ihl * 4);
>
> pseudoh.ip_src = orig_iphdr->saddr ;
> pseudoh.ip_dst = orig_iphdr->daddr ;
> pseudoh.reserve = 0 ;
> pseudoh.type = orig_iphdr->protocol ;
> pseudoh.length = htons (sizeof (struct tcphdr) + tcpopt_len + tcpdata_len) ;
>
> int totaltcp_len = sizeof(struct pseudohdr) + sizeof(struct tcphdr) +
> tcpopt_len + tcpdata_len;
>
> unsigned short *tcp = (unsigned short *)malloc (totaltcp_len);
>
> memcpy ((unsigned char *)tcp, &pseudoh, sizeof(struct pseudohdr));
> memcpy ((unsigned char *)tcp + sizeof(struct pseudohdr), (unsigned
> char *)orig_tcphdr, sizeof(struct tcphdr));
> if (tcpopt_len > 0)
> memcpy ((unsigned char *)tcp + sizeof(struct pseudohdr) +
> sizeof(struct tcphdr), (unsigned char *)orig_iphdr + (orig_iphdr->ihl
> * 4) + sizeof(struct tcphdr), tcpopt_len);
>
> if (tcpdata_len > 0)
> memcpy ((unsigned char *)tcp + sizeof(struct pseudohdr) +
> sizeof(struct tcphdr) + tcpopt_len, (unsigned char *)orig_tcphdr +
> (orig_tcphdr->doff * 4), tcpdata_len);
>
> #if 0
> printf("pseudo length: %d\n",pseudoh.length);
> printf("tcp hdr length: %d\n",orig_tcphdr->doff*4);
> printf("tcp hdr struct length: %d\n",sizeof(struct tcphdr));
> printf("tcphdr->doff = %d, tcp opt length:
> %d\n",orig_tcphdr->doff,tcpopt_len);
> printf("tcp total+psuedo length: %d\n",totaltcp_len);
>
> fflush(stdout);
>
> printf("tcp data len: %d, data start %u\n",
> tcpdata_len,orig_tcphdr + (orig_tcphdr->doff*4));
> #endif
>
> return (checksum (tcp, totaltcp_len)) ;
> }
>
> static void filter(
> unsigned char *packet, unsigned int payload_len)
> {
> struct iphdr *iphdr;
> struct tcphdr *tcphdr;
>
> printf ("in filter function\n");
>
> iphdr = (struct iphdr *)packet;
> /* check need some datas */
> if (payload_len < sizeof(struct iphdr) + sizeof(struct tcphdr)) {
> return;
> }
> /* check IP version */
> if (iphdr->protocol == IPPROTO_TCP)
> {
> tcphdr = (struct tcphdr *)(((u_int32_t *)packet) + 4 * iphdr->ihl);
>
> if (iphdr->daddr == foreign.s_addr)
> {
> printf ("packet DEST addr = %s\n",inet_ntoa(foreign));
> fprintf (stderr, "changing pkt's DEST addr from FOREIGN to LOCAL\n");
> iphdr->daddr = local.s_addr;
> tcphdr->check = 0 ; // checksum will be calculated later
> iphdr->check = checksum(
> (unsigned short *)iphdr,
> sizeof(struct iphdr));
> tcphdr->check = get_tcp_chksum(
> tcphdr,
> iphdr);
> }
> }
>
> }
>
> static int cb(struct nfq_q_handle *qh, struct nfgenmsg *nfmsg,
> struct nfq_data *nfa, void *data)
> {
> int id = 0;
> struct nfqnl_msg_packet_hdr *ph;
> struct queued_pckt q_pckt;
> u_int32_t mark,ifi;
> int ret;
> char *payload;
>
> printf("entering callback\n");
> ph = nfq_get_msg_packet_hdr(nfa);
> if (ph){
> id = ntohl(ph->packet_id);
> printf("hw_protocol=0x%04x hook=%u id=%u ",
> ntohs(ph->hw_protocol), ph->hook, id);
> }
>
> mark = nfq_get_nfmark(nfa);
> if (mark)
> printf("mark=%u ", mark);
>
> ifi = nfq_get_indev(nfa);
> if (ifi)
> printf("indev=%u ", ifi);
>
> ifi = nfq_get_outdev(nfa);
> if (ifi)
> printf("outdev=%u ", ifi);
>
> q_pckt.payload_len = nfq_get_payload(nfa, &(q_pckt.payload));
> if (q_pckt.payload_len >= 0)
> {
> printf("payload_len=%d ", q_pckt.payload_len);
> fputc('\n', stdout);
> filter((unsigned char *)q_pckt.payload, q_pckt.payload_len);
> }
>
> printf("setting verdict of packet id %d\n",id);
> return nfq_set_verdict(qh, id, NF_ACCEPT, q_pckt.payload_len, q_pckt.payload);
> }
>
> int main(int argc, char **argv)
> {
> struct nfq_handle *h;
> struct nfq_q_handle *qh;
> struct nfnl_handle *nh;
> int fd;
> int rv;
> unsigned char buf[BUFSIZE];
>
> if (argc == 1)
> {
> inet_aton("10.102.130.222", &(foreign));
> inet_aton("10.102.130.105", &(local));
> } else if (argc == 3)
> {
> inet_aton(argv[1], &(foreign));
> inet_aton(argv[2], &(local));
> }
> else
> {
> printf("Usage: argv[0] [foreign_addr local_addr]\n");
> return 0;
> }
>
> printf("opening library handle\n");
> h = nfq_open();
> if (!h) {
> fprintf(stderr, "error during nfq_open()\n");
> return 0;
> }
>
> printf("unbinding existing nf_queue handler for AF_INET (if any)\n");
> if (nfq_unbind_pf(h, AF_INET) < 0) {
> fprintf(stderr, "error during nfq_unbind_pf()\n");
> exit(1);
> }
>
> printf("binding nfnetlink_queue as nf_queue handler for AF_INET\n");
> if (nfq_bind_pf(h, AF_INET) < 0) {
> fprintf(stderr, "error during nfq_bind_pf()\n");
> exit(1);
> }
>
> printf("binding this socket to queue '0'\n");
> qh = nfq_create_queue(h, 0, &cb, NULL);
> if (!qh) {
> fprintf(stderr, "error during nfq_create_queue()\n");
> exit(1);
> }
>
> printf("setting copy_packet mode\n");
> if (nfq_set_mode(qh, NFQNL_COPY_PACKET, BUFSIZE) < 0) {
> fprintf(stderr, "can't set packet_copy mode\n");
> exit(1);
> }
>
> nh = nfq_nfnlh(h);
> fd = nfnl_fd(nh);
>
> while ((rv = recv(fd, buf, BUFSIZE, 0)) && rv >= 0) {
> printf("pkt received\n");
> nfq_handle_packet(h, buf, rv);
> printf("pkt handled\n");
> }
>
> printf("unbinding from queue 0\n");
> nfq_destroy_queue(qh);
>
> printf("closing library handle\n");
> nfq_close(h);
>
> exit(0);
> }
> /* end - Source Code */
>
> /* TCP dump */
> tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 96 bytes
> 12:59:36.706161 IP (tos 0x0, ttl 64, id 61974, offset 0, flags [DF],
> proto: TCP (6), length: 60, bad cksum 75 (->8e24)!) 10.102.35.76.36898
> > 10.102.130.105.colubris: S, cksum 0xc6d5 (incorrect (-> 0xc74a),
> 366446207:366446207(0) win 5840 <mss 1460,sackOK,timestamp 14775401
> 0,nop,wscale 6>
> 0x0000: 0012 010a 5f4c 000b cd3a 5bfb 0800 4500
> 0x0010: 003c f216 4000 4006 0075 0a66 234c 0a66
> 0x0020: 8269 9022 0da2 15d7 867f 0000 0000 a002
> 0x0030: 16d0 c6d5 0000 0204 05b4 0402 080a 00e1
> 0x0040: 7469 0000 0000 0103 0306
> 12:59:39.708619 IP (tos 0x0, ttl 64, id 61975, offset 0, flags [DF],
> proto: TCP (6), length: 60, bad cksum 75 (->8e23)!) 10.102.35.76.36898
> > 10.102.130.105.colubris: S, cksum 0xc3e7 (incorrect (-> 0xc45c),
> 366446207:366446207(0) win 5840 <mss 1460,sackOK,timestamp 14776151
> 0,nop,wscale 6>
> 0x0000: 0012 010a 5f4c 000b cd3a 5bfb 0800 4500
> 0x0010: 003c f217 4000 4006 0075 0a66 234c 0a66
> 0x0020: 8269 9022 0da2 15d7 867f 0000 0000 a002
> 0x0030: 16d0 c3e7 0000 0204 05b4 0402 080a 00e1
> 0x0040: 7757 0000 0000 0103 0306
>
> 2 packets captured
> 4 packets received by filter
> 0 packets dropped by kernel
> /* End - TCP dump */
>
> Attached is the dump for ethreal also.
>
> --
> Regards,
> Gaurav Aggarwal
>
>


--
Regards,
Gaurav Aggarwal

Attachment: nfq_test_modified.c
Description: Binary data

tcpdump: listening on eth0
21:00:01.490920 10.102.35.76.38067 > 10.102.35.24.3490: S [bad tcp cksum fdff!] 1064689745:1064689745(0) win 5840 <mss 1460,sackOK,timestamp 4491010 0,nop,wscale 7> (DF) (ttl 64, id 26451, len 60)
4500 003c 6753 4000 4006 7839 0a66 234c
0a66 2318 94b3 0da2 3f75 e051 0000 0000
a002 16d0 8c9f 0000 0204 05b4 0402 080a
0044 8702 0000 0000 0103 0307
21:00:04.489859 10.102.35.76.38067 > 10.102.35.24.3490: S [bad tcp cksum fdff!] 1064689745:1064689745(0) win 5840 <mss 1460,sackOK,timestamp 4491760 0,nop,wscale 7> (DF) (ttl 64, id 26452, len 60)
4500 003c 6754 4000 4006 7838 0a66 234c
0a66 2318 94b3 0da2 3f75 e051 0000 0000
a002 16d0 89b1 0000 0204 05b4 0402 080a
0044 89f0 0000 0000 0103 0307
21:00:06.489554 arp who-has 10.102.35.24 tell 10.102.35.76
0001 0800 0604 0001 000b cd3a 5bfb 0a66
234c 0000 0000 0000 0a66 2318 0000 0000
0000 0000 0000 0000 0000 0000 0000
21:00:06.489596 arp reply 10.102.35.24 is-at 0:80:c8:1:56:13
0001 0800 0604 0002 0080 c801 5613 0a66
2318 000b cd3a 5bfb 0a66 234c

4 packets received by filter
0 packets dropped by kernel

Attachment: ethreal_dump.pcap
Description: Binary data