Search code examples
tcppacket

How i can Send TCP Raw Socket with Ip Flags and Tcp Control Flags


I try to programm a Code which is able to build and send raw Sockets with the ability to fill all Fields in the IP Header and TCP Header.

I can actualy set all field for tcp header. If i set the IP Flags

short int ip_moreFrag :1, ip_doNotFrag:1, ip_reserved : 1;

to 0and follow the packet in Wireshark, i see that all the Fields i set is set too. enter image description here

But if i set the Ip Flags to 1 and follow the packet on Wireshark, i can see that the Protokol of my packet changed to Ipv4 and the flags are not set in Wireshark. Wireshark IPv4 Protokol Why?

I could not find the isue why the Programm is not send tcp packets if i set one of the ip flags to 1. Could you help me out?


Here is my Code:

// Run as root or SUID 0,

#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

struct ipheader {
    unsigned char ip_hl :4, ip_v :4;
    unsigned char ip_tos :8;
    unsigned short int ip_len :16;
    unsigned short int ip_id :16;
    unsigned short int ip_off :13;

    unsigned short int ip_moreFrag :1, ip_doNotFrag:1, ip_reserved : 1;

    unsigned char ip_ttl :8;
    unsigned char ip_p :8;
    unsigned short int ip_sum :16;
    unsigned int iph_sourceip :32;
    unsigned int iph_destip :32;
};


/* Structure of a TCP header */
struct tcpheader {
    unsigned short int th_sport :16;
    unsigned short int th_dport :16;
    unsigned int th_seq :32;
    unsigned int th_acknum :32;

    unsigned char th_reserved :4, th_off :4;


    unsigned short int th_fin :1, th_syn :1, th_rst :1, th_psh :1, th_ack :1,
            th_urg :1, th_cwr :1, th_ece :1;


    unsigned short int th_win :16;
    unsigned short int th_sum :16;
    unsigned short int th_urp :16;
};


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);

    sd = socket(PF_INET, SOCK_RAW, IPPROTO_RAW);
    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("1000"));
    din.sin_port = htons(atoi("1000"));

    // Source IP, can be any, modify as needed
    sin.sin_addr.s_addr = inet_addr("1.2.3.4");
    din.sin_addr.s_addr = inet_addr("127.0.0.1");

    // IP structure
    ip->ip_v = 4;
    ip->ip_hl = 5;

    ip->ip_tos = 16;
    ip->ip_off = 0;

    ip->ip_reserved = 0;
    ip->ip_doNotFrag = 0;
    ip->ip_moreFrag = 0;

    ip->ip_len = htons(sizeof(struct ipheader) + sizeof(struct tcpheader));
    ip->ip_id = htons(54321);
    ip->ip_ttl = 255;
    ip->ip_p = 6; // TCP
    ip->ip_sum = 0; // Done by kernel

    ip->iph_sourceip = inet_addr("1.2.3.4");

    ip->iph_destip = inet_addr("127.0.0.1");

    tcp->th_sport = htons(atoi("1000"));

    tcp->th_dport = htons(atoi("1000"));
    tcp->th_seq = htonl(0);
    tcp->th_acknum = 0;

//  tcp->th_hlen = 4;


    tcp->th_reserved = 0;
    tcp->th_off = 5;


    tcp->th_fin = 1;

    tcp->th_syn = 1;
    tcp->th_rst = 1;
    tcp->th_psh = 1;
    tcp->th_ack = 1;
    tcp->th_urg = 1;
    tcp->th_cwr = 1;
    tcp->th_ece = 1;


    tcp->th_win = htons(32767);
    tcp->th_sum = 0; // Done by kernel
    tcp->th_urp = 0;

    // IP checksum calculation
    ip->ip_sum = 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");

    unsigned int count;

    if (sendto(sd, buffer, ip->ip_len, 0, (struct sockaddr *) &din, sizeof(din))
            < 0)
            // Verify
            {
        perror("sendto() error");
        exit(-1);
    } else
        printf("Count #%u - sendto() is OK\n", count);

    close(sd);
    return 0;
}

Solution

  • Your program assumes that your compiler arranges bitfields from least-significant to most-significant within a 2-byte unit for these structure members:

        unsigned short int ip_off :13;
        unsigned short int ip_moreFrag :1, ip_doNotFrag:1, ip_reserved : 1;
    

    but Wireshark shows that that assumption is incorrect. If you look at your second packet capture where, apparently, two of the ip_moreFrag, ip_doNotFrag and ip_reserved members are set to 1 you'll see that Wireshark does not show any of the corresponding R, DF or MF flag bits set in the IP header. Those flag bits are all 0 in the packet.

    Instead, setting those bitfields caused two bits to be set to 1 in the Fragment offset field. That causes Wireshark to believe that this packet contains only a fragment of a larger datagram, and that the data in this packet that follows its IP header belongs at position 768 in the larger datagram. Data at position 768 can not be a TCP header (the TCP header would be found at position 0) and therefore Wireshark does not interpret any of the data in this packet as a TCP header.

    To fix, you can either reorder the bitfields in your struct ipheader so that they map to the correct position in the on-the-wire header, or you can switch to a different technique that doesn't depend on bitfields for building the header. Using bitfields is fragile, because compilers have a lot of freedom in how they arrange bitfields in memory. Even if you manage to get the bitfield mechanism working with the compiler you're using right now, there's no guarantee that it will work the same way with a different compiler or with a different release of your current compiler or with an invocation of your compiler with different compilation options.

    If you decide to continue with bitfields then you should check that the TCP flag bits in your struct tcpheader are doing what you need. They might or might not match up with the correct bits in the on-the-wire format. All you know from that first packet capture is that if you set them all to 1 then you get 0xff in the flag field, but you don't know for sure that the various structure members control the right bits. For instance, try setting just th_syn to 0 and see if Wireshark agrees that the SYN flag has been turned off.