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