IwontDoIt
IwontDoIt

Reputation: 33

sending custom tcp packet using sendto() in c?

To be precise I have created this simple TCP program to send a simple TCP packet over network to a destination machine, but somehow Don't really know, what am I doing wrong but it don't appear to be sending any packets to the destination host. also I can't find it in my wireshark.

rawtcp.c:

//---cat rawtcp.c---
// Run as root or SUID 0, just datagram no data/payload

#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#include <netinet/in.h>
#include <netinet/tcp.h>

// Packet length
#define PCKT_LEN 8192
#pragma pack(push, 1)
// May create separate header file (.h) for all
// headers' structures
// IP header's structure
struct ipheader {
 unsigned char      iph_ihl:4, /* Little-endian */
                iph_ver:4;
 unsigned char      iph_tos;
 unsigned short int iph_len;
 unsigned short int iph_ident;
 unsigned short int iph_offset;
 unsigned char      iph_ttl;
 unsigned char      iph_protocol;
 unsigned short int iph_chksum;
 unsigned int       iph_sourceip;
 unsigned int       iph_destip;
};

/* Structure of a TCP header */
struct tcpheader {
 unsigned short int tcph_srcport;
 unsigned short int tcph_destport;
 unsigned int       tcph_seqnum;
 unsigned int       tcph_acknum;
 unsigned char      tcph_reserved:4, tcph_offset:4;
 // unsigned char tcph_flags;
 unsigned int
       tcp_res1:4,       /*little-endian*/
       tcph_hlen:4,      /*length of tcp header in 32-bit words*/
       tcph_fin:1,       /*Finish flag "fin"*/
       tcph_syn:1,       /*Synchronize sequence numbers to start a connection*/
       tcph_rst:1,       /*Reset flag */
       tcph_psh:1,       /*Push, sends data to the application*/
       tcph_ack:1,       /*acknowledge*/
       tcph_urg:1,       /*urgent pointer*/
       tcph_res2:2;

 unsigned short int tcph_win;
 unsigned short int tcph_chksum;
 unsigned short int tcph_urgptr;
};
#pragma pack(pop)
// Simple checksum function, may use others such as Cyclic Redundancy Check, CRC
unsigned short csum(unsigned short *buf, int len)
{
        unsigned long sum;
        for(sum=0; len>0; len--)
        sum += *buf++;
        sum = (sum >> 16) + (sum &0xffff);
        sum += (sum >> 16);
        return (unsigned short)(~sum);
}

int main(int argc, char *argv[])
{
    int sd;

    // No data, just datagram
    char buffer[PCKT_LEN];

    // The size of the headers
    struct ipheader *ip = (struct ipheader *) buffer;
    struct tcpheader *tcp = (struct tcpheader *) (buffer + sizeof(struct ipheader));
    struct sockaddr_in sin, din;

    int one = 1;
    const int *val = &one;
    memset(buffer, 0, PCKT_LEN);

    if(argc != 5)
    {
        printf("- Invalid parameters!!!\n");
        printf("- Usage: %s <source hostname/IP> <source port> <target hostname/IP> <target port>\n", argv[0]);
        exit(-1);
    }

    sd = socket(PF_INET, SOCK_RAW, IPPROTO_TCP);
    if(sd < 0)
    {
        perror("socket() error");
        exit(-1);
    } else
        printf("socket()-SOCK_RAW and tcp protocol is OK.\n");

    // The source is redundant, may be used later if needed
    // Address family
    sin.sin_family = AF_INET;
    din.sin_family = AF_INET;

    // Source port, can be any, modify as needed
    sin.sin_port = htons(atoi(argv[2]));
    din.sin_port = htons(atoi(argv[4]));

    // Source IP, can be any, modify as needed
    sin.sin_addr.s_addr = inet_addr(argv[1]);
    din.sin_addr.s_addr = inet_addr(argv[3]);

    // IP structure
    ip->iph_ihl = 5;
    ip->iph_ver = 4;
    ip->iph_tos = 16;
    ip->iph_len = htons(sizeof(struct ipheader) + sizeof(struct tcpheader));
    ip->iph_ident = htons(54321);
    ip->iph_offset = 0;
    ip->iph_ttl = 64;
    ip->iph_protocol = 6; // TCP
    ip->iph_chksum = 0; // Done by kernel

    // Source IP, modify as needed, spoofed, we accept through command line argument
    ip->iph_sourceip = inet_addr(argv[1]);

    // Destination IP, modify as needed, but here we accept through command line argument
    ip->iph_destip = inet_addr(argv[3]);

    // The TCP structure. The source port, spoofed, we accept through the command line
    tcp->tcph_srcport = htons(atoi(argv[2]));

    // The destination port, we accept through command line
    tcp->tcph_destport = htons(atoi(argv[4]));
    tcp->tcph_seqnum = htonl(1);
    tcp->tcph_acknum = 0;
    tcp->tcph_offset = 5;
    tcp->tcph_syn = 1;
    tcp->tcph_ack = 0;
    tcp->tcph_win = htons(32767);
    tcp->tcph_chksum = 0; // Done by kernel
    tcp->tcph_urgptr = 0;

    // IP checksum calculation
    ip->iph_chksum = htons(csum((unsigned short *) buffer, (sizeof(struct ipheader) + sizeof(struct tcpheader))));

    // Inform the kernel do not fill up the headers' structure, we fabricated our own
    if(setsockopt(sd, IPPROTO_IP, IP_HDRINCL, val, sizeof(one)) < 0)
    {
        perror("setsockopt() error");
        exit(-1);
    } else
        printf("setsockopt() is OK\n");
    printf("Using:::::Destination IP: %s port: %u, Source IP: %s port: %u.\n", argv[1], atoi(argv[2]), argv[3], atoi(argv[4]));

    // sendto() loop, send every 2 second for 50 counts
    unsigned int count;
    for(count = 0; count < 20; count++)
    {
        if(sendto(sd, buffer, ip->iph_len, 0, (struct sockaddr *)&din, sizeof(din)) < 0)
    // Verify
    {
        perror("sendto() error");
        exit(-1);
    } else
        printf("Count #%u - sendto() is OK\n", count);

        sleep(2);
   }
   close(sd);
   return 0;
}

