/* * libnet * libnet_build_gre.c - GRE packet assembler * * Copyright (c) 2003 Frédéric Raynal * 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. * */ #include "common.h" /* * 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(uint16_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)->" ? */ uint32_t libnet_getgre_length(uint16_t fv) { uint32_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(uint16_t fv, uint16_t type, uint16_t sum, uint16_t offset, uint32_t key, uint32_t seq, uint16_t len, const uint8_t *payload, uint32_t payload_s, libnet_t *l, libnet_ptag_t ptag) { uint32_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); if (libnet_pblock_append(l, p, (uint8_t *)&gre_hdr, LIBNET_GRE_H) == -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); if (libnet_pblock_append(l, p, (uint8_t*)&sum, sizeof(gre_hdr.gre_sum)) == -1) { /* err msg set in libnet_pblock_append() */ goto bad; } offset = htons(offset); if (libnet_pblock_append(l, p, (uint8_t*)&offset, sizeof(gre_hdr.gre_offset)) == -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); if (libnet_pblock_append(l, p, (uint8_t*)&key, sizeof(gre_hdr.gre_key)) == -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); if (libnet_pblock_append(l, p, (uint8_t*)&seq, sizeof(gre_hdr.gre_seq)) == -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(uint16_t fv, uint16_t type, uint16_t sum, uint16_t offset, uint32_t key, uint32_t seq, uint16_t len, const uint8_t *payload, uint32_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(uint16_t af, uint8_t offset, uint8_t length, uint8_t *routing, const uint8_t *payload, uint32_t payload_s, libnet_t *l, libnet_ptag_t ptag) { uint32_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; if (libnet_pblock_append(l, p, (uint8_t *)&sre_hdr, LIBNET_GRE_SRE_H) == -1) { /* err msg set in libnet_pblock_append() */ goto bad; } if ((routing && !length) || (!routing && length)) { snprintf(l->err_buf, LIBNET_ERRBUF_SIZE, "%s(): routing inconsistency", __func__); goto bad; } if (routing && length) { if (libnet_pblock_append(l, p, routing, length) == -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) { uint32_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); } if (libnet_pblock_append(l, p, (uint8_t *)&zero, LIBNET_GRE_SRE_H) == -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); } /** * Local Variables: * indent-tabs-mode: nil * c-file-style: "stroustrup" * End: */