diff options
author | Sam Roberts <sroberts@sroberts-desktop.(none)> | 2009-02-18 14:19:16 -0800 |
---|---|---|
committer | Sam Roberts <sroberts@sroberts-desktop.(none)> | 2009-02-18 14:19:16 -0800 |
commit | 5ca6de2575d65f7664f326bee9fedd414a578a34 (patch) | |
tree | 60ee74be734f223b6186cdff0bae02f64f3ab380 /libnet/src/libnet_pblock.c | |
parent | c35d5ad62a9b45153cf638272a3d590729728c12 (diff) | |
download | libnet-5ca6de2575d65f7664f326bee9fedd414a578a34.tar.gz |
v1.1.3-RC-01 from http://www.packetfactory.net/projects/libnet/
Diffstat (limited to 'libnet/src/libnet_pblock.c')
-rw-r--r-- | libnet/src/libnet_pblock.c | 511 |
1 files changed, 511 insertions, 0 deletions
diff --git a/libnet/src/libnet_pblock.c b/libnet/src/libnet_pblock.c new file mode 100644 index 0000000..96af5a1 --- /dev/null +++ b/libnet/src/libnet_pblock.c @@ -0,0 +1,511 @@ +/* + * $Id: libnet_pblock.c,v 1.14 2004/11/09 07:05:07 mike Exp $ + * + * libnet + * libnet_pblock.c - Memory protocol block routines. + * + * Copyright (c) 1998 - 2004 Mike D. Schiffman <mike@infonexus.com> + * 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 + +libnet_pblock_t * +libnet_pblock_probe(libnet_t *l, libnet_ptag_t ptag, u_int32_t n, u_int8_t type) +{ + int offset; + libnet_pblock_t *p; + + if (ptag == LIBNET_PTAG_INITIALIZER) + { + /* + * Create a new pblock and enough buffer space for the packet. + */ + p = libnet_pblock_new(l, n); + if (p == NULL) + { + /* err msg set in libnet_pblock_new() */ + return (NULL); + } + } + else + { + /* + * Update this pblock, don't create a new one. Note that if the + * new packet size is larger than the old one we will do a malloc. + */ + p = libnet_pblock_find(l, ptag); + + if (p == NULL) + { + /* err msg set in libnet_pblock_find() */ + return (NULL); + } + if (p->type != type) + { + snprintf(l->err_buf, LIBNET_ERRBUF_SIZE, + "%s(): ptag refers to different type than expected (0x%x != 0x%x)", + __func__, p->type, type); + return (NULL); + } + /* + * If size is greater than the original block of memory, we need + * to malloc more memory. Should we use realloc? + */ + if (n > p->b_len) + { + offset = n - p->b_len; /* how many bytes larger new pblock is */ + free(p->buf); + p->buf = malloc(n); + if (p->buf == NULL) + { + snprintf(l->err_buf, LIBNET_ERRBUF_SIZE, + "%s(): can't resize pblock buffer: %s\n", __func__, + strerror(errno)); + return (NULL); + } + memset(p->buf, 0, n); + p->h_len += offset; /* new length for checksums */ + p->b_len = n; /* new buf len */ + l->total_size += offset; + } + else + { + offset = p->b_len - n; + p->h_len -= offset; /* new length for checksums */ + p->b_len = n; /* new buf len */ + l->total_size -= offset; + } + p->copied = 0; /* reset copied counter */ + } + return (p); +} + +libnet_pblock_t * +libnet_pblock_new(libnet_t *l, u_int32_t size) +{ + libnet_pblock_t *p; + /* + * Should we do error checking the size of the pblock here, or + * should we rely on the underlying operating system to complain when + * the user tries to write some ridiculously huge packet? + */ + + /* make the head node if it doesn't exist */ + if (l->protocol_blocks == NULL) + { + p = l->protocol_blocks = malloc(sizeof (libnet_pblock_t)); + if (p == NULL) + { + goto bad; + } + memset(p, 0, sizeof (libnet_pblock_t)); + } + else + { + p = l->pblock_end; + p->next = malloc(sizeof (libnet_pblock_t)); + + if (p->next == NULL) + { + goto bad; + } + memset(p->next, 0, sizeof (libnet_pblock_t)); + p->next->prev = p; + p = p->next; + } + + p->buf = malloc(size); + if (p->buf == NULL) + { + free(p); + p = NULL; + goto bad; + } + memset(p->buf, 0, size); + p->b_len = size; + l->total_size += size; + l->n_pblocks++; + return (p); + + bad: + snprintf(l->err_buf, LIBNET_ERRBUF_SIZE, "%s(): malloc(): %s\n", __func__, + strerror(errno)); + return (NULL); +} + +int +libnet_pblock_swap(libnet_t *l, libnet_ptag_t ptag1, libnet_ptag_t ptag2) +{ + libnet_pblock_t *p1, *p2; + + p1 = libnet_pblock_find(l, ptag1); + p2 = libnet_pblock_find(l, ptag2); + if (p1 == NULL || p2 == NULL) + { + /* error set elsewhere */ + return (-1); + } + + p2->prev = p1->prev; + p1->next = p2->next; + p2->next = p1; + p1->prev = p2; + + if (p1->next) + { + p1->next->prev = p1; + } + + if (p2->prev) + { + p2->prev->next = p2; + } + else + { + /* first node on the list */ + l->protocol_blocks = p2; + } + + if (l->pblock_end == p2) + { + l->pblock_end = p1; + } + return (1); +} + +int +libnet_pblock_insert_before(libnet_t *l, libnet_ptag_t ptag1, + libnet_ptag_t ptag2) +{ + libnet_pblock_t *p1, *p2; + + p1 = libnet_pblock_find(l, ptag1); + p2 = libnet_pblock_find(l, ptag2); + if (p1 == NULL || p2 == NULL) + { + /* error set elsewhere */ + return (-1); + } + + p2->prev = p1->prev; + p2->next = p1; + p1->prev = p2; + + if (p2->prev) + { + p2->prev->next = p2; + } + else + { + /* first node on the list */ + l->protocol_blocks = p2; + } + + return (1); +} + +libnet_pblock_t * +libnet_pblock_find(libnet_t *l, libnet_ptag_t ptag) +{ + libnet_pblock_t *p; + + for (p = l->protocol_blocks; p; p = p->next) + { + if (p->ptag == ptag) + { + return (p); + } + } + snprintf(l->err_buf, LIBNET_ERRBUF_SIZE, + "%s(): couldn't find protocol block\n", __func__); + return (NULL); +} + +int +libnet_pblock_append(libnet_t *l, libnet_pblock_t *p, u_int8_t *buf, + u_int32_t len) +{ + if (p->copied + len > p->b_len) + { + snprintf(l->err_buf, LIBNET_ERRBUF_SIZE, + "%s(): memcpy would overflow buffer\n", __func__); + return (-1); + } + memcpy(p->buf + p->copied, buf, len); + p->copied += len; + return (1); +} + +void +libnet_pblock_setflags(libnet_pblock_t *p, u_int8_t flags) +{ + p->flags = flags; +} + +libnet_ptag_t +libnet_pblock_update(libnet_t *l, libnet_pblock_t *p, u_int32_t h, +u_int8_t type) +{ + p->type = type; + p->ptag = ++(l->ptag_state); + p->h_len = h; + l->pblock_end = p; /* point end of pblock list here */ + + return (p->ptag); +} + +int +libnet_pblock_coalesce(libnet_t *l, u_int8_t **packet, u_int32_t *size) +{ + libnet_pblock_t *p, *q; + u_int32_t c, n; + + /* + * Determine the offset required to keep memory aligned (strict + * architectures like solaris enforce this, but's a good practice + * either way). This is only required on the link layer with the + * 14 byte ethernet offset (others are similarly unkind). + */ + if (l->injection_type == LIBNET_LINK || + l->injection_type == LIBNET_LINK_ADV) + { + /* 8 byte alignment should work */ + l->aligner = 8 - (l->link_offset % 8); + } + else + { + l->aligner = 0; + } + + *packet = malloc(l->aligner + l->total_size); + if (*packet == NULL) + { + snprintf(l->err_buf, LIBNET_ERRBUF_SIZE, "%s(): malloc(): %s\n", + __func__, strerror(errno)); + return (-1); + } + + memset(*packet, 0, l->aligner + l->total_size); + + if (l->injection_type == LIBNET_RAW4 && + l->pblock_end->type == LIBNET_PBLOCK_IPV4_H) + { + libnet_pblock_setflags(l->pblock_end, LIBNET_PBLOCK_DO_CHECKSUM); + } + + /* additional sanity checks to perform if we're not in advanced mode */ + if (!(l->injection_type & LIBNET_ADV_MASK)) + { + switch (l->injection_type) + { + case LIBNET_LINK: + if ((l->pblock_end->type != LIBNET_PBLOCK_TOKEN_RING_H) && + (l->pblock_end->type != LIBNET_PBLOCK_FDDI_H) && + (l->pblock_end->type != LIBNET_PBLOCK_ETH_H) && + (l->pblock_end->type != LIBNET_PBLOCK_802_1Q_H) && + (l->pblock_end->type != LIBNET_PBLOCK_ISL_H) && + (l->pblock_end->type != LIBNET_PBLOCK_802_3_H)) + { + snprintf(l->err_buf, LIBNET_ERRBUF_SIZE, + "%s(): packet assembly cannot find a layer 2 header\n", + __func__); + return (-1); + } + break; + case LIBNET_RAW4: + if ((l->pblock_end->type != LIBNET_PBLOCK_IPV4_H)) + { + snprintf(l->err_buf, LIBNET_ERRBUF_SIZE, + "%s(): packet assembly cannot find an IPv4 header\n", + __func__); + return (-1); + } + break; + case LIBNET_RAW6: + if ((l->pblock_end->type != LIBNET_PBLOCK_IPV6_H)) + { + snprintf(l->err_buf, LIBNET_ERRBUF_SIZE, + "%s(): packet assembly cannot find an IPv6 header\n", + __func__); + return (-1); + } + break; + default: + /* we should not end up here ever */ + snprintf(l->err_buf, LIBNET_ERRBUF_SIZE, + "%s(): suddenly the dungeon collapses -- you die\n", + __func__); + return (-1); + break; + } + } + + q = NULL; + for (n = l->aligner + l->total_size, p = l->protocol_blocks; p || q; ) + { + if (q) + { + p = p->next; + } + if (p) + { + n -= p->b_len; + /* copy over the packet chunk */ + memcpy(*packet + n, p->buf, p->b_len); + } + if (q) + { + if (p == NULL || ((p->flags) & LIBNET_PBLOCK_DO_CHECKSUM)) + { + if ((q->flags) & LIBNET_PBLOCK_DO_CHECKSUM) + { + int offset = (l->total_size + l->aligner) - q->ip_offset; + c = libnet_do_checksum(l, *packet + offset, + libnet_pblock_p2p(q->type), q->h_len); + if (c == -1) + { + /* err msg set in libnet_do_checksum() */ + return (-1); + } + } + q = p; + } + } + else + { + q = p; + } + } + *size = l->aligner + l->total_size; + + /* + * Set the packet pointer to the true beginning of the packet and set + * the size for transmission. + */ + if ((l->injection_type == LIBNET_LINK || + l->injection_type == LIBNET_LINK_ADV) && l->aligner) + { + *packet += l->aligner; + *size -= l->aligner; + } + return (1); +} + +void +libnet_pblock_delete(libnet_t *l, libnet_pblock_t *p) +{ + if (p) + { + l->total_size -= p->b_len; + l->n_pblocks--; + if (p->prev) + { + p->prev->next = p->next; + } + else + { + l->protocol_blocks = p->next; + } + + if (p->next) + { + p->next->prev = p->prev; + } + else + { + l->pblock_end = p->prev; + } + + if (p->buf) + { + free(p->buf); + } + + free(p); + p = NULL; + } +} + +int +libnet_pblock_p2p(u_int8_t type) +{ + /* for checksum; return the protocol number given a pblock type*/ + switch (type) + { + case LIBNET_PBLOCK_CDP_H: + return (LIBNET_PROTO_CDP); + case LIBNET_PBLOCK_ICMPV4_H: + case LIBNET_PBLOCK_ICMPV4_ECHO_H: + case LIBNET_PBLOCK_ICMPV4_MASK_H: + case LIBNET_PBLOCK_ICMPV4_UNREACH_H: + case LIBNET_PBLOCK_ICMPV4_TIMXCEED_H: + case LIBNET_PBLOCK_ICMPV4_REDIRECT_H: + case LIBNET_PBLOCK_ICMPV4_TS_H: + return (IPPROTO_ICMP); + case LIBNET_PBLOCK_IGMP_H: + return (IPPROTO_IGMP); + case LIBNET_PBLOCK_IPV4_H: + return (IPPROTO_IP); + case LIBNET_ISL_H: + return (LIBNET_PROTO_ISL); + case LIBNET_PBLOCK_OSPF_H: + return (IPPROTO_OSPF); + case LIBNET_PBLOCK_LS_RTR_H: + return (IPPROTO_OSPF_LSA); + case LIBNET_PBLOCK_TCP_H: + return (IPPROTO_TCP); + case LIBNET_PBLOCK_UDP_H: + return (IPPROTO_UDP); + case LIBNET_PBLOCK_VRRP_H: + return (IPPROTO_VRRP); + case LIBNET_PBLOCK_GRE_H: + return (IPPROTO_GRE); + default: + return (-1); + } +} + +void +libnet_pblock_record_ip_offset(libnet_t *l, u_int32_t offset) +{ + libnet_pblock_t *p = l->pblock_end; + + do + { + p->ip_offset = offset; + p = p->prev; + } while (p && p->type != LIBNET_PBLOCK_IPV4_H); +} + + +/* EOF */ |