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