summaryrefslogtreecommitdiff
path: root/keyexchange/isakmpd-20041012/log.c
diff options
context:
space:
mode:
Diffstat (limited to 'keyexchange/isakmpd-20041012/log.c')
-rw-r--r--keyexchange/isakmpd-20041012/log.c706
1 files changed, 706 insertions, 0 deletions
diff --git a/keyexchange/isakmpd-20041012/log.c b/keyexchange/isakmpd-20041012/log.c
new file mode 100644
index 0000000..5d8080c
--- /dev/null
+++ b/keyexchange/isakmpd-20041012/log.c
@@ -0,0 +1,706 @@
+/* $OpenBSD: log.c,v 1.49 2004/08/08 19:11:06 deraadt Exp $ */
+/* $EOM: log.c,v 1.30 2000/09/29 08:19:23 niklas Exp $ */
+
+/*
+ * Copyright (c) 1998, 1999, 2001 Niklas Hallqvist. All rights reserved.
+ * Copyright (c) 1999, 2000, 2001, 2003 Håkan Olsson. 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 ``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 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 code was written under funding by Ericsson Radio Systems.
+ */
+
+#include <sys/types.h>
+#include <sys/time.h>
+
+#ifdef USE_DEBUG
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+#include <netinet/udp.h>
+#include <arpa/inet.h>
+
+#ifdef HAVE_PCAP
+#include <pcap.h>
+#else
+#include "sysdep/common/pcap.h"
+#endif
+
+#endif /* USE_DEBUG */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <stdarg.h>
+#include <unistd.h>
+
+#include "conf.h"
+#include "isakmp_num.h"
+#include "log.h"
+#include "monitor.h"
+#include "util.h"
+
+static void _log_print(int, int, const char *, va_list, int, int);
+
+static FILE *log_output;
+
+int verbose_logging = 0;
+#if defined (USE_DEBUG)
+static int log_level[LOG_ENDCLASS];
+
+#define TCPDUMP_MAGIC 0xa1b2c3d4
+#define SNAPLEN (64 * 1024)
+
+struct packhdr {
+ struct pcap_pkthdr pcap;/* pcap file packet header */
+ union {
+ struct ip ip4; /* IPv4 header (w/o options) */
+ struct ip6_hdr ip6; /* IPv6 header */
+ } ip;
+};
+
+struct isakmp_hdr {
+ u_int8_t icookie[8], rcookie[8];
+ u_int8_t next, ver, type, flags;
+ u_int32_t msgid, len;
+};
+
+static char *pcaplog_file = NULL;
+static FILE *packet_log;
+static u_int8_t *packet_buf = NULL;
+
+static int udp_cksum(struct packhdr *, const struct udphdr *,
+ u_int16_t *, int);
+static u_int16_t in_cksum(const u_int16_t *, int);
+#endif /* USE_DEBUG */
+
+void
+log_init(int debug)
+{
+ if (debug)
+ log_output = stderr;
+ else
+ log_to(0); /* syslog */
+}
+
+void
+log_reinit(void)
+{
+ struct conf_list *logging;
+#ifdef USE_DEBUG
+ struct conf_list_node *logclass;
+ int class, level;
+#endif /* USE_DEBUG */
+
+ logging = conf_get_list("General", "Logverbose");
+ if (logging) {
+ verbose_logging = 1;
+ conf_free_list(logging);
+ }
+#ifdef USE_DEBUG
+ logging = conf_get_list("General", "Loglevel");
+ if (!logging)
+ return;
+
+ for (logclass = TAILQ_FIRST(&logging->fields); logclass;
+ logclass = TAILQ_NEXT(logclass, link)) {
+ if (sscanf(logclass->field, "%d=%d", &class, &level) != 2) {
+ if (sscanf(logclass->field, "A=%d", &level) == 1)
+ for (class = 0; class < LOG_ENDCLASS; class++)
+ log_debug_cmd(class, level);
+ else {
+ log_print("init: invalid logging class or "
+ "level: %s", logclass->field);
+ continue;
+ }
+ } else
+ log_debug_cmd(class, level);
+ }
+ conf_free_list(logging);
+#endif /* USE_DEBUG */
+}
+
+void
+log_to(FILE *f)
+{
+ if (!log_output && f)
+ closelog();
+ log_output = f;
+ if (!f)
+ openlog("isakmpd", LOG_PID | LOG_CONS, LOG_DAEMON);
+}
+
+FILE *
+log_current(void)
+{
+ return log_output;
+}
+
+static char *
+_log_get_class(int error_class)
+{
+ /* XXX For test purposes. To be removed later on? */
+ static char *class_text[] = LOG_CLASSES_TEXT;
+
+ if (error_class < 0)
+ return "Dflt";
+ else if (error_class >= LOG_ENDCLASS)
+ return "Unkn";
+ else
+ return class_text[error_class];
+}
+
+static void
+_log_print(int error, int syslog_level, const char *fmt, va_list ap,
+ int class, int level)
+{
+ char buffer[LOG_SIZE], nbuf[LOG_SIZE + 32];
+ static const char fallback_msg[] =
+ "write to log file failed (errno %d), redirecting to syslog";
+ int len;
+ struct tm *tm;
+ struct timeval now;
+ time_t t;
+
+ len = vsnprintf(buffer, sizeof buffer, fmt, ap);
+ if (len > 0 && len < (int) sizeof buffer - 1 && error)
+ snprintf(buffer + len, sizeof buffer - len, ": %s",
+ strerror(errno));
+ if (log_output) {
+ gettimeofday(&now, 0);
+ t = now.tv_sec;
+ tm = localtime(&t);
+ if (class >= 0)
+ snprintf(nbuf, sizeof nbuf,
+ "%02d%02d%02d.%06ld %s %02d ",
+ tm->tm_hour, tm->tm_min, tm->tm_sec, now.tv_usec,
+ _log_get_class(class), level);
+ else /* LOG_PRINT (-1) or LOG_REPORT (-2) */
+ snprintf(nbuf, sizeof nbuf, "%02d%02d%02d.%06ld %s ",
+ tm->tm_hour, tm->tm_min, tm->tm_sec, now.tv_usec,
+ class == LOG_PRINT ? "Default" : "Report>");
+ strlcat(nbuf, buffer, sizeof nbuf);
+#if defined (USE_PRIVSEP)
+ strlcat(nbuf, getuid() ? "" : " [priv]", LOG_SIZE + 32);
+#endif
+ strlcat(nbuf, "\n", sizeof nbuf);
+
+ if (fwrite(nbuf, strlen(nbuf), 1, log_output) == 0) {
+ /* Report fallback. */
+ syslog(LOG_ALERT, fallback_msg, errno);
+ fprintf(log_output, fallback_msg, errno);
+
+ /*
+ * Close log_output to prevent isakmpd from locking
+ * the file. We may need to explicitly close stdout
+ * to do this properly.
+ * XXX - Figure out how to match two FILE *'s and
+ * rewrite.
+ */
+ if (fileno(log_output) != -1 &&
+ fileno(stdout) == fileno(log_output))
+ fclose(stdout);
+ fclose(log_output);
+
+ /* Fallback to syslog. */
+ log_to(0);
+
+ /* (Re)send current message to syslog(). */
+ syslog(class == LOG_REPORT ? LOG_ALERT :
+ syslog_level, "%s", buffer);
+ }
+ } else
+ syslog(class == LOG_REPORT ? LOG_ALERT : syslog_level, "%s",
+ buffer);
+}
+
+#ifdef USE_DEBUG
+void
+log_debug(int cls, int level, const char *fmt, ...)
+{
+ va_list ap;
+
+ /*
+ * If we are not debugging this class, or the level is too low, just
+ * return.
+ */
+ if (cls >= 0 && (log_level[cls] == 0 || level > log_level[cls]))
+ return;
+ va_start(ap, fmt);
+ _log_print(0, LOG_INFO, fmt, ap, cls, level);
+ va_end(ap);
+}
+
+void
+log_debug_buf(int cls, int level, const char *header, const u_int8_t *buf,
+ size_t sz)
+{
+ size_t i, j;
+ char s[73];
+
+ /*
+ * If we are not debugging this class, or the level is too low, just
+ * return.
+ */
+ if (cls >= 0 && (log_level[cls] == 0 || level > log_level[cls]))
+ return;
+
+ log_debug(cls, level, "%s:", header);
+ for (i = j = 0; i < sz;) {
+ snprintf(s + j, sizeof s - j, "%02x", buf[i++]);
+ j += 2;
+ if (i % 4 == 0) {
+ if (i % 32 == 0) {
+ s[j] = '\0';
+ log_debug(cls, level, "%s", s);
+ j = 0;
+ } else
+ s[j++] = ' ';
+ }
+ }
+ if (j) {
+ s[j] = '\0';
+ log_debug(cls, level, "%s", s);
+ }
+}
+
+void
+log_debug_cmd(int cls, int level)
+{
+ if (cls < 0 || cls >= LOG_ENDCLASS) {
+ log_print("log_debug_cmd: invalid debugging class %d", cls);
+ return;
+ }
+ if (level < 0) {
+ log_print("log_debug_cmd: invalid debugging level %d for "
+ "class %d", level, cls);
+ return;
+ }
+ if (level == log_level[cls])
+ log_print("log_debug_cmd: log level unchanged for class %d",
+ cls);
+ else {
+ log_print("log_debug_cmd: log level changed from %d to %d "
+ "for class %d", log_level[cls], level, cls);
+ log_level[cls] = level;
+ }
+}
+
+void
+log_debug_toggle(void)
+{
+ static int log_level_copy[LOG_ENDCLASS], toggle = 0;
+
+ if (!toggle) {
+ LOG_DBG((LOG_MISC, 50, "log_debug_toggle: "
+ "debug levels cleared"));
+ memcpy(&log_level_copy, &log_level, sizeof log_level);
+ memset(&log_level, 0, sizeof log_level);
+ } else {
+ memcpy(&log_level, &log_level_copy, sizeof log_level);
+ LOG_DBG((LOG_MISC, 50, "log_debug_toggle: "
+ "debug levels restored"));
+ }
+ toggle = !toggle;
+}
+#endif /* USE_DEBUG */
+
+void
+log_print(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ _log_print(0, LOG_NOTICE, fmt, ap, LOG_PRINT, 0);
+ va_end(ap);
+}
+
+void
+log_verbose(const char *fmt, ...)
+{
+ va_list ap;
+#ifdef USE_DEBUG
+ int i;
+#endif /* USE_DEBUG */
+
+ if (verbose_logging == 0)
+ return;
+
+#ifdef USE_DEBUG
+ for (i = 0; i < LOG_ENDCLASS; i++)
+ if (log_level[i] > 0)
+ return;
+#endif
+
+ va_start(ap, fmt);
+ _log_print(0, LOG_NOTICE, fmt, ap, LOG_PRINT, 0);
+ va_end(ap);
+}
+
+void
+log_error(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ _log_print(1, LOG_ERR, fmt, ap, LOG_PRINT, 0);
+ va_end(ap);
+}
+
+void
+log_fatal(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ _log_print(1, LOG_CRIT, fmt, ap, LOG_PRINT, 0);
+ va_end(ap);
+#ifdef USE_PRIVSEP
+ monitor_exit(1);
+#else
+ exit(1);
+#endif
+}
+
+#ifdef USE_DEBUG
+void
+log_packet_init(char *newname)
+{
+ struct pcap_file_header sf_hdr;
+ struct stat st;
+ mode_t old_umask;
+ char *mode;
+
+ /* Allocate packet buffer first time through. */
+ if (!packet_buf)
+ packet_buf = malloc(SNAPLEN);
+
+ if (!packet_buf) {
+ log_error("log_packet_init: malloc (%d) failed", SNAPLEN);
+ return;
+ }
+ if (pcaplog_file && strcmp(pcaplog_file, PCAP_FILE_DEFAULT) != 0)
+ free(pcaplog_file);
+
+ pcaplog_file = strdup(newname);
+ if (!pcaplog_file) {
+ log_error("log_packet_init: strdup (\"%s\") failed", newname);
+ return;
+ }
+ /* Does the file already exist? XXX lstat() or stat()? */
+#if defined (USE_PRIVSEP)
+ /* XXX This is a fstat! */
+ if (monitor_stat(pcaplog_file, &st) == 0) {
+#else
+ if (lstat(pcaplog_file, &st) == 0) {
+#endif
+ /* Sanity checks. */
+ if ((st.st_mode & S_IFMT) != S_IFREG) {
+ log_print("log_packet_init: existing capture file is "
+ "not a regular file");
+ return;
+ }
+ if ((st.st_mode & (S_IRWXG | S_IRWXO)) != 0) {
+ log_print("log_packet_init: existing capture "
+ "file has bad modes");
+ return;
+ }
+ /*
+ * XXX It would be nice to check if it actually is a pcap
+ * file...
+ */
+
+ mode = "a";
+ } else
+ mode = "w";
+
+ old_umask = umask(S_IRWXG | S_IRWXO);
+ packet_log = monitor_fopen(pcaplog_file, mode);
+ umask(old_umask);
+
+ if (!packet_log) {
+ log_error("log_packet_init: fopen (\"%s\", \"%s\") failed",
+ pcaplog_file, mode);
+ return;
+ }
+ log_print("log_packet_init: "
+ "starting IKE packet capture to file \"%s\"", pcaplog_file);
+
+ /* If this is a new file, we need to write a PCAP header to it. */
+ if (*mode == 'w') {
+ sf_hdr.magic = TCPDUMP_MAGIC;
+ sf_hdr.version_major = PCAP_VERSION_MAJOR;
+ sf_hdr.version_minor = PCAP_VERSION_MINOR;
+ sf_hdr.thiszone = 0;
+ sf_hdr.snaplen = SNAPLEN;
+ sf_hdr.sigfigs = 0;
+ sf_hdr.linktype = DLT_LOOP;
+
+ fwrite((char *) &sf_hdr, sizeof sf_hdr, 1, packet_log);
+ fflush(packet_log);
+ }
+}
+
+void
+log_packet_restart(char *newname)
+{
+ if (packet_log) {
+ log_print("log_packet_restart: capture already active on "
+ "file \"%s\"", pcaplog_file);
+ return;
+ }
+ if (newname)
+ log_packet_init(newname);
+ else if (!pcaplog_file)
+ log_packet_init(PCAP_FILE_DEFAULT);
+ else
+ log_packet_init(pcaplog_file);
+}
+
+void
+log_packet_stop(void)
+{
+ /* Stop capture. */
+ if (packet_log) {
+ fclose(packet_log);
+ log_print("log_packet_stop: stopped capture");
+ }
+ packet_log = 0;
+}
+
+void
+log_packet_iov(struct sockaddr *src, struct sockaddr *dst, struct iovec *iov,
+ int iovcnt)
+{
+ struct isakmp_hdr *isakmphdr;
+ struct packhdr hdr;
+ struct udphdr udp;
+ struct timeval tv;
+ int off, datalen, hdrlen, i, add_espmarker = 0;
+ const u_int32_t espmarker = 0;
+
+ for (i = 0, datalen = 0; i < iovcnt; i++)
+ datalen += iov[i].iov_len;
+
+ if (!packet_log || datalen > SNAPLEN)
+ return;
+
+ /* copy packet into buffer */
+ for (i = 0, off = 0; i < iovcnt; i++) {
+ memcpy(packet_buf + off, iov[i].iov_base, iov[i].iov_len);
+ off += iov[i].iov_len;
+ }
+
+ memset(&hdr, 0, sizeof hdr);
+ memset(&udp, 0, sizeof udp);
+
+ /* isakmp - turn off the encryption bit in the isakmp hdr */
+ isakmphdr = (struct isakmp_hdr *) packet_buf;
+ isakmphdr->flags &= ~(ISAKMP_FLAGS_ENC);
+
+ /* udp */
+ udp.uh_sport = sockaddr_port(src);
+ udp.uh_dport = sockaddr_port(dst);
+ datalen += sizeof udp;
+#if defined (USE_NAT_TRAVERSAL)
+ if (ntohs(udp.uh_sport) == 4500 ||
+ ntohs(udp.uh_dport) == 4500) { /* XXX Quick and dirty */
+ add_espmarker = 1;
+ datalen += sizeof espmarker;
+ }
+#endif
+ udp.uh_ulen = htons(datalen);
+
+ /* ip */
+ switch (src->sa_family) {
+ default:
+ /* Assume IPv4. XXX Can 'default' ever happen here? */
+ hdr.ip.ip4.ip_src.s_addr = 0x02020202;
+ hdr.ip.ip4.ip_dst.s_addr = 0x01010101;
+ /* The rest of the setup is common to AF_INET. */
+ goto setup_ip4;
+
+ case AF_INET:
+ hdr.ip.ip4.ip_src.s_addr =
+ ((struct sockaddr_in *)src)->sin_addr.s_addr;
+ hdr.ip.ip4.ip_dst.s_addr =
+ ((struct sockaddr_in *)dst)->sin_addr.s_addr;
+
+setup_ip4:
+ hdrlen = sizeof hdr.ip.ip4;
+ hdr.ip.ip4.ip_v = 0x4;
+ hdr.ip.ip4.ip_hl = 0x5;
+ hdr.ip.ip4.ip_p = IPPROTO_UDP;
+ hdr.ip.ip4.ip_len = htons(datalen + hdrlen);
+ /* Let's use the IP ID as a "packet counter". */
+ i = ntohs(hdr.ip.ip4.ip_id) + 1;
+ hdr.ip.ip4.ip_id = htons(i);
+ /* Calculate IP header checksum. */
+ hdr.ip.ip4.ip_sum = in_cksum((u_int16_t *) & hdr.ip.ip4,
+ hdr.ip.ip4.ip_hl << 2);
+ break;
+
+ case AF_INET6:
+ hdrlen = sizeof(hdr.ip.ip6);
+ hdr.ip.ip6.ip6_vfc = IPV6_VERSION;
+ hdr.ip.ip6.ip6_nxt = IPPROTO_UDP;
+ hdr.ip.ip6.ip6_plen = udp.uh_ulen;
+ memcpy(&hdr.ip.ip6.ip6_src,
+ &((struct sockaddr_in6 *)src)->sin6_addr,
+ sizeof hdr.ip.ip6.ip6_src);
+ memcpy(&hdr.ip.ip6.ip6_dst,
+ &((struct sockaddr_in6 *)dst)->sin6_addr,
+ sizeof hdr.ip.ip6.ip6_dst);
+ break;
+ }
+
+ /* Calculate UDP checksum. */
+ udp.uh_sum = udp_cksum(&hdr, &udp, (u_int16_t *) packet_buf, src->sa_family);
+ /* pcap file packet header */
+ gettimeofday(&tv, 0);
+ hdr.pcap.ts.tv_sec = tv.tv_sec;
+ hdr.pcap.ts.tv_usec = tv.tv_usec;
+ hdr.pcap.caplen = datalen + hdrlen;
+ hdr.pcap.len = datalen + hdrlen;
+
+ hdrlen += sizeof(struct pcap_pkthdr);
+ datalen -= sizeof(struct udphdr);
+
+ /* Write to pcap file. */
+ fwrite(&hdr, hdrlen, 1, packet_log); /* pcap + IP */
+ fwrite(&udp, sizeof(struct udphdr), 1, packet_log); /* UDP */
+ if (add_espmarker) {
+ fwrite(&espmarker, sizeof espmarker, 1, packet_log);
+ datalen -= sizeof espmarker;
+ }
+ fwrite(packet_buf, datalen, 1, packet_log); /* IKE-data */
+ fflush(packet_log);
+}
+
+/* Copied from tcpdump/print-udp.c, mostly rewritten. */
+static int
+udp_cksum(struct packhdr *hdr, const struct udphdr *u, u_int16_t *d, int af)
+{
+ struct ip *ip4;
+ struct ip6_hdr *ip6;
+ int i, hdrlen, tlen = ntohs(u->uh_ulen) - sizeof(struct udphdr);
+
+ union phu {
+ struct ip4pseudo {
+ struct in_addr src;
+ struct in_addr dst;
+ u_int8_t z;
+ u_int8_t proto;
+ u_int16_t len;
+ } ip4p;
+ struct ip6pseudo {
+ struct in6_addr src;
+ struct in6_addr dst;
+ u_int32_t plen;
+ u_int16_t z0;
+ u_int8_t z1;
+ u_int8_t nxt;
+ } ip6p;
+ u_int16_t pa[20];
+ } phu;
+ const u_int16_t *sp;
+ u_int32_t sum;
+
+ /* Setup pseudoheader. */
+ memset(phu.pa, 0, sizeof phu);
+ switch (af) {
+ case AF_INET:
+ ip4 = &hdr->ip.ip4;
+ memcpy(&phu.ip4p.src, &ip4->ip_src, sizeof(struct in_addr));
+ memcpy(&phu.ip4p.dst, &ip4->ip_dst, sizeof(struct in_addr));
+ phu.ip4p.proto = ip4->ip_p;
+ phu.ip4p.len = u->uh_ulen;
+ hdrlen = sizeof phu.ip4p;
+ break;
+
+ case AF_INET6:
+ ip6 = &hdr->ip.ip6;
+ memcpy(&phu.ip6p.src, &ip6->ip6_src, sizeof(phu.ip6p.src));
+ memcpy(&phu.ip6p.dst, &ip6->ip6_dst, sizeof(phu.ip6p.dst));
+ phu.ip6p.plen = u->uh_ulen;
+ phu.ip6p.nxt = ip6->ip6_nxt;
+ hdrlen = sizeof phu.ip6p;
+ break;
+
+ default:
+ return 0;
+ }
+
+ /* IPv6 wants a 0xFFFF checksum "on error", not 0x0. */
+ if (tlen < 0)
+ return (af == AF_INET ? 0 : 0xFFFF);
+
+ sum = 0;
+ for (i = 0; i < hdrlen; i += 2)
+ sum += phu.pa[i / 2];
+
+ sp = (u_int16_t *) u;
+ for (i = 0; i < (int)sizeof(struct udphdr); i += 2)
+ sum += *sp++;
+
+ sp = d;
+ for (i = 0; i < (tlen & ~1); i += 2)
+ sum += *sp++;
+
+ if (tlen & 1)
+ sum += htons((*(const char *)sp) << 8);
+
+ while (sum > 0xffff)
+ sum = (sum & 0xffff) + (sum >> 16);
+ sum = ~sum & 0xffff;
+
+ return sum;
+}
+
+/* Copied from tcpdump/print-ip.c, modified. */
+static u_int16_t
+in_cksum(const u_int16_t *w, int len)
+{
+ int nleft = len, sum = 0;
+ u_int16_t answer;
+
+ while (nleft > 1) {
+ sum += *w++;
+ nleft -= 2;
+ }
+ if (nleft == 1)
+ sum += htons(*(u_char *) w << 8);
+
+ sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
+ sum += (sum >> 16); /* add carry */
+ answer = ~sum; /* truncate to 16 bits */
+ return answer;
+}
+
+#endif /* USE_DEBUG */