summaryrefslogtreecommitdiff
path: root/libnet/src/libnet_build_gre.c
diff options
context:
space:
mode:
Diffstat (limited to 'libnet/src/libnet_build_gre.c')
-rw-r--r--libnet/src/libnet_build_gre.c399
1 files changed, 399 insertions, 0 deletions
diff --git a/libnet/src/libnet_build_gre.c b/libnet/src/libnet_build_gre.c
new file mode 100644
index 0000000..620c27a
--- /dev/null
+++ b/libnet/src/libnet_build_gre.c
@@ -0,0 +1,399 @@
+/*
+ * libnet
+ * libnet_build_gre.c - GRE packet assembler
+ *
+ * Copyright (c) 2003 Frédéric Raynal <pappy@security-labs.org>
+ * 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.
+ *
+ */
+
+#if (HAVE_CONFIG_H)
+#include "../include/config.h"
+#endif
+#if (!(_WIN32) || (__CYGWIN__))
+#include "../include/libnet.h"
+#else
+#include "../include/win32/libnet.h"
+#endif
+
+/*
+ * Overall packet
+ *
+ * The entire encapsulated packet would then have the form:
+ *
+ * ---------------------------------
+ * | |
+ * | Delivery Header |
+ * | |
+ * ---------------------------------
+ * | |
+ * | GRE Header |
+ * | |
+ * ---------------------------------
+ * | |
+ * | Payload packet |
+ * | |
+ * ---------------------------------
+ *
+ * RFC 1701 defines a header.
+ * A new RFC (2784) has changed the header and proposed to remove the key
+ * and seqnum.
+ * A newer RFC (2890) has changed the header proposed in RFC 2784 by putting
+ * back key and seqnum.
+ * These will be supported the day IETF'guys stop this mess !
+ *
+ * FR
+ */
+
+
+/*
+ * Generic Routing Encapsulation (GRE)
+ * RFC 1701 http://www.faqs.org/rfcs/rfc1701.html
+ *
+ *
+ * Packet header
+ *
+ * The GRE packet header has the form:
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |C|R|K|S|s|Recur| Flags | Ver | Protocol Type |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Checksum (optional) | Offset (optional) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Key (optional) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Sequence Number (optional) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Routing (optional) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * Enhanced GRE header
+ *
+ * See rfc 2637 for details. It is used for PPTP tunneling.
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |C|R|K|S|s|Recur|A| Flags | Ver | Protocol Type |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Key (HW) Payload Length | Key (LW) Call ID |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Sequence Number (Optional) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Acknowledgment Number (Optional) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ */
+#if 0
+static void
+__libnet_print_gre_flags_ver(u_int16_t fv)
+{
+ printf("version = %d (%d) -> ",
+ fv & GRE_VERSION_MASK, libnet_getgre_length(fv));
+ if (fv & GRE_CSUM)
+ {
+ printf("CSUM ");
+ }
+ if (fv & GRE_ROUTING)
+ {
+ printf("ROUTING ");
+ }
+ if (fv & GRE_KEY)
+ {
+ printf("KEY ");
+ }
+ if (fv & GRE_SEQ)
+ {
+ printf("SEQ ");
+ }
+ if (fv & GRE_ACK)
+ {
+ printf("ACK ");
+ }
+ printf("\n");
+}
+#endif
+
+/* FIXME: what is the portability of the "((struct libnet_gre_hdr*)0)->" ? */
+u_int32_t
+libnet_getgre_length(u_int16_t fv)
+{
+
+ u_int32_t n = LIBNET_GRE_H;
+ /*
+ * If either the Checksum Present bit or the Routing Present bit are
+ * set, BOTH the Checksum and Offset fields are present in the GRE
+ * packet.
+ */
+
+ if ((!(fv & GRE_VERSION_MASK) && (fv & (GRE_CSUM|GRE_ROUTING))) || /* v0 */
+ (fv & GRE_VERSION_MASK) ) /* v1 */
+ {
+ n += sizeof( ((struct libnet_gre_hdr *)0)->gre_sum) +
+ sizeof( ((struct libnet_gre_hdr *)0)->gre_offset);
+ }
+
+ if ((!(fv & GRE_VERSION_MASK) && (fv & GRE_KEY)) || /* v0 */
+ ( (fv & GRE_VERSION_MASK) && (fv & GRE_SEQ)) ) /* v1 */
+ {
+ n += sizeof( ((struct libnet_gre_hdr *)0)->gre_key);
+ }
+
+ if ((!(fv & GRE_VERSION_MASK) && (fv & GRE_SEQ)) || /* v0 */
+ ( (fv & GRE_VERSION_MASK) && (fv & GRE_ACK)) ) /* v1 */
+ {
+ n += sizeof( ((struct libnet_gre_hdr *)0)->gre_seq );
+ }
+
+ return (n);
+}
+
+libnet_ptag_t
+libnet_build_gre(u_int16_t fv, u_int16_t type, u_int16_t sum,
+u_int16_t offset, u_int32_t key, u_int32_t seq, u_int16_t len,
+u_int8_t *payload, u_int32_t payload_s, libnet_t *l, libnet_ptag_t ptag)
+{
+ u_int32_t n;
+ libnet_pblock_t *p;
+ struct libnet_gre_hdr gre_hdr;
+
+ if (l == NULL)
+ {
+ return (-1);
+ }
+
+ n = libnet_getgre_length(fv) + payload_s;
+
+ /*
+ * Find the existing protocol block if a ptag is specified, or create
+ * a new one.
+ */
+ p = libnet_pblock_probe(l, ptag, n, LIBNET_PBLOCK_GRE_H);
+ if (p == NULL)
+ {
+ return (-1);
+ }
+
+ gre_hdr.flags_ver = htons(fv);
+ gre_hdr.type = htons(type);
+ n = libnet_pblock_append(l, p, (u_int8_t *)&gre_hdr, LIBNET_GRE_H);
+ if (n == -1)
+ {
+ /* err msg set in libnet_pblock_append() */
+ goto bad;
+ }
+
+ if ((!(fv & GRE_VERSION_MASK) && (fv & (GRE_CSUM|GRE_ROUTING))) || /* v0 */
+ (fv & GRE_VERSION_MASK)) /* v1 */
+ {
+ sum = htons(sum);
+ n = libnet_pblock_append(l, p, (u_int8_t*)&sum,
+ sizeof(gre_hdr.gre_sum));
+ if (n == -1)
+ {
+ /* err msg set in libnet_pblock_append() */
+ goto bad;
+ }
+ offset = htons(offset);
+ n = libnet_pblock_append(l, p, (u_int8_t*)&offset,
+ sizeof(gre_hdr.gre_offset));
+ if (n == -1)
+ {
+ /* err msg set in libnet_pblock_append() */
+ goto bad;
+ }
+ }
+
+ if ((!(fv & GRE_VERSION_MASK) && (fv & GRE_KEY)) || /* v0 */
+ ( (fv & GRE_VERSION_MASK) && (fv & GRE_SEQ)) ) /* v1 */
+ {
+ key = htonl(key);
+ n = libnet_pblock_append(l, p, (u_int8_t*)&key,
+ sizeof(gre_hdr.gre_key));
+ if (n == -1)
+ {
+ /* err msg set in libnet_pblock_append() */
+ goto bad;
+ }
+ }
+
+ if ((!(fv & GRE_VERSION_MASK) && (fv & GRE_SEQ)) || /* v0 */
+ ( (fv & GRE_VERSION_MASK) && (fv & GRE_ACK)) ) /* v1 */
+ {
+ seq = htonl(seq);
+ n = libnet_pblock_append(l, p, (u_int8_t*)&seq,
+ sizeof(gre_hdr.gre_seq));
+ if (n == -1)
+ {
+ /* err msg set in libnet_pblock_append() */
+ goto bad;
+ }
+ }
+
+ /* boilerplate payload sanity check / append macro */
+ LIBNET_DO_PAYLOAD(l, p);
+
+ if ( (fv & GRE_CSUM) && (!sum) )
+ {
+ libnet_pblock_setflags(p, LIBNET_PBLOCK_DO_CHECKSUM);
+ }
+
+ return (ptag ? ptag : libnet_pblock_update(l, p, len, LIBNET_PBLOCK_GRE_H));
+
+bad:
+ libnet_pblock_delete(l, p);
+ return (-1);
+}
+
+libnet_ptag_t
+libnet_build_egre(u_int16_t fv, u_int16_t type, u_int16_t sum,
+u_int16_t offset, u_int32_t key, u_int32_t seq, u_int16_t len,
+u_int8_t *payload, u_int32_t payload_s, libnet_t *l, libnet_ptag_t ptag)
+{
+ return (libnet_build_gre(fv, type, sum, offset, key, seq, len,
+ payload, payload_s, l, ptag));
+}
+
+/*
+ * Routing (variable)
+ *
+ * The Routing field is optional and is present only if the Routing
+ * Present bit is set to 1.
+ *
+ * The Routing field is a list of Source Route Entries (SREs). Each
+ * SRE has the form:
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Address Family | SRE Offset | SRE Length |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Routing Information ...
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ */
+libnet_ptag_t
+libnet_build_gre_sre(u_int16_t af, u_int8_t offset, u_int8_t length,
+u_int8_t *routing, u_int8_t *payload, u_int32_t payload_s, libnet_t *l,
+libnet_ptag_t ptag)
+{
+ u_int32_t n;
+ libnet_pblock_t *p;
+ struct libnet_gre_sre_hdr sre_hdr;
+
+ if (l == NULL)
+ {
+ return (-1);
+ }
+
+ n = LIBNET_GRE_SRE_H + length + payload_s;
+
+ /*
+ * Find the existing protocol block if a ptag is specified, or create
+ * a new one.
+ */
+ p = libnet_pblock_probe(l, ptag, n, LIBNET_PBLOCK_GRE_SRE_H);
+ if (p == NULL)
+ {
+ return (-1);
+ }
+ sre_hdr.af = htons(af);
+ sre_hdr.sre_offset = offset;
+ sre_hdr.sre_length = length;
+ n = libnet_pblock_append(l, p, (u_int8_t *)&sre_hdr, LIBNET_GRE_SRE_H);
+ if (n == -1)
+ {
+ /* err msg set in libnet_pblock_append() */
+ goto bad;
+ }
+
+ if ((routing && !length) || (!routing && length))
+ {
+ sprintf(l->err_buf, "%s(): routing inconsistency\n", __func__);
+ goto bad;
+ }
+
+ if (routing && length)
+ {
+ n = libnet_pblock_append(l, p, routing, length);
+ if (n == -1)
+ {
+ /* err msg set in libnet_pblock_append() */
+ goto bad;
+ }
+ }
+
+ /* boilerplate payload sanity check / append macro */
+ LIBNET_DO_PAYLOAD(l, p);
+
+ return (ptag ? ptag : libnet_pblock_update(l, p, 0,
+ LIBNET_PBLOCK_GRE_SRE_H));
+
+bad:
+ libnet_pblock_delete(l, p);
+ return (-1);
+
+}
+
+libnet_ptag_t
+libnet_build_gre_last_sre(libnet_t *l, libnet_ptag_t ptag)
+{
+ u_int32_t n, zero = 0;
+ libnet_pblock_t *p;
+
+ if (l == NULL)
+ {
+ return (-1);
+ }
+
+ n = LIBNET_GRE_SRE_H;
+
+ /*
+ * Find the existing protocol block if a ptag is specified, or create
+ * a new one.
+ */
+ p = libnet_pblock_probe(l, ptag, n, LIBNET_PBLOCK_GRE_H);
+ if (p == NULL)
+ {
+ return (-1);
+ }
+
+ n = libnet_pblock_append(l, p, (u_int8_t *)&zero, LIBNET_GRE_SRE_H);
+ if (n == -1)
+ {
+ /* err msg set in libnet_pblock_append() */
+ goto bad;
+ }
+
+ return (ptag ? ptag : libnet_pblock_update(l, p, 0,
+ LIBNET_PBLOCK_GRE_SRE_H));
+
+bad:
+ libnet_pblock_delete(l, p);
+ return (-1);
+
+}
+/* EOF */