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