xref: /titanic_52/usr/src/lib/libtsnet/common/misc.c (revision 7b0bedd42192a2f6bcd6fc4b637d23892303a962)
145916cd2Sjpk /*
245916cd2Sjpk  * CDDL HEADER START
345916cd2Sjpk  *
445916cd2Sjpk  * The contents of this file are subject to the terms of the
545916cd2Sjpk  * Common Development and Distribution License (the "License").
645916cd2Sjpk  * You may not use this file except in compliance with the License.
745916cd2Sjpk  *
845916cd2Sjpk  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
945916cd2Sjpk  * or http://www.opensolaris.org/os/licensing.
1045916cd2Sjpk  * See the License for the specific language governing permissions
1145916cd2Sjpk  * and limitations under the License.
1245916cd2Sjpk  *
1345916cd2Sjpk  * When distributing Covered Code, include this CDDL HEADER in each
1445916cd2Sjpk  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1545916cd2Sjpk  * If applicable, add the following below this CDDL HEADER, with the
1645916cd2Sjpk  * fields enclosed by brackets "[]" replaced with your own identifying
1745916cd2Sjpk  * information: Portions Copyright [yyyy] [name of copyright owner]
1845916cd2Sjpk  *
1945916cd2Sjpk  * CDDL HEADER END
2045916cd2Sjpk  */
2145916cd2Sjpk /*
22*7b0bedd4SRic Aleshire  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
2345916cd2Sjpk  * Use is subject to license terms.
2445916cd2Sjpk  *
2545916cd2Sjpk  * From "misc.c	5.15	00/05/31 SMI; TSOL 2.x"
2645916cd2Sjpk  */
2745916cd2Sjpk 
2845916cd2Sjpk /*
2945916cd2Sjpk  *	Miscellaneous user interfaces to trusted label functions.
3045916cd2Sjpk  */
3145916cd2Sjpk 
3245916cd2Sjpk 
3345916cd2Sjpk #include <ctype.h>
3445916cd2Sjpk #include <stdio.h>
3545916cd2Sjpk #include <stdlib.h>
3645916cd2Sjpk #include <strings.h>
3745916cd2Sjpk #include <errno.h>
3845916cd2Sjpk #include <libintl.h>
3945916cd2Sjpk #include <libtsnet.h>
4045916cd2Sjpk #include <tsol/label.h>
4145916cd2Sjpk 
4245916cd2Sjpk #include <net/route.h>
4345916cd2Sjpk 
4445916cd2Sjpk #define	MAX_ATTR_LEN	1024
4545916cd2Sjpk 
4645916cd2Sjpk /*
4745916cd2Sjpk  * Parse off an entry from a line.  Entry is stored in 'outbuf'.  Returned
4845916cd2Sjpk  * value is a pointer to the first unprocessed input character from 'instr'.
4945916cd2Sjpk  */
5045916cd2Sjpk const char *
5145916cd2Sjpk parse_entry(char *outbuf, size_t outlen, const char *instr,
5245916cd2Sjpk     const char *delimit)
5345916cd2Sjpk {
5445916cd2Sjpk 	boolean_t escape_state = B_FALSE;
5545916cd2Sjpk 	boolean_t any_white;
5645916cd2Sjpk 	char chr;
5745916cd2Sjpk 
5845916cd2Sjpk 	any_white = strchr(delimit, '\n') != NULL;
5945916cd2Sjpk 
6045916cd2Sjpk 	/*
6145916cd2Sjpk 	 * User may specify outlen as 0 to skip over a field without storing
6245916cd2Sjpk 	 * it anywhere.  Otherwise, we need at least one byte for the
6345916cd2Sjpk 	 * terminating NUL plus one byte to store another byte from instr.
6445916cd2Sjpk 	 */
6545916cd2Sjpk 	while (outlen != 1 && (chr = *instr++) != '\0') {
6645916cd2Sjpk 		if (!escape_state) {
6745916cd2Sjpk 			if (chr == '\\') {
6845916cd2Sjpk 				escape_state = B_TRUE;
6945916cd2Sjpk 				continue;
7045916cd2Sjpk 			}
7145916cd2Sjpk 			if (strchr(delimit, chr) != NULL)
7245916cd2Sjpk 				break;
7345916cd2Sjpk 			if (any_white && isspace(chr))
7445916cd2Sjpk 				break;
7545916cd2Sjpk 		}
7645916cd2Sjpk 		escape_state = B_FALSE;
7745916cd2Sjpk 		if (outlen > 0) {
7845916cd2Sjpk 			*outbuf++ = chr;
7945916cd2Sjpk 			outlen--;
8045916cd2Sjpk 		}
8145916cd2Sjpk 	}
8245916cd2Sjpk 	if (outlen != 1)
8345916cd2Sjpk 		instr--;
8445916cd2Sjpk 	if (escape_state)
8545916cd2Sjpk 		instr--;
8645916cd2Sjpk 	if (outlen > 0)
8745916cd2Sjpk 		*outbuf = '\0';
8845916cd2Sjpk 	return (instr);
8945916cd2Sjpk }
9045916cd2Sjpk 
91*7b0bedd4SRic Aleshire char *
92*7b0bedd4SRic Aleshire sl_to_str(const m_label_t *sl)
9345916cd2Sjpk {
94*7b0bedd4SRic Aleshire 	char *sl_str = NULL;
95*7b0bedd4SRic Aleshire 	static char unknown_str[] = "UNKNOWN";
9645916cd2Sjpk 
9745916cd2Sjpk 	if (sl == NULL)
98*7b0bedd4SRic Aleshire 		return (strdup(unknown_str));
9945916cd2Sjpk 
100*7b0bedd4SRic Aleshire 	if ((label_to_str(sl, &sl_str, M_LABEL, DEF_NAMES) != 0) &&
101*7b0bedd4SRic Aleshire 	    (label_to_str(sl, &sl_str, M_INTERNAL, DEF_NAMES) != 0))
102*7b0bedd4SRic Aleshire 		return (strdup(unknown_str));
103*7b0bedd4SRic Aleshire 
10445916cd2Sjpk 	return (sl_str);
10545916cd2Sjpk }
10645916cd2Sjpk 
10745916cd2Sjpk static const char *rtsa_keywords[] = {
10845916cd2Sjpk #define	SAK_MINSL	0
10945916cd2Sjpk 	"min_sl",
11045916cd2Sjpk #define	SAK_MAXSL	1
11145916cd2Sjpk 	"max_sl",
11245916cd2Sjpk #define	SAK_DOI		2
11345916cd2Sjpk 	"doi",
11445916cd2Sjpk #define	SAK_CIPSO	3
11545916cd2Sjpk 	"cipso",
116e34b0294Swy83408 #define	SAK_SL		4
117e34b0294Swy83408 	"sl",
118e34b0294Swy83408 #define	SAK_INVAL	5
11945916cd2Sjpk 	NULL
12045916cd2Sjpk };
12145916cd2Sjpk 
12245916cd2Sjpk const char *
12345916cd2Sjpk rtsa_to_str(const struct rtsa_s *rtsa, char *line, size_t len)
12445916cd2Sjpk {
12545916cd2Sjpk 	size_t slen;
12645916cd2Sjpk 	uint32_t mask, i;
127*7b0bedd4SRic Aleshire 	char *sl_str = NULL;
12845916cd2Sjpk 
12945916cd2Sjpk 	slen = 0;
13045916cd2Sjpk 	*line = '\0';
13145916cd2Sjpk 	mask = rtsa->rtsa_mask;
13245916cd2Sjpk 
13345916cd2Sjpk 	for (i = 1; mask != 0 && i != 0 && slen < len - 1; i <<= 1) {
13445916cd2Sjpk 		if (!(i & (RTSA_MINSL|RTSA_MAXSL|RTSA_DOI|RTSA_CIPSO)))
13545916cd2Sjpk 			continue;
13645916cd2Sjpk 		if (!(i & mask))
13745916cd2Sjpk 			continue;
13845916cd2Sjpk 		if (slen != 0)
13945916cd2Sjpk 			line[slen++] = ',';
14045916cd2Sjpk 		switch (i & mask) {
14145916cd2Sjpk 		case RTSA_MINSL:
142e34b0294Swy83408 			if ((mask & RTSA_MAXSL) &&
143e34b0294Swy83408 			    blequal(&rtsa->rtsa_slrange.lower_bound,
144e34b0294Swy83408 			    &rtsa->rtsa_slrange.upper_bound)) {
145*7b0bedd4SRic Aleshire 
146*7b0bedd4SRic Aleshire 				sl_str =
147*7b0bedd4SRic Aleshire 				    sl_to_str(&rtsa->rtsa_slrange.lower_bound);
148e34b0294Swy83408 				slen += snprintf(line + slen, len - slen,
149*7b0bedd4SRic Aleshire 				    "sl=%s", sl_str);
150*7b0bedd4SRic Aleshire 				free(sl_str);
151*7b0bedd4SRic Aleshire 				sl_str = NULL;
152e34b0294Swy83408 				mask ^= RTSA_MAXSL;
153e34b0294Swy83408 				break;
154e34b0294Swy83408 			}
155*7b0bedd4SRic Aleshire 			sl_str = sl_to_str(&rtsa->rtsa_slrange.lower_bound);
15645916cd2Sjpk 			slen += snprintf(line + slen, len - slen, "min_sl=%s",
157*7b0bedd4SRic Aleshire 			    sl_str);
158*7b0bedd4SRic Aleshire 			free(sl_str);
159*7b0bedd4SRic Aleshire 			sl_str = NULL;
16045916cd2Sjpk 			break;
16145916cd2Sjpk 		case RTSA_MAXSL:
162*7b0bedd4SRic Aleshire 			sl_str = sl_to_str(&rtsa->rtsa_slrange.upper_bound);
16345916cd2Sjpk 			slen += snprintf(line + slen, len - slen, "max_sl=%s",
164*7b0bedd4SRic Aleshire 			    sl_str);
165*7b0bedd4SRic Aleshire 			free(sl_str);
166*7b0bedd4SRic Aleshire 			sl_str = NULL;
16745916cd2Sjpk 			break;
16845916cd2Sjpk 		case RTSA_DOI:
16945916cd2Sjpk 			slen += snprintf(line + slen, len - slen, "doi=%d",
17045916cd2Sjpk 			    rtsa->rtsa_doi);
17145916cd2Sjpk 			break;
17245916cd2Sjpk 		case RTSA_CIPSO:
17345916cd2Sjpk 			slen += snprintf(line + slen, len - slen, "cipso");
17445916cd2Sjpk 			break;
17545916cd2Sjpk 		}
17645916cd2Sjpk 	}
17745916cd2Sjpk 
17845916cd2Sjpk 	return (line);
17945916cd2Sjpk }
18045916cd2Sjpk 
18145916cd2Sjpk boolean_t
18245916cd2Sjpk rtsa_keyword(const char *options, struct rtsa_s *sp, int *errp, char **errstrp)
18345916cd2Sjpk {
18445916cd2Sjpk 	const char *valptr, *nxtopt;
18545916cd2Sjpk 	uint32_t mask = 0, doi;
18645916cd2Sjpk 	int key;
187*7b0bedd4SRic Aleshire 	m_label_t *min_sl = NULL, *max_sl = NULL;
18845916cd2Sjpk 	char attrbuf[MAX_ATTR_LEN];
18945916cd2Sjpk 	const char **keyword;
19045916cd2Sjpk 	int err;
19145916cd2Sjpk 	char *errstr, *cp;
19245916cd2Sjpk 
19345916cd2Sjpk 	if (errp == NULL)
19445916cd2Sjpk 		errp = &err;
19545916cd2Sjpk 	if (errstrp == NULL)
19645916cd2Sjpk 		errstrp = &errstr;
19745916cd2Sjpk 
19845916cd2Sjpk 	*errstrp = (char *)options;
19945916cd2Sjpk 
20045916cd2Sjpk 	while (*options != '\0') {
20145916cd2Sjpk 		valptr = parse_entry(attrbuf, sizeof (attrbuf), options, ",=");
20245916cd2Sjpk 
20345916cd2Sjpk 		if (attrbuf[0] == '\0') {
20445916cd2Sjpk 			*errstrp = (char *)options;
20545916cd2Sjpk 			*errp = LTSNET_ILL_ENTRY;
206*7b0bedd4SRic Aleshire 			goto out_err;
20745916cd2Sjpk 		}
20845916cd2Sjpk 		for (keyword = rtsa_keywords; *keyword != NULL; keyword++)
20945916cd2Sjpk 			if (strcmp(*keyword, attrbuf) == 0)
21045916cd2Sjpk 				break;
21145916cd2Sjpk 		if ((key = keyword - rtsa_keywords) == SAK_INVAL) {
21245916cd2Sjpk 			*errstrp = (char *)options;
21345916cd2Sjpk 			*errp = LTSNET_ILL_KEY;
214*7b0bedd4SRic Aleshire 			goto out_err;
21545916cd2Sjpk 		}
21645916cd2Sjpk 		if ((key == SAK_CIPSO && *valptr == '=') ||
21745916cd2Sjpk 		    (key != SAK_CIPSO && *valptr != '=')) {
21845916cd2Sjpk 			*errstrp = (char *)valptr;
21945916cd2Sjpk 			*errp = LTSNET_ILL_VALDELIM;
220*7b0bedd4SRic Aleshire 			goto out_err;
22145916cd2Sjpk 		}
22245916cd2Sjpk 
22345916cd2Sjpk 		nxtopt = valptr;
22445916cd2Sjpk 		if (*valptr == '=') {
22545916cd2Sjpk 			valptr++;
22645916cd2Sjpk 			nxtopt = parse_entry(attrbuf, sizeof (attrbuf),
22745916cd2Sjpk 			    valptr, ",=");
22845916cd2Sjpk 			if (*nxtopt == '=') {
22945916cd2Sjpk 				*errstrp = (char *)nxtopt;
23045916cd2Sjpk 				*errp = LTSNET_ILL_KEYDELIM;
231*7b0bedd4SRic Aleshire 				goto out_err;
23245916cd2Sjpk 			}
23345916cd2Sjpk 		}
23445916cd2Sjpk 		if (*nxtopt == ',')
23545916cd2Sjpk 			nxtopt++;
23645916cd2Sjpk 
23745916cd2Sjpk 		switch (key) {
23845916cd2Sjpk 		case SAK_MINSL:
23945916cd2Sjpk 			if (mask & RTSA_MINSL) {
24045916cd2Sjpk 				*errstrp = (char *)options;
24145916cd2Sjpk 				*errp = LTSNET_DUP_KEY;
242*7b0bedd4SRic Aleshire 				goto out_err;
24345916cd2Sjpk 			}
244*7b0bedd4SRic Aleshire 			m_label_free(min_sl);		/* in case of duplicate */
245*7b0bedd4SRic Aleshire 			min_sl = NULL;
246*7b0bedd4SRic Aleshire 			if (str_to_label(attrbuf, &min_sl, MAC_LABEL,
247*7b0bedd4SRic Aleshire 			    L_NO_CORRECTION, NULL) != 0) {
24845916cd2Sjpk 				*errstrp = (char *)valptr;
24945916cd2Sjpk 				*errp = LTSNET_ILL_LOWERBOUND;
250*7b0bedd4SRic Aleshire 				goto out_err;
25145916cd2Sjpk 			}
25245916cd2Sjpk 			mask |= RTSA_MINSL;
25345916cd2Sjpk 			break;
25445916cd2Sjpk 
25545916cd2Sjpk 		case SAK_MAXSL:
25645916cd2Sjpk 			if (mask & RTSA_MAXSL) {
25745916cd2Sjpk 				*errstrp = (char *)options;
25845916cd2Sjpk 				*errp = LTSNET_DUP_KEY;
259*7b0bedd4SRic Aleshire 				goto out_err;
26045916cd2Sjpk 			}
261*7b0bedd4SRic Aleshire 			m_label_free(max_sl);		/* in case of duplicate */
262*7b0bedd4SRic Aleshire 			max_sl = NULL;
263*7b0bedd4SRic Aleshire 			if (str_to_label(attrbuf, &max_sl, MAC_LABEL,
264*7b0bedd4SRic Aleshire 			    L_NO_CORRECTION, NULL) != 0) {
26545916cd2Sjpk 				*errstrp = (char *)valptr;
26645916cd2Sjpk 				*errp = LTSNET_ILL_UPPERBOUND;
267*7b0bedd4SRic Aleshire 				goto out_err;
26845916cd2Sjpk 			}
26945916cd2Sjpk 			mask |= RTSA_MAXSL;
27045916cd2Sjpk 			break;
27145916cd2Sjpk 
272e34b0294Swy83408 		case SAK_SL:
273e34b0294Swy83408 			if (mask & (RTSA_MAXSL|RTSA_MINSL)) {
274e34b0294Swy83408 				*errstrp = (char *)options;
275e34b0294Swy83408 				*errp = LTSNET_DUP_KEY;
276*7b0bedd4SRic Aleshire 				goto out_err;
277e34b0294Swy83408 			}
278*7b0bedd4SRic Aleshire 			m_label_free(min_sl);		/* in case of duplicate */
279*7b0bedd4SRic Aleshire 			min_sl = NULL;
280*7b0bedd4SRic Aleshire 			if (str_to_label(attrbuf, &min_sl, MAC_LABEL,
281*7b0bedd4SRic Aleshire 			    L_NO_CORRECTION, NULL) != 0) {
282e34b0294Swy83408 				*errstrp = (char *)valptr;
283e34b0294Swy83408 				*errp = LTSNET_ILL_LABEL;
284*7b0bedd4SRic Aleshire 				goto out_err;
285e34b0294Swy83408 			}
286*7b0bedd4SRic Aleshire 			*max_sl = *min_sl;
287e34b0294Swy83408 			mask |= (RTSA_MINSL | RTSA_MAXSL);
288e34b0294Swy83408 			break;
289e34b0294Swy83408 
29045916cd2Sjpk 		case SAK_DOI:
29145916cd2Sjpk 			if (mask & RTSA_DOI) {
29245916cd2Sjpk 				*errstrp = (char *)options;
29345916cd2Sjpk 				*errp = LTSNET_DUP_KEY;
294*7b0bedd4SRic Aleshire 				goto out_err;
29545916cd2Sjpk 			}
29645916cd2Sjpk 			errno = 0;
29745916cd2Sjpk 			doi = strtoul(attrbuf, &cp, 0);
29845916cd2Sjpk 			if (doi == 0 || errno != 0 || *cp != '\0') {
29945916cd2Sjpk 				*errstrp = (char *)valptr;
30045916cd2Sjpk 				*errp = LTSNET_ILL_DOI;
301*7b0bedd4SRic Aleshire 				goto out_err;
30245916cd2Sjpk 			}
30345916cd2Sjpk 			mask |= RTSA_DOI;
30445916cd2Sjpk 			break;
30545916cd2Sjpk 
30645916cd2Sjpk 		case SAK_CIPSO:
30745916cd2Sjpk 			if (mask & RTSA_CIPSO) {
30845916cd2Sjpk 				*errstrp = (char *)options;
30945916cd2Sjpk 				*errp = LTSNET_DUP_KEY;
310*7b0bedd4SRic Aleshire 				goto out_err;
31145916cd2Sjpk 			}
31245916cd2Sjpk 			mask |= RTSA_CIPSO;
31345916cd2Sjpk 			break;
31445916cd2Sjpk 		}
31545916cd2Sjpk 
31645916cd2Sjpk 		options = nxtopt;
31745916cd2Sjpk 	}
31845916cd2Sjpk 
31945916cd2Sjpk 	/* Defaults to CIPSO if not specified */
32045916cd2Sjpk 	mask |= RTSA_CIPSO;
32145916cd2Sjpk 
32245916cd2Sjpk 	/* If RTSA_CIPSO is specified, RTSA_DOI must be specified */
32345916cd2Sjpk 	if (!(mask & RTSA_DOI)) {
32445916cd2Sjpk 		*errp = LTSNET_NO_DOI;
325*7b0bedd4SRic Aleshire 		goto out_err;
32645916cd2Sjpk 	}
32745916cd2Sjpk 
32845916cd2Sjpk 	/* SL range must be specified */
32945916cd2Sjpk 	if (!(mask & (RTSA_MINSL|RTSA_MAXSL))) {
33045916cd2Sjpk 		*errp = LTSNET_NO_RANGE;
331*7b0bedd4SRic Aleshire 		goto out_err;
33245916cd2Sjpk 	}
33345916cd2Sjpk 	if (!(mask & RTSA_MINSL)) {
33445916cd2Sjpk 		*errp = LTSNET_NO_LOWERBOUND;
335*7b0bedd4SRic Aleshire 		goto out_err;
33645916cd2Sjpk 	}
33745916cd2Sjpk 	if (!(mask & RTSA_MAXSL)) {
33845916cd2Sjpk 		*errp = LTSNET_NO_UPPERBOUND;
339*7b0bedd4SRic Aleshire 		goto out_err;
34045916cd2Sjpk 	}
34145916cd2Sjpk 
34245916cd2Sjpk 	/* SL range must have upper bound dominating lower bound */
343*7b0bedd4SRic Aleshire 	if (!bldominates(max_sl, min_sl)) {
34445916cd2Sjpk 		*errp = LTSNET_ILL_RANGE;
345*7b0bedd4SRic Aleshire 		goto out_err;
34645916cd2Sjpk 	}
34745916cd2Sjpk 
34845916cd2Sjpk 	if (mask & RTSA_MINSL)
349*7b0bedd4SRic Aleshire 		sp->rtsa_slrange.lower_bound = *min_sl;
35045916cd2Sjpk 	if (mask & RTSA_MAXSL)
351*7b0bedd4SRic Aleshire 		sp->rtsa_slrange.upper_bound = *max_sl;
35245916cd2Sjpk 	if (mask & RTSA_DOI)
35345916cd2Sjpk 		sp->rtsa_doi = doi;
35445916cd2Sjpk 	sp->rtsa_mask = mask;
35545916cd2Sjpk 
356*7b0bedd4SRic Aleshire 	m_label_free(min_sl);
357*7b0bedd4SRic Aleshire 	m_label_free(max_sl);
358*7b0bedd4SRic Aleshire 
35945916cd2Sjpk 	return (B_TRUE);
360*7b0bedd4SRic Aleshire 
361*7b0bedd4SRic Aleshire out_err:
362*7b0bedd4SRic Aleshire 	m_label_free(min_sl);
363*7b0bedd4SRic Aleshire 	m_label_free(max_sl);
364*7b0bedd4SRic Aleshire 
365*7b0bedd4SRic Aleshire 	return (B_FALSE);
36645916cd2Sjpk }
36745916cd2Sjpk 
36845916cd2Sjpk /* Keep in sync with libtsnet.h */
36945916cd2Sjpk static const char *tsol_errlist[] = {
37045916cd2Sjpk 	"No error",
37145916cd2Sjpk 	"System error",
37245916cd2Sjpk 	"Empty string or end of list",
37345916cd2Sjpk 	"Entry is malformed",
37445916cd2Sjpk 	"Missing name",
37545916cd2Sjpk 	"Missing attributes",
37645916cd2Sjpk 	"Illegal name",
37745916cd2Sjpk 	"Illegal keyword delimiter",
37845916cd2Sjpk 	"Unknown keyword",
37945916cd2Sjpk 	"Duplicate keyword",
38045916cd2Sjpk 	"Illegal value delimiter",
38145916cd2Sjpk 	"Missing host type",
38245916cd2Sjpk 	"Illegal host type",
38345916cd2Sjpk 	"Missing label",
38445916cd2Sjpk 	"Illegal label",
38545916cd2Sjpk 	"Missing label range",
38645916cd2Sjpk 	"Illegal label range",
38745916cd2Sjpk 	"No lower bound in range",
38845916cd2Sjpk 	"Illegal lower bound in range",
38945916cd2Sjpk 	"No upper bound in range",
39045916cd2Sjpk 	"Illegal upper bound in range",
39145916cd2Sjpk 	"Missing DOI",
39245916cd2Sjpk 	"Illegal DOI",
39345916cd2Sjpk 	"Too many entries in set",
39445916cd2Sjpk 	"Missing address/network",
39545916cd2Sjpk 	"Illegal address/network",
39645916cd2Sjpk 	"Illegal flag",
39745916cd2Sjpk 	"Illegal MLP specification",
39845916cd2Sjpk 	"Unacceptable keyword for type"
39945916cd2Sjpk };
40045916cd2Sjpk static const int tsol_nerr = sizeof (tsol_errlist) / sizeof (*tsol_errlist);
40145916cd2Sjpk 
40245916cd2Sjpk const char *
40345916cd2Sjpk tsol_strerror(int libtserr, int errnoval)
40445916cd2Sjpk {
40545916cd2Sjpk 	if (libtserr == LTSNET_SYSERR)
40645916cd2Sjpk 		return (strerror(errnoval));
40745916cd2Sjpk 	if (libtserr >= 0 && libtserr < tsol_nerr)
40845916cd2Sjpk 		return (gettext(tsol_errlist[libtserr]));
40945916cd2Sjpk 	return (gettext("Unknown error"));
41045916cd2Sjpk }
411