diff options
author | Lorry Tar Creator <lorry-tar-importer@baserock.org> | 2015-03-02 19:04:49 +0000 |
---|---|---|
committer | <> | 2015-05-08 15:30:59 +0000 |
commit | f800382616186a5d30e28d8b2c51e97a9a8360f2 (patch) | |
tree | 0d5270190548a37223d14b54383ce8a3d3af5302 /omapip/trace.c | |
download | isc-dhcp-tarball-master.tar.gz |
Imported from /home/lorry/working-area/delta_isc-dhcp-tarball/dhcp-4.2.8.tar.gz.HEADdhcp-4.2.8master
Diffstat (limited to 'omapip/trace.c')
-rw-r--r-- | omapip/trace.c | 719 |
1 files changed, 719 insertions, 0 deletions
diff --git a/omapip/trace.c b/omapip/trace.c new file mode 100644 index 0000000..f4115c1 --- /dev/null +++ b/omapip/trace.c @@ -0,0 +1,719 @@ +/* trace.c + + Subroutines that support tracing of OMAPI wire transactions and + provide a mechanism for programs using OMAPI to trace their own + transactions... */ + +/* + * Copyright (c) 2012,2014 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 2009-2010 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 2004-2007 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 2001-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT + * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * <info@isc.org> + * https://www.isc.org/ + * + */ + +#include "dhcpd.h" +#include <omapip/omapip_p.h> +#include <errno.h> + +#if defined (TRACING) +void (*trace_set_time_hook) (TIME); +static int tracing_stopped; +static int traceoutfile; +static int traceindex; +static trace_type_t **trace_types; +static int trace_type_count; +static int trace_type_max; +static trace_type_t *new_trace_types; +static FILE *traceinfile; +static tracefile_header_t tracefile_header; +static int trace_playback_flag; +trace_type_t trace_time_marker; + +#if defined (DEBUG_MEMORY_LEAKAGE) || defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT) +extern omapi_array_t *trace_listeners; +extern omapi_array_t *omapi_connections; + +extern int errno; + +void trace_free_all () +{ + trace_type_t *tp; + int i; + tp = new_trace_types; + while (tp) { + new_trace_types = tp -> next; + if (tp -> name) { + dfree (tp -> name, MDL); + tp -> name = (char *)0; + } + dfree (tp, MDL); + tp = new_trace_types; + } + for (i = 0; i < trace_type_count; i++) { + if (trace_types [i]) { + if (trace_types [i] -> name) + dfree (trace_types [i] -> name, MDL); + dfree (trace_types [i], MDL); + } + } + dfree (trace_types, MDL); + trace_types = (trace_type_t **)0; + trace_type_count = trace_type_max = 0; + + omapi_array_free (&trace_listeners, MDL); + omapi_array_free (&omapi_connections, MDL); +} +#endif + +static isc_result_t trace_type_record (trace_type_t *, + unsigned, const char *, int); + +int trace_playback () +{ + return trace_playback_flag; +} + +int trace_record () +{ + if (traceoutfile && !tracing_stopped) + return 1; + return 0; +} + +isc_result_t trace_init (void (*set_time) (TIME), + const char *file, int line) +{ + trace_type_t *root_type; + static int root_setup = 0; + + if (root_setup) + return ISC_R_SUCCESS; + + trace_set_time_hook = set_time; + + root_type = trace_type_register ("trace-index-mapping", + (void *)0, trace_index_map_input, + trace_index_stop_tracing, file, line); + if (!root_type) + return ISC_R_UNEXPECTED; + if (new_trace_types == root_type) + new_trace_types = new_trace_types -> next; + root_type -> index = 0; + trace_type_stash (root_type); + + root_setup = 1; + return ISC_R_SUCCESS; +} + +isc_result_t trace_begin (const char *filename, + const char *file, int line) +{ + tracefile_header_t tfh; + int status; + trace_type_t *tptr, *next; + isc_result_t result; + + if (traceoutfile) { + log_error ("%s(%d): trace_begin called twice", + file, line); + return DHCP_R_INVALIDARG; + } + + traceoutfile = open (filename, O_CREAT | O_WRONLY | O_EXCL, 0600); + if (traceoutfile < 0 && errno == EEXIST) { + log_error ("WARNING: Overwriting trace file \"%s\"", filename); + traceoutfile = open (filename, O_WRONLY | O_EXCL | O_TRUNC, + 0600); + } + + if (traceoutfile < 0) { + log_error ("%s(%d): trace_begin: %s: %m", + file, line, filename); + return ISC_R_UNEXPECTED; + } +#if defined (HAVE_SETFD) + if (fcntl (traceoutfile, F_SETFD, 1) < 0) + log_error ("Can't set close-on-exec on %s: %m", filename); +#endif + + tfh.magic = htonl (TRACEFILE_MAGIC); + tfh.version = htonl (TRACEFILE_VERSION); + tfh.hlen = htonl (sizeof (tracefile_header_t)); + tfh.phlen = htonl (sizeof (tracepacket_t)); + + status = write (traceoutfile, &tfh, sizeof tfh); + if (status < 0) { + log_error ("%s(%d): trace_begin write failed: %m", file, line); + return ISC_R_UNEXPECTED; + } else if (status != sizeof tfh) { + log_error ("%s(%d): trace_begin: short write (%d:%ld)", + file, line, status, (long)(sizeof tfh)); + trace_stop (); + return ISC_R_UNEXPECTED; + } + + /* Stash all the types that have already been set up. */ + if (new_trace_types) { + next = new_trace_types; + new_trace_types = (trace_type_t *)0; + for (tptr = next; tptr; tptr = next) { + next = tptr -> next; + if (tptr -> index != 0) { + result = (trace_type_record + (tptr, + strlen (tptr -> name), file, line)); + if (result != ISC_R_SUCCESS) + return status; + } + } + } + + return ISC_R_SUCCESS; +} + +isc_result_t trace_write_packet (trace_type_t *ttype, unsigned length, + const char *buf, const char *file, int line) +{ + trace_iov_t iov; + + iov.buf = buf; + iov.len = length; + return trace_write_packet_iov (ttype, 1, &iov, file, line); +} + +isc_result_t trace_write_packet_iov (trace_type_t *ttype, + int count, trace_iov_t *iov, + const char *file, int line) +{ + tracepacket_t tmp; + int status; + int i; + int length; + + /* Really shouldn't get called here, but it may be hard to turn off + tracing midstream if the trace file write fails or something. */ + if (tracing_stopped) + return 0; + + if (!ttype) { + log_error ("%s(%d): trace_write_packet with null trace type", + file ? file : "<unknown file>", line); + return DHCP_R_INVALIDARG; + } + if (!traceoutfile) { + log_error ("%s(%d): trace_write_packet with no tracefile.", + file ? file : "<unknown file>", line); + return DHCP_R_INVALIDARG; + } + + /* Compute the total length of the iov. */ + length = 0; + for (i = 0; i < count; i++) + length += iov [i].len; + + /* We have to swap out the data, because it may be read back on a + machine of different endianness. */ + memset(&tmp, 0, sizeof(tmp)); + tmp.type_index = htonl (ttype -> index); + tmp.when = htonl (time ((time_t *)0)); /* XXX */ + tmp.length = htonl (length); + + status = write (traceoutfile, &tmp, sizeof tmp); + if (status < 0) { + log_error ("%s(%d): trace_write_packet write failed: %m", + file, line); + return ISC_R_UNEXPECTED; + } else if (status != sizeof tmp) { + log_error ("%s(%d): trace_write_packet: short write (%d:%ld)", + file, line, status, (long)(sizeof tmp)); + trace_stop (); + } + + for (i = 0; i < count; i++) { + status = write (traceoutfile, iov [i].buf, iov [i].len); + if (status < 0) { + log_error ("%s(%d): %s write failed: %m", + file, line, "trace_write_packet"); + return ISC_R_UNEXPECTED; + } else if (status != iov [i].len) { + log_error ("%s(%d): %s: short write (%d:%d)", + file, line, + "trace_write_packet", status, length); + trace_stop (); + } + } + + /* Write padding on the end of the packet to align the next + packet to an 8-byte boundary. This is in case we decide to + use mmap in some clever way later on. */ + if (length % 8) { + static char zero [] = { 0, 0, 0, 0, 0, 0, 0 }; + unsigned padl = 8 - (length % 8); + + status = write (traceoutfile, zero, padl); + if (status < 0) { + log_error ("%s(%d): trace_write_packet write failed: %m", + file, line); + return ISC_R_UNEXPECTED; + } else if (status != padl) { + log_error ("%s(%d): trace_write_packet: short write (%d:%d)", + file, line, status, padl); + trace_stop (); + } + } + + return ISC_R_SUCCESS; +} + +void trace_type_stash (trace_type_t *tptr) +{ + trace_type_t **vec; + int delta; + if (trace_type_max <= tptr -> index) { + delta = tptr -> index - trace_type_max + 10; + vec = dmalloc (((trace_type_max + delta) * + sizeof (trace_type_t *)), MDL); + if (!vec) + return; + memset (&vec [trace_type_max], 0, + (sizeof (trace_type_t *)) * delta); + trace_type_max += delta; + if (trace_types) { + memcpy (vec, trace_types, + trace_type_count * sizeof (trace_type_t *)); + dfree (trace_types, MDL); + } + trace_types = vec; + } + trace_types [tptr -> index] = tptr; + if (tptr -> index >= trace_type_count) + trace_type_count = tptr -> index + 1; +} + +trace_type_t *trace_type_register (const char *name, + void *baggage, + void (*have_packet) (trace_type_t *, + unsigned, char *), + void (*stop_tracing) (trace_type_t *), + const char *file, int line) +{ + trace_type_t *ttmp; + unsigned slen = strlen (name); + isc_result_t status; + + ttmp = dmalloc (sizeof *ttmp, file, line); + if (!ttmp) + return ttmp; + ttmp -> index = -1; + ttmp -> name = dmalloc (slen + 1, file, line); + if (!ttmp -> name) { + dfree (ttmp, file, line); + return (trace_type_t *)0; + } + strcpy (ttmp -> name, name); + ttmp -> have_packet = have_packet; + ttmp -> stop_tracing = stop_tracing; + + if (traceoutfile) { + status = trace_type_record (ttmp, slen, file, line); + if (status != ISC_R_SUCCESS) { + dfree (ttmp -> name, file, line); + dfree (ttmp, file, line); + return (trace_type_t *)0; + } + } else { + ttmp -> next = new_trace_types; + new_trace_types = ttmp; + } + + return ttmp; +} + +static isc_result_t trace_type_record (trace_type_t *ttmp, unsigned slen, + const char *file, int line) +{ + trace_index_mapping_t *tim; + isc_result_t status; + + tim = dmalloc (slen + TRACE_INDEX_MAPPING_SIZE, file, line); + if (!tim) + return ISC_R_NOMEMORY; + ttmp -> index = ++traceindex; + trace_type_stash (ttmp); + tim -> index = htonl (ttmp -> index); + memcpy (tim -> name, ttmp -> name, slen); + status = trace_write_packet (trace_types [0], + slen + TRACE_INDEX_MAPPING_SIZE, + (char *)tim, file, line); + dfree (tim, file, line); + return status; +} + +/* Stop all registered trace types from trying to trace. */ + +void trace_stop (void) +{ + int i; + + for (i = 0; i < trace_type_count; i++) + if (trace_types [i] -> stop_tracing) + (*(trace_types [i] -> stop_tracing)) + (trace_types [i]); + tracing_stopped = 1; +} + +void trace_index_map_input (trace_type_t *ttype, unsigned length, char *buf) +{ + trace_index_mapping_t *tmap; + unsigned len; + trace_type_t *tptr, **prev; + + if (length < TRACE_INDEX_MAPPING_SIZE) { + log_error ("short trace index mapping"); + return; + } + tmap = (trace_index_mapping_t *)buf; + + prev = &new_trace_types; + for (tptr = new_trace_types; tptr; tptr = tptr -> next) { + len = strlen (tptr -> name); + if (len == length - TRACE_INDEX_MAPPING_SIZE && + !memcmp (tptr -> name, tmap -> name, len)) { + tptr -> index = ntohl (tmap -> index); + trace_type_stash (tptr); + *prev = tptr -> next; + return; + } + prev = &tptr -> next; + } + + log_error ("No registered trace type for type name %.*s", + (int)length - TRACE_INDEX_MAPPING_SIZE, tmap -> name); + return; +} + +void trace_index_stop_tracing (trace_type_t *ttype) { } + +void trace_replay_init (void) +{ + trace_playback_flag = 1; +} + +void trace_file_replay (const char *filename) +{ + tracepacket_t *tpkt = NULL; + int status; + char *buf = NULL; + unsigned buflen; + unsigned bufmax = 0; + trace_type_t *ttype = NULL; + isc_result_t result; + int len; + + traceinfile = fopen (filename, "r"); + if (!traceinfile) { + log_error("Can't open tracefile %s: %m", filename); + return; + } +#if defined (HAVE_SETFD) + if (fcntl (fileno(traceinfile), F_SETFD, 1) < 0) + log_error("Can't set close-on-exec on %s: %m", filename); +#endif + status = fread(&tracefile_header, 1, + sizeof tracefile_header, traceinfile); + if (status < sizeof tracefile_header) { + if (ferror(traceinfile)) + log_error("Error reading trace file header: %m"); + else + log_error("Short read on trace file header: %d %ld.", + status, (long)(sizeof tracefile_header)); + goto out; + } + tracefile_header.magic = ntohl(tracefile_header.magic); + tracefile_header.version = ntohl(tracefile_header.version); + tracefile_header.hlen = ntohl(tracefile_header.hlen); + tracefile_header.phlen = ntohl(tracefile_header.phlen); + + if (tracefile_header.magic != TRACEFILE_MAGIC) { + log_error("%s: not a dhcp trace file.", filename); + goto out; + } + if (tracefile_header.version > TRACEFILE_VERSION) { + log_error ("tracefile version %ld > current %ld.", + (long int)tracefile_header.version, + (long int)TRACEFILE_VERSION); + goto out; + } + if (tracefile_header.phlen < sizeof *tpkt) { + log_error("tracefile packet size too small - %ld < %ld", + (long int)tracefile_header.phlen, + (long int)sizeof *tpkt); + goto out; + } + len = (sizeof tracefile_header) - tracefile_header.hlen; + if (len < 0) { + log_error("tracefile header size too small - %ld < %ld", + (long int)tracefile_header.hlen, + (long int)sizeof tracefile_header); + goto out; + } + if (len > 0) { + status = fseek(traceinfile, (long)len, SEEK_CUR); + if (status < 0) { + log_error("can't seek past header: %m"); + goto out; + } + } + + tpkt = dmalloc((unsigned)tracefile_header.phlen, MDL); + if (tpkt == NULL) { + log_error ("can't allocate trace packet header."); + goto out; + } + + while ((result = trace_get_next_packet(&ttype, tpkt, &buf, &buflen, + &bufmax)) == ISC_R_SUCCESS) { + (*ttype->have_packet)(ttype, tpkt->length, buf); + ttype = NULL; + } + out: + fclose(traceinfile); + if (buf != NULL) + dfree(buf, MDL); + if (tpkt != NULL) + dfree(tpkt, MDL); +} + +/* Get the next packet from the file. If ttp points to a nonzero pointer + to a trace type structure, check the next packet to see if it's of the + expected type, and back off if not. */ + +isc_result_t trace_get_next_packet (trace_type_t **ttp, + tracepacket_t *tpkt, + char **buf, unsigned *buflen, + unsigned *bufmax) +{ + trace_type_t *ttype; + unsigned paylen; + int status, curposok = 0; + fpos_t curpos; + + while(1) { + curposok = 0; + status = fgetpos(traceinfile, &curpos); + if (status < 0) { + log_error("Can't save tracefile position: %m"); + } else { + curposok = 1; + } + + status = fread(tpkt, 1, (size_t)tracefile_header.phlen, + traceinfile); + if (status < tracefile_header.phlen) { + if (ferror(traceinfile)) + log_error("Error reading trace packet header: " + "%m"); + else if (status == 0) + return ISC_R_EOF; + else + log_error ("Short read on trace packet header:" + " %ld %ld.", + (long int)status, + (long int)tracefile_header.phlen); + return DHCP_R_PROTOCOLERROR; + } + + /* Swap the packet. */ + tpkt->type_index = ntohl(tpkt -> type_index); + tpkt->length = ntohl(tpkt -> length); + tpkt->when = ntohl(tpkt -> when); + + /* See if there's a handler for this packet type. */ + if (tpkt->type_index < trace_type_count && + trace_types[tpkt->type_index]) + ttype = trace_types[tpkt->type_index]; + else { + log_error ("Trace packet with unknown index %ld", + (long int)tpkt->type_index); + return DHCP_R_PROTOCOLERROR; + } + + /* + * Determine if we should try to expire any timer events. + * We do so if: + * we aren't looking for a specific type of packet + * we have a hook to use to update the timer + * the timestamp on the packet doesn't match the current time + * When we do so we rewind the file to the beginning of this + * packet and then try for a new packet. This allows + * any code triggered by a timeout to get the current packet + * while we get the next one. + */ + + if ((ttp != NULL) && (*ttp == NULL) && + (tpkt->when != cur_tv.tv_sec) && + (trace_set_time_hook != NULL)) { + if (curposok == 0) { + log_error("no curpos for fsetpos in " + "tracefile"); + return DHCP_R_PROTOCOLERROR; + } + + status = fsetpos(traceinfile, &curpos); + if (status < 0) { + log_error("fsetpos in tracefile failed: %m"); + return DHCP_R_PROTOCOLERROR; + } + + (*trace_set_time_hook) (tpkt->when); + continue; + } + break; + } + + /* If we were supposed to get a particular kind of packet, + check to see that we got the right kind. */ + if (ttp && *ttp && ttype != *ttp) { + log_error ("Read packet type %s when expecting %s", + ttype -> name, (*ttp) -> name); + status = fsetpos (traceinfile, &curpos); + if (status < 0) { + log_error ("fsetpos in tracefile failed: %m"); + return DHCP_R_PROTOCOLERROR; + } + return ISC_R_UNEXPECTEDTOKEN; + } + + paylen = tpkt -> length; + if (paylen % 8) + paylen += 8 - (tpkt -> length % 8); + + /* allocate a buffer if we need one or current buffer is too small */ + if ((*buf == NULL) || (paylen > (*bufmax))) { + if ((*buf)) + dfree ((*buf), MDL); + (*bufmax) = ((paylen + 1023) & ~1023U); + (*buf) = dmalloc ((*bufmax), MDL); + if (!(*buf)) { + log_error ("Can't allocate input buffer sized %d", + (*bufmax)); + return ISC_R_NOMEMORY; + } + } + + status = fread ((*buf), 1, paylen, traceinfile); + if (status < paylen) { + if (ferror (traceinfile)) + log_error ("Error reading trace payload: %m"); + else + log_error ("Short read on trace payload: %d %d.", + status, paylen); + return DHCP_R_PROTOCOLERROR; + } + + /* Store the actual length of the payload. */ + *buflen = tpkt -> length; + + if (ttp) + *ttp = ttype; + return ISC_R_SUCCESS; +} + +isc_result_t trace_get_packet (trace_type_t **ttp, + unsigned *buflen, char **buf) +{ + tracepacket_t *tpkt; + unsigned bufmax = 0; + isc_result_t status; + + if (!buf || *buf) + return DHCP_R_INVALIDARG; + + tpkt = dmalloc ((unsigned)tracefile_header.phlen, MDL); + if (!tpkt) { + log_error ("can't allocate trace packet header."); + return ISC_R_NOMEMORY; + } + + status = trace_get_next_packet (ttp, tpkt, buf, buflen, &bufmax); + + dfree (tpkt, MDL); + return status; +} + +/* Get a packet from the trace input file that contains a file with the + specified name. We don't hunt for the packet - it should be the next + packet in the tracefile. If it's not, or something else bad happens, + return an error code. */ + +isc_result_t trace_get_file (trace_type_t *ttype, + const char *filename, unsigned *len, char **buf) +{ + fpos_t curpos; + unsigned max = 0; + tracepacket_t *tpkt; + int status; + isc_result_t result; + + /* Disallow some obvious bogosities. */ + if (!buf || !len || *buf) + return DHCP_R_INVALIDARG; + + /* Save file position in case of filename mismatch. */ + status = fgetpos (traceinfile, &curpos); + if (status < 0) + log_error ("Can't save tracefile position: %m"); + + tpkt = dmalloc ((unsigned)tracefile_header.phlen, MDL); + if (!tpkt) { + log_error ("can't allocate trace packet header."); + return ISC_R_NOMEMORY; + } + + result = trace_get_next_packet (&ttype, tpkt, buf, len, &max); + /* done with tpkt, free it */ + dfree (tpkt, MDL); + if (result != ISC_R_SUCCESS) { + if (*buf) { + dfree (*buf, MDL); + *buf = NULL; + } + return result; + } + + /* Make sure the filename is right. */ + if (strcmp (filename, *buf)) { + log_error ("Read file %s when expecting %s", *buf, filename); + dfree (*buf, MDL); + *buf = NULL; + + status = fsetpos (traceinfile, &curpos); + if (status < 0) { + log_error ("fsetpos in tracefile failed: %m"); + return DHCP_R_PROTOCOLERROR; + } + return ISC_R_UNEXPECTEDTOKEN; + } + + return ISC_R_SUCCESS; +} +#endif /* TRACING */ |