1
votes

To be precise, I have a simple program which crafts a TCP syn packet and send it to a remote host.

Code TCP.c:

//Data part
data = datagram + sizeof(struct iphdr) + sizeof(struct tcphdr);
strcpy(data, datagram);
//some address resolution
strcpy(source_ip , sourceip);
sin.sin_family = AF_INET;
sin.sin_port = htons(80);
sin.sin_addr.s_addr = inet_addr (destinationip);

//Fill in the IP Header
iph->ihl = 5;
iph->version = 4;
iph->tos = 0;
iph->tot_len = sizeof (struct iphdr) + sizeof (struct tcphdr) + strlen(data);
iph->id = htonl (54321); //Id of this packet
iph->frag_off = 0;
iph->ttl = 255;
iph->protocol = IPPROTO_TCP;
iph->check = 0;      //Set to 0 before calculating checksum
iph->saddr = inet_addr ( source_ip );    //Spoof the source ip address
iph->daddr = sin.sin_addr.s_addr;

//Ip checksum
iph->check = csum ((unsigned short *) datagram, iph->tot_len);

//TCP Header
tcph->source  = htons (sourceport);
tcph->dest    = htons (destinationport);
tcph->seq     = htonl (seqn);
tcph->ack_seq = htonl (ackn);
tcph->doff    = 5;  //tcp header size
tcph->fin     = fin;
tcph->syn     = syn;
tcph->rst     = rst;
tcph->psh     = push;
tcph->ack     = ack;
tcph->urg     = urg;
tcph->window  = htons (win); /* maximum allowed window size */
tcph->check   = 0; //leave checksum 0 now, filled later by pseudo header
tcph->urg_ptr = 0;

//Now the TCP checksum
psh.source_address = inet_addr( source_ip );
psh.dest_address = sin.sin_addr.s_addr;
psh.placeholder = 0;
psh.protocol = IPPROTO_TCP;
psh.tcp_length = htons(sizeof(struct tcphdr) + strlen(data) );

int psize = sizeof(struct pseudo_header) + sizeof(struct tcphdr) + strlen(data);
pseudogram = malloc(psize);

memcpy(pseudogram , (char*) &psh , sizeof (struct pseudo_header));
memcpy(pseudogram + sizeof(struct pseudo_header) , tcph , sizeof(struct tcphdr) + strlen(data));

tcph->check = csum( (unsigned short*) pseudogram , psize);

//IP_HDRINCL to tell the kernel that headers are included in the packet
int one = 1;
const int *val = &one;

if (setsockopt (s, IPPROTO_IP, IP_HDRINCL, val, sizeof (one)) < 0)
{
    perror("Error setting IP_HDRINCL");
    exit(0);
}
while (1)
{
    //Send the packet
    if (sendto (s, datagram, iph->tot_len ,  0, (struct sockaddr *) &sin, sizeof (sin)) < 0)
    {
        perror("sendto failed");
    }
    //Data send successfully
    else
    {
        printf ("  Packet Send. Length : %d \n" , iph->tot_len);
    }
sleep(2);
}

So far it work just fine, I can see wireshark in my source machine generating a syn packet and sending it to my destination machine.

In my Destination host I have a simple server which listens on port 8000.

What I basically Do is using this TCP packet generator I am sending a syn packet to my server and then the server responds with syn, ack, but then the source machine(above program) sends back rst. which does not complete the 3-way-handshake.

What I want to do is to complete the 3-way-handshake with my TCP.c program.

I would really Appreciate if you could tell me how do I do that? Do I use recv() to receive back the syn/ack and then craft a new packet as ack and responds with it or what?

Regards :)

1
The SYN/ACK will come back via IP, which will then send the segment to TCP based on the Protocol number in the IP header. TCP knows nothing of this, so it properly sends RST.Ron Maupin

1 Answers

2
votes

As Ron Maupin has pointed out, the returning SYN/ACK is processed by the local IP stack because it belongs to TCP. Since there's no half-open socket a RST is sent back.

If you really do want to take over low-level TCP you'll need to either

  1. prevent the SYN/ACK from reaching the local IP stack (by a combination of packet capturing and filtering with pcap or similar - easiest approach)
  2. deactivate the IP stack altogether by unbinding the IP protocol from the NIC (hard - you need to emulate the full stack)
  3. or register as protocol handler for TCP (hard - you need to blend into the stack; thx Luis).

EDIT

To be able to unconfigure the IP stack there are two approaches (one of them is practically unfeasible, as the system, lacking tcp/ip protocols, gets to a practically unusable state):

  • deactivate globally tcp by reconfiguring the kernel and disable completely the tcp module. As I said before, this is normally unfeasible.

  • deactivate IP on the network card. You can follow this procedure by not configuring any ip address on the network card, so it processes protocols by discarding packets and you can only get access to the packets by attaching to the protocol with a RAW socket. Almost all these techniques require root access, so are hidden to normal user processes. When you configure an ip address for a network card, you connect it to IP. Unfortunately, it is very difficult to unconnect just TCP, as many of the original NetBSD callbacks for packet dispatching were hardcoded and much functionality and efficiency of the network code comes from being hardwired. Only recompiling a kernel without TCP support would be a way to do this.