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