Bad TCP checksum error

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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

Attachment: nfq_test.c
Description: Binary data

Attachment: dump.pcap
Description: Binary data


[Index of Archives]     [Kernel Newbies]     [Netfilter]     [Bugtraq]     [Photo]     [Stuff]     [Gimp]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Video 4 Linux]     [Linux for the blind]     [Linux Resources]
  Powered by Linux