views:

464

answers:

1

I am putting together a port scanner as a learning exercise. My problem is I'm trying to set the maximum segment size option(MSS) in the TCP header. I had a look at tcp.h, but I'm having trouble figuring out how to set it. I was hoping there would be an option like this:

tcp_header->mss(32000);

Something similar to the above was in tcp.h but not in the right struct. Admittedly, I'm still fairly new to reading struct definitions and I couldn't make much sense out of tcp.h so in the end I tried just tacking on the necessary bytes to the end of the TCP header:

struct tcphdr *CreateTcpHeader()
{
    struct tcphdr *tcp_header;

    tcp_header = (struct tcphdr *)malloc(sizeof(struct tcphdr)+4*sizeof(int));


    tcp_header->source = htons(SRC_PORT);
    tcp_header->dest = htons(DST_PORT);
    tcp_header->seq = htonl(0);             
    tcp_header->ack_seq = htonl(0);         
    tcp_header->res1 = 0;
    tcp_header->doff = (sizeof(struct tcphdr))/4;
    tcp_header->syn = 1;
    tcp_header->window = htons(4096);
    tcp_header->check = 0; /* Will calculate the checksum with pseudo-header later */
    tcp_header->urg_ptr = 0;


    /*memcpy the mss data onto the end of the tcp header. */
    int mssCode = 2;
    int mssLength = 4;
    uint16_t mss = htonl(32000);
    int offset = sizeof(struct tcphdr);
    memcpy( (tcp_header+offset), &mssCode, 1 );
    memcpy( (tcp_header+offset+1), &mssLength, 1 );
    memcpy( (tcp_header+offset+2), &mss, 2);

    return (tcp_header);
}

But after I wrote it it was clear that is wasn't a real solution, plus it still doesn't work :P So is there a better way?

+2  A: 

The struct tcphdr in tcp.h defines the mandatory part of the TCP header. (Look at the TCP header and you can match the definitions in struct tcphdr to your the actual bits to appear in the header.) Structs in C have a constant size, but TCP allows optional data. The header length field (doff in the structure) is the total length of the header, including options, so you'll need to add one word to account for the MSS option:

tcp_header->doff = (sizeof(struct tcphdr))/4 + 1;

Let's define a structure for the MSS option:

struct tcp_option_mss {
    uint8_t kind; /* 2 */
    uint8_t len; /* 4 */
    uint16_t mss;
} __attribute__((packed));

Now you can populate the structure, in the right order:

/*memcpy the mss data onto the end of the tcp header. */
struct tcp_option_mss mss;
mss.kind = 2;
mss.len = 4;
mss.mss = htons(32000);

Let's go one step further and define a single structure for your packet, to let the compiler help us out:

struct tcphdr_mss {
    struct tcphdr tcp_header;
    struct tcp_option_mss mss;
};

(You may need to add an end-of-option-list option at the end, and nop options to pad the option list to 8 bytes.)

Now we can put all the pieces together:

struct tcphdr *CreateTcpHeader()
{
    struct tcphdr_mss *tcp_header;

    tcp_header = malloc(sizeof(struct tcphdr_mss));

    tcp_header->tcp_header.source = htons(SRC_PORT);
    tcp_header->tcp_header.dest = htons(DST_PORT);
    tcp_header->tcp_header.seq = htonl(0);             
    tcp_header->tcp_header.ack_seq = htonl(0);         
    tcp_header->tcp_header.res1 = 0;
    tcp_header->tcp_header.doff = (sizeof(struct tcphdr_mss))/4;
    tcp_header->tcp_header.syn = 1;
    tcp_header->tcp_header.window = htons(4096);
    tcp_header->tcp_header.check = 0; /* Will calculate the checksum with pseudo-header later */
    tcp_header->tcp_header.urg_ptr = 0;

    tcp_header->mss.kind = 2;
    tcp_header->mss.len = 2;
    tcp_header->mss.mss = htons(32000);

    return (tcp_header);
}
Commodore Jaeger
ah it all makes sense now. Thanks a bunch.
teflon19