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