log.c revision 1.53
1/* $OpenBSD: log.c,v 1.53 2005/04/08 16:14:04 deraadt Exp $	 */
2/* $EOM: log.c,v 1.30 2000/09/29 08:19:23 niklas Exp $	 */
3
4/*
5 * Copyright (c) 1998, 1999, 2001 Niklas Hallqvist.  All rights reserved.
6 * Copyright (c) 1999, 2000, 2001, 2003 H�kan Olsson.  All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/*
30 * This code was written under funding by Ericsson Radio Systems.
31 */
32
33#include <sys/types.h>
34#include <sys/time.h>
35
36#ifdef USE_DEBUG
37#include <sys/socket.h>
38#include <sys/stat.h>
39#include <sys/uio.h>
40#include <netinet/in.h>
41#include <netinet/in_systm.h>
42#include <netinet/ip.h>
43#include <netinet/ip6.h>
44#include <netinet/udp.h>
45#include <arpa/inet.h>
46
47#include <pcap.h>
48
49#endif				/* USE_DEBUG */
50
51#include <errno.h>
52#include <stdio.h>
53#include <stdlib.h>
54#include <string.h>
55#include <syslog.h>
56#include <stdarg.h>
57#include <unistd.h>
58
59#include "conf.h"
60#include "isakmp_num.h"
61#include "log.h"
62#include "monitor.h"
63#include "util.h"
64
65static void	_log_print(int, int, const char *, va_list, int, int);
66
67static FILE	*log_output;
68
69int		verbose_logging = 0;
70#if defined (USE_DEBUG)
71static int	log_level[LOG_ENDCLASS];
72
73#define TCPDUMP_MAGIC	0xa1b2c3d4
74#define SNAPLEN		(64 * 1024)
75
76struct packhdr {
77	struct pcap_pkthdr pcap;/* pcap file packet header */
78	u_int32_t sa_family;	/* address family */
79	union {
80		struct ip       ip4;	/* IPv4 header (w/o options) */
81		struct ip6_hdr  ip6;	/* IPv6 header */
82	} ip;
83};
84
85struct isakmp_hdr {
86	u_int8_t        icookie[8], rcookie[8];
87	u_int8_t        next, ver, type, flags;
88	u_int32_t       msgid, len;
89};
90
91static char    *pcaplog_file = NULL;
92static FILE    *packet_log;
93static u_int8_t *packet_buf = NULL;
94
95static int      udp_cksum(struct packhdr *, const struct udphdr *,
96    u_int16_t *);
97static u_int16_t in_cksum(const u_int16_t *, int);
98#endif				/* USE_DEBUG */
99
100void
101log_init(int debug)
102{
103	if (debug)
104		log_output = stderr;
105	else
106		log_to(0);	/* syslog */
107}
108
109void
110log_reinit(void)
111{
112	struct conf_list *logging;
113#ifdef USE_DEBUG
114	struct conf_list_node *logclass;
115	int		class, level;
116#endif				/* USE_DEBUG */
117
118	logging = conf_get_list("General", "Logverbose");
119	if (logging) {
120		verbose_logging = 1;
121		conf_free_list(logging);
122	}
123#ifdef USE_DEBUG
124	logging = conf_get_list("General", "Loglevel");
125	if (!logging)
126		return;
127
128	for (logclass = TAILQ_FIRST(&logging->fields); logclass;
129	    logclass = TAILQ_NEXT(logclass, link)) {
130		if (sscanf(logclass->field, "%d=%d", &class, &level) != 2) {
131			if (sscanf(logclass->field, "A=%d", &level) == 1)
132				for (class = 0; class < LOG_ENDCLASS; class++)
133					log_debug_cmd(class, level);
134			else {
135				log_print("init: invalid logging class or "
136				    "level: %s", logclass->field);
137				continue;
138			}
139		} else
140			log_debug_cmd(class, level);
141	}
142	conf_free_list(logging);
143#endif				/* USE_DEBUG */
144}
145
146void
147log_to(FILE *f)
148{
149	if (!log_output && f)
150		closelog();
151	log_output = f;
152	if (!f)
153		openlog("isakmpd", LOG_PID | LOG_CONS, LOG_DAEMON);
154}
155
156FILE *
157log_current(void)
158{
159	return log_output;
160}
161
162static char *
163_log_get_class(int error_class)
164{
165	/* XXX For test purposes. To be removed later on?  */
166	static char	*class_text[] = LOG_CLASSES_TEXT;
167
168	if (error_class < 0)
169		return "Dflt";
170	else if (error_class >= LOG_ENDCLASS)
171		return "Unkn";
172	else
173		return class_text[error_class];
174}
175
176static void
177_log_print(int error, int syslog_level, const char *fmt, va_list ap,
178    int class, int level)
179{
180	char		buffer[LOG_SIZE], nbuf[LOG_SIZE + 32];
181	static const char fallback_msg[] =
182	    "write to log file failed (errno %d), redirecting to syslog";
183	int		len;
184	struct tm      *tm;
185	struct timeval  now;
186	time_t          t;
187
188	len = vsnprintf(buffer, sizeof buffer, fmt, ap);
189	if (len > 0 && len < (int) sizeof buffer - 1 && error)
190		snprintf(buffer + len, sizeof buffer - len, ": %s",
191		    strerror(errno));
192	if (log_output) {
193		gettimeofday(&now, 0);
194		t = now.tv_sec;
195		tm = localtime(&t);
196		if (class >= 0)
197			snprintf(nbuf, sizeof nbuf,
198			    "%02d%02d%02d.%06ld %s %02d ",
199			    tm->tm_hour, tm->tm_min, tm->tm_sec, now.tv_usec,
200			    _log_get_class(class), level);
201		else /* LOG_PRINT (-1) or LOG_REPORT (-2) */
202			snprintf(nbuf, sizeof nbuf, "%02d%02d%02d.%06ld %s ",
203			    tm->tm_hour, tm->tm_min, tm->tm_sec, now.tv_usec,
204			    class == LOG_PRINT ? "Default" : "Report>");
205		strlcat(nbuf, buffer, sizeof nbuf);
206#if defined (USE_PRIVSEP)
207		strlcat(nbuf, getuid() ? "" : " [priv]", LOG_SIZE + 32);
208#endif
209		strlcat(nbuf, "\n", sizeof nbuf);
210
211		if (fwrite(nbuf, strlen(nbuf), 1, log_output) == 0) {
212			/* Report fallback.  */
213			syslog(LOG_ALERT, fallback_msg, errno);
214			fprintf(log_output, fallback_msg, errno);
215
216			/*
217			 * Close log_output to prevent isakmpd from locking
218			 * the file.  We may need to explicitly close stdout
219			 * to do this properly.
220			 * XXX - Figure out how to match two FILE *'s and
221			 * rewrite.
222			 */
223			if (fileno(log_output) != -1 &&
224			    fileno(stdout) == fileno(log_output))
225				fclose(stdout);
226			fclose(log_output);
227
228			/* Fallback to syslog.  */
229			log_to(0);
230
231			/* (Re)send current message to syslog().  */
232			syslog(class == LOG_REPORT ? LOG_ALERT :
233			    syslog_level, "%s", buffer);
234		}
235	} else
236		syslog(class == LOG_REPORT ? LOG_ALERT : syslog_level, "%s",
237		    buffer);
238}
239
240#ifdef USE_DEBUG
241void
242log_debug(int cls, int level, const char *fmt, ...)
243{
244	va_list         ap;
245
246	/*
247	 * If we are not debugging this class, or the level is too low, just
248	 * return.
249         */
250	if (cls >= 0 && (log_level[cls] == 0 || level > log_level[cls]))
251		return;
252	va_start(ap, fmt);
253	_log_print(0, LOG_INFO, fmt, ap, cls, level);
254	va_end(ap);
255}
256
257void
258log_debug_buf(int cls, int level, const char *header, const u_int8_t *buf,
259    size_t sz)
260{
261	size_t	i, j;
262	char	s[73];
263
264	/*
265	 * If we are not debugging this class, or the level is too low, just
266	 * return.
267         */
268	if (cls >= 0 && (log_level[cls] == 0 || level > log_level[cls]))
269		return;
270
271	log_debug(cls, level, "%s:", header);
272	for (i = j = 0; i < sz;) {
273		snprintf(s + j, sizeof s - j, "%02x", buf[i++]);
274		j += 2;
275		if (i % 4 == 0) {
276			if (i % 32 == 0) {
277				s[j] = '\0';
278				log_debug(cls, level, "%s", s);
279				j = 0;
280			} else
281				s[j++] = ' ';
282		}
283	}
284	if (j) {
285		s[j] = '\0';
286		log_debug(cls, level, "%s", s);
287	}
288}
289
290void
291log_debug_cmd(int cls, int level)
292{
293	if (cls < 0 || cls >= LOG_ENDCLASS) {
294		log_print("log_debug_cmd: invalid debugging class %d", cls);
295		return;
296	}
297	if (level < 0) {
298		log_print("log_debug_cmd: invalid debugging level %d for "
299		    "class %d", level, cls);
300		return;
301	}
302	if (level == log_level[cls])
303		log_print("log_debug_cmd: log level unchanged for class %d",
304		    cls);
305	else {
306		log_print("log_debug_cmd: log level changed from %d to %d "
307		    "for class %d", log_level[cls], level, cls);
308		log_level[cls] = level;
309	}
310}
311
312void
313log_debug_toggle(void)
314{
315	static int	log_level_copy[LOG_ENDCLASS], toggle = 0;
316
317	if (!toggle) {
318		LOG_DBG((LOG_MISC, 50, "log_debug_toggle: "
319		    "debug levels cleared"));
320		memcpy(&log_level_copy, &log_level, sizeof log_level);
321		bzero(&log_level, sizeof log_level);
322	} else {
323		memcpy(&log_level, &log_level_copy, sizeof log_level);
324		LOG_DBG((LOG_MISC, 50, "log_debug_toggle: "
325		    "debug levels restored"));
326	}
327	toggle = !toggle;
328}
329#endif				/* USE_DEBUG */
330
331void
332log_print(const char *fmt, ...)
333{
334	va_list	ap;
335
336	va_start(ap, fmt);
337	_log_print(0, LOG_NOTICE, fmt, ap, LOG_PRINT, 0);
338	va_end(ap);
339}
340
341void
342log_verbose(const char *fmt, ...)
343{
344	va_list	ap;
345#ifdef USE_DEBUG
346	int	i;
347#endif				/* USE_DEBUG */
348
349	if (verbose_logging == 0)
350		return;
351
352#ifdef USE_DEBUG
353	for (i = 0; i < LOG_ENDCLASS; i++)
354		if (log_level[i] > 0)
355			return;
356#endif
357
358	va_start(ap, fmt);
359	_log_print(0, LOG_NOTICE, fmt, ap, LOG_PRINT, 0);
360	va_end(ap);
361}
362
363void
364log_error(const char *fmt, ...)
365{
366	va_list	ap;
367
368	va_start(ap, fmt);
369	_log_print(1, LOG_ERR, fmt, ap, LOG_PRINT, 0);
370	va_end(ap);
371}
372
373void
374log_fatal(const char *fmt, ...)
375{
376	va_list	ap;
377
378	va_start(ap, fmt);
379	_log_print(1, LOG_CRIT, fmt, ap, LOG_PRINT, 0);
380	va_end(ap);
381#ifdef USE_PRIVSEP
382	monitor_exit(1);
383#else
384	exit(1);
385#endif
386}
387
388#ifdef USE_DEBUG
389void
390log_packet_init(char *newname)
391{
392	struct pcap_file_header sf_hdr;
393	struct stat     st;
394	mode_t          old_umask;
395	char           *mode;
396
397	/* Allocate packet buffer first time through.  */
398	if (!packet_buf)
399		packet_buf = malloc(SNAPLEN);
400
401	if (!packet_buf) {
402		log_error("log_packet_init: malloc (%d) failed", SNAPLEN);
403		return;
404	}
405	if (pcaplog_file && strcmp(pcaplog_file, PCAP_FILE_DEFAULT) != 0)
406		free(pcaplog_file);
407
408	pcaplog_file = strdup(newname);
409	if (!pcaplog_file) {
410		log_error("log_packet_init: strdup (\"%s\") failed", newname);
411		return;
412	}
413	/* Does the file already exist?  XXX lstat() or stat()?  */
414#if defined (USE_PRIVSEP)
415	/* XXX This is a fstat! */
416	if (monitor_stat(pcaplog_file, &st) == 0) {
417#else
418	if (lstat(pcaplog_file, &st) == 0) {
419#endif
420		/* Sanity checks.  */
421		if ((st.st_mode & S_IFMT) != S_IFREG) {
422			log_print("log_packet_init: existing capture file is "
423			    "not a regular file");
424			return;
425		}
426		if ((st.st_mode & (S_IRWXG | S_IRWXO)) != 0) {
427			log_print("log_packet_init: existing capture "
428			    "file has bad modes");
429			return;
430		}
431		/*
432		 * XXX It would be nice to check if it actually is a pcap
433		 * file...
434		 */
435
436		mode = "a";
437	} else
438		mode = "w";
439
440	old_umask = umask(S_IRWXG | S_IRWXO);
441	packet_log = monitor_fopen(pcaplog_file, mode);
442	umask(old_umask);
443
444	if (!packet_log) {
445		log_error("log_packet_init: fopen (\"%s\", \"%s\") failed",
446		    pcaplog_file, mode);
447		return;
448	}
449	log_print("log_packet_init: "
450	    "starting IKE packet capture to file \"%s\"", pcaplog_file);
451
452	/* If this is a new file, we need to write a PCAP header to it.  */
453	if (*mode == 'w') {
454		sf_hdr.magic = TCPDUMP_MAGIC;
455		sf_hdr.version_major = PCAP_VERSION_MAJOR;
456		sf_hdr.version_minor = PCAP_VERSION_MINOR;
457		sf_hdr.thiszone = 0;
458		sf_hdr.snaplen = SNAPLEN;
459		sf_hdr.sigfigs = 0;
460		sf_hdr.linktype = DLT_LOOP;
461
462		fwrite((char *) &sf_hdr, sizeof sf_hdr, 1, packet_log);
463		fflush(packet_log);
464	}
465}
466
467void
468log_packet_restart(char *newname)
469{
470	if (packet_log) {
471		log_print("log_packet_restart: capture already active on "
472		    "file \"%s\"", pcaplog_file);
473		return;
474	}
475	if (newname)
476		log_packet_init(newname);
477	else if (!pcaplog_file)
478		log_packet_init(PCAP_FILE_DEFAULT);
479	else
480		log_packet_init(pcaplog_file);
481}
482
483void
484log_packet_stop(void)
485{
486	/* Stop capture.  */
487	if (packet_log) {
488		fclose(packet_log);
489		log_print("log_packet_stop: stopped capture");
490	}
491	packet_log = 0;
492}
493
494void
495log_packet_iov(struct sockaddr *src, struct sockaddr *dst, struct iovec *iov,
496    int iovcnt)
497{
498	struct isakmp_hdr *isakmphdr;
499	struct packhdr  hdr;
500	struct udphdr   udp;
501	struct timeval  tv;
502	int             off, datalen, hdrlen, i, add_espmarker = 0;
503	const u_int32_t	espmarker = 0;
504
505	for (i = 0, datalen = 0; i < iovcnt; i++)
506		datalen += iov[i].iov_len;
507
508	if (!packet_log || datalen > SNAPLEN)
509		return;
510
511	/* copy packet into buffer */
512	for (i = 0, off = 0; i < iovcnt; i++) {
513		memcpy(packet_buf + off, iov[i].iov_base, iov[i].iov_len);
514		off += iov[i].iov_len;
515	}
516
517	bzero(&hdr, sizeof hdr);
518	bzero(&udp, sizeof udp);
519
520	/* isakmp - turn off the encryption bit in the isakmp hdr */
521	isakmphdr = (struct isakmp_hdr *) packet_buf;
522	isakmphdr->flags &= ~(ISAKMP_FLAGS_ENC);
523
524	/* udp */
525	udp.uh_sport = sockaddr_port(src);
526	udp.uh_dport = sockaddr_port(dst);
527	datalen += sizeof udp;
528#if defined (USE_NAT_TRAVERSAL)
529	if (ntohs(udp.uh_sport) == 4500 ||
530	    ntohs(udp.uh_dport) == 4500) { /* XXX Quick and dirty */
531		add_espmarker = 1;
532		datalen += sizeof espmarker;
533	}
534#endif
535	udp.uh_ulen = htons(datalen);
536
537	/* ip */
538	hdr.sa_family = htonl(src->sa_family);
539	switch (src->sa_family) {
540	default:
541		/* Assume IPv4. XXX Can 'default' ever happen here?  */
542		hdr.sa_family = htonl(AF_INET);
543		hdr.ip.ip4.ip_src.s_addr = 0x02020202;
544		hdr.ip.ip4.ip_dst.s_addr = 0x01010101;
545		/* The rest of the setup is common to AF_INET.  */
546		goto setup_ip4;
547
548	case AF_INET:
549		hdr.ip.ip4.ip_src.s_addr =
550		    ((struct sockaddr_in *)src)->sin_addr.s_addr;
551		hdr.ip.ip4.ip_dst.s_addr =
552		    ((struct sockaddr_in *)dst)->sin_addr.s_addr;
553
554setup_ip4:
555		hdrlen = sizeof hdr.ip.ip4;
556		hdr.ip.ip4.ip_v = 0x4;
557		hdr.ip.ip4.ip_hl = 0x5;
558		hdr.ip.ip4.ip_p = IPPROTO_UDP;
559		hdr.ip.ip4.ip_len = htons(datalen + hdrlen);
560		/* Let's use the IP ID as a "packet counter".  */
561		i = ntohs(hdr.ip.ip4.ip_id) + 1;
562		hdr.ip.ip4.ip_id = htons(i);
563		/* Calculate IP header checksum. */
564		hdr.ip.ip4.ip_sum = in_cksum((u_int16_t *) & hdr.ip.ip4,
565		    hdr.ip.ip4.ip_hl << 2);
566		break;
567
568	case AF_INET6:
569		hdrlen = sizeof(hdr.ip.ip6);
570		hdr.ip.ip6.ip6_vfc = IPV6_VERSION;
571		hdr.ip.ip6.ip6_nxt = IPPROTO_UDP;
572		hdr.ip.ip6.ip6_plen = udp.uh_ulen;
573		memcpy(&hdr.ip.ip6.ip6_src,
574		    &((struct sockaddr_in6 *)src)->sin6_addr,
575		    sizeof hdr.ip.ip6.ip6_src);
576		memcpy(&hdr.ip.ip6.ip6_dst,
577		    &((struct sockaddr_in6 *)dst)->sin6_addr,
578		    sizeof hdr.ip.ip6.ip6_dst);
579		break;
580	}
581
582	/* Calculate UDP checksum.  */
583	udp.uh_sum = udp_cksum(&hdr, &udp, (u_int16_t *) packet_buf);
584	hdrlen += sizeof hdr.sa_family;
585
586	/* pcap file packet header */
587	gettimeofday(&tv, 0);
588	hdr.pcap.ts.tv_sec = tv.tv_sec;
589	hdr.pcap.ts.tv_usec = tv.tv_usec;
590	hdr.pcap.caplen = datalen + hdrlen;
591	hdr.pcap.len = datalen + hdrlen;
592
593	hdrlen += sizeof(struct pcap_pkthdr);
594	datalen -= sizeof(struct udphdr);
595
596	/* Write to pcap file.  */
597	fwrite(&hdr, hdrlen, 1, packet_log);	/* pcap + IP */
598	fwrite(&udp, sizeof(struct udphdr), 1, packet_log);	/* UDP */
599	if (add_espmarker) {
600		fwrite(&espmarker, sizeof espmarker, 1, packet_log);
601		datalen -= sizeof espmarker;
602	}
603	fwrite(packet_buf, datalen, 1, packet_log);	/* IKE-data */
604	fflush(packet_log);
605}
606
607/* Copied from tcpdump/print-udp.c, mostly rewritten.  */
608static int
609udp_cksum(struct packhdr *hdr, const struct udphdr *u, u_int16_t *d)
610{
611	struct ip	*ip4;
612	struct ip6_hdr	*ip6;
613	int	i, hdrlen, tlen = ntohs(u->uh_ulen) - sizeof(struct udphdr);
614
615	union phu {
616		struct ip4pseudo {
617			struct in_addr  src;
618			struct in_addr  dst;
619			u_int8_t        z;
620			u_int8_t        proto;
621			u_int16_t       len;
622		} ip4p;
623		struct ip6pseudo {
624			struct in6_addr src;
625			struct in6_addr dst;
626			u_int32_t       plen;
627			u_int16_t       z0;
628			u_int8_t        z1;
629			u_int8_t        nxt;
630		} ip6p;
631		u_int16_t       pa[20];
632	} phu;
633	const u_int16_t *sp;
634	u_int32_t       sum;
635
636	/* Setup pseudoheader.  */
637	bzero(phu.pa, sizeof phu);
638	switch (ntohl(hdr->sa_family)) {
639	case AF_INET:
640		ip4 = &hdr->ip.ip4;
641		memcpy(&phu.ip4p.src, &ip4->ip_src, sizeof(struct in_addr));
642		memcpy(&phu.ip4p.dst, &ip4->ip_dst, sizeof(struct in_addr));
643		phu.ip4p.proto = ip4->ip_p;
644		phu.ip4p.len = u->uh_ulen;
645		hdrlen = sizeof phu.ip4p;
646		break;
647
648	case AF_INET6:
649		ip6 = &hdr->ip.ip6;
650		memcpy(&phu.ip6p.src, &ip6->ip6_src, sizeof(phu.ip6p.src));
651		memcpy(&phu.ip6p.dst, &ip6->ip6_dst, sizeof(phu.ip6p.dst));
652		phu.ip6p.plen = u->uh_ulen;
653		phu.ip6p.nxt = ip6->ip6_nxt;
654		hdrlen = sizeof phu.ip6p;
655		break;
656
657	default:
658		return 0;
659	}
660
661	/* IPv6 wants a 0xFFFF checksum "on error", not 0x0.  */
662	if (tlen < 0)
663		return (ntohl(hdr->sa_family) == AF_INET ? 0 : 0xFFFF);
664
665	sum = 0;
666	for (i = 0; i < hdrlen; i += 2)
667		sum += phu.pa[i / 2];
668
669	sp = (const u_int16_t *)u;
670	for (i = 0; i < (int)sizeof(struct udphdr); i += 2)
671		sum += *sp++;
672
673	sp = d;
674	for (i = 0; i < (tlen & ~1); i += 2)
675		sum += *sp++;
676
677	if (tlen & 1)
678		sum += htons((*(const char *)sp) << 8);
679
680	while (sum > 0xffff)
681		sum = (sum & 0xffff) + (sum >> 16);
682	sum = ~sum & 0xffff;
683
684	return sum;
685}
686
687/* Copied from tcpdump/print-ip.c, modified.  */
688static u_int16_t
689in_cksum(const u_int16_t *w, int len)
690{
691	int		nleft = len, sum = 0;
692	u_int16_t       answer;
693
694	while (nleft > 1) {
695		sum += *w++;
696		nleft -= 2;
697	}
698	if (nleft == 1)
699		sum += htons(*(const u_char *)w << 8);
700
701	sum = (sum >> 16) + (sum & 0xffff);	/* add hi 16 to low 16 */
702	sum += (sum >> 16);	/* add carry */
703	answer = ~sum;		/* truncate to 16 bits */
704	return answer;
705}
706
707#endif				/* USE_DEBUG */
708