* Bad TCP checksum error
@ 2007-10-26 7:41 Gaurav Aggarwal
2007-10-26 16:03 ` Gaurav Aggarwal
0 siblings, 1 reply; 3+ messages in thread
From: Gaurav Aggarwal @ 2007-10-26 7:41 UTC (permalink / raw)
To: linux-net-owner
Cc: netfilter-devel, netdev, linux-net, linux-kernel, davidsen,
Gaurav Aggarwal
[-- Attachment #1: Type: text/plain, Size: 8621 bytes --]
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 #2: nfq_test.c --]
[-- Type: application/octet-stream, Size: 6887 bytes --]
/* 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);
}
[-- Attachment #3: dump.pcap --]
[-- Type: application/octet-stream, Size: 204 bytes --]
^ permalink raw reply [flat|nested] 3+ messages in thread* Re: Bad TCP checksum error
2007-10-26 7:41 Bad TCP checksum error Gaurav Aggarwal
@ 2007-10-26 16:03 ` Gaurav Aggarwal
2007-10-26 16:59 ` Rick Jones
0 siblings, 1 reply; 3+ messages in thread
From: Gaurav Aggarwal @ 2007-10-26 16:03 UTC (permalink / raw)
To: linux-net-owner
Cc: netfilter-devel, netdev, linux-net, linux-kernel, davidsen,
Gaurav Aggarwal
[-- Attachment #1: Type: text/plain, Size: 11607 bytes --]
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@gmail.com> 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 #2: nfq_test_modified.c --]
[-- Type: application/octet-stream, Size: 4157 bytes --]
/* Compile with gcc -lnfnetlink -lnetfilter_queue */
#include <stdio.h>
#include <stdlib.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;
};
inline u_int16_t checksum_update_32(
u_int16_t old_check,
u_int32_t old,
u_int32_t new)
{
u_int32_t l;
old_check = ~old_check;
old = ~old;
l = (u_int32_t)old_check + ((old >> 16) + (old & 0xffff)) + ((new >> 16) + (new & 0xffff));
return ~((u_int16_t)((l >> 16) + (l & 0xffff)));
}
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));
printf ("local addr = %s\n",inet_ntoa(local));
fprintf (stderr, "changing pkt's DEST addr from FOREIGN to LOCAL\n");
iphdr->check = checksum_update_32(
iphdr->check,
iphdr->daddr,
local.s_addr);
tcphdr->check = checksum_update_32(
tcphdr->check,
iphdr->daddr,
local.s_addr);
iphdr->daddr = local.s_addr;
}
}
}
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];
inet_aton("10.102.35.22", &(foreign));
inet_aton("10.102.35.24", &(local));
printf("opening library handle\n");
h = nfq_open();
if (!h) {
fprintf(stderr, "error during nfq_open()\n");
exit(1);
}
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);
}
[-- Attachment #3: tcpdump.txt --]
[-- Type: text/plain, Size: 1136 bytes --]
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 #4: ethreal_dump.pcap --]
[-- Type: application/octet-stream, Size: 338 bytes --]
^ permalink raw reply [flat|nested] 3+ messages in thread* Re: Bad TCP checksum error
2007-10-26 16:03 ` Gaurav Aggarwal
@ 2007-10-26 16:59 ` Rick Jones
0 siblings, 0 replies; 3+ messages in thread
From: Rick Jones @ 2007-10-26 16:59 UTC (permalink / raw)
To: Gaurav Aggarwal
Cc: netfilter-devel, netdev, linux-net, linux-kernel, davidsen
Checksum Offload on the NIC(s) can complicate things. First, if you are tracing
on the sender, the tracepoint is before the NIC has computed the full checksum.
IIRC only a partial checksum is passed-down to the NIC when CKO is in use.
So, making certain your trace is from the "wire" or the receiver rather than the
sender would be a good thing, and trying again with CKO disabled on the
interface(s) (via ethtool) might be something worth looking at. Ultimately,
doing the partial checksum modificiations in a CKO-friendly manner might be a
good thing.
rick jones
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2007-10-26 16:59 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-10-26 7:41 Bad TCP checksum error Gaurav Aggarwal
2007-10-26 16:03 ` Gaurav Aggarwal
2007-10-26 16:59 ` Rick Jones
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).