xref: /titanic_41/usr/src/lib/libipsecutil/common/ipsec_util.c (revision 2eeaed14a5e2ed9bd811643ad5bffc3510ca0310)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <unistd.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <stdarg.h>
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <fcntl.h>
35 #include <sys/sysconf.h>
36 #include <strings.h>
37 #include <ctype.h>
38 #include <errno.h>
39 #include <sys/socket.h>
40 #include <netdb.h>
41 #include <netinet/in.h>
42 #include <arpa/inet.h>
43 #include <net/pfkeyv2.h>
44 #include <net/pfpolicy.h>
45 #include <libintl.h>
46 #include <setjmp.h>
47 #include <libgen.h>
48 #include <libscf.h>
49 
50 #include "ipsec_util.h"
51 #include "ikedoor.h"
52 
53 /*
54  * This file contains support functions that are shared by the ipsec
55  * utilities and daemons including ipseckey(1m), ikeadm(1m) and in.iked(1m).
56  */
57 
58 
59 #define	EFD(file) (((file) == stdout) ? stderr : (file))
60 
61 /* Set standard default/initial values for globals... */
62 boolean_t pflag = B_FALSE;	/* paranoid w.r.t. printing keying material */
63 boolean_t nflag = B_FALSE;	/* avoid nameservice? */
64 boolean_t interactive = B_FALSE;	/* util not running on cmdline */
65 boolean_t readfile = B_FALSE;	/* cmds are being read from a file */
66 uint_t	lineno = 0;		/* track location if reading cmds from file */
67 uint_t	lines_added = 0;
68 uint_t	lines_parsed = 0;
69 jmp_buf	env;		/* for error recovery in interactive/readfile modes */
70 char *my_fmri = NULL;
71 FILE *debugfile = stderr;
72 
73 /*
74  * Print errno and exit if cmdline or readfile, reset state if interactive
75  * The error string *what should be dgettext()'d before calling bail().
76  */
77 void
78 bail(char *what)
79 {
80 	if (errno != 0)
81 		warn(what);
82 	else
83 		warnx(dgettext(TEXT_DOMAIN, "Error: %s"), what);
84 	if (readfile) {
85 		return;
86 	}
87 	if (interactive && !readfile)
88 		longjmp(env, 2);
89 	EXIT_FATAL(NULL);
90 }
91 
92 /*
93  * Print caller-supplied variable-arg error msg, then exit if cmdline or
94  * readfile, or reset state if interactive.
95  */
96 /*PRINTFLIKE1*/
97 void
98 bail_msg(char *fmt, ...)
99 {
100 	va_list	ap;
101 	char	msgbuf[BUFSIZ];
102 
103 	va_start(ap, fmt);
104 	(void) vsnprintf(msgbuf, BUFSIZ, fmt, ap);
105 	va_end(ap);
106 	if (readfile)
107 		warnx(dgettext(TEXT_DOMAIN,
108 		    "ERROR on line %u:\n%s\n"), lineno,  msgbuf);
109 	else
110 		warnx(dgettext(TEXT_DOMAIN, "ERROR: %s\n"), msgbuf);
111 
112 	if (interactive && !readfile)
113 		longjmp(env, 1);
114 
115 	EXIT_FATAL(NULL);
116 }
117 
118 
119 /*
120  * dump_XXX functions produce ASCII output from various structures.
121  *
122  * Because certain errors need to do this to stderr, dump_XXX functions
123  * take a FILE pointer.
124  *
125  * If an error occured while writing to the specified file, these
126  * functions return -1, zero otherwise.
127  */
128 
129 int
130 dump_sockaddr(struct sockaddr *sa, uint8_t prefixlen, boolean_t addr_only,
131     FILE *where, boolean_t ignore_nss)
132 {
133 	struct sockaddr_in	*sin;
134 	struct sockaddr_in6	*sin6;
135 	char			*printable_addr, *protocol;
136 	uint8_t			*addrptr;
137 	/* Add 4 chars to hold '/nnn' for prefixes. */
138 	char			storage[INET6_ADDRSTRLEN + 4];
139 	uint16_t		port;
140 	boolean_t		unspec;
141 	struct hostent		*hp;
142 	int			getipnode_errno, addrlen;
143 
144 	switch (sa->sa_family) {
145 	case AF_INET:
146 		/* LINTED E_BAD_PTR_CAST_ALIGN */
147 		sin = (struct sockaddr_in *)sa;
148 		addrptr = (uint8_t *)&sin->sin_addr;
149 		port = sin->sin_port;
150 		protocol = "AF_INET";
151 		unspec = (sin->sin_addr.s_addr == 0);
152 		addrlen = sizeof (sin->sin_addr);
153 		break;
154 	case AF_INET6:
155 		/* LINTED E_BAD_PTR_CAST_ALIGN */
156 		sin6 = (struct sockaddr_in6 *)sa;
157 		addrptr = (uint8_t *)&sin6->sin6_addr;
158 		port = sin6->sin6_port;
159 		protocol = "AF_INET6";
160 		unspec = IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr);
161 		addrlen = sizeof (sin6->sin6_addr);
162 		break;
163 	default:
164 		return (0);
165 	}
166 
167 	if (inet_ntop(sa->sa_family, addrptr, storage, INET6_ADDRSTRLEN) ==
168 	    NULL) {
169 		printable_addr = dgettext(TEXT_DOMAIN, "Invalid IP address.");
170 	} else {
171 		char prefix[5];	/* "/nnn" with terminator. */
172 
173 		(void) snprintf(prefix, sizeof (prefix), "/%d", prefixlen);
174 		printable_addr = storage;
175 		if (prefixlen != 0) {
176 			(void) strlcat(printable_addr, prefix,
177 			    sizeof (storage));
178 		}
179 	}
180 	if (addr_only) {
181 		if (fprintf(where, "%s", printable_addr) < 0)
182 			return (-1);
183 	} else {
184 		if (fprintf(where, dgettext(TEXT_DOMAIN,
185 		    "%s: port %d, %s"), protocol,
186 		    ntohs(port), printable_addr) < 0)
187 			return (-1);
188 		if (ignore_nss == B_FALSE) {
189 			/*
190 			 * Do AF_independent reverse hostname lookup here.
191 			 */
192 			if (unspec) {
193 				if (fprintf(where,
194 				    dgettext(TEXT_DOMAIN,
195 				    " <unspecified>")) < 0)
196 					return (-1);
197 			} else {
198 				hp = getipnodebyaddr((char *)addrptr, addrlen,
199 				    sa->sa_family, &getipnode_errno);
200 				if (hp != NULL) {
201 					if (fprintf(where,
202 					    " (%s)", hp->h_name) < 0)
203 						return (-1);
204 					freehostent(hp);
205 				} else {
206 					if (fprintf(where,
207 					    dgettext(TEXT_DOMAIN,
208 					    " <unknown>")) < 0)
209 						return (-1);
210 				}
211 			}
212 		}
213 		if (fputs(".\n", where) == EOF)
214 			return (-1);
215 	}
216 	return (0);
217 }
218 
219 /*
220  * Dump a key and bitlen
221  */
222 int
223 dump_key(uint8_t *keyp, uint_t bitlen, FILE *where)
224 {
225 	int	numbytes;
226 
227 	numbytes = SADB_1TO8(bitlen);
228 	/* The & 0x7 is to check for leftover bits. */
229 	if ((bitlen & 0x7) != 0)
230 		numbytes++;
231 	while (numbytes-- != 0) {
232 		if (pflag) {
233 			/* Print no keys if paranoid */
234 			if (fprintf(where, "XX") < 0)
235 				return (-1);
236 		} else {
237 			if (fprintf(where, "%02x", *keyp++) < 0)
238 				return (-1);
239 		}
240 	}
241 	if (fprintf(where, "/%u", bitlen) < 0)
242 		return (-1);
243 	return (0);
244 }
245 
246 /*
247  * Print an authentication or encryption algorithm
248  */
249 static int
250 dump_generic_alg(uint8_t alg_num, int proto_num, FILE *where)
251 {
252 	struct ipsecalgent *alg;
253 
254 	alg = getipsecalgbynum(alg_num, proto_num, NULL);
255 	if (alg == NULL) {
256 		if (fprintf(where, dgettext(TEXT_DOMAIN,
257 		    "<unknown %u>"), alg_num) < 0)
258 			return (-1);
259 		return (0);
260 	}
261 
262 	/*
263 	 * Special-case <none> for backward output compat.
264 	 * Assume that SADB_AALG_NONE == SADB_EALG_NONE.
265 	 */
266 	if (alg_num == SADB_AALG_NONE) {
267 		if (fputs(dgettext(TEXT_DOMAIN,
268 		    "<none>"), where) == EOF)
269 			return (-1);
270 	} else {
271 		if (fputs(alg->a_names[0], where) == EOF)
272 			return (-1);
273 	}
274 
275 	freeipsecalgent(alg);
276 	return (0);
277 }
278 
279 int
280 dump_aalg(uint8_t aalg, FILE *where)
281 {
282 	return (dump_generic_alg(aalg, IPSEC_PROTO_AH, where));
283 }
284 
285 int
286 dump_ealg(uint8_t ealg, FILE *where)
287 {
288 	return (dump_generic_alg(ealg, IPSEC_PROTO_ESP, where));
289 }
290 
291 /*
292  * Print an SADB_IDENTTYPE string
293  *
294  * Also return TRUE if the actual ident may be printed, FALSE if not.
295  *
296  * If rc is not NULL, set its value to -1 if an error occured while writing
297  * to the specified file, zero otherwise.
298  */
299 boolean_t
300 dump_sadb_idtype(uint8_t idtype, FILE *where, int *rc)
301 {
302 	boolean_t canprint = B_TRUE;
303 	int rc_val = 0;
304 
305 	switch (idtype) {
306 	case SADB_IDENTTYPE_PREFIX:
307 		if (fputs(dgettext(TEXT_DOMAIN, "prefix"), where) == EOF)
308 			rc_val = -1;
309 		break;
310 	case SADB_IDENTTYPE_FQDN:
311 		if (fputs(dgettext(TEXT_DOMAIN, "FQDN"), where) == EOF)
312 			rc_val = -1;
313 		break;
314 	case SADB_IDENTTYPE_USER_FQDN:
315 		if (fputs(dgettext(TEXT_DOMAIN,
316 		    "user-FQDN (mbox)"), where) == EOF)
317 			rc_val = -1;
318 		break;
319 	case SADB_X_IDENTTYPE_DN:
320 		if (fputs(dgettext(TEXT_DOMAIN, "ASN.1 DER Distinguished Name"),
321 		    where) == EOF)
322 			rc_val = -1;
323 		canprint = B_FALSE;
324 		break;
325 	case SADB_X_IDENTTYPE_GN:
326 		if (fputs(dgettext(TEXT_DOMAIN, "ASN.1 DER Generic Name"),
327 		    where) == EOF)
328 			rc_val = -1;
329 		canprint = B_FALSE;
330 		break;
331 	case SADB_X_IDENTTYPE_KEY_ID:
332 		if (fputs(dgettext(TEXT_DOMAIN, "Generic key id"),
333 		    where) == EOF)
334 			rc_val = -1;
335 		break;
336 	case SADB_X_IDENTTYPE_ADDR_RANGE:
337 		if (fputs(dgettext(TEXT_DOMAIN, "Address range"), where) == EOF)
338 			rc_val = -1;
339 		break;
340 	default:
341 		if (fprintf(where, dgettext(TEXT_DOMAIN,
342 		    "<unknown %u>"), idtype) < 0)
343 			rc_val = -1;
344 		break;
345 	}
346 
347 	if (rc != NULL)
348 		*rc = rc_val;
349 
350 	return (canprint);
351 }
352 
353 /*
354  * Slice an argv/argc vector from an interactive line or a read-file line.
355  */
356 static int
357 create_argv(char *ibuf, int *newargc, char ***thisargv)
358 {
359 	unsigned int argvlen = START_ARG;
360 	char **current;
361 	boolean_t firstchar = B_TRUE;
362 	boolean_t inquotes = B_FALSE;
363 
364 	*thisargv = malloc(sizeof (char *) * argvlen);
365 	if ((*thisargv) == NULL)
366 		return (MEMORY_ALLOCATION);
367 	current = *thisargv;
368 	*current = NULL;
369 
370 	for (; *ibuf != '\0'; ibuf++) {
371 		if (isspace(*ibuf)) {
372 			if (inquotes) {
373 				continue;
374 			}
375 			if (*current != NULL) {
376 				*ibuf = '\0';
377 				current++;
378 				if (*thisargv + argvlen == current) {
379 					/* Regrow ***thisargv. */
380 					if (argvlen == TOO_MANY_ARGS) {
381 						free(*thisargv);
382 						return (TOO_MANY_TOKENS);
383 					}
384 					/* Double the allocation. */
385 					current = realloc(*thisargv,
386 					    sizeof (char *) * (argvlen << 1));
387 					if (current == NULL) {
388 						free(*thisargv);
389 						return (MEMORY_ALLOCATION);
390 					}
391 					*thisargv = current;
392 					current += argvlen;
393 					argvlen <<= 1;	/* Double the size. */
394 				}
395 				*current = NULL;
396 			}
397 		} else {
398 			if (firstchar) {
399 				firstchar = B_FALSE;
400 				if (*ibuf == COMMENT_CHAR || *ibuf == '\n') {
401 					free(*thisargv);
402 					return (COMMENT_LINE);
403 				}
404 			}
405 			if (*ibuf == QUOTE_CHAR) {
406 				if (inquotes) {
407 					inquotes = B_FALSE;
408 					*ibuf = '\0';
409 				} else {
410 					inquotes = B_TRUE;
411 				}
412 				continue;
413 			}
414 			if (*current == NULL) {
415 				*current = ibuf;
416 				(*newargc)++;
417 			}
418 		}
419 	}
420 
421 	/*
422 	 * Tricky corner case...
423 	 * I've parsed _exactly_ the amount of args as I have space.  It
424 	 * won't return NULL-terminated, and bad things will happen to
425 	 * the caller.
426 	 */
427 	if (argvlen == *newargc) {
428 		current = realloc(*thisargv, sizeof (char *) * (argvlen + 1));
429 		if (current == NULL) {
430 			free(*thisargv);
431 			return (MEMORY_ALLOCATION);
432 		}
433 		*thisargv = current;
434 		current[argvlen] = NULL;
435 	}
436 
437 	return (SUCCESS);
438 }
439 
440 /*
441  * Enter a mode where commands are read from a file.  Treat stdin special.
442  */
443 void
444 do_interactive(FILE *infile, char *configfile, char *promptstring,
445     char *my_fmri, parse_cmdln_fn parseit)
446 {
447 	char		ibuf[IBUF_SIZE], holder[IBUF_SIZE];
448 	char		*hptr, **thisargv, *ebuf;
449 	int		thisargc;
450 	boolean_t	continue_in_progress = B_FALSE;
451 
452 	(void) setjmp(env);
453 
454 	ebuf = NULL;
455 	interactive = B_TRUE;
456 	bzero(ibuf, IBUF_SIZE);
457 
458 	if (infile == stdin) {
459 		(void) printf("%s", promptstring);
460 		(void) fflush(stdout);
461 	} else {
462 		readfile = B_TRUE;
463 	}
464 
465 	while (fgets(ibuf, IBUF_SIZE, infile) != NULL) {
466 		if (readfile)
467 			lineno++;
468 		thisargc = 0;
469 		thisargv = NULL;
470 
471 		/*
472 		 * Check byte IBUF_SIZE - 2, because byte IBUF_SIZE - 1 will
473 		 * be null-terminated because of fgets().
474 		 */
475 		if (ibuf[IBUF_SIZE - 2] != '\0') {
476 			ipsecutil_exit(SERVICE_FATAL, my_fmri, debugfile,
477 			    dgettext(TEXT_DOMAIN, "Line %d too big."), lineno);
478 		}
479 
480 		if (!continue_in_progress) {
481 			/* Use -2 because of \n from fgets. */
482 			if (ibuf[strlen(ibuf) - 2] == CONT_CHAR) {
483 				/*
484 				 * Can use strcpy here, I've checked the
485 				 * length already.
486 				 */
487 				(void) strcpy(holder, ibuf);
488 				hptr = &(holder[strlen(holder)]);
489 
490 				/* Remove the CONT_CHAR from the string. */
491 				hptr[-2] = ' ';
492 
493 				continue_in_progress = B_TRUE;
494 				bzero(ibuf, IBUF_SIZE);
495 				continue;
496 			}
497 		} else {
498 			/* Handle continuations... */
499 			(void) strncpy(hptr, ibuf,
500 			    (size_t)(&(holder[IBUF_SIZE]) - hptr));
501 			if (holder[IBUF_SIZE - 1] != '\0') {
502 				ipsecutil_exit(SERVICE_FATAL, my_fmri,
503 				    debugfile, dgettext(TEXT_DOMAIN,
504 				    "Command buffer overrun."));
505 			}
506 			/* Use - 2 because of \n from fgets. */
507 			if (hptr[strlen(hptr) - 2] == CONT_CHAR) {
508 				bzero(ibuf, IBUF_SIZE);
509 				hptr += strlen(hptr);
510 
511 				/* Remove the CONT_CHAR from the string. */
512 				hptr[-2] = ' ';
513 
514 				continue;
515 			} else {
516 				continue_in_progress = B_FALSE;
517 				/*
518 				 * I've already checked the length...
519 				 */
520 				(void) strcpy(ibuf, holder);
521 			}
522 		}
523 
524 		/*
525 		 * Just in case the command fails keep a copy of the
526 		 * command buffer for diagnostic output.
527 		 */
528 		if (readfile) {
529 			/*
530 			 * The error buffer needs to be big enough to
531 			 * hold the longest command string, plus
532 			 * some extra text, see below.
533 			 */
534 			ebuf = calloc((IBUF_SIZE * 2), sizeof (char));
535 			if (ebuf == NULL) {
536 				ipsecutil_exit(SERVICE_FATAL, my_fmri,
537 				    debugfile, dgettext(TEXT_DOMAIN,
538 				    "Memory allocation error."));
539 			} else {
540 				(void) snprintf(ebuf, (IBUF_SIZE * 2),
541 				    dgettext(TEXT_DOMAIN,
542 				    "Config file entry near line %u "
543 				    "caused error(s) or warnings:\n\n%s\n\n"),
544 				    lineno, ibuf);
545 			}
546 		}
547 
548 		switch (create_argv(ibuf, &thisargc, &thisargv)) {
549 		case TOO_MANY_TOKENS:
550 			ipsecutil_exit(SERVICE_BADCONF, my_fmri, debugfile,
551 			    dgettext(TEXT_DOMAIN, "Too many input tokens."));
552 			break;
553 		case MEMORY_ALLOCATION:
554 			ipsecutil_exit(SERVICE_BADCONF, my_fmri, debugfile,
555 			    dgettext(TEXT_DOMAIN, "Memory allocation error."));
556 			break;
557 		case COMMENT_LINE:
558 			/* Comment line. */
559 			free(ebuf);
560 			break;
561 		default:
562 			if (thisargc != 0) {
563 				lines_parsed++;
564 				/* ebuf consumed */
565 				parseit(thisargc, thisargv, ebuf, readfile);
566 			} else {
567 				free(ebuf);
568 			}
569 			free(thisargv);
570 			if (infile == stdin) {
571 				(void) printf("%s", promptstring);
572 				(void) fflush(stdout);
573 			}
574 			break;
575 		}
576 		bzero(ibuf, IBUF_SIZE);
577 	}
578 
579 	/*
580 	 * The following code is ipseckey specific. This should never be
581 	 * used by ikeadm which also calls this function because ikeadm
582 	 * only runs interactively. If this ever changes this code block
583 	 * sould be revisited.
584 	 */
585 	if (readfile) {
586 		if (lines_parsed != 0 && lines_added == 0) {
587 			ipsecutil_exit(SERVICE_BADCONF, my_fmri, debugfile,
588 			    dgettext(TEXT_DOMAIN, "Configuration file did not "
589 			    "contain any valid SAs"));
590 		}
591 
592 		/*
593 		 * There were errors. Putting the service in maintenance mode.
594 		 * When svc.startd(1M) allows services to degrade themselves,
595 		 * this should be revisited.
596 		 *
597 		 * If this function was called from a program running as a
598 		 * smf_method(5), print a warning message. Don't spew out the
599 		 * errors as these will end up in the smf(5) log file which is
600 		 * publically readable, the errors may contain sensitive
601 		 * information.
602 		 */
603 		if ((lines_added < lines_parsed) && (configfile != NULL)) {
604 			if (my_fmri != NULL) {
605 				ipsecutil_exit(SERVICE_BADCONF, my_fmri,
606 				    debugfile, dgettext(TEXT_DOMAIN,
607 				    "The configuration file contained %d "
608 				    "errors.\n"
609 				    "Manually check the configuration with:\n"
610 				    "ipseckey -c %s\n"
611 				    "Use svcadm(1M) to clear maintenance "
612 				    "condition when errors are resolved.\n"),
613 				    lines_parsed - lines_added, configfile);
614 			} else {
615 				EXIT_BADCONFIG(NULL);
616 			}
617 		} else {
618 			if (my_fmri != NULL)
619 				ipsecutil_exit(SERVICE_EXIT_OK, my_fmri,
620 				    debugfile, dgettext(TEXT_DOMAIN,
621 				    "%d actions successfully processed."),
622 				    lines_added);
623 		}
624 	} else {
625 		(void) putchar('\n');
626 		(void) fflush(stdout);
627 	}
628 	EXIT_OK(NULL);
629 }
630 
631 /*
632  * Functions to parse strings that represent a debug or privilege level.
633  * These functions are copied from main.c and door.c in usr.lib/in.iked/common.
634  * If this file evolves into a common library that may be used by in.iked
635  * as well as the usr.sbin utilities, those duplicate functions should be
636  * deleted.
637  *
638  * A privilege level may be represented by a simple keyword, corresponding
639  * to one of the possible levels.  A debug level may be represented by a
640  * series of keywords, separated by '+' or '-', indicating categories to
641  * be added or removed from the set of categories in the debug level.
642  * For example, +all-op corresponds to level 0xfffffffb (all flags except
643  * for D_OP set); while p1+p2+pfkey corresponds to level 0x38.  Note that
644  * the leading '+' is implicit; the first keyword in the list must be for
645  * a category that is to be added.
646  *
647  * These parsing functions make use of a local version of strtok, strtok_d,
648  * which includes an additional parameter, char *delim.  This param is filled
649  * in with the character which ends the returned token.  In other words,
650  * this version of strtok, in addition to returning the token, also returns
651  * the single character delimiter from the original string which marked the
652  * end of the token.
653  */
654 static char *
655 strtok_d(char *string, const char *sepset, char *delim)
656 {
657 	static char	*lasts;
658 	char		*q, *r;
659 
660 	/* first or subsequent call */
661 	if (string == NULL)
662 		string = lasts;
663 
664 	if (string == 0)		/* return if no tokens remaining */
665 		return (NULL);
666 
667 	q = string + strspn(string, sepset);	/* skip leading separators */
668 
669 	if (*q == '\0')			/* return if no tokens remaining */
670 		return (NULL);
671 
672 	if ((r = strpbrk(q, sepset)) == NULL) {		/* move past token */
673 		lasts = 0;	/* indicate that this is last token */
674 	} else {
675 		*delim = *r;	/* save delimitor */
676 		*r = '\0';
677 		lasts = r + 1;
678 	}
679 	return (q);
680 }
681 
682 static keywdtab_t	privtab[] = {
683 	{ IKE_PRIV_MINIMUM,	"base" },
684 	{ IKE_PRIV_MODKEYS,	"modkeys" },
685 	{ IKE_PRIV_KEYMAT,	"keymat" },
686 	{ IKE_PRIV_MINIMUM,	"0" },
687 };
688 
689 int
690 privstr2num(char *str)
691 {
692 	keywdtab_t	*pp;
693 	char		*endp;
694 	int		 priv;
695 
696 	for (pp = privtab; pp < A_END(privtab); pp++) {
697 		if (strcasecmp(str, pp->kw_str) == 0)
698 			return (pp->kw_tag);
699 	}
700 
701 	priv = strtol(str, &endp, 0);
702 	if (*endp == '\0')
703 		return (priv);
704 
705 	return (-1);
706 }
707 
708 static keywdtab_t	dbgtab[] = {
709 	{ D_CERT,	"cert" },
710 	{ D_KEY,	"key" },
711 	{ D_OP,		"op" },
712 	{ D_P1,		"p1" },
713 	{ D_P1,		"phase1" },
714 	{ D_P2,		"p2" },
715 	{ D_P2,		"phase2" },
716 	{ D_PFKEY,	"pfkey" },
717 	{ D_POL,	"pol" },
718 	{ D_POL,	"policy" },
719 	{ D_PROP,	"prop" },
720 	{ D_DOOR,	"door" },
721 	{ D_CONFIG,	"config" },
722 	{ D_ALL,	"all" },
723 	{ 0,		"0" },
724 };
725 
726 int
727 dbgstr2num(char *str)
728 {
729 	keywdtab_t	*dp;
730 
731 	for (dp = dbgtab; dp < A_END(dbgtab); dp++) {
732 		if (strcasecmp(str, dp->kw_str) == 0)
733 			return (dp->kw_tag);
734 	}
735 	return (D_INVALID);
736 }
737 
738 int
739 parsedbgopts(char *optarg)
740 {
741 	char	*argp, *endp, op, nextop;
742 	int	mask = 0, new;
743 
744 	mask = strtol(optarg, &endp, 0);
745 	if (*endp == '\0')
746 		return (mask);
747 
748 	op = optarg[0];
749 	if (op != '-')
750 		op = '+';
751 	argp = strtok_d(optarg, "+-", &nextop);
752 	do {
753 		new = dbgstr2num(argp);
754 		if (new == D_INVALID) {
755 			/* we encountered an invalid keywd */
756 			return (new);
757 		}
758 		if (op == '+') {
759 			mask |= new;
760 		} else {
761 			mask &= ~new;
762 		}
763 		op = nextop;
764 	} while ((argp = strtok_d(NULL, "+-", &nextop)) != NULL);
765 
766 	return (mask);
767 }
768 
769 
770 /*
771  * functions to manipulate the kmcookie-label mapping file
772  */
773 
774 /*
775  * Open, lockf, fdopen the given file, returning a FILE * on success,
776  * or NULL on failure.
777  */
778 FILE *
779 kmc_open_and_lock(char *name)
780 {
781 	int	fd, rtnerr;
782 	FILE	*fp;
783 
784 	if ((fd = open(name, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR)) < 0) {
785 		return (NULL);
786 	}
787 	if (lockf(fd, F_LOCK, 0) < 0) {
788 		return (NULL);
789 	}
790 	if ((fp = fdopen(fd, "a+")) == NULL) {
791 		return (NULL);
792 	}
793 	if (fseek(fp, 0, SEEK_SET) < 0) {
794 		/* save errno in case fclose changes it */
795 		rtnerr = errno;
796 		(void) fclose(fp);
797 		errno = rtnerr;
798 		return (NULL);
799 	}
800 	return (fp);
801 }
802 
803 /*
804  * Extract an integer cookie and string label from a line from the
805  * kmcookie-label file.  Return -1 on failure, 0 on success.
806  */
807 int
808 kmc_parse_line(char *line, int *cookie, char **label)
809 {
810 	char	*cookiestr;
811 
812 	*cookie = 0;
813 	*label = NULL;
814 
815 	cookiestr = strtok(line, " \t\n");
816 	if (cookiestr == NULL) {
817 		return (-1);
818 	}
819 
820 	/* Everything that follows, up to the newline, is the label. */
821 	*label = strtok(NULL, "\n");
822 	if (*label == NULL) {
823 		return (-1);
824 	}
825 
826 	*cookie = atoi(cookiestr);
827 	return (0);
828 }
829 
830 /*
831  * Insert a mapping into the file (if it's not already there), given the
832  * new label.  Return the assigned cookie, or -1 on error.
833  */
834 int
835 kmc_insert_mapping(char *label)
836 {
837 	FILE	*map;
838 	char	linebuf[IBUF_SIZE];
839 	char	*cur_label;
840 	int	max_cookie = 0, cur_cookie, rtn_cookie;
841 	int	rtnerr = 0;
842 	boolean_t	found = B_FALSE;
843 
844 	/* open and lock the file; will sleep until lock is available */
845 	if ((map = kmc_open_and_lock(KMCFILE)) == NULL) {
846 		/* kmc_open_and_lock() sets errno appropriately */
847 		return (-1);
848 	}
849 
850 	while (fgets(linebuf, sizeof (linebuf), map) != NULL) {
851 
852 		/* Skip blank lines, which often come near EOF. */
853 		if (strlen(linebuf) == 0)
854 			continue;
855 
856 		if (kmc_parse_line(linebuf, &cur_cookie, &cur_label) < 0) {
857 			rtnerr = EINVAL;
858 			goto error;
859 		}
860 
861 		if (cur_cookie > max_cookie)
862 			max_cookie = cur_cookie;
863 
864 		if ((!found) && (strcmp(cur_label, label) == 0)) {
865 			found = B_TRUE;
866 			rtn_cookie = cur_cookie;
867 		}
868 	}
869 
870 	if (!found) {
871 		rtn_cookie = ++max_cookie;
872 		if ((fprintf(map, "%u\t%s\n", rtn_cookie, label) < 0) ||
873 		    (fflush(map) < 0)) {
874 			rtnerr = errno;
875 			goto error;
876 		}
877 	}
878 	(void) fclose(map);
879 
880 	return (rtn_cookie);
881 
882 error:
883 	(void) fclose(map);
884 	errno = rtnerr;
885 	return (-1);
886 }
887 
888 /*
889  * Lookup the given cookie and return its corresponding label.  Return
890  * a pointer to the label on success, NULL on error (or if the label is
891  * not found).  Note that the returned label pointer points to a static
892  * string, so the label will be overwritten by a subsequent call to the
893  * function; the function is also not thread-safe as a result.
894  */
895 char *
896 kmc_lookup_by_cookie(int cookie)
897 {
898 	FILE		*map;
899 	static char	linebuf[IBUF_SIZE];
900 	char		*cur_label;
901 	int		cur_cookie;
902 
903 	if ((map = kmc_open_and_lock(KMCFILE)) == NULL) {
904 		return (NULL);
905 	}
906 
907 	while (fgets(linebuf, sizeof (linebuf), map) != NULL) {
908 
909 		if (kmc_parse_line(linebuf, &cur_cookie, &cur_label) < 0) {
910 			(void) fclose(map);
911 			return (NULL);
912 		}
913 
914 		if (cookie == cur_cookie) {
915 			(void) fclose(map);
916 			return (cur_label);
917 		}
918 	}
919 	(void) fclose(map);
920 
921 	return (NULL);
922 }
923 
924 /*
925  * Parse basic extension headers and return in the passed-in pointer vector.
926  * Return values include:
927  *
928  *	KGE_OK	Everything's nice and parsed out.
929  *		If there are no extensions, place NULL in extv[0].
930  *	KGE_DUP	There is a duplicate extension.
931  *		First instance in appropriate bin.  First duplicate in
932  *		extv[0].
933  *	KGE_UNK	Unknown extension type encountered.  extv[0] contains
934  *		unknown header.
935  *	KGE_LEN	Extension length error.
936  *	KGE_CHK	High-level reality check failed on specific extension.
937  *
938  * My apologies for some of the pointer arithmetic in here.  I'm thinking
939  * like an assembly programmer, yet trying to make the compiler happy.
940  */
941 int
942 spdsock_get_ext(spd_ext_t *extv[], spd_msg_t *basehdr, uint_t msgsize,
943     char *diag_buf, uint_t diag_buf_len)
944 {
945 	int i;
946 
947 	if (diag_buf != NULL)
948 		diag_buf[0] = '\0';
949 
950 	for (i = 1; i <= SPD_EXT_MAX; i++)
951 		extv[i] = NULL;
952 
953 	i = 0;
954 	/* Use extv[0] as the "current working pointer". */
955 
956 	extv[0] = (spd_ext_t *)(basehdr + 1);
957 	msgsize = SPD_64TO8(msgsize);
958 
959 	while ((char *)extv[0] < ((char *)basehdr + msgsize)) {
960 		/* Check for unknown headers. */
961 		i++;
962 
963 		if (extv[0]->spd_ext_type == 0 ||
964 		    extv[0]->spd_ext_type > SPD_EXT_MAX) {
965 			if (diag_buf != NULL) {
966 				(void) snprintf(diag_buf, diag_buf_len,
967 				    "spdsock ext 0x%X unknown: 0x%X",
968 				    i, extv[0]->spd_ext_type);
969 			}
970 			return (KGE_UNK);
971 		}
972 
973 		/*
974 		 * Check length.  Use uint64_t because extlen is in units
975 		 * of 64-bit words.  If length goes beyond the msgsize,
976 		 * return an error.  (Zero length also qualifies here.)
977 		 */
978 		if (extv[0]->spd_ext_len == 0 ||
979 		    (uint8_t *)((uint64_t *)extv[0] + extv[0]->spd_ext_len) >
980 		    (uint8_t *)((uint8_t *)basehdr + msgsize))
981 			return (KGE_LEN);
982 
983 		/* Check for redundant headers. */
984 		if (extv[extv[0]->spd_ext_type] != NULL)
985 			return (KGE_DUP);
986 
987 		/* If I make it here, assign the appropriate bin. */
988 		extv[extv[0]->spd_ext_type] = extv[0];
989 
990 		/* Advance pointer (See above for uint64_t ptr reasoning.) */
991 		extv[0] = (spd_ext_t *)
992 		    ((uint64_t *)extv[0] + extv[0]->spd_ext_len);
993 	}
994 
995 	/* Everything's cool. */
996 
997 	/*
998 	 * If extv[0] == NULL, then there are no extension headers in this
999 	 * message.  Ensure that this is the case.
1000 	 */
1001 	if (extv[0] == (spd_ext_t *)(basehdr + 1))
1002 		extv[0] = NULL;
1003 
1004 	return (KGE_OK);
1005 }
1006 
1007 const char *
1008 spdsock_diag(int diagnostic)
1009 {
1010 	switch (diagnostic) {
1011 	case SPD_DIAGNOSTIC_NONE:
1012 		return (dgettext(TEXT_DOMAIN, "no error"));
1013 	case SPD_DIAGNOSTIC_UNKNOWN_EXT:
1014 		return (dgettext(TEXT_DOMAIN, "unknown extension"));
1015 	case SPD_DIAGNOSTIC_BAD_EXTLEN:
1016 		return (dgettext(TEXT_DOMAIN, "bad extension length"));
1017 	case SPD_DIAGNOSTIC_NO_RULE_EXT:
1018 		return (dgettext(TEXT_DOMAIN, "no rule extension"));
1019 	case SPD_DIAGNOSTIC_BAD_ADDR_LEN:
1020 		return (dgettext(TEXT_DOMAIN, "bad address len"));
1021 	case SPD_DIAGNOSTIC_MIXED_AF:
1022 		return (dgettext(TEXT_DOMAIN, "mixed address family"));
1023 	case SPD_DIAGNOSTIC_ADD_NO_MEM:
1024 		return (dgettext(TEXT_DOMAIN, "add: no memory"));
1025 	case SPD_DIAGNOSTIC_ADD_WRONG_ACT_COUNT:
1026 		return (dgettext(TEXT_DOMAIN, "add: wrong action count"));
1027 	case SPD_DIAGNOSTIC_ADD_BAD_TYPE:
1028 		return (dgettext(TEXT_DOMAIN, "add: bad type"));
1029 	case SPD_DIAGNOSTIC_ADD_BAD_FLAGS:
1030 		return (dgettext(TEXT_DOMAIN, "add: bad flags"));
1031 	case SPD_DIAGNOSTIC_ADD_INCON_FLAGS:
1032 		return (dgettext(TEXT_DOMAIN, "add: inconsistent flags"));
1033 	case SPD_DIAGNOSTIC_MALFORMED_LCLPORT:
1034 		return (dgettext(TEXT_DOMAIN, "malformed local port"));
1035 	case SPD_DIAGNOSTIC_DUPLICATE_LCLPORT:
1036 		return (dgettext(TEXT_DOMAIN, "duplicate local port"));
1037 	case SPD_DIAGNOSTIC_MALFORMED_REMPORT:
1038 		return (dgettext(TEXT_DOMAIN, "malformed remote port"));
1039 	case SPD_DIAGNOSTIC_DUPLICATE_REMPORT:
1040 		return (dgettext(TEXT_DOMAIN, "duplicate remote port"));
1041 	case SPD_DIAGNOSTIC_MALFORMED_PROTO:
1042 		return (dgettext(TEXT_DOMAIN, "malformed proto"));
1043 	case SPD_DIAGNOSTIC_DUPLICATE_PROTO:
1044 		return (dgettext(TEXT_DOMAIN, "duplicate proto"));
1045 	case SPD_DIAGNOSTIC_MALFORMED_LCLADDR:
1046 		return (dgettext(TEXT_DOMAIN, "malformed local address"));
1047 	case SPD_DIAGNOSTIC_DUPLICATE_LCLADDR:
1048 		return (dgettext(TEXT_DOMAIN, "duplicate local address"));
1049 	case SPD_DIAGNOSTIC_MALFORMED_REMADDR:
1050 		return (dgettext(TEXT_DOMAIN, "malformed remote address"));
1051 	case SPD_DIAGNOSTIC_DUPLICATE_REMADDR:
1052 		return (dgettext(TEXT_DOMAIN, "duplicate remote address"));
1053 	case SPD_DIAGNOSTIC_MALFORMED_ACTION:
1054 		return (dgettext(TEXT_DOMAIN, "malformed action"));
1055 	case SPD_DIAGNOSTIC_DUPLICATE_ACTION:
1056 		return (dgettext(TEXT_DOMAIN, "duplicate action"));
1057 	case SPD_DIAGNOSTIC_MALFORMED_RULE:
1058 		return (dgettext(TEXT_DOMAIN, "malformed rule"));
1059 	case SPD_DIAGNOSTIC_DUPLICATE_RULE:
1060 		return (dgettext(TEXT_DOMAIN, "duplicate rule"));
1061 	case SPD_DIAGNOSTIC_MALFORMED_RULESET:
1062 		return (dgettext(TEXT_DOMAIN, "malformed ruleset"));
1063 	case SPD_DIAGNOSTIC_DUPLICATE_RULESET:
1064 		return (dgettext(TEXT_DOMAIN, "duplicate ruleset"));
1065 	case SPD_DIAGNOSTIC_INVALID_RULE_INDEX:
1066 		return (dgettext(TEXT_DOMAIN, "invalid rule index"));
1067 	case SPD_DIAGNOSTIC_BAD_SPDID:
1068 		return (dgettext(TEXT_DOMAIN, "bad spdid"));
1069 	case SPD_DIAGNOSTIC_BAD_MSG_TYPE:
1070 		return (dgettext(TEXT_DOMAIN, "bad message type"));
1071 	case SPD_DIAGNOSTIC_UNSUPP_AH_ALG:
1072 		return (dgettext(TEXT_DOMAIN, "unsupported AH algorithm"));
1073 	case SPD_DIAGNOSTIC_UNSUPP_ESP_ENCR_ALG:
1074 		return (dgettext(TEXT_DOMAIN,
1075 		    "unsupported ESP encryption algorithm"));
1076 	case SPD_DIAGNOSTIC_UNSUPP_ESP_AUTH_ALG:
1077 		return (dgettext(TEXT_DOMAIN,
1078 		    "unsupported ESP authentication algorithm"));
1079 	case SPD_DIAGNOSTIC_UNSUPP_AH_KEYSIZE:
1080 		return (dgettext(TEXT_DOMAIN, "unsupported AH key size"));
1081 	case SPD_DIAGNOSTIC_UNSUPP_ESP_ENCR_KEYSIZE:
1082 		return (dgettext(TEXT_DOMAIN,
1083 		    "unsupported ESP encryption key size"));
1084 	case SPD_DIAGNOSTIC_UNSUPP_ESP_AUTH_KEYSIZE:
1085 		return (dgettext(TEXT_DOMAIN,
1086 		    "unsupported ESP authentication key size"));
1087 	case SPD_DIAGNOSTIC_NO_ACTION_EXT:
1088 		return (dgettext(TEXT_DOMAIN, "No ACTION extension"));
1089 	case SPD_DIAGNOSTIC_ALG_ID_RANGE:
1090 		return (dgettext(TEXT_DOMAIN, "invalid algorithm identifer"));
1091 	case SPD_DIAGNOSTIC_ALG_NUM_KEY_SIZES:
1092 		return (dgettext(TEXT_DOMAIN,
1093 		    "number of key sizes inconsistent"));
1094 	case SPD_DIAGNOSTIC_ALG_NUM_BLOCK_SIZES:
1095 		return (dgettext(TEXT_DOMAIN,
1096 		    "number of block sizes inconsistent"));
1097 	case SPD_DIAGNOSTIC_ALG_MECH_NAME_LEN:
1098 		return (dgettext(TEXT_DOMAIN, "invalid mechanism name length"));
1099 	case SPD_DIAGNOSTIC_NOT_GLOBAL_OP:
1100 		return (dgettext(TEXT_DOMAIN,
1101 		    "operation not applicable to all policies"));
1102 	case SPD_DIAGNOSTIC_NO_TUNNEL_SELECTORS:
1103 		return (dgettext(TEXT_DOMAIN,
1104 		    "using selectors on a transport-mode tunnel"));
1105 	default:
1106 		return (dgettext(TEXT_DOMAIN, "unknown diagnostic"));
1107 	}
1108 }
1109 
1110 /*
1111  * PF_KEY Diagnostic table.
1112  *
1113  * PF_KEY NOTE:  If you change pfkeyv2.h's SADB_X_DIAGNOSTIC_* space, this is
1114  * where you need to add new messages.
1115  */
1116 
1117 const char *
1118 keysock_diag(int diagnostic)
1119 {
1120 	switch (diagnostic) {
1121 	case SADB_X_DIAGNOSTIC_NONE:
1122 		return (dgettext(TEXT_DOMAIN, "No diagnostic"));
1123 	case SADB_X_DIAGNOSTIC_UNKNOWN_MSG:
1124 		return (dgettext(TEXT_DOMAIN, "Unknown message type"));
1125 	case SADB_X_DIAGNOSTIC_UNKNOWN_EXT:
1126 		return (dgettext(TEXT_DOMAIN, "Unknown extension type"));
1127 	case SADB_X_DIAGNOSTIC_BAD_EXTLEN:
1128 		return (dgettext(TEXT_DOMAIN, "Bad extension length"));
1129 	case SADB_X_DIAGNOSTIC_UNKNOWN_SATYPE:
1130 		return (dgettext(TEXT_DOMAIN,
1131 		    "Unknown Security Association type"));
1132 	case SADB_X_DIAGNOSTIC_SATYPE_NEEDED:
1133 		return (dgettext(TEXT_DOMAIN,
1134 		    "Specific Security Association type needed"));
1135 	case SADB_X_DIAGNOSTIC_NO_SADBS:
1136 		return (dgettext(TEXT_DOMAIN,
1137 		    "No Security Association Databases present"));
1138 	case SADB_X_DIAGNOSTIC_NO_EXT:
1139 		return (dgettext(TEXT_DOMAIN,
1140 		    "No extensions needed for message"));
1141 	case SADB_X_DIAGNOSTIC_BAD_SRC_AF:
1142 		return (dgettext(TEXT_DOMAIN, "Bad source address family"));
1143 	case SADB_X_DIAGNOSTIC_BAD_DST_AF:
1144 		return (dgettext(TEXT_DOMAIN,
1145 		    "Bad destination address family"));
1146 	case SADB_X_DIAGNOSTIC_BAD_PROXY_AF:
1147 		return (dgettext(TEXT_DOMAIN,
1148 		    "Bad inner-source address family"));
1149 	case SADB_X_DIAGNOSTIC_AF_MISMATCH:
1150 		return (dgettext(TEXT_DOMAIN,
1151 		    "Source/destination address family mismatch"));
1152 	case SADB_X_DIAGNOSTIC_BAD_SRC:
1153 		return (dgettext(TEXT_DOMAIN, "Bad source address value"));
1154 	case SADB_X_DIAGNOSTIC_BAD_DST:
1155 		return (dgettext(TEXT_DOMAIN, "Bad destination address value"));
1156 	case SADB_X_DIAGNOSTIC_ALLOC_HSERR:
1157 		return (dgettext(TEXT_DOMAIN,
1158 		    "Soft allocations limit more than hard limit"));
1159 	case SADB_X_DIAGNOSTIC_BYTES_HSERR:
1160 		return (dgettext(TEXT_DOMAIN,
1161 		    "Soft bytes limit more than hard limit"));
1162 	case SADB_X_DIAGNOSTIC_ADDTIME_HSERR:
1163 		return (dgettext(TEXT_DOMAIN, "Soft add expiration time later "
1164 		    "than hard expiration time"));
1165 	case SADB_X_DIAGNOSTIC_USETIME_HSERR:
1166 		return (dgettext(TEXT_DOMAIN, "Soft use expiration time later "
1167 		    "than hard expiration time"));
1168 	case SADB_X_DIAGNOSTIC_MISSING_SRC:
1169 		return (dgettext(TEXT_DOMAIN, "Missing source address"));
1170 	case SADB_X_DIAGNOSTIC_MISSING_DST:
1171 		return (dgettext(TEXT_DOMAIN, "Missing destination address"));
1172 	case SADB_X_DIAGNOSTIC_MISSING_SA:
1173 		return (dgettext(TEXT_DOMAIN, "Missing SA extension"));
1174 	case SADB_X_DIAGNOSTIC_MISSING_EKEY:
1175 		return (dgettext(TEXT_DOMAIN, "Missing encryption key"));
1176 	case SADB_X_DIAGNOSTIC_MISSING_AKEY:
1177 		return (dgettext(TEXT_DOMAIN, "Missing authentication key"));
1178 	case SADB_X_DIAGNOSTIC_MISSING_RANGE:
1179 		return (dgettext(TEXT_DOMAIN, "Missing SPI range"));
1180 	case SADB_X_DIAGNOSTIC_DUPLICATE_SRC:
1181 		return (dgettext(TEXT_DOMAIN, "Duplicate source address"));
1182 	case SADB_X_DIAGNOSTIC_DUPLICATE_DST:
1183 		return (dgettext(TEXT_DOMAIN, "Duplicate destination address"));
1184 	case SADB_X_DIAGNOSTIC_DUPLICATE_SA:
1185 		return (dgettext(TEXT_DOMAIN, "Duplicate SA extension"));
1186 	case SADB_X_DIAGNOSTIC_DUPLICATE_EKEY:
1187 		return (dgettext(TEXT_DOMAIN, "Duplicate encryption key"));
1188 	case SADB_X_DIAGNOSTIC_DUPLICATE_AKEY:
1189 		return (dgettext(TEXT_DOMAIN, "Duplicate authentication key"));
1190 	case SADB_X_DIAGNOSTIC_DUPLICATE_RANGE:
1191 		return (dgettext(TEXT_DOMAIN, "Duplicate SPI range"));
1192 	case SADB_X_DIAGNOSTIC_MALFORMED_SRC:
1193 		return (dgettext(TEXT_DOMAIN, "Malformed source address"));
1194 	case SADB_X_DIAGNOSTIC_MALFORMED_DST:
1195 		return (dgettext(TEXT_DOMAIN, "Malformed destination address"));
1196 	case SADB_X_DIAGNOSTIC_MALFORMED_SA:
1197 		return (dgettext(TEXT_DOMAIN, "Malformed SA extension"));
1198 	case SADB_X_DIAGNOSTIC_MALFORMED_EKEY:
1199 		return (dgettext(TEXT_DOMAIN, "Malformed encryption key"));
1200 	case SADB_X_DIAGNOSTIC_MALFORMED_AKEY:
1201 		return (dgettext(TEXT_DOMAIN, "Malformed authentication key"));
1202 	case SADB_X_DIAGNOSTIC_MALFORMED_RANGE:
1203 		return (dgettext(TEXT_DOMAIN, "Malformed SPI range"));
1204 	case SADB_X_DIAGNOSTIC_AKEY_PRESENT:
1205 		return (dgettext(TEXT_DOMAIN, "Authentication key not needed"));
1206 	case SADB_X_DIAGNOSTIC_EKEY_PRESENT:
1207 		return (dgettext(TEXT_DOMAIN, "Encryption key not needed"));
1208 	case SADB_X_DIAGNOSTIC_PROP_PRESENT:
1209 		return (dgettext(TEXT_DOMAIN, "Proposal extension not needed"));
1210 	case SADB_X_DIAGNOSTIC_SUPP_PRESENT:
1211 		return (dgettext(TEXT_DOMAIN,
1212 		    "Supported algorithms extension not needed"));
1213 	case SADB_X_DIAGNOSTIC_BAD_AALG:
1214 		return (dgettext(TEXT_DOMAIN,
1215 		    "Unsupported authentication algorithm"));
1216 	case SADB_X_DIAGNOSTIC_BAD_EALG:
1217 		return (dgettext(TEXT_DOMAIN,
1218 		    "Unsupported encryption algorithm"));
1219 	case SADB_X_DIAGNOSTIC_BAD_SAFLAGS:
1220 		return (dgettext(TEXT_DOMAIN, "Invalid SA flags"));
1221 	case SADB_X_DIAGNOSTIC_BAD_SASTATE:
1222 		return (dgettext(TEXT_DOMAIN, "Invalid SA state"));
1223 	case SADB_X_DIAGNOSTIC_BAD_AKEYBITS:
1224 		return (dgettext(TEXT_DOMAIN,
1225 		    "Bad number of authentication bits"));
1226 	case SADB_X_DIAGNOSTIC_BAD_EKEYBITS:
1227 		return (dgettext(TEXT_DOMAIN,
1228 		    "Bad number of encryption bits"));
1229 	case SADB_X_DIAGNOSTIC_ENCR_NOTSUPP:
1230 		return (dgettext(TEXT_DOMAIN,
1231 		    "Encryption not supported for this SA type"));
1232 	case SADB_X_DIAGNOSTIC_WEAK_EKEY:
1233 		return (dgettext(TEXT_DOMAIN, "Weak encryption key"));
1234 	case SADB_X_DIAGNOSTIC_WEAK_AKEY:
1235 		return (dgettext(TEXT_DOMAIN, "Weak authentication key"));
1236 	case SADB_X_DIAGNOSTIC_DUPLICATE_KMP:
1237 		return (dgettext(TEXT_DOMAIN,
1238 		    "Duplicate key management protocol"));
1239 	case SADB_X_DIAGNOSTIC_DUPLICATE_KMC:
1240 		return (dgettext(TEXT_DOMAIN,
1241 		    "Duplicate key management cookie"));
1242 	case SADB_X_DIAGNOSTIC_MISSING_NATT_LOC:
1243 		return (dgettext(TEXT_DOMAIN, "Missing NAT-T local address"));
1244 	case SADB_X_DIAGNOSTIC_MISSING_NATT_REM:
1245 		return (dgettext(TEXT_DOMAIN, "Missing NAT-T remote address"));
1246 	case SADB_X_DIAGNOSTIC_DUPLICATE_NATT_LOC:
1247 		return (dgettext(TEXT_DOMAIN, "Duplicate NAT-T local address"));
1248 	case SADB_X_DIAGNOSTIC_DUPLICATE_NATT_REM:
1249 		return (dgettext(TEXT_DOMAIN,
1250 		    "Duplicate NAT-T remote address"));
1251 	case SADB_X_DIAGNOSTIC_MALFORMED_NATT_LOC:
1252 		return (dgettext(TEXT_DOMAIN, "Malformed NAT-T local address"));
1253 	case SADB_X_DIAGNOSTIC_MALFORMED_NATT_REM:
1254 		return (dgettext(TEXT_DOMAIN,
1255 		    "Malformed NAT-T remote address"));
1256 	case SADB_X_DIAGNOSTIC_DUPLICATE_NATT_PORTS:
1257 		return (dgettext(TEXT_DOMAIN, "Duplicate NAT-T ports"));
1258 	case SADB_X_DIAGNOSTIC_MISSING_INNER_SRC:
1259 		return (dgettext(TEXT_DOMAIN, "Missing inner source address"));
1260 	case SADB_X_DIAGNOSTIC_MISSING_INNER_DST:
1261 		return (dgettext(TEXT_DOMAIN,
1262 		    "Missing inner destination address"));
1263 	case SADB_X_DIAGNOSTIC_DUPLICATE_INNER_SRC:
1264 		return (dgettext(TEXT_DOMAIN,
1265 		    "Duplicate inner source address"));
1266 	case SADB_X_DIAGNOSTIC_DUPLICATE_INNER_DST:
1267 		return (dgettext(TEXT_DOMAIN,
1268 		    "Duplicate inner destination address"));
1269 	case SADB_X_DIAGNOSTIC_MALFORMED_INNER_SRC:
1270 		return (dgettext(TEXT_DOMAIN,
1271 		    "Malformed inner source address"));
1272 	case SADB_X_DIAGNOSTIC_MALFORMED_INNER_DST:
1273 		return (dgettext(TEXT_DOMAIN,
1274 		    "Malformed inner destination address"));
1275 	case SADB_X_DIAGNOSTIC_PREFIX_INNER_SRC:
1276 		return (dgettext(TEXT_DOMAIN,
1277 		    "Invalid inner-source prefix length "));
1278 	case SADB_X_DIAGNOSTIC_PREFIX_INNER_DST:
1279 		return (dgettext(TEXT_DOMAIN,
1280 		    "Invalid inner-destination prefix length"));
1281 	case SADB_X_DIAGNOSTIC_BAD_INNER_DST_AF:
1282 		return (dgettext(TEXT_DOMAIN,
1283 		    "Bad inner-destination address family"));
1284 	case SADB_X_DIAGNOSTIC_INNER_AF_MISMATCH:
1285 		return (dgettext(TEXT_DOMAIN,
1286 		    "Inner source/destination address family mismatch"));
1287 	case SADB_X_DIAGNOSTIC_BAD_NATT_REM_AF:
1288 		return (dgettext(TEXT_DOMAIN,
1289 		    "Bad NAT-T remote address family"));
1290 	case SADB_X_DIAGNOSTIC_BAD_NATT_LOC_AF:
1291 		return (dgettext(TEXT_DOMAIN,
1292 		    "Bad NAT-T local address family"));
1293 	case SADB_X_DIAGNOSTIC_PROTO_MISMATCH:
1294 		return (dgettext(TEXT_DOMAIN,
1295 		    "Source/desination protocol mismatch"));
1296 	case SADB_X_DIAGNOSTIC_INNER_PROTO_MISMATCH:
1297 		return (dgettext(TEXT_DOMAIN,
1298 		    "Inner source/desination protocol mismatch"));
1299 	case SADB_X_DIAGNOSTIC_DUAL_PORT_SETS:
1300 		return (dgettext(TEXT_DOMAIN,
1301 		    "Both inner ports and outer ports are set"));
1302 	default:
1303 		return (dgettext(TEXT_DOMAIN, "Unknown diagnostic code"));
1304 	}
1305 }
1306 
1307 /*
1308  * Convert an IPv6 mask to a prefix len.  I assume all IPv6 masks are
1309  * contiguous, so I stop at the first zero bit!
1310  */
1311 int
1312 in_masktoprefix(uint8_t *mask, boolean_t is_v4mapped)
1313 {
1314 	int rc = 0;
1315 	uint8_t last;
1316 	int limit = IPV6_ABITS;
1317 
1318 	if (is_v4mapped) {
1319 		mask += ((IPV6_ABITS - IP_ABITS)/8);
1320 		limit = IP_ABITS;
1321 	}
1322 
1323 	while (*mask == 0xff) {
1324 		rc += 8;
1325 		if (rc == limit)
1326 			return (limit);
1327 		mask++;
1328 	}
1329 
1330 	last = *mask;
1331 	while (last != 0) {
1332 		rc++;
1333 		last = (last << 1) & 0xff;
1334 	}
1335 
1336 	return (rc);
1337 }
1338 
1339 /*
1340  * Expand the diagnostic code into a message.
1341  */
1342 void
1343 print_diagnostic(FILE *file, uint16_t diagnostic)
1344 {
1345 	/* Use two spaces so above strings can fit on the line. */
1346 	(void) fprintf(file, dgettext(TEXT_DOMAIN,
1347 	    "  Diagnostic code %u:  %s.\n"),
1348 	    diagnostic, keysock_diag(diagnostic));
1349 }
1350 
1351 /*
1352  * Prints the base PF_KEY message.
1353  */
1354 void
1355 print_sadb_msg(FILE *file, struct sadb_msg *samsg, time_t wallclock,
1356     boolean_t vflag)
1357 {
1358 	if (wallclock != 0)
1359 		printsatime(file, wallclock, dgettext(TEXT_DOMAIN,
1360 		    "%sTimestamp: %s\n"), "", NULL,
1361 		    vflag);
1362 
1363 	(void) fprintf(file, dgettext(TEXT_DOMAIN,
1364 	    "Base message (version %u) type "),
1365 	    samsg->sadb_msg_version);
1366 	switch (samsg->sadb_msg_type) {
1367 	case SADB_RESERVED:
1368 		(void) fprintf(file, dgettext(TEXT_DOMAIN,
1369 		    "RESERVED (warning: set to 0)"));
1370 		break;
1371 	case SADB_GETSPI:
1372 		(void) fprintf(file, "GETSPI");
1373 		break;
1374 	case SADB_UPDATE:
1375 		(void) fprintf(file, "UPDATE");
1376 		break;
1377 	case SADB_ADD:
1378 		(void) fprintf(file, "ADD");
1379 		break;
1380 	case SADB_DELETE:
1381 		(void) fprintf(file, "DELETE");
1382 		break;
1383 	case SADB_GET:
1384 		(void) fprintf(file, "GET");
1385 		break;
1386 	case SADB_ACQUIRE:
1387 		(void) fprintf(file, "ACQUIRE");
1388 		break;
1389 	case SADB_REGISTER:
1390 		(void) fprintf(file, "REGISTER");
1391 		break;
1392 	case SADB_EXPIRE:
1393 		(void) fprintf(file, "EXPIRE");
1394 		break;
1395 	case SADB_FLUSH:
1396 		(void) fprintf(file, "FLUSH");
1397 		break;
1398 	case SADB_DUMP:
1399 		(void) fprintf(file, "DUMP");
1400 		break;
1401 	case SADB_X_PROMISC:
1402 		(void) fprintf(file, "X_PROMISC");
1403 		break;
1404 	case SADB_X_INVERSE_ACQUIRE:
1405 		(void) fprintf(file, "X_INVERSE_ACQUIRE");
1406 		break;
1407 	default:
1408 		(void) fprintf(file, dgettext(TEXT_DOMAIN,
1409 		    "Unknown (%u)"), samsg->sadb_msg_type);
1410 		break;
1411 	}
1412 	(void) fprintf(file, dgettext(TEXT_DOMAIN, ", SA type "));
1413 
1414 	switch (samsg->sadb_msg_satype) {
1415 	case SADB_SATYPE_UNSPEC:
1416 		(void) fprintf(file, dgettext(TEXT_DOMAIN,
1417 		    "<unspecified/all>"));
1418 		break;
1419 	case SADB_SATYPE_AH:
1420 		(void) fprintf(file, "AH");
1421 		break;
1422 	case SADB_SATYPE_ESP:
1423 		(void) fprintf(file, "ESP");
1424 		break;
1425 	case SADB_SATYPE_RSVP:
1426 		(void) fprintf(file, "RSVP");
1427 		break;
1428 	case SADB_SATYPE_OSPFV2:
1429 		(void) fprintf(file, "OSPFv2");
1430 		break;
1431 	case SADB_SATYPE_RIPV2:
1432 		(void) fprintf(file, "RIPv2");
1433 		break;
1434 	case SADB_SATYPE_MIP:
1435 		(void) fprintf(file, dgettext(TEXT_DOMAIN, "Mobile IP"));
1436 		break;
1437 	default:
1438 		(void) fprintf(file, dgettext(TEXT_DOMAIN,
1439 		    "<unknown %u>"), samsg->sadb_msg_satype);
1440 		break;
1441 	}
1442 
1443 	(void) fprintf(file, ".\n");
1444 
1445 	if (samsg->sadb_msg_errno != 0) {
1446 		(void) fprintf(file, dgettext(TEXT_DOMAIN,
1447 		    "Error %s from PF_KEY.\n"),
1448 		    strerror(samsg->sadb_msg_errno));
1449 		print_diagnostic(file, samsg->sadb_x_msg_diagnostic);
1450 	}
1451 
1452 	(void) fprintf(file, dgettext(TEXT_DOMAIN,
1453 	    "Message length %u bytes, seq=%u, pid=%u.\n"),
1454 	    SADB_64TO8(samsg->sadb_msg_len), samsg->sadb_msg_seq,
1455 	    samsg->sadb_msg_pid);
1456 }
1457 
1458 /*
1459  * Print the SA extension for PF_KEY.
1460  */
1461 void
1462 print_sa(FILE *file, char *prefix, struct sadb_sa *assoc)
1463 {
1464 	if (assoc->sadb_sa_len != SADB_8TO64(sizeof (*assoc))) {
1465 		warnxfp(EFD(file), dgettext(TEXT_DOMAIN,
1466 		    "WARNING: SA info extension length (%u) is bad."),
1467 		    SADB_64TO8(assoc->sadb_sa_len));
1468 	}
1469 
1470 	(void) fprintf(file, dgettext(TEXT_DOMAIN,
1471 	    "%sSADB_ASSOC spi=0x%x, replay=%u, state="),
1472 	    prefix, ntohl(assoc->sadb_sa_spi), assoc->sadb_sa_replay);
1473 	switch (assoc->sadb_sa_state) {
1474 	case SADB_SASTATE_LARVAL:
1475 		(void) fprintf(file, dgettext(TEXT_DOMAIN, "LARVAL"));
1476 		break;
1477 	case SADB_SASTATE_MATURE:
1478 		(void) fprintf(file, dgettext(TEXT_DOMAIN, "MATURE"));
1479 		break;
1480 	case SADB_SASTATE_DYING:
1481 		(void) fprintf(file, dgettext(TEXT_DOMAIN, "DYING"));
1482 		break;
1483 	case SADB_SASTATE_DEAD:
1484 		(void) fprintf(file, dgettext(TEXT_DOMAIN, "DEAD"));
1485 		break;
1486 	default:
1487 		(void) fprintf(file, dgettext(TEXT_DOMAIN,
1488 		    "<unknown %u>"), assoc->sadb_sa_state);
1489 	}
1490 
1491 	if (assoc->sadb_sa_auth != SADB_AALG_NONE) {
1492 		(void) fprintf(file, dgettext(TEXT_DOMAIN,
1493 		    "\n%sAuthentication algorithm = "),
1494 		    prefix);
1495 		(void) dump_aalg(assoc->sadb_sa_auth, file);
1496 	}
1497 
1498 	if (assoc->sadb_sa_encrypt != SADB_EALG_NONE) {
1499 		(void) fprintf(file, dgettext(TEXT_DOMAIN,
1500 		    "\n%sEncryption algorithm = "), prefix);
1501 		(void) dump_ealg(assoc->sadb_sa_encrypt, file);
1502 	}
1503 
1504 	(void) fprintf(file, dgettext(TEXT_DOMAIN, "\n%sflags=0x%x < "), prefix,
1505 	    assoc->sadb_sa_flags);
1506 	if (assoc->sadb_sa_flags & SADB_SAFLAGS_PFS)
1507 		(void) fprintf(file, "PFS ");
1508 	if (assoc->sadb_sa_flags & SADB_SAFLAGS_NOREPLAY)
1509 		(void) fprintf(file, "NOREPLAY ");
1510 
1511 	/* BEGIN Solaris-specific flags. */
1512 	if (assoc->sadb_sa_flags & SADB_X_SAFLAGS_USED)
1513 		(void) fprintf(file, "X_USED ");
1514 	if (assoc->sadb_sa_flags & SADB_X_SAFLAGS_UNIQUE)
1515 		(void) fprintf(file, "X_UNIQUE ");
1516 	if (assoc->sadb_sa_flags & SADB_X_SAFLAGS_AALG1)
1517 		(void) fprintf(file, "X_AALG1 ");
1518 	if (assoc->sadb_sa_flags & SADB_X_SAFLAGS_AALG2)
1519 		(void) fprintf(file, "X_AALG2 ");
1520 	if (assoc->sadb_sa_flags & SADB_X_SAFLAGS_EALG1)
1521 		(void) fprintf(file, "X_EALG1 ");
1522 	if (assoc->sadb_sa_flags & SADB_X_SAFLAGS_EALG2)
1523 		(void) fprintf(file, "X_EALG2 ");
1524 	if (assoc->sadb_sa_flags & SADB_X_SAFLAGS_NATT_LOC)
1525 		(void) fprintf(file, "X_NATT_LOC ");
1526 	if (assoc->sadb_sa_flags & SADB_X_SAFLAGS_NATT_REM)
1527 		(void) fprintf(file, "X_NATT_REM ");
1528 	if (assoc->sadb_sa_flags & SADB_X_SAFLAGS_TUNNEL)
1529 		(void) fprintf(file, "X_TUNNEL ");
1530 	/* END Solaris-specific flags. */
1531 
1532 	(void) fprintf(file, ">\n");
1533 }
1534 
1535 void
1536 printsatime(FILE *file, int64_t lt, const char *msg, const char *pfx,
1537     const char *pfx2, boolean_t vflag)
1538 {
1539 	char tbuf[TBUF_SIZE]; /* For strftime() call. */
1540 	const char *tp = tbuf;
1541 	time_t t = lt;
1542 	struct tm res;
1543 
1544 	if (t != lt) {
1545 		if (lt > 0)
1546 			t = LONG_MAX;
1547 		else
1548 			t = LONG_MIN;
1549 	}
1550 
1551 	if (strftime(tbuf, TBUF_SIZE, NULL, localtime_r(&t, &res)) == 0)
1552 		tp = dgettext(TEXT_DOMAIN, "<time conversion failed>");
1553 	(void) fprintf(file, msg, pfx, tp);
1554 	if (vflag && (pfx2 != NULL))
1555 		(void) fprintf(file, dgettext(TEXT_DOMAIN,
1556 		    "%s\t(raw time value %llu)\n"), pfx2, lt);
1557 }
1558 
1559 /*
1560  * Print the SA lifetime information.  (An SADB_EXT_LIFETIME_* extension.)
1561  */
1562 void
1563 print_lifetimes(FILE *file, time_t wallclock, struct sadb_lifetime *current,
1564     struct sadb_lifetime *hard, struct sadb_lifetime *soft, boolean_t vflag)
1565 {
1566 	int64_t scratch;
1567 	char *soft_prefix = dgettext(TEXT_DOMAIN, "SLT: ");
1568 	char *hard_prefix = dgettext(TEXT_DOMAIN, "HLT: ");
1569 	char *current_prefix = dgettext(TEXT_DOMAIN, "CLT: ");
1570 
1571 	if (current != NULL &&
1572 	    current->sadb_lifetime_len != SADB_8TO64(sizeof (*current))) {
1573 		warnxfp(EFD(file), dgettext(TEXT_DOMAIN,
1574 		    "WARNING: CURRENT lifetime extension length (%u) is bad."),
1575 		    SADB_64TO8(current->sadb_lifetime_len));
1576 	}
1577 
1578 	if (hard != NULL &&
1579 	    hard->sadb_lifetime_len != SADB_8TO64(sizeof (*hard))) {
1580 		warnxfp(EFD(file), dgettext(TEXT_DOMAIN,
1581 		    "WARNING: HARD lifetime extension length (%u) is bad."),
1582 		    SADB_64TO8(hard->sadb_lifetime_len));
1583 	}
1584 
1585 	if (soft != NULL &&
1586 	    soft->sadb_lifetime_len != SADB_8TO64(sizeof (*soft))) {
1587 		warnxfp(EFD(file), dgettext(TEXT_DOMAIN,
1588 		    "WARNING: SOFT lifetime extension length (%u) is bad."),
1589 		    SADB_64TO8(soft->sadb_lifetime_len));
1590 	}
1591 
1592 	(void) fprintf(file, " LT: Lifetime information\n");
1593 
1594 	if (current != NULL) {
1595 		/* Express values as current values. */
1596 		(void) fprintf(file, dgettext(TEXT_DOMAIN,
1597 		    "%s%llu bytes protected, %u allocations used.\n"),
1598 		    current_prefix, current->sadb_lifetime_bytes,
1599 		    current->sadb_lifetime_allocations);
1600 		printsatime(file, current->sadb_lifetime_addtime,
1601 		    dgettext(TEXT_DOMAIN, "%sSA added at time %s\n"),
1602 		    current_prefix, current_prefix, vflag);
1603 		if (current->sadb_lifetime_usetime != 0) {
1604 			printsatime(file, current->sadb_lifetime_usetime,
1605 			    dgettext(TEXT_DOMAIN,
1606 			    "%sSA first used at time %s\n"),
1607 			    current_prefix, current_prefix, vflag);
1608 		}
1609 		printsatime(file, wallclock, dgettext(TEXT_DOMAIN,
1610 		    "%sTime now is %s\n"), current_prefix, current_prefix,
1611 		    vflag);
1612 	}
1613 
1614 	if (soft != NULL) {
1615 		(void) fprintf(file, dgettext(TEXT_DOMAIN,
1616 		    "%sSoft lifetime information:  "),
1617 		    soft_prefix);
1618 		(void) fprintf(file, dgettext(TEXT_DOMAIN,
1619 		    "%llu bytes of lifetime, %u "
1620 		    "allocations.\n"), soft->sadb_lifetime_bytes,
1621 		    soft->sadb_lifetime_allocations);
1622 		(void) fprintf(file, dgettext(TEXT_DOMAIN,
1623 		    "%s%llu seconds of post-add lifetime.\n"),
1624 		    soft_prefix, soft->sadb_lifetime_addtime);
1625 		(void) fprintf(file, dgettext(TEXT_DOMAIN,
1626 		    "%s%llu seconds of post-use lifetime.\n"),
1627 		    soft_prefix, soft->sadb_lifetime_usetime);
1628 		/* If possible, express values as time remaining. */
1629 		if (current != NULL) {
1630 			if (soft->sadb_lifetime_bytes != 0)
1631 				(void) fprintf(file, dgettext(TEXT_DOMAIN,
1632 				    "%s%llu more bytes can be protected.\n"),
1633 				    soft_prefix,
1634 				    (soft->sadb_lifetime_bytes >
1635 				    current->sadb_lifetime_bytes) ?
1636 				    (soft->sadb_lifetime_bytes -
1637 				    current->sadb_lifetime_bytes) : (0));
1638 			if (soft->sadb_lifetime_addtime != 0 ||
1639 			    (soft->sadb_lifetime_usetime != 0 &&
1640 			    current->sadb_lifetime_usetime != 0)) {
1641 				int64_t adddelta, usedelta;
1642 
1643 				if (soft->sadb_lifetime_addtime != 0) {
1644 					adddelta =
1645 					    current->sadb_lifetime_addtime +
1646 					    soft->sadb_lifetime_addtime -
1647 					    wallclock;
1648 				} else {
1649 					adddelta = TIME_MAX;
1650 				}
1651 
1652 				if (soft->sadb_lifetime_usetime != 0 &&
1653 				    current->sadb_lifetime_usetime != 0) {
1654 					usedelta =
1655 					    current->sadb_lifetime_usetime +
1656 					    soft->sadb_lifetime_usetime -
1657 					    wallclock;
1658 				} else {
1659 					usedelta = TIME_MAX;
1660 				}
1661 				(void) fprintf(file, "%s", soft_prefix);
1662 				scratch = MIN(adddelta, usedelta);
1663 				if (scratch >= 0) {
1664 					(void) fprintf(file,
1665 					    dgettext(TEXT_DOMAIN,
1666 					    "Soft expiration occurs in %lld "
1667 					    "seconds, "), scratch);
1668 				} else {
1669 					(void) fprintf(file,
1670 					    dgettext(TEXT_DOMAIN,
1671 					    "Soft expiration occurred "));
1672 				}
1673 				scratch += wallclock;
1674 				printsatime(file, scratch, dgettext(TEXT_DOMAIN,
1675 				    "%sat %s.\n"), "", soft_prefix, vflag);
1676 			}
1677 		}
1678 	}
1679 
1680 	if (hard != NULL) {
1681 		(void) fprintf(file, dgettext(TEXT_DOMAIN,
1682 		    "%sHard lifetime information:  "), hard_prefix);
1683 		(void) fprintf(file, dgettext(TEXT_DOMAIN,
1684 		    "%llu bytes of lifetime, %u allocations.\n"),
1685 		    hard->sadb_lifetime_bytes, hard->sadb_lifetime_allocations);
1686 		(void) fprintf(file, dgettext(TEXT_DOMAIN,
1687 		    "%s%llu seconds of post-add lifetime.\n"),
1688 		    hard_prefix, hard->sadb_lifetime_addtime);
1689 		(void) fprintf(file, dgettext(TEXT_DOMAIN,
1690 		    "%s%llu seconds of post-use lifetime.\n"),
1691 		    hard_prefix, hard->sadb_lifetime_usetime);
1692 		/* If possible, express values as time remaining. */
1693 		if (current != NULL) {
1694 			if (hard->sadb_lifetime_bytes != 0)
1695 				(void) fprintf(file, dgettext(TEXT_DOMAIN,
1696 				    "%s%llu more bytes can be protected.\n"),
1697 				    hard_prefix,
1698 				    (hard->sadb_lifetime_bytes >
1699 				    current->sadb_lifetime_bytes) ?
1700 				    (hard->sadb_lifetime_bytes -
1701 				    current->sadb_lifetime_bytes) : (0));
1702 			if (hard->sadb_lifetime_addtime != 0 ||
1703 			    (hard->sadb_lifetime_usetime != 0 &&
1704 			    current->sadb_lifetime_usetime != 0)) {
1705 				int64_t adddelta, usedelta;
1706 
1707 				if (hard->sadb_lifetime_addtime != 0) {
1708 					adddelta =
1709 					    current->sadb_lifetime_addtime +
1710 					    hard->sadb_lifetime_addtime -
1711 					    wallclock;
1712 				} else {
1713 					adddelta = TIME_MAX;
1714 				}
1715 
1716 				if (hard->sadb_lifetime_usetime != 0 &&
1717 				    current->sadb_lifetime_usetime != 0) {
1718 					usedelta =
1719 					    current->sadb_lifetime_usetime +
1720 					    hard->sadb_lifetime_usetime -
1721 					    wallclock;
1722 				} else {
1723 					usedelta = TIME_MAX;
1724 				}
1725 				(void) fprintf(file, "%s", hard_prefix);
1726 				scratch = MIN(adddelta, usedelta);
1727 				if (scratch >= 0) {
1728 					(void) fprintf(file,
1729 					    dgettext(TEXT_DOMAIN,
1730 					    "Hard expiration occurs in %lld "
1731 					    "seconds, "), scratch);
1732 				} else {
1733 					(void) fprintf(file,
1734 					    dgettext(TEXT_DOMAIN,
1735 					    "Hard expiration occured "));
1736 				}
1737 				scratch += wallclock;
1738 				printsatime(file, scratch, dgettext(TEXT_DOMAIN,
1739 				    "%sat %s.\n"), "", hard_prefix, vflag);
1740 			}
1741 		}
1742 	}
1743 }
1744 
1745 /*
1746  * Print an SADB_EXT_ADDRESS_* extension.
1747  */
1748 void
1749 print_address(FILE *file, char *prefix, struct sadb_address *addr,
1750     boolean_t ignore_nss)
1751 {
1752 	struct protoent *pe;
1753 
1754 	(void) fprintf(file, "%s", prefix);
1755 	switch (addr->sadb_address_exttype) {
1756 	case SADB_EXT_ADDRESS_SRC:
1757 		(void) fprintf(file, dgettext(TEXT_DOMAIN, "Source address "));
1758 		break;
1759 	case SADB_X_EXT_ADDRESS_INNER_SRC:
1760 		(void) fprintf(file, dgettext(TEXT_DOMAIN,
1761 		    "Inner source address "));
1762 		break;
1763 	case SADB_EXT_ADDRESS_DST:
1764 		(void) fprintf(file, dgettext(TEXT_DOMAIN,
1765 		    "Destination address "));
1766 		break;
1767 	case SADB_X_EXT_ADDRESS_INNER_DST:
1768 		(void) fprintf(file, dgettext(TEXT_DOMAIN,
1769 		    "Inner destination address "));
1770 		break;
1771 	case SADB_X_EXT_ADDRESS_NATT_LOC:
1772 		(void) fprintf(file, dgettext(TEXT_DOMAIN,
1773 		    "NAT-T local address "));
1774 		break;
1775 	case SADB_X_EXT_ADDRESS_NATT_REM:
1776 		(void) fprintf(file, dgettext(TEXT_DOMAIN,
1777 		    "NAT-T remote address "));
1778 		break;
1779 	}
1780 
1781 	(void) fprintf(file, dgettext(TEXT_DOMAIN,
1782 	    "(proto=%d"), addr->sadb_address_proto);
1783 	if (ignore_nss == B_FALSE) {
1784 		if (addr->sadb_address_proto == 0) {
1785 			(void) fprintf(file, dgettext(TEXT_DOMAIN,
1786 			    "/<unspecified>"));
1787 		} else if ((pe = getprotobynumber(addr->sadb_address_proto))
1788 		    != NULL) {
1789 			(void) fprintf(file, "/%s", pe->p_name);
1790 		} else {
1791 			(void) fprintf(file, dgettext(TEXT_DOMAIN,
1792 			    "/<unknown>"));
1793 		}
1794 	}
1795 	(void) fprintf(file, dgettext(TEXT_DOMAIN, ")\n%s"), prefix);
1796 	(void) dump_sockaddr((struct sockaddr *)(addr + 1),
1797 	    addr->sadb_address_prefixlen, B_FALSE, file, ignore_nss);
1798 }
1799 
1800 /*
1801  * Print an SADB_EXT_KEY extension.
1802  */
1803 void
1804 print_key(FILE *file, char *prefix, struct sadb_key *key)
1805 {
1806 	(void) fprintf(file, "%s", prefix);
1807 
1808 	switch (key->sadb_key_exttype) {
1809 	case SADB_EXT_KEY_AUTH:
1810 		(void) fprintf(file, dgettext(TEXT_DOMAIN, "Authentication"));
1811 		break;
1812 	case SADB_EXT_KEY_ENCRYPT:
1813 		(void) fprintf(file, dgettext(TEXT_DOMAIN, "Encryption"));
1814 		break;
1815 	}
1816 
1817 	(void) fprintf(file, dgettext(TEXT_DOMAIN, " key.\n%s"), prefix);
1818 	(void) dump_key((uint8_t *)(key + 1), key->sadb_key_bits, file);
1819 	(void) fprintf(file, "\n");
1820 }
1821 
1822 /*
1823  * Print an SADB_EXT_IDENTITY_* extension.
1824  */
1825 void
1826 print_ident(FILE *file, char *prefix, struct sadb_ident *id)
1827 {
1828 	boolean_t canprint = B_TRUE;
1829 
1830 	(void) fprintf(file, "%s", prefix);
1831 	switch (id->sadb_ident_exttype) {
1832 	case SADB_EXT_IDENTITY_SRC:
1833 		(void) fprintf(file, dgettext(TEXT_DOMAIN, "Source"));
1834 		break;
1835 	case SADB_EXT_IDENTITY_DST:
1836 		(void) fprintf(file, dgettext(TEXT_DOMAIN, "Destination"));
1837 		break;
1838 	}
1839 
1840 	(void) fprintf(file, dgettext(TEXT_DOMAIN,
1841 	    " identity, uid=%d, type "), id->sadb_ident_id);
1842 	canprint = dump_sadb_idtype(id->sadb_ident_type, file, NULL);
1843 	(void) fprintf(file, "\n%s", prefix);
1844 	if (canprint)
1845 		(void) fprintf(file, "%s\n", (char *)(id + 1));
1846 	else
1847 		(void) fprintf(file, dgettext(TEXT_DOMAIN, "<cannot print>\n"));
1848 }
1849 
1850 /*
1851  * Print an SADB_SENSITIVITY extension.
1852  */
1853 void
1854 print_sens(FILE *file, char *prefix, struct sadb_sens *sens)
1855 {
1856 	uint64_t *bitmap = (uint64_t *)(sens + 1);
1857 	int i;
1858 
1859 	(void) fprintf(file, dgettext(TEXT_DOMAIN,
1860 	    "%sSensitivity DPD %d, sens level=%d, integ level=%d\n"),
1861 	    prefix, sens->sadb_sens_dpd, sens->sadb_sens_sens_level,
1862 	    sens->sadb_sens_integ_level);
1863 	for (i = 0; sens->sadb_sens_sens_len-- > 0; i++, bitmap++)
1864 		(void) fprintf(file, dgettext(TEXT_DOMAIN,
1865 		    "%s Sensitivity BM extended word %d 0x%llx\n"),
1866 		    prefix, i, *bitmap);
1867 	for (i = 0; sens->sadb_sens_integ_len-- > 0; i++, bitmap++)
1868 		(void) fprintf(stderr, dgettext(TEXT_DOMAIN,
1869 		    "%s Integrity BM extended word %d 0x%llx\n"),
1870 		    prefix, i, *bitmap);
1871 }
1872 
1873 /*
1874  * Print an SADB_EXT_PROPOSAL extension.
1875  */
1876 void
1877 print_prop(FILE *file, char *prefix, struct sadb_prop *prop)
1878 {
1879 	struct sadb_comb *combs;
1880 	int i, numcombs;
1881 
1882 	(void) fprintf(file, dgettext(TEXT_DOMAIN,
1883 	    "%sProposal, replay counter = %u.\n"), prefix,
1884 	    prop->sadb_prop_replay);
1885 
1886 	numcombs = prop->sadb_prop_len - SADB_8TO64(sizeof (*prop));
1887 	numcombs /= SADB_8TO64(sizeof (*combs));
1888 
1889 	combs = (struct sadb_comb *)(prop + 1);
1890 
1891 	for (i = 0; i < numcombs; i++) {
1892 		(void) fprintf(file, dgettext(TEXT_DOMAIN,
1893 		    "%s Combination #%u "), prefix, i + 1);
1894 		if (combs[i].sadb_comb_auth != SADB_AALG_NONE) {
1895 			(void) fprintf(file, dgettext(TEXT_DOMAIN,
1896 			    "Authentication = "));
1897 			(void) dump_aalg(combs[i].sadb_comb_auth, file);
1898 			(void) fprintf(file, dgettext(TEXT_DOMAIN,
1899 			    "  minbits=%u, maxbits=%u.\n%s "),
1900 			    combs[i].sadb_comb_auth_minbits,
1901 			    combs[i].sadb_comb_auth_maxbits, prefix);
1902 		}
1903 
1904 		if (combs[i].sadb_comb_encrypt != SADB_EALG_NONE) {
1905 			(void) fprintf(file, dgettext(TEXT_DOMAIN,
1906 			    "Encryption = "));
1907 			(void) dump_ealg(combs[i].sadb_comb_encrypt, file);
1908 			(void) fprintf(file, dgettext(TEXT_DOMAIN,
1909 			    "  minbits=%u, maxbits=%u.\n%s "),
1910 			    combs[i].sadb_comb_encrypt_minbits,
1911 			    combs[i].sadb_comb_encrypt_maxbits, prefix);
1912 		}
1913 
1914 		(void) fprintf(file, dgettext(TEXT_DOMAIN, "HARD: "));
1915 		if (combs[i].sadb_comb_hard_allocations)
1916 			(void) fprintf(file, dgettext(TEXT_DOMAIN, "alloc=%u "),
1917 			    combs[i].sadb_comb_hard_allocations);
1918 		if (combs[i].sadb_comb_hard_bytes)
1919 			(void) fprintf(file, dgettext(TEXT_DOMAIN,
1920 			    "bytes=%llu "), combs[i].sadb_comb_hard_bytes);
1921 		if (combs[i].sadb_comb_hard_addtime)
1922 			(void) fprintf(file, dgettext(TEXT_DOMAIN,
1923 			    "post-add secs=%llu "),
1924 			    combs[i].sadb_comb_hard_addtime);
1925 		if (combs[i].sadb_comb_hard_usetime)
1926 			(void) fprintf(file, dgettext(TEXT_DOMAIN,
1927 			    "post-use secs=%llu"),
1928 			    combs[i].sadb_comb_hard_usetime);
1929 
1930 		(void) fprintf(file, dgettext(TEXT_DOMAIN, "\n%s SOFT: "),
1931 		    prefix);
1932 		if (combs[i].sadb_comb_soft_allocations)
1933 			(void) fprintf(file, dgettext(TEXT_DOMAIN, "alloc=%u "),
1934 			    combs[i].sadb_comb_soft_allocations);
1935 		if (combs[i].sadb_comb_soft_bytes)
1936 			(void) fprintf(file, dgettext(TEXT_DOMAIN,
1937 			    "bytes=%llu "), combs[i].sadb_comb_soft_bytes);
1938 		if (combs[i].sadb_comb_soft_addtime)
1939 			(void) fprintf(file, dgettext(TEXT_DOMAIN,
1940 			    "post-add secs=%llu "),
1941 			    combs[i].sadb_comb_soft_addtime);
1942 		if (combs[i].sadb_comb_soft_usetime)
1943 			(void) fprintf(file, dgettext(TEXT_DOMAIN,
1944 			    "post-use secs=%llu"),
1945 			    combs[i].sadb_comb_soft_usetime);
1946 		(void) fprintf(file, "\n");
1947 	}
1948 }
1949 
1950 /*
1951  * Print an extended proposal (SADB_X_EXT_EPROP).
1952  */
1953 void
1954 print_eprop(FILE *file, char *prefix, struct sadb_prop *eprop)
1955 {
1956 	uint64_t *sofar;
1957 	struct sadb_x_ecomb *ecomb;
1958 	struct sadb_x_algdesc *algdesc;
1959 	int i, j;
1960 
1961 	(void) fprintf(file, dgettext(TEXT_DOMAIN,
1962 	    "%sExtended Proposal, replay counter = %u, "), prefix,
1963 	    eprop->sadb_prop_replay);
1964 	(void) fprintf(file, dgettext(TEXT_DOMAIN,
1965 	    "number of combinations = %u.\n"), eprop->sadb_x_prop_numecombs);
1966 
1967 	sofar = (uint64_t *)(eprop + 1);
1968 	ecomb = (struct sadb_x_ecomb *)sofar;
1969 
1970 	for (i = 0; i < eprop->sadb_x_prop_numecombs; ) {
1971 		(void) fprintf(file, dgettext(TEXT_DOMAIN,
1972 		    "%s Extended combination #%u:\n"), prefix, ++i);
1973 
1974 		(void) fprintf(file, dgettext(TEXT_DOMAIN, "%s HARD: "),
1975 		    prefix);
1976 		(void) fprintf(file, dgettext(TEXT_DOMAIN, "alloc=%u, "),
1977 		    ecomb->sadb_x_ecomb_hard_allocations);
1978 		(void) fprintf(file, dgettext(TEXT_DOMAIN, "bytes=%llu, "),
1979 		    ecomb->sadb_x_ecomb_hard_bytes);
1980 		(void) fprintf(file, dgettext(TEXT_DOMAIN,
1981 		    "post-add secs=%llu, "), ecomb->sadb_x_ecomb_hard_addtime);
1982 		(void) fprintf(file, dgettext(TEXT_DOMAIN,
1983 		    "post-use secs=%llu\n"), ecomb->sadb_x_ecomb_hard_usetime);
1984 
1985 		(void) fprintf(file, dgettext(TEXT_DOMAIN, "%s SOFT: "),
1986 		    prefix);
1987 		(void) fprintf(file, dgettext(TEXT_DOMAIN, "alloc=%u, "),
1988 		    ecomb->sadb_x_ecomb_soft_allocations);
1989 		(void) fprintf(file, dgettext(TEXT_DOMAIN, "bytes=%llu, "),
1990 		    ecomb->sadb_x_ecomb_soft_bytes);
1991 		(void) fprintf(file, dgettext(TEXT_DOMAIN,
1992 		    "post-add secs=%llu, "),
1993 		    ecomb->sadb_x_ecomb_soft_addtime);
1994 		(void) fprintf(file, dgettext(TEXT_DOMAIN,
1995 		    "post-use secs=%llu\n"), ecomb->sadb_x_ecomb_soft_usetime);
1996 
1997 		sofar = (uint64_t *)(ecomb + 1);
1998 		algdesc = (struct sadb_x_algdesc *)sofar;
1999 
2000 		for (j = 0; j < ecomb->sadb_x_ecomb_numalgs; ) {
2001 			(void) fprintf(file, dgettext(TEXT_DOMAIN,
2002 			    "%s Alg #%u "), prefix, ++j);
2003 			switch (algdesc->sadb_x_algdesc_satype) {
2004 			case SADB_SATYPE_ESP:
2005 				(void) fprintf(file, dgettext(TEXT_DOMAIN,
2006 				    "for ESP "));
2007 				break;
2008 			case SADB_SATYPE_AH:
2009 				(void) fprintf(file, dgettext(TEXT_DOMAIN,
2010 				    "for AH "));
2011 				break;
2012 			default:
2013 				(void) fprintf(file, dgettext(TEXT_DOMAIN,
2014 				    "for satype=%d "),
2015 				    algdesc->sadb_x_algdesc_satype);
2016 			}
2017 			switch (algdesc->sadb_x_algdesc_algtype) {
2018 			case SADB_X_ALGTYPE_CRYPT:
2019 				(void) fprintf(file, dgettext(TEXT_DOMAIN,
2020 				    "Encryption = "));
2021 				(void) dump_ealg(algdesc->sadb_x_algdesc_alg,
2022 				    file);
2023 				break;
2024 			case SADB_X_ALGTYPE_AUTH:
2025 				(void) fprintf(file, dgettext(TEXT_DOMAIN,
2026 				    "Authentication = "));
2027 				(void) dump_aalg(algdesc->sadb_x_algdesc_alg,
2028 				    file);
2029 				break;
2030 			default:
2031 				(void) fprintf(file, dgettext(TEXT_DOMAIN,
2032 				    "algtype(%d) = alg(%d)"),
2033 				    algdesc->sadb_x_algdesc_algtype,
2034 				    algdesc->sadb_x_algdesc_alg);
2035 				break;
2036 			}
2037 
2038 			(void) fprintf(file, dgettext(TEXT_DOMAIN,
2039 			    "  minbits=%u, maxbits=%u.\n"),
2040 			    algdesc->sadb_x_algdesc_minbits,
2041 			    algdesc->sadb_x_algdesc_maxbits);
2042 
2043 			sofar = (uint64_t *)(++algdesc);
2044 		}
2045 		ecomb = (struct sadb_x_ecomb *)sofar;
2046 	}
2047 }
2048 
2049 /*
2050  * Print an SADB_EXT_SUPPORTED extension.
2051  */
2052 void
2053 print_supp(FILE *file, char *prefix, struct sadb_supported *supp)
2054 {
2055 	struct sadb_alg *algs;
2056 	int i, numalgs;
2057 
2058 	(void) fprintf(file, dgettext(TEXT_DOMAIN, "%sSupported "), prefix);
2059 	switch (supp->sadb_supported_exttype) {
2060 	case SADB_EXT_SUPPORTED_AUTH:
2061 		(void) fprintf(file, dgettext(TEXT_DOMAIN, "authentication"));
2062 		break;
2063 	case SADB_EXT_SUPPORTED_ENCRYPT:
2064 		(void) fprintf(file, dgettext(TEXT_DOMAIN, "encryption"));
2065 		break;
2066 	}
2067 	(void) fprintf(file, dgettext(TEXT_DOMAIN, " algorithms.\n"));
2068 
2069 	algs = (struct sadb_alg *)(supp + 1);
2070 	numalgs = supp->sadb_supported_len - SADB_8TO64(sizeof (*supp));
2071 	numalgs /= SADB_8TO64(sizeof (*algs));
2072 	for (i = 0; i < numalgs; i++) {
2073 		uint16_t exttype = supp->sadb_supported_exttype;
2074 
2075 		(void) fprintf(file, "%s", prefix);
2076 		switch (exttype) {
2077 		case SADB_EXT_SUPPORTED_AUTH:
2078 			(void) dump_aalg(algs[i].sadb_alg_id, file);
2079 			break;
2080 		case SADB_EXT_SUPPORTED_ENCRYPT:
2081 			(void) dump_ealg(algs[i].sadb_alg_id, file);
2082 			break;
2083 		}
2084 		(void) fprintf(file, dgettext(TEXT_DOMAIN,
2085 		    " minbits=%u, maxbits=%u, ivlen=%u"),
2086 		    algs[i].sadb_alg_minbits, algs[i].sadb_alg_maxbits,
2087 		    algs[i].sadb_alg_ivlen);
2088 		if (exttype == SADB_EXT_SUPPORTED_ENCRYPT)
2089 			(void) fprintf(file, dgettext(TEXT_DOMAIN,
2090 			    ", increment=%u"), algs[i].sadb_x_alg_increment);
2091 		(void) fprintf(file, dgettext(TEXT_DOMAIN, ".\n"));
2092 	}
2093 }
2094 
2095 /*
2096  * Print an SADB_EXT_SPIRANGE extension.
2097  */
2098 void
2099 print_spirange(FILE *file, char *prefix, struct sadb_spirange *range)
2100 {
2101 	(void) fprintf(file, dgettext(TEXT_DOMAIN,
2102 	    "%sSPI Range, min=0x%x, max=0x%x\n"), prefix,
2103 	    htonl(range->sadb_spirange_min),
2104 	    htonl(range->sadb_spirange_max));
2105 }
2106 
2107 /*
2108  * Print an SADB_X_EXT_KM_COOKIE extension.
2109  */
2110 
2111 void
2112 print_kmc(FILE *file, char *prefix, struct sadb_x_kmc *kmc)
2113 {
2114 	char *cookie_label;
2115 
2116 	if ((cookie_label = kmc_lookup_by_cookie(kmc->sadb_x_kmc_cookie)) ==
2117 	    NULL)
2118 		cookie_label = dgettext(TEXT_DOMAIN, "<Label not found.>");
2119 
2120 	(void) fprintf(file, dgettext(TEXT_DOMAIN,
2121 	    "%sProtocol %u, cookie=\"%s\" (%u)\n"), prefix,
2122 	    kmc->sadb_x_kmc_proto, cookie_label, kmc->sadb_x_kmc_cookie);
2123 }
2124 
2125 /*
2126  * Take a PF_KEY message pointed to buffer and print it.  Useful for DUMP
2127  * and GET.
2128  */
2129 void
2130 print_samsg(FILE *file, uint64_t *buffer, boolean_t want_timestamp,
2131     boolean_t vflag, boolean_t ignore_nss)
2132 {
2133 	uint64_t *current;
2134 	struct sadb_msg *samsg = (struct sadb_msg *)buffer;
2135 	struct sadb_ext *ext;
2136 	struct sadb_lifetime *currentlt = NULL, *hardlt = NULL, *softlt = NULL;
2137 	int i;
2138 	time_t wallclock;
2139 
2140 	(void) time(&wallclock);
2141 
2142 	print_sadb_msg(file, samsg, want_timestamp ? wallclock : 0, vflag);
2143 	current = (uint64_t *)(samsg + 1);
2144 	while (current - buffer < samsg->sadb_msg_len) {
2145 		int lenbytes;
2146 
2147 		ext = (struct sadb_ext *)current;
2148 		lenbytes = SADB_64TO8(ext->sadb_ext_len);
2149 		switch (ext->sadb_ext_type) {
2150 		case SADB_EXT_SA:
2151 			print_sa(file, dgettext(TEXT_DOMAIN,
2152 			    "SA: "), (struct sadb_sa *)current);
2153 			break;
2154 		/*
2155 		 * Pluck out lifetimes and print them at the end.  This is
2156 		 * to show relative lifetimes.
2157 		 */
2158 		case SADB_EXT_LIFETIME_CURRENT:
2159 			currentlt = (struct sadb_lifetime *)current;
2160 			break;
2161 		case SADB_EXT_LIFETIME_HARD:
2162 			hardlt = (struct sadb_lifetime *)current;
2163 			break;
2164 		case SADB_EXT_LIFETIME_SOFT:
2165 			softlt = (struct sadb_lifetime *)current;
2166 			break;
2167 
2168 		case SADB_EXT_ADDRESS_SRC:
2169 			print_address(file, dgettext(TEXT_DOMAIN, "SRC: "),
2170 			    (struct sadb_address *)current, ignore_nss);
2171 			break;
2172 		case SADB_X_EXT_ADDRESS_INNER_SRC:
2173 			print_address(file, dgettext(TEXT_DOMAIN, "INS: "),
2174 			    (struct sadb_address *)current, ignore_nss);
2175 			break;
2176 		case SADB_EXT_ADDRESS_DST:
2177 			print_address(file, dgettext(TEXT_DOMAIN, "DST: "),
2178 			    (struct sadb_address *)current, ignore_nss);
2179 			break;
2180 		case SADB_X_EXT_ADDRESS_INNER_DST:
2181 			print_address(file, dgettext(TEXT_DOMAIN, "IND: "),
2182 			    (struct sadb_address *)current, ignore_nss);
2183 			break;
2184 		case SADB_EXT_KEY_AUTH:
2185 			print_key(file, dgettext(TEXT_DOMAIN,
2186 			    "AKY: "), (struct sadb_key *)current);
2187 			break;
2188 		case SADB_EXT_KEY_ENCRYPT:
2189 			print_key(file, dgettext(TEXT_DOMAIN,
2190 			    "EKY: "), (struct sadb_key *)current);
2191 			break;
2192 		case SADB_EXT_IDENTITY_SRC:
2193 			print_ident(file, dgettext(TEXT_DOMAIN, "SID: "),
2194 			    (struct sadb_ident *)current);
2195 			break;
2196 		case SADB_EXT_IDENTITY_DST:
2197 			print_ident(file, dgettext(TEXT_DOMAIN, "DID: "),
2198 			    (struct sadb_ident *)current);
2199 			break;
2200 		case SADB_EXT_SENSITIVITY:
2201 			print_sens(file, dgettext(TEXT_DOMAIN, "SNS: "),
2202 			    (struct sadb_sens *)current);
2203 			break;
2204 		case SADB_EXT_PROPOSAL:
2205 			print_prop(file, dgettext(TEXT_DOMAIN, "PRP: "),
2206 			    (struct sadb_prop *)current);
2207 			break;
2208 		case SADB_EXT_SUPPORTED_AUTH:
2209 			print_supp(file, dgettext(TEXT_DOMAIN, "SUA: "),
2210 			    (struct sadb_supported *)current);
2211 			break;
2212 		case SADB_EXT_SUPPORTED_ENCRYPT:
2213 			print_supp(file, dgettext(TEXT_DOMAIN, "SUE: "),
2214 			    (struct sadb_supported *)current);
2215 			break;
2216 		case SADB_EXT_SPIRANGE:
2217 			print_spirange(file, dgettext(TEXT_DOMAIN, "SPR: "),
2218 			    (struct sadb_spirange *)current);
2219 			break;
2220 		case SADB_X_EXT_EPROP:
2221 			print_eprop(file, dgettext(TEXT_DOMAIN, "EPR: "),
2222 			    (struct sadb_prop *)current);
2223 			break;
2224 		case SADB_X_EXT_KM_COOKIE:
2225 			print_kmc(file, dgettext(TEXT_DOMAIN, "KMC: "),
2226 			    (struct sadb_x_kmc *)current);
2227 			break;
2228 		case SADB_X_EXT_ADDRESS_NATT_REM:
2229 			print_address(file, dgettext(TEXT_DOMAIN, "NRM: "),
2230 			    (struct sadb_address *)current, ignore_nss);
2231 			break;
2232 		case SADB_X_EXT_ADDRESS_NATT_LOC:
2233 			print_address(file, dgettext(TEXT_DOMAIN, "NLC: "),
2234 			    (struct sadb_address *)current, ignore_nss);
2235 			break;
2236 		default:
2237 			(void) fprintf(file, dgettext(TEXT_DOMAIN,
2238 			    "UNK: Unknown ext. %d, len %d.\n"),
2239 			    ext->sadb_ext_type, lenbytes);
2240 			for (i = 0; i < ext->sadb_ext_len; i++)
2241 				(void) fprintf(file, dgettext(TEXT_DOMAIN,
2242 				    "UNK: 0x%llx\n"), ((uint64_t *)ext)[i]);
2243 			break;
2244 		}
2245 		current += (lenbytes == 0) ?
2246 		    SADB_8TO64(sizeof (struct sadb_ext)) : ext->sadb_ext_len;
2247 	}
2248 	/*
2249 	 * Print lifetimes NOW.
2250 	 */
2251 	if (currentlt != NULL || hardlt != NULL || softlt != NULL)
2252 		print_lifetimes(file, wallclock, currentlt, hardlt, softlt,
2253 		    vflag);
2254 
2255 	if (current - buffer != samsg->sadb_msg_len) {
2256 		warnxfp(EFD(file), dgettext(TEXT_DOMAIN,
2257 		    "WARNING: insufficient buffer space or corrupt message."));
2258 	}
2259 
2260 	(void) fflush(file);	/* Make sure our message is out there. */
2261 }
2262 
2263 /*
2264  * save_XXX functions are used when "saving" the SA tables to either a
2265  * file or standard output.  They use the dump_XXX functions where needed,
2266  * but mostly they use the rparseXXX functions.
2267  */
2268 
2269 /*
2270  * Print save information for a lifetime extension.
2271  *
2272  * NOTE : It saves the lifetime in absolute terms.  For example, if you
2273  * had a hard_usetime of 60 seconds, you'll save it as 60 seconds, even though
2274  * there may have been 59 seconds burned off the clock.
2275  */
2276 boolean_t
2277 save_lifetime(struct sadb_lifetime *lifetime, FILE *ofile)
2278 {
2279 	char *prefix;
2280 
2281 	prefix = (lifetime->sadb_lifetime_exttype == SADB_EXT_LIFETIME_SOFT) ?
2282 	    "soft" : "hard";
2283 
2284 	if (putc('\t', ofile) == EOF)
2285 		return (B_FALSE);
2286 
2287 	if (lifetime->sadb_lifetime_allocations != 0 && fprintf(ofile,
2288 	    "%s_alloc %u ", prefix, lifetime->sadb_lifetime_allocations) < 0)
2289 		return (B_FALSE);
2290 
2291 	if (lifetime->sadb_lifetime_bytes != 0 && fprintf(ofile,
2292 	    "%s_bytes %llu ", prefix, lifetime->sadb_lifetime_bytes) < 0)
2293 		return (B_FALSE);
2294 
2295 	if (lifetime->sadb_lifetime_addtime != 0 && fprintf(ofile,
2296 	    "%s_addtime %llu ", prefix, lifetime->sadb_lifetime_addtime) < 0)
2297 		return (B_FALSE);
2298 
2299 	if (lifetime->sadb_lifetime_usetime != 0 && fprintf(ofile,
2300 	    "%s_usetime %llu ", prefix, lifetime->sadb_lifetime_usetime) < 0)
2301 		return (B_FALSE);
2302 
2303 	return (B_TRUE);
2304 }
2305 
2306 /*
2307  * Print save information for an address extension.
2308  */
2309 boolean_t
2310 save_address(struct sadb_address *addr, FILE *ofile)
2311 {
2312 	char *printable_addr, buf[INET6_ADDRSTRLEN];
2313 	const char *prefix, *pprefix;
2314 	struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)(addr + 1);
2315 	struct sockaddr_in *sin = (struct sockaddr_in *)sin6;
2316 	int af = sin->sin_family;
2317 
2318 	/*
2319 	 * Address-family reality check.
2320 	 */
2321 	if (af != AF_INET6 && af != AF_INET)
2322 		return (B_FALSE);
2323 
2324 	switch (addr->sadb_address_exttype) {
2325 	case SADB_EXT_ADDRESS_SRC:
2326 		prefix = "src";
2327 		pprefix = "sport";
2328 		break;
2329 	case SADB_X_EXT_ADDRESS_INNER_SRC:
2330 		prefix = "isrc";
2331 		pprefix = "isport";
2332 		break;
2333 	case SADB_EXT_ADDRESS_DST:
2334 		prefix = "dst";
2335 		pprefix = "dport";
2336 		break;
2337 	case SADB_X_EXT_ADDRESS_INNER_DST:
2338 		prefix = "idst";
2339 		pprefix = "idport";
2340 		break;
2341 	case SADB_X_EXT_ADDRESS_NATT_LOC:
2342 		prefix = "nat_loc ";
2343 		pprefix = "nat_lport";
2344 		break;
2345 	case SADB_X_EXT_ADDRESS_NATT_REM:
2346 		prefix = "nat_rem ";
2347 		pprefix = "nat_rport";
2348 		break;
2349 	}
2350 
2351 	if (fprintf(ofile, "    %s ", prefix) < 0)
2352 		return (B_FALSE);
2353 
2354 	/*
2355 	 * Do not do address-to-name translation, given that we live in
2356 	 * an age of names that explode into many addresses.
2357 	 */
2358 	printable_addr = (char *)inet_ntop(af,
2359 	    (af == AF_INET) ? (char *)&sin->sin_addr : (char *)&sin6->sin6_addr,
2360 	    buf, sizeof (buf));
2361 	if (printable_addr == NULL)
2362 		printable_addr = "Invalid IP address.";
2363 	if (fprintf(ofile, "%s", printable_addr) < 0)
2364 		return (B_FALSE);
2365 	if (addr->sadb_address_prefixlen != 0 &&
2366 	    !((addr->sadb_address_prefixlen == 32 && af == AF_INET) ||
2367 	    (addr->sadb_address_prefixlen == 128 && af == AF_INET6))) {
2368 		if (fprintf(ofile, "/%d", addr->sadb_address_prefixlen) < 0)
2369 			return (B_FALSE);
2370 	}
2371 
2372 	/*
2373 	 * The port is in the same position for struct sockaddr_in and
2374 	 * struct sockaddr_in6.  We exploit that property here.
2375 	 */
2376 	if ((pprefix != NULL) && (sin->sin_port != 0))
2377 		(void) fprintf(ofile, " %s %d", pprefix, ntohs(sin->sin_port));
2378 
2379 	return (B_TRUE);
2380 }
2381 
2382 /*
2383  * Print save information for a key extension. Returns whether writing
2384  * to the specified output file was successful or not.
2385  */
2386 boolean_t
2387 save_key(struct sadb_key *key, FILE *ofile)
2388 {
2389 	char *prefix;
2390 
2391 	if (putc('\t', ofile) == EOF)
2392 		return (B_FALSE);
2393 
2394 	prefix = (key->sadb_key_exttype == SADB_EXT_KEY_AUTH) ? "auth" : "encr";
2395 
2396 	if (fprintf(ofile, "%skey ", prefix) < 0)
2397 		return (B_FALSE);
2398 
2399 	if (dump_key((uint8_t *)(key + 1), key->sadb_key_bits, ofile) == -1)
2400 		return (B_FALSE);
2401 
2402 	return (B_TRUE);
2403 }
2404 
2405 /*
2406  * Print save information for an identity extension.
2407  */
2408 boolean_t
2409 save_ident(struct sadb_ident *ident, FILE *ofile)
2410 {
2411 	char *prefix;
2412 
2413 	if (putc('\t', ofile) == EOF)
2414 		return (B_FALSE);
2415 
2416 	prefix = (ident->sadb_ident_exttype == SADB_EXT_IDENTITY_SRC) ? "src" :
2417 	    "dst";
2418 
2419 	if (fprintf(ofile, "%sidtype %s ", prefix,
2420 	    rparseidtype(ident->sadb_ident_type)) < 0)
2421 		return (B_FALSE);
2422 
2423 	if (ident->sadb_ident_type == SADB_X_IDENTTYPE_DN ||
2424 	    ident->sadb_ident_type == SADB_X_IDENTTYPE_GN) {
2425 		if (fprintf(ofile, dgettext(TEXT_DOMAIN,
2426 		    "<can-not-print>")) < 0)
2427 			return (B_FALSE);
2428 	} else {
2429 		if (fprintf(ofile, "%s", (char *)(ident + 1)) < 0)
2430 			return (B_FALSE);
2431 	}
2432 
2433 	return (B_TRUE);
2434 }
2435 
2436 /*
2437  * "Save" a security association to an output file.
2438  *
2439  * NOTE the lack of calls to dgettext() because I'm outputting parseable stuff.
2440  * ALSO NOTE that if you change keywords (see parsecmd()), you'll have to
2441  * change them here as well.
2442  */
2443 void
2444 save_assoc(uint64_t *buffer, FILE *ofile)
2445 {
2446 	int terrno;
2447 	boolean_t seen_proto = B_FALSE, seen_iproto = B_FALSE;
2448 	uint64_t *current;
2449 	struct sadb_address *addr;
2450 	struct sadb_msg *samsg = (struct sadb_msg *)buffer;
2451 	struct sadb_ext *ext;
2452 
2453 #define	tidyup() \
2454 	terrno = errno; (void) fclose(ofile); errno = terrno; \
2455 	interactive = B_FALSE
2456 
2457 #define	savenl() if (fputs(" \\\n", ofile) == EOF) \
2458 	{ bail(dgettext(TEXT_DOMAIN, "savenl")); }
2459 
2460 	if (fputs("# begin assoc\n", ofile) == EOF)
2461 		bail(dgettext(TEXT_DOMAIN,
2462 		    "save_assoc: Opening comment of SA"));
2463 	if (fprintf(ofile, "add %s ", rparsesatype(samsg->sadb_msg_satype)) < 0)
2464 		bail(dgettext(TEXT_DOMAIN, "save_assoc: First line of SA"));
2465 	savenl();
2466 
2467 	current = (uint64_t *)(samsg + 1);
2468 	while (current - buffer < samsg->sadb_msg_len) {
2469 		struct sadb_sa *assoc;
2470 
2471 		ext = (struct sadb_ext *)current;
2472 		addr = (struct sadb_address *)ext;  /* Just in case... */
2473 		switch (ext->sadb_ext_type) {
2474 		case SADB_EXT_SA:
2475 			assoc = (struct sadb_sa *)ext;
2476 			if (assoc->sadb_sa_state != SADB_SASTATE_MATURE) {
2477 				if (fprintf(ofile, "# WARNING: SA was dying "
2478 				    "or dead.\n") < 0) {
2479 					tidyup();
2480 					bail(dgettext(TEXT_DOMAIN,
2481 					    "save_assoc: fprintf not mature"));
2482 				}
2483 			}
2484 			if (fprintf(ofile, "    spi 0x%x ",
2485 			    ntohl(assoc->sadb_sa_spi)) < 0) {
2486 				tidyup();
2487 				bail(dgettext(TEXT_DOMAIN,
2488 				    "save_assoc: fprintf spi"));
2489 			}
2490 			if (assoc->sadb_sa_encrypt != SADB_EALG_NONE) {
2491 				if (fprintf(ofile, "encr_alg %s ",
2492 				    rparsealg(assoc->sadb_sa_encrypt,
2493 				    IPSEC_PROTO_ESP)) < 0) {
2494 					tidyup();
2495 					bail(dgettext(TEXT_DOMAIN,
2496 					    "save_assoc: fprintf encrypt"));
2497 				}
2498 			}
2499 			if (assoc->sadb_sa_auth != SADB_AALG_NONE) {
2500 				if (fprintf(ofile, "auth_alg %s ",
2501 				    rparsealg(assoc->sadb_sa_auth,
2502 				    IPSEC_PROTO_AH)) < 0) {
2503 					tidyup();
2504 					bail(dgettext(TEXT_DOMAIN,
2505 					    "save_assoc: fprintf auth"));
2506 				}
2507 			}
2508 			if (fprintf(ofile, "replay %d ",
2509 			    assoc->sadb_sa_replay) < 0) {
2510 				tidyup();
2511 				bail(dgettext(TEXT_DOMAIN,
2512 				    "save_assoc: fprintf replay"));
2513 			}
2514 			if (assoc->sadb_sa_flags & (SADB_X_SAFLAGS_NATT_LOC |
2515 			    SADB_X_SAFLAGS_NATT_REM)) {
2516 				if (fprintf(ofile, "encap udp") < 0) {
2517 					tidyup();
2518 					bail(dgettext(TEXT_DOMAIN,
2519 					    "save_assoc: fprintf encap"));
2520 				}
2521 			}
2522 			savenl();
2523 			break;
2524 		case SADB_EXT_LIFETIME_HARD:
2525 		case SADB_EXT_LIFETIME_SOFT:
2526 			if (!save_lifetime((struct sadb_lifetime *)ext,
2527 			    ofile)) {
2528 				tidyup();
2529 				bail(dgettext(TEXT_DOMAIN, "save_lifetime"));
2530 			}
2531 			savenl();
2532 			break;
2533 		case SADB_X_EXT_ADDRESS_INNER_SRC:
2534 		case SADB_X_EXT_ADDRESS_INNER_DST:
2535 			if (!seen_iproto && addr->sadb_address_proto) {
2536 				(void) fprintf(ofile, "    iproto %d",
2537 				    addr->sadb_address_proto);
2538 				savenl();
2539 				seen_iproto = B_TRUE;
2540 			}
2541 			goto skip_srcdst;  /* Hack to avoid cases below... */
2542 			/* FALLTHRU */
2543 		case SADB_EXT_ADDRESS_SRC:
2544 		case SADB_EXT_ADDRESS_DST:
2545 			if (!seen_proto && addr->sadb_address_proto) {
2546 				(void) fprintf(ofile, "    proto %d",
2547 				    addr->sadb_address_proto);
2548 				savenl();
2549 				seen_proto = B_TRUE;
2550 			}
2551 			/* FALLTHRU */
2552 		case SADB_X_EXT_ADDRESS_NATT_REM:
2553 		case SADB_X_EXT_ADDRESS_NATT_LOC:
2554 skip_srcdst:
2555 			if (!save_address(addr, ofile)) {
2556 				tidyup();
2557 				bail(dgettext(TEXT_DOMAIN, "save_address"));
2558 			}
2559 			savenl();
2560 			break;
2561 		case SADB_EXT_KEY_AUTH:
2562 		case SADB_EXT_KEY_ENCRYPT:
2563 			if (!save_key((struct sadb_key *)ext, ofile)) {
2564 				tidyup();
2565 				bail(dgettext(TEXT_DOMAIN, "save_address"));
2566 			}
2567 			savenl();
2568 			break;
2569 		case SADB_EXT_IDENTITY_SRC:
2570 		case SADB_EXT_IDENTITY_DST:
2571 			if (!save_ident((struct sadb_ident *)ext, ofile)) {
2572 				tidyup();
2573 				bail(dgettext(TEXT_DOMAIN, "save_address"));
2574 			}
2575 			savenl();
2576 			break;
2577 		case SADB_EXT_SENSITIVITY:
2578 		default:
2579 			/* Skip over irrelevant extensions. */
2580 			break;
2581 		}
2582 		current += ext->sadb_ext_len;
2583 	}
2584 
2585 	if (fputs(dgettext(TEXT_DOMAIN, "\n# end assoc\n\n"), ofile) == EOF) {
2586 		tidyup();
2587 		bail(dgettext(TEXT_DOMAIN, "save_assoc: last fputs"));
2588 	}
2589 }
2590 
2591 /*
2592  * Open the output file for the "save" command.
2593  */
2594 FILE *
2595 opensavefile(char *filename)
2596 {
2597 	int fd;
2598 	FILE *retval;
2599 	struct stat buf;
2600 
2601 	/*
2602 	 * If the user specifies "-" or doesn't give a filename, then
2603 	 * dump to stdout.  Make sure to document the dangers of files
2604 	 * that are NFS, directing your output to strange places, etc.
2605 	 */
2606 	if (filename == NULL || strcmp("-", filename) == 0)
2607 		return (stdout);
2608 
2609 	/*
2610 	 * open the file with the create bits set.  Since I check for
2611 	 * real UID == root in main(), I won't worry about the ownership
2612 	 * problem.
2613 	 */
2614 	fd = open(filename, O_WRONLY | O_EXCL | O_CREAT | O_TRUNC, S_IRUSR);
2615 	if (fd == -1) {
2616 		if (errno != EEXIST)
2617 			bail_msg("%s %s: %s", filename, dgettext(TEXT_DOMAIN,
2618 			    "open error"),
2619 			    strerror(errno));
2620 		fd = open(filename, O_WRONLY | O_TRUNC, 0);
2621 		if (fd == -1)
2622 			bail_msg("%s %s: %s", filename, dgettext(TEXT_DOMAIN,
2623 			    "open error"), strerror(errno));
2624 		if (fstat(fd, &buf) == -1) {
2625 			(void) close(fd);
2626 			bail_msg("%s fstat: %s", filename, strerror(errno));
2627 		}
2628 		if (S_ISREG(buf.st_mode) &&
2629 		    ((buf.st_mode & S_IAMB) != S_IRUSR)) {
2630 			warnx(dgettext(TEXT_DOMAIN,
2631 			    "WARNING: Save file already exists with "
2632 			    "permission %o."), buf.st_mode & S_IAMB);
2633 			warnx(dgettext(TEXT_DOMAIN,
2634 			    "Normal users may be able to read IPsec "
2635 			    "keying material."));
2636 		}
2637 	}
2638 
2639 	/* Okay, we have an FD.  Assign it to a stdio FILE pointer. */
2640 	retval = fdopen(fd, "w");
2641 	if (retval == NULL) {
2642 		(void) close(fd);
2643 		bail_msg("%s %s: %s", filename, dgettext(TEXT_DOMAIN,
2644 		    "fdopen error"), strerror(errno));
2645 	}
2646 	return (retval);
2647 }
2648 
2649 const char *
2650 do_inet_ntop(const void *addr, char *cp, size_t size)
2651 {
2652 	boolean_t isv4;
2653 	struct in6_addr *inaddr6 = (struct in6_addr *)addr;
2654 	struct in_addr inaddr;
2655 
2656 	if ((isv4 = IN6_IS_ADDR_V4MAPPED(inaddr6)) == B_TRUE) {
2657 		IN6_V4MAPPED_TO_INADDR(inaddr6, &inaddr);
2658 	}
2659 
2660 	return (inet_ntop(isv4 ? AF_INET : AF_INET6,
2661 	    isv4 ? (void *)&inaddr : inaddr6, cp, size));
2662 }
2663 
2664 char numprint[NBUF_SIZE];
2665 
2666 /*
2667  * Parse and reverse parse a specific SA type (AH, ESP, etc.).
2668  */
2669 static struct typetable {
2670 	char *type;
2671 	int token;
2672 } type_table[] = {
2673 	{"all", SADB_SATYPE_UNSPEC},
2674 	{"ah",  SADB_SATYPE_AH},
2675 	{"esp", SADB_SATYPE_ESP},
2676 	/* PF_KEY NOTE:  More to come if net/pfkeyv2.h gets updated. */
2677 	{NULL, 0}	/* Token value is irrelevant for this entry. */
2678 };
2679 
2680 char *
2681 rparsesatype(int type)
2682 {
2683 	struct typetable *tt = type_table;
2684 
2685 	while (tt->type != NULL && type != tt->token)
2686 		tt++;
2687 
2688 	if (tt->type == NULL) {
2689 		(void) snprintf(numprint, NBUF_SIZE, "%d", type);
2690 	} else {
2691 		return (tt->type);
2692 	}
2693 
2694 	return (numprint);
2695 }
2696 
2697 
2698 /*
2699  * Return a string containing the name of the specified numerical algorithm
2700  * identifier.
2701  */
2702 char *
2703 rparsealg(uint8_t alg, int proto_num)
2704 {
2705 	static struct ipsecalgent *holder = NULL; /* we're single-threaded */
2706 
2707 	if (holder != NULL)
2708 		freeipsecalgent(holder);
2709 
2710 	holder = getipsecalgbynum(alg, proto_num, NULL);
2711 	if (holder == NULL) {
2712 		(void) snprintf(numprint, NBUF_SIZE, "%d", alg);
2713 		return (numprint);
2714 	}
2715 
2716 	return (*(holder->a_names));
2717 }
2718 
2719 /*
2720  * Parse and reverse parse out a source/destination ID type.
2721  */
2722 static struct idtypes {
2723 	char *idtype;
2724 	uint8_t retval;
2725 } idtypes[] = {
2726 	{"prefix",	SADB_IDENTTYPE_PREFIX},
2727 	{"fqdn",	SADB_IDENTTYPE_FQDN},
2728 	{"domain",	SADB_IDENTTYPE_FQDN},
2729 	{"domainname",	SADB_IDENTTYPE_FQDN},
2730 	{"user_fqdn",	SADB_IDENTTYPE_USER_FQDN},
2731 	{"mailbox",	SADB_IDENTTYPE_USER_FQDN},
2732 	{"der_dn",	SADB_X_IDENTTYPE_DN},
2733 	{"der_gn",	SADB_X_IDENTTYPE_GN},
2734 	{NULL,		0}
2735 };
2736 
2737 char *
2738 rparseidtype(uint16_t type)
2739 {
2740 	struct idtypes *idp;
2741 
2742 	for (idp = idtypes; idp->idtype != NULL; idp++) {
2743 		if (type == idp->retval)
2744 			return (idp->idtype);
2745 	}
2746 
2747 	(void) snprintf(numprint, NBUF_SIZE, "%d", type);
2748 	return (numprint);
2749 }
2750 
2751 /*
2752  * This is a general purpose exit function, calling functions can specify an
2753  * error type. If the command calling this function was started by smf(5) the
2754  * error type could be used as a hint to the restarter. In the future this
2755  * function could be used to do something more intelligent with a process that
2756  * encounters an error.
2757  *
2758  * The function will handle an optional variable args error message, this
2759  * will be written to the error stream, typically a log file or stderr.
2760  */
2761 void
2762 ipsecutil_exit(exit_type_t type, char *fmri, FILE *fp, const char *fmt, ...)
2763 {
2764 	int exit_status;
2765 	va_list args;
2766 
2767 	if (fp == NULL)
2768 		fp = stderr;
2769 	if (fmt != NULL) {
2770 		va_start(args, fmt);
2771 		vwarnxfp(fp, fmt, args);
2772 		va_end(args);
2773 	}
2774 
2775 	if (fmri == NULL) {
2776 		/* Command being run directly from a shell. */
2777 		switch (type) {
2778 		case SERVICE_EXIT_OK:
2779 			exit_status = 0;
2780 			break;
2781 		case SERVICE_DEGRADE:
2782 			return;
2783 			break;
2784 		case SERVICE_BADPERM:
2785 		case SERVICE_BADCONF:
2786 		case SERVICE_MAINTAIN:
2787 		case SERVICE_DISABLE:
2788 		case SERVICE_FATAL:
2789 		case SERVICE_RESTART:
2790 			warnxfp(fp, "Fatal error - exiting.");
2791 			exit_status = 1;
2792 			break;
2793 		}
2794 	} else {
2795 		/* Command being run as a smf(5) method. */
2796 		switch (type) {
2797 		case SERVICE_EXIT_OK:
2798 			exit_status = SMF_EXIT_OK;
2799 			break;
2800 		case SERVICE_DEGRADE:
2801 			return;
2802 			break;
2803 		case SERVICE_BADPERM:
2804 			warnxfp(fp, dgettext(TEXT_DOMAIN,
2805 			    "Permission error with %s."), fmri);
2806 			exit_status = SMF_EXIT_ERR_PERM;
2807 			break;
2808 		case SERVICE_BADCONF:
2809 			warnxfp(fp, dgettext(TEXT_DOMAIN,
2810 			    "Bad configuration of service %s."), fmri);
2811 			exit_status = SMF_EXIT_ERR_FATAL;
2812 			break;
2813 		case SERVICE_MAINTAIN:
2814 			warnxfp(fp, dgettext(TEXT_DOMAIN,
2815 			    "Service %s needs maintenance."), fmri);
2816 			exit_status = SMF_EXIT_ERR_FATAL;
2817 			break;
2818 		case SERVICE_DISABLE:
2819 			exit_status = SMF_EXIT_ERR_FATAL;
2820 			break;
2821 		case SERVICE_FATAL:
2822 			warnxfp(fp, dgettext(TEXT_DOMAIN,
2823 			    "Service %s fatal error."), fmri);
2824 			exit_status = SMF_EXIT_ERR_FATAL;
2825 			break;
2826 		case SERVICE_RESTART:
2827 			exit_status = 1;
2828 			break;
2829 		}
2830 	}
2831 	(void) fflush(fp);
2832 	(void) fclose(fp);
2833 	exit(exit_status);
2834 }
2835