diff options
author | Sam Roberts <vieuxtech@gmail.com> | 2019-09-23 13:10:22 -0700 |
---|---|---|
committer | Sam Roberts <vieuxtech@gmail.com> | 2019-09-23 13:46:14 -0700 |
commit | 4cacef3582dcb94aae7da412f4027f04fa317f57 (patch) | |
tree | a72e0d39c8e721f103be41f32c3f19191c015f0c /src/libnet_pblock.c | |
parent | 923f9a43eb9b45f992bbc107732684193454487a (diff) | |
download | libnet-4cacef3582dcb94aae7da412f4027f04fa317f57.tar.gz |
Move libnet/ content to top of git repo
1. Moved scripts into libnet/scripts/, moved debian/ into libnet/,
and other top-level content into libnet/.
2. Moved libnet/* up to ./
Diffstat (limited to 'src/libnet_pblock.c')
-rw-r--r-- | src/libnet_pblock.c | 618 |
1 files changed, 618 insertions, 0 deletions
diff --git a/src/libnet_pblock.c b/src/libnet_pblock.c new file mode 100644 index 0000000..bf2a548 --- /dev/null +++ b/src/libnet_pblock.c @@ -0,0 +1,618 @@ +/* + * $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. + * + */ + +#include "common.h" +#include <assert.h> + +libnet_pblock_t * +libnet_pblock_probe(libnet_t *l, libnet_ptag_t ptag, uint32_t b_len, uint8_t type) +{ + int offset; + libnet_pblock_t *p; + + if (ptag == LIBNET_PTAG_INITIALIZER) + { + return libnet_pblock_new(l, b_len); + } + + /* + * 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 (b_len > p->b_len) + { + offset = b_len - p->b_len; /* how many bytes larger new pblock is */ + free(p->buf); + p->buf = malloc(b_len); + if (p->buf == NULL) + { + snprintf(l->err_buf, LIBNET_ERRBUF_SIZE, + "%s(): can't resize pblock buffer: %s", __func__, + strerror(errno)); + return (NULL); + } + memset(p->buf, 0, b_len); + p->h_len += offset; /* new length for checksums */ + p->b_len = b_len; /* new buf len */ + l->total_size += offset; + } + else + { + offset = p->b_len - b_len; + p->h_len -= offset; /* new length for checksums */ + p->b_len = b_len; /* new buf len */ + l->total_size -= offset; + } + p->copied = 0; /* reset copied counter */ + + return (p); +} + +static void* zmalloc(libnet_t* l, uint32_t size, const char* func) +{ + void* v = malloc(size); + if(v) + memset(v, 0, size); + else + snprintf(l->err_buf, LIBNET_ERRBUF_SIZE, "%s(): malloc(): %s", func, + strerror(errno)); + return v; +} + +libnet_pblock_t * +libnet_pblock_new(libnet_t *l, uint32_t b_len) +{ + libnet_pblock_t *p = zmalloc(l, sizeof(libnet_pblock_t), __func__); + if(!p) + return NULL; + + p->buf = zmalloc(l, b_len, __func__); + + if(!p->buf) + { + free(p); + return NULL; + } + + p->b_len = b_len; + + l->total_size += b_len; + l->n_pblocks++; + + /* make the head node if it doesn't exist */ + if (l->protocol_blocks == NULL) + { + l->protocol_blocks = p; + l->pblock_end = p; + } + else + { + l->pblock_end->next = p; + p->prev = l->pblock_end; + l->pblock_end = p; + } + + return p; +} + +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); +} + +static void libnet_pblock_remove_from_list(libnet_t *l, libnet_pblock_t *p) +{ + 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; + } +} + +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); + } + + /* check for already present before */ + if(p2->next == p1) + return 1; + + libnet_pblock_remove_from_list(l, p2); + + /* insert p2 into list */ + 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", __func__); + return (NULL); +} + +int +libnet_pblock_append(libnet_t *l, libnet_pblock_t *p, const void *buf, uint32_t len) +{ + if (len && !buf) + { + snprintf(l->err_buf, LIBNET_ERRBUF_SIZE, + "%s(): payload inconsistency", __func__); + return -1; + } + + if (p->copied + len > p->b_len) + { + snprintf(l->err_buf, LIBNET_ERRBUF_SIZE, + "%s(): memcpy would overflow buffer", __func__); + return (-1); + } + memcpy(p->buf + p->copied, buf, len); + p->copied += len; + return (1); +} + +void +libnet_pblock_setflags(libnet_pblock_t *p, uint8_t flags) +{ + p->flags = flags; +} + +/* FIXME both ptag setting and end setting should be done in pblock new and/or pblock probe. */ +libnet_ptag_t +libnet_pblock_update(libnet_t *l, libnet_pblock_t *p, uint32_t h_len, uint8_t type) +{ + p->type = type; + p->ptag = ++(l->ptag_state); + p->h_len = h_len; + l->pblock_end = p; /* point end of pblock list here */ + + return (p->ptag); +} + +static int pblock_is_ip(libnet_pblock_t* p) +{ + return p->type == LIBNET_PBLOCK_IPV4_H || p->type == LIBNET_PBLOCK_IPV6_H; +} + +/* q is either an ip hdr, or is followed by an ip hdr. return the offset + * from end of packet. if there is no offset, we'll return the total size, + * and things will break later + */ +static int calculate_ip_offset(libnet_t* l, libnet_pblock_t* q) +{ + int ip_offset = 0; + libnet_pblock_t* p = l->protocol_blocks; + for(; p && p != q; p = p->next) { + ip_offset += p->b_len; + } + assert(p == q); /* if not true, then q is not a pblock! */ + + for(; p; p = p->next) { + ip_offset += p->b_len; + if(pblock_is_ip(p)) + break; + } + + return ip_offset; +} + +int +libnet_pblock_coalesce(libnet_t *l, uint8_t **packet, uint32_t *size) +{ + /* + * 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; + } + + if(!l->total_size && !l->aligner) { + /* Avoid allocating zero bytes of memory, it perturbs electric fence. */ + *packet = malloc(1); + **packet =1; + } else { + *packet = malloc(l->aligner + l->total_size); + } + if (*packet == NULL) + { + snprintf(l->err_buf, LIBNET_ERRBUF_SIZE, "%s(): malloc(): %s", + __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", + __func__); + goto err; + } + 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", + __func__); + goto err; + } + 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", + __func__); + goto err; + } + break; + default: + /* we should not end up here ever */ + snprintf(l->err_buf, LIBNET_ERRBUF_SIZE, + "%s(): suddenly the dungeon collapses -- you die", + __func__); + goto err; + break; + } + } + + /* Build packet from end to start. */ + { + /* + From top to bottom, go through pblocks pairwise: + + p is the currently being copied pblock, and steps through every block + q is the prev pblock to p that needs checksumming, it will + not step through every block as p does, it will skip any that do not + need checksumming. + n offset from start of packet to beginning of block we are writing + + q is NULL on first iteration + p is NULL on last iteration + + Checksums are done on q, to give p a chance to be copied over, since + checksumming q can require a lower-level header to be encoded, in the + case of IP protocols (which are the only kinds handled by libnet's + checksum implementation). + + This is very obscure, or would be much more clear if it was done in + two loops. + */ + libnet_pblock_t *q = NULL; + libnet_pblock_t *p = NULL; + uint32_t n; + + 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 0 + printf("-- n %d/%d cksum? %d\n", n, l->aligner + l->total_size, + q && + (p == NULL || (p->flags & LIBNET_PBLOCK_DO_CHECKSUM)) && + (q->flags & LIBNET_PBLOCK_DO_CHECKSUM)); + if(q) + { + printf(" iph %d/%d offset -%d\n", + (l->total_size + l->aligner) - q->ip_offset, + l->total_size + l->aligner, + q->ip_offset + ); + } + if (p) + { + printf("p %p ptag %d b_len %d h_len %d cksum? %d type %s\n", + p, p->ptag, + p->b_len, p->h_len, + p->flags & LIBNET_PBLOCK_DO_CHECKSUM, + libnet_diag_dump_pblock_type(p->type) + ); + } + if (q) + { + printf("q %p ptag %d b_len %d h_len %d cksum? %d type %s\n", + q, q->ptag, + q->b_len, q->h_len, + q->flags & LIBNET_PBLOCK_DO_CHECKSUM, + libnet_diag_dump_pblock_type(q->type) + ); + } +#endif + if (q) + { + if (p == NULL || (p->flags & LIBNET_PBLOCK_DO_CHECKSUM)) + { + if (q->flags & LIBNET_PBLOCK_DO_CHECKSUM) + { + uint32_t c; + uint8_t* end = *packet + l->aligner + l->total_size; + uint8_t* beg = *packet + n; + int ip_offset = calculate_ip_offset(l, q); + uint8_t* iph = end - ip_offset; +#if 0 + printf("p %d/%s q %d/%s offset calculated %d\n", + p ? p->ptag : -1, p ? libnet_diag_dump_pblock_type(p->type) : "nil", + q->ptag, libnet_diag_dump_pblock_type(q->type), + ip_offset); +#endif + c = libnet_inet_checksum(l, iph, + libnet_pblock_p2p(q->type), q->h_len, + beg, end); + if (c == -1) + { + /* err msg set in libnet_do_checksum() */ + goto err; + } + } + 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); + +err: + free(*packet); + *packet = NULL; + return (-1); +} + +void +libnet_pblock_delete(libnet_t *l, libnet_pblock_t *p) +{ + if (p) + { + l->total_size -= p->b_len; + l->n_pblocks--; + + libnet_pblock_remove_from_list(l, p); + + if (p->buf) + { + free(p->buf); + p->buf = NULL; + } + + free(p); + } +} + +int +libnet_pblock_p2p(uint8_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_ICMPV6_H: + case LIBNET_PBLOCK_ICMPV6_ECHO_H: + case LIBNET_PBLOCK_ICMPV6_UNREACH_H: + case LIBNET_PBLOCK_ICMPV6_NDP_NSOL_H: + case LIBNET_PBLOCK_ICMPV6_NDP_NADV_H: + return (IPPROTO_ICMPV6); + case LIBNET_PBLOCK_IGMP_H: + return (IPPROTO_IGMP); + case LIBNET_PBLOCK_IPV4_H: + return (IPPROTO_IP); + case LIBNET_PBLOCK_IPV6_H: + return (IPPROTO_IPV6); + 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, libnet_pblock_t *p) +{ + (void) l; + (void) p; + /* For backwards compatibility, libnet_pblock_t no longer includes + an ip_offset, so calling this is unnecessary. + */ +} + + |