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>