xref: /netbsd-src/crypto/dist/ipsec-tools/src/setkey/setkey.c (revision fad4c9f71477ae11cea2ee75ec82151ac770a534)
1 /*	$NetBSD: setkey.c,v 1.10 2005/12/04 20:46:40 manu Exp $	*/
2 
3 /*	$KAME: setkey.c,v 1.36 2003/09/24 23:52:51 itojun Exp $	*/
4 
5 /*
6  * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. Neither the name of the project nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 #ifdef HAVE_CONFIG_H
35 #include "config.h"
36 #endif
37 
38 #include <sys/types.h>
39 #include <sys/param.h>
40 #include <sys/socket.h>
41 #include <sys/time.h>
42 #include <sys/stat.h>
43 #include <sys/sysctl.h>
44 #include <err.h>
45 #include <netinet/in.h>
46 #include <net/pfkeyv2.h>
47 #ifdef HAVE_NETINET6_IPSEC
48 #  include <netinet6/ipsec.h>
49 #else
50 #  include <netinet/ipsec.h>
51 #endif
52 
53 #include <stdio.h>
54 #include <stdlib.h>
55 #include <limits.h>
56 #include <string.h>
57 #include <ctype.h>
58 #include <unistd.h>
59 #include <errno.h>
60 #include <netdb.h>
61 #include <fcntl.h>
62 #include <dirent.h>
63 #include <time.h>
64 
65 #ifdef HAVE_READLINE
66 #include <readline/readline.h>
67 #include <readline/history.h>
68 #endif
69 
70 #include "config.h"
71 #include "libpfkey.h"
72 #include "package_version.h"
73 #define extern /* so that variables in extern.h are not extern... */
74 #include "extern.h"
75 
76 #define strlcpy(d,s,l) (strncpy(d,s,l), (d)[(l)-1] = '\0')
77 
78 void usage __P((int));
79 int main __P((int, char **));
80 int get_supported __P((void));
81 void sendkeyshort __P((u_int));
82 void promisc __P((void));
83 int postproc __P((struct sadb_msg *, int));
84 int verifypriority __P((struct sadb_msg *m));
85 int fileproc __P((const char *));
86 const char *numstr __P((int));
87 void shortdump_hdr __P((void));
88 void shortdump __P((struct sadb_msg *));
89 static void printdate __P((void));
90 static int32_t gmt2local __P((time_t));
91 void stdin_loop __P((void));
92 
93 #define MODE_SCRIPT	1
94 #define MODE_CMDDUMP	2
95 #define MODE_CMDFLUSH	3
96 #define MODE_PROMISC	4
97 #define MODE_STDIN	5
98 
99 int so;
100 
101 int f_forever = 0;
102 int f_all = 0;
103 int f_verbose = 0;
104 int f_mode = 0;
105 int f_cmddump = 0;
106 int f_policy = 0;
107 int f_hexdump = 0;
108 int f_tflag = 0;
109 int f_notreally = 0;
110 int f_withports = 0;
111 #ifdef HAVE_POLICY_FWD
112 int f_rfcmode = 1;
113 #define RK_OPTS "rk"
114 #else
115 int f_rkwarn = 0;
116 #define RK_OPTS ""
117 static void rkwarn(void);
118 static void
119 rkwarn(void)
120 {
121 	if (!f_rkwarn) {
122 		f_rkwarn = 1;
123 		printf("warning: -r and -k options are not supported in this environment\n");
124 	}
125 }
126 
127 #endif
128 static time_t thiszone;
129 
130 void
131 usage(int only_version)
132 {
133 	printf("setkey @(#) %s (%s)\n", TOP_PACKAGE_STRING, TOP_PACKAGE_URL);
134 	if (! only_version) {
135 		printf("usage: setkey [-v" RK_OPTS "] file ...\n");
136 		printf("       setkey [-nv" RK_OPTS "] -c\n");
137 		printf("       setkey [-nv" RK_OPTS "] -f filename\n");
138 		printf("       setkey [-Palpv" RK_OPTS "] -D\n");
139 		printf("       setkey [-Pv] -F\n");
140 		printf("       setkey [-H] -x\n");
141 		printf("       setkey [-V] [-h]\n");
142 	}
143 	exit(1);
144 }
145 
146 int
147 main(argc, argv)
148 	int argc;
149 	char **argv;
150 {
151 	FILE *fp = stdin;
152 	int c;
153 
154 	if (argc == 1) {
155 		usage(0);
156 		/* NOTREACHED */
157 	}
158 
159 	thiszone = gmt2local(0);
160 
161 	while ((c = getopt(argc, argv, "acdf:HlnvxDFPphVrk?")) != -1) {
162 		switch (c) {
163 		case 'c':
164 			f_mode = MODE_STDIN;
165 #ifdef HAVE_READLINE
166 			/* disable filename completion */
167 			rl_bind_key('\t', rl_insert);
168 #endif
169 			break;
170 		case 'f':
171 			f_mode = MODE_SCRIPT;
172 			if ((fp = fopen(optarg, "r")) == NULL) {
173 				err(1, "fopen");
174 				/*NOTREACHED*/
175 			}
176 			break;
177 		case 'D':
178 			f_mode = MODE_CMDDUMP;
179 			break;
180 		case 'F':
181 			f_mode = MODE_CMDFLUSH;
182 			break;
183 		case 'a':
184 			f_all = 1;
185 			break;
186 		case 'l':
187 			f_forever = 1;
188 			break;
189 		case 'n':
190 			f_notreally = 1;
191 			break;
192 #ifdef __NetBSD__
193 		case 'h':
194 #endif
195 		case 'H':
196 			f_hexdump = 1;
197 			break;
198 		case 'x':
199 			f_mode = MODE_PROMISC;
200 			f_tflag++;
201 			break;
202 		case 'P':
203 			f_policy = 1;
204 			break;
205 		case 'p':
206 			f_withports = 1;
207 			break;
208 		case 'v':
209 			f_verbose = 1;
210 			break;
211 		case 'r':
212 #ifdef HAVE_POLICY_FWD
213 			f_rfcmode = 1;
214 #else
215 			rkwarn();
216 #endif
217 			break;
218 		case 'k':
219 #ifdef HAVE_POLICY_FWD
220 			f_rfcmode = 0;
221 #else
222 			rkwarn();
223 #endif
224 			break;
225 		case 'V':
226 			usage(1);
227 			break;
228 			/*NOTREACHED*/
229 #ifndef __NetBSD__
230 		case 'h':
231 #endif
232 		case '?':
233 		default:
234 			usage(0);
235 			/*NOTREACHED*/
236 		}
237 	}
238 
239 	argc -= optind;
240 	argv += optind;
241 
242 	if (argc > 0) {
243 		while (argc--)
244 			if (fileproc(*argv++) < 0) {
245 				err(1, "%s", argv[-1]);
246 				/*NOTREACHED*/
247 			}
248 		exit(0);
249 	}
250 
251 	so = pfkey_open();
252 	if (so < 0) {
253 		perror("pfkey_open");
254 		exit(1);
255 	}
256 
257 	switch (f_mode) {
258 	case MODE_CMDDUMP:
259 		sendkeyshort(f_policy ? SADB_X_SPDDUMP : SADB_DUMP);
260 		break;
261 	case MODE_CMDFLUSH:
262 		sendkeyshort(f_policy ? SADB_X_SPDFLUSH: SADB_FLUSH);
263 		break;
264 	case MODE_SCRIPT:
265 		if (get_supported() < 0) {
266 			errx(1, "%s", ipsec_strerror());
267 			/*NOTREACHED*/
268 		}
269 		if (parse(&fp))
270 			exit (1);
271 		break;
272 	case MODE_STDIN:
273 		if (get_supported() < 0) {
274 			errx(1, "%s", ipsec_strerror());
275 			/*NOTREACHED*/
276 		}
277 		stdin_loop();
278 		break;
279 	case MODE_PROMISC:
280 		promisc();
281 		/*NOTREACHED*/
282 	default:
283 		usage(0);
284 		/*NOTREACHED*/
285 	}
286 
287 	exit(0);
288 }
289 
290 int
291 get_supported()
292 {
293 
294 	if (pfkey_send_register(so, SADB_SATYPE_UNSPEC) < 0)
295 		return -1;
296 
297 	if (pfkey_recv_register(so) < 0)
298 		return -1;
299 
300 	return (0);
301 }
302 
303 void
304 stdin_loop()
305 {
306 	char line[1024], *semicolon, *comment;
307 	size_t linelen = 0;
308 
309 	memset (line, 0, sizeof(line));
310 
311 	parse_init();
312 	while (1) {
313 #ifdef HAVE_READLINE
314 		char *rbuf;
315 		rbuf = readline ("");
316 		if (! rbuf)
317 			break;
318 #else
319 		char rbuf[1024];
320 		rbuf[0] = '\0';
321 		fgets (rbuf, sizeof(rbuf), stdin);
322 		if (!rbuf[0])
323 			break;
324 		if (rbuf[strlen(rbuf)-1] == '\n')
325 			rbuf[strlen(rbuf)-1] = '\0';
326 #endif
327 		comment = strchr(rbuf, '#');
328 		if (comment)
329 			*comment = '\0';
330 
331 		if (!rbuf[0])
332 			continue;
333 
334 		linelen += snprintf (&line[linelen], sizeof(line) - linelen,
335 				     "%s%s", linelen > 0 ? " " : "", rbuf);
336 
337 		semicolon = strchr(line, ';');
338 		while (semicolon) {
339 			char saved_char = *++semicolon;
340 			*semicolon = '\0';
341 #ifdef HAVE_READLINE
342 			add_history (line);
343 #endif
344 
345 #ifdef HAVE_PFKEY_POLICY_PRIORITY
346 			last_msg_type = -1;  /* invalid message type */
347 #endif
348 
349 			parse_string (line);
350 			if (exit_now)
351 				return;
352 			if (saved_char) {
353 				*semicolon = saved_char;
354 				linelen = strlen (semicolon);
355 				memmove (line, semicolon, linelen + 1);
356 				semicolon = strchr(line, ';');
357 			}
358 			else {
359 				semicolon = NULL;
360 				linelen = 0;
361 			}
362 		}
363 	}
364 }
365 
366 void
367 sendkeyshort(type)
368         u_int type;
369 {
370 	struct sadb_msg msg;
371 
372 	msg.sadb_msg_version = PF_KEY_V2;
373 	msg.sadb_msg_type = type;
374 	msg.sadb_msg_errno = 0;
375 	msg.sadb_msg_satype = SADB_SATYPE_UNSPEC;
376 	msg.sadb_msg_len = PFKEY_UNIT64(sizeof(msg));
377 	msg.sadb_msg_reserved = 0;
378 	msg.sadb_msg_seq = 0;
379 	msg.sadb_msg_pid = getpid();
380 
381 	sendkeymsg((char *)&msg, sizeof(msg));
382 
383 	return;
384 }
385 
386 void
387 promisc()
388 {
389 	struct sadb_msg msg;
390 	u_char rbuf[1024 * 32];	/* XXX: Enough ? Should I do MSG_PEEK ? */
391 	ssize_t l;
392 
393 	msg.sadb_msg_version = PF_KEY_V2;
394 	msg.sadb_msg_type = SADB_X_PROMISC;
395 	msg.sadb_msg_errno = 0;
396 	msg.sadb_msg_satype = 1;
397 	msg.sadb_msg_len = PFKEY_UNIT64(sizeof(msg));
398 	msg.sadb_msg_reserved = 0;
399 	msg.sadb_msg_seq = 0;
400 	msg.sadb_msg_pid = getpid();
401 
402 	if ((l = send(so, &msg, sizeof(msg), 0)) < 0) {
403 		err(1, "send");
404 		/*NOTREACHED*/
405 	}
406 
407 	while (1) {
408 		struct sadb_msg *base;
409 
410 		if ((l = recv(so, rbuf, sizeof(*base), MSG_PEEK)) < 0) {
411 			err(1, "recv");
412 			/*NOTREACHED*/
413 		}
414 
415 		if (l != sizeof(*base))
416 			continue;
417 
418 		base = (struct sadb_msg *)rbuf;
419 		if ((l = recv(so, rbuf, PFKEY_UNUNIT64(base->sadb_msg_len),
420 				0)) < 0) {
421 			err(1, "recv");
422 			/*NOTREACHED*/
423 		}
424 		printdate();
425 		if (f_hexdump) {
426 			int i;
427 			for (i = 0; i < l; i++) {
428 				if (i % 16 == 0)
429 					printf("%08x: ", i);
430 				printf("%02x ", rbuf[i] & 0xff);
431 				if (i % 16 == 15)
432 					printf("\n");
433 			}
434 			if (l % 16)
435 				printf("\n");
436 		}
437 		/* adjust base pointer for promisc mode */
438 		if (base->sadb_msg_type == SADB_X_PROMISC) {
439 			if ((ssize_t)sizeof(*base) < l)
440 				base++;
441 			else
442 				base = NULL;
443 		}
444 		if (base) {
445 			kdebug_sadb(base);
446 			printf("\n");
447 			fflush(stdout);
448 		}
449 	}
450 }
451 
452 int
453 sendkeymsg(buf, len)
454 	char *buf;
455 	size_t len;
456 {
457 	u_char rbuf[1024 * 32];	/* XXX: Enough ? Should I do MSG_PEEK ? */
458 	ssize_t l;
459 	struct sadb_msg *msg;
460 
461 	if (f_notreally) {
462 		goto end;
463 	}
464 
465     {
466 	struct timeval tv;
467 	tv.tv_sec = 1;
468 	tv.tv_usec = 0;
469 	if (setsockopt(so, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) {
470 		perror("setsockopt");
471 		goto end;
472 	}
473     }
474 
475 	if (f_forever)
476 		shortdump_hdr();
477 again:
478 	if (f_verbose) {
479 		kdebug_sadb((struct sadb_msg *)buf);
480 		printf("\n");
481 	}
482 	if (f_hexdump) {
483 		int i;
484 		for (i = 0; i < len; i++) {
485 			if (i % 16 == 0)
486 				printf("%08x: ", i);
487 			printf("%02x ", buf[i] & 0xff);
488 			if (i % 16 == 15)
489 				printf("\n");
490 		}
491 		if (len % 16)
492 			printf("\n");
493 	}
494 
495 	if ((l = send(so, buf, len, 0)) < 0) {
496 		perror("send");
497 		goto end;
498 	}
499 
500 	msg = (struct sadb_msg *)rbuf;
501 	do {
502 		if ((l = recv(so, rbuf, sizeof(rbuf), 0)) < 0) {
503 			perror("recv");
504 			goto end;
505 		}
506 
507 		if (PFKEY_UNUNIT64(msg->sadb_msg_len) != l) {
508 			warnx("invalid keymsg length");
509 			break;
510 		}
511 
512 		if (f_verbose) {
513 			kdebug_sadb((struct sadb_msg *)rbuf);
514 			printf("\n");
515 		}
516 		if (postproc(msg, l) < 0)
517 			break;
518 	} while (msg->sadb_msg_errno || msg->sadb_msg_seq);
519 
520 	if (f_forever) {
521 		fflush(stdout);
522 		sleep(1);
523 		goto again;
524 	}
525 
526 end:
527 	return (0);
528 }
529 
530 int
531 postproc(msg, len)
532 	struct sadb_msg *msg;
533 	int len;
534 {
535 #ifdef HAVE_PFKEY_POLICY_PRIORITY
536 	static int priority_support_check = 0;
537 #endif
538 
539 	if (msg->sadb_msg_errno != 0) {
540 		char inf[80];
541 		const char *errmsg = NULL;
542 
543 		if (f_mode == MODE_SCRIPT)
544 			snprintf(inf, sizeof(inf), "The result of line %d: ", lineno);
545 		else
546 			inf[0] = '\0';
547 
548 		switch (msg->sadb_msg_errno) {
549 		case ENOENT:
550 			switch (msg->sadb_msg_type) {
551 			case SADB_DELETE:
552 			case SADB_GET:
553 			case SADB_X_SPDDELETE:
554 				errmsg = "No entry";
555 				break;
556 			case SADB_DUMP:
557 				errmsg = "No SAD entries";
558 				break;
559 			case SADB_X_SPDDUMP:
560 				errmsg = "No SPD entries";
561 				break;
562 			}
563 			break;
564 		default:
565 			errmsg = strerror(msg->sadb_msg_errno);
566 		}
567 		printf("%s%s.\n", inf, errmsg);
568 		return (-1);
569 	}
570 
571 	switch (msg->sadb_msg_type) {
572 	case SADB_GET:
573 		if (f_withports)
574 			pfkey_sadump_withports(msg);
575 		else
576 			pfkey_sadump(msg);
577 		break;
578 
579 	case SADB_DUMP:
580 		/* filter out DEAD SAs */
581 		if (!f_all) {
582 			caddr_t mhp[SADB_EXT_MAX + 1];
583 			struct sadb_sa *sa;
584 			pfkey_align(msg, mhp);
585 			pfkey_check(mhp);
586 			if ((sa = (struct sadb_sa *)mhp[SADB_EXT_SA]) != NULL) {
587 				if (sa->sadb_sa_state == SADB_SASTATE_DEAD)
588 					break;
589 			}
590 		}
591 		if (f_forever) {
592 			/* TODO: f_withports */
593 			shortdump(msg);
594 		} else {
595 			if (f_withports)
596 				pfkey_sadump_withports(msg);
597 			else
598 				pfkey_sadump(msg);
599 		}
600 		msg = (struct sadb_msg *)((caddr_t)msg +
601 				     PFKEY_UNUNIT64(msg->sadb_msg_len));
602 		if (f_verbose) {
603 			kdebug_sadb((struct sadb_msg *)msg);
604 			printf("\n");
605 		}
606 		break;
607 
608 	case SADB_X_SPDGET:
609 		if (f_withports)
610 			pfkey_spdump_withports(msg);
611 		else
612 			pfkey_spdump(msg);
613 		break;
614 
615 	case SADB_X_SPDDUMP:
616 		if (f_withports)
617 			pfkey_spdump_withports(msg);
618 		else
619 			pfkey_spdump(msg);
620 		if (msg->sadb_msg_seq == 0) break;
621 		msg = (struct sadb_msg *)((caddr_t)msg +
622 				     PFKEY_UNUNIT64(msg->sadb_msg_len));
623 		if (f_verbose) {
624 			kdebug_sadb((struct sadb_msg *)msg);
625 			printf("\n");
626 		}
627 		break;
628 #ifdef HAVE_PFKEY_POLICY_PRIORITY
629 	case SADB_X_SPDADD:
630 		if (last_msg_type == SADB_X_SPDADD && last_priority != 0 &&
631 		    msg->sadb_msg_pid == getpid() && !priority_support_check) {
632 			priority_support_check = 1;
633 			if (!verifypriority(msg))
634 				printf ("WARNING: Kernel does not support policy priorities\n");
635 		}
636 		break;
637 #endif
638 	}
639 
640 	return (0);
641 }
642 
643 #ifdef HAVE_PFKEY_POLICY_PRIORITY
644 int
645 verifypriority(m)
646 	struct sadb_msg *m;
647 {
648 	caddr_t mhp[SADB_EXT_MAX + 1];
649 	struct sadb_x_policy *xpl;
650 
651 	/* check pfkey message. */
652 	if (pfkey_align(m, mhp)) {
653 		printf("(%s\n", ipsec_strerror());
654 		return 0;
655 	}
656 	if (pfkey_check(mhp)) {
657 		printf("%s\n", ipsec_strerror());
658 		return 0;
659 	}
660 
661 	xpl = (struct sadb_x_policy *) mhp[SADB_X_EXT_POLICY];
662 
663 	if (xpl == NULL) {
664 		printf("no X_POLICY extension.\n");
665 		return 0;
666 	}
667 
668 	/* now make sure they match */
669 	if (last_priority != xpl->sadb_x_policy_priority)
670 		return 0;
671 
672 	return 1;
673 }
674 #endif
675 
676 int
677 fileproc(filename)
678 	const char *filename;
679 {
680 	int fd;
681 	ssize_t len, l;
682 	u_char *p, *ep;
683 	struct sadb_msg *msg;
684 	u_char rbuf[1024 * 32];	/* XXX: Enough ? Should I do MSG_PEEK ? */
685 
686 	fd = open(filename, O_RDONLY);
687 	if (fd < 0)
688 		return -1;
689 
690 	l = 0;
691 	while (1) {
692 		len = read(fd, rbuf + l, sizeof(rbuf) - l);
693 		if (len < 0) {
694 			close(fd);
695 			return -1;
696 		} else if (len == 0)
697 			break;
698 		l += len;
699 	}
700 
701 	if (l < sizeof(struct sadb_msg)) {
702 		close(fd);
703 		errno = EINVAL;
704 		return -1;
705 	}
706 	close(fd);
707 
708 	p = rbuf;
709 	ep = rbuf + l;
710 
711 	while (p < ep) {
712 		msg = (struct sadb_msg *)p;
713 		len = PFKEY_UNUNIT64(msg->sadb_msg_len);
714 		postproc(msg, len);
715 		p += len;
716 	}
717 
718 	return (0);
719 }
720 
721 
722 /*------------------------------------------------------------*/
723 static const char *satype[] = {
724 	NULL, NULL, "ah", "esp"
725 };
726 static const char *sastate[] = {
727 	"L", "M", "D", "d"
728 };
729 static const char *ipproto[] = {
730 /*0*/	"ip", "icmp", "igmp", "ggp", "ip4",
731 	NULL, "tcp", NULL, "egp", NULL,
732 /*10*/	NULL, NULL, NULL, NULL, NULL,
733 	NULL, NULL, "udp", NULL, NULL,
734 /*20*/	NULL, NULL, "idp", NULL, NULL,
735 	NULL, NULL, NULL, NULL, "tp",
736 /*30*/	NULL, NULL, NULL, NULL, NULL,
737 	NULL, NULL, NULL, NULL, NULL,
738 /*40*/	NULL, "ip6", NULL, "rt6", "frag6",
739 	NULL, "rsvp", "gre", NULL, NULL,
740 /*50*/	"esp", "ah", NULL, NULL, NULL,
741 	NULL, NULL, NULL, "icmp6", "none",
742 /*60*/	"dst6",
743 };
744 
745 #define STR_OR_ID(x, tab) \
746 	(((x) < sizeof(tab)/sizeof(tab[0]) && tab[(x)])	? tab[(x)] : numstr(x))
747 
748 const char *
749 numstr(x)
750 	int x;
751 {
752 	static char buf[20];
753 	snprintf(buf, sizeof(buf), "#%d", x);
754 	return buf;
755 }
756 
757 void
758 shortdump_hdr()
759 {
760 	printf("%-4s %-3s %-1s %-8s %-7s %s -> %s\n",
761 		"time", "p", "s", "spi", "ltime", "src", "dst");
762 }
763 
764 void
765 shortdump(msg)
766 	struct sadb_msg *msg;
767 {
768 	caddr_t mhp[SADB_EXT_MAX + 1];
769 	char buf[NI_MAXHOST], pbuf[NI_MAXSERV];
770 	struct sadb_sa *sa;
771 	struct sadb_address *saddr;
772 	struct sadb_lifetime *lts, *lth, *ltc;
773 	struct sockaddr *s;
774 	u_int t;
775 	time_t cur = time(0);
776 
777 	pfkey_align(msg, mhp);
778 	pfkey_check(mhp);
779 
780 	printf("%02lu%02lu", (u_long)(cur % 3600) / 60, (u_long)(cur % 60));
781 
782 	printf(" %-3s", STR_OR_ID(msg->sadb_msg_satype, satype));
783 
784 	if ((sa = (struct sadb_sa *)mhp[SADB_EXT_SA]) != NULL) {
785 		printf(" %-1s", STR_OR_ID(sa->sadb_sa_state, sastate));
786 		printf(" %08x", (u_int32_t)ntohl(sa->sadb_sa_spi));
787 	} else
788 		printf("%-1s %-8s", "?", "?");
789 
790 	lts = (struct sadb_lifetime *)mhp[SADB_EXT_LIFETIME_SOFT];
791 	lth = (struct sadb_lifetime *)mhp[SADB_EXT_LIFETIME_HARD];
792 	ltc = (struct sadb_lifetime *)mhp[SADB_EXT_LIFETIME_CURRENT];
793 	if (lts && lth && ltc) {
794 		if (ltc->sadb_lifetime_addtime == 0)
795 			t = (u_long)0;
796 		else
797 			t = (u_long)(cur - ltc->sadb_lifetime_addtime);
798 		if (t >= 1000)
799 			strlcpy(buf, " big/", sizeof(buf));
800 		else
801 			snprintf(buf, sizeof(buf), " %3lu/", (u_long)t);
802 		printf("%s", buf);
803 
804 		t = (u_long)lth->sadb_lifetime_addtime;
805 		if (t >= 1000)
806 			strlcpy(buf, "big", sizeof(buf));
807 		else
808 			snprintf(buf, sizeof(buf), "%-3lu", (u_long)t);
809 		printf("%s", buf);
810 	} else
811 		printf(" ??\?/???");	/* backslash to avoid trigraph ??/ */
812 
813 	printf(" ");
814 
815 	if ((saddr = (struct sadb_address *)mhp[SADB_EXT_ADDRESS_SRC]) != NULL) {
816 		if (saddr->sadb_address_proto)
817 			printf("%s ", STR_OR_ID(saddr->sadb_address_proto, ipproto));
818 		s = (struct sockaddr *)(saddr + 1);
819 		getnameinfo(s, sysdep_sa_len(s), buf, sizeof(buf),
820 			pbuf, sizeof(pbuf), NI_NUMERICHOST|NI_NUMERICSERV);
821 		if (strcmp(pbuf, "0") != 0)
822 			printf("%s[%s]", buf, pbuf);
823 		else
824 			printf("%s", buf);
825 	} else
826 		printf("?");
827 
828 	printf(" -> ");
829 
830 	if ((saddr = (struct sadb_address *)mhp[SADB_EXT_ADDRESS_DST]) != NULL) {
831 		if (saddr->sadb_address_proto)
832 			printf("%s ", STR_OR_ID(saddr->sadb_address_proto, ipproto));
833 
834 		s = (struct sockaddr *)(saddr + 1);
835 		getnameinfo(s, sysdep_sa_len(s), buf, sizeof(buf),
836 			pbuf, sizeof(pbuf), NI_NUMERICHOST|NI_NUMERICSERV);
837 		if (strcmp(pbuf, "0") != 0)
838 			printf("%s[%s]", buf, pbuf);
839 		else
840 			printf("%s", buf);
841 	} else
842 		printf("?");
843 
844 	printf("\n");
845 }
846 
847 /* From: tcpdump(1):gmt2local.c and util.c */
848 /*
849  * Print the timestamp
850  */
851 static void
852 printdate()
853 {
854 	struct timeval tp;
855 	int s;
856 
857 	if (gettimeofday(&tp, NULL) == -1) {
858 		perror("gettimeofday");
859 		return;
860 	}
861 
862 	if (f_tflag == 1) {
863 		/* Default */
864 		s = (tp.tv_sec + thiszone ) % 86400;
865 		(void)printf("%02d:%02d:%02d.%06u ",
866 		    s / 3600, (s % 3600) / 60, s % 60, (u_int32_t)tp.tv_usec);
867 	} else if (f_tflag > 1) {
868 		/* Unix timeval style */
869 		(void)printf("%u.%06u ",
870 		    (u_int32_t)tp.tv_sec, (u_int32_t)tp.tv_usec);
871 	}
872 
873 	printf("\n");
874 }
875 
876 /*
877  * Returns the difference between gmt and local time in seconds.
878  * Use gmtime() and localtime() to keep things simple.
879  */
880 int32_t
881 gmt2local(time_t t)
882 {
883 	register int dt, dir;
884 	register struct tm *gmt, *loc;
885 	struct tm sgmt;
886 
887 	if (t == 0)
888 		t = time(NULL);
889 	gmt = &sgmt;
890 	*gmt = *gmtime(&t);
891 	loc = localtime(&t);
892 	dt = (loc->tm_hour - gmt->tm_hour) * 60 * 60 +
893 	    (loc->tm_min - gmt->tm_min) * 60;
894 
895 	/*
896 	 * If the year or julian day is different, we span 00:00 GMT
897 	 * and must add or subtract a day. Check the year first to
898 	 * avoid problems when the julian day wraps.
899 	 */
900 	dir = loc->tm_year - gmt->tm_year;
901 	if (dir == 0)
902 		dir = loc->tm_yday - gmt->tm_yday;
903 	dt += dir * 24 * 60 * 60;
904 
905 	return (dt);
906 }
907