]> git.ipfire.org Git - thirdparty/dhcp.git/commitdiff
Support for tracing omapi and related connections.
authorTed Lemon <source@isc.org>
Mon, 12 Feb 2001 20:48:04 +0000 (20:48 +0000)
committerTed Lemon <source@isc.org>
Mon, 12 Feb 2001 20:48:04 +0000 (20:48 +0000)
omapip/trace.c [new file with mode: 0644]

diff --git a/omapip/trace.c b/omapip/trace.c
new file mode 100644 (file)
index 0000000..19bbb63
--- /dev/null
@@ -0,0 +1,614 @@
+/* trace.c
+
+   Subroutines that support dhcp tracing... */
+
+/*
+ * Copyright (c) 2001 Internet Software Consortium.
+ * 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.
+ * 3. Neither the name of The Internet Software Consortium nor the names
+ *    of its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM 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 INTERNET SOFTWARE CONSORTIUM 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.
+ *
+ * This software has been written for the Internet Software Consortium
+ * by Ted Lemon, as part of a project for Nominum, Inc.   To learn more
+ * about the Internet Software Consortium, see http://www.isc.org/.  To
+ * learn more about Nominum, Inc., see ``http://www.nominum.com''.
+ */
+
+#include <omapip/omapip_p.h>
+
+#if defined (TRACING)
+void (*trace_set_time_hook) (u_int32_t);
+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;
+
+int trace_playback ()
+{
+       return trace_playback_flag;
+}
+
+int trace_record ()
+{
+       if (traceoutfile)
+               return 1;
+       return 0;
+}
+
+isc_result_t trace_init (void (*set_time) (u_int32_t),
+                        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;
+       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;
+
+       if (traceoutfile) {
+               log_error ("%s(%d): trace_begin called twice",
+                          file, line);
+               return ISC_R_INVALIDARG;
+       }
+
+       traceoutfile = open (filename, O_CREAT | O_WRONLY, 0644);
+       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:%d)",
+                          file, line, status, sizeof tfh);
+               trace_stop ();
+       }
+       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 ISC_R_INVALIDARG;
+       }
+       if (!traceoutfile) {
+               log_error ("%s(%d): trace_write_packet with no tracefile.",
+                          file ? file : "<unknown file>", line);
+               return ISC_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. */
+       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:%d)",
+                          file, line, status, 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, *tptr;
+       trace_index_mapping_t *tim;
+       unsigned slen = strlen (name);
+
+       if (traceoutfile) {
+               tim = dmalloc (slen + TRACE_INDEX_MAPPING_SIZE, file, line);
+               if (!tim)
+                       return (trace_type_t *)0;
+       }
+
+       ttmp = dmalloc (sizeof *ttmp, file, line);
+       if (!ttmp) {
+               dfree (tim, file, line);
+               return ttmp;
+       }
+       if (traceoutfile)
+               ttmp -> index = ++traceindex;
+       else
+               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 (ttmp) {
+               if (ttmp -> index != -1)
+                       trace_type_stash (ttmp);
+               else {
+                       ttmp -> next = new_trace_types;
+                       new_trace_types = ttmp;
+               }
+       }
+
+       /* Assemble the trace mapping to be written out to the trace file. */
+       if (traceoutfile) {
+               tim -> index = htonl (ttmp -> index);
+               memcpy (tim -> name, name, slen);
+               if (trace_write_packet (trace_types [0],
+                                       slen + TRACE_INDEX_MAPPING_SIZE,
+                                       (char *)tim, file, line) !=
+                   ISC_R_SUCCESS) {
+                       dfree (ttmp -> name, file, line);
+                       dfree (ttmp, file, line);
+                       ttmp = (trace_type_t *)0;
+               }
+               dfree (tim, file, line);
+       }
+
+       return ttmp;
+}
+                                                  
+/* 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 = (tracepacket_t *)0;
+       int status;
+       char *buf = (char *)0;
+       int buflen;
+       unsigned bufmax = 0;
+       trace_type_t *ttype = (trace_type_t *)0;
+       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 %d.",
+                                  status, 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 %d > current %d.",
+                          tracefile_header.version, TRACEFILE_VERSION);
+               goto out;
+       }
+       if (tracefile_header.phlen < sizeof *tpkt) {
+               log_error ("tracefile packet size too small - %d < %d",
+                          tracefile_header.phlen, sizeof *tpkt);
+               goto out;
+       }
+       len = (sizeof tracefile_header) - tracefile_header.hlen;
+       if (len < 0) {
+               log_error ("tracefile header size too small - %d < %d",
+                          tracefile_header.hlen, 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) {
+               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 = (trace_type_t *)0;
+       }
+      out:
+       fclose (traceinfile);
+       if (buf)
+               dfree (buf, MDL);
+       if (tpkt)
+               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;
+       int len;
+       fpos_t curpos;
+
+       status = fgetpos (traceinfile, &curpos);
+       if (status < 0)
+               log_error ("Can't save tracefile position: %m");
+
+       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: %d %d.",
+                                  status, tracefile_header.phlen);
+               return ISC_R_PROTOCOLERROR;
+       }
+
+       /* Swap the packet. */
+       tpkt -> type_index = ntohl (tpkt -> type_index);
+       tpkt -> length = ntohl (tpkt -> length);
+       tpkt -> when = ntohl (tpkt -> length);
+       
+       /* 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 %d",
+                          tpkt -> type_index);
+               return ISC_R_PROTOCOLERROR;
+       }
+
+       /* 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 ISC_R_PROTOCOLERROR;
+               }
+               return ISC_R_UNEXPECTEDTOKEN;
+       }
+
+       paylen = tpkt -> length;
+       if (paylen % 8)
+               paylen += 8 - (tpkt -> length % 8);
+       if (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 ISC_R_PROTOCOLERROR;
+       }
+
+       /* Store the actual length of the payload. */
+       *buflen = tpkt -> length;
+
+       if (trace_set_time_hook)
+               (*trace_set_time_hook) (tpkt -> when);
+
+       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 ISC_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 ISC_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);
+       if (result != ISC_R_SUCCESS) {
+               dfree (tpkt, MDL);
+               if (*buf)
+                       dfree (*buf, MDL);
+               return result;
+       }
+
+       /* Make sure the filename is right. */
+       if (strcmp (filename, *buf)) {
+               log_error ("Read file %s when expecting %s", *buf, filename);
+               status = fsetpos (traceinfile, &curpos);
+               if (status < 0) {
+                       log_error ("fsetpos in tracefile failed: %m");
+                       dfree (tpkt, MDL);
+                       dfree (*buf, MDL);
+                       return ISC_R_PROTOCOLERROR;
+               }
+               return ISC_R_UNEXPECTEDTOKEN;
+       }
+
+       dfree (tpkt, MDL);
+       return ISC_R_SUCCESS;
+}
+#endif /* TRACING */