while compiling it, it compiles just fine. also when I run the program as it suppose to be as ./rawtcp 192.168.1.152 1000 192.168.1.151 1000 it says:

socket()-SOCK_RAW and tcp protocol is OK.
setsockopt() is OK
Using:::::Destination IP: 192.168.1.151 port: 1000, Source IP: 192.168.1.152     port: 1000.
Count #0 - sendto() is OK
Count #1 - sendto() is OK

but while filtering wireshark as tcp.port == 1000 it never show up (Note: source ip is 192.168.1.152 and destination ip is 192.168.1.151). but If I do a simple hping3 -p 1000 192.168.1.151 it works just fine and I can see it in wireshark too.

I would really appreciate if you could tell me what am I doing wrong in my code :)

Upvotes: 2

Views: 5531

Answers (2)

Candy Gumdrop
Candy Gumdrop

Reputation: 2785

First of all, you are missing the <arpa/inet.h> header, which provides the inet_addr function you are using. Your program should still compile under C89 without this due to implicit function declaration but it is bad practice to rely on that and it could lead to subtle bugs.

One issue is your misuse of #pragma pack.

Before you declare your structs, you should do #pragma pack(push, 1), which both sets struct packing alignment to 1, and pushes the previous state of packing alignment to a stack. After you are finished declaring your structs, you can then do #pragma pack(pop) to reset struct packing back to normal in any following declarations. In the case of your program, omitting #pragma pack(pop) after your declarations should still work, but it is good practice to reset this, in case you ever do declare any other structs which you don't want to be tightly-packed, or you include any other header files afterwards.

#pragma pack(push, 1)
struct a {
    /* ... */
};

struct b {
    /* ... */
};
#pragma pack(pop)

The other problem with your struct ipheader is that you have both an 8-bit iph_flags and a 16-bit iph_offset, while the flags field in an IP header should be 3 bits and the offset field should be 13 bits (both of these together adding up to 16 bits). This means you have an extra 8 bits in your header which shouldn't be there. Since you are filling both of these fields with zero anyway, you can fix this by removing iph_flags from your struct completely, and just keeping the one 16-bit iph_offset field, filled with zero, which actually spans the space of the 3-bit and the 13-bit field in the IP header.

With these fixes, your program works for me and a packet can be seen in Wireshark.

Upvotes: 2

Sacha.R
Sacha.R

Reputation: 286

I think your port is already used.

You should change port for 30000 or higher, the 1000 ports are often take by other applications

You should also debug using telnet [ip] [port]

Upvotes: -1

Related Questions