\nHere is the entire packet captured by Wireshark on the client side (divided into 4 images below). Note that the packets sent by hping3 are totally equal except the values for ip identification, source port, sequence number and checksum:
Images removed
\n\n
\nHere is the hex dump of the packet.
Hexdump removed
\n\nEDIT 2
Ok, now I have created the pseudo header according to RFC793. The pseudo header is just used for the tcp checksum calculation. Now the IP header seems to be correct, but Wireshark complains about that the packet does not contain a full TCP header and it really seems corrupted because some of the fields contains strange values that I have not set. \n
\nFirst I allocate a buffer (called tcp_header
) with space for the tcp header and the pseudo header. Second, I create a buffer for the ip header containing space for ip, tcp and pseudo headers.
\nFirst I fill the tcp_header
with its data and then I copy it to the ip_header
before sending it with the sendto
function.
\nDoes something go wrong when I copy the contents of tcp_packet
to ip_packet
or am I doing something else wrong?
#include <cstdlib>\n#include <stdio.h>\n#include <unistd.h>\n#include <net/if.h>\n#include <sys/socket.h>\n#include <netinet/ip.h>\n#define __FAVOR_BSD 1\n#include <netinet/tcp.h>\n#include <arpa/inet.h>\n#include <linux/if_packet.h>\n#include <linux/if_ether.h>\n#include <sys/ioctl.h>\n#include <string.h>\n#include <iostream>\n#include <net/ethernet.h>\n#include <time.h>\n\n#define PCKT_LEN 1024\n\nstruct pseudohdr\n{\n __u32 saddr;\n __u32 daddr;\n __u8 zero;\n __u8 protocol;\n __u16 lenght;\n};\n\n#define PSEUDOHDR_SIZE sizeof(struct pseudohdr)\n\nunsigned short csum(unsigned short *buf, int len) {\n unsigned long sum;\n for(sum=0; len>0; len-=2)\n sum += *buf++;\n sum = (sum >> 16) + (sum &0xffff);\n sum += (sum >> 16);\n return (unsigned short)(~sum);\n}\n\n\nint main(int argc, char** argv) {\n\n srand(time(NULL));\n\n char *ip_packet = new char[sizeof(struct iphdr) + \n sizeof(struct tcphdr)]();\n\n char *tcp_packet = new char[sizeof(struct pseudohdr) + \n sizeof(struct tcphdr)]();\n\n struct pseudohdr *pseudoheader = (struct pseudohdr*) tcp_packet;\n class tcphdr *tcp = (struct tcphdr *) (tcp_packet + sizeof(struct pseudohdr));\n class iphdr *ip = (struct iphdr *) ip_packet;\n\n\n class sockaddr_in sin, din;\n\n int sd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);\n if(sd < 0) {\n perror(\"socket() error\");\n exit(-1);\n } else {\n printf(\"socket()-SOCK_RAW and tcp protocol is OK.\\n\");\n }\n\n // Randomize src port\n int srcport = rand()%100+25000;\n\n sin.sin_family = AF_INET; // Address family\n sin.sin_addr.s_addr = inet_addr(\"192.168.2.80\");\n sin.sin_port = htons(srcport); // Source port\n\n din.sin_family = AF_INET;\n din.sin_addr.s_addr = inet_addr(\"192.168.2.6\");\n din.sin_port = htons(80); // Destination port\n\n /* tcp pseudo header */\n memcpy(&pseudoheader->saddr, &sin.sin_addr.s_addr, 4);\n memcpy(&pseudoheader->daddr, &din.sin_addr.s_addr, 4);\n pseudoheader->protocol = 6; /* tcp */\n pseudoheader->lenght = htons(sizeof(struct pseudohdr) + sizeof(struct tcphdr));\n\n\n ip->ihl = 5;\n ip->version = 4;\n ip->tos = 0;\n ip->tot_len = sizeof(class iphdr) + sizeof(class tcphdr);\n ip->id = htons((getpid() & 255) + rand()%100+30000);\n ip->frag_off = 0;\n ip->ttl = 32;\n ip->protocol = 6; // TCP\n ip->check = 0; // Done by kernel\n memcpy(&ip->saddr, (char*)&sin.sin_addr, sizeof(ip->saddr));\n memcpy(&ip->daddr, (char*)&din.sin_addr, sizeof(ip->daddr));\n\n\n // The TCP structure\n tcp->th_sport = htons(srcport);\n tcp->th_dport = htons(80); // Destination port\n tcp->th_seq = htonl(rand()%100+1000);\n tcp->th_ack = htonl(rand()%30);\n tcp->th_off = 5;\n tcp->th_flags = TH_SYN;\n tcp->th_win = htons(1024);\n tcp->th_urp = 0;\n\n // Now calculate tcp checksum\n tcp->th_sum = csum((unsigned short *) tcp_packet, sizeof(struct pseudohdr) + sizeof(struct tcphdr));\n\n // Copy tcp_packet to ip_packet\n memcpy(ip_packet + sizeof(struct iphdr), tcp_packet+sizeof(struct pseudohdr), sizeof(struct tcphdr));\n\n // Bind socket to interface\n int one = 1;\n const int *val = &one;\n const char opt[] = \"eth0\";\n\n if(setsockopt(sd, IPPROTO_IP, IP_HDRINCL, (char *)&one, sizeof(one)) < 0) {\n perror(\"setsockopt() error\");\n exit(-1);\n }\n else\n printf(\"setsockopt() is OK\\n\");\n\n if(sendto(sd, ip_packet, ip->tot_len, 0, (sockaddr*)&din, sizeof(din)) < 0) {\n perror(\"sendto() error\");\n exit(-1);\n }\n else\n printf(\"Send OK!\");\n\n close(sd);\n return 0;\n}\n
\n\nThe tcp contents of the packet:
Images removed
\n\nEdit 3
\nNow I have found something interesting. Study the cheksums on this picture: \n
The checksum is in network order and shall thus be read in reversed order, as 0x06c0
(and not is as it is stated above as 0xc006). That is equal to the decimal value of 1728
. Wireshark says the correct cheksum should be 0x12c0
which gives a decimal value of 4800
.
\n4800-1728=3072
. That is the difference between the actual checksum and the correct checksum calculated by Wireshark in all packets that is sent by my program.\n
So, if I simply add that value to the cheksum result:
tcp->th_sum = csum((unsigned short *) tcp_packet, sizeof(struct pseudohdr) + sizeof(struct tcphdr)) + 3072;\n
\n\n...then all packets get the correct checksum and receives a corresponding SYN-ACK
.
\nWhy the magic number 3072???
Reputation: 2917
I am experimenting with raw sockets and I have just written a small program that sends TCP packets with the syn
flag set. I can see the packets coming with Wireshark on the server side and they look good, but the server never responds with any syn-ack
packets.
I have compared the syn
packets that my program constructs (see code below) with the ones that hping3
sends (because the packets of hping3 always get a syn-ack
). The only that differs between my syn packets and hping3's syn packets are the ip identification
number, tcp source port
(which is randomized in hping3), tcp sequence number
(which is also randomized in hping3) and the ip checksum
field. All these four fields are based on some random numbers, that is why they differ. All other fields are equal! But my program does not get any syn-acks but hping3 does!
I am using Kali Linux for sending the packets (of course as root) and CentOS for the server.
Have I missed something essential or just missunderstood anything?
Removed code
EDIT
Here is the entire packet captured by Wireshark on the client side (divided into 4 images below). Note that the packets sent by hping3 are totally equal except the values for ip identification, source port, sequence number and checksum:
Images removed
Here is the hex dump of the packet.
Hexdump removed
EDIT 2
Ok, now I have created the pseudo header according to RFC793. The pseudo header is just used for the tcp checksum calculation. Now the IP header seems to be correct, but Wireshark complains about that the packet does not contain a full TCP header and it really seems corrupted because some of the fields contains strange values that I have not set.
First I allocate a buffer (called tcp_header
) with space for the tcp header and the pseudo header. Second, I create a buffer for the ip header containing space for ip, tcp and pseudo headers.
First I fill the tcp_header
with its data and then I copy it to the ip_header
before sending it with the sendto
function.
Does something go wrong when I copy the contents of tcp_packet
to ip_packet
or am I doing something else wrong?
#include <cstdlib>
#include <stdio.h>
#include <unistd.h>
#include <net/if.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#define __FAVOR_BSD 1
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <linux/if_packet.h>
#include <linux/if_ether.h>
#include <sys/ioctl.h>
#include <string.h>
#include <iostream>
#include <net/ethernet.h>
#include <time.h>
#define PCKT_LEN 1024
struct pseudohdr
{
__u32 saddr;
__u32 daddr;
__u8 zero;
__u8 protocol;
__u16 lenght;
};
#define PSEUDOHDR_SIZE sizeof(struct pseudohdr)
unsigned short csum(unsigned short *buf, int len) {
unsigned long sum;
for(sum=0; len>0; len-=2)
sum += *buf++;
sum = (sum >> 16) + (sum &0xffff);
sum += (sum >> 16);
return (unsigned short)(~sum);
}
int main(int argc, char** argv) {
srand(time(NULL));
char *ip_packet = new char[sizeof(struct iphdr) +
sizeof(struct tcphdr)]();
char *tcp_packet = new char[sizeof(struct pseudohdr) +
sizeof(struct tcphdr)]();
struct pseudohdr *pseudoheader = (struct pseudohdr*) tcp_packet;
class tcphdr *tcp = (struct tcphdr *) (tcp_packet + sizeof(struct pseudohdr));
class iphdr *ip = (struct iphdr *) ip_packet;
class sockaddr_in sin, din;
int sd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
if(sd < 0) {
perror("socket() error");
exit(-1);
} else {
printf("socket()-SOCK_RAW and tcp protocol is OK.\n");
}
// Randomize src port
int srcport = rand()%100+25000;
sin.sin_family = AF_INET; // Address family
sin.sin_addr.s_addr = inet_addr("192.168.2.80");
sin.sin_port = htons(srcport); // Source port
din.sin_family = AF_INET;
din.sin_addr.s_addr = inet_addr("192.168.2.6");
din.sin_port = htons(80); // Destination port
/* tcp pseudo header */
memcpy(&pseudoheader->saddr, &sin.sin_addr.s_addr, 4);
memcpy(&pseudoheader->daddr, &din.sin_addr.s_addr, 4);
pseudoheader->protocol = 6; /* tcp */
pseudoheader->lenght = htons(sizeof(struct pseudohdr) + sizeof(struct tcphdr));
ip->ihl = 5;
ip->version = 4;
ip->tos = 0;
ip->tot_len = sizeof(class iphdr) + sizeof(class tcphdr);
ip->id = htons((getpid() & 255) + rand()%100+30000);
ip->frag_off = 0;
ip->ttl = 32;
ip->protocol = 6; // TCP
ip->check = 0; // Done by kernel
memcpy(&ip->saddr, (char*)&sin.sin_addr, sizeof(ip->saddr));
memcpy(&ip->daddr, (char*)&din.sin_addr, sizeof(ip->daddr));
// The TCP structure
tcp->th_sport = htons(srcport);
tcp->th_dport = htons(80); // Destination port
tcp->th_seq = htonl(rand()%100+1000);
tcp->th_ack = htonl(rand()%30);
tcp->th_off = 5;
tcp->th_flags = TH_SYN;
tcp->th_win = htons(1024);
tcp->th_urp = 0;
// Now calculate tcp checksum
tcp->th_sum = csum((unsigned short *) tcp_packet, sizeof(struct pseudohdr) + sizeof(struct tcphdr));
// Copy tcp_packet to ip_packet
memcpy(ip_packet + sizeof(struct iphdr), tcp_packet+sizeof(struct pseudohdr), sizeof(struct tcphdr));
// Bind socket to interface
int one = 1;
const int *val = &one;
const char opt[] = "eth0";
if(setsockopt(sd, IPPROTO_IP, IP_HDRINCL, (char *)&one, sizeof(one)) < 0) {
perror("setsockopt() error");
exit(-1);
}
else
printf("setsockopt() is OK\n");
if(sendto(sd, ip_packet, ip->tot_len, 0, (sockaddr*)&din, sizeof(din)) < 0) {
perror("sendto() error");
exit(-1);
}
else
printf("Send OK!");
close(sd);
return 0;
}
The tcp contents of the packet:
Images removed
Edit 3
Now I have found something interesting. Study the cheksums on this picture:
The checksum is in network order and shall thus be read in reversed order, as 0x06c0
(and not is as it is stated above as 0xc006). That is equal to the decimal value of 1728
. Wireshark says the correct cheksum should be 0x12c0
which gives a decimal value of 4800
.
4800-1728=3072
. That is the difference between the actual checksum and the correct checksum calculated by Wireshark in all packets that is sent by my program.
So, if I simply add that value to the cheksum result:
tcp->th_sum = csum((unsigned short *) tcp_packet, sizeof(struct pseudohdr) + sizeof(struct tcphdr)) + 3072;
...then all packets get the correct checksum and receives a corresponding SYN-ACK
.
Why the magic number 3072???
Upvotes: 7
Views: 2231
Reputation: 2234
I am not content with the check sum algorithm you are using. The one suggested by Stevens:
uint16_t
in_cksum(uint16_t *addr, int len)
{
int nleft = len;
uint32_t sum = 0;
uint16_t *w = addr;
uint16_t answer = 0;
/*
* Our 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.
*/
while (nleft > 1) {
sum += *w++;
nleft -= 2;
}
/* 4mop up an odd byte, if necessary */
if (nleft == 1) {
*(unsigned char *)(&answer) = *(unsigned char *)w ;
sum += answer;
}
/* 4add back carry outs from top 16 bits to low 16 bits */
sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
sum += (sum >> 16); /* add carry */
answer = ~sum; /* truncate to 16 bits */
return(answer);
}
Upvotes: 1