Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 9 Mar 2001 15:06:27 -0500 (EST)
From:      Robert Watson <rwatson@freebsd.org>
To:        Walter Goralski <walterg@juniper.net>
Cc:        "'freebsd-hackers@freebsd.org'" <freebsd-hackers@freebsd.org>, "'freebsd-net@freebsd.org'" <freebsd-net@freebsd.org>
Subject:   Re: Generating SYN packets.
Message-ID:  <Pine.NEB.3.96L.1010309144757.86098B-100000@fledge.watson.org>
In-Reply-To: <C0D6C1C24CDBE1449BFEF1B72AFBF3A7057ECEFE@postal.jnpr.net>

next in thread | previous in thread | raw e-mail | index | archive | help

Generally speaking, the most portable way to generate IP packets is to do
so using the raw IP socket interface.  However, I've also successfully
generated packets using IPDIVERT, BPF, and custom kernel modules :-).  I
found the IPDIVERT performed quite nicely and was useful for exploring
firewall rules, as you can reinsert packets at arbitrary points in the
ruleset.  The key elements of the process involve opening an appropriate
raw socket, generating the TCP SYN packet, and then sending the packet out
on the raw socket.  I'm including excerpts of appropriate code below.  If
you decide to use some of it, feel free to attach the 2-clause BSD license
at the bottom of the file to your resulting code.  :-)

Opening the socket is fairly straight-forward:

        socket_raw = socket(PF_INET, SOCK_RAW, IPPROTO_RAW);
        if (socket_raw == -1) {
                perror("socket");
                return (-1);
        }

There are a few socket options you should sec.  This includes IP_OPTIONS
to disable any existing IP options:

        error = setsockopt(socket_raw, IPPROTO_IP, IP_OPTIONS, NULL, 0);
        if (error) {
                perror("setsockopt");
                return (-1);
        }

Also, IP_HDRINCL to indicate to the IP stack that you're going to include
a complete IP header in your packet, so it shouldn't prepend one:

        int ip_hdrincl = 1;
        error = setsockopt(socket_raw, IPPROTO_IP, IP_HDRINCL,
            (char *)&ip_hdrincl, sizeof(ip_hdrincl));
        if (error) {
                perror("setsockopt");
                exit(-1);
        }

Depending on how long you're going to have the socket open for, you may
want to close the read component of the socket or the buffers there will
fill up with other IP garbage coming off the network, including ICMP, etc.

        error = shutdown(socket_raw, SHUT_RD);
        if (error) {
                perror("shutdown");
                exit(-1);
        }

Assembling the TCP packet is the more tricky part.  There are two
important parts: first, filling in the necessary IP and TCP header
information, and second, generating the necessary checksums.  You can also
optionally throw in TCP options, such as MSS negotiation, SACK
negotiation, and things like ECN support.  Here's some pseudo-code that
covers some of this, but you'll need to tweak it as needed.  It assumes
that the provided struct in_addr's are already in network byte order, but
not the port numbers:

struct tcphdr_pseudo {
        u_long  thp_from;
        u_long  thp_to;
        char    thp_zero;
        char    thp_proto;
        u_short thp_len;
        struct tcphdr   thp_tcphdr;
};

...

        char    *buf;
        int     buflen;
        struct ip       *ip;
        struct tcphdr   *tcphdr;
        struct tcphdr_pseudo    tcphdr_pseudo;

        bzero(buf, buflen);
        ip = (struct ip *) buf;
        tcphdr = (struct tcphdr *) (ip + 1);

        ip->ip_tos = 0;
        ip->ip_off = 0;
        ip->ip_p = IPPROTO_TCP;
        ip->ip_hl = sizeof(struct ip) >> 2;
        ip->ip_v = 4;
        ip->ip_len = ntohs(htons(sizeof(struct ip) + sizeof(struct tcphdr)));
        ip->ip_src = from;
        ip->ip_dst = to;
        ip->ip_ttl = 255;
        ip->ip_sum = 0;
        ip->ip_id = random();

I usually borrow the IP checksumming routing from /sbin/ping, sticking it
in a seperate source file, and naming the function in_cksum(), taking two
arguments: a pointer to the data, and size of the data.  Generate the
IP-layer checksum first.

        ip->ip_sum = in_cksum((u_short *)ip, sizeof(struct ip));

Now fill in the TCP header.

        memset(tcphdr, '\0', sizeof(*tcphdr));
        tcphdr->th_sport = htons(12312);
        tcphdr->th_dport = htons(toport);
        tcphdr->th_seq = htonl(123123123);
        tcphdr->th_ack = 0;
        tcphdr->th_off = 5;
        tcphdr->th_flags = TCP_SYN;
        tcphdr->th_win = 1024;  /* arbitrary */
        tcphdr->th_urp = 0;
        tcphdr->th_sum = 0;

Next, you'll need to generate the TCP checksum -- to do this, fill in the
pseudo-header using the same fields as the real TCP and IP headers, and
set the TCP header checksum to the in_cksum() of the psuedo-header: 

        tcphdr_pseudo.thp_from = ip->ip_src.s_addr;
        tcphdr_pseudo.thp_to = ip->ip_dst.s_addr;
        tcphdr_pseudo.thp_zero = 0;
        tcphdr_pseudo.thp_tcphdr = *tcphdr;
        tcphdr_pseudo.thp_proto = IPPROTO_TCP;
        tcphdr_pseudo.thp_len = htons(sizeof(struct tcphdr));

        tcphdr->th_sum = in_cksum((u_short *) &tcphdr_pseudo,
            sizeof(tcphdr_pseudo));

The final length of the packet is sizeof(struct ip) + sizeof(struct
tcphdr).  Note that I did not initialize the MSS TCP option, and that the
window size I selected is probably inappropriate.  Finally, I want to send
the packet out over the previously opened raw socket:

        int     outlen;
        outlen = sendto(socket_raw, buf, len, 0, (struct sockaddr *) sa,
            sizeof(*sa));
        if (outlen != len)
                perror("sendto");

As noted above, a license is attached below.  This is fairly
straight-forward code, and you can probably find similar stuff in various
Stevens books, which I would highly recommend as a source of further
information.

Robert N M Watson             FreeBSD Core Team, TrustedBSD Project
robert@fledge.watson.org      NAI Labs, Safeport Network Services

/*-
 * Copyright (c) 1999, 2000, 2001 Robert N. M. Watson
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * $FreeBSD: $
 */




To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-net" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?Pine.NEB.3.96L.1010309144757.86098B-100000>