xref: /titanic_41/usr/src/cmd/cmd-inet/usr.sbin/ipqosconf/ipqosconf.c (revision dfb96a4f56fb431b915bc67e5d9d5c8d4f4f6679)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /* enable debug output and some debug asserts */
30 #undef	_IPQOS_CONF_DEBUG
31 
32 #include <stdlib.h>
33 #include <unistd.h>
34 #include <libintl.h>
35 #include <signal.h>
36 #include <strings.h>
37 #include <sys/nvpair.h>
38 #include <stdio.h>
39 #include <netinet/in.h>
40 #include <arpa/inet.h>
41 #include <ctype.h>
42 #include <sys/socket.h>
43 #include <limits.h>
44 #include <netdb.h>
45 #include <fcntl.h>
46 #include <sys/types.h>
47 #include <sys/stat.h>
48 #include <errno.h>
49 #include <libipp.h>
50 #include <ipp/ipp_config.h>
51 #include <ipp/ipgpc/ipgpc.h>
52 #include <ipp/ipp.h>
53 #ifdef	_IPQOS_CONF_DEBUG
54 #include <assert.h>
55 #endif
56 #include <sys/sockio.h>
57 #include <syslog.h>
58 #include <stdarg.h>
59 #include <libintl.h>
60 #include <locale.h>
61 #include <pwd.h>
62 #include "ipqosconf.h"
63 
64 #if	defined(_IPQOS_CONF_DEBUG)
65 
66 /* debug level */
67 static int ipqosconf_dbg_flgs =
68 /*
69  */
70 RBK |
71 MHME |
72 KRET |
73 DIFF |
74 APPLY |
75 L2 |
76 L1 |
77 L0 |
78 0;
79 
80 
81 
82 #define	IPQOSCDBG0(lvl, x)\
83 	if (lvl & ipqosconf_dbg_flgs)\
84 		(void) fprintf(stderr, x)
85 
86 #define	IPQOSCDBG1(lvl, x, y)\
87 	if (lvl & ipqosconf_dbg_flgs)\
88 		(void) fprintf(stderr, x, y)
89 
90 #define	IPQOSCDBG2(lvl, x, y, z)\
91 	if (lvl & ipqosconf_dbg_flgs)\
92 		(void) fprintf(stderr, x, y, z)
93 
94 #define	IPQOSCDBG3(lvl, x, y, z, a)\
95 	if (lvl & ipqosconf_dbg_flgs)\
96 		(void) fprintf(stderr, x, y, z, a)
97 
98 #define	IPQOSCDBG4(lvl, x, y, z, a, b)\
99 	if (lvl & ipqosconf_dbg_flgs)\
100 		(void) fprintf(stderr, x, y, z, a, b)
101 
102 #define	IPQOSCDBG5(lvl, x, y, z, a, b, c)\
103 	if (lvl & ipqosconf_dbg_flgs)\
104 		(void) fprintf(stderr, x, y, z, a, b, c)
105 
106 #else	/* defined(_IPQOS_CONF_DEBUG) && !defined(lint) */
107 
108 #define	IPQOSCDBG0(lvl, x)
109 #define	IPQOSCDBG1(lvl, x, y)
110 #define	IPQOSCDBG2(lvl, x, y, z)
111 #define	IPQOSCDBG3(lvl, x, y, z, a)
112 #define	IPQOSCDBG4(lvl, x, y, z, a, b)
113 #define	IPQOSCDBG5(lvl, x, y, z, a, b, c)
114 
115 #endif	/* defined(_IPQOS_CONF_DEBUG) */
116 
117 
118 
119 /* function prototypes */
120 
121 static int modify_params(char *, nvlist_t **, int, boolean_t);
122 static int add_class(char *, char *, int, boolean_t, char *);
123 static int modify_class(char *, char *, int, boolean_t, char *,
124     enum ipp_flags);
125 static int remove_class(char *, char *, int, enum ipp_flags);
126 static int add_filter(char *, ipqos_conf_filter_t *, int);
127 static int modify_filter(char *, ipqos_conf_filter_t *, int);
128 static int remove_filter(char *, char *, int, int);
129 static boolean_t arrays_equal(int *, int *, uint32_t);
130 static int diffclass(ipqos_conf_class_t *, ipqos_conf_class_t *);
131 static int diffparams(ipqos_conf_params_t *, ipqos_conf_params_t *, char *);
132 static int difffilter(ipqos_conf_filter_t *, ipqos_conf_filter_t *, char *);
133 static int add_filters(ipqos_conf_filter_t *, char *, int, boolean_t);
134 static int add_classes(ipqos_conf_class_t *, char *,  int, boolean_t);
135 static int modify_items(ipqos_conf_action_t *);
136 static int add_items(ipqos_conf_action_t *, boolean_t);
137 static int add_item(ipqos_conf_action_t *, boolean_t);
138 static int remove_items(ipqos_conf_action_t *, boolean_t);
139 static int remove_item(ipqos_conf_action_t *, boolean_t);
140 static int undo_modifys(ipqos_conf_action_t *, ipqos_conf_action_t *);
141 static int applydiff(ipqos_conf_action_t *, ipqos_conf_action_t *);
142 static int rollback(ipqos_conf_action_t *, ipqos_conf_action_t *);
143 static int rollback_recover(ipqos_conf_action_t *);
144 static ipqos_conf_class_t *classexist(char *, ipqos_conf_class_t *);
145 static ipqos_conf_filter_t *filterexist(char *, int, ipqos_conf_filter_t *);
146 static ipqos_conf_action_t *actionexist(char *, ipqos_conf_action_t *);
147 static int diffnvlists(nvlist_t *, nvlist_t *, char *, int *, place_t);
148 static int diffaction(ipqos_conf_action_t *, ipqos_conf_action_t *);
149 static int diffconf(ipqos_conf_action_t *, ipqos_conf_action_t *);
150 static int readllong(char *, long long *, char **);
151 static int readuint8(char *, uint8_t *, char **);
152 static int readuint16(char *, uint16_t *, char **);
153 static int readint16(char *, int16_t *, char **);
154 static int readint32(char *, int *, char **);
155 static int readuint32(char *, uint32_t *, char **);
156 static int readbool(char *, boolean_t *);
157 static void setmask(int, in6_addr_t *, int);
158 static int readtoken(FILE *, char **);
159 static nvpair_t *find_nvpair(nvlist_t *, char *);
160 static char *prepend_module_name(char *, char *);
161 static int readnvpair(FILE *, FILE *, nvlist_t **, nvpair_t **,
162     ipqos_nvtype_t *, place_t, char *);
163 static int add_aref(ipqos_conf_act_ref_t **, char *, char *);
164 static int readparams(FILE *, FILE *, char *, ipqos_conf_params_t *);
165 static int readclass(FILE *, char *, ipqos_conf_class_t **, char **, int);
166 static int readfilter(FILE *, FILE *, char *, ipqos_conf_filter_t **, char **,
167     int);
168 static FILE *validmod(char *, int *);
169 static int readaction(FILE *, ipqos_conf_action_t **);
170 static int actions_unique(ipqos_conf_action_t *, char **);
171 static int validconf(ipqos_conf_action_t *, int);
172 static int readconf(FILE *, ipqos_conf_action_t **);
173 static int flush(boolean_t *);
174 static int atomic_flush(boolean_t);
175 static int flushconf();
176 static int writeconf(ipqos_conf_action_t *, char *);
177 static int commitconf();
178 static int applyconf(char *ifile);
179 static int block_all_signals();
180 static int restore_all_signals();
181 static int unlock(int fd);
182 static int lock();
183 static int viewconf(int);
184 static void usage();
185 static int valid_name(char *);
186 static int in_cycle(ipqos_conf_action_t *);
187 static int readtype(FILE *, char *, char *, ipqos_nvtype_t *, str_val_nd_t **,
188     char *, boolean_t, place_t *);
189 static int read_int_array_info(char *, str_val_nd_t **, uint32_t *, int *,
190     int *, char *);
191 static str_val_nd_t *read_enum_nvs(char *, char *);
192 static int add_str_val_entry(str_val_nd_t **, char *, uint32_t);
193 static void free_str_val_entrys(str_val_nd_t *);
194 static void get_str_val_value_range(str_val_nd_t *, int *, int *);
195 static int read_enum_value(FILE *, char *, str_val_nd_t *, uint32_t *);
196 static int read_mapped_values(FILE *, nvlist_t **, char *, char *,
197     int);
198 static int read_int_array(FILE *, char *, int **, uint32_t, int, int,
199     str_val_nd_t *);
200 static int str_val_list_lookup(str_val_nd_t *, char *, uint32_t *);
201 static int parse_kparams(char *, ipqos_conf_params_t *, nvlist_t *);
202 static int parse_kclass(ipqos_conf_class_t *, nvlist_t *);
203 static int parse_kfilter(ipqos_conf_filter_t *, nvlist_t *);
204 static int parse_kaction(nvlist_t *, ipqos_actinfo_prm_t *);
205 static int readkconf(ipqos_conf_action_t **);
206 static void print_int_array(FILE *, int *, uint32_t, int, int, str_val_nd_t *,
207     int);
208 static void printrange(FILE *fp, uint32_t, uint32_t);
209 static void printenum(FILE *, uint32_t, str_val_nd_t *);
210 static void printproto(FILE *, uint8_t);
211 static void printport(FILE *, uint16_t);
212 static int printnvlist(FILE *, char *, nvlist_t *, int, ipqos_conf_filter_t *,
213     int, place_t);
214 static int virtual_action(char *);
215 static void free_arefs(ipqos_conf_act_ref_t *);
216 static void print_action_nm(FILE *, char *);
217 static int add_orig_ipqosconf(nvlist_t *);
218 static char *get_originator_nm(uint32_t);
219 static void mark_classes_filters_new(ipqos_conf_action_t *);
220 static void mark_classes_filters_del(ipqos_conf_action_t *);
221 static void mark_config_new(ipqos_conf_action_t *);
222 static int printifname(FILE *, int);
223 static int readifindex(char *, int *);
224 static void cleanup_string_table(char **, int);
225 static int domultihome(ipqos_conf_filter_t *, ipqos_conf_filter_t **,
226     boolean_t);
227 static int dup_filter(ipqos_conf_filter_t *, ipqos_conf_filter_t **, int, int,
228     void *, void *, int);
229 static void free_actions(ipqos_conf_action_t *);
230 static ipqos_conf_filter_t *alloc_filter();
231 static void free_filter(ipqos_conf_filter_t *);
232 static int read_curl_begin(FILE *);
233 static ipqos_conf_class_t *alloc_class(void);
234 static int diffclasses(ipqos_conf_action_t *old, ipqos_conf_action_t *new);
235 static int difffilters(ipqos_conf_action_t *old, ipqos_conf_action_t *new);
236 static int dup_class(ipqos_conf_class_t *src, ipqos_conf_class_t **dst);
237 static int add_action(ipqos_conf_action_t *act);
238 static int masktocidr(int af, in6_addr_t *mask);
239 static int read_perm_items(int, FILE *, char *, char ***, int *);
240 static int in_string_table(char *stable[], int size, char *string);
241 static void list_end(ipqos_list_el_t **listp, ipqos_list_el_t ***lendpp);
242 static void add_to_list(ipqos_list_el_t **listp, ipqos_list_el_t *el);
243 static int read_cfile_ver(FILE *, char *);
244 static char *quote_ws_string(const char *);
245 static int read_tfile_ver(FILE *, char *, char *);
246 static int ver_str_to_int(char *);
247 static void printuser(FILE *fp, uid_t uid);
248 static int readuser(char *str, uid_t *uid);
249 
250 /*
251  * macros to call list functions with the more complex list element type
252  * cast to the skeletal type iqpos_list_el_t.
253  */
254 #define	LIST_END(list, end)\
255 	list_end((ipqos_list_el_t **)list,  (ipqos_list_el_t ***)end)
256 #define	ADD_TO_LIST(list, el)\
257 	add_to_list((ipqos_list_el_t **)list, (ipqos_list_el_t *)el)
258 
259 /*
260  *	Macros to produce a quoted string containing the value of a
261  *	preprocessor macro. For example, if SIZE is defined to be 256,
262  *	VAL2STR(SIZE) is "256". This is used to construct format
263  *	strings for scanf-family functions below.
264  */
265 #define	QUOTE(x)	#x
266 #define	VAL2STR(x)	QUOTE(x)
267 
268 
269 /* globals */
270 
271 /* table of supported parameter types and enum value */
272 static str_val_t nv_types[] = {
273 {"uint8",		IPQOS_DATA_TYPE_UINT8},
274 {"int16",		IPQOS_DATA_TYPE_INT16},
275 {"uint16",		IPQOS_DATA_TYPE_UINT16},
276 {"int32",		IPQOS_DATA_TYPE_INT32},
277 {"uint32",		IPQOS_DATA_TYPE_UINT32},
278 {"boolean",		IPQOS_DATA_TYPE_BOOLEAN},
279 {"string",		IPQOS_DATA_TYPE_STRING},
280 {"action",		IPQOS_DATA_TYPE_ACTION},
281 {"address",		IPQOS_DATA_TYPE_ADDRESS},
282 {"port",		IPQOS_DATA_TYPE_PORT},
283 {"protocol",		IPQOS_DATA_TYPE_PROTO},
284 {"enum",		IPQOS_DATA_TYPE_ENUM},
285 {"ifname",		IPQOS_DATA_TYPE_IFNAME},
286 {"mindex",		IPQOS_DATA_TYPE_M_INDEX},
287 {"int_array",		IPQOS_DATA_TYPE_INT_ARRAY},
288 {"user",		IPQOS_DATA_TYPE_USER},
289 {"",			0}
290 };
291 
292 /* table of name to id mappings for originator field */
293 
294 static str_val_t originators[] = {
295 {IPP_CONFIG_NAME_PERMANENT,	IPP_CONFIG_PERMANENT},
296 {IPP_CONFIG_NAME_IPQOSCONF,	IPP_CONFIG_IPQOSCONF},
297 {IPP_CONFIG_NAME_FTPCL,		IPP_CONFIG_FTPCL},
298 {"", -1}
299 };
300 
301 /* current parse line */
302 static int lineno;
303 
304 /* verbose output flag */
305 static int verbose;
306 
307 /* use syslog for msg reporting flag */
308 static int use_syslog;
309 
310 #ifdef	_IPQOS_CONF_DEBUG
311 /*
312  * flag used to indicate that a rollback should be carried out regardless.
313  * Only settable during debug.
314  */
315 static int force_rback = 0;
316 #endif	/* _IPQOS_CONF_DEBUG */
317 
318 /*
319  * delivers messages to either syslog or stderr, dependant upon the
320  * the state of the flags use_syslog and verbose. The type
321  * of the msg as given in msg_type is indicated in the output msg.
322  *
323  * valid message types are:
324  * o  MT_ERROR (standard error message)
325  * o  MT_ENOSTR (error message with system error string appended)
326  * o  MT_WARNING (warning message)
327  * o  MT_LOG (logging message)
328  *
329  * Log messages only go to syslog. Warning messages only go to stderr
330  * and only when the verbose flag is set. All other messages go by default
331  * to the console; to syslog if syslog flag set, and to both if both
332  * syslog and verbose are set.
333  *
334  */
335 /*PRINTFLIKE2*/
336 static void
337 ipqos_msg(enum msg_type msgt, char *format, ...)
338 {
339 	va_list ap;
340 	char str_buf[IPQOS_MSG_BUF_SZ];
341 	char fmt_buf[IPQOS_MSG_BUF_SZ];
342 	char *cp;
343 
344 	IPQOSCDBG0(L1, "In ipqos_msg:\n");
345 
346 	va_start(ap, format);
347 
348 	/*
349 	 * send msgs to syslog if use_syslog set (except warning msgs),
350 	 * or a log msg.
351 	 */
352 	if ((use_syslog && (msgt != MT_WARNING)) || msgt == MT_LOG) {
353 
354 		/* fill in format string */
355 		(void) vsnprintf(str_buf, IPQOS_MSG_BUF_SZ, format, ap);
356 
357 		/*
358 		 * print message to syslog with appropriate severity
359 		 */
360 		if (msgt == MT_ERROR) {
361 			syslog(LOG_ERR, str_buf);
362 		} else if (msgt == MT_LOG) {
363 			syslog(LOG_INFO, str_buf);
364 		/*
365 		 * for errno message type suffix with %m for syslog to
366 		 * interpret.
367 		 */
368 		} else if (msgt == MT_ENOSTR) {
369 			/*
370 			 * remove any newline in message parameter.
371 			 * syslog will reapply a newline for us later.
372 			 */
373 			if ((cp = strchr(str_buf, '\n')) != NULL)
374 				*cp = '\0';
375 			(void) strlcat(str_buf, ": %m", IPQOS_MSG_BUF_SZ);
376 			syslog(LOG_ERR, str_buf);
377 		}
378 	}
379 
380 	/*
381 	 * send msgs to stderr if use_syslog not set (except log msgs), or
382 	 * if verbose set.
383 	 */
384 	if ((!use_syslog && (msgt != MT_LOG)) || (verbose)) {
385 
386 		/*
387 		 * prefix message with appropriate severity string
388 		 */
389 		if (msgt == MT_ERROR) {
390 			(void) strlcpy(fmt_buf, gettext("Error: "),
391 			    IPQOS_MSG_BUF_SZ);
392 		} else if (msgt == MT_WARNING) {
393 			if (!verbose) { /* don't show warn msg if !verbose */
394 				va_end(ap);
395 				return;
396 			}
397 			(void) strlcpy(fmt_buf, gettext("Warning: "),
398 			    IPQOS_MSG_BUF_SZ);
399 		} else if (msgt == MT_ENOSTR) {
400 			(void) strlcpy(fmt_buf, gettext("Error: "),
401 			    IPQOS_MSG_BUF_SZ);
402 		} else if (msgt == MT_LOG) {
403 			(void) strlcpy(fmt_buf, gettext("Notice: "),
404 			    IPQOS_MSG_BUF_SZ);
405 		}
406 		(void) strlcat(fmt_buf, format, IPQOS_MSG_BUF_SZ);
407 
408 		/*
409 		 * for errno message type suffix message with errno string
410 		 */
411 		if (msgt == MT_ENOSTR) {
412 			/*
413 			 * get rid of any newline in passed message.
414 			 * we'll apply another later.
415 			 */
416 			if ((cp = strchr(fmt_buf, '\n')) != NULL)
417 				*cp = '\0';
418 			(void) strlcat(fmt_buf, ": ", IPQOS_MSG_BUF_SZ);
419 			(void) strlcat(fmt_buf, strerror(errno),
420 			    IPQOS_MSG_BUF_SZ);
421 		}
422 
423 		/*
424 		 * append a newline to message if not one already.
425 		 */
426 		if ((cp = strchr(fmt_buf, '\n')) == NULL)
427 			(void) strlcat(fmt_buf, "\n", IPQOS_MSG_BUF_SZ);
428 
429 		(void) vfprintf(stderr, fmt_buf, ap);
430 	}
431 
432 	va_end(ap);
433 }
434 
435 /* **************** kernel filter/class/params manipulation fns *********** */
436 
437 
438 /*
439  * modify the kernel parameters of the action action_nm using the nvlist
440  * parameter nvl and setting the stats according to stats_enable.
441  * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCES.
442  */
443 
444 static int
445 modify_params(
446 char *action_name,
447 nvlist_t **nvl,
448 int module_version,
449 boolean_t stats_enable)
450 {
451 
452 	int res;
453 	int created = 0;
454 
455 	IPQOSCDBG1(APPLY, "In modify_params: action: %s\n", action_name);
456 
457 	/* create nvlist if NULL */
458 	if (*nvl == NULL) {
459 		created++;
460 		res = nvlist_alloc(nvl, NV_UNIQUE_NAME, 0);
461 		if (res != 0) {
462 			ipqos_msg(MT_ENOSTR, "nvlist_alloc");
463 			return (IPQOS_CONF_ERR);
464 		}
465 	}
466 
467 	/* add params modify config type */
468 	res = nvlist_add_byte(*nvl, IPP_CONFIG_TYPE, IPP_SET);
469 	if (res != 0) {
470 		ipqos_msg(MT_ENOSTR, "nvlist_add_byte");
471 		goto fail;
472 	}
473 
474 	/*
475 	 * add module version
476 	 */
477 	if (nvlist_add_uint32(*nvl, IPP_MODULE_VERSION,
478 	    (uint32_t)module_version) != 0) {
479 		ipqos_msg(MT_ENOSTR, "nvlist_add_uint32");
480 		goto fail;
481 	}
482 
483 	/* add stats_enable */
484 	res = nvlist_add_uint32(*nvl, IPP_ACTION_STATS_ENABLE,
485 	    (uint32_t)stats_enable);
486 	if (res != 0) {
487 		ipqos_msg(MT_ENOSTR, "nvlist_add_uint32");
488 		goto fail;
489 	}
490 
491 	/* add ipqosconf as originator */
492 	res = add_orig_ipqosconf(*nvl);
493 	if (res != IPQOS_CONF_SUCCESS) {
494 		goto fail;
495 	}
496 
497 	/* call lib to do modify */
498 	res = ipp_action_modify(action_name, nvl, 0);
499 	if (res != 0) {
500 
501 		/* invalid parameters */
502 
503 		if (errno == EINVAL) {
504 			ipqos_msg(MT_ERROR,
505 			    gettext("Invalid parameters for action %s.\n"),
506 			    action_name);
507 
508 
509 		} else if (errno == ENOENT) {
510 			ipqos_msg(MT_ERROR,
511 			    gettext("Mandatory parameter missing for "
512 			    "action %s.\n"), action_name);
513 
514 
515 		} else {	/* unexpected error */
516 			ipqos_msg(MT_ERROR, gettext("Failed to modify action "
517 			    "%s parameters: %s.\n"), action_name,
518 			    strerror(errno));
519 		}
520 
521 		goto fail;
522 	}
523 
524 	return (IPQOS_CONF_SUCCESS);
525 fail:
526 	if (created && *nvl != NULL) {
527 		nvlist_free(*nvl);
528 		*nvl = NULL;
529 	}
530 	return (IPQOS_CONF_ERR);
531 }
532 
533 /*
534  * add a class to the kernel action action_name called class_name with
535  * stats set according to stats_enable and the first action set to
536  * first_action.
537  * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCES.
538  */
539 static int
540 add_class(
541 char *action_name,
542 char *class_name,
543 int module_version,
544 boolean_t stats_enable,
545 char *first_action)
546 {
547 
548 	nvlist_t *nvl;
549 
550 	IPQOSCDBG4(APPLY, "add_class: action: %s, class: %s, "
551 	    "first_action: %s, stats: %s\n", action_name, class_name,
552 	    first_action, (stats_enable == B_TRUE ? "true" : "false"));
553 
554 
555 	/* create nvlist */
556 	if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) {
557 		ipqos_msg(MT_ENOSTR, "nvlist_alloc");
558 		return (IPQOS_CONF_ERR);
559 	}
560 
561 	/* add 'add class' config type */
562 	if (nvlist_add_byte(nvl, IPP_CONFIG_TYPE, CLASSIFIER_ADD_CLASS) != 0) {
563 		ipqos_msg(MT_ENOSTR, "nvlist_add_byte");
564 		goto fail;
565 	}
566 
567 	/*
568 	 * add module version
569 	 */
570 	if (nvlist_add_uint32(nvl, IPP_MODULE_VERSION,
571 	    (uint32_t)module_version) != 0) {
572 		ipqos_msg(MT_ENOSTR, "nvlist_add_uint32");
573 		goto fail;
574 	}
575 
576 	/* add class name */
577 	if (nvlist_add_string(nvl, CLASSIFIER_CLASS_NAME, class_name) != 0) {
578 		ipqos_msg(MT_ENOSTR, "nvlist_add_string");
579 		goto fail;
580 	}
581 
582 	/* add next action */
583 	if (nvlist_add_string(nvl, CLASSIFIER_NEXT_ACTION, first_action) != 0) {
584 		ipqos_msg(MT_ENOSTR, "nvlist_add_string");
585 		goto fail;
586 	}
587 
588 	/* add stats_enable */
589 	if (nvlist_add_uint32(nvl, CLASSIFIER_CLASS_STATS_ENABLE,
590 	    (uint32_t)stats_enable) != 0) {
591 		ipqos_msg(MT_ENOSTR, "nvlist_add_uint32");
592 		goto fail;
593 	}
594 
595 	/* add ipqosconf as originator */
596 	if (add_orig_ipqosconf(nvl) != IPQOS_CONF_SUCCESS) {
597 		goto fail;
598 	}
599 
600 	/* call lib to do modify */
601 	if (ipp_action_modify(action_name, &nvl, 0) != 0) {
602 
603 		/* ipgpc max classes */
604 
605 		if (errno == ENOSPC &&
606 		    strcmp(action_name, IPGPC_CLASSIFY) == 0) {
607 			ipqos_msg(MT_ERROR,
608 			    gettext("Max number of classes reached in %s.\n"),
609 			    IPGPC_NAME);
610 
611 		/* other errors */
612 
613 		} else {
614 			ipqos_msg(MT_ERROR,
615 			    gettext("Failed to create class %s in action "
616 			    "%s: %s.\n"), class_name, action_name,
617 			    strerror(errno));
618 		}
619 
620 		goto fail;
621 	}
622 
623 	return (IPQOS_CONF_SUCCESS);
624 fail:
625 	if (nvl != NULL)
626 		nvlist_free(nvl);
627 	return (IPQOS_CONF_ERR);
628 }
629 
630 
631 /*
632  * modify the class in the kernel action action_name called class_name with
633  * stats set according to stats_enable and the first action set to
634  * first_action.
635  * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCES.
636  */
637 static int
638 modify_class(
639 char *action_name,
640 char *class_name,
641 int module_version,
642 boolean_t stats_enable,
643 char *first_action,
644 enum ipp_flags flags)
645 {
646 
647 	nvlist_t *nvl;
648 
649 	IPQOSCDBG5(APPLY, "modify_class: action: %s, class: %s, first: %s, "
650 	    "stats: %s, flags: %x\n", action_name, class_name, first_action,
651 	    stats_enable == B_TRUE ? "true" : "false", flags);
652 
653 
654 	/* create nvlist */
655 	if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) {
656 		ipqos_msg(MT_ENOSTR, "nvlist_alloc");
657 		return (IPQOS_CONF_ERR);
658 	}
659 
660 	/* add 'modify class' config type */
661 	if (nvlist_add_byte(nvl, IPP_CONFIG_TYPE, CLASSIFIER_MODIFY_CLASS) !=
662 	    0) {
663 		ipqos_msg(MT_ENOSTR, "nvlist_add_byte");
664 		goto fail;
665 	}
666 
667 	/*
668 	 * add module version
669 	 */
670 	if (nvlist_add_uint32(nvl, IPP_MODULE_VERSION,
671 	    (uint32_t)module_version) != 0) {
672 		ipqos_msg(MT_ENOSTR, "nvlist_add_uint32");
673 		goto fail;
674 	}
675 
676 	/* add class name */
677 	if (nvlist_add_string(nvl, CLASSIFIER_CLASS_NAME, class_name) != 0) {
678 		ipqos_msg(MT_ENOSTR, "nvlist_add_string");
679 		goto fail;
680 	}
681 
682 	/* add next action */
683 	if (nvlist_add_string(nvl, CLASSIFIER_NEXT_ACTION, first_action) != 0) {
684 		ipqos_msg(MT_ENOSTR, "nvlist_add_string");
685 		goto fail;
686 	}
687 
688 	/* add stats enable */
689 	if (nvlist_add_uint32(nvl, CLASSIFIER_CLASS_STATS_ENABLE,
690 	    (uint32_t)stats_enable) != 0) {
691 		ipqos_msg(MT_ENOSTR, "nvlist_add_uint32");
692 		goto fail;
693 	}
694 
695 	/* add originator ipqosconf */
696 	if (add_orig_ipqosconf(nvl) != IPQOS_CONF_SUCCESS) {
697 		goto fail;
698 	}
699 
700 	/* call lib to do modify */
701 	if (ipp_action_modify(action_name, &nvl, flags) != 0) {
702 
703 		/* generic error message */
704 
705 		ipqos_msg(MT_ERROR,
706 		    gettext("Modifying class %s in action %s failed: %s.\n"),
707 		    class_name, action_name, strerror(errno));
708 
709 		goto fail;
710 	}
711 
712 	return (IPQOS_CONF_SUCCESS);
713 fail:
714 	if (nvl != NULL)
715 		nvlist_free(nvl);
716 	return (IPQOS_CONF_ERR);
717 }
718 
719 /*
720  * removes the class class_name from the kernel action action_name. The
721  * flags argument can currently be set to IPP_ACTION_DESTROY which will
722  * result in the action this class references being destroyed.
723  * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCES.
724  */
725 static int
726 remove_class(
727 char *action_name,
728 char *class_name,
729 int module_version,
730 enum ipp_flags flags)
731 {
732 
733 	nvlist_t *nvl;
734 
735 	IPQOSCDBG3(APPLY, "remove_class: action: %s, class: %s, "
736 	    "flags: %x\n", action_name, class_name, flags);
737 
738 	/* allocate nvlist */
739 	if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) {
740 		ipqos_msg(MT_ENOSTR, "nvlist_alloc");
741 		return (IPQOS_CONF_ERR);
742 	}
743 
744 	/* add 'remove class' config type */
745 	if (nvlist_add_byte(nvl, IPP_CONFIG_TYPE, CLASSIFIER_REMOVE_CLASS) !=
746 	    0) {
747 		ipqos_msg(MT_ENOSTR, "nvlist_add_byte");
748 		goto fail;
749 	}
750 
751 	/*
752 	 * add module version
753 	 */
754 	if (nvlist_add_uint32(nvl, IPP_MODULE_VERSION,
755 	    (uint32_t)module_version) != 0) {
756 		ipqos_msg(MT_ENOSTR, "nvlist_add_uint32");
757 		goto fail;
758 	}
759 
760 	/* add class name */
761 	if (nvlist_add_string(nvl, CLASSIFIER_CLASS_NAME, class_name) != 0) {
762 		ipqos_msg(MT_ENOSTR, "nvlist_add_string");
763 		goto fail;
764 	}
765 
766 	if (ipp_action_modify(action_name, &nvl, flags) != 0) {
767 
768 		/* generic error message */
769 
770 		ipqos_msg(MT_ERROR,
771 		    gettext("Removing class %s in action %s failed: %s.\n"),
772 		    class_name, action_name, strerror(errno));
773 
774 		goto fail;
775 	}
776 
777 	return (IPQOS_CONF_SUCCESS);
778 fail:
779 	if (nvl != NULL)
780 		nvlist_free(nvl);
781 	return (IPQOS_CONF_ERR);
782 }
783 
784 /*
785  * add the filter flt to the kernel action named action_name.
786  * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCES.
787  */
788 static int
789 add_filter(
790 char *action_name,
791 ipqos_conf_filter_t *flt,
792 int module_version)
793 {
794 
795 	nvlist_t *nvl = flt->nvlist;
796 	char ipvsbuf[IPQOS_INT_STR_LEN];
797 
798 	IPQOSCDBG4(APPLY, "add_filter: action: %s, filter: %s, "
799 	    "instance: %d, class: %s\n", action_name, flt->name,
800 	    flt->instance, flt->class_name);
801 
802 
803 	/* add 'add filter' config type to filter nvlist */
804 	if (nvlist_add_byte(nvl, IPP_CONFIG_TYPE, CLASSIFIER_ADD_FILTER) != 0) {
805 		ipqos_msg(MT_ENOSTR, "nvlist_add_byte");
806 		return (IPQOS_CONF_ERR);
807 	}
808 
809 	/*
810 	 * add module version
811 	 */
812 	if (nvlist_add_uint32(nvl, IPP_MODULE_VERSION,
813 	    (uint32_t)module_version) != 0) {
814 		ipqos_msg(MT_ENOSTR, "nvlist_add_uint32");
815 		return (IPQOS_CONF_ERR);
816 	}
817 
818 	/* add filter name to nvlist */
819 	if (nvlist_add_string(nvl, CLASSIFIER_FILTER_NAME, flt->name) != 0) {
820 		ipqos_msg(MT_ENOSTR, "nvlist_add_string");
821 		return (IPQOS_CONF_ERR);
822 	}
823 
824 	/* add class name to nvlist */
825 	if (nvlist_add_string(nvl, CLASSIFIER_CLASS_NAME, flt->class_name) !=
826 	    0) {
827 		ipqos_msg(MT_ENOSTR, "nvlist_add_string");
828 		return (IPQOS_CONF_ERR);
829 	}
830 
831 	/* add ipqosconf as originator to nvlist */
832 	if (add_orig_ipqosconf(nvl) != IPQOS_CONF_SUCCESS) {
833 		return (IPQOS_CONF_ERR);
834 	}
835 
836 	/* add ipgpc specific nv entrys */
837 	if (strcmp(action_name, IPGPC_CLASSIFY) == 0) {
838 
839 		/* add src and dst nodes to nvlist if present */
840 
841 		if (flt->src_nd_name != NULL &&
842 		    nvlist_add_string(nvl, IPGPC_SADDR_HOSTNAME,
843 		    flt->src_nd_name) != 0) {
844 			ipqos_msg(MT_ENOSTR, "nvlist_add_string");
845 			return (IPQOS_CONF_ERR);
846 		}
847 		if (flt->dst_nd_name != NULL &&
848 		    nvlist_add_string(nvl, IPGPC_DADDR_HOSTNAME,
849 		    flt->dst_nd_name) != 0) {
850 			ipqos_msg(MT_ENOSTR, "nvlist_add_string");
851 			return (IPQOS_CONF_ERR);
852 		}
853 
854 		/*
855 		 * add ip_version to private list element if present.
856 		 * NOTE: this value is of only real use to ipqosconf so
857 		 * it is placed in this opaque private field.
858 		 */
859 		if (flt->ip_versions != 0) {
860 			(void) sprintf(ipvsbuf, "%d", flt->ip_versions);
861 			if (nvlist_add_string(nvl, IPGPC_FILTER_PRIVATE,
862 			    ipvsbuf) != 0) {
863 				ipqos_msg(MT_ENOSTR, "nvlist_add_string");
864 				return (IPQOS_CONF_ERR);
865 			}
866 		}
867 
868 		/* add filter instance if present */
869 
870 		if (nvlist_add_int32(nvl, IPGPC_FILTER_INSTANCE,
871 		    flt->instance) != 0) {
872 			ipqos_msg(MT_ENOSTR, "nvlist_add_int32");
873 			return (IPQOS_CONF_ERR);
874 		}
875 	}
876 
877 	if (ipp_action_modify(action_name, &flt->nvlist, 0) != 0) {
878 
879 		/* invalid parameters */
880 
881 		if (errno == EINVAL) {
882 			ipqos_msg(MT_ERROR,
883 			    gettext("Invalid/missing parameters for filter "
884 			    "%s in action %s.\n"), flt->name, action_name);
885 
886 		/* max ipgpc filters/classes */
887 
888 		} else if (errno == ENOSPC &&
889 		    strcmp(action_name, IPGPC_CLASSIFY) == 0) {
890 			ipqos_msg(MT_ERROR, gettext("Max number of filters "
891 			    "reached in action %s.\n"), IPGPC_NAME);
892 
893 		/* anything other errnos */
894 		} else {
895 			ipqos_msg(MT_ERROR,
896 			    gettext("Failed to create filter %s in action "
897 			    "%s: %s.\n"), flt->name, action_name,
898 			    strerror(errno));
899 		}
900 
901 		return (IPQOS_CONF_ERR);
902 	}
903 
904 	return (IPQOS_CONF_SUCCESS);
905 }
906 
907 
908 /*
909  * modify the filter flt in the kernel action named action_name.
910  * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCES.
911  */
912 static int
913 modify_filter(
914 char *action_name,
915 ipqos_conf_filter_t *flt,
916 int module_version)
917 {
918 
919 	nvlist_t *nvl = flt->nvlist;
920 	char ipvsbuf[IPQOS_INT_STR_LEN];
921 
922 	IPQOSCDBG4(APPLY, "modify_filter: action: %s, filter: %s, "
923 	    "instance: %d, class: %s\n", action_name, flt->name,
924 	    flt->instance, flt->class_name);
925 
926 /* show src address and dst address if present */
927 #ifdef	_IPQOS_CONF_DEBUG
928 	if (ipqosconf_dbg_flgs & APPLY) {
929 		uint_t tmp;
930 		in6_addr_t *add;
931 		char st[100];
932 
933 		if (nvlist_lookup_uint32_array(nvl, IPGPC_SADDR,
934 		    (uint32_t **)&add, &tmp) == 0) {
935 			(void) fprintf(stderr, "saddr: %s\n",
936 			    inet_ntop(AF_INET6, add, st, 100));
937 		}
938 
939 		if (nvlist_lookup_uint32_array(nvl, IPGPC_DADDR,
940 		    (uint32_t **)&add, &tmp) == 0) {
941 			(void) fprintf(stderr, "daddr: %s\n",
942 			    inet_ntop(AF_INET6, add, st, 100));
943 		}
944 	}
945 #endif	/* _IPQOS_CONF_DEBUG */
946 
947 	/* add 'modify filter' config type to filters nvlist */
948 	if (nvlist_add_byte(nvl, IPP_CONFIG_TYPE,
949 	    CLASSIFIER_MODIFY_FILTER) != 0) {
950 		ipqos_msg(MT_ENOSTR, "nvlist_add_byte");
951 		return (IPQOS_CONF_ERR);
952 	}
953 
954 	/*
955 	 * add module version
956 	 */
957 	if (nvlist_add_uint32(nvl, IPP_MODULE_VERSION,
958 	    (uint32_t)module_version) != 0) {
959 		ipqos_msg(MT_ENOSTR, "nvlist_add_uint32");
960 		return (IPQOS_CONF_ERR);
961 	}
962 
963 	/* add filter name to nvlist */
964 	if (nvlist_add_string(nvl, CLASSIFIER_FILTER_NAME, flt->name) != 0) {
965 		ipqos_msg(MT_ENOSTR, "nvlist_add_string");
966 		return (IPQOS_CONF_ERR);
967 	}
968 
969 	/* add class name to nvlist */
970 	if (nvlist_add_string(nvl, CLASSIFIER_CLASS_NAME, flt->class_name) !=
971 	    0) {
972 		ipqos_msg(MT_ENOSTR, "nvlist_add_string");
973 		return (IPQOS_CONF_ERR);
974 	}
975 
976 	/* add originator ipqosconf to nvlist */
977 	if (add_orig_ipqosconf(nvl) != IPQOS_CONF_SUCCESS) {
978 		return (IPQOS_CONF_ERR);
979 	}
980 
981 	/* add ipgpc specific nvpairs */
982 	if (strcmp(action_name, IPGPC_CLASSIFY) == 0) {
983 
984 		/* add src and dst nodes to nvlist if present */
985 
986 		if (flt->src_nd_name &&
987 		    nvlist_add_string(nvl, IPGPC_SADDR_HOSTNAME,
988 		    flt->src_nd_name) != 0) {
989 			ipqos_msg(MT_ENOSTR, "nvlist_add_string");
990 			return (IPQOS_CONF_ERR);
991 		}
992 		if (flt->dst_nd_name &&
993 		    nvlist_add_string(nvl, IPGPC_DADDR_HOSTNAME,
994 		    flt->dst_nd_name) != 0) {
995 			ipqos_msg(MT_ENOSTR, "nvlist_add_string");
996 			return (IPQOS_CONF_ERR);
997 		}
998 
999 		/*
1000 		 * add ip_version to private list element if present.
1001 		 * NOTE: this value is of only real use to ipqosconf so
1002 		 * it is placed in this opaque private field.
1003 		 */
1004 		if (flt->ip_versions != 0) {
1005 			(void) sprintf(ipvsbuf, "%d", flt->ip_versions);
1006 			if (nvlist_add_string(nvl, IPGPC_FILTER_PRIVATE,
1007 			    ipvsbuf) != 0) {
1008 				ipqos_msg(MT_ENOSTR, "nvlist_add_string");
1009 				return (IPQOS_CONF_ERR);
1010 			}
1011 		}
1012 
1013 		/* add filter instance if present */
1014 
1015 		if (nvlist_add_int32(nvl, IPGPC_FILTER_INSTANCE,
1016 		    flt->instance) != 0) {
1017 			ipqos_msg(MT_ENOSTR, "nvlist_add_int32");
1018 			return (IPQOS_CONF_ERR);
1019 		}
1020 	}
1021 
1022 	if (ipp_action_modify(action_name, &flt->nvlist, 0) != 0) {
1023 
1024 		/* invalid parameters */
1025 
1026 		if (errno == EINVAL) {
1027 			ipqos_msg(MT_ERROR, gettext("Missing/Invalid "
1028 			    "parameter for filter %s in action %s.\n"),
1029 			    flt->name, action_name);
1030 
1031 		/* any other errnos */
1032 
1033 		} else {
1034 			ipqos_msg(MT_ERROR,
1035 			    gettext("Failed to modify filter %s in action %s: "
1036 			    "%s.\n"), flt->name, action_name, strerror(errno));
1037 		}
1038 
1039 		return (IPQOS_CONF_ERR);
1040 	}
1041 
1042 	return (IPQOS_CONF_SUCCESS);
1043 }
1044 
1045 /*
1046  * remove the filter named filter_name instance number instance from the
1047  * kernel action action_name.
1048  * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCES.
1049  */
1050 static int
1051 remove_filter(
1052 char *action_name,
1053 char *filter_name,
1054 int instance,
1055 int module_version)
1056 {
1057 
1058 	nvlist_t *nvl;
1059 
1060 	IPQOSCDBG2(APPLY, "remove_filter: action: %s, filter: %s\n",
1061 	    action_name, filter_name);
1062 
1063 	/* create nvlist */
1064 	if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) {
1065 		ipqos_msg(MT_ENOSTR, "nvlist_alloc");
1066 		return (IPQOS_CONF_ERR);
1067 	}
1068 
1069 	/* add 'remove filter' config type to list */
1070 	if (nvlist_add_byte(nvl, IPP_CONFIG_TYPE, CLASSIFIER_REMOVE_FILTER)
1071 	!= 0) {
1072 		ipqos_msg(MT_ENOSTR, "nvlist_add_byte");
1073 		return (IPQOS_CONF_ERR);
1074 	}
1075 
1076 	/*
1077 	 * add module version
1078 	 */
1079 	if (nvlist_add_uint32(nvl, IPP_MODULE_VERSION,
1080 	    (uint32_t)module_version) != 0) {
1081 		ipqos_msg(MT_ENOSTR, "nvlist_add_uint32");
1082 		return (IPQOS_CONF_ERR);
1083 	}
1084 
1085 	/* add filter name to list */
1086 	if (nvlist_add_string(nvl, CLASSIFIER_FILTER_NAME, filter_name) != 0) {
1087 		ipqos_msg(MT_ENOSTR, "nvlist_add_string");
1088 		return (IPQOS_CONF_ERR);
1089 	}
1090 
1091 	/* add instance number if part of multi-instance filter */
1092 	if (instance != -1 && nvlist_add_int32(nvl, IPGPC_FILTER_INSTANCE,
1093 	    instance) != 0) {
1094 		ipqos_msg(MT_ENOSTR, "nvlist_add_int32");
1095 		return (IPQOS_CONF_ERR);
1096 	}
1097 
1098 	/* call into lib to remove */
1099 	if (ipp_action_modify(action_name, &nvl, 0) != 0) {
1100 
1101 		/* generic error message */
1102 
1103 		ipqos_msg(MT_ERROR,
1104 		    gettext("Removing filter %s in action %s failed: %s.\n"),
1105 		    filter_name, action_name, strerror(errno));
1106 
1107 		return (IPQOS_CONF_ERR);
1108 	}
1109 
1110 	return (IPQOS_CONF_SUCCESS);
1111 }
1112 
1113 /* ******************************************************************* */
1114 
1115 
1116 /*
1117  * add originator nvpair set to ipqosconf to nvl.
1118  * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCESS.
1119  */
1120 static int
1121 add_orig_ipqosconf(nvlist_t *nvl)
1122 {
1123 
1124 	if (nvlist_add_uint32(nvl, IPP_CONFIG_ORIGINATOR,
1125 	    IPP_CONFIG_IPQOSCONF) != 0) {
1126 		ipqos_msg(MT_ENOSTR, "nvlist_add_uint32: originator:");
1127 		return (IPQOS_CONF_ERR);
1128 	}
1129 
1130 	return (IPQOS_CONF_SUCCESS);
1131 }
1132 
1133 /* ************************* differencing functions ************************ */
1134 
1135 
1136 /*
1137  * compares the contents of arrays array1 and array2, both of size size, and
1138  * returns B_TRUE or B_FALSE if they're equal or not respectively.
1139  * RETURNS: B_TRUE if equal, else B_FALSE.
1140  */
1141 static boolean_t
1142 arrays_equal(
1143 int array1[],
1144 int array2[],
1145 uint32_t size)
1146 {
1147 	int x;
1148 
1149 	for (x = 0; x < size; x++) {
1150 		if (array1[x] != array2[x])
1151 			return (B_FALSE);
1152 	}
1153 	return (B_TRUE);
1154 }
1155 
1156 /*
1157  * difference class old against class new. It marks the new class as
1158  * modified if it is different.
1159  * RETURNS: IPQOS_CONF_SUCCESS.
1160  */
1161 static int
1162 diffclass(
1163 ipqos_conf_class_t *old,
1164 ipqos_conf_class_t *new)
1165 {
1166 
1167 	IPQOSCDBG0(L0, "In diffclass:\n");
1168 
1169 	/* two different spec'd actions */
1170 	if (strcmp(old->alist->name, new->alist->name) != 0) {
1171 		IPQOSCDBG1(DIFF, "marking class %s as modified\n", new->name);
1172 
1173 		new->modified = B_TRUE;
1174 		return (IPQOS_CONF_SUCCESS);
1175 	}
1176 
1177 	/* different stats values */
1178 	if (old->stats_enable != new->stats_enable) {
1179 		IPQOSCDBG1(DIFF, "marking class %s as modified\n", new->name);
1180 
1181 		new->modified = B_TRUE;
1182 		return (IPQOS_CONF_SUCCESS);
1183 	}
1184 
1185 	return (IPQOS_CONF_SUCCESS);
1186 }
1187 
1188 /*
1189  * difference params set old against params set new of module module_name. It
1190  * marks the new params as modified if different.
1191  * RETURNS: if error IPQOS_CONF_ERR, else IPQOS_CONF_SUCCESS.
1192  */
1193 static int
1194 diffparams(
1195 ipqos_conf_params_t *old,
1196 ipqos_conf_params_t *new,
1197 char *module_name)
1198 {
1199 
1200 	int diff;
1201 	int res;
1202 
1203 	IPQOSCDBG0(L0, "In diffparams\n");
1204 
1205 	/* diff stats */
1206 	if (old->stats_enable != new->stats_enable) {
1207 
1208 		new->modified = B_TRUE;
1209 		return (IPQOS_CONF_SUCCESS);
1210 	}
1211 
1212 	/* diff module specific params */
1213 	res = diffnvlists(old->nvlist, new->nvlist, module_name, &diff,
1214 	    PL_PARAMS);
1215 	if (res != IPQOS_CONF_SUCCESS) {
1216 		return (res);
1217 	}
1218 	if (diff) {
1219 
1220 		new->modified = B_TRUE;
1221 	}
1222 
1223 	return (IPQOS_CONF_SUCCESS);
1224 }
1225 
1226 /*
1227  * differences filter old against filter new of module module_name. It marks
1228  * filter new as different if so.
1229  * RETURNS: if error IPQOS_CONF_ERR, else IPQOS_CONF_SUCCESS.
1230  */
1231 static int
1232 difffilter(
1233 ipqos_conf_filter_t *old,
1234 ipqos_conf_filter_t *new,
1235 char *module_name)
1236 {
1237 
1238 	int res;
1239 	int diff;
1240 
1241 	IPQOSCDBG0(L0, "In difffilter\n");
1242 
1243 	/* compare class name */
1244 
1245 	if (strcmp(old->class_name, new->class_name) != 0) {
1246 		IPQOSCDBG1(DIFF, "Marking filter %s as modified\n", new->name);
1247 
1248 		new->modified = B_TRUE;
1249 		return (IPQOS_CONF_SUCCESS);
1250 	}
1251 
1252 	/* compare module specific params */
1253 
1254 	res = diffnvlists(old->nvlist, new->nvlist, module_name, &diff,
1255 	    PL_FILTER);
1256 	if (res != IPQOS_CONF_SUCCESS) {
1257 		return (res);
1258 	}
1259 
1260 	if (diff) {
1261 		IPQOSCDBG1(DIFF, "Marking filter %s as modified\n", new->name);
1262 		new->modified = B_TRUE;
1263 	}
1264 
1265 	return (IPQOS_CONF_SUCCESS);
1266 }
1267 
1268 
1269 /*
1270  * mark all the filters and classes in parameter action either
1271  * for deletion (if they are ipqosconf originated) or for modification.
1272  */
1273 static void
1274 mark_classes_filters_del(ipqos_conf_action_t *action)
1275 {
1276 
1277 	ipqos_conf_filter_t *flt;
1278 	ipqos_conf_class_t *cls;
1279 
1280 	IPQOSCDBG1(L1, "In mark_classes_filters_del: action: %s\n",
1281 	    action->name);
1282 
1283 	/* mark all non-permanent filters for del and permanent to modify */
1284 	for (flt = action->filters; flt; flt = flt->next) {
1285 		if (flt->originator == IPP_CONFIG_PERMANENT) {
1286 			IPQOSCDBG1(DIFF, "Marking prm filter %s as modified.\n",
1287 			    flt->name);
1288 
1289 			flt->modified = B_TRUE;
1290 		} else {
1291 			IPQOSCDBG1(DIFF, "Marking filter %s as del.\n",
1292 			    flt->name);
1293 
1294 			flt->todel = B_TRUE;
1295 		}
1296 	}
1297 
1298 	/* mark all non-permanent classes for del and permanent to modify */
1299 	for (cls = action->classes; cls; cls = cls->next) {
1300 		if (cls->originator == IPP_CONFIG_PERMANENT) {
1301 			IPQOSCDBG1(DIFF, "Marking prm class %s as modified.\n",
1302 			    cls->name);
1303 
1304 			cls->modified = B_TRUE;
1305 		} else {
1306 			IPQOSCDBG1(DIFF, "Marking class %s as del.\n",
1307 			    cls->name);
1308 
1309 			cls->todel = B_TRUE;
1310 		}
1311 	}
1312 }
1313 
1314 /*
1315  * mark all classes and filters either new (non-permanent) or modified.
1316  */
1317 static void
1318 mark_classes_filters_new(ipqos_conf_action_t *action)
1319 {
1320 
1321 	ipqos_conf_filter_t *flt;
1322 	ipqos_conf_class_t *cls;
1323 
1324 	IPQOSCDBG1(L1, "In mark_classes_filters_new: action: %s\n",
1325 	    action->name);
1326 
1327 	/* mark all permanent filters as modified and all others new */
1328 
1329 	for (flt = action->filters; flt; flt = flt->next) {
1330 		if (flt->originator == IPP_CONFIG_PERMANENT) {
1331 			IPQOSCDBG1(DIFF, "Marking prm filter %s as modified.\n",
1332 			    flt->name);
1333 
1334 			flt->modified = B_TRUE;
1335 			action->modified = B_TRUE;
1336 		} else {
1337 			IPQOSCDBG1(DIFF, "Marking filter %s as new.\n",
1338 			    flt->name);
1339 
1340 			flt->new = B_TRUE;
1341 		}
1342 	}
1343 
1344 	/* mark all permanent classes as modified and all others new */
1345 	for (cls = action->classes; cls; cls = cls->next) {
1346 		if (cls->originator == IPP_CONFIG_PERMANENT) {
1347 			IPQOSCDBG1(DIFF, "Marking prm class %s as modified.\n",
1348 			    cls->name);
1349 
1350 			cls->modified = B_TRUE;
1351 			action->modified = B_TRUE;
1352 		} else {
1353 			IPQOSCDBG1(DIFF, "Marking class %s as new.\n",
1354 			    cls->name);
1355 
1356 			cls->new = B_TRUE;
1357 		}
1358 	}
1359 }
1360 
1361 /*
1362  * Marks all the actions and their constituent elements in conf
1363  * as new.
1364  */
1365 static void
1366 mark_config_new(
1367 ipqos_conf_action_t *conf)
1368 {
1369 	while (conf != NULL) {
1370 		IPQOSCDBG1(DIFF, "Marking action %s as new\n", conf->name);
1371 		mark_classes_filters_new(conf);
1372 		conf->new = B_TRUE;
1373 		conf->visited = 0;
1374 		conf = conf->next;
1375 	}
1376 }
1377 
1378 /*
1379  * differences the configuration  in new against old marking the actions
1380  * and their contents appropriately.
1381  * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCESS.
1382  */
1383 static int
1384 diffconf(
1385 ipqos_conf_action_t *old,
1386 ipqos_conf_action_t *new)
1387 {
1388 
1389 	int res;
1390 	ipqos_conf_action_t *act;
1391 	ipqos_conf_action_t *tmp;
1392 
1393 	IPQOSCDBG0((L0 | DIFF), "In diffconf\n");
1394 
1395 	/* check the new actions against the old */
1396 
1397 	for (act = new; act; act = act->next) {
1398 
1399 		/* if action not in old mark it and it's contents as new */
1400 
1401 		if ((tmp = actionexist(act->name, old)) == NULL) {
1402 			IPQOSCDBG1(DIFF, "marking act %s as new\n", act->name);
1403 
1404 			act->new = B_TRUE;
1405 			mark_classes_filters_new(act);
1406 			continue;
1407 		}
1408 
1409 		/* if action in old diff old against new */
1410 
1411 		res = diffaction(tmp, act);
1412 		if (res != IPQOS_CONF_SUCCESS) {
1413 			return (res);
1414 		}
1415 	}
1416 
1417 	/*
1418 	 * mark actions, and their contents, in old but not new that were
1419 	 * created by us for del.
1420 	 */
1421 
1422 	for (act = old; act; act = act->next) {
1423 		if (act->params->originator == IPP_CONFIG_IPQOSCONF &&
1424 		    actionexist(act->name, new) == NULL) {
1425 			IPQOSCDBG1(DIFF, "marking act %s for del\n", act->name);
1426 
1427 			act->todel = B_TRUE;
1428 			mark_classes_filters_del(act);
1429 		}
1430 	}
1431 
1432 	return (IPQOS_CONF_SUCCESS);
1433 }
1434 
1435 /*
1436  * differences action old against action new, comparing its classes, filters
1437  * and parameters. If it is different the new action is marked as modified
1438  * and it's different sub-objects are also marked approriately.
1439  * RETURNS: IPQOS_CONF_ERR if error, else IPQOS_CONF_SUCCESS.
1440  */
1441 static int
1442 diffaction(
1443 ipqos_conf_action_t *old,
1444 ipqos_conf_action_t *new)
1445 {
1446 
1447 	int res;
1448 
1449 	IPQOSCDBG0(L0, "In diffaction\n");
1450 
1451 	/* compare and mark classes */
1452 	res = diffclasses(old, new);
1453 	if (res != IPQOS_CONF_SUCCESS) {
1454 		return (res);
1455 	}
1456 
1457 	/* compare and mark filters */
1458 	res = difffilters(old, new);
1459 	if (res != IPQOS_CONF_SUCCESS) {
1460 		return (res);
1461 	}
1462 
1463 	/* compare and mark parameters */
1464 	res = diffparams(old->params, new->params, old->module);
1465 	if (res != IPQOS_CONF_SUCCESS) {
1466 		return (res);
1467 	}
1468 
1469 	/* mark action as modified if params are */
1470 	if (new->params->modified == B_TRUE) {
1471 		IPQOSCDBG1(DIFF, "Marking params for action %s modified\n",
1472 		    new->name);
1473 
1474 		new->modified = B_TRUE;
1475 	}
1476 
1477 	return (IPQOS_CONF_SUCCESS);
1478 }
1479 
1480 /*
1481  * differences the set of classes in new against those in old, marking any
1482  * that are new/modified, approriately in the new class, and any removed
1483  * in the old class appropriately. Also marks the action which has had an
1484  * object within marked, as modified.
1485  * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCESS.
1486  */
1487 
1488 static int
1489 diffclasses(
1490 ipqos_conf_action_t *old,
1491 ipqos_conf_action_t *new)
1492 {
1493 
1494 
1495 	ipqos_conf_class_t *cls;
1496 	ipqos_conf_class_t *tmpc;
1497 	ipqos_conf_class_t *ncls;
1498 	int res;
1499 
1500 
1501 	/* loop through old classes checking for classes not present in new */
1502 
1503 	for (cls = old->classes; cls; cls = cls->next) {
1504 
1505 		if (classexist(cls->name, new->classes) == NULL) {
1506 
1507 			/* if we created original class mark for deletion */
1508 
1509 			if (cls->originator == IPP_CONFIG_IPQOSCONF) {
1510 				IPQOSCDBG1(DIFF, "marking class %s for del\n",
1511 				    cls->name);
1512 
1513 				cls->todel = B_TRUE;
1514 
1515 				/* mark old action */
1516 				old->modified = B_TRUE;
1517 
1518 			/*
1519 			 * if permanent class and next action created by us
1520 			 * copy it, set it's next action to continue and
1521 			 * add it to new action. This will cause the class
1522 			 * to be marked as and modified. This returns the class
1523 			 * to an assumed default state and prevents the
1524 			 * case where the class is pointing at an action
1525 			 * we want to remove and therefore couldn't without
1526 			 * this forced modify.
1527 			 */
1528 			} else if (cls->originator == IPP_CONFIG_PERMANENT &&
1529 			    cls->alist->action &&	/* not virtual action */
1530 			    cls->alist->action->params->originator ==
1531 			    IPP_CONFIG_IPQOSCONF) {
1532 
1533 				/* copy class */
1534 
1535 				res = dup_class(cls, &ncls);
1536 				if (res != IPQOS_CONF_SUCCESS) {
1537 					return (IPQOS_CONF_ERR);
1538 				}
1539 
1540 				/* set next action to continue */
1541 
1542 				(void) strcpy(ncls->alist->name,
1543 				    IPP_ANAME_CONT);
1544 
1545 				/* add to news classes to be diffed below */
1546 				ADD_TO_LIST(&new->classes, ncls);
1547 			}
1548 		}
1549 	}
1550 
1551 	/* loop through new classes checking for new / modified classes */
1552 
1553 	for (cls = new->classes; cls; cls = cls->next) {
1554 
1555 		/* new ipqosconf class */
1556 
1557 		if ((tmpc = classexist(cls->name, old->classes)) == NULL ||
1558 		    (tmpc->originator != IPP_CONFIG_IPQOSCONF &&
1559 		    tmpc->originator != IPP_CONFIG_PERMANENT)) {
1560 			IPQOSCDBG1(DIFF, "marking class %s new\n",
1561 			    cls->name);
1562 
1563 			cls->new = B_TRUE;
1564 
1565 			new->modified = B_TRUE;	/* mark new action */
1566 			continue;
1567 
1568 		/* existing ipqosconf/perm class */
1569 		} else {
1570 			res = diffclass(tmpc, cls);
1571 			if (res != IPQOS_CONF_SUCCESS) {
1572 				return (res);
1573 			}
1574 
1575 			if (cls->modified == B_TRUE) {
1576 				new->modified = B_TRUE;
1577 			}
1578 		}
1579 	}
1580 
1581 	return (IPQOS_CONF_SUCCESS);
1582 }
1583 
1584 /*
1585  * differences the set of filters in new against those in old, marking any
1586  * that are new/modified, approriately in the new filter/s, and any removed
1587  * in the old filter appropriately. Also marks the action which has had an
1588  * object within marked, as modified.
1589  * RETURNS: IPQOS_CONF_SUCCESS (we return an int for symmetry with diffclasses
1590  * and difffparams).
1591  */
1592 static int
1593 difffilters(
1594 ipqos_conf_action_t *old,
1595 ipqos_conf_action_t *new)
1596 {
1597 
1598 	ipqos_conf_filter_t *flt;
1599 	ipqos_conf_filter_t *tmpf;
1600 	int maxi;
1601 	int newi;
1602 	int res;
1603 
1604 	/* check for new/modified filters */
1605 
1606 	for (flt = new->filters; flt; flt = flt->next) {
1607 
1608 		/* new ipqosconf filter */
1609 
1610 		if ((tmpf = filterexist(flt->name, -1, old->filters)) == NULL) {
1611 
1612 			/* mark all instances of this filter as new */
1613 			for (;;) {
1614 				IPQOSCDBG1(DIFF, "Marking filter %s as "
1615 				    "new\n", flt->name);
1616 
1617 				flt->new = B_TRUE;
1618 
1619 
1620 				if (flt->next == NULL ||
1621 				    strcmp(flt->next->name, flt->name) != 0) {
1622 					break;
1623 				}
1624 				flt = flt->next;
1625 			}
1626 			new->modified = B_TRUE;	/* mark new action */
1627 
1628 		/* ipqosconf/permanent filter existed */
1629 		} else {
1630 			/*
1631 			 * if ip node name force filter refresh - ie. mark
1632 			 * all old filter instances as todel and all new new.
1633 			 */
1634 			if (tmpf->src_nd_name || tmpf->dst_nd_name ||
1635 			    flt->src_nd_name || flt->dst_nd_name) {
1636 
1637 				/* init max previous filter instance */
1638 				maxi = tmpf->instance;
1639 
1640 				/* mark old instances for deletion */
1641 				do {
1642 					IPQOSCDBG2(DIFF, "Marking filter "
1643 					    "%s, instance %d for del\n",
1644 					    tmpf->name, tmpf->instance);
1645 
1646 					tmpf->todel = B_TRUE;
1647 
1648 					/*
1649 					 * check and update previous instance
1650 					 * max.
1651 					 */
1652 					if (tmpf->instance > maxi) {
1653 						maxi = tmpf->instance;
1654 					}
1655 
1656 					tmpf = tmpf->next;
1657 				} while (tmpf != NULL &&
1658 					strcmp(tmpf->name, flt->name) == 0);
1659 
1660 				/*
1661 				 * use the max previous instance + 1 for
1662 				 * the start of the new instance numbers.
1663 				 */
1664 				newi = (uint32_t)++maxi % INT_MAX;
1665 
1666 				/*
1667 				 * mark new instances for addition and
1668 				 * give new instance number.
1669 				 */
1670 				for (;;) {
1671 					IPQOSCDBG2(DIFF, "Marking filter "
1672 					    "%s, instance %d as new\n",
1673 					    flt->name, newi);
1674 
1675 					flt->new = B_TRUE;
1676 					flt->instance = newi++;
1677 					if (flt->next == NULL ||
1678 					    strcmp(flt->next->name,
1679 					    flt->name) != 0) {
1680 						break;
1681 					}
1682 					flt = flt->next;
1683 				}
1684 				new->modified = B_TRUE; /* mark new action */
1685 
1686 				/* mark old action */
1687 				old->modified = B_TRUE;
1688 
1689 			/* non-node name filter */
1690 			} else {
1691 				/* compare and mark as modified if diff */
1692 
1693 				res = difffilter(tmpf, flt, new->module);
1694 				if (res != IPQOS_CONF_SUCCESS) {
1695 					return (res);
1696 				}
1697 				if (flt->modified == B_TRUE) {
1698 					/* mark action if diff */
1699 					new->modified = B_TRUE;
1700 				}
1701 			}
1702 		}
1703 	}
1704 
1705 	/*
1706 	 * Check for deleted ipqosconf created filters and mark
1707 	 * any found for deletion.
1708 	 * For non-ipqosconf generated filters, including permanent
1709 	 * ones (none of these exist at the moment) we just leave
1710 	 * the filter unmarked.
1711 	 */
1712 	for (flt = old->filters; flt; flt = flt->next) {
1713 
1714 		if (flt->originator == IPP_CONFIG_IPQOSCONF &&
1715 		    filterexist(flt->name, -1, new->filters) == NULL) {
1716 
1717 			/* mark all old instances for deletions */
1718 			for (;;) {
1719 				IPQOSCDBG2(DIFF, "marking flt %s, inst %d "
1720 				    "for del\n", flt->name, flt->instance);
1721 
1722 				flt->todel = B_TRUE;
1723 				old->modified = B_TRUE; /* mark old action */
1724 
1725 				if (flt->next == NULL ||
1726 				    strcmp(flt->next->name, flt->name) != 0) {
1727 					break;
1728 				}
1729 				flt = flt->next;
1730 			}
1731 		}
1732 	}
1733 
1734 	return (IPQOS_CONF_SUCCESS);
1735 }
1736 
1737 
1738 /*
1739  * differences the elements of nvlists old and new using the types file
1740  * for module name to interpret the element types. It sets pdiff to either
1741  * 0 or 1 if they are the same or different respectively.
1742  * RETURNS: IPQOS_CONF_ERR if any errors, else IPQOS_CONF_SUCCESS.
1743  */
1744 static int
1745 diffnvlists(
1746 nvlist_t *old,
1747 nvlist_t *new,
1748 char *module_name,
1749 int *pdiff,
1750 place_t place)
1751 {
1752 
1753 	int first_pass = 1;
1754 	nvlist_t *tmp;
1755 	int res;
1756 	nvpair_t *nvp;
1757 	FILE *tfp;
1758 	str_val_nd_t *enum_nvs;
1759 	char dfltst[IPQOS_VALST_MAXLEN+1] = "";
1760 	char *lo;
1761 	ipqos_nvtype_t type;
1762 	char *nme;
1763 	int diff;
1764 	int openerr;
1765 
1766 
1767 	IPQOSCDBG0(L0, "In diffnvlists\n");
1768 
1769 	/* open stream to types file */
1770 
1771 	tfp = validmod(module_name, &openerr);
1772 	if (tfp == NULL) {
1773 		if (openerr) {
1774 			ipqos_msg(MT_ENOSTR, "fopen");
1775 		}
1776 		return (IPQOS_CONF_ERR);
1777 	}
1778 start:
1779 	/*
1780 	 * loop through each of the elements of the new list comparing
1781 	 * it with the old one if present. If the old one isn't present
1782 	 * then it is compared with the default value for that type (if
1783 	 * set). Any time the values are determined to be different
1784 	 * or the default value is to be used but isn't present the diff
1785 	 * param is set to 1 and we return.
1786 	 *
1787 	 * If the loop runs its course then the new and old nvlists are
1788 	 * reversed and the loop is entered for a second time.
1789 	 */
1790 	nvp = nvlist_next_nvpair(new, NULL);
1791 	while (nvp != NULL) {
1792 
1793 		/* get name */
1794 		nme = nvpair_name(nvp);
1795 
1796 		/*
1797 		 * get type.
1798 		 */
1799 		place = PL_ANY;
1800 		res = readtype(tfp, module_name, SHORT_NAME(nme), &type,
1801 		    &enum_nvs, dfltst, B_TRUE, &place);
1802 		if (res != IPQOS_CONF_SUCCESS) {
1803 			return (res);
1804 		}
1805 
1806 		/* init diff to 1 */
1807 		diff = 1;
1808 
1809 		switch (type) {
1810 
1811 		/* interface name */
1812 		case IPQOS_DATA_TYPE_IFINDEX: {
1813 			uint32_t ifidx;
1814 			uint32_t oifidx;
1815 
1816 			/* get new value */
1817 			(void) nvpair_value_uint32(nvp, &ifidx);
1818 
1819 			/* compare against old if present */
1820 
1821 			res = nvlist_lookup_uint32(old, nme, &oifidx);
1822 			if (res == 0) {
1823 				/* diff values */
1824 				diff = (ifidx != oifidx);
1825 
1826 			/* not in old so see if new value is default */
1827 
1828 			} else {
1829 				diff = (ifidx != 0);
1830 			}
1831 			break;
1832 		}
1833 		/* protocol */
1834 		case IPQOS_DATA_TYPE_PROTO: {
1835 			uchar_t proto;
1836 			uchar_t oproto;
1837 
1838 			(void) nvpair_value_byte(nvp, &proto);
1839 
1840 			res = nvlist_lookup_byte(old, nme, &oproto);
1841 			if (res == 0) {
1842 				diff = (proto != oproto);
1843 			} else {
1844 				diff = (proto != 0);
1845 			}
1846 			break;
1847 		}
1848 		/* port */
1849 		case IPQOS_DATA_TYPE_PORT: {
1850 			uint16_t port;
1851 			uint16_t oport;
1852 
1853 			(void) nvpair_value_uint16(nvp, &port);
1854 			res = nvlist_lookup_uint16(old, nme, &oport);
1855 			if (res == 0) {
1856 				diff = (port != oport);
1857 			} else {
1858 				diff = (port != 0);
1859 			}
1860 			break;
1861 		}
1862 		/* action name / string */
1863 		case IPQOS_DATA_TYPE_ACTION:
1864 		case IPQOS_DATA_TYPE_STRING: {
1865 			char *str;
1866 			char *ostr;
1867 
1868 			(void) nvpair_value_string(nvp, &str);
1869 			res = nvlist_lookup_string(old, nme, &ostr);
1870 			if (res == 0) {
1871 				diff = strcmp(str, ostr);
1872 			} else if (*dfltst) {
1873 				diff = strcmp(str, dfltst);
1874 			}
1875 			break;
1876 		}
1877 		/* address mask / address */
1878 		case IPQOS_DATA_TYPE_ADDRESS_MASK:
1879 		case IPQOS_DATA_TYPE_ADDRESS: {
1880 			in6_addr_t *in6;
1881 			in6_addr_t *oin6;
1882 			uint_t x;
1883 
1884 			/*
1885 			 * all addresses are stored as v6 addresses, so
1886 			 * a uint32_t[4] array is used.
1887 			 */
1888 
1889 			/* lookup new value */
1890 
1891 			(void) nvpair_value_uint32_array(nvp,
1892 			    (uint32_t **)&in6, &x);
1893 
1894 			/* see if there's an old value and diff it */
1895 
1896 			res = nvlist_lookup_uint32_array(old, nme,
1897 			    (uint32_t **)&oin6, &x);
1898 			if (res == 0) {
1899 				/* diff each of the 16 v6 address bytes */
1900 
1901 				for (x = 0; x < 16; x++) {
1902 					if (in6->s6_addr[x] !=
1903 					    oin6->s6_addr[x]) {
1904 						diff++;
1905 						break;
1906 					}
1907 				}
1908 			}
1909 			break;
1910 		}
1911 		/* boolean */
1912 		case IPQOS_DATA_TYPE_BOOLEAN: {
1913 			boolean_t bl;
1914 			boolean_t obl;
1915 
1916 			(void) nvpair_value_uint32(nvp, (uint32_t *)&bl);
1917 
1918 			/* see if there's an old value and diff it */
1919 			res = nvlist_lookup_uint32(old, nme, (uint32_t *)&obl);
1920 			if (res == 0) {
1921 				diff = (bl != obl);
1922 
1923 			/* compare against default if present */
1924 			} else if (*dfltst) {
1925 				res = readbool(dfltst, &obl);
1926 				if (res == IPQOS_CONF_SUCCESS) {
1927 					diff = (bl != obl);
1928 				}
1929 			}
1930 			break;
1931 		}
1932 		/* uint 8 */
1933 		case IPQOS_DATA_TYPE_UINT8: {
1934 			uint8_t u8;
1935 			uint8_t ou8;
1936 
1937 			(void) nvpair_value_byte(nvp, (uchar_t *)&u8);
1938 			res = nvlist_lookup_byte(old, nme, (uchar_t *)&ou8);
1939 			if (res == 0) {
1940 				diff = (u8 != ou8);
1941 			} else if (*dfltst) {
1942 				res = readuint8(dfltst, &ou8, &lo);
1943 				if (res == IPQOS_CONF_SUCCESS) {
1944 					diff = (u8 != ou8);
1945 				}
1946 			}
1947 			break;
1948 		}
1949 		/* int 16 */
1950 		case IPQOS_DATA_TYPE_INT16: {
1951 			int16_t i16;
1952 			int16_t oi16;
1953 
1954 			(void) nvpair_value_int16(nvp, &i16);
1955 			res = nvlist_lookup_int16(old, nme, &oi16);
1956 			if (res == 0) {
1957 				diff = (i16 != oi16);
1958 			} else if (*dfltst) {
1959 				res = readint16(dfltst, &oi16, &lo);
1960 				if (res == IPQOS_CONF_SUCCESS) {
1961 					diff = (i16 != oi16);
1962 				}
1963 			}
1964 			break;
1965 		}
1966 		/* uint16 */
1967 		case IPQOS_DATA_TYPE_UINT16: {
1968 			uint16_t ui16;
1969 			uint16_t oui16;
1970 
1971 			(void) nvpair_value_uint16(nvp, &ui16);
1972 			res = nvlist_lookup_uint16(old, nme, &oui16);
1973 			if (res == 0) {
1974 				diff = (ui16 != oui16);
1975 			} else if (*dfltst) {
1976 				res = readuint16(dfltst, &oui16, &lo);
1977 				if (res == IPQOS_CONF_SUCCESS) {
1978 					diff = (ui16 != oui16);
1979 				}
1980 			}
1981 			break;
1982 		}
1983 		/*
1984 		 * int32 and user.
1985 		 * Since user uids are stored in an int32 nvpair we can use
1986 		 * the same comparison code.
1987 		 */
1988 		case IPQOS_DATA_TYPE_USER:
1989 		case IPQOS_DATA_TYPE_INT32: {
1990 			int32_t i32;
1991 			int32_t oi32;
1992 
1993 			(void) nvpair_value_int32(nvp, &i32);
1994 			res = nvlist_lookup_int32(old, nme, &oi32);
1995 			if (res == 0) {
1996 				diff = (i32 != oi32);
1997 			} else if (*dfltst) {
1998 				res = readint32(dfltst, &oi32, &lo);
1999 				if (res == IPQOS_CONF_SUCCESS) {
2000 					diff = (i32 != oi32);
2001 				}
2002 			}
2003 			break;
2004 		}
2005 		/* uint32 */
2006 		case IPQOS_DATA_TYPE_UINT32: {
2007 			uint32_t ui32;
2008 			uint32_t oui32;
2009 
2010 			(void) nvpair_value_uint32(nvp, &ui32);
2011 			res = nvlist_lookup_uint32(old, nme, &oui32);
2012 			if (res == 0) {
2013 				diff = (ui32 != oui32);
2014 			} else if (*dfltst) {
2015 				res = readuint32(dfltst, &oui32, &lo);
2016 				if (res == IPQOS_CONF_SUCCESS) {
2017 					diff = (ui32 != oui32);
2018 				}
2019 			}
2020 			break;
2021 		}
2022 		/* enumeration */
2023 		case IPQOS_DATA_TYPE_ENUM: {
2024 			uint32_t eval;
2025 			uint32_t oeval;
2026 
2027 			(void) nvpair_value_uint32(nvp, &eval);
2028 			res = nvlist_lookup_uint32(old, nme, &oeval);
2029 			if (res == 0) {
2030 				diff = (eval != oeval);
2031 			} else if (*dfltst) {
2032 				res = readuint32(dfltst, &oeval, &lo);
2033 				if (res == IPQOS_CONF_SUCCESS) {
2034 					diff = (eval != oeval);
2035 				}
2036 			}
2037 			break;
2038 		}
2039 		case IPQOS_DATA_TYPE_M_INDEX: {
2040 			uint8_t idx, oidx;
2041 
2042 			(void) nvpair_value_byte(nvp, &idx);
2043 			res = nvlist_lookup_byte(old, nme, &oidx);
2044 			if (res == 0)
2045 				diff = (idx != oidx);
2046 			break;
2047 		}
2048 		case IPQOS_DATA_TYPE_INT_ARRAY: {
2049 			int *oarr, *arr;
2050 			uint32_t osize, size;
2051 
2052 			(void) nvpair_value_int32_array(nvp, &arr, &size);
2053 			res = nvlist_lookup_int32_array(old, nme, &oarr,
2054 			    &osize);
2055 			if (res == 0)
2056 				diff = (arrays_equal(arr, oarr, size) ==
2057 				    B_FALSE);
2058 			break;
2059 		}
2060 #ifdef	_IPQOS_CONF_DEBUG
2061 		default: {
2062 			/* shouldn't get here as all types should be covered */
2063 			assert(1);
2064 		}
2065 #endif
2066 		}	/* switch */
2067 		if (diff != 0) {
2068 			IPQOSCDBG1(DIFF, "parameter %s different\n", nme);
2069 			*pdiff = 1;
2070 			(void) fclose(tfp);
2071 			return (IPQOS_CONF_SUCCESS);
2072 		}
2073 
2074 
2075 		nvp = nvlist_next_nvpair(new, nvp);
2076 
2077 	}
2078 
2079 	/* now compare all the stuff in the second list with the first */
2080 	if (first_pass) {
2081 		tmp = old;
2082 		old = new;
2083 		new = tmp;
2084 		first_pass = 0;
2085 		goto start;
2086 	}
2087 
2088 	(void) fclose(tfp);
2089 
2090 	*pdiff = 0;
2091 	return (IPQOS_CONF_SUCCESS);
2092 }
2093 
2094 
2095 
2096 /* ************************** difference application *********************** */
2097 
2098 
2099 
2100 /*
2101  * causes all items marked as requiring change in actions and old_actions
2102  * to have the change applied.
2103  * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCESS.
2104  */
2105 static int
2106 applydiff(
2107 ipqos_conf_action_t *actions,
2108 ipqos_conf_action_t *old_actions)
2109 {
2110 
2111 	int res;
2112 
2113 	IPQOSCDBG0(L1, "In applydiff:\n");
2114 
2115 
2116 	/* add each item marked as new */
2117 
2118 	res = add_items(actions, B_FALSE);
2119 	if (res != IPQOS_CONF_SUCCESS) {
2120 		return (res);
2121 	}
2122 
2123 	/* modify items marked for modification */
2124 
2125 	res = modify_items(actions);
2126 	if (res != IPQOS_CONF_SUCCESS) {
2127 		return (res);
2128 	}
2129 
2130 	/* delete items marked for deletion */
2131 
2132 	res = remove_items(old_actions, B_FALSE);
2133 	if (res != IPQOS_CONF_SUCCESS) {
2134 		return (res);
2135 	}
2136 
2137 	return (IPQOS_CONF_SUCCESS);
2138 }
2139 
2140 static int
2141 add_items(
2142 ipqos_conf_action_t *actions,
2143 boolean_t rem_undo)
2144 {
2145 
2146 	int res;
2147 	ipqos_conf_action_t *act;
2148 
2149 	IPQOSCDBG1(L1, "In add_items, rem_undo: %u\n", rem_undo);
2150 
2151 	/*
2152 	 * we need to create ipgpc action before any others as some actions
2153 	 * such as ftpcl which make calls to it depend on it being there on
2154 	 * their creation.
2155 	 */
2156 	act = actionexist(IPGPC_CLASSIFY, actions);
2157 	if (act &&
2158 	    (rem_undo == B_FALSE && act->new == B_TRUE ||
2159 	    rem_undo == B_TRUE && act->deleted == B_TRUE)) {
2160 
2161 		res = add_action(act);
2162 		if (res != IPQOS_CONF_SUCCESS) {
2163 			return (res);
2164 		}
2165 	}
2166 
2167 	/*
2168 	 * loop though action list and add any actions marked as
2169 	 * new/modified action and apply any additions there, then return.
2170 	 */
2171 
2172 	for (act = actions; act; act = act->next) {
2173 		res = add_item(act, rem_undo);
2174 		if (res != IPQOS_CONF_SUCCESS) {
2175 			return (IPQOS_CONF_ERR);
2176 		}
2177 	}
2178 
2179 	return (IPQOS_CONF_SUCCESS);
2180 }
2181 
2182 
2183 /*
2184  *
2185  * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCESS.
2186  */
2187 static int
2188 add_item(
2189 ipqos_conf_action_t *actions,
2190 boolean_t rem_undo)
2191 {
2192 
2193 	ipqos_conf_action_t *act = actions;
2194 	int res;
2195 	ipqos_conf_class_t *cls;
2196 	ipqos_conf_act_ref_t *pact;
2197 
2198 	IPQOSCDBG2(L1, "In add_item: action: %s, rem_undo: %u\n",
2199 	    actions->name, rem_undo);
2200 
2201 	/* if already visited return immediately */
2202 
2203 	if (act->visited == ADD_VISITED) {
2204 		IPQOSCDBG0(L1, "Early exit due to visited\n");
2205 		return (IPQOS_CONF_SUCCESS);
2206 	}
2207 	act->visited = ADD_VISITED;
2208 
2209 
2210 	/* recurse to last action in tree */
2211 
2212 	for (cls = act->classes; cls; cls = cls->next) {
2213 
2214 		/* if not virtual action */
2215 
2216 		if (cls->alist->action) {
2217 			res = add_item(cls->alist->action, rem_undo);
2218 			if (res != IPQOS_CONF_SUCCESS) {
2219 				return (res);
2220 			}
2221 		}
2222 	}
2223 
2224 	for (pact = act->params->actions; pact; pact = pact->next) {
2225 
2226 		/* if not virtual */
2227 
2228 		if (pact->action) {
2229 			res = add_item(pact->action, rem_undo);
2230 			if (res != IPQOS_CONF_SUCCESS) {
2231 				return (res);
2232 			}
2233 		}
2234 	}
2235 
2236 
2237 	/* if action marked as new and not ipgpc, create */
2238 
2239 	if (((rem_undo == B_FALSE && act->new == B_TRUE) ||
2240 	    (rem_undo == B_TRUE && act->deleted == B_TRUE)) &&
2241 	    strcmp(act->name, IPGPC_CLASSIFY) != 0) {
2242 		res = add_action(act);
2243 		if (res != IPQOS_CONF_SUCCESS) {
2244 			return (res);
2245 		}
2246 	}
2247 
2248 	/* add any classes and filters marked as new */
2249 
2250 	if (add_classes(act->classes, act->name, act->module_version,
2251 	    rem_undo) != IPQOS_CONF_SUCCESS ||
2252 	    add_filters(act->filters, act->name, act->module_version,
2253 	    rem_undo) != IPQOS_CONF_SUCCESS) {
2254 		return (IPQOS_CONF_ERR);
2255 	}
2256 
2257 	return (IPQOS_CONF_SUCCESS);
2258 }
2259 
2260 
2261 /*
2262  * Uses the contents of acts params nvlist and adds an originator
2263  * element set to ipqosconf and the stats parameter. This list
2264  * is then used as the parameter to a call to ipp_action_create to create
2265  * this action in the kernel.
2266  * RETURNS: IPQOS_CONF_ERR on err, else IPQOS_CONF_SUCCESS.
2267  */
2268 static int
2269 add_action(ipqos_conf_action_t *act)
2270 {
2271 
2272 	int res;
2273 	nvlist_t **nvl;
2274 
2275 	IPQOSCDBG2(APPLY, "add_action: action: %s, module: %s\n", act->name,
2276 	    act->module);
2277 
2278 	nvl = &act->params->nvlist;
2279 
2280 	/* alloc params nvlist if not already one */
2281 
2282 	if (*nvl == NULL) {
2283 		res = nvlist_alloc(nvl, NV_UNIQUE_NAME, 0);
2284 		if (res != 0) {
2285 			ipqos_msg(MT_ENOSTR, "nvlist_alloc");
2286 			return (IPQOS_CONF_ERR);
2287 		}
2288 	}
2289 
2290 	/*
2291 	 * add module version
2292 	 */
2293 	if (nvlist_add_uint32(*nvl, IPP_MODULE_VERSION,
2294 	    (uint32_t)act->module_version) != 0) {
2295 		ipqos_msg(MT_ENOSTR, "nvlist_add_uint32");
2296 		return (IPQOS_CONF_ERR);
2297 	}
2298 
2299 	/* add action stats */
2300 
2301 	if (nvlist_add_uint32(*nvl, IPP_ACTION_STATS_ENABLE,
2302 	    (uint32_t)act->params->stats_enable) != 0) {
2303 		ipqos_msg(MT_ENOSTR, "nvlist_add_uint32: action stats");
2304 		return (IPQOS_CONF_ERR);
2305 	}
2306 
2307 	/* add ipqosconf originator id */
2308 
2309 	if (add_orig_ipqosconf(*nvl) != IPQOS_CONF_SUCCESS) {
2310 		return (IPQOS_CONF_ERR);
2311 	}
2312 
2313 	/* call into lib to create action */
2314 
2315 	res = ipp_action_create(act->module, act->name, nvl, 0);
2316 	if (res != 0) {
2317 		IPQOSCDBG2(APPLY, "Create action %s, module %s failed\n",
2318 		    act->name, act->module);
2319 
2320 		/* invalid params */
2321 
2322 		if (errno == EINVAL) {
2323 			ipqos_msg(MT_ERROR,
2324 			    gettext("Invalid Parameters for action %s.\n"),
2325 			    act->name);
2326 
2327 		} else if (errno == ENOENT) {
2328 			ipqos_msg(MT_ERROR,
2329 			    gettext("Missing required parameter for action "
2330 			    "%s.\n"), act->name);
2331 
2332 		} else {	/* unexpected error */
2333 			ipqos_msg(MT_ERROR, gettext("Failed to create action "
2334 			    "%s: %s.\n"), act->name, strerror(errno));
2335 		}
2336 
2337 		return (IPQOS_CONF_ERR);
2338 	}
2339 
2340 	/* mark action as created */
2341 	act->cr_mod = B_TRUE;
2342 
2343 	return (IPQOS_CONF_SUCCESS);
2344 }
2345 
2346 /*
2347  * for each of the filters in parameter filters if rem_undo is false and
2348  * the filter is marked as new or if rem_undo is true and the filter is
2349  * marked as deleted then add the filter to the kernel action named by action
2350  * and if successful mark as created.
2351  * RETURNS: IPQOS_CONF_ERR on errors, else IPQOS_CONF_SUCCESS.
2352  */
2353 static int
2354 add_filters(
2355 ipqos_conf_filter_t *filters,
2356 char *action,
2357 int module_version,
2358 boolean_t rem_undo)
2359 {
2360 
2361 	ipqos_conf_filter_t *flt;
2362 
2363 	IPQOSCDBG0(L1, "In add_filters\n");
2364 
2365 	/* loop through filters in filters param */
2366 	for (flt = filters; flt; flt = flt->next) {
2367 		/*
2368 		 * skip filter if in normal mode and not new filter or
2369 		 * if doing rollback and filter wasn't previously deleted.
2370 		 */
2371 		if ((rem_undo == B_FALSE && flt->new == B_FALSE) ||
2372 		    (rem_undo == B_TRUE && flt->deleted == B_FALSE)) {
2373 			continue;
2374 		}
2375 
2376 		/* add filter to action */
2377 		if (add_filter(action, flt, module_version) !=
2378 		    IPQOS_CONF_SUCCESS) {
2379 			return (IPQOS_CONF_ERR);
2380 		}
2381 
2382 		/* mark as created */
2383 		flt->cr_mod = B_TRUE;
2384 	}
2385 
2386 	return (IPQOS_CONF_SUCCESS);
2387 }
2388 
2389 /*
2390  * for each of the classes in parameter classes if rem_undo is false and
2391  * the class is marked as new or if rem_undo is true and the class is
2392  * marked as deleted then add the class to the kernel action named by action
2393  * and if successful mark as created.
2394  * RETURNS: IPQOS_CONF_ERR on errors, else IPQOS_CONF_SUCCESS.
2395  */
2396 int
2397 add_classes(
2398 ipqos_conf_class_t *classes,
2399 char *action,
2400 int module_version,
2401 boolean_t rem_undo) {
2402 
2403 	int res;
2404 	ipqos_conf_class_t *cls;
2405 
2406 	IPQOSCDBG0(L1, "In add_classes\n");
2407 
2408 	/* for each class */
2409 	for (cls = classes; cls; cls = cls->next) {
2410 		/*
2411 		 * skip class if in normal mode and not new class or
2412 		 * if doing rollback and class wasn't deleted.
2413 		 */
2414 		if ((rem_undo == B_FALSE && cls->new == B_FALSE) ||
2415 		(rem_undo == B_TRUE && cls->deleted == B_FALSE)) {
2416 			continue;
2417 		}
2418 
2419 		/* add class to action */
2420 		res = add_class(action, cls->name, module_version,
2421 		    cls->stats_enable, cls->alist->name);
2422 		if (res != IPQOS_CONF_SUCCESS) {
2423 			return (IPQOS_CONF_ERR);
2424 		}
2425 
2426 		/* mark class as created */
2427 		cls->cr_mod = B_TRUE;
2428 	}
2429 
2430 	return (IPQOS_CONF_SUCCESS);
2431 }
2432 
2433 /*
2434  * For each of the actions in actions remove the action if marked as
2435  * such or remove any objects within marked as such.
2436  * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCESS.
2437  */
2438 static int
2439 remove_items(
2440 ipqos_conf_action_t *actions,
2441 boolean_t add_undo)
2442 {
2443 
2444 	int res;
2445 	ipqos_conf_action_t *act;
2446 
2447 	IPQOSCDBG1(L0, "In remove_items, add_undo: %u\n", add_undo);
2448 
2449 	/*
2450 	 * loop through actions removing any actions, or action contents
2451 	 * that are marked as such.
2452 	 */
2453 	for (act = actions; act; act = act->next) {
2454 		res = remove_item(act, add_undo);
2455 		if (res != IPQOS_CONF_SUCCESS) {
2456 			return (res);
2457 		}
2458 	}
2459 
2460 	return (IPQOS_CONF_SUCCESS);
2461 }
2462 
2463 /*
2464  * Deletes this action if marked for deletion or any of it's contents marked
2465  * for deletion. If the action is marked for deletion any actions referencing
2466  * this action are destroyed first if marked or have their contents destroyed
2467  * if marked. This is recursive.
2468  * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCESS.
2469  */
2470 static int
2471 remove_item(
2472 ipqos_conf_action_t *act,
2473 boolean_t add_undo)
2474 {
2475 
2476 	ipqos_conf_class_t *cls;
2477 	ipqos_conf_filter_t *flt;
2478 	ipqos_conf_act_ref_t *dep;
2479 	int res;
2480 
2481 	IPQOSCDBG3(L1, "In remove_item: action: %s, add_undo: %u, mod: %u\n",
2482 	    act->name, add_undo, act->modified);
2483 
2484 
2485 	/* return immmediately if previously visited in remove phase */
2486 
2487 	if (act->visited == REM_VISITED) {
2488 		IPQOSCDBG0(L1, "Exit due to REM_VISITED set\n");
2489 		return (IPQOS_CONF_SUCCESS);
2490 	}
2491 	act->visited = REM_VISITED;
2492 
2493 
2494 	/* if this action is to be deleted */
2495 
2496 	if (add_undo == B_FALSE && act->todel == B_TRUE ||
2497 	    add_undo == B_TRUE && act->new == B_TRUE &&
2498 	    act->cr_mod == B_TRUE) {
2499 
2500 		/* modify parent actions first */
2501 
2502 		for (dep = act->dependencies; dep; dep = dep->next) {
2503 			res = remove_item(dep->action, add_undo);
2504 			if (res != IPQOS_CONF_SUCCESS) {
2505 				return (res);
2506 			}
2507 		}
2508 
2509 		/* delete this action */
2510 
2511 			IPQOSCDBG1(APPLY, "deleting action %s\n", act->name);
2512 		res = ipp_action_destroy(act->name, 0);
2513 		if (res != 0) {
2514 			IPQOSCDBG1(APPLY, "failed to destroy action %s\n",
2515 			    act->name);
2516 			return (IPQOS_CONF_ERR);
2517 		}
2518 
2519 		/* flag as deleted */
2520 
2521 		act->deleted = B_TRUE;
2522 
2523 	/* if modified action */
2524 
2525 	} else if (act->modified == B_TRUE) {
2526 
2527 		/* loop through removing any filters marked for del */
2528 
2529 		for (flt = act->filters; flt; flt = flt->next) {
2530 			if ((add_undo == B_FALSE && flt->todel == B_TRUE) ||
2531 			    (add_undo == B_TRUE && flt->new == B_TRUE &&
2532 			    flt->cr_mod == B_TRUE)) {
2533 
2534 				/* do deletion */
2535 
2536 				res = remove_filter(act->name, flt->name,
2537 				    flt->instance, act->module_version);
2538 				if (res != IPQOS_CONF_SUCCESS) {
2539 					IPQOSCDBG2(APPLY, "failed to destroy "
2540 					    "filter %s, inst: %d\n", flt->name,
2541 					    flt->instance);
2542 
2543 					return (IPQOS_CONF_ERR);
2544 				}
2545 
2546 				/* flag deleted */
2547 
2548 				flt->deleted = B_TRUE;
2549 			}
2550 		}
2551 
2552 		/* remove any classes marked for del */
2553 
2554 		for (cls = act->classes; cls; cls = cls->next) {
2555 			if ((add_undo == B_FALSE && cls->todel == B_TRUE) ||
2556 			    (add_undo == B_TRUE && cls->new == B_TRUE &&
2557 			    cls->cr_mod == B_TRUE)) {
2558 
2559 				/* do deletion */
2560 
2561 				res = remove_class(act->name, cls->name,
2562 				    act->module_version, 0);
2563 				if (res != IPQOS_CONF_SUCCESS) {
2564 					IPQOSCDBG1(APPLY, "failed to destroy "
2565 					    "class %s\n", cls->name);
2566 
2567 					return (IPQOS_CONF_ERR);
2568 				}
2569 
2570 				/* flag deleted */
2571 
2572 				cls->deleted = B_TRUE;
2573 			}
2574 		}
2575 
2576 		/* mark action as having been modified */
2577 
2578 		act->cr_mod = B_TRUE;
2579 	}
2580 
2581 	return (IPQOS_CONF_SUCCESS);
2582 }
2583 
2584 /*
2585  * for each of the actions in parameter actions apply any objects marked as
2586  * modified as a modification to the kernel action represented.
2587  * RETURNS: IPQOS_CONF_ERR on err, else IPQOS_CONF_SUCCESS.
2588  */
2589 static int
2590 modify_items(ipqos_conf_action_t *actions)
2591 {
2592 
2593 	ipqos_conf_action_t *act;
2594 	int res;
2595 	ipqos_conf_filter_t *flt;
2596 	ipqos_conf_class_t *cls;
2597 
2598 
2599 	IPQOSCDBG0(L1, "In modify_items\n");
2600 
2601 	/* loop through actions in parameter actions */
2602 
2603 	for (act = actions; act; act = act->next) {
2604 
2605 		/* skip unchanged actions */
2606 
2607 		if (act->modified == B_FALSE) {
2608 			continue;
2609 		}
2610 
2611 		/* apply any parameter mods */
2612 
2613 		if (act->params->modified) {
2614 			res = modify_params(act->name,
2615 			    &act->params->nvlist,
2616 			    act->module_version, act->params->stats_enable);
2617 			if (res != IPQOS_CONF_SUCCESS) {
2618 				return (IPQOS_CONF_ERR);
2619 			}
2620 
2621 			act->params->cr_mod = B_TRUE;
2622 		}
2623 
2624 		/* apply any class mods */
2625 
2626 		for (cls = act->classes; cls; cls = cls->next) {
2627 			if (cls->modified) {
2628 				res = modify_class(act->name, cls->name,
2629 				    act->module_version, cls->stats_enable,
2630 				    cls->alist->name, 0);
2631 				if (res != IPQOS_CONF_SUCCESS) {
2632 					return (IPQOS_CONF_ERR);
2633 				}
2634 
2635 				/* mark modification done */
2636 				cls->cr_mod = B_TRUE;
2637 			}
2638 		}
2639 
2640 		/* apply any filter mods */
2641 
2642 		for (flt = act->filters; flt; flt = flt->next) {
2643 			if (flt->modified) {
2644 				res = modify_filter(act->name, flt,
2645 				    act->module_version);
2646 				if (res != 0) {
2647 					return (IPQOS_CONF_ERR);
2648 				}
2649 
2650 				/* mark modification done */
2651 				flt->cr_mod = B_TRUE;
2652 			}
2653 		}
2654 
2655 		/* mark action modified */
2656 
2657 		act->cr_mod = B_TRUE;
2658 	}
2659 
2660 	return (IPQOS_CONF_SUCCESS);
2661 }
2662 
2663 /*
2664  * For each of the objects of each of the actions in nactions that are
2665  * marked as having been modified the object modification is done in
2666  * reverse using the same named object from oactions.
2667  * RETURNS: IPQOS_CONF_ERR on error, IPQOS_CONF_SUCCESS otherwise.
2668  */
2669 static int
2670 undo_modifys(
2671 ipqos_conf_action_t *oactions,
2672 ipqos_conf_action_t *nactions)
2673 {
2674 
2675 	ipqos_conf_filter_t *flt;
2676 	ipqos_conf_class_t *cls;
2677 	ipqos_conf_action_t *act;
2678 	ipqos_conf_action_t *oldact;
2679 	ipqos_conf_filter_t *oldflt;
2680 	ipqos_conf_class_t *oldcls;
2681 	int res;
2682 
2683 	IPQOSCDBG0(L1, "In undo_modifys:\n");
2684 
2685 	/* loop throught new actions */
2686 
2687 	for (act = nactions; act; act = act->next) {
2688 		oldact = actionexist(act->name, oactions);
2689 
2690 		/*
2691 		 * if the action was new then it will be removed and
2692 		 * any permamanent items that were marked for modify
2693 		 * will dissappear, so ignore action.
2694 		 */
2695 		if (oldact == NULL) {
2696 			continue;
2697 		}
2698 
2699 		/* if parameters were modified switch them back */
2700 
2701 		if (act->params->modified == B_TRUE &&
2702 		    act->params->cr_mod == B_TRUE) {
2703 			res = modify_params(act->name,
2704 			    &oldact->params->nvlist,
2705 			    act->module_version, act->params->stats_enable);
2706 			if (res != IPQOS_CONF_SUCCESS) {
2707 				return (res);
2708 			}
2709 		}
2710 
2711 		/* for each filter in action if filter modified switch back */
2712 
2713 		for (flt = act->filters; flt; flt = flt->next) {
2714 			if (flt->modified == B_TRUE &&
2715 			    flt->cr_mod == B_TRUE) {
2716 				oldflt = filterexist(flt->name, -1,
2717 				    oldact->filters);
2718 				res = modify_filter(act->name, oldflt,
2719 				    act->module_version);
2720 				if (res != IPQOS_CONF_SUCCESS) {
2721 					return (res);
2722 				}
2723 			}
2724 		}
2725 
2726 		/* for each class in action if class modified switch back */
2727 
2728 		for (cls = act->classes; cls; cls = cls->next) {
2729 			if (cls->modified == B_TRUE &&
2730 			    cls->cr_mod == B_TRUE) {
2731 				oldcls = classexist(cls->name, oldact->classes);
2732 				if (oldcls->alist) {
2733 					res = modify_class(act->name,
2734 					    cls->name, act->module_version,
2735 					    oldcls->stats_enable,
2736 					    oldcls->alist->name, 0);
2737 				}
2738 				if (res != IPQOS_CONF_SUCCESS) {
2739 					return (res);
2740 				}
2741 			}
2742 		}
2743 	}
2744 
2745 	/*
2746 	 * Go through the old actions modifying perm filters and classes
2747 	 * whose action was deleted.
2748 	 *
2749 	 */
2750 	for (act = oactions; act != NULL; act = act->next) {
2751 
2752 		if (act->deleted == B_FALSE) {
2753 			continue;
2754 		}
2755 
2756 		for (flt = act->filters; flt != NULL; flt = flt->next) {
2757 			if (flt->originator == IPP_CONFIG_PERMANENT) {
2758 				res = modify_filter(act->name, flt,
2759 				    act->module_version);
2760 				if (res != IPQOS_CONF_SUCCESS) {
2761 					return (res);
2762 				}
2763 			}
2764 		}
2765 
2766 		for (cls = act->classes; cls != NULL; cls = cls->next) {
2767 			if (cls->originator == IPP_CONFIG_PERMANENT) {
2768 				res = modify_class(act->name, cls->name,
2769 				    act->module_version, cls->stats_enable,
2770 				    cls->alist->name, 0);
2771 				if (res != IPQOS_CONF_SUCCESS) {
2772 					return (res);
2773 				}
2774 			}
2775 
2776 		}
2777 	}
2778 
2779 	return (IPQOS_CONF_SUCCESS);
2780 }
2781 
2782 
2783 /*
2784  * causes all changes marked as being done in actions and old_actions
2785  * to be undone.
2786  * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCESS.
2787  */
2788 static int
2789 rollback(
2790 ipqos_conf_action_t *actions,
2791 ipqos_conf_action_t *old_actions)
2792 {
2793 
2794 	int res;
2795 
2796 	IPQOSCDBG0(RBK, "In rollback:\n");
2797 
2798 	/* re-add items that were deleted */
2799 
2800 	res = add_items(old_actions, B_TRUE);
2801 	if (res != IPQOS_CONF_SUCCESS) {
2802 		return (res);
2803 	}
2804 
2805 	/* change modified items back how they were */
2806 
2807 	res = undo_modifys(old_actions, actions);
2808 	if (res != IPQOS_CONF_SUCCESS) {
2809 		return (res);
2810 	}
2811 
2812 	/* remove new items that were added */
2813 
2814 	res = remove_items(actions, B_TRUE);
2815 	if (res != IPQOS_CONF_SUCCESS) {
2816 		return (res);
2817 	}
2818 
2819 	return (IPQOS_CONF_SUCCESS);
2820 }
2821 
2822 /* ******************************* print config **************************** */
2823 
2824 /*
2825  * Prints the username of the user with uid 'uid' to 'fp' if the uid belongs
2826  * to a known user on the system, otherwise just print 'uid'.
2827  */
2828 static void
2829 printuser(
2830 FILE *fp,
2831 uid_t uid)
2832 {
2833 	struct passwd *pwd;
2834 
2835 	IPQOSCDBG0(L0, "In printuser\n");
2836 
2837 	pwd = getpwuid(uid);
2838 	if (pwd != NULL) {
2839 		(void) fprintf(fp, "%s\n", pwd->pw_name);
2840 	} else {
2841 		(void) fprintf(fp, "%u\n", (int)uid);
2842 	}
2843 }
2844 
2845 /*
2846  * print either a single value of start to fp (if start equals end), else
2847  * print start'-'end if start is the smaller of the two values, otherwise
2848  * print end'-'start.
2849  */
2850 static void
2851 printrange(
2852 FILE *fp,
2853 uint32_t start,
2854 uint32_t end)
2855 {
2856 	uint32_t tmp;
2857 
2858 	if (start > end) {
2859 		tmp = start;
2860 		start = end;
2861 		end = tmp;
2862 	}
2863 
2864 	(void) fprintf(fp, "%u", start);
2865 	if (end != start)
2866 		(void) fprintf(fp, "-%u", end);
2867 }
2868 
2869 /*
2870  * print the contents of the array arr to fp in the form:
2871  * {0-6:1;7-12:2;13:3.....} or {0-6:GREEN;7-12:YELLOW:...}
2872  * dependant upon whether this is an integer or enumerated array resectively
2873  * (if enum_nvs isn't set to NULL this is assumed to be an enumerated array);
2874  * where 0-6 is the range of indexes with value 1 (or GREEN), 7-12 the range
2875  * with value 2 (or YELLOW), and so forth. size is the array size and llimit
2876  * and ulimit are the lower and upper limits of the array values printed
2877  * respectively. For enumerated arrays enum_nvs carries the list of name
2878  * and value pairs and ulimit and llimit parameters are ignored and instead
2879  * determined from the enum_nvs list.
2880  */
2881 static void
2882 print_int_array(
2883 FILE *fp,
2884 int arr[],
2885 uint32_t size,
2886 int llimit,
2887 int ulimit,
2888 str_val_nd_t *enum_nvs,
2889 int tab_inserts)
2890 {
2891 	int x, y;
2892 	uint32_t first, last;
2893 	boolean_t first_entry;	/* first 'ranges:value' to be printed ? */
2894 	boolean_t first_range;	/* first range for a value to be printed ? */
2895 	boolean_t found_range;	/* did we find a range for this value ? */
2896 
2897 	IPQOSCDBG4(L0, "In print_int_array: size: %u, llimit: %u, ulimit: %u, "
2898 	    "enum_nvs: %x \n", size, llimit, ulimit, enum_nvs);
2899 
2900 	/*
2901 	 * if an enumeration retrieve value range.
2902 	 */
2903 	if (enum_nvs != NULL)
2904 		get_str_val_value_range(enum_nvs, &llimit, &ulimit);
2905 
2906 	/*
2907 	 * print opening curl.
2908 	 */
2909 	(void) fprintf(fp, "%c\n", CURL_BEGIN);
2910 	PRINT_TABS(fp, tab_inserts + 1);
2911 
2912 	first_entry = B_TRUE;
2913 	/*
2914 	 * for each value in range.
2915 	 */
2916 	for (x = llimit; x <= ulimit; x++) {
2917 		found_range = B_FALSE;
2918 		first_range = B_TRUE;
2919 		y = 0;
2920 		/*
2921 		 * scan array and print ranges of indexes with value x.
2922 		 */
2923 		while (y < size) {
2924 			/*
2925 			 * get first occurence of value for this range.
2926 			 */
2927 			while ((arr[y] != x) && (y < size))
2928 				y++;
2929 			if (y == size) {
2930 				break;
2931 			} else {
2932 				found_range = B_TRUE;
2933 			}
2934 			first = y;
2935 
2936 			/*
2937 			 * get last occurence of value for this range.
2938 			 */
2939 			while ((arr[y] == x) && (y < size))
2940 				y++;
2941 			last = y - 1;
2942 
2943 			/*
2944 			 * print entry delimiter (semi-colon)? It must be
2945 			 * the first range for this value and this mustn't
2946 			 * be the first 'ranges:value' entry.
2947 			 */
2948 			if (!first_entry && first_range) {
2949 				(void) fprintf(fp, ";\n");
2950 				PRINT_TABS(fp, tab_inserts + 1);
2951 			} else {
2952 				first_entry = B_FALSE;
2953 			}
2954 
2955 			/*
2956 			 * print comma (range delimeter) only if there was
2957 			 * a previous range for this value.
2958 			 */
2959 			if (!first_range) {
2960 				(void) fprintf(fp, ",");
2961 			} else {
2962 				first_range = B_FALSE;
2963 			}
2964 
2965 			/*
2966 			 * print range.
2967 			 */
2968 			printrange(fp, first, last);
2969 		}
2970 		/*
2971 		 * only print a colon and value if we found a range with
2972 		 * this value.
2973 		 */
2974 		if (found_range) {
2975 			(void) fprintf(fp, ":");
2976 
2977 			/*
2978 			 * print numeric/symbolic value.
2979 			 */
2980 			if (enum_nvs) {
2981 				printenum(fp, x, enum_nvs);
2982 			} else {
2983 				(void) fprintf(fp, "%d", x);
2984 			}
2985 		}
2986 	}
2987 
2988 	/*
2989 	 * print closing curl.
2990 	 */
2991 	(void) fprintf(fp, "\n");
2992 	PRINT_TABS(fp, tab_inserts);
2993 	(void) fprintf(fp, "%c\n", CURL_END);
2994 }
2995 
2996 /* print the protocol name for proto, or if unknown protocol number proto. */
2997 static void
2998 printproto(
2999 FILE *fp,
3000 uint8_t proto)
3001 {
3002 
3003 	struct protoent *pent;
3004 
3005 	pent = getprotobynumber(proto);
3006 	if (pent != NULL) {
3007 		(void) fprintf(fp, "%s\n", pent->p_name);
3008 	} else {
3009 		(void) fprintf(fp, "%u\n", proto);
3010 	}
3011 }
3012 
3013 /*
3014  * prints the name associated with interface with index ifindex to fp.
3015  * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCESS.
3016  */
3017 static int
3018 printifname(
3019 FILE *fp,
3020 int ifindex)
3021 {
3022 
3023 	int s;
3024 	struct lifconf lc;
3025 	struct lifnum ln;
3026 	struct lifreq *lr;
3027 	char *buf;
3028 	int len;
3029 	char *cp;
3030 	int ret;
3031 	int x;
3032 	int idx;
3033 
3034 	/* open socket */
3035 
3036 	if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
3037 		ipqos_msg(MT_ENOSTR, gettext("opening AF_INET socket"));
3038 		return (IPQOS_CONF_ERR);
3039 	}
3040 
3041 	/* get number of lifreq structs that need to be alloc'd for */
3042 
3043 	ln.lifn_family = AF_UNSPEC;
3044 	ln.lifn_flags = 0;
3045 	ret = ioctl(s, SIOCGLIFNUM, &ln);
3046 	if (ret < 0) {
3047 		ipqos_msg(MT_ENOSTR, "SIOCLIFNUM ioctl");
3048 		(void) close(s);
3049 		return (IPQOS_CONF_ERR);
3050 	}
3051 
3052 	/* allocate buffer for SIOGLIFCONF ioctl */
3053 
3054 	len = ln.lifn_count * sizeof (struct lifreq);
3055 	buf = malloc(len);
3056 	if (buf == NULL) {
3057 		ipqos_msg(MT_ENOSTR, "malloc");
3058 		(void) close(s);
3059 		return (IPQOS_CONF_ERR);
3060 	}
3061 
3062 	/* setup lifconf params for ioctl */
3063 
3064 	lc.lifc_family = AF_UNSPEC;
3065 	lc.lifc_flags = 0;
3066 	lc.lifc_len = len;
3067 	lc.lifc_buf = buf;
3068 
3069 	/* do SIOCGLIFCONF ioctl */
3070 
3071 	ret = ioctl(s, SIOCGLIFCONF, &lc);
3072 	if (ret < 0) {
3073 		ipqos_msg(MT_ENOSTR, "SIGLIFCONF");
3074 		(void) close(s);
3075 		free(buf);
3076 		return (IPQOS_CONF_ERR);
3077 	}
3078 	(void) close(s);
3079 
3080 	/*
3081 	 * for each interface name given in the returned lifreq list get
3082 	 * it's index and compare with ifindex param. Break if equal.
3083 	 */
3084 	for (x = ln.lifn_count, lr = lc.lifc_req; x > 0; x--, lr++) {
3085 		ret = readifindex(lr->lifr_name, &idx);
3086 		if (ret != IPQOS_CONF_SUCCESS) {
3087 			free(buf);
3088 			return (IPQOS_CONF_ERR);
3089 		}
3090 		if (idx == ifindex) {
3091 			break;
3092 		}
3093 	}
3094 	free(buf);
3095 
3096 	if (x == 0) {
3097 		IPQOSCDBG1(L1, "Failed to find if index %u in returned "
3098 		    "if list.\n", ifindex);
3099 		return (IPQOS_CONF_ERR);
3100 	}
3101 	/* truncate any logical suffix */
3102 
3103 	if ((cp = strchr(lr->lifr_name, '@')) != NULL) {
3104 		*cp = NULL;
3105 	}
3106 
3107 	/* print interface name */
3108 	(void) fprintf(fp, "%s\n", lr->lifr_name);
3109 
3110 	return (IPQOS_CONF_SUCCESS);
3111 }
3112 
3113 /*
3114  * print to fp the enumeration clause evaluating to the value val using the
3115  * names/values given in enum_nvs.
3116  */
3117 static void
3118 printenum(
3119 FILE *fp,
3120 uint32_t val,
3121 str_val_nd_t *enum_nvs)
3122 {
3123 
3124 	boolean_t isfirstval = B_TRUE;
3125 	str_val_nd_t *name_val = enum_nvs;
3126 
3127 	/* for each value in enum_nvs if same bit set in val print name */
3128 
3129 	while (name_val) {
3130 		if ((name_val->sv.value & val) == name_val->sv.value) {
3131 			if (isfirstval == B_TRUE) {
3132 				(void) fprintf(fp, "%s", name_val->sv.string);
3133 				isfirstval = B_FALSE;
3134 			} else {
3135 				(void) fprintf(fp, ", %s", name_val->sv.string);
3136 			}
3137 		}
3138 		name_val = name_val->next;
3139 	}
3140 }
3141 
3142 
3143 /* prints the service name of port, or if unknown the number to fp. */
3144 static void
3145 printport(
3146 FILE *fp,
3147 uint16_t port)
3148 {
3149 
3150 	struct servent *sent;
3151 
3152 	sent = getservbyport(port, NULL);
3153 	if (sent != NULL) {
3154 		(void) fprintf(fp, "%s\n", sent->s_name);
3155 	} else {
3156 		(void) fprintf(fp, "%u\n", ntohs(port));
3157 	}
3158 }
3159 
3160 /*
3161  * prints tp fp the name and value of all user specifiable parameters in the
3162  * nvlist.
3163  * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCESS.
3164  */
3165 static int
3166 printnvlist(
3167 FILE *fp,
3168 char *module,
3169 nvlist_t *nvl,
3170 int printall,	/* are we want ip addresses printing if node name */
3171 ipqos_conf_filter_t *flt,	/* used to determine if node name set */
3172 int tab_inserts,
3173 place_t place)
3174 {
3175 	FILE *tfp;
3176 	nvpair_t *nvp;
3177 	char *name;
3178 	ipqos_nvtype_t type;
3179 	str_val_nd_t *enum_nvs;
3180 	int ret;
3181 	char dfltst[IPQOS_VALST_MAXLEN+1];
3182 	char *param;
3183 	int openerr;
3184 	int res;
3185 
3186 	IPQOSCDBG0(L1, "In printnvlist\n");
3187 
3188 
3189 	/* open stream to types file */
3190 
3191 	tfp = validmod(module, &openerr);
3192 	if (tfp == NULL) {
3193 		if (openerr) {
3194 			ipqos_msg(MT_ENOSTR, "fopen");
3195 		}
3196 		return (IPQOS_CONF_ERR);
3197 	}
3198 
3199 
3200 	/* go through list getting param name and type and printing it */
3201 
3202 	nvp = nvlist_next_nvpair(nvl, NULL);
3203 	while (nvp) {
3204 
3205 		/* get nvpair name */
3206 		name = nvpair_name(nvp);
3207 		IPQOSCDBG1(L0, "processing element %s.\n", name);
3208 
3209 		/* skip ipgpc params that are not explicitly user settable */
3210 
3211 		if (strcmp(name, IPGPC_FILTER_TYPE) == 0 ||
3212 		    strcmp(name, IPGPC_SADDR_MASK) == 0 ||
3213 		    strcmp(name, IPGPC_DADDR_MASK) == 0 ||
3214 		    strcmp(name, IPGPC_SPORT_MASK) == 0 ||
3215 		    strcmp(name, IPGPC_DPORT_MASK) == 0) {
3216 			nvp = nvlist_next_nvpair(nvl, nvp);
3217 			continue;
3218 		}
3219 
3220 		param = SHORT_NAME(name);
3221 
3222 		/*
3223 		 * get parameter type from types file.
3224 		 */
3225 		place = PL_ANY;
3226 		ret = readtype(tfp, module, param, &type, &enum_nvs, dfltst,
3227 		    B_TRUE, &place);
3228 		if (ret != IPQOS_CONF_SUCCESS) {
3229 			return (ret);
3230 		}
3231 
3232 		/*
3233 		 * for map entries we don't print the map value, only
3234 		 * the index value it was derived from.
3235 		 */
3236 		if (place == PL_MAP) {
3237 			nvp = nvlist_next_nvpair(nvl, nvp);
3238 			continue;
3239 		}
3240 
3241 		/*
3242 		 * the ifindex is converted to the name and printed out
3243 		 * so print the parameter name as ifname.
3244 		 */
3245 		if (strcmp(name, IPGPC_IF_INDEX) == 0) {
3246 			PRINT_TABS(fp, tab_inserts);
3247 			(void) fprintf(fp, "%s ", IPQOS_IFNAME_STR);
3248 		/*
3249 		 * we may not print the address due to us instead printing
3250 		 * the node name in printfilter, therefore we leave the
3251 		 * printing of the parameter in the addresses switch case code.
3252 		 */
3253 		} else if ((strcmp(name, IPGPC_SADDR) != 0 &&
3254 		    strcmp(name, IPGPC_DADDR) != 0)) {
3255 			PRINT_TABS(fp, tab_inserts);
3256 			(void) fprintf(fp, "%s ", param);
3257 		}
3258 
3259 		switch (type) {
3260 			case IPQOS_DATA_TYPE_IFINDEX: {
3261 				uint32_t ifidx;
3262 
3263 				(void) nvpair_value_uint32(nvp, &ifidx);
3264 				(void) printifname(fp, ifidx);
3265 				break;
3266 			}
3267 			case IPQOS_DATA_TYPE_BOOLEAN: {
3268 				boolean_t bl;
3269 
3270 				(void) nvpair_value_uint32(nvp,
3271 				    (uint32_t *)&bl);
3272 				(void) fprintf(fp, "%s\n",
3273 				    bl == B_TRUE ? "true" : "false");
3274 				break;
3275 			}
3276 			case IPQOS_DATA_TYPE_ACTION: {
3277 				char *strval;
3278 
3279 				(void) nvpair_value_string(nvp, &strval);
3280 				print_action_nm(fp, strval);
3281 				break;
3282 			}
3283 			case IPQOS_DATA_TYPE_STRING: {
3284 				char *strval;
3285 
3286 				(void) nvpair_value_string(nvp, &strval);
3287 				(void) fprintf(fp, "%s\n",
3288 				    quote_ws_string(strval));
3289 				break;
3290 			}
3291 			case IPQOS_DATA_TYPE_ADDRESS: {
3292 				uint_t tmp;
3293 				in6_addr_t *addr;
3294 				char addrstr[INET6_ADDRSTRLEN];
3295 				uchar_t ftype;
3296 				int af;
3297 				in6_addr_t *mask;
3298 
3299 				/*
3300 				 * skip addresses that have node names for
3301 				 * non printall listings.
3302 				 */
3303 				if (printall == 0 &&
3304 				    (strcmp(nvpair_name(nvp), IPGPC_SADDR) ==
3305 				    0 && flt->src_nd_name ||
3306 				    strcmp(nvpair_name(nvp), IPGPC_DADDR) ==
3307 				    0 && flt->dst_nd_name)) {
3308 					break;
3309 				}
3310 
3311 				/* we skipped this above */
3312 
3313 				PRINT_TABS(fp, tab_inserts);
3314 				(void) fprintf(fp, "%s ", param);
3315 
3316 				(void) nvpair_value_uint32_array(nvp,
3317 				    (uint32_t **)&addr, &tmp);
3318 
3319 				/* get filter type */
3320 
3321 				(void) nvlist_lookup_byte(nvl,
3322 				    IPGPC_FILTER_TYPE, &ftype);
3323 				if (ftype == IPGPC_V4_FLTR) {
3324 					af = AF_INET;
3325 					addr = (in6_addr_t *)
3326 					&V4_PART_OF_V6((*addr));
3327 				} else {
3328 					af = AF_INET6;
3329 				}
3330 				/* get mask */
3331 
3332 				if (strcmp(nvpair_name(nvp), IPGPC_SADDR) ==
3333 				    0) {
3334 					ret = nvlist_lookup_uint32_array(nvl,
3335 					    IPGPC_SADDR_MASK,
3336 					    (uint32_t **)&mask, &tmp);
3337 				} else {
3338 					ret = nvlist_lookup_uint32_array(nvl,
3339 					    IPGPC_DADDR_MASK,
3340 					    (uint32_t **)&mask, &tmp);
3341 				}
3342 
3343 				/* print address/mask to fp */
3344 
3345 				(void) fprintf(fp, "%s/%u\n",
3346 				    inet_ntop(af, addr, addrstr,
3347 				    INET6_ADDRSTRLEN), masktocidr(af, mask));
3348 				break;
3349 			}
3350 			case IPQOS_DATA_TYPE_ENUM: {
3351 				uint32_t val;
3352 
3353 				(void) nvpair_value_uint32(nvp, &val);
3354 
3355 				/*
3356 				 * print list of tokens resulting in val
3357 				 */
3358 				(void) fprintf(fp, "{ ");
3359 				printenum(fp, val, enum_nvs);
3360 				(void) fprintf(fp, " }\n");
3361 				break;
3362 			}
3363 			case IPQOS_DATA_TYPE_PORT: {
3364 				uint16_t port;
3365 
3366 				(void) nvpair_value_uint16(nvp, &port);
3367 				printport(fp, port);
3368 				break;
3369 			}
3370 			case IPQOS_DATA_TYPE_PROTO: {
3371 				uint8_t proto;
3372 
3373 				(void) nvpair_value_byte(nvp, &proto);
3374 				printproto(fp, proto);
3375 				break;
3376 			}
3377 			case IPQOS_DATA_TYPE_M_INDEX:
3378 			case IPQOS_DATA_TYPE_UINT8: {
3379 				uchar_t u8;
3380 
3381 				(void) nvpair_value_byte(nvp, &u8);
3382 				(void) fprintf(fp, "%u\n", u8);
3383 				break;
3384 			}
3385 			case IPQOS_DATA_TYPE_UINT16: {
3386 				uint16_t u16;
3387 
3388 				(void) nvpair_value_uint16(nvp, &u16);
3389 				(void) fprintf(fp, "%u\n", u16);
3390 				break;
3391 			}
3392 			case IPQOS_DATA_TYPE_INT16: {
3393 				int16_t i16;
3394 
3395 				(void) nvpair_value_int16(nvp, &i16);
3396 				(void) fprintf(fp, "%d\n", i16);
3397 				break;
3398 			}
3399 			case IPQOS_DATA_TYPE_UINT32: {
3400 				uint32_t u32;
3401 
3402 				(void) nvpair_value_uint32(nvp, &u32);
3403 				(void) fprintf(fp, "%u\n", u32);
3404 				break;
3405 			}
3406 			case IPQOS_DATA_TYPE_INT32: {
3407 				int i32;
3408 
3409 				(void) nvpair_value_int32(nvp, &i32);
3410 				(void) fprintf(fp, "%d\n", i32);
3411 				break;
3412 			}
3413 			case IPQOS_DATA_TYPE_INT_ARRAY: {
3414 				str_val_nd_t *arr_enum_nvs = NULL;
3415 				uint32_t size;
3416 				int llimit, ulimit;
3417 				int *arr;
3418 
3419 				(void) nvpair_value_int32_array(nvp, &arr,
3420 				    &size);
3421 
3422 				/*
3423 				 * read array info from types file.
3424 				 */
3425 				res = read_int_array_info(dfltst,
3426 				    &arr_enum_nvs, &size, &llimit, &ulimit,
3427 				    module);
3428 
3429 				/*
3430 				 * print array with numbers, or symbols
3431 				 * if enumerated.
3432 				 */
3433 				if (res == IPQOS_CONF_SUCCESS) {
3434 					print_int_array(fp, arr, size,
3435 					    llimit, ulimit, arr_enum_nvs,
3436 					    tab_inserts);
3437 					if (arr_enum_nvs != NULL) {
3438 						free_str_val_entrys(
3439 						    arr_enum_nvs);
3440 					}
3441 				}
3442 				break;
3443 			}
3444 			case IPQOS_DATA_TYPE_USER: {
3445 				uid_t uid;
3446 
3447 				(void) nvpair_value_int32(nvp, (int *)&uid);
3448 				printuser(fp, uid);
3449 				break;
3450 			}
3451 #ifdef	_IPQOS_CONF_DEBUG
3452 			default: {
3453 				/*
3454 				 * we should have catered for all used data
3455 				 * types that readtype returns.
3456 				 */
3457 				assert(1);
3458 			}
3459 #endif
3460 		}
3461 
3462 		nvp = nvlist_next_nvpair(nvl, nvp);
3463 	}
3464 
3465 	(void) fclose(tfp);
3466 	return (IPQOS_CONF_SUCCESS);
3467 }
3468 
3469 /*
3470  * print a parameter clause for the parmeters given in params to fp.
3471  * If printall is set, then the originator of the parameter object is printed.
3472  * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCESS.
3473  */
3474 static int
3475 printparams(
3476 FILE *fp,
3477 char *module,
3478 ipqos_conf_params_t *params,
3479 int printall,
3480 int tab_inserts)
3481 {
3482 
3483 	int res;
3484 
3485 	/* print opening clause */
3486 
3487 	PRINT_TABS(fp, tab_inserts);
3488 	(void) fprintf(fp, IPQOS_CONF_PARAMS_STR " {\n");
3489 
3490 	/* print originator name if printall flag set */
3491 
3492 	if (printall) {
3493 		PRINT_TABS(fp, tab_inserts + 1);
3494 		(void) fprintf(stdout, "Originator %s\n",
3495 		    quote_ws_string(get_originator_nm(params->originator)));
3496 	}
3497 
3498 	/* print global stats */
3499 
3500 	PRINT_TABS(fp, tab_inserts + 1);
3501 	(void) fprintf(fp, IPQOS_CONF_GLOBAL_STATS_STR " %s\n",
3502 	    params->stats_enable == B_TRUE ? "true" : "false");
3503 
3504 	/* print module specific parameters */
3505 	res = printnvlist(fp, module, params->nvlist, printall, NULL,
3506 	    tab_inserts + 1, PL_PARAMS);
3507 	if (res != IPQOS_CONF_SUCCESS) {
3508 		return (res);
3509 	}
3510 
3511 	PRINT_TABS(fp, tab_inserts);
3512 	(void) fprintf(fp, "}\n");
3513 
3514 	return (IPQOS_CONF_SUCCESS);
3515 }
3516 
3517 /*
3518  * print the interpreted name of the action_nm parameter if it is a special
3519  * action, else action_nm verbatim to fp parameter.
3520  */
3521 static void
3522 print_action_nm(FILE *fp, char *action_nm)
3523 {
3524 
3525 	if (strcmp(action_nm, IPP_ANAME_CONT) == 0) {
3526 		(void) fprintf(fp, IPQOS_CONF_CONT_STR "\n");
3527 	} else if (strcmp(action_nm, IPP_ANAME_DEFER) == 0) {
3528 		(void) fprintf(fp, IPQOS_CONF_DEFER_STR "\n");
3529 	} else if (strcmp(action_nm, IPP_ANAME_DROP) == 0) {
3530 		(void) fprintf(fp, IPQOS_CONF_DROP_STR "\n");
3531 	} else {
3532 		(void) fprintf(fp, "%s\n", quote_ws_string(action_nm));
3533 	}
3534 }
3535 
3536 /*
3537  * print a class clause for class to fp. If printall is set the originator
3538  * is printed.
3539  */
3540 static void
3541 printclass(
3542 FILE *fp,
3543 ipqos_conf_class_t *class,
3544 int printall,
3545 int tab_inserts)
3546 {
3547 
3548 	/* print opening clause */
3549 
3550 	PRINT_TABS(fp, tab_inserts);
3551 	(void) fprintf(fp, IPQOS_CONF_CLASS_STR " {\n");
3552 
3553 
3554 	/* if printall flag print originator name */
3555 
3556 	if (printall) {
3557 		PRINT_TABS(fp, tab_inserts + 1);
3558 		(void) fprintf(stdout, "Originator %s\n",
3559 		    get_originator_nm(class->originator));
3560 	}
3561 
3562 	/* print name, next action and stats enable */
3563 
3564 	PRINT_TABS(fp, tab_inserts + 1);
3565 	(void) fprintf(fp, IPQOS_CONF_NAME_STR " %s\n",
3566 	    quote_ws_string(class->name));
3567 	PRINT_TABS(fp, tab_inserts + 1);
3568 	(void) fprintf(fp, IPQOS_CONF_NEXT_ACTION_STR " ");
3569 	    print_action_nm(fp, class->alist->name);
3570 	PRINT_TABS(fp, tab_inserts + 1);
3571 	(void) fprintf(fp, IPQOS_CONF_STATS_ENABLE_STR " %s\n",
3572 	    class->stats_enable == B_TRUE ? "true" : "false");
3573 
3574 	PRINT_TABS(fp, tab_inserts);
3575 	(void) fprintf(fp, "}\n");
3576 }
3577 
3578 /*
3579  * Returns a ptr to the originator name associated with origid. If unknown
3580  * id returns ptr to "unknown".
3581  * RETURNS: ptr to originator name, or if id not known "unknown".
3582  */
3583 static char *
3584 get_originator_nm(uint32_t origid)
3585 {
3586 
3587 	int x;
3588 
3589 	/* scan originators table for origid */
3590 
3591 	for (x = 0; originators[x].value != -1 &&
3592 	    originators[x].value != origid; x++) {}
3593 
3594 	/* if we've reached end of array due to unknown type return "unknown" */
3595 
3596 	if (originators[x].value == -1) {
3597 		return ("unknown");
3598 	}
3599 
3600 	return (originators[x].string);
3601 }
3602 
3603 /*
3604  * print a filter clause for filter pointed to by filter out to fp. If printall
3605  * is set then the originator is printed, for filters with node names instance
3606  * numbers are printed, and the filter pointer isn't advanced to point at the
3607  * last instance of the printed filter.
3608  * RETURNS: IPQOS_CONF_ERR on errors, else IPQOS_CONF_SUCCESS.
3609  */
3610 static int
3611 printfilter(
3612 FILE *fp,
3613 char *module,
3614 ipqos_conf_filter_t **filter,
3615 int printall,
3616 int tab_inserts)
3617 {
3618 
3619 	int res;
3620 
3621 	/* print opening clause */
3622 
3623 	PRINT_TABS(fp, tab_inserts);
3624 	(void) fprintf(fp, IPQOS_CONF_FILTER_STR " {\n");
3625 
3626 	/* print originator if printall flag set */
3627 
3628 	if (printall) {
3629 		PRINT_TABS(fp, tab_inserts + 1);
3630 		(void) fprintf(stdout, "Originator %s\n",
3631 		    quote_ws_string(get_originator_nm((*filter)->originator)));
3632 	}
3633 
3634 	/* print name and class */
3635 
3636 	PRINT_TABS(fp, tab_inserts + 1);
3637 	(void) fprintf(fp, IPQOS_CONF_NAME_STR " %s\n",
3638 	    quote_ws_string((*filter)->name));
3639 	PRINT_TABS(fp, tab_inserts + 1);
3640 	(void) fprintf(fp, IPQOS_CONF_CLASS_STR " %s\n",
3641 	    quote_ws_string((*filter)->class_name));
3642 
3643 	/* print the instance if printall and potential mhomed addresses */
3644 
3645 	if (printall && ((*filter)->src_nd_name || (*filter)->dst_nd_name)) {
3646 		PRINT_TABS(fp, tab_inserts + 1);
3647 		(void) fprintf(fp, "Instance %u\n", (*filter)->instance);
3648 	}
3649 
3650 	/* print node names if any */
3651 
3652 	if ((*filter)->src_nd_name) {
3653 		PRINT_TABS(fp, tab_inserts + 1);
3654 		(void) fprintf(fp, "%s %s\n", strchr(IPGPC_SADDR, '.') + 1,
3655 		    (*filter)->src_nd_name);
3656 	}
3657 	if ((*filter)->dst_nd_name) {
3658 		PRINT_TABS(fp, tab_inserts + 1);
3659 		(void) fprintf(fp, "%s %s\n", strchr(IPGPC_DADDR, '.') + 1,
3660 		    (*filter)->dst_nd_name);
3661 	}
3662 
3663 	/* print ip_version enumeration if set */
3664 
3665 	if ((*filter)->ip_versions != 0) {
3666 		PRINT_TABS(fp, tab_inserts + 1);
3667 		(void) fprintf(fp, IPQOS_CONF_IP_VERSION_STR " {");
3668 		if (VERSION_IS_V4(*filter)) {
3669 			(void) fprintf(fp, " V4");
3670 		}
3671 		if (VERSION_IS_V6(*filter)) {
3672 			(void) fprintf(fp, " V6");
3673 		}
3674 		(void) fprintf(fp, " }\n");
3675 	}
3676 
3677 	/* print other module specific parameters parameters */
3678 
3679 	res = printnvlist(fp, module, (*filter)->nvlist, printall, *filter,
3680 	    tab_inserts + 1, PL_FILTER);
3681 	if (res != IPQOS_CONF_SUCCESS) {
3682 		return (res);
3683 	}
3684 
3685 	PRINT_TABS(fp, tab_inserts);
3686 	(void) fprintf(fp, "}\n");
3687 
3688 	/*
3689 	 * if not printall advance filter parameter to last instance of this
3690 	 * filter.
3691 	 */
3692 
3693 	if (!printall) {
3694 		for (;;) {
3695 			if ((*filter)->next == NULL ||
3696 			    strcmp((*filter)->name, (*filter)->next->name) !=
3697 			    0) {
3698 				break;
3699 			}
3700 			*filter = (*filter)->next;
3701 		}
3702 	}
3703 
3704 	return (IPQOS_CONF_SUCCESS);
3705 }
3706 
3707 /*
3708  * Returns a pointer to str if no whitespace is present, else it returns
3709  * a pointer to a string with the contents of str enclose in double quotes.
3710  * This returned strings contents may change in subsequent calls so a copy
3711  * should be made of it if the caller wishes to retain it.
3712  */
3713 static char *
3714 quote_ws_string(const char *str)
3715 {
3716 	static char *buf = NULL;
3717 	const char *cp;	/* we don't modify the contents of str so const */
3718 
3719 	IPQOSCDBG0(L0, "In quote_ws_string\n");
3720 
3721 	/*
3722 	 * Just return str if no whitespace.
3723 	 */
3724 	for (cp = str; (*cp != '\0') && !isspace(*cp); cp++)
3725 		;
3726 	if (*cp == '\0')
3727 		return ((char *)str);
3728 
3729 	if (buf == NULL) {
3730 		/*
3731 		 * if first run just allocate buffer of
3732 		 * strlen(str) + 2 quote characters + NULL terminator.
3733 		 */
3734 		buf = malloc(strlen(str) + 3);
3735 	} else if ((strlen(str) + 2) > strlen(buf)) {
3736 		/*
3737 		 * Not first run, so check if we have a big enough buffer
3738 		 * and if not reallocate the buffer to a sufficient size.
3739 		 */
3740 		buf = realloc(buf, strlen(str) + 3);
3741 	}
3742 	if (buf == NULL)
3743 		return ("");
3744 
3745 	/*
3746 	 * copy string into buffer with quotes.
3747 	 */
3748 	(void) strcpy(buf, "\"");
3749 	(void) strcat(buf, str);
3750 	(void) strcat(buf, "\"");
3751 
3752 	return (buf);
3753 }
3754 
3755 /*
3756  * print an action clause for action to fp. If the printall flag is set
3757  * then all filters and classes (regardless of their originator) and
3758  * their originators are displayed.
3759  * RETURNS: IPQOS_CONF_ERR on errors, else IPQOS_CONF_SUCCESS.
3760  */
3761 static int
3762 printaction(
3763 FILE *fp,
3764 ipqos_conf_action_t *action,
3765 int printall,
3766 int tab_inserts)
3767 {
3768 
3769 	ipqos_conf_filter_t *flt;
3770 	ipqos_conf_class_t *cls;
3771 	int res;
3772 
3773 	/* print opening clause, module and name */
3774 
3775 	PRINT_TABS(fp, tab_inserts);
3776 	(void) fprintf(fp, IPQOS_CONF_ACTION_STR " {\n");
3777 	PRINT_TABS(fp, tab_inserts + 1);
3778 	(void) fprintf(fp, IPQOS_CONF_MODULE_STR " %s\n",
3779 	    quote_ws_string(action->module));
3780 	PRINT_TABS(fp, tab_inserts + 1);
3781 	(void) fprintf(fp, "name %s\n", quote_ws_string(action->name));
3782 
3783 	/* print params clause */
3784 
3785 	(void) fprintf(fp, "\n");
3786 	res = printparams(fp, action->module, action->params, printall,
3787 	    tab_inserts + 1);
3788 	if (res != IPQOS_CONF_SUCCESS) {
3789 		return (res);
3790 	}
3791 
3792 	/*
3793 	 * print classes clause for each class if printall is set, else
3794 	 * just ipqosconf created or permanent classes.
3795 	 */
3796 	for (cls = action->classes; cls != NULL; cls = cls->next) {
3797 		if (printall ||
3798 		    cls->originator == IPP_CONFIG_IPQOSCONF ||
3799 		    cls->originator == IPP_CONFIG_PERMANENT) {
3800 			(void) fprintf(fp, "\n");
3801 			printclass(fp, cls, printall, tab_inserts + 1);
3802 		}
3803 	}
3804 
3805 	/*
3806 	 * print filter clause for each filter if printall is set, else
3807 	 * just ipqosconf created or permanent filters.
3808 	 */
3809 	for (flt = action->filters; flt != NULL; flt = flt->next) {
3810 		if (printall ||
3811 		    flt->originator == IPP_CONFIG_IPQOSCONF ||
3812 		    flt->originator == IPP_CONFIG_PERMANENT) {
3813 			(void) fprintf(fp, "\n");
3814 			res = printfilter(fp, action->module, &flt, printall,
3815 			    tab_inserts + 1);
3816 			if (res != IPQOS_CONF_SUCCESS) {
3817 				return (res);
3818 			}
3819 		}
3820 	}
3821 
3822 	PRINT_TABS(fp, tab_inserts);
3823 	(void) fprintf(fp, "}\n");
3824 
3825 	return (IPQOS_CONF_SUCCESS);
3826 }
3827 
3828 
3829 
3830 /* *************************************************************** */
3831 
3832 
3833 static void
3834 list_end(
3835 ipqos_list_el_t **listp,
3836 ipqos_list_el_t ***lendpp)
3837 {
3838 	*lendpp = listp;
3839 	while (**lendpp != NULL) {
3840 		*lendpp = &(**lendpp)->next;
3841 	}
3842 }
3843 
3844 static void
3845 add_to_list(
3846 ipqos_list_el_t **listp,
3847 ipqos_list_el_t *el)
3848 {
3849 	el->next = *listp;
3850 	*listp = el;
3851 }
3852 
3853 /*
3854  * given mask calculates the number of bits it spans. The mask must be
3855  * continuous.
3856  * RETURNS: number of bits spanned.
3857  */
3858 static int
3859 masktocidr(
3860 int af,
3861 in6_addr_t *mask)
3862 {
3863 	int zeros = 0;
3864 	int byte;
3865 	int cidr;
3866 
3867 	/*
3868 	 * loop through from lowest byte to highest byte counting the
3869 	 * number of zero bits till hitting a one bit.
3870 	 */
3871 	for (byte = 15; byte >= 0; byte--) {
3872 		/*
3873 		 * zero byte, so add 8 to zeros.
3874 		 */
3875 		if (mask->s6_addr[byte] == 0) {
3876 			zeros += 8;
3877 		/*
3878 		 * non-zero byte, add zero count to zeros.
3879 		 */
3880 		} else {
3881 			zeros += (ffs((int)mask->s6_addr[byte]) - 1);
3882 			break;
3883 		}
3884 	}
3885 	/*
3886 	 * translate zero bits to 32 or 128 bit mask based on af.
3887 	 */
3888 	if (af == AF_INET) {
3889 		cidr = 32 - zeros;
3890 	} else {
3891 		cidr = 128 - zeros;
3892 	}
3893 
3894 	return (cidr);
3895 }
3896 
3897 /*
3898  * Sets the first prefix_len bits in the v4 or v6 address (based upon af)
3899  * contained in the v6 address referenced by addr to 1.
3900  */
3901 static void
3902 setmask(int prefix_len, in6_addr_t *addr, int af)
3903 {
3904 
3905 	int i;
3906 	int shift;
3907 	int maskstartbit = 128 - prefix_len;
3908 	int end_u32;
3909 
3910 	IPQOSCDBG2(L1, "In setmask, prefix_len: %u, af: %s\n", prefix_len,
3911 	    af == AF_INET ? "AF_INET" : "AF_INET6");
3912 
3913 	/* zero addr */
3914 	bzero(addr, sizeof (in6_addr_t));
3915 
3916 
3917 	/* set which 32bits in *addr are relevant to this af */
3918 
3919 	if (af == AF_INET) {
3920 		end_u32 = 3;
3921 		maskstartbit = 32 - prefix_len;
3922 	/* AF_INET6 */
3923 	} else {
3924 		end_u32 = 0;
3925 	}
3926 	/*
3927 	 * go through each of the 32bit quantities in 128 bit in6_addr_t
3928 	 * and set appropriate bits according to prefix_len.
3929 	 */
3930 	for (i = 3; i >= end_u32; i--) {
3931 
3932 		/* does the prefix apply to this 32bits? */
3933 
3934 		if (maskstartbit < ((4 - i) * 32)) {
3935 
3936 			/* is this 32bits fully masked? */
3937 
3938 			if (maskstartbit <= ((3 - i) * 32)) {
3939 				shift = 0;
3940 			} else {
3941 				shift = maskstartbit % 32;
3942 			}
3943 			addr->_S6_un._S6_u32[i] = (uint32_t)~0;
3944 			addr->_S6_un._S6_u32[i] =
3945 			    addr->_S6_un._S6_u32[i] >> shift;
3946 			addr->_S6_un._S6_u32[i] =
3947 			    addr->_S6_un._S6_u32[i] << shift;
3948 		}
3949 
3950 		/* translate to NBO */
3951 		addr->_S6_un._S6_u32[i] = htonl(addr->_S6_un._S6_u32[i]);
3952 	}
3953 }
3954 
3955 /*
3956  * search nvlist for an element with the name specified and return a ptr
3957  * to it if found.
3958  * RETURNS: pointer to nvpair named name if found, else NULL.
3959  */
3960 static nvpair_t *
3961 find_nvpair(nvlist_t *nvl, char *name)
3962 {
3963 
3964 	nvpair_t *nvp;
3965 	nvpair_t *match = NULL;
3966 	char *nvp_name;
3967 
3968 	IPQOSCDBG0(L1, "In find_nvpair\n");
3969 
3970 	nvp = nvlist_next_nvpair(nvl, NULL);
3971 	while (nvp) {
3972 		nvp_name = nvpair_name(nvp);
3973 		if (strcmp(name, nvp_name) == 0) {
3974 			match = nvp;
3975 		}
3976 		nvp = nvlist_next_nvpair(nvl, nvp);
3977 	}
3978 
3979 	return (match);
3980 }
3981 
3982 /*
3983  * returns a string containing module_name '.' name.
3984  * RETURNS: IPQOS_CONF_ERR if error, else IPQOS_CONF_SUCCESS.
3985  */
3986 static char *
3987 prepend_module_name(
3988 char *name,
3989 char *module)
3990 {
3991 
3992 	char *ret;
3993 
3994 	IPQOSCDBG0(L2, "In prepend_module_name\n");
3995 
3996 	ret = malloc(strlen(module) + strlen(".") + strlen(name) + 1);
3997 	if (ret == NULL) {
3998 		ipqos_msg(MT_ENOSTR, "malloc");
3999 		return (NULL);
4000 	}
4001 
4002 	(void) strcpy(ret, module);
4003 	(void) strcat(ret, ".");
4004 	(void) strcat(ret, name);
4005 
4006 	return (ret);
4007 }
4008 
4009 #if 0
4010 
4011 /*
4012  * check if element with matching s1 and s2 string is in table table.
4013  * RETURNS: 1 if found else 0.
4014  */
4015 static int
4016 in_str_str_table(
4017 str_str_t *table,
4018 char *s1,
4019 char *s2)
4020 {
4021 
4022 	str_str_t *ss = table;
4023 
4024 	/* loop through table till matched or end */
4025 
4026 	while (ss->s1[0] != '\0' &&
4027 	    (strcmp(ss->s1, s1) != 0 || strcmp(ss->s2, s2) != 0)) {
4028 		ss++;
4029 	}
4030 
4031 	if (ss->s1[0] != '\0') {
4032 		return (1);
4033 	}
4034 
4035 	return (0);
4036 }
4037 #endif	/* 0 */
4038 
4039 /*
4040  * check whether name is a valid action/class/filter name.
4041  * RETURNS: IPQOS_CONF_ERR if invalid name else IPQOS_CONF_SUCCESS.
4042  */
4043 static int
4044 valid_name(char *name)
4045 {
4046 
4047 	IPQOSCDBG1(L1, "In valid_name: name: %s\n", name);
4048 
4049 	/* first char can't be '!' */
4050 	if (name[0] == '!') {
4051 		ipqos_msg(MT_ERROR, gettext("Name not allowed to start with "
4052 		    "'!', line %u.\n"), lineno);
4053 		return (IPQOS_CONF_ERR);
4054 	}
4055 
4056 	/* can't exceed IPQOS_CONF_NAME_LEN size */
4057 	if (strlen(name) >= IPQOS_CONF_NAME_LEN) {
4058 		ipqos_msg(MT_ERROR, gettext("Name exceeds maximum name length "
4059 		    "line %u.\n"), lineno);
4060 		return (IPQOS_CONF_ERR);
4061 	}
4062 
4063 	return (IPQOS_CONF_SUCCESS);
4064 }
4065 
4066 /* ********************* string value manip fns ************************** */
4067 
4068 
4069 /*
4070  * searches through the str_val_nd_t list of string value pairs finding
4071  * the minimum and maximum values for value and places them in the
4072  * integers pointed at by min and max.
4073  */
4074 static void
4075 get_str_val_value_range(
4076 str_val_nd_t *svnp,
4077 int *min,
4078 int *max)
4079 {
4080 	if (svnp != NULL) {
4081 		*min = *max = svnp->sv.value;
4082 		svnp = svnp->next;
4083 	}
4084 	while (svnp != NULL) {
4085 		if (svnp->sv.value > *max)
4086 			*max = svnp->sv.value;
4087 		if (svnp->sv.value < *min)
4088 			*min = svnp->sv.value;
4089 		svnp = svnp->next;
4090 	}
4091 }
4092 
4093 /*
4094  * add an entry with string string and value val to sv_entrys.
4095  * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCESS.
4096  */
4097 static int
4098 add_str_val_entry(
4099 str_val_nd_t **sv_entrys,
4100 char *string,
4101 uint32_t val)
4102 {
4103 
4104 	str_val_nd_t *sv_entry;
4105 
4106 	IPQOSCDBG2(L1, "In add_str_val_entry: string: %s, val: %u\n", string,
4107 	    val);
4108 
4109 	/* alloc new node */
4110 
4111 	sv_entry = malloc(sizeof (str_val_nd_t));
4112 	if (sv_entry == NULL) {
4113 		return (IPQOS_CONF_ERR);
4114 	}
4115 
4116 	/* populate node */
4117 
4118 	sv_entry->sv.string = malloc(strlen(string) + 1);
4119 	if (sv_entry->sv.string == NULL) {
4120 		free(sv_entry);
4121 		ipqos_msg(MT_ENOSTR, "malloc");
4122 		return (IPQOS_CONF_ERR);
4123 	} else {
4124 		(void) strcpy(sv_entry->sv.string, string);
4125 	}
4126 	sv_entry->sv.value = val;
4127 
4128 	/* place at start of sv_entrys list */
4129 
4130 	sv_entry->next = *sv_entrys;
4131 	*sv_entrys = sv_entry;
4132 
4133 	return (IPQOS_CONF_SUCCESS);
4134 }
4135 
4136 
4137 /* frees all the elements of sv_entrys. */
4138 static void
4139 free_str_val_entrys(
4140 str_val_nd_t *sv_entrys)
4141 {
4142 
4143 	str_val_nd_t *sve = sv_entrys;
4144 	str_val_nd_t *tmp;
4145 
4146 	IPQOSCDBG0(L1, "In free_str_val_entrys\n");
4147 
4148 	while (sve) {
4149 		free(sve->sv.string);
4150 		tmp = sve->next;
4151 		free(sve);
4152 		sve = tmp;
4153 	}
4154 }
4155 
4156 /*
4157  * finds the value associated with string and assigns it to value ref'd by
4158  * val.
4159  * RETURNS: IPQOS_CONF_ERR if string not found, else IPQOS_CONF_SUCCESS.
4160  */
4161 static int
4162 str_val_list_lookup(
4163 str_val_nd_t *svs,
4164 char *string,
4165 uint32_t *val)
4166 {
4167 
4168 	str_val_nd_t *sv = svs;
4169 
4170 	IPQOSCDBG1(L1, "In str_val_list_lookup: %s\n", string);
4171 
4172 	/* loop through list and exit when found or list end */
4173 
4174 	while (sv != NULL) {
4175 		if (strcmp(sv->sv.string, string) == 0) {
4176 			break;
4177 		}
4178 		sv = sv->next;
4179 	}
4180 
4181 	/* ret error if not found */
4182 
4183 	if (sv == NULL) {
4184 		return (IPQOS_CONF_ERR);
4185 	}
4186 
4187 	*val = sv->sv.value;
4188 
4189 	IPQOSCDBG1(L1, "svll: Value returned is %u\n", *val);
4190 	return (IPQOS_CONF_SUCCESS);
4191 }
4192 
4193 
4194 /* ************************ conf file read fns ***************************** */
4195 
4196 /*
4197  * Reads a uid or username from string 'str' and assigns either the uid
4198  * or associated uid respectively to storage pointed at by 'uid'. The
4199  * function determines whether to read a uid by checking whether the first
4200  * character of 'str' is numeric, in which case it reads a uid; otherwise it
4201  * assumes a username.
4202  * RETURNS: IPQOS_CONF_ERR if a NULL string pointer is passed, the read uid
4203  * doesn't have an entry on the system, or the read username doesn't have an
4204  * entry on the system.
4205  */
4206 static int
4207 readuser(
4208 char *str,
4209 uid_t *uid)
4210 {
4211 	struct passwd *pwd;
4212 	char *lo;
4213 
4214 	IPQOSCDBG1(L0, "In readuser, str: %s\n", str);
4215 
4216 	if (str == NULL)
4217 		return (IPQOS_CONF_ERR);
4218 	/*
4219 	 * Check if this appears to be a uid, and if so check that a
4220 	 * corresponding user exists.
4221 	 */
4222 	if (isdigit((int)str[0])) {
4223 		/*
4224 		 * Read a 32bit integer and check in doing so that
4225 		 * we have consumed the whole string.
4226 		 */
4227 		if (readint32(str, (int *)uid, &lo) != IPQOS_CONF_SUCCESS ||
4228 		    *lo != '\0')
4229 			return (IPQOS_CONF_ERR);
4230 		if (getpwuid(*uid) == NULL)
4231 			return (IPQOS_CONF_ERR);
4232 
4233 	} else {	/* This must be a username, so lookup the uid. */
4234 		pwd = getpwnam(str);
4235 		if (pwd == NULL) {
4236 			return (IPQOS_CONF_ERR);
4237 		} else {
4238 			*uid = pwd->pw_uid;
4239 		}
4240 	}
4241 	return (IPQOS_CONF_SUCCESS);
4242 }
4243 
4244 /*
4245  * Reads a range from range_st, either of form 'a-b' or simply 'a'.
4246  * In the former case lower and upper have their values set to a
4247  * and b respectively; in the later lower and upper have both
4248  * their values set to a.
4249  * RETURNS: IPQOS_CONF_ERR if there's a parse error, else IPQOS_CONF_SUCCESS.
4250  */
4251 static int
4252 readrange(
4253 char *range_st,
4254 int *lower,
4255 int *upper)
4256 {
4257 	char *cp;
4258 	char *end, *end2;
4259 
4260 	IPQOSCDBG1(L0, "In readrange: string: %s\n", range_st);
4261 
4262 	/*
4263 	 * get range boundarys.
4264 	 */
4265 	cp = strchr(range_st, '-');
4266 
4267 	if (cp != NULL) {	/* we have a range */
4268 		*cp++ = '\0';
4269 		*lower = (int)strtol(range_st, &end, 10);
4270 		*upper = (int)strtol(cp, &end2, 10);
4271 		SKIPWS(end);
4272 		SKIPWS(end2);
4273 		if ((range_st == end) || (*end != NULL) ||
4274 		    (cp == end) || (*end2 != NULL)) {
4275 			IPQOSCDBG0(L0, "Failed reading a-b\n");
4276 			return (IPQOS_CONF_ERR);
4277 		}
4278 
4279 	} else {		/* single value */
4280 
4281 		*lower = *upper = (int)strtol(range_st, &end, 10);
4282 		SKIPWS(end);
4283 		if ((range_st == end) || (*end != NULL)) {
4284 			IPQOSCDBG0(L0, "Failed reading a\n");
4285 			return (IPQOS_CONF_ERR);
4286 		}
4287 	}
4288 
4289 	return (IPQOS_CONF_SUCCESS);
4290 }
4291 
4292 /*
4293  * Reads the values of an integer array from fp whose format is:
4294  * '{'RANGE[,RANGE[..]]:VALUE[;RANGE:VALUE[..]]'}', creates an array of size
4295  * arr_size, applies the values to it and points arrp at this array.
4296  * RANGE is one set of array indexes over which this value is to
4297  * be applied, and VALUE either an integer within the range
4298  * llimit - ulimit, or if enum_nvs isn't NULL, an enumeration value
4299  * found in the list enum_nvs. Those values which aren't explicity set
4300  * will be set to -1.
4301  *
4302  * RETURNS: IPQOS_CONF_ERR on resource or parse error, else IPQOS_CONF_SUCCESS.
4303  */
4304 static int
4305 read_int_array(
4306 FILE *fp,
4307 char *first_token,
4308 int **arrp,
4309 uint32_t arr_size,
4310 int llimit,
4311 int ulimit,
4312 str_val_nd_t *enum_nvs)
4313 {
4314 
4315 	char buf[5 * IPQOS_CONF_LINEBUF_SZ];
4316 	char *token;
4317 	char *range;
4318 	char *ranges;
4319 	char *svalue;
4320 	int value;
4321 	int res;
4322 	char *entry;
4323 	char *tmp;
4324 	char *end;
4325 	int lower, upper;
4326 	int x;
4327 	uint32_t startln;
4328 
4329 	IPQOSCDBG4(L0, "In read_int_array: size: %u, lower: %u, upper: %u, "
4330 	    "first_token: %s\n", arr_size, llimit, ulimit, first_token);
4331 
4332 	/*
4333 	 * read beginning curl.
4334 	 */
4335 	if (first_token[0] != CURL_BEGIN) {
4336 		ipqos_msg(MT_ERROR, gettext("\'{\' missing at line "
4337 		    "%u.\n"), lineno);
4338 		return (IPQOS_CONF_ERR);
4339 	}
4340 
4341 	/*
4342 	 * allocate and initialise array for holding read values.
4343 	 */
4344 	*arrp = malloc(arr_size * sizeof (int));
4345 	if (*arrp == NULL) {
4346 		ipqos_msg(MT_ENOSTR, "malloc");
4347 		return (IPQOS_CONF_ERR);
4348 	}
4349 	(void) memset(*arrp, -1, arr_size * sizeof (int));
4350 
4351 	/*
4352 	 * read whole array declaration string into buffer.
4353 	 * this is because readtoken doesn't interpret our
4354 	 * delimeter values specially and may return them
4355 	 * within another string.
4356 	 */
4357 	startln = lineno;	/* store starting lineno for error reports */
4358 	buf[0] = '\0';
4359 	res = readtoken(fp, &token);
4360 	while ((res != IPQOS_CONF_CURL_END) && (res != IPQOS_CONF_ERR) &&
4361 	    (res != IPQOS_CONF_EOF)) {
4362 		(void) strlcat(buf, token, sizeof (buf));
4363 		free(token);
4364 		res = readtoken(fp, &token);
4365 	}
4366 	if (res != IPQOS_CONF_CURL_END) {
4367 		goto array_err;
4368 	}
4369 	IPQOSCDBG1(L0, "array declaration buffer contains: %s\n", buf);
4370 
4371 	/*
4372 	 * loop reading "ranges ':' value;" till end of buffer.
4373 	 */
4374 	entry = strtok(buf, ";");
4375 	while (entry != NULL) {
4376 		svalue = strchr(entry, ':');
4377 		if (svalue == NULL) {	/* missing value string */
4378 			IPQOSCDBG0(L0, "Missing value string\n");
4379 			goto array_err;
4380 		}
4381 		*svalue++ = '\0';
4382 		ranges = entry;
4383 
4384 		/*
4385 		 * get value of number or enumerated symbol.
4386 		 */
4387 		if (enum_nvs) {
4388 			/*
4389 			 * get rid of surrounding whitespace so as not to
4390 			 * confuse read_enum_value.
4391 			 */
4392 			SKIPWS(svalue);
4393 			tmp = svalue;
4394 			while (*tmp != '\0') {
4395 				if (isspace(*tmp)) {
4396 					*tmp = '\0';
4397 					break;
4398 				} else {
4399 					tmp++;
4400 				}
4401 			}
4402 
4403 			/*
4404 			 * read enumeration value.
4405 			 */
4406 			res = read_enum_value(NULL, svalue, enum_nvs,
4407 			    (uint32_t *)&value);
4408 			if (res != IPQOS_CONF_SUCCESS)
4409 				goto array_err;
4410 		} else {
4411 			value = (int)strtol(svalue, &end, 10);
4412 			SKIPWS(end);
4413 			if ((svalue == end) || (*end != NULL)) {
4414 				IPQOSCDBG0(L0, "Invalid value\n");
4415 				goto array_err;
4416 			}
4417 			IPQOSCDBG1(L0, "value: %u\n", value);
4418 
4419 			/*
4420 			 * check value within valid range.
4421 			 */
4422 			if ((value < llimit) || (value > ulimit)) {
4423 				IPQOSCDBG0(L0, "value out of range\n");
4424 				goto array_err;
4425 			}
4426 		}
4427 
4428 		/*
4429 		 * loop reading ranges for this value.
4430 		 */
4431 		range = strtok_r(ranges, ",", &tmp);
4432 		while (range != NULL) {
4433 			res = readrange(range, &lower, &upper);
4434 			if (res != IPQOS_CONF_SUCCESS)
4435 				goto array_err;
4436 			IPQOSCDBG2(L0, "range: %u - %u\n", lower, upper);
4437 
4438 
4439 			if (upper < lower) {
4440 				uint32_t u = lower;
4441 				lower = upper;
4442 				upper = u;
4443 			}
4444 
4445 			/*
4446 			 * check range valid for array size.
4447 			 */
4448 			if ((lower < 0) || (upper > arr_size)) {
4449 				IPQOSCDBG0(L0, "Range out of array "
4450 				    "dimensions\n");
4451 				goto array_err;
4452 			}
4453 
4454 			/*
4455 			 * add this value to array indexes within range.
4456 			 */
4457 			for (x = lower; x <= upper; x++)
4458 				(*arrp)[x] = value;
4459 
4460 			/*
4461 			 * get next range.
4462 			 */
4463 			range = strtok_r(NULL, ",", &tmp);
4464 		}
4465 
4466 		entry = strtok(NULL, ";");
4467 	}
4468 
4469 	return (IPQOS_CONF_SUCCESS);
4470 
4471 array_err:
4472 	ipqos_msg(MT_ERROR,
4473 	    gettext("Array declaration line %u is invalid.\n"), startln);
4474 	free(*arrp);
4475 	return (IPQOS_CONF_ERR);
4476 }
4477 
4478 static int
4479 readllong(char *str, long long *llp, char **lo)
4480 {
4481 
4482 	*llp = strtoll(str, lo, 0);
4483 	if (*lo == str) {
4484 		return (IPQOS_CONF_ERR);
4485 	}
4486 	return (IPQOS_CONF_SUCCESS);
4487 }
4488 
4489 static int
4490 readuint8(char *str, uint8_t *ui8, char **lo)
4491 {
4492 
4493 	long long tmp;
4494 
4495 	if (readllong(str, &tmp, lo) != 0) {
4496 		return (IPQOS_CONF_ERR);
4497 	}
4498 	if (tmp > UCHAR_MAX || tmp < 0) {
4499 		return (IPQOS_CONF_ERR);
4500 	}
4501 	*ui8 = (uint8_t)tmp;
4502 	return (IPQOS_CONF_SUCCESS);
4503 }
4504 
4505 static int
4506 readuint16(char *str, uint16_t *ui16, char **lo)
4507 {
4508 	long long tmp;
4509 
4510 	if (readllong(str, &tmp, lo) != IPQOS_CONF_SUCCESS) {
4511 		return (IPQOS_CONF_ERR);
4512 	}
4513 	if (tmp > USHRT_MAX || tmp < 0) {
4514 		return (IPQOS_CONF_ERR);
4515 	}
4516 	*ui16 = (uint16_t)tmp;
4517 	return (IPQOS_CONF_SUCCESS);
4518 }
4519 
4520 static int
4521 readint16(char *str, int16_t *i16, char **lo)
4522 {
4523 	long long tmp;
4524 
4525 	if (readllong(str, &tmp, lo) != 0) {
4526 		return (IPQOS_CONF_ERR);
4527 	}
4528 	if (tmp > SHRT_MAX || tmp < SHRT_MIN) {
4529 		return (IPQOS_CONF_ERR);
4530 	}
4531 	*i16 = (int16_t)tmp;
4532 	return (IPQOS_CONF_SUCCESS);
4533 }
4534 
4535 static int
4536 readint32(char *str, int *i32, char **lo)
4537 {
4538 	long long tmp;
4539 
4540 	if (readllong(str, &tmp, lo) != IPQOS_CONF_SUCCESS) {
4541 		return (IPQOS_CONF_ERR);
4542 	}
4543 	if (tmp > INT_MAX || tmp < INT_MIN) {
4544 		return (IPQOS_CONF_ERR);
4545 	}
4546 	*i32 = tmp;
4547 	return (IPQOS_CONF_SUCCESS);
4548 }
4549 
4550 static int
4551 readuint32(char *str, uint32_t *ui32, char **lo)
4552 {
4553 	long long tmp;
4554 
4555 	if (readllong(str, &tmp, lo) != IPQOS_CONF_SUCCESS) {
4556 		return (IPQOS_CONF_ERR);
4557 	}
4558 	if (tmp > UINT_MAX || tmp < 0) {
4559 		return (IPQOS_CONF_ERR);
4560 	}
4561 	*ui32 = (uint32_t)tmp;
4562 	return (IPQOS_CONF_SUCCESS);
4563 }
4564 
4565 /*
4566  * retrieves the index associated with the interface named ifname and assigns
4567  * it to the int pointed to by ifindex.
4568  * RETURNS: IPQOS_CONF_ERR on errors, else IPQOS_CONF_SUCCESS.
4569  */
4570 static int
4571 readifindex(
4572 char *ifname,
4573 int *ifindex)
4574 {
4575 
4576 	int s;
4577 	struct lifreq lifrq;
4578 
4579 
4580 	/* open socket */
4581 
4582 	if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
4583 		ipqos_msg(MT_ENOSTR, gettext("opening AF_INET socket"));
4584 		return (IPQOS_CONF_ERR);
4585 	}
4586 
4587 	/* copy ifname into lifreq */
4588 
4589 	(void) strlcpy(lifrq.lifr_name, ifname, LIFNAMSIZ);
4590 
4591 	/* do SIOGLIFINDEX ioctl */
4592 
4593 	if (ioctl(s, SIOCGLIFINDEX, (caddr_t)&lifrq) == -1) {
4594 		(void) close(s);
4595 		return (IPQOS_CONF_ERR);
4596 	}
4597 
4598 	/* Warn if a virtual interface is specified */
4599 	if ((ioctl(s, SIOCGLIFFLAGS, (caddr_t)&lifrq) != -1) &&
4600 	    (lifrq.lifr_flags & IFF_VIRTUAL)) {
4601 		ipqos_msg(MT_WARNING, gettext("Invalid interface"));
4602 	}
4603 	(void) close(s);
4604 	*ifindex = lifrq.lifr_index;
4605 	return (IPQOS_CONF_SUCCESS);
4606 }
4607 
4608 /*
4609  * Case insensitively compares the string in str with IPQOS_CONF_TRUE_STR
4610  * and IPQOS_CONF_FALSE_STR and sets boolean pointed to by bool accordingly.
4611  * RETURNS: if failure to match either IPQOS_CONF_ERR, else IPQOS_CONF_SUCCESS.
4612  */
4613 static int
4614 readbool(char *str, boolean_t *bool)
4615 {
4616 
4617 	if (strcasecmp(str, IPQOS_CONF_TRUE_STR) == 0) {
4618 		*bool = B_TRUE;
4619 	} else if (strcasecmp(str, IPQOS_CONF_FALSE_STR) == 0) {
4620 		*bool = B_FALSE;
4621 	} else {
4622 		return (IPQOS_CONF_ERR);
4623 	}
4624 
4625 	return (IPQOS_CONF_SUCCESS);
4626 }
4627 
4628 /*
4629  * reads a protocol name/number from proto_str and assigns the number
4630  * to the uint8 ref'd by proto.
4631  * RETURNS: If not a valid name or protocol number IPQOS_CONF_ERR, else
4632  * IPQOS_CONF_SUCCESS.
4633  */
4634 static int
4635 readproto(char *proto_str, uint8_t *proto)
4636 {
4637 
4638 	struct protoent *pent;
4639 	char *lo;
4640 	int res;
4641 
4642 	IPQOSCDBG1(L1, "In readproto: string: %s\n", proto_str);
4643 
4644 	/* try name lookup */
4645 
4646 	pent = getprotobyname(proto_str);
4647 	if (pent) {
4648 		*proto = pent->p_proto;
4649 
4650 	/* check valid protocol number */
4651 	} else {
4652 		res = readuint8(proto_str, proto, &lo);
4653 		if (res != IPQOS_CONF_SUCCESS || proto == 0) {
4654 			return (IPQOS_CONF_ERR);
4655 		}
4656 	}
4657 
4658 	return (IPQOS_CONF_SUCCESS);
4659 }
4660 
4661 /*
4662  * reads either a port service, or a port number from port_str and assigns
4663  * the associated port number to short ref'd by port.
4664  * RETURNS: If invalid name and number IPQOS_CONF_ERR, else IPQOS_CONF_SUCCESS.
4665  */
4666 static int
4667 readport(char *port_str, uint16_t *port)
4668 {
4669 
4670 	struct servent *sent;
4671 	char *tmp;
4672 
4673 	IPQOSCDBG1(L1, "In readport: string: %s\n", port_str);
4674 
4675 	/* try service name lookup */
4676 	sent = getservbyname(port_str, NULL);
4677 
4678 	/* failed name lookup so read port number */
4679 	if (sent == NULL) {
4680 		if (readuint16(port_str, port, &tmp) != IPQOS_CONF_SUCCESS ||
4681 		    *port == 0) {
4682 			return (IPQOS_CONF_ERR);
4683 		}
4684 		*port = htons(*port);
4685 	} else {
4686 		*port = sent->s_port;
4687 	}
4688 
4689 	return (IPQOS_CONF_SUCCESS);
4690 }
4691 
4692 
4693 /*
4694  * Reads a curly brace, a string enclosed in double quotes, or a whitespace/
4695  * curly brace delimited string. If a double quote enclosed string the
4696  * closing quotes need to be on the same line.
4697  * RETURNS:
4698  * on reading a CURL_BEGIN token it returns IPQOS_CONF_CURL_BEGIN,
4699  * on reading a CURL_END token it returns IPQOS_CONF_CURL_END,
4700  * on reading another valid token it returns IPQOS_CONF_SUCCESS.
4701  * for each of these token is set to point at the read string.
4702  * at EOF it returns IPQOS_CONF_EOF and if errors it returns IPQOS_CONF_ERR.
4703  */
4704 static int
4705 readtoken(
4706 FILE *fp,
4707 char **token)
4708 {
4709 
4710 	char *st, *tmp;
4711 	int len;
4712 	int quoted = 0;
4713 	char *cmnt;
4714 	char *bpos;
4715 	int rembuf;
4716 
4717 	static char *lo;
4718 	static char *buf = NULL;
4719 	static int bufsize;
4720 
4721 	/* if first call initialize line buf to default size */
4722 
4723 	if (buf == NULL) {
4724 		bufsize = IPQOS_CONF_LINEBUF_SZ;
4725 		buf = malloc(bufsize);
4726 		if (buf == NULL) {
4727 			ipqos_msg(MT_ENOSTR, "malloc");
4728 			return (IPQOS_CONF_ERR);
4729 		}
4730 	}
4731 
4732 	/* set buffer postition and size to use whole buffer */
4733 
4734 	bpos = buf;
4735 	rembuf = bufsize;
4736 
4737 
4738 	/*
4739 	 * loop reading lines until we've read a line with a non-whitespace
4740 	 * char.
4741 	 */
4742 
4743 	do {
4744 		/* if no leftover from previous invocation */
4745 
4746 		if (lo == NULL) {
4747 
4748 			/*
4749 			 * loop reading into buffer doubling if necessary until
4750 			 * we have either read a complete line or reached the
4751 			 * end of file.
4752 			 */
4753 			for (;;) {
4754 				st = fgets(bpos, rembuf, fp);
4755 
4756 				if (st == NULL) {
4757 
4758 					/* if read error */
4759 					if (ferror(fp)) {
4760 						free(buf);
4761 						buf = NULL;
4762 						ipqos_msg(MT_ENOSTR,
4763 						    "fgets");
4764 						return (IPQOS_CONF_ERR);
4765 
4766 					/* end of file */
4767 					} else {
4768 						free(buf);
4769 						buf = NULL;
4770 						*token = NULL;
4771 						return (IPQOS_CONF_EOF);
4772 					}
4773 				} else {
4774 					/* if read a newline */
4775 
4776 					if (buf[strlen(buf) - 1] == '\n') {
4777 						lineno++;
4778 						break;
4779 
4780 					/* if read the last line */
4781 
4782 					} else if (feof(fp)) {
4783 						break;
4784 
4785 					/*
4786 					 * not read a full line so buffer size
4787 					 * is too small, double it and retry.
4788 					 */
4789 					} else {
4790 						bufsize *= 2;
4791 						tmp = realloc(buf, bufsize);
4792 						if (tmp == NULL) {
4793 							ipqos_msg(MT_ENOSTR,
4794 							    "realloc");
4795 							free(buf);
4796 							return (IPQOS_CONF_ERR);
4797 						} else {
4798 							buf = tmp;
4799 						}
4800 
4801 						/*
4802 						 * make parameters to fgets read
4803 						 * into centre of doubled buffer
4804 						 * so we retain what we've
4805 						 * already read.
4806 						 */
4807 						bpos = &buf[(bufsize / 2) - 1];
4808 						rembuf = (bufsize / 2) + 1;
4809 					}
4810 				}
4811 			}
4812 
4813 			st = buf;
4814 
4815 		/* previous leftover, assign to st */
4816 
4817 		} else {
4818 			st = lo;
4819 			lo = NULL;
4820 		}
4821 
4822 		/* truncate at comment */
4823 
4824 		cmnt = strchr(st, '#');
4825 		if (cmnt) {
4826 			*cmnt = '\0';
4827 		}
4828 
4829 		/* Skip any whitespace */
4830 
4831 		while (isspace(*st) && st != '\0') {
4832 			st++;
4833 		}
4834 
4835 	} while (*st == '\0');
4836 
4837 
4838 	/* find end of token */
4839 
4840 	tmp = st;
4841 
4842 	/* if curl advance 1 char */
4843 
4844 	if (*tmp == CURL_BEGIN || *tmp == CURL_END) {
4845 		tmp++;
4846 
4847 
4848 	/* if dbl quote read until matching quote */
4849 
4850 	} else if (*tmp == '"') {
4851 		quoted++;
4852 		tmp = ++st;
4853 
4854 		while (*tmp != '"' && *tmp != '\n' && *tmp != '\0') {
4855 			tmp++;
4856 		}
4857 		if (*tmp != '"') {
4858 			ipqos_msg(MT_ERROR, gettext("Quoted string exceeds "
4859 			    "line, line %u.\n"), lineno);
4860 			free(buf);
4861 			return (IPQOS_CONF_ERR);
4862 		}
4863 
4864 	/* normal token */
4865 	} else {
4866 		/* find first whitespace, curl, newline or string end */
4867 
4868 		while (!isspace(*tmp) && *tmp != CURL_BEGIN &&
4869 		    *tmp != CURL_END && *tmp != '\n' && *tmp != '\0') {
4870 			tmp++;
4871 		}
4872 	}
4873 
4874 	/* copy token to return */
4875 	len = tmp - st;
4876 	*token = malloc(len + 1);
4877 	if (!*token) {
4878 		free(buf);
4879 		ipqos_msg(MT_ENOSTR, "malloc");
4880 		return (IPQOS_CONF_ERR);
4881 	}
4882 	bcopy(st, *token, len);
4883 	(*token)[len] = '\0';
4884 
4885 	/* if just read quoted string remove quote from remaining string */
4886 
4887 	if (quoted) {
4888 		tmp++;
4889 	}
4890 
4891 	/* if not end of string, store rest for latter parsing */
4892 
4893 	if (*tmp != '\0' && *tmp != '\n') {
4894 		lo = tmp;
4895 	}
4896 
4897 	/* for curl_end and curl_begin return special ret codes */
4898 
4899 	if ((*token)[1] == '\0') {
4900 		if (**token == CURL_BEGIN) {
4901 			return (IPQOS_CONF_CURL_BEGIN);
4902 		} else if (**token == CURL_END) {
4903 			return (IPQOS_CONF_CURL_END);
4904 		}
4905 	}
4906 
4907 	return (IPQOS_CONF_SUCCESS);
4908 }
4909 
4910 /*
4911  * Reads an enumeration bitmask definition from line. The format is:
4912  * { NAME=VAL, NAME2=VAL2 }. The resulting names and values are returned.
4913  * RETURNS: NULL on error, else ptr to name/values.
4914  */
4915 static str_val_nd_t *
4916 read_enum_nvs(char *line, char *module_name)
4917 {
4918 
4919 	str_val_nd_t *enum_vals = NULL;
4920 	char *cp;
4921 	char *start;
4922 	char *name = NULL;
4923 	int len;
4924 	uint32_t val;
4925 	int ret;
4926 	int readc;
4927 
4928 	IPQOSCDBG1(L1, "In read_enum_nvs, line: %s\n", line);
4929 
4930 	/* read opening brace */
4931 
4932 	cp = strchr(line, CURL_BEGIN);
4933 	if (cp == NULL) {
4934 		IPQOSCDBG0(L1, "missing curl begin\n");
4935 		goto fail;
4936 	} else {
4937 		start = cp + 1;
4938 	}
4939 
4940 	/*
4941 	 * loop reading 'name = value' entrys seperated by comma until
4942 	 * reach closing brace.
4943 	 */
4944 
4945 	for (;;) {
4946 		SKIPWS(start);
4947 		if (*start == '\0') {
4948 			IPQOSCDBG0(L1, "missing closing bracket\n");
4949 			goto fail;
4950 		}
4951 
4952 		/*
4953 		 * read name - read until whitespace, '=', closing curl,
4954 		 * or string end.
4955 		 */
4956 
4957 		for (cp = start;
4958 		    !isspace(*cp) && *cp != '=' && *cp != CURL_END &&
4959 		    *cp != '\0'; cp++) {}
4960 
4961 		if (*cp == '\0') {
4962 			IPQOSCDBG0(L1, "Unexpected line end in enum def'n\n");
4963 			goto fail;
4964 
4965 		/* finished definition, exit loop */
4966 		} else if (*cp == CURL_END) {
4967 			break;
4968 		}
4969 
4970 		/* store name */
4971 
4972 		len = cp - start;
4973 		name = malloc(len + 1);
4974 		if (name == NULL) {
4975 			ipqos_msg(MT_ENOSTR, "malloc");
4976 			goto fail;
4977 		}
4978 		bcopy(start, name, len);
4979 		name[len] = NULL;
4980 		IPQOSCDBG1(L0, "Stored name: %s\n", name);
4981 
4982 		/* read assignment */
4983 
4984 		start = strchr(cp, '=');
4985 		if (start == NULL) {
4986 			IPQOSCDBG0(L1, "Missing = in enum def'n\n");
4987 			goto fail;
4988 		}
4989 
4990 		/* read value */
4991 
4992 		ret = sscanf(++start, "%x%n", &val, &readc);
4993 		if (ret != 1) {
4994 			IPQOSCDBG1(L1, "sscanf of value failed, string: %s\n",
4995 			    cp);
4996 			goto fail;
4997 		}
4998 
4999 		/* add name value to set */
5000 
5001 		ret = add_str_val_entry(&enum_vals, name, val);
5002 		if (ret != IPQOS_CONF_SUCCESS) {
5003 			IPQOSCDBG0(L1, "Failed to add str_val entry\n");
5004 			goto fail;
5005 		}
5006 		free(name);
5007 		name = NULL;
5008 
5009 		/* try reading comma */
5010 		cp = strchr(start, ',');
5011 
5012 		if (cp != NULL) {
5013 			start = cp + 1;
5014 
5015 		/* no comma, advance to char past value last read */
5016 		} else {
5017 			start += readc;
5018 		}
5019 	}
5020 
5021 	return (enum_vals);
5022 fail:
5023 	free_str_val_entrys(enum_vals);
5024 	if (name != NULL)
5025 		free(name);
5026 
5027 	/* if a parse error */
5028 
5029 	if (errno == 0) {
5030 		ipqos_msg(MT_ERROR, gettext("Types file for module %s is "
5031 		    "corrupt.\n"), module_name);
5032 	}
5033 
5034 	return (NULL);
5035 }
5036 
5037 /*
5038  * Given mapped_list with is a comma seperated list of map names, and value,
5039  * which is used to index into these maps, the function creates x new entries
5040  * in nvpp, where x is the number of map names specified. Each of these
5041  * entries has the value from the map in the position indexed by value and
5042  * with name module.${MAP_NAME}. The maps are contained in the modules config
5043  * file and have the form:
5044  * map map1 uint32 1,23,32,45,3
5045  * As you can see the map values are uint32, and along with uint8 are the
5046  * only supported types at the moment.
5047  *
5048  * RETURNS: IPQOS_CONF_ERR if one of the maps specified in mapped_list
5049  * doesn't exist, if value is not a valid map position for a map, or if
5050  * there's a resource failure. otherwise IPQOS_CONF_SUCCESS is returned.
5051  */
5052 static int
5053 read_mapped_values(
5054 FILE *tfp,
5055 nvlist_t **nvlp,
5056 char *module,
5057 char *mapped_list,
5058 int value)
5059 {
5060 	char *map_name, *lastparam, *tmpname;
5061 	int res;
5062 	ipqos_nvtype_t type;
5063 	char dfltst[IPQOS_VALST_MAXLEN+1] = "";
5064 	str_val_nd_t *enum_nvs;
5065 	place_t place;
5066 
5067 	IPQOSCDBG0(L1, "In read_mapped_values\n");
5068 
5069 	map_name = (char *)strtok_r(mapped_list, ",", &lastparam);
5070 	while (map_name != NULL) {
5071 		char *tokval, *lastval;
5072 		int index = 0;
5073 
5074 		/*
5075 		 * get map info from types file.
5076 		 */
5077 		place = PL_MAP;
5078 		res = readtype(tfp, module, map_name, &type, &enum_nvs,
5079 		    dfltst, B_FALSE, &place);
5080 		if (res != IPQOS_CONF_SUCCESS) {
5081 			return (IPQOS_CONF_ERR);
5082 		}
5083 
5084 		/*
5085 		 * Just keep browsing the list till we get to the element
5086 		 * with the index from the value parameter or the end.
5087 		 */
5088 		tokval = (char *)strtok_r(dfltst, ",", &lastval);
5089 		for (;;) {
5090 			if (tokval == NULL) {
5091 				ipqos_msg(MT_ERROR,
5092 				    gettext("Invalid value, %u, line %u.\n"),
5093 				    value, lineno);
5094 				return (IPQOS_CONF_ERR);
5095 			}
5096 			if (index++ == value) {
5097 				break;
5098 			}
5099 			tokval = (char *)strtok_r(NULL, ",", &lastval);
5100 		}
5101 
5102 
5103 		/*
5104 		 * create fully qualified parameter name for map value.
5105 		 */
5106 		tmpname = prepend_module_name(map_name, module);
5107 		if (tmpname == NULL) {
5108 			return (IPQOS_CONF_ERR);
5109 		}
5110 
5111 		/*
5112 		 * add map value with fqn to parameter nvlist.
5113 		 */
5114 		IPQOSCDBG2(L0, "Adding map %s, value %u to nvlist\n",
5115 		    tmpname, atoi(tokval));
5116 		switch (type) {
5117 			case IPQOS_DATA_TYPE_UINT8: {
5118 				res = nvlist_add_byte(*nvlp, tmpname,
5119 				    (uint8_t)atoi(tokval));
5120 				if (res != 0)  {
5121 					free(tmpname);
5122 					ipqos_msg(MT_ENOSTR,
5123 					    "nvlist_add_uint8");
5124 					return (IPQOS_CONF_ERR);
5125 				}
5126 				break;
5127 			}
5128 			case IPQOS_DATA_TYPE_UINT32: {
5129 				res = nvlist_add_uint32(*nvlp, tmpname,
5130 				    (uint32_t)atoi(tokval));
5131 				if (res != 0)  {
5132 					free(tmpname);
5133 					ipqos_msg(MT_ENOSTR,
5134 					    "nvlist_add_uint32");
5135 					return (IPQOS_CONF_ERR);
5136 				}
5137 				break;
5138 			}
5139 			default: {
5140 				ipqos_msg(MT_ERROR,
5141 				    gettext("Types file for module %s is "
5142 				    "corrupt.\n"), module);
5143 				IPQOSCDBG1(L0, "Unsupported map type for "
5144 				    "parameter %s given in types file.\n",
5145 				    map_name);
5146 				return (IPQOS_CONF_ERR);
5147 			}
5148 		}
5149 		free(tmpname);
5150 
5151 		map_name = (char *)strtok_r(NULL, ",", &lastparam);
5152 	}
5153 
5154 	return (IPQOS_CONF_SUCCESS);
5155 }
5156 
5157 /*
5158  * Parses the string info_str into it's components. Its format is:
5159  * SIZE','[ENUM_DEF | RANGE], where SIZE is the size of the array,
5160  * ENUM_DEF is the definition of the enumeration for this array,
5161  * and RANGE is the set of values this array can accept. In
5162  * the event this array has an enumeration definition enum_nvs is
5163  * set to point at a str_val_nd_t structure which stores the names
5164  * and values associated with this enumeration. Otherwise, if this
5165  * is not an enumerated array, lower and upper are set to the lower
5166  * and upper values of RANGE.
5167  * RETURNS: IPQOS_CONF_ERR due to unexpected parse errors, else
5168  * IPQOS_CONF_SUCCESS.
5169  */
5170 static int
5171 read_int_array_info(
5172 char *info_str,
5173 str_val_nd_t **enum_nvs,
5174 uint32_t *size,
5175 int *lower,
5176 int *upper,
5177 char *module)
5178 {
5179 	int res;
5180 	char *end;
5181 	char *token;
5182 	char *tmp;
5183 
5184 	IPQOSCDBG1(L0, "In read_array_info: info_str: %s\n",
5185 	    (info_str != NULL) ? info_str : "NULL");
5186 
5187 	if (info_str == NULL) {
5188 		IPQOSCDBG0(L0, "Null info string\n");
5189 		goto fail;
5190 	}
5191 
5192 	/*
5193 	 * read size.
5194 	 */
5195 	token = strtok(info_str, ",");
5196 	*size = (uint32_t)strtol(token, &end, 10);
5197 	SKIPWS(end);
5198 	if ((end == token) || (*end != NULL)) {
5199 		IPQOSCDBG0(L0, "Invalid size\n");
5200 		goto fail;
5201 	}
5202 	IPQOSCDBG1(L0, "read size: %u\n", *size);
5203 
5204 	/*
5205 	 * check we have another string.
5206 	 */
5207 	token = strtok(NULL, "\n");
5208 	if (token == NULL) {
5209 		IPQOSCDBG0(L0, "Missing range/enum def\n");
5210 		goto fail;
5211 	}
5212 	IPQOSCDBG1(L0, "range/enum def: %s\n", token);
5213 
5214 	/*
5215 	 * check if enumeration set or integer set and read enumeration
5216 	 * definition or integer range respectively.
5217 	 */
5218 	tmp = strchr(token, CURL_BEGIN);
5219 	if (tmp == NULL) {	/* a numeric range */
5220 		res = readrange(token, lower, upper);
5221 		if (res != IPQOS_CONF_SUCCESS) {
5222 			IPQOSCDBG0(L0, "Failed reading range\n");
5223 			goto fail;
5224 		}
5225 	} else {		/* an enumeration */
5226 		*enum_nvs = read_enum_nvs(token, module);
5227 		if (*enum_nvs == NULL) {
5228 			IPQOSCDBG0(L0, "Failed reading enum def\n");
5229 			goto fail;
5230 		}
5231 	}
5232 
5233 	return (IPQOS_CONF_SUCCESS);
5234 fail:
5235 	ipqos_msg(MT_ERROR,
5236 	    gettext("Types file for module %s is corrupt.\n"), module);
5237 	return (IPQOS_CONF_ERR);
5238 }
5239 
5240 /*
5241  * reads the value of an enumeration parameter from first_token and fp.
5242  * first_token is the first token of the value.
5243  * The format expected is NAME | { NAME1 [, NAME2 ] [, NAME3 ]  }.
5244  * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCESS.
5245  */
5246 static int
5247 read_enum_value(
5248 FILE *fp,
5249 char *first_token,
5250 str_val_nd_t *enum_vals,
5251 uint32_t *val)
5252 {
5253 
5254 	uint32_t u32;
5255 	int ret;
5256 	char *tk;
5257 	char *lo = NULL;
5258 	char *cm;
5259 	int name_expected = 0;
5260 
5261 	IPQOSCDBG0(L1, "In read_enum_value\n");
5262 
5263 	/* init param val */
5264 	*val = 0;
5265 
5266 	/* first token not curl_begin, so lookup its value */
5267 
5268 	if (*first_token != CURL_BEGIN) {
5269 		ret = str_val_list_lookup(enum_vals, first_token, val);
5270 		if (ret != IPQOS_CONF_SUCCESS) {
5271 			ipqos_msg(MT_ERROR,
5272 			    gettext("Unrecognized value, %s, line %u.\n"),
5273 			    first_token, lineno);
5274 			return (ret);
5275 		}
5276 
5277 	/* curl_begin, so read values till curl_end, dicing at ',' */
5278 	} else {
5279 
5280 		name_expected++;
5281 
5282 		for (;;) {
5283 
5284 			/*
5285 			 * no leftover from pervious iteration so read new
5286 			 * token. This leftover happens because readtoken
5287 			 * doesn't interpret comma's as special characters
5288 			 * and thus could return 'val1,val2' as one token.
5289 			 * If this happens the val1 will be used in the
5290 			 * current iteration and what follows saved in lo
5291 			 * for processing by successive iterations.
5292 			 */
5293 
5294 			if (lo == NULL) {
5295 				ret = readtoken(fp, &tk);
5296 				if (ret == IPQOS_CONF_ERR) {
5297 					return (ret);
5298 				} else if (ret == IPQOS_CONF_EOF) {
5299 					ipqos_msg(MT_ERROR,
5300 					    gettext("Unexpected EOF.\n"));
5301 					return (IPQOS_CONF_ERR);
5302 
5303 				}
5304 			} else {	/* previous leftover, so use it */
5305 
5306 				IPQOSCDBG1(L1, "Using leftover %s.\n", lo);
5307 				tk = lo;
5308 				lo = NULL;
5309 			}
5310 
5311 			if (name_expected) {
5312 				if (ret == IPQOS_CONF_CURL_END ||
5313 				    tk[0] == ',') {
5314 					ipqos_msg(MT_ERROR,
5315 					    gettext("Malformed value list "
5316 					    "line %u.\n"), lineno);
5317 					free(tk);
5318 					return (IPQOS_CONF_ERR);
5319 				}
5320 
5321 				/*
5322 				 * check if this token contains a ',' and
5323 				 * if so store it and what follows for next
5324 				 * iteration.
5325 				 */
5326 				cm = strchr(tk, ',');
5327 				if (cm != NULL) {
5328 					lo = malloc(strlen(cm) + 1);
5329 					if (lo == NULL) {
5330 						ipqos_msg(MT_ENOSTR, "malloc");
5331 						free(tk);
5332 						return (IPQOS_CONF_ERR);
5333 					}
5334 
5335 					(void) strcpy(lo, cm);
5336 					*cm = '\0';
5337 				}
5338 
5339 
5340 				/* get name value and add to total val */
5341 
5342 				ret = str_val_list_lookup(enum_vals, tk, &u32);
5343 				if (ret != IPQOS_CONF_SUCCESS) {
5344 					ipqos_msg(MT_ERROR,
5345 					    gettext("Unrecognized value, %s, "
5346 					    "line %u.\n"), tk, lineno);
5347 					free(tk);
5348 					return (IPQOS_CONF_ERR);
5349 				}
5350 
5351 				*val = *val | u32;
5352 				name_expected--;
5353 
5354 			/* comma or curl end accepted */
5355 			} else {
5356 
5357 				/* we've reached curl_end so break */
5358 
5359 				if (ret == IPQOS_CONF_CURL_END) {
5360 					free(tk);
5361 					break;
5362 
5363 				/* not curl end and not comma */
5364 
5365 				} else if (tk[0] != ',') {
5366 					ipqos_msg(MT_ERROR,
5367 					    gettext("Malformed value list "
5368 					    "line %u.\n"), lineno);
5369 					free(tk);
5370 					return (IPQOS_CONF_ERR);
5371 				}
5372 
5373 				/*
5374 				 * store anything after the comma for next
5375 				 * iteration.
5376 				 */
5377 				if (tk[1] != '\0') {
5378 					lo = malloc(strlen(&tk[1]) + 1);
5379 					if (lo == NULL) {
5380 						ipqos_msg(MT_ENOSTR, "malloc");
5381 						free(tk);
5382 						return (IPQOS_CONF_ERR);
5383 					}
5384 					(void) strcpy(lo, &tk[1]);
5385 				}
5386 
5387 				name_expected++;
5388 			}
5389 
5390 			free(tk);
5391 		}
5392 	}
5393 
5394 	IPQOSCDBG1(L1, "value returned is: %u\n", *val);
5395 
5396 	return (IPQOS_CONF_SUCCESS);
5397 }
5398 
5399 /*
5400  * read the set of permanent classes/filter from the types file ref'd by tfp
5401  * and store them in a string table pointed to by perm_items,
5402  * with *nitems getting set to number of items read. perm_filters is set
5403  * to 1 if we're searching for permanent filters, else 0 for classes.
5404  * RETURNS: IPQOS_CONF_ERR if any errors, else IPQOS_CONF_SUCCESS.
5405  */
5406 static int
5407 read_perm_items(
5408 int perm_filters,
5409 FILE *tfp,
5410 char *module_name,
5411 char ***perm_items,
5412 int *nitems)
5413 {
5414 
5415 	char lbuf[IPQOS_CONF_TYPE_LINE_LEN];
5416 	int cnt = 0;
5417 	char name[IPQOS_CONF_NAME_LEN+1];
5418 	char foo[IPQOS_CONF_NAME_LEN+1];
5419 	int res;
5420 	char **items = NULL;
5421 	char **tmp;
5422 	char *marker;
5423 
5424 	IPQOSCDBG0(L1, "In read_perm_items\n");
5425 
5426 
5427 	/* seek to start of types file */
5428 
5429 	if (fseek(tfp, 0, SEEK_SET) != 0) {
5430 		ipqos_msg(MT_ENOSTR, "fseek");
5431 		return (IPQOS_CONF_ERR);
5432 	}
5433 
5434 	/* select which marker were looking for */
5435 
5436 	if (perm_filters) {
5437 		marker = IPQOS_CONF_PERM_FILTER_MK;
5438 	} else {
5439 		marker = IPQOS_CONF_PERM_CLASS_MK;
5440 	}
5441 
5442 	/* scan file line by line till end */
5443 
5444 	while (fgets(lbuf, IPQOS_CONF_TYPE_LINE_LEN, tfp) != NULL) {
5445 
5446 		/*
5447 		 * if the line is marked as containing a default item name
5448 		 * read the name, extend the items string array
5449 		 * and store the string off the array.
5450 		 */
5451 		if (strncmp(lbuf, marker, strlen(marker)) == 0) {
5452 
5453 			res = sscanf(lbuf,
5454 			    "%" VAL2STR(IPQOS_CONF_NAME_LEN) "s"
5455 			    "%" VAL2STR(IPQOS_CONF_NAME_LEN) "s",
5456 			    foo, name);
5457 			if (res < 2) {
5458 				ipqos_msg(MT_ERROR,
5459 				    gettext("Types file for module %s is "
5460 				    "corrupt.\n"), module_name);
5461 				IPQOSCDBG1(L0, "Missing name with a %s.\n",
5462 				    marker);
5463 				goto fail;
5464 			}
5465 
5466 			/* extend items array to accomodate new item */
5467 
5468 			tmp = realloc(items, (cnt + 1) * sizeof (char *));
5469 			if (tmp == NULL) {
5470 				ipqos_msg(MT_ENOSTR, "realloc");
5471 				goto fail;
5472 			} else {
5473 				items = tmp;
5474 			}
5475 
5476 			/* copy and store item name */
5477 
5478 			items[cnt] = malloc(strlen(name) + 1);
5479 			if (items[cnt] == NULL) {
5480 				ipqos_msg(MT_ENOSTR, "malloc");
5481 				goto fail;
5482 			}
5483 
5484 			(void) strcpy(items[cnt], name);
5485 			cnt++;
5486 
5487 
5488 			IPQOSCDBG1(L1, "stored %s in perm items array\n",
5489 			    name);
5490 		}
5491 	}
5492 
5493 	*perm_items = items;
5494 	*nitems = cnt;
5495 
5496 	return (IPQOS_CONF_SUCCESS);
5497 fail:
5498 	for (cnt--; cnt >= 0; cnt--)
5499 		free(items[cnt]);
5500 	free(items);
5501 	return (IPQOS_CONF_ERR);
5502 }
5503 
5504 /*
5505  * Searches types file ref'd by tfp for the parameter named name
5506  * with the place corresponding with place parameter. The format
5507  * of the lines in the file are:
5508  * PLACE NAME TYPE [ ENUM_DEF ] [ DEFAULT_STR ]
5509  * The ENUM_DEF is an enumeration definition and is only present
5510  * for parameters of type enum. DEFAULT_STR is a default value for
5511  * this parameter. If present type is set to the appropriate type
5512  * enumeration and dfltst filled with DEFAULT_STR if one was set.
5513  * Also if the type is enum enum_nvps is made to point at a
5514  * set of name value pairs representing ENUM_DEF.
5515  *
5516  * RETURNS: If any resource errors occur, or a matching parameter
5517  * isn't found IPQOS_CONF_ERR is returned, else IPQOS_CONF_SUCCESS.
5518  */
5519 static int
5520 readtype(
5521 FILE *tfp,
5522 char *module_name,
5523 char *name,
5524 ipqos_nvtype_t *type,
5525 str_val_nd_t **enum_nvps,
5526 char *dfltst,
5527 boolean_t allow_ipgpc_priv,
5528 place_t *place)
5529 {
5530 
5531 	int ac;
5532 	char lbuf[IPQOS_CONF_TYPE_LINE_LEN];
5533 	char param[IPQOS_CONF_PNAME_LEN+1];
5534 	char typest[IPQOS_CONF_TYPE_LEN+1];
5535 	char place_st[IPQOS_CONF_TYPE_LEN+1];
5536 	char *cp;
5537 	int x;
5538 	char *ipgpc_nm;
5539 	int found = 0;
5540 
5541 	IPQOSCDBG1(L1, "In readtype: param: %s\n", name);
5542 
5543 
5544 	/*
5545 	 * if allow_ipgpc_priv is true then we allow ipgpc parameters that are
5546 	 * private between ipqosconf and ipgpc. eg. address masks, port masks.
5547 	 */
5548 	if (allow_ipgpc_priv && strcmp(module_name, IPGPC_NAME) == 0) {
5549 		ipgpc_nm = prepend_module_name(name, IPGPC_NAME);
5550 		if (ipgpc_nm == NULL) {
5551 			return (IPQOS_CONF_ERR);
5552 		}
5553 
5554 		if (strcmp(ipgpc_nm, IPGPC_SADDR_MASK) == 0 ||
5555 		    strcmp(ipgpc_nm, IPGPC_DADDR_MASK) == 0) {
5556 			*type = IPQOS_DATA_TYPE_ADDRESS_MASK;
5557 			return (IPQOS_CONF_SUCCESS);
5558 		} else if (strcmp(ipgpc_nm, IPGPC_SPORT_MASK) == 0 ||
5559 		    strcmp(ipgpc_nm, IPGPC_DPORT_MASK) == 0) {
5560 			*type = IPQOS_DATA_TYPE_UINT16;
5561 			return (IPQOS_CONF_SUCCESS);
5562 		} else if (strcmp(ipgpc_nm, IPGPC_FILTER_TYPE) == 0) {
5563 			*type = IPQOS_DATA_TYPE_UINT32;
5564 			return (IPQOS_CONF_SUCCESS);
5565 		} else if (strcmp(ipgpc_nm, IPGPC_IF_INDEX) == 0) {
5566 			*type = IPQOS_DATA_TYPE_IFINDEX;
5567 			return (IPQOS_CONF_SUCCESS);
5568 		}
5569 
5570 		free(ipgpc_nm);
5571 	}
5572 
5573 	/*
5574 	 * read upto and including module version line.
5575 	 */
5576 	if (read_tfile_ver(tfp, IPQOS_MOD_STR, module_name) == -1)
5577 		return (IPQOS_CONF_ERR);
5578 
5579 
5580 	/*
5581 	 * loop reading lines of the types file until named parameter
5582 	 * found or EOF.
5583 	 */
5584 	while (fgets(lbuf, IPQOS_CONF_TYPE_LINE_LEN, tfp) != NULL) {
5585 
5586 		/*
5587 		 * check whether blank or commented line; if so skip
5588 		 */
5589 		for (cp = lbuf; isspace(*cp) && *cp != '\0'; cp++) {}
5590 		if (*cp == '\0' || *cp == '#') {
5591 			continue;
5592 		}
5593 
5594 		dfltst[0] = '\0';
5595 
5596 		/*
5597 		 * read place, param, type and if present default str
5598 		 * from line.
5599 		 */
5600 		ac = sscanf(lbuf,
5601 		    "%" VAL2STR(IPQOS_CONF_TYPE_LEN) "s "
5602 		    "%" VAL2STR(IPQOS_CONF_PNAME_LEN) "s "
5603 		    "%" VAL2STR(IPQOS_CONF_TYPE_LEN) "s "
5604 		    "%" VAL2STR(IPQOS_VALST_MAXLEN) "s",
5605 		    place_st, param, typest, dfltst);
5606 		if (ac < 3) {
5607 			ipqos_msg(MT_ERROR,
5608 			    gettext("Types file for module %s is corrupt.\n"),
5609 			    module_name);
5610 			IPQOSCDBG0(L0, "sscanf failed to read 3 strings.\n");
5611 			return (IPQOS_CONF_ERR);
5612 		}
5613 
5614 		/*
5615 		 * if the place and name match no need to look any further.
5616 		 */
5617 		if ((*place == PL_ANY) ||
5618 		    ((*place == PL_PARAMS) &&
5619 		    strcmp(place_st, IPQOS_PLACE_PRM_STR) == 0) ||
5620 		    ((*place == PL_FILTER) &&
5621 		    strcmp(place_st, IPQOS_PLACE_FILTER_STR) == 0) ||
5622 		    ((*place == PL_MAP) &&
5623 		    strcmp(place_st, IPQOS_PLACE_MAP_STR) == 0)) {
5624 			if (strcmp(param, name) == 0) {
5625 				found++;
5626 				break;
5627 			}
5628 		}
5629 	}
5630 	if (found == 0) {
5631 		ipqos_msg(MT_ERROR,
5632 		    gettext("Invalid parameter, %s, line %u.\n"), name,
5633 		    lineno);
5634 		return (IPQOS_CONF_ERR);
5635 	}
5636 
5637 	/*
5638 	 * set the place parameter to the actual place when the PL_ANY flag
5639 	 * was set.
5640 	 */
5641 	if (*place == PL_ANY) {
5642 		if (strcmp(place_st, IPQOS_PLACE_PRM_STR) == 0) {
5643 			*place = PL_PARAMS;
5644 		} else if (strcmp(place_st, IPQOS_PLACE_FILTER_STR) == 0) {
5645 			*place = PL_FILTER;
5646 		} else if (strcmp(place_st, IPQOS_PLACE_MAP_STR) == 0) {
5647 			*place = PL_MAP;
5648 		}
5649 	}
5650 
5651 	/*
5652 	 * get type enumeration
5653 	 */
5654 	for (x = 0; nv_types[x].string[0]; x++) {
5655 		if (strcmp(nv_types[x].string, typest) == 0) {
5656 			break;
5657 		}
5658 	}
5659 	/*
5660 	 * check that we have a type corresponding with the one the types
5661 	 * file specifies.
5662 	 */
5663 	if (nv_types[x].string[0] == '\0') {
5664 		ipqos_msg(MT_ERROR,
5665 		    gettext("Types file for module %s is corrupt.\n"),
5666 		    module_name);
5667 		return (IPQOS_CONF_ERR);
5668 	}
5669 	*type = nv_types[x].value;
5670 
5671 	/*
5672 	 * if enumeration type get set of name/vals and any default value
5673 	 */
5674 	if (*type == IPQOS_DATA_TYPE_ENUM) {
5675 		*enum_nvps = read_enum_nvs(lbuf, module_name);
5676 		if (*enum_nvps == NULL) {
5677 			return (IPQOS_CONF_ERR);
5678 		}
5679 
5680 		dfltst[0] = '\0';
5681 		cp = strchr(lbuf, CURL_END);
5682 		(void) sscanf(++cp,
5683 		    "%" VAL2STR(IPQOS_VALST_MAXLEN) "s", dfltst);
5684 	}
5685 
5686 
5687 	IPQOSCDBG2(L1, "read type: %s default: %s\n", nv_types[x].string,
5688 	    *dfltst ? dfltst : "None");
5689 	return (IPQOS_CONF_SUCCESS);
5690 }
5691 
5692 
5693 /*
5694  * Reads a name and a value from file ref'd by cfp into list indirectly
5695  * ref'd by nvlp; If this list is NULL it will be created to accomodate
5696  * the name/value. The name must be either a special token for
5697  * for the place, or be present in the module types file ref'd by tfp.
5698  * *type is set to the enumeration of the type of the parameter and
5699  * nvp to point at the element with the nvlp ref'd list.
5700  * RETURNS: IPQOS_CONF_CURL_END if read CURL_END as name,
5701  * IPQOS_CONF_ERR on errors, else IPQOS_CONF_SUCCESS.
5702  */
5703 static int
5704 readnvpair(
5705 FILE *cfp,
5706 FILE *tfp,
5707 nvlist_t **nvlp,
5708 nvpair_t **nvp,
5709 ipqos_nvtype_t *type,
5710 place_t place,
5711 char *module_name)
5712 {
5713 
5714 	char *name = NULL;
5715 	char *valst = NULL;
5716 	int res;
5717 	char *tmp;
5718 	str_val_nd_t *enum_nvs = NULL;
5719 	char dfltst[IPQOS_VALST_MAXLEN+1];
5720 
5721 	IPQOSCDBG0(L1, "in readnvpair\n");
5722 
5723 	/*
5724 	 * read nvpair name
5725 	 */
5726 	res = readtoken(cfp, &name);
5727 
5728 	/*
5729 	 * if reached eof, curl end or error encountered return to caller
5730 	 */
5731 	if (res == IPQOS_CONF_EOF) {
5732 		ipqos_msg(MT_ERROR, gettext("Unexpected EOF.\n"));
5733 		return (IPQOS_CONF_ERR);
5734 	} else if (res == IPQOS_CONF_ERR) {
5735 		return (res);
5736 	} else if (res == IPQOS_CONF_CURL_END) {
5737 		free(name);
5738 		return (res);
5739 	}
5740 
5741 	/*
5742 	 * read nvpair value
5743 	 */
5744 	res = readtoken(cfp, &valst);
5745 
5746 	/*
5747 	 * check we've read a valid value
5748 	 */
5749 	if (res != IPQOS_CONF_SUCCESS && res != IPQOS_CONF_CURL_BEGIN) {
5750 		if (res == IPQOS_CONF_EOF) {
5751 			ipqos_msg(MT_ERROR, gettext("Unexpected EOF.\n"));
5752 		} else if (res == IPQOS_CONF_CURL_END) {
5753 			ipqos_msg(MT_ERROR,
5754 			    gettext("Missing parameter value line %u.\n"),
5755 			    lineno);
5756 			free(valst);
5757 		}	/* we do nothing special for IPQOS_CONF_ERR */
5758 		free(name);
5759 		return (IPQOS_CONF_ERR);
5760 	}
5761 
5762 	/*
5763 	 * check for generic parameters.
5764 	 */
5765 
5766 	if ((place == PL_CLASS) &&
5767 	    strcmp(name, IPQOS_CONF_NEXT_ACTION_STR) == 0) {
5768 		*type = IPQOS_DATA_TYPE_ACTION;
5769 
5770 	} else if (place == PL_PARAMS &&
5771 	    strcmp(name, IPQOS_CONF_GLOBAL_STATS_STR) == 0 ||
5772 	    place == PL_CLASS &&
5773 	    strcmp(name, IPQOS_CONF_STATS_ENABLE_STR) == 0) {
5774 		*type = IPQOS_DATA_TYPE_BOOLEAN;
5775 
5776 	} else if (tfp == NULL ||
5777 	    ((place != PL_PARAMS) && strcmp(name, IPQOS_CONF_NAME_STR) == 0) ||
5778 	    (place == PL_FILTER) && (strcmp(name, IPQOS_CONF_CLASS_STR) ==
5779 	    0) ||
5780 	    (place == PL_ACTION) && (strcmp(name, IPQOS_CONF_MODULE_STR) ==
5781 	    0)) {
5782 		*type = IPQOS_DATA_TYPE_STRING;
5783 
5784 	} else {	/* if not generic parameter */
5785 		/*
5786 		 * get type from types file
5787 		 */
5788 		if (readtype(tfp, module_name, name, type, &enum_nvs, dfltst,
5789 		    B_FALSE, &place) != IPQOS_CONF_SUCCESS) {
5790 			free(name);
5791 			free(valst);
5792 			return (IPQOS_CONF_ERR);
5793 		}
5794 
5795 		/*
5796 		 * get full module prefix parameter name
5797 		 */
5798 		tmp = name;
5799 		if ((name = prepend_module_name(name, module_name)) == NULL) {
5800 			name = tmp;
5801 			goto fail;
5802 		}
5803 		free(tmp);
5804 	}
5805 
5806 	IPQOSCDBG3(L1, "NVP, name: %s, str_value: %s, type: %s\n", name,
5807 	    valst, nv_types[*type].string);
5808 
5809 
5810 	/*
5811 	 * create nvlist if not present already
5812 	 */
5813 	if (*nvlp == NULL) {
5814 		res = nvlist_alloc(nvlp, NV_UNIQUE_NAME, 0);
5815 		if (res != 0) {
5816 			ipqos_msg(MT_ENOSTR, "nvlist_alloc");
5817 			free(name);
5818 			free(valst);
5819 			return (IPQOS_CONF_ERR);
5820 		}
5821 	}
5822 
5823 	/*
5824 	 * check we haven't already read this parameter
5825 	 */
5826 	if (find_nvpair(*nvlp, name)) {
5827 		ipqos_msg(MT_ERROR, gettext("Duplicate parameter line %u.\n"),
5828 		    lineno);
5829 		goto fail;
5830 	}
5831 
5832 	/*
5833 	 * convert value string to appropriate type and add to nvlist
5834 	 */
5835 
5836 	switch (*type) {
5837 		case IPQOS_DATA_TYPE_IFNAME: {
5838 			uint32_t ifidx;
5839 
5840 			res = readifindex(valst, (int *)&ifidx);
5841 			if (res == IPQOS_CONF_SUCCESS) {
5842 				res = nvlist_add_uint32(*nvlp, IPGPC_IF_INDEX,
5843 				    ifidx);
5844 				if (res != 0) {
5845 					ipqos_msg(MT_ENOSTR,
5846 					    "nvlist_add_uint32");
5847 					goto fail;
5848 				}
5849 				(void) nvlist_remove_all(*nvlp, name);
5850 				/*
5851 				 * change name to point at the name of the
5852 				 * new ifindex nvlist entry as name is used
5853 				 * later in the function.
5854 				 */
5855 				free(name);
5856 				name = malloc(strlen(IPGPC_IF_INDEX) + 1);
5857 				if (name == NULL) {
5858 					ipqos_msg(MT_ENOSTR, "malloc");
5859 					goto fail;
5860 				}
5861 				(void) strcpy(name, IPGPC_IF_INDEX);
5862 			}
5863 			break;
5864 		}
5865 		case IPQOS_DATA_TYPE_PROTO: {
5866 			uint8_t proto;
5867 
5868 			res = readproto(valst, &proto);
5869 			if (res == IPQOS_CONF_SUCCESS) {
5870 				res = nvlist_add_byte(*nvlp, name, proto);
5871 				if (res != 0) {
5872 					ipqos_msg(MT_ENOSTR, "nvlist_add_byte");
5873 					goto fail;
5874 				}
5875 			}
5876 			break;
5877 		}
5878 		case IPQOS_DATA_TYPE_PORT: {
5879 			uint16_t port;
5880 
5881 			res = readport(valst, &port);
5882 			if (res == IPQOS_CONF_SUCCESS) {
5883 
5884 				/* add port */
5885 
5886 				res = nvlist_add_uint16(*nvlp, name, port);
5887 				if (res != 0) {
5888 					ipqos_msg(MT_ENOSTR,
5889 					    "nvlist_add_uint16");
5890 					goto fail;
5891 				}
5892 
5893 				/* add appropriate all ones port mask */
5894 
5895 				if (strcmp(name, IPGPC_DPORT) == 0) {
5896 					res = nvlist_add_uint16(*nvlp,
5897 					    IPGPC_DPORT_MASK, ~0);
5898 
5899 				} else if (strcmp(name, IPGPC_SPORT) == 0) {
5900 					res = nvlist_add_uint16(*nvlp,
5901 					    IPGPC_SPORT_MASK, ~0);
5902 				}
5903 				if (res != 0) {
5904 					ipqos_msg(MT_ENOSTR,
5905 					    "nvlist_add_uint16");
5906 					goto fail;
5907 				}
5908 			}
5909 			break;
5910 		}
5911 		case IPQOS_DATA_TYPE_ADDRESS:
5912 		case IPQOS_DATA_TYPE_ACTION:
5913 		case IPQOS_DATA_TYPE_STRING:
5914 			res = nvlist_add_string(*nvlp, name, valst);
5915 			if (res != 0) {
5916 				ipqos_msg(MT_ENOSTR, "nvlist_add_string");
5917 				goto fail;
5918 			}
5919 			break;
5920 		case IPQOS_DATA_TYPE_BOOLEAN: {
5921 			boolean_t b;
5922 
5923 			res = readbool(valst, &b);
5924 			if (res == IPQOS_CONF_SUCCESS) {
5925 				res = nvlist_add_uint32(*nvlp, name,
5926 				    (uint32_t)b);
5927 				if (res != 0) {
5928 					ipqos_msg(MT_ENOSTR,
5929 					    "nvlist_add_uint32");
5930 					goto fail;
5931 				}
5932 			}
5933 			break;
5934 		}
5935 		case IPQOS_DATA_TYPE_UINT8: {
5936 			uint8_t u8;
5937 
5938 			res = readuint8(valst, &u8, &tmp);
5939 			if (res == IPQOS_CONF_SUCCESS) {
5940 				res = nvlist_add_byte(*nvlp, name, u8);
5941 				if (res != 0) {
5942 					ipqos_msg(MT_ENOSTR, "nvlist_add_byte");
5943 					goto fail;
5944 				}
5945 			}
5946 			break;
5947 		}
5948 		case IPQOS_DATA_TYPE_INT16: {
5949 			int16_t i16;
5950 
5951 			res = readint16(valst, &i16, &tmp);
5952 			if (res == IPQOS_CONF_SUCCESS) {
5953 				res = nvlist_add_int16(*nvlp, name, i16);
5954 				if (res != 0) {
5955 					ipqos_msg(MT_ENOSTR,
5956 					    "nvlist_add_int16");
5957 					goto fail;
5958 				}
5959 			}
5960 			break;
5961 		}
5962 		case IPQOS_DATA_TYPE_UINT16: {
5963 			uint16_t u16;
5964 
5965 			res = readuint16(valst, &u16, &tmp);
5966 			if (res == IPQOS_CONF_SUCCESS) {
5967 				res = nvlist_add_uint16(*nvlp, name, u16);
5968 				if (res != 0) {
5969 					ipqos_msg(MT_ENOSTR,
5970 					    "nvlist_add_int16");
5971 					goto fail;
5972 				}
5973 			}
5974 			break;
5975 		}
5976 		case IPQOS_DATA_TYPE_INT32: {
5977 			int i32;
5978 
5979 			res = readint32(valst, &i32, &tmp);
5980 			if (res == IPQOS_CONF_SUCCESS) {
5981 				res = nvlist_add_int32(*nvlp, name, i32);
5982 				if (res != 0) {
5983 					ipqos_msg(MT_ENOSTR,
5984 					    "nvlist_add_int32");
5985 					goto fail;
5986 				}
5987 			}
5988 			break;
5989 		}
5990 		case IPQOS_DATA_TYPE_UINT32: {
5991 			uint32_t u32;
5992 
5993 			res = readuint32(valst, &u32, &tmp);
5994 			if (res == IPQOS_CONF_SUCCESS) {
5995 				res = nvlist_add_uint32(*nvlp, name, u32);
5996 				if (res != 0) {
5997 					ipqos_msg(MT_ENOSTR,
5998 					    "nvlist_add_uint32");
5999 					goto fail;
6000 				}
6001 			}
6002 			break;
6003 		}
6004 		case IPQOS_DATA_TYPE_ENUM: {
6005 			uint32_t val;
6006 
6007 			res = read_enum_value(cfp, valst, enum_nvs, &val);
6008 			if (res == IPQOS_CONF_SUCCESS) {
6009 				res = nvlist_add_uint32(*nvlp, name, val);
6010 				if (res != 0) {
6011 					ipqos_msg(MT_ENOSTR,
6012 					    "nvlist_add_uint32");
6013 					goto fail;
6014 				}
6015 			} else {
6016 				goto fail;
6017 			}
6018 			break;
6019 		}
6020 		/*
6021 		 * For now the dfltst contains a comma separated list of the
6022 		 * type we need this parameter to be mapped to.
6023 		 * read_mapped_values will fill in all the mapped parameters
6024 		 * and their values in the nvlist.
6025 		 */
6026 		case IPQOS_DATA_TYPE_M_INDEX: {
6027 			uint8_t u8;
6028 
6029 			res = readuint8(valst, &u8, &tmp);
6030 			if (res == IPQOS_CONF_SUCCESS) {
6031 				res = nvlist_add_byte(*nvlp, name, u8);
6032 				if (res != 0) {
6033 					ipqos_msg(MT_ENOSTR,
6034 					    "nvlist_add_uint8");
6035 					goto fail;
6036 				}
6037 			} else {
6038 				*type = IPQOS_DATA_TYPE_UINT8;
6039 				break;
6040 			}
6041 			res = read_mapped_values(tfp, nvlp, module_name,
6042 			    dfltst, u8);
6043 			if (res != IPQOS_CONF_SUCCESS) {
6044 				goto fail;
6045 			}
6046 			break;
6047 		}
6048 		case IPQOS_DATA_TYPE_INT_ARRAY: {
6049 			str_val_nd_t *arr_enum_nvs = NULL;
6050 			uint32_t size;
6051 			int llimit = 0, ulimit = 0;
6052 			int *arr;
6053 
6054 			/*
6055 			 * read array info from types file.
6056 			 */
6057 			res = read_int_array_info(dfltst, &arr_enum_nvs, &size,
6058 			    &llimit, &ulimit, module_name);
6059 			if (res != IPQOS_CONF_SUCCESS) {
6060 				goto fail;
6061 			}
6062 
6063 			/*
6064 			 * read array contents from config file and construct
6065 			 * array with them.
6066 			 */
6067 			res = read_int_array(cfp, valst, &arr, size, llimit,
6068 			    ulimit, arr_enum_nvs);
6069 			if (res != IPQOS_CONF_SUCCESS) {
6070 				goto fail;
6071 			}
6072 
6073 			/*
6074 			 * add array to nvlist.
6075 			 */
6076 			res = nvlist_add_int32_array(*nvlp, name, arr, size);
6077 			if (res != 0) {
6078 				ipqos_msg(MT_ENOSTR, "nvlist_add_int32");
6079 				goto fail;
6080 			}
6081 
6082 			/*
6083 			 * free uneeded resources.
6084 			 */
6085 			free(arr);
6086 			if (arr_enum_nvs)
6087 				free_str_val_entrys(arr_enum_nvs);
6088 
6089 			break;
6090 		}
6091 		case IPQOS_DATA_TYPE_USER: {
6092 			uid_t uid;
6093 
6094 			res = readuser(valst, &uid);
6095 			if (res == IPQOS_CONF_SUCCESS) {
6096 				res = nvlist_add_int32(*nvlp, name, (int)uid);
6097 				if (res != 0) {
6098 					ipqos_msg(MT_ENOSTR,
6099 					    "nvlist_add_int32");
6100 					goto fail;
6101 				}
6102 			}
6103 			break;
6104 		}
6105 #ifdef	_IPQOS_CONF_DEBUG
6106 		default: {
6107 			/*
6108 			 * we shouldn't have a type that doesn't have a switch
6109 			 * entry.
6110 			 */
6111 			assert(1);
6112 		}
6113 #endif
6114 	}
6115 	if (res != 0) {
6116 		ipqos_msg(MT_ERROR, gettext("Invalid %s, line %u.\n"),
6117 		    nv_types[*type].string, lineno);
6118 		goto fail;
6119 	}
6120 
6121 	/* set the nvp parameter to point at the newly added nvlist entry */
6122 
6123 	*nvp = find_nvpair(*nvlp, name);
6124 
6125 	free(name);
6126 	free(valst);
6127 	if (enum_nvs)
6128 		free_str_val_entrys(enum_nvs);
6129 	return (IPQOS_CONF_SUCCESS);
6130 fail:
6131 	if (name != NULL)
6132 		free(name);
6133 	if (valst != NULL)
6134 		free(valst);
6135 	if (enum_nvs != NULL)
6136 		free_str_val_entrys(enum_nvs);
6137 	return (IPQOS_CONF_ERR);
6138 }
6139 
6140 /*
6141  * read a parameter clause from cfp into *params.
6142  * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCES.
6143  */
6144 static int
6145 readparams(
6146 FILE *cfp,
6147 FILE *tfp,
6148 char *module_name,
6149 ipqos_conf_params_t *params)
6150 {
6151 
6152 	int res;
6153 	nvpair_t *nvp;
6154 	ipqos_nvtype_t type;
6155 	boolean_t bl;
6156 	char *nm;
6157 	char *action;
6158 	char tmp[IPQOS_CONF_PNAME_LEN];
6159 	int read_stats = 0;
6160 
6161 	IPQOSCDBG0(L0, "in readparams\n");
6162 
6163 	/* read beginning curl */
6164 
6165 	res = read_curl_begin(cfp);
6166 	if (res != IPQOS_CONF_SUCCESS) {
6167 		return (res);
6168 	}
6169 
6170 	/*
6171 	 * loop reading nvpairs, adding to params nvlist until encounter
6172 	 * CURL_END.
6173 	 */
6174 	for (;;) {
6175 		/* read nvpair */
6176 
6177 		res = readnvpair(cfp, tfp, &params->nvlist,
6178 		    &nvp, &type, PL_PARAMS, module_name);
6179 		if (res == IPQOS_CONF_ERR) {
6180 			goto fail;
6181 
6182 		/* we have finished reading params */
6183 
6184 		} else if (res == IPQOS_CONF_CURL_END) {
6185 			break;
6186 		}
6187 
6188 		/*
6189 		 * read global stats - place into params struct and remove
6190 		 * from nvlist.
6191 		 */
6192 		if (strcmp(nvpair_name(nvp), IPQOS_CONF_GLOBAL_STATS_STR) ==
6193 		    0) {
6194 			/* check we haven't read stats before */
6195 
6196 			if (read_stats) {
6197 				ipqos_msg(MT_ERROR,
6198 				    gettext("Duplicate parameter line %u.\n"),
6199 				    lineno);
6200 				goto fail;
6201 			}
6202 			read_stats++;
6203 
6204 			(void) nvpair_value_uint32(nvp, (uint32_t *)&bl);
6205 			params->stats_enable = bl;
6206 			(void) nvlist_remove_all(params->nvlist,
6207 			    IPQOS_CONF_GLOBAL_STATS_STR);
6208 
6209 
6210 		/*
6211 		 * read action type parameter - add it to list of action refs.
6212 		 * also, if it's one of continue or drop virtual actions
6213 		 * change the action name to their special ipp names in
6214 		 * the action ref list and the nvlist.
6215 		 */
6216 		} else if (type == IPQOS_DATA_TYPE_ACTION) {
6217 
6218 			/* get name and value from nvlist */
6219 
6220 			nm = nvpair_name(nvp);
6221 			(void) nvpair_value_string(nvp, &action);
6222 
6223 			/* if virtual action names change to ipp name */
6224 
6225 			if ((strcmp(action, IPQOS_CONF_CONT_STR) == 0) ||
6226 			    strcmp(action, IPQOS_CONF_DROP_STR) == 0) {
6227 				/*
6228 				 * we copy nm to a seperate buffer as nv_pair
6229 				 * name above gave us a ptr to internal
6230 				 * memory which causes strange behaviour
6231 				 * when we re-value that nvlist element.
6232 				 */
6233 				(void) strlcpy(tmp, nm, sizeof (tmp));
6234 				nm = tmp;
6235 
6236 
6237 				/* modify nvlist entry and change action */
6238 
6239 				if (strcmp(action, IPQOS_CONF_CONT_STR) == 0) {
6240 					action = IPP_ANAME_CONT;
6241 					res = nvlist_add_string(params->nvlist,
6242 					    nm, action);
6243 				} else {
6244 					action = IPP_ANAME_DROP;
6245 					res = nvlist_add_string(params->nvlist,
6246 					    nm, action);
6247 				}
6248 				if (res != 0) {
6249 					ipqos_msg(MT_ENOSTR,
6250 					    "nvlist_add_string");
6251 					goto fail;
6252 				}
6253 			}
6254 
6255 			/* add action reference to params */
6256 
6257 			res = add_aref(&params->actions, nm, action);
6258 		}
6259 	}
6260 
6261 	return (IPQOS_CONF_SUCCESS);
6262 fail:
6263 
6264 	if (params->nvlist) {
6265 		nvlist_free(params->nvlist);
6266 		params->nvlist = NULL;
6267 	}
6268 	if (params->actions) {
6269 		free_arefs(params->actions);
6270 		params->actions = NULL;
6271 	}
6272 	return (IPQOS_CONF_ERR);
6273 }
6274 
6275 /* ************************* class manip fns ****************************** */
6276 
6277 
6278 
6279 /*
6280  * make dst point at a dupicate class struct with duplicate elements to src.
6281  * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCESS.
6282  */
6283 static int
6284 dup_class(
6285 ipqos_conf_class_t *src,
6286 ipqos_conf_class_t **dst)
6287 {
6288 
6289 	ipqos_conf_class_t *cls;
6290 	int res;
6291 
6292 	IPQOSCDBG1(DIFF, "In dup_class: class: %s\n", src->name);
6293 	cls = alloc_class();
6294 	if (cls == NULL) {
6295 		return (IPQOS_CONF_ERR);
6296 	}
6297 
6298 	/* struct copy */
6299 	*cls = *src;
6300 
6301 	/* we're not interested in the nvlist for a class */
6302 	cls->nvlist = NULL;
6303 
6304 
6305 	/* copy first action reference */
6306 	cls->alist = NULL;
6307 	res = add_aref(&cls->alist, src->alist->field, src->alist->name);
6308 	if (res != IPQOS_CONF_SUCCESS) {
6309 		free(cls);
6310 		return (res);
6311 	}
6312 
6313 	*dst = cls;
6314 
6315 	return (IPQOS_CONF_SUCCESS);
6316 }
6317 
6318 /*
6319  * create a zero'd class struct and return a ptr to it.
6320  * RETURNS: ptr to struct on success, NULL otherwise.
6321  */
6322 static ipqos_conf_class_t *
6323 alloc_class()
6324 {
6325 
6326 	ipqos_conf_class_t *class;
6327 
6328 	class = malloc(sizeof (ipqos_conf_class_t));
6329 	if (class) {
6330 		bzero(class, sizeof (ipqos_conf_class_t));
6331 	} else {
6332 		ipqos_msg(MT_ENOSTR, "malloc");
6333 	}
6334 
6335 	return (class);
6336 }
6337 
6338 /* frees up all memory occupied by a filter struct and its contents. */
6339 static void
6340 free_class(ipqos_conf_class_t *cls)
6341 {
6342 
6343 	if (cls == NULL)
6344 		return;
6345 
6346 	/* free its nvlist if present */
6347 
6348 	if (cls->nvlist)
6349 		nvlist_free(cls->nvlist);
6350 
6351 	/* free its action refs if present */
6352 
6353 	if (cls->alist)
6354 		free_arefs(cls->alist);
6355 
6356 	/* finally free class itself */
6357 	free(cls);
6358 }
6359 
6360 /*
6361  * Checks whether there is a class called class_nm  in classes list.
6362  * RETURNS: ptr to first matched class, else if not matched NULL.
6363  */
6364 static ipqos_conf_class_t *
6365 classexist(
6366 char *class_nm,
6367 ipqos_conf_class_t *classes)
6368 {
6369 
6370 	ipqos_conf_class_t *cls;
6371 
6372 	IPQOSCDBG1(L1, "In classexist: name: %s\n", class_nm);
6373 
6374 	for (cls = classes; cls; cls = cls->next) {
6375 		if (strcmp(class_nm, cls->name) == 0) {
6376 			break;
6377 		}
6378 	}
6379 
6380 	return (cls);
6381 }
6382 
6383 
6384 
6385 /* ************************** filter manip fns **************************** */
6386 
6387 
6388 
6389 /*
6390  * Checks whether there is a filter called filter_nm with instance number
6391  * instance in filters list created by us or permanent. Instance value -1
6392  * is a wildcard.
6393  * RETURNS: ptr to first matched filter, else if not matched NULL.
6394  */
6395 static ipqos_conf_filter_t *
6396 filterexist(
6397 char *filter_nm,
6398 int instance,
6399 ipqos_conf_filter_t *filters)
6400 {
6401 
6402 	IPQOSCDBG2(L1, "In filterexist: name :%s, inst: %d\n", filter_nm,
6403 	    instance);
6404 
6405 	while (filters) {
6406 		if (strcmp(filters->name, filter_nm) == 0 &&
6407 		    (instance == -1 || filters->instance == instance) &&
6408 		    (filters->originator == IPP_CONFIG_IPQOSCONF ||
6409 		    filters->originator == IPP_CONFIG_PERMANENT)) {
6410 			break;
6411 		}
6412 		filters = filters->next;
6413 	}
6414 	return (filters);
6415 }
6416 
6417 /*
6418  * allocate and zero a filter structure.
6419  * RETURNS: NULL on error, else ptr to filter struct.
6420  */
6421 static ipqos_conf_filter_t *
6422 alloc_filter()
6423 {
6424 
6425 	ipqos_conf_filter_t *flt;
6426 
6427 	flt = malloc(sizeof (ipqos_conf_filter_t));
6428 	if (flt) {
6429 		bzero(flt, sizeof (ipqos_conf_filter_t));
6430 		flt->instance = -1;
6431 	} else {
6432 		ipqos_msg(MT_ENOSTR, "malloc");
6433 	}
6434 
6435 	return (flt);
6436 }
6437 
6438 /* free flt and all it's contents. */
6439 
6440 static void
6441 free_filter(ipqos_conf_filter_t *flt)
6442 {
6443 
6444 	IPQOSCDBG2(L1, "In free_filter: filter: %s, inst: %d\n", flt->name,
6445 	    flt->instance);
6446 
6447 	if (flt == NULL)
6448 		return;
6449 
6450 	if (flt->src_nd_name)
6451 		free(flt->src_nd_name);
6452 	if (flt->dst_nd_name)
6453 		free(flt->dst_nd_name);
6454 	if (flt->nvlist) {
6455 		nvlist_free(flt->nvlist);
6456 	}
6457 	free(flt);
6458 }
6459 
6460 /*
6461  * makes a copy of ofilter and its contents and points nfilter at it. It
6462  * also adds an instance number to the filter and if either saddr or
6463  * daddr are non-null that address to the filters nvlist along with
6464  * an all 1s address mask and the af.
6465  * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCES.
6466  */
6467 static int
6468 dup_filter(
6469 ipqos_conf_filter_t *ofilter,
6470 ipqos_conf_filter_t **nfilter,
6471 int af,
6472 int inv6,	/* if saddr or daddr set and v4 filter are they in v6 addr */
6473 void *saddr,
6474 void *daddr,
6475 int inst)
6476 {
6477 
6478 	ipqos_conf_filter_t *nf;
6479 	int res;
6480 	in6_addr_t v6addr;
6481 	in6_addr_t all_1s_v6;
6482 
6483 	IPQOSCDBG4(MHME, "In dup_filter: name: %s, af: %u, inv6: %u, ins: %d\n",
6484 	    ofilter->name, af, inv6, inst);
6485 
6486 /* show src address and dst address if present */
6487 #ifdef	_IPQOS_CONF_DEBUG
6488 	if (ipqosconf_dbg_flgs & MHME) {
6489 		char st[100];
6490 
6491 		if (saddr) {
6492 			(void) fprintf(stderr, "saddr: %s\n",
6493 			    inet_ntop(inv6 ? AF_INET6 : AF_INET, saddr, st,
6494 			    100));
6495 		}
6496 
6497 		if (daddr) {
6498 			(void) fprintf(stderr, "daddr: %s\n",
6499 			    inet_ntop(inv6 ? AF_INET6 : AF_INET, daddr, st,
6500 			    100));
6501 		}
6502 	}
6503 #endif	/* _IPQOS_CONF_DEBUG */
6504 
6505 	/* init local v6 address to 0 */
6506 	(void) bzero(&v6addr, sizeof (in6_addr_t));
6507 
6508 	/* create an all 1s address for use as mask */
6509 	(void) memset(&all_1s_v6, ~0, sizeof (in6_addr_t));
6510 
6511 	/* create a new filter */
6512 
6513 	nf = alloc_filter();
6514 	if (nf == NULL) {
6515 		return (IPQOS_CONF_ERR);
6516 	}
6517 
6518 	/* struct copy old filter to new */
6519 	*nf = *ofilter;
6520 
6521 	/* copy src filters nvlist if there is one to copy */
6522 
6523 	if (ofilter->nvlist) {
6524 		res = nvlist_dup(ofilter->nvlist, &nf->nvlist, 0);
6525 		if (res != 0) {
6526 			ipqos_msg(MT_ENOSTR, "nvlist_dup");
6527 			goto fail;
6528 		}
6529 	}
6530 
6531 	/* copy src and dst node names if present */
6532 
6533 	if (ofilter->src_nd_name) {
6534 		nf->src_nd_name = malloc(strlen(ofilter->src_nd_name) + 1);
6535 		if (nf->src_nd_name == NULL) {
6536 			ipqos_msg(MT_ENOSTR, "malloc");
6537 			goto fail;
6538 		}
6539 		(void) strcpy(nf->src_nd_name, ofilter->src_nd_name);
6540 	}
6541 	if (ofilter->dst_nd_name) {
6542 		nf->dst_nd_name = malloc(strlen(ofilter->dst_nd_name) + 1);
6543 		if (nf->dst_nd_name == NULL) {
6544 			ipqos_msg(MT_ENOSTR, "malloc");
6545 			goto fail;
6546 		}
6547 		(void) strcpy(nf->dst_nd_name, ofilter->dst_nd_name);
6548 	}
6549 
6550 	/* add filter addresses type */
6551 
6552 	res = nvlist_add_byte(nf->nvlist, IPGPC_FILTER_TYPE,
6553 	    af == AF_INET ? IPGPC_V4_FLTR : IPGPC_V6_FLTR);
6554 	if (res != 0) {
6555 		ipqos_msg(MT_ENOSTR, "nvlist_add_byte");
6556 		goto fail;
6557 	}
6558 	IPQOSCDBG1(MHME, "adding address type %s in dup filter\n",
6559 	    af == AF_INET ? "AF_INET" : "AF_INET6");
6560 
6561 	/* add saddr if present */
6562 
6563 	if (saddr) {
6564 		if (af == AF_INET && !inv6) {
6565 			V4_PART_OF_V6(v6addr) = *(uint32_t *)saddr;
6566 			saddr = &v6addr;
6567 		}
6568 
6569 		/* add address and all 1's mask */
6570 
6571 		if (nvlist_add_uint32_array(nf->nvlist, IPGPC_SADDR,
6572 		    (uint32_t *)saddr, 4) != 0 ||
6573 		    nvlist_add_uint32_array(nf->nvlist, IPGPC_SADDR_MASK,
6574 		    (uint32_t *)&all_1s_v6, 4) != 0) {
6575 			ipqos_msg(MT_ENOSTR, "nvlist_add_uint32_array");
6576 			goto fail;
6577 		}
6578 
6579 	}
6580 
6581 	/* add daddr if present */
6582 
6583 	if (daddr) {
6584 		if (af == AF_INET && !inv6) {
6585 			V4_PART_OF_V6(v6addr) = *(uint32_t *)daddr;
6586 			daddr = &v6addr;
6587 		}
6588 
6589 		/* add address and all 1's mask */
6590 
6591 		if (nvlist_add_uint32_array(nf->nvlist, IPGPC_DADDR,
6592 		    (uint32_t *)daddr, 4) != 0 ||
6593 		    nvlist_add_uint32_array(nf->nvlist, IPGPC_DADDR_MASK,
6594 		    (uint32_t *)&all_1s_v6, 4) != 0) {
6595 			ipqos_msg(MT_ENOSTR, "nvlist_add_uint32_array");
6596 			goto fail;
6597 		}
6598 	}
6599 
6600 	/* add filter instance */
6601 
6602 	nf->instance = inst;
6603 
6604 	*nfilter = nf;
6605 	return (IPQOS_CONF_SUCCESS);
6606 fail:
6607 	free_filter(nf);
6608 	return (IPQOS_CONF_ERR);
6609 }
6610 
6611 
6612 
6613 /* ************************* action manip fns ********************** */
6614 
6615 
6616 
6617 /*
6618  * create and zero action structure and a params structure hung off of it.
6619  * RETURNS: ptr to allocated action on success, else NULL.
6620  */
6621 static ipqos_conf_action_t *
6622 alloc_action()
6623 {
6624 
6625 	ipqos_conf_action_t *action;
6626 
6627 	action = (ipqos_conf_action_t *)malloc(sizeof (ipqos_conf_action_t));
6628 	if (action == NULL) {
6629 		ipqos_msg(MT_ENOSTR, "malloc");
6630 		return (action);
6631 	}
6632 	bzero(action, sizeof (ipqos_conf_action_t));
6633 
6634 	action->params = (ipqos_conf_params_t *)
6635 			malloc(sizeof (ipqos_conf_params_t));
6636 	if (action->params == NULL) {
6637 		free(action);
6638 		return (NULL);
6639 	}
6640 	bzero(action->params, sizeof (ipqos_conf_params_t));
6641 	action->params->stats_enable = B_FALSE;
6642 
6643 	return (action);
6644 }
6645 
6646 /*
6647  * free all the memory used in all the actions in actions list.
6648  */
6649 static void
6650 free_actions(
6651 ipqos_conf_action_t *actions)
6652 {
6653 
6654 	ipqos_conf_action_t *act = actions;
6655 	ipqos_conf_action_t *next;
6656 	ipqos_conf_filter_t *flt, *nf;
6657 	ipqos_conf_class_t *cls, *nc;
6658 
6659 	while (act != NULL) {
6660 		/* free parameters */
6661 
6662 		if (act->params != NULL) {
6663 			free_arefs(act->params->actions);
6664 			if (act->params->nvlist != NULL) {
6665 				nvlist_free(act->params->nvlist);
6666 			}
6667 			free(act->params);
6668 		}
6669 
6670 		/* free action nvlist */
6671 
6672 		if (act->nvlist != NULL)
6673 			free(act->nvlist);
6674 
6675 		/* free filters */
6676 
6677 		flt = act->filters;
6678 		while (flt != NULL) {
6679 			nf = flt->next;
6680 			free_filter(flt);
6681 			flt = nf;
6682 		}
6683 
6684 		/* free classes */
6685 
6686 		cls = act->classes;
6687 		while (cls != NULL) {
6688 			nc = cls->next;
6689 			free_class(cls);
6690 			cls = nc;
6691 		}
6692 
6693 		/* free permanent classes table */
6694 		cleanup_string_table(act->perm_classes, act->num_perm_classes);
6695 
6696 		/* free filters to retry */
6697 
6698 		flt = act->retry_filters;
6699 		while (flt != NULL) {
6700 			nf = flt->next;
6701 			free_filter(flt);
6702 			flt = nf;
6703 		}
6704 
6705 		/* free dependency pointers */
6706 		free_arefs(act->dependencies);
6707 
6708 		next = act->next;
6709 		free(act);
6710 		act = next;
6711 	}
6712 }
6713 
6714 /*
6715  * Checks whether there is an action called action_name in actions list.
6716  * RETURNS: ptr to first matched action, else if not matched NULL.
6717  *
6718  */
6719 static ipqos_conf_action_t *
6720 actionexist(
6721 char *action_name,
6722 ipqos_conf_action_t *actions)
6723 {
6724 
6725 	IPQOSCDBG1(L1, "In actionexist: name: %s\n", action_name);
6726 
6727 	while (actions) {
6728 		if (strcmp(action_name, actions->name) == 0) {
6729 			break;
6730 		}
6731 		actions = actions->next;
6732 	}
6733 
6734 	return (actions);
6735 }
6736 
6737 /* **************************** act ref manip fns ******************** */
6738 
6739 
6740 /*
6741  * add an action reference element with parameter field and action
6742  * action_name to arefs.
6743  * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCES.
6744  */
6745 static int
6746 add_aref(
6747 ipqos_conf_act_ref_t **arefs,
6748 char *field,
6749 char *action_name)
6750 {
6751 
6752 	ipqos_conf_act_ref_t *aref;
6753 
6754 	IPQOSCDBG1(L1, "add_aref: action: %s.\n", action_name);
6755 
6756 	/* allocate zero'd aref */
6757 
6758 	aref = malloc(sizeof (ipqos_conf_act_ref_t));
6759 	if (aref == NULL) {
6760 		ipqos_msg(MT_ENOSTR, "malloc");
6761 		return (IPQOS_CONF_ERR);
6762 	}
6763 	(void) bzero(aref, sizeof (ipqos_conf_act_ref_t));
6764 
6765 	/* copy parameter name if present */
6766 
6767 	if (field)
6768 		(void) strlcpy(aref->field, field, IPQOS_CONF_PNAME_LEN);
6769 
6770 	/* copy action name */
6771 	(void) strlcpy(aref->name, action_name, IPQOS_CONF_NAME_LEN);
6772 
6773 	/* place at head of list */
6774 
6775 	aref->next = *arefs;
6776 	*arefs = aref;
6777 
6778 	return (IPQOS_CONF_SUCCESS);
6779 }
6780 
6781 /*
6782  * free all the memory used by the action references in arefs.
6783  */
6784 static void
6785 free_arefs(
6786 ipqos_conf_act_ref_t *arefs)
6787 {
6788 
6789 	ipqos_conf_act_ref_t *aref = arefs;
6790 	ipqos_conf_act_ref_t *next;
6791 
6792 	while (aref) {
6793 		if (aref->nvlist)
6794 			nvlist_free(aref->nvlist);
6795 		next = aref->next;
6796 		free(aref);
6797 		aref = next;
6798 	}
6799 }
6800 
6801 
6802 
6803 /* *************************************************************** */
6804 
6805 
6806 
6807 /*
6808  * checks whether aname is a valid action name.
6809  * RETURNS: IPQOS_CONF_ERR if invalid, else IPQOS_CONF_SUCCESS.
6810  */
6811 static int
6812 valid_aname(char *aname)
6813 {
6814 
6815 	/*
6816 	 * dissallow the use of the name of a virtual action, either
6817 	 * the ipqosconf name, or the longer ipp names.
6818 	 */
6819 	if (strcmp(aname, IPQOS_CONF_CONT_STR) == 0 ||
6820 	    strcmp(aname, IPQOS_CONF_DEFER_STR) == 0 ||
6821 	    strcmp(aname, IPQOS_CONF_DROP_STR) == 0 ||
6822 	    virtual_action(aname)) {
6823 		ipqos_msg(MT_ERROR, gettext("Invalid action name line %u.\n"),
6824 		    lineno);
6825 		return (IPQOS_CONF_ERR);
6826 	}
6827 
6828 	return (IPQOS_CONF_SUCCESS);
6829 }
6830 
6831 /*
6832  * Opens a stream to the types file for module module_name (assuming
6833  * that the file path is TYPES_FILE_DIR/module_name.types). if
6834  * a file open failure occurs, *openerr is set to 1.
6835  * RETURNS: NULL on error, else stream ptr to module types file.
6836  */
6837 static FILE *
6838 validmod(
6839 char *module_name,
6840 int *openerr)
6841 {
6842 
6843 	FILE *fp;
6844 	char *path;
6845 
6846 	IPQOSCDBG1(L1, "In validmod: module_name: %s\n", module_name);
6847 
6848 	*openerr = 0;
6849 
6850 	/* create modules type file path */
6851 
6852 	path = malloc(strlen(TYPES_FILE_DIR) + strlen(module_name) +
6853 	    strlen(".types") + 1);
6854 	if (path == NULL) {
6855 		ipqos_msg(MT_ENOSTR, "malloc");
6856 		return (NULL);
6857 	}
6858 	(void) strcpy(path, TYPES_FILE_DIR);
6859 	(void) strcat(path, module_name);
6860 	(void) strcat(path, ".types");
6861 
6862 
6863 	IPQOSCDBG1(L1, "opening file %s\n", path);
6864 
6865 	/* open stream to types file */
6866 
6867 	fp = fopen(path, "r");
6868 	if (fp == NULL) {
6869 		(*openerr)++;
6870 	}
6871 
6872 	free(path);
6873 	return (fp);
6874 }
6875 
6876 
6877 /*
6878  * read a class clause from cfp into a class struct and point class at this.
6879  * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCESS.
6880  */
6881 static int
6882 readclass(
6883 FILE *cfp,
6884 char *module_name,
6885 ipqos_conf_class_t **class,
6886 char **perm_classes,
6887 int num_perm_classes)
6888 {
6889 
6890 	int nm, act;
6891 	int res;
6892 	nvpair_t *nvp;
6893 	ipqos_nvtype_t type;
6894 	char *name;
6895 	char *action;
6896 	int stats;
6897 
6898 	IPQOSCDBG0(L0, "in readclass\n");
6899 
6900 	/* create and zero class struct */
6901 
6902 	*class = alloc_class();
6903 	if (!*class) {
6904 		return (IPQOS_CONF_ERR);
6905 	}
6906 	(*class)->originator = IPP_CONFIG_IPQOSCONF;
6907 
6908 	/* get starting line for error reporting */
6909 	(*class)->lineno = lineno;
6910 
6911 	/* read curl_begin */
6912 
6913 	res = read_curl_begin(cfp);
6914 	if (res != IPQOS_CONF_SUCCESS) {
6915 		goto fail;
6916 	}
6917 
6918 	/* loop reading parameters till read curl_end */
6919 
6920 	stats = nm = act = 0;
6921 	for (;;) {
6922 		/* read nvpair */
6923 		res = readnvpair(cfp, NULL, &(*class)->nvlist,
6924 		    &nvp, &type, PL_CLASS, module_name);
6925 		if (res == IPQOS_CONF_ERR) {
6926 			goto fail;
6927 
6928 		/* reached end of class clause */
6929 		} else if (res == IPQOS_CONF_CURL_END) {
6930 			break;
6931 		}
6932 
6933 		/*
6934 		 * catch name and action nv pairs and stats if present
6935 		 * and place values in class structure.
6936 		 */
6937 
6938 		/* name */
6939 
6940 		if (nm == 0 &&
6941 		    strcmp(nvpair_name(nvp), IPQOS_CONF_NAME_STR) == 0) {
6942 
6943 			(void) nvpair_value_string(nvp, &name);
6944 
6945 			if (valid_name(name) != IPQOS_CONF_SUCCESS) {
6946 				goto fail;
6947 			}
6948 			(void) strcpy((*class)->name, name);
6949 			nm++;
6950 
6951 		/* next action */
6952 
6953 		} else if (act == 0 &&
6954 		    strcmp(nvpair_name(nvp), IPQOS_CONF_NEXT_ACTION_STR) == 0) {
6955 
6956 			(void) nvpair_value_string(nvp, &action);
6957 
6958 			/*
6959 			 * if next action string continue string set action to
6960 			 * IPP_ANAME_CONT, else if drop string IPP_ANAME_DROP
6961 			 */
6962 			if (strcmp(action, IPQOS_CONF_CONT_STR) == 0) {
6963 				action = IPP_ANAME_CONT;
6964 			} else if (strcmp(action, IPQOS_CONF_DROP_STR) == 0) {
6965 				action = IPP_ANAME_DROP;
6966 			}
6967 
6968 			/* add an action reference to action list */
6969 
6970 			res = add_aref(&(*class)->alist,
6971 			    IPQOS_CONF_NEXT_ACTION_STR, action);
6972 			if (res != IPQOS_CONF_SUCCESS) {
6973 				goto fail;
6974 			}
6975 			act++;
6976 
6977 		/* class stats enable */
6978 
6979 		} else if (stats == 0 &&
6980 		    strcmp(nvpair_name(nvp), IPQOS_CONF_STATS_ENABLE_STR) ==
6981 		    0) {
6982 			boolean_t bl;
6983 
6984 			(void) nvpair_value_uint32(nvp, (uint32_t *)&bl);
6985 			(*class)->stats_enable = bl;
6986 
6987 			stats++;
6988 
6989 		/* no other / duplicate parameters allowed */
6990 
6991 		} else {
6992 			ipqos_msg(MT_ERROR,
6993 			    gettext("Unexpected parameter line %u.\n"), lineno);
6994 			goto fail;
6995 		}
6996 	}
6997 	if (nm == 0 || act == 0) {
6998 		ipqos_msg(MT_ERROR,
6999 		    gettext("Missing class name/next action before line %u.\n"),
7000 		    lineno);
7001 		goto fail;
7002 	}
7003 
7004 	/* change class originator field to permanent if permanent class */
7005 
7006 	if (in_string_table(perm_classes, num_perm_classes, (*class)->name)) {
7007 	    IPQOSCDBG1(L0, "Setting class %s as permanent.\n", (*class)->name);
7008 		(*class)->originator = IPP_CONFIG_PERMANENT;
7009 	}
7010 
7011 	return (IPQOS_CONF_SUCCESS);
7012 fail:
7013 	if (*class)
7014 		free_class(*class);
7015 	return (IPQOS_CONF_ERR);
7016 }
7017 
7018 /*
7019  * This function assumes either src_nd_name or dst_node_nm are set in filter.
7020  *
7021  * Creates one of more copies of filter according to the ip versions
7022  * requested (or assumed) and the resolution of the src and dst address
7023  * node names if spec'd. If both node names are spec'd then a filter is
7024  * created for each pair of addresses (one from each node name) that is
7025  * compatible with the chosen address family, otherwise a filter copy is
7026  * created for just each address of the single node name that is
7027  * compatible.
7028  * If filter->ip_versions has been set that is used to determine the
7029  * af's we will create filters for, else if a numeric address was
7030  * added the family of that will be used, otherwise we fall back
7031  * to both v4 and v6 addresses.
7032  *
7033  * Any name lookup failures that occur are checked to see whether the failure
7034  * was a soft or hard failure and the nlerr field of filter set accordingly
7035  * before the error is returned.
7036  *
7037  * RETURNS: IPQOS_CONF_ERR on any error, else IPQOS_CONF_SUCCESS.
7038  */
7039 
7040 static int
7041 domultihome(
7042 ipqos_conf_filter_t *filter,
7043 ipqos_conf_filter_t **flist,
7044 boolean_t last_retry)
7045 {
7046 
7047 	uint32_t ftype;
7048 	int v4 = 1, v6 = 1;	/* default lookup family is v4 and v6 */
7049 	int saf, daf;
7050 	struct hostent *shp = NULL;
7051 	struct hostent *dhp = NULL;
7052 	in6_addr_t daddr, saddr;
7053 	int idx = 0;
7054 	ipqos_conf_filter_t *nfilter;
7055 	int res;
7056 	int ernum;
7057 	int in32b = 0;
7058 	char **sp, **dp;
7059 
7060 	IPQOSCDBG3(MHME, "In domultihome: filter: %s, src_node: %s, "
7061 	    "dst_node: %s\n", filter->name,
7062 	    (filter->src_nd_name ? filter->src_nd_name : "NULL"),
7063 	    (filter->dst_nd_name ? filter->dst_nd_name : "NULL"));
7064 
7065 	/* check if we've read an ip_version request to get the versions */
7066 
7067 	if (filter->ip_versions != 0) {
7068 		v4 = VERSION_IS_V4(filter);
7069 		v6 = VERSION_IS_V6(filter);
7070 
7071 	/* otherwise check if we've read a numeric address and get versions */
7072 
7073 	} else if (nvlist_lookup_uint32(filter->nvlist, IPGPC_FILTER_TYPE,
7074 	    &ftype) == 0) {
7075 		if (ftype == IPGPC_V4_FLTR) {
7076 			v6--;
7077 		} else {
7078 			v4--;
7079 		}
7080 	}
7081 
7082 	/* read saddrs if src node name */
7083 
7084 	if (filter->src_nd_name) {
7085 
7086 		/* v4 only address */
7087 
7088 		if (v4 && !v6) {
7089 			in32b++;
7090 			shp = getipnodebyname(filter->src_nd_name, AF_INET,
7091 			    AI_ADDRCONFIG, &ernum);
7092 
7093 		/* v6 only  */
7094 
7095 		} else if (v6 && !v4) {
7096 			shp = getipnodebyname(filter->src_nd_name, AF_INET6,
7097 			    AI_DEFAULT, &ernum);
7098 
7099 		/* v4 and v6 */
7100 
7101 		} else if (v6 && v4) {
7102 			shp = getipnodebyname(filter->src_nd_name, AF_INET6,
7103 			    AI_DEFAULT|AI_ALL, &ernum);
7104 		}
7105 
7106 #ifdef	TESTING_RETRY
7107 if (!last_retry) {
7108 	filter->nlerr = IPQOS_LOOKUP_RETRY;
7109 	goto fail;
7110 }
7111 #endif
7112 
7113 		/*
7114 		 * if lookup error determine whether it was a soft or hard
7115 		 * failure and mark as such in filter.
7116 		 */
7117 		if (shp == NULL) {
7118 			if (ernum != TRY_AGAIN) {
7119 				ipqos_msg(MT_ERROR, gettext("Failed to "
7120 				    "resolve src host name for filter at "
7121 				    "line %u, ignoring filter.\n"),
7122 				    filter->lineno);
7123 				filter->nlerr = IPQOS_LOOKUP_FAIL;
7124 			} else {
7125 				if (last_retry) {
7126 					ipqos_msg(MT_ERROR, gettext("Failed "
7127 					    "to resolve src host name for "
7128 					    "filter at line %u, ignoring "
7129 					    "filter.\n"), filter->lineno);
7130 				}
7131 				filter->nlerr = IPQOS_LOOKUP_RETRY;
7132 			}
7133 			goto fail;
7134 		}
7135 	}
7136 
7137 	/* read daddrs if dst node name */
7138 	if (filter->dst_nd_name) {
7139 
7140 		/* v4 only address */
7141 
7142 		if (v4 && !v6) {
7143 			in32b++;
7144 			dhp = getipnodebyname(filter->dst_nd_name, AF_INET,
7145 			    AI_ADDRCONFIG, &ernum);
7146 
7147 		/* v6 only */
7148 
7149 		} else if (v6 && !v4) {
7150 			dhp = getipnodebyname(filter->dst_nd_name, AF_INET6,
7151 			    AI_DEFAULT, &ernum);
7152 
7153 		/*  v6 and v4 addresses */
7154 
7155 		} else {
7156 			dhp = getipnodebyname(filter->dst_nd_name, AF_INET6,
7157 			    AI_DEFAULT|AI_ALL, &ernum);
7158 		}
7159 
7160 		if (dhp == NULL) {
7161 			if (ernum != TRY_AGAIN) {
7162 				ipqos_msg(MT_ERROR, gettext("Failed to "
7163 				    "resolve dst host name for filter at "
7164 				    "line %u, ignoring filter.\n"),
7165 				    filter->lineno);
7166 				filter->nlerr = IPQOS_LOOKUP_FAIL;
7167 			} else {
7168 				if (last_retry) {
7169 					ipqos_msg(MT_ERROR, gettext("Failed "
7170 					    "to resolve dst host name for "
7171 					    "filter at line %u, ignoring "
7172 					    "filter.\n"), filter->lineno);
7173 				}
7174 				filter->nlerr = IPQOS_LOOKUP_RETRY;
7175 			}
7176 			goto fail;
7177 		}
7178 	}
7179 
7180 	/*
7181 	 * if src and dst node name, create set of filters; one for each
7182 	 * src and dst address of matching types.
7183 	 */
7184 	if (filter->src_nd_name && filter->dst_nd_name) {
7185 
7186 		for (sp = shp->h_addr_list; *sp != NULL; sp++) {
7187 			(void) bcopy(*sp, &saddr, shp->h_length);
7188 
7189 			/* get saddr family */
7190 
7191 			if (in32b || IN6_IS_ADDR_V4MAPPED(&saddr)) {
7192 				saf = AF_INET;
7193 			} else {
7194 				saf = AF_INET6;
7195 			}
7196 
7197 			for (dp = dhp->h_addr_list; *dp != NULL; dp++) {
7198 				(void) bcopy(*dp, &daddr, dhp->h_length);
7199 
7200 				/* get daddr family */
7201 
7202 				if (in32b || IN6_IS_ADDR_V4MAPPED(&daddr)) {
7203 					daf = AF_INET;
7204 				} else {
7205 					daf = AF_INET6;
7206 				}
7207 
7208 				/*
7209 				 * if saddr and daddr same af duplicate
7210 				 * filter adding addresses and new instance
7211 				 * number and add to flist filter list.
7212 				 */
7213 
7214 				if (daf == saf) {
7215 
7216 					res = dup_filter(filter, &nfilter, saf,
7217 					    !in32b, &saddr, &daddr, ++idx);
7218 					if (res != IPQOS_CONF_SUCCESS) {
7219 						goto fail;
7220 					}
7221 					ADD_TO_LIST(flist, nfilter);
7222 				}
7223 			}
7224 		}
7225 
7226 	/* if src name only create set of filters, one for each node address */
7227 
7228 	} else if (filter->src_nd_name) {
7229 
7230 		for (sp = shp->h_addr_list; *sp != NULL; sp++) {
7231 			(void) bcopy(*sp, &saddr, shp->h_length);
7232 
7233 			/* get af */
7234 
7235 			if (in32b || IN6_IS_ADDR_V4MAPPED(&saddr)) {
7236 				saf = AF_INET;
7237 			} else {
7238 				saf = AF_INET6;
7239 			}
7240 
7241 
7242 			/*
7243 			 * dup filter adding saddr and new instance num and
7244 			 * add to flist filter list.
7245 			 */
7246 			res = dup_filter(filter, &nfilter, saf, !in32b, &saddr,
7247 			    NULL, ++idx);
7248 			if (res != IPQOS_CONF_SUCCESS) {
7249 				goto fail;
7250 			}
7251 
7252 			ADD_TO_LIST(flist, nfilter);
7253 
7254 		}
7255 
7256 	/* if dname only create set of filters, one for each node address */
7257 
7258 	} else {
7259 		for (dp = dhp->h_addr_list; *dp != NULL; dp++) {
7260 			(void) bcopy(*dp, &daddr, dhp->h_length);
7261 
7262 			/* get af */
7263 
7264 			if (in32b || IN6_IS_ADDR_V4MAPPED(&daddr)) {
7265 				daf = AF_INET;
7266 			} else {
7267 				daf = AF_INET6;
7268 			}
7269 
7270 			/*
7271 			 * dup filter adding daddr and new instance num and
7272 			 * add to flist filter list.
7273 			 */
7274 			res = dup_filter(filter, &nfilter, daf, !in32b, NULL,
7275 			    &daddr, ++idx);
7276 			if (res != IPQOS_CONF_SUCCESS) {
7277 				goto fail;
7278 			}
7279 
7280 			ADD_TO_LIST(flist, nfilter);
7281 		}
7282 	}
7283 
7284 	if (shp)
7285 		freehostent(shp);
7286 	if (dhp)
7287 		freehostent(dhp);
7288 	return (IPQOS_CONF_SUCCESS);
7289 fail:
7290 	/*
7291 	 * should really clean up any filters that we have created,
7292 	 * however, free_actions called from readaction will cleam them up.
7293 	 */
7294 	if (shp)
7295 		freehostent(shp);
7296 	if (dhp)
7297 		freehostent(dhp);
7298 	return (IPQOS_CONF_ERR);
7299 }
7300 
7301 
7302 /*
7303  * read a filter clause from cfp into a filter struct and point filter
7304  * at this.
7305  * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCES.
7306  */
7307 static int
7308 readfilter(
7309 FILE *cfp,
7310 FILE *tfp,
7311 char *module_name,
7312 ipqos_conf_filter_t **filter,
7313 char **perm_filters,
7314 int num_perm_filters)
7315 {
7316 
7317 	int res;
7318 	int nm, cls, ipv;
7319 	in6_addr_t mask;
7320 	char *addr_str;
7321 	char *sl = NULL;
7322 	in6_addr_t addr;
7323 	int sa;
7324 	struct hostent *hp;
7325 	int err_num;
7326 	int v4 = 0, v6 = 0;
7327 	uchar_t mlen;
7328 	char *tmp;
7329 	nvpair_t *nvp;
7330 	ipqos_nvtype_t type;
7331 	char *name;
7332 	char *class;
7333 	uchar_t b;
7334 	in6_addr_t v6addr;
7335 
7336 	IPQOSCDBG0(L0, "in readfilter\n");
7337 
7338 
7339 	/* create and zero filter struct */
7340 
7341 	*filter = alloc_filter();
7342 	if (*filter == NULL) {
7343 		return (IPQOS_CONF_ERR);
7344 	}
7345 	(*filter)->originator = IPP_CONFIG_IPQOSCONF;
7346 
7347 	/* get starting line for error reporting */
7348 	(*filter)->lineno = lineno;
7349 
7350 	/* read beginning curl */
7351 
7352 	res = read_curl_begin(cfp);
7353 	if (res != IPQOS_CONF_SUCCESS) {
7354 		goto fail;
7355 	}
7356 
7357 
7358 	/*
7359 	 * loop reading nvpairs onto nvlist until encounter CURL_END
7360 	 */
7361 	ipv = nm = cls = 0;
7362 	for (;;) {
7363 		/* read nvpair */
7364 
7365 		res = readnvpair(cfp, tfp, &(*filter)->nvlist,
7366 		    &nvp, &type, PL_FILTER, module_name);
7367 		if (res == IPQOS_CONF_ERR) {
7368 			goto fail;
7369 
7370 		/* reached the end of filter definition */
7371 
7372 		} else if (res == IPQOS_CONF_CURL_END) {
7373 			break;
7374 		}
7375 
7376 		/*
7377 		 * catch name and class and place value into filter
7378 		 * structure.
7379 		 */
7380 
7381 		/* read filter name */
7382 
7383 		if (strcmp(nvpair_name(nvp), IPQOS_CONF_NAME_STR) == 0) {
7384 			if (nm != 0) {
7385 				ipqos_msg(MT_ERROR,
7386 				    gettext("Duplicate parameter line %u.\n"),
7387 				    lineno);
7388 				goto fail;
7389 			}
7390 
7391 			(void) nvpair_value_string(nvp, &name);
7392 			if (valid_name(name) != IPQOS_CONF_SUCCESS) {
7393 				goto fail;
7394 			}
7395 
7396 			(void) strcpy((*filter)->name, name);
7397 			(void) nvlist_remove_all((*filter)->nvlist,
7398 			    IPQOS_CONF_NAME_STR);
7399 			nm++;
7400 
7401 		/* read class name */
7402 
7403 		} else if (strcmp(nvpair_name(nvp), IPQOS_CONF_CLASS_STR) ==
7404 		    0) {
7405 			if (cls != 0) {
7406 				ipqos_msg(MT_ERROR,
7407 				    gettext("Duplicate parameter line %u.\n"),
7408 				    lineno);
7409 				goto fail;
7410 			}
7411 
7412 			if (nvpair_value_string(nvp, &class) != 0) {
7413 				ipqos_msg(MT_ENOSTR, "nvpair_value_string");
7414 				break;
7415 			}
7416 			if (valid_name(class) != IPQOS_CONF_SUCCESS) {
7417 				goto fail;
7418 			}
7419 			(void) strcpy((*filter)->class_name, class);
7420 			(void) nvlist_remove_all((*filter)->nvlist,
7421 			    IPQOS_CONF_CLASS_STR);
7422 			cls++;
7423 
7424 		/*
7425 		 * if a src or dst ip node name/address. For those that
7426 		 * are determined to be addresses we convert them from
7427 		 * strings here and add to the filter nvlist; for node names
7428 		 * we add the name to the filter struct for readaction to
7429 		 * process.
7430 		 */
7431 		} else if (strcmp(nvpair_name(nvp), IPGPC_SADDR) == 0 ||
7432 		    strcmp(nvpair_name(nvp), IPGPC_DADDR) == 0) {
7433 
7434 			sa = 0;
7435 
7436 			if (strcmp(nvpair_name(nvp), IPGPC_SADDR) == 0) {
7437 				sa++;
7438 			}
7439 
7440 			(void) nvpair_value_string(nvp, &addr_str);
7441 
7442 			/*
7443 			 * get the address mask if present.
7444 			 * make a copy so that the nvlist element that
7445 			 * it is part of doesn't dissapear and causes probs.
7446 			 */
7447 			sl = strchr(addr_str, '/');
7448 			if (sl) {
7449 				*sl = '\0';
7450 				tmp = malloc(strlen(++sl) + 1);
7451 				if (tmp == NULL) {
7452 					ipqos_msg(MT_ENOSTR, "malloc");
7453 					goto fail;
7454 				}
7455 				(void) strcpy(tmp, sl);
7456 				sl = tmp;
7457 			}
7458 
7459 
7460 			/* if a numeric address */
7461 
7462 			if (inet_pton(AF_INET, addr_str, &addr) == 1 ||
7463 			    inet_pton(AF_INET6, addr_str, &addr) == 1) {
7464 
7465 				/* get address */
7466 
7467 				hp = getipnodebyname(addr_str, AF_INET6,
7468 				    AI_DEFAULT, &err_num);
7469 				if (hp == NULL) {
7470 					ipqos_msg(MT_ENOSTR,
7471 					    "getipnodebyname");
7472 					goto fail;
7473 				}
7474 
7475 				(void) bcopy(hp->h_addr_list[0], &v6addr,
7476 				    hp->h_length);
7477 				freehostent(hp);
7478 
7479 				/* determine address type */
7480 
7481 				v4 = IN6_IS_ADDR_V4MAPPED(&v6addr);
7482 				if (!v4) {
7483 					v6++;
7484 				}
7485 
7486 				/*
7487 				 * check any previous addresses have same
7488 				 * version.
7489 				 */
7490 				if (nvlist_lookup_byte((*filter)->nvlist,
7491 				    IPGPC_FILTER_TYPE, &b) == 0) {
7492 					if (v4 && b != IPGPC_V4_FLTR ||
7493 					    v6 && b != IPGPC_V6_FLTR) {
7494 						ipqos_msg(MT_ERROR,
7495 						    gettext("Incompatible "
7496 						    "address version line "
7497 						    "%u.\n"), lineno);
7498 						goto fail;
7499 					}
7500 				}
7501 
7502 				/*
7503 				 * check that if ip_version spec'd it
7504 				 * corresponds.
7505 				 */
7506 				if ((*filter)->ip_versions != 0) {
7507 					if (v4 && !VERSION_IS_V4(*filter) ||
7508 					    v6 && !VERSION_IS_V6(*filter)) {
7509 						ipqos_msg(MT_ERROR,
7510 						    gettext("Incompatible "
7511 						    "address version line %u"
7512 						    ".\n"), lineno);
7513 						goto fail;
7514 					}
7515 				}
7516 
7517 				/* add the address type */
7518 
7519 				res = nvlist_add_byte(
7520 				(*filter)->nvlist, IPGPC_FILTER_TYPE,
7521 				    v4 ? IPGPC_V4_FLTR : IPGPC_V6_FLTR);
7522 				if (res != 0) {
7523 					ipqos_msg(MT_ENOSTR,
7524 					    "nvlist_add_byte");
7525 					goto fail;
7526 				}
7527 
7528 				/* add address to list */
7529 
7530 				res = nvlist_add_uint32_array((*filter)->nvlist,
7531 				    sa ? IPGPC_SADDR : IPGPC_DADDR,
7532 				    (uint32_t *)&v6addr, 4);
7533 				if (res != 0) {
7534 					ipqos_msg(MT_ENOSTR,
7535 					    "nvlist_add_uint32_array");
7536 					goto fail;
7537 				}
7538 
7539 
7540 				/*
7541 				 * add mask entry in list.
7542 				 */
7543 
7544 				if (sl) {	/* have CIDR mask */
7545 					char *lo;
7546 					res = readuint8(sl, &mlen, &lo);
7547 					if (res != IPQOS_CONF_SUCCESS ||
7548 					    v4 && mlen > 32 ||
7549 					    !v4 && mlen > 128 ||
7550 					    mlen == 0) {
7551 						ipqos_msg(MT_ERROR,
7552 						    gettext("Invalid CIDR "
7553 						    "mask line %u.\n"), lineno);
7554 						goto fail;
7555 					}
7556 					setmask(mlen, &mask,
7557 					    v4 ? AF_INET : AF_INET6);
7558 					free(sl);
7559 				} else {
7560 					/* no CIDR mask spec'd - use all 1s */
7561 
7562 					(void) memset(&mask, ~0,
7563 					    sizeof (in6_addr_t));
7564 				}
7565 				res = nvlist_add_uint32_array((*filter)->nvlist,
7566 				    sa ? IPGPC_SADDR_MASK : IPGPC_DADDR_MASK,
7567 				    (uint32_t *)&mask, 4);
7568 				if (res != 0) {
7569 					ipqos_msg(MT_ENOSTR,
7570 					    "nvlist_add_uint32_arr");
7571 					goto fail;
7572 				}
7573 
7574 			/* inet_pton returns fail - we assume a node name */
7575 
7576 			} else {
7577 				/*
7578 				 * doesn't make sense to have a mask
7579 				 * with a node name.
7580 				 */
7581 				if (sl) {
7582 					ipqos_msg(MT_ERROR,
7583 					    gettext("Address masks aren't "
7584 					    "allowed for host names line "
7585 					    "%u.\n"), lineno);
7586 					goto fail;
7587 				}
7588 
7589 				/*
7590 				 * store node name in filter struct for
7591 				 * later resolution.
7592 				 */
7593 				if (sa) {
7594 					(*filter)->src_nd_name =
7595 					    malloc(strlen(addr_str) + 1);
7596 					(void) strcpy((*filter)->src_nd_name,
7597 					    addr_str);
7598 				} else {
7599 					(*filter)->dst_nd_name =
7600 					    malloc(strlen(addr_str) + 1);
7601 					(void) strcpy((*filter)->dst_nd_name,
7602 					    addr_str);
7603 				}
7604 			}
7605 
7606 		/* ip_version enumeration */
7607 
7608 		} else if (strcmp(nvpair_name(nvp), IPQOS_CONF_IP_VERSION) ==
7609 		    0) {
7610 			/* check we haven't read ip_version before */
7611 			if (ipv) {
7612 				ipqos_msg(MT_ERROR,
7613 				    gettext("Duplicate parameter line %u.\n"),
7614 				    lineno);
7615 				goto fail;
7616 			}
7617 			ipv++;
7618 
7619 			/* get bitmask value */
7620 
7621 			(void) nvpair_value_uint32(nvp,
7622 			    &(*filter)->ip_versions);
7623 
7624 			/*
7625 			 * check that if either ip address is spec'd it
7626 			 * corresponds.
7627 			 */
7628 			if (v4 && !VERSION_IS_V4(*filter) ||
7629 			    v6 && !VERSION_IS_V6(*filter)) {
7630 				ipqos_msg(MT_ERROR, gettext("Incompatible "
7631 				    "address version line %u.\n"), lineno);
7632 				goto fail;
7633 			}
7634 
7635 			/* remove ip_version from nvlist */
7636 
7637 			(void) nvlist_remove_all((*filter)->nvlist,
7638 			    IPQOS_CONF_IP_VERSION);
7639 		}
7640 	}
7641 	if (nm == 0 || cls == 0) {
7642 		ipqos_msg(MT_ERROR, gettext("Missing filter/class name "
7643 		    "before line %u.\n"), lineno);
7644 		goto fail;
7645 	}
7646 
7647 	if (in_string_table(perm_filters, num_perm_filters, (*filter)->name)) {
7648 		IPQOSCDBG1(L0, "Setting filter %s as permanent.\n",
7649 		    (*filter)->name);
7650 
7651 		(*filter)->originator = IPP_CONFIG_PERMANENT;
7652 	}
7653 
7654 	return (IPQOS_CONF_SUCCESS);
7655 fail:
7656 	if (*filter)
7657 		free_filter(*filter);
7658 	if (hp)
7659 		freehostent(hp);
7660 	if (sl)
7661 		free(sl);
7662 
7663 	return (IPQOS_CONF_ERR);
7664 }
7665 
7666 /*
7667  * reads the curl begin token from cfp stream.
7668  * RETURNS: IPQOS_CONF_ERR if not read successfully, else IPQOS_CONF_SUCCES.
7669  */
7670 static int
7671 read_curl_begin(FILE *cfp)
7672 {
7673 
7674 	int res;
7675 	char *st;
7676 
7677 	res = readtoken(cfp, &st);
7678 
7679 	if (res != IPQOS_CONF_CURL_BEGIN) {
7680 		if (res == IPQOS_CONF_EOF) {
7681 			ipqos_msg(MT_ERROR, gettext("Unexpected EOF.\n"));
7682 
7683 		/* if CURL_END or something else */
7684 		} else if (res != IPQOS_CONF_ERR) {
7685 			free(st);
7686 			ipqos_msg(MT_ERROR, gettext("\'{\' missing at line "
7687 			    "%u.\n"), lineno);
7688 		}
7689 		return (IPQOS_CONF_ERR);
7690 	}
7691 
7692 	free(st);
7693 	return (IPQOS_CONF_SUCCESS);
7694 }
7695 
7696 /*
7697  * This function parses the parameter string version into a version of the
7698  * form "%u.%u" (as a sscanf format string). It then encodes this into an
7699  * int and returns this encoding.
7700  * RETURNS: -1 if an invalid string, else the integer encoding.
7701  */
7702 static int
7703 ver_str_to_int(
7704 char *version)
7705 {
7706 	uint32_t major, minor;
7707 	int ver;
7708 
7709 	if (sscanf(version, "%u.%u", &major, &minor) != 2) {
7710 		IPQOSCDBG0(L0, "Failed to process version number string\n");
7711 		return (-1);
7712 	}
7713 
7714 	ver = (int)((major * 10000) + minor);
7715 	return (ver);
7716 }
7717 
7718 /*
7719  * This function scans through the stream fp line by line looking for
7720  * a line beginning with version_tag and returns a integer encoding of
7721  * the version following it.
7722  *
7723  * RETURNS: If the version definition isn't found or the version is not
7724  * a valid version (%u.%u) then -1 is returned, else an integer encoding
7725  * of the read version.
7726  */
7727 static int
7728 read_tfile_ver(
7729 FILE *fp,
7730 char *version_tag,
7731 char *module_name)
7732 {
7733 	char lbuf[IPQOS_CONF_LINEBUF_SZ];
7734 	char buf[IPQOS_CONF_LINEBUF_SZ+1];
7735 	char buf2[IPQOS_CONF_LINEBUF_SZ+1];
7736 	int found = 0;
7737 	int version;
7738 
7739 	/*
7740 	 * reset to file start
7741 	 */
7742 	if (fseek(fp, 0, SEEK_SET) != 0) {
7743 		ipqos_msg(MT_ENOSTR, "fseek");
7744 		return (-1);
7745 	}
7746 
7747 	/*
7748 	 * loop reading lines till found the one beginning with version_tag.
7749 	 */
7750 	while (fgets(lbuf, IPQOS_CONF_LINEBUF_SZ, fp) != NULL) {
7751 		if ((sscanf(lbuf,
7752 		    "%" VAL2STR(IPQOS_CONF_LINEBUF_SZ) "s"
7753 		    "%" VAL2STR(IPQOS_CONF_LINEBUF_SZ) "s",
7754 		    buf, buf2) == 2) &&
7755 		    (strcmp(buf, version_tag) == 0)) {
7756 			found++;
7757 			break;
7758 		}
7759 	}
7760 	if (found == 0) {
7761 		ipqos_msg(MT_ERROR, gettext("Types file for module %s is "
7762 		    "corrupt.\n"), module_name);
7763 		IPQOSCDBG1(L1, "Couldn't find %s in types file\n",
7764 		    version_tag);
7765 		return (-1);
7766 	}
7767 
7768 	/*
7769 	 * convert version string into int.
7770 	 */
7771 	if ((version = ver_str_to_int(buf2)) == -1) {
7772 		ipqos_msg(MT_ERROR, gettext("Types file for module %s is "
7773 		    "corrupt.\n"), module_name);
7774 		return (-1);
7775 	}
7776 
7777 	return (version);
7778 }
7779 
7780 /*
7781  * read action clause and params/classes/filters clauses within and
7782  * store in and hang off an action structure, and point action at it.
7783  * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCES.
7784  */
7785 static int
7786 readaction(
7787 FILE *cfp,
7788 ipqos_conf_action_t **action)
7789 {
7790 
7791 	char *st;
7792 	FILE *tfp = NULL;
7793 	int nm, md;
7794 	int readprms = 0;
7795 	int res;
7796 	char *strval;
7797 	char *name;
7798 	nvpair_t *nvp;
7799 	ipqos_nvtype_t type;
7800 	ipqos_conf_filter_t *filter;
7801 	ipqos_conf_class_t *class;
7802 	int oe;
7803 	char **perm_filters;
7804 	int num_perm_filters;
7805 	int tf_fmt_ver;
7806 
7807 	IPQOSCDBG0(L0, "in readaction\n");
7808 
7809 	res = readtoken(cfp, &st);
7810 	if (res == IPQOS_CONF_ERR || res == IPQOS_CONF_EOF) {
7811 		return (res);
7812 	} else if (strcmp(st, IPQOS_CONF_ACTION_STR) != 0) {
7813 			ipqos_msg(MT_ERROR, gettext("Missing %s token line "
7814 			    "%u.\n"), IPQOS_CONF_ACTION_STR, lineno);
7815 			free(st);
7816 			return (IPQOS_CONF_ERR);
7817 	}
7818 	free(st);
7819 
7820 	/* create action structure */
7821 
7822 	*action = alloc_action();
7823 	if (*action == NULL) {
7824 		return (IPQOS_CONF_ERR);
7825 	}
7826 	(*action)->params->originator = IPP_CONFIG_IPQOSCONF;
7827 
7828 
7829 	/* get starting line for error reporting */
7830 	(*action)->lineno = lineno;
7831 
7832 	/* read beginning curl */
7833 
7834 	res = read_curl_begin(cfp);
7835 	if (res != IPQOS_CONF_SUCCESS) {
7836 		goto fail;
7837 	}
7838 
7839 	/* loop till read both action name and module */
7840 
7841 	nm = md = 0;
7842 	do {
7843 		/* read nvpair */
7844 
7845 		res = readnvpair(cfp, NULL, &(*action)->nvlist, &nvp, &type,
7846 		    PL_ACTION, NULL);
7847 		if (res == IPQOS_CONF_ERR) {
7848 			goto fail;
7849 
7850 		/* read curl_end */
7851 
7852 		} else if (res == IPQOS_CONF_CURL_END) {
7853 			if (nm == 0 || md == 0) {
7854 				ipqos_msg(MT_ERROR,
7855 				    gettext("Missing action name/ module "
7856 				    "before line %u.\n"), lineno);
7857 				goto fail;
7858 			}
7859 		}
7860 
7861 
7862 		/* store name and module in action structure */
7863 
7864 		name = nvpair_name(nvp);
7865 
7866 		/* read action name */
7867 
7868 		if (nm == 0 && strcmp(name, IPQOS_CONF_NAME_STR) == 0) {
7869 
7870 			(void) nvpair_value_string(nvp, &strval);
7871 
7872 			/* check name is valid */
7873 
7874 			if (valid_name(strval) != IPQOS_CONF_SUCCESS ||
7875 			    valid_aname(strval) != IPQOS_CONF_SUCCESS) {
7876 				goto fail;
7877 			}
7878 
7879 			/* store and remove from list */
7880 
7881 			(void) strcpy((*action)->name, strval);
7882 			/* remove name from nvlist */
7883 			(void) nvlist_remove_all((*action)->nvlist,
7884 			    IPQOS_CONF_NAME_STR);
7885 
7886 			nm++;
7887 
7888 		/* read module name */
7889 
7890 		} else if (md == 0 &&
7891 		    strcmp(name, IPQOS_CONF_MODULE_STR) == 0) {
7892 			/*
7893 			 * check that module has a type file and get
7894 			 * open stream to it.
7895 			 */
7896 			(void) nvpair_value_string(nvp, &strval);
7897 			if ((tfp = validmod(strval, &oe)) == NULL) {
7898 				if (oe) {
7899 					if (errno == ENOENT) {
7900 						ipqos_msg(MT_ERROR,
7901 						    gettext("Invalid "
7902 						    "module name line %u.\n"),
7903 						    lineno);
7904 					} else {
7905 						ipqos_msg(MT_ENOSTR, "fopen");
7906 					}
7907 				}
7908 				goto fail;
7909 			}
7910 
7911 			/*
7912 			 * move module name to action struct
7913 			 */
7914 			(void) strlcpy((*action)->module, strval,
7915 			    IPQOS_CONF_NAME_LEN);
7916 			(void) nvlist_remove_all((*action)->nvlist,
7917 			    IPQOS_CONF_MODULE_STR);
7918 			md++;
7919 
7920 		/* duplicate/other parameter */
7921 
7922 		} else {
7923 			ipqos_msg(MT_ERROR,
7924 			    gettext("Unexpected parameter line %u.\n"),
7925 			    lineno);
7926 			goto fail;
7927 		}
7928 
7929 	} while (nm == 0 || md == 0);
7930 
7931 	/*
7932 	 * check that if the ipgpc action it is named correctly
7933 	 */
7934 	if ((strcmp((*action)->module, IPGPC_NAME) == 0) &&
7935 	    (strcmp((*action)->name, IPGPC_CLASSIFY) != 0)) {
7936 		ipqos_msg(MT_ERROR,
7937 		    gettext("%s action has incorrect name line %u.\n"),
7938 		    IPGPC_NAME, (*action)->lineno);
7939 		goto fail;
7940 	}
7941 
7942 	/* get list of permanent classes */
7943 
7944 	res = read_perm_items(0, tfp, (*action)->module,
7945 	    &(*action)->perm_classes, &(*action)->num_perm_classes);
7946 	if (res != IPQOS_CONF_SUCCESS) {
7947 		goto fail;
7948 	}
7949 
7950 	/* get list of permanent filters */
7951 
7952 	res = read_perm_items(1, tfp, (*action)->module,
7953 	    &perm_filters, &num_perm_filters);
7954 	if (res != IPQOS_CONF_SUCCESS) {
7955 		goto fail;
7956 	}
7957 
7958 	/*
7959 	 * get types file format version and check its supported.
7960 	 */
7961 	if ((tf_fmt_ver = read_tfile_ver(tfp, IPQOS_FMT_STR,
7962 	    (*action)->module)) == -1)
7963 		goto fail;
7964 	if (IPP_MAJOR_MODULE_VER(tf_fmt_ver) > 1 ||
7965 	    IPP_MINOR_MODULE_VER(tf_fmt_ver) > 0) {
7966 		ipqos_msg(MT_ERROR, gettext("Types file for module %s is "
7967 		    "incompatible.\n"), (*action)->module);
7968 		IPQOSCDBG0(L1, "Unsupported fmt major/minor version\n");
7969 		goto fail;
7970 	}
7971 
7972 	/*
7973 	 * get module version
7974 	 */
7975 	if (((*action)->module_version = read_tfile_ver(tfp, IPQOS_MOD_STR,
7976 	    (*action)->module)) == -1)
7977 		goto fail;
7978 
7979 	/* read filter/class/params blocks until CURL_END */
7980 
7981 	for (;;) {
7982 		/* read token */
7983 		res = readtoken(cfp, &st);
7984 
7985 		if (res == IPQOS_CONF_ERR) {
7986 			goto fail;
7987 		} else if (res == IPQOS_CONF_EOF) {
7988 			ipqos_msg(MT_ERROR, gettext("Unexpected EOF.\n"));
7989 			goto fail;
7990 
7991 		/* read CURL_END - end of action definition */
7992 
7993 		} else if (res == IPQOS_CONF_CURL_END) {
7994 			free(st);
7995 			break;
7996 		}
7997 
7998 
7999 		/*
8000 		 * read in either a filter/class or parameter block.
8001 		 */
8002 
8003 		/* read filter */
8004 
8005 		if (strcmp(st, IPQOS_CONF_FILTER_STR) == 0) {
8006 			free(st);
8007 
8008 			res = readfilter(cfp, tfp, (*action)->module, &filter,
8009 			    perm_filters, num_perm_filters);
8010 			if (res != IPQOS_CONF_SUCCESS) {
8011 				goto fail;
8012 			}
8013 
8014 			/*
8015 			 * if we read a host name for either src or dst addr
8016 			 * resolve the hostnames and create the appropriate
8017 			 * number of filters.
8018 			 */
8019 
8020 			if (filter->src_nd_name || filter->dst_nd_name) {
8021 
8022 				res = domultihome(filter, &(*action)->filters,
8023 				    B_FALSE);
8024 				/*
8025 				 * if a lookup fails and the filters
8026 				 * marked as retry we add it to a list
8027 				 * for another attempt later, otherwise
8028 				 * it is thrown away.
8029 				 */
8030 				if (res != IPQOS_CONF_SUCCESS) {
8031 
8032 					/* if not name lookup problem */
8033 
8034 					if (filter->nlerr == 0) {
8035 						free_filter(filter);
8036 						goto fail;
8037 
8038 					/* name lookup problem */
8039 
8040 					/*
8041 					 * if intermitent lookup failure
8042 					 * add to list of filters to
8043 					 * retry later.
8044 					 */
8045 					} else if (filter->nlerr ==
8046 					    IPQOS_LOOKUP_RETRY) {
8047 						filter->nlerr = 0;
8048 						ADD_TO_LIST(
8049 						    &(*action)->retry_filters,
8050 						    filter);
8051 					/*
8052 					 * for non-existing names
8053 					 * ignore the filter.
8054 					 */
8055 					} else {
8056 						free_filter(filter);
8057 					}
8058 
8059 				/* creation of new filters successful */
8060 
8061 				} else {
8062 					free_filter(filter);
8063 				}
8064 
8065 			/* non-node name filter */
8066 
8067 			} else {
8068 				ADD_TO_LIST(&(*action)->filters, filter);
8069 			}
8070 
8071 		/* read class */
8072 
8073 		} else if (strcmp(st, IPQOS_CONF_CLASS_STR) == 0) {
8074 			free(st);
8075 			res = readclass(cfp, (*action)->module, &class,
8076 			    (*action)->perm_classes,
8077 			    (*action)->num_perm_classes);
8078 			if (res != IPQOS_CONF_SUCCESS) {
8079 				goto fail;
8080 			}
8081 
8082 			ADD_TO_LIST(&(*action)->classes, class);
8083 
8084 		/* read params */
8085 
8086 		} else if (strcmp(st, IPQOS_CONF_PARAMS_STR) == 0) {
8087 			free(st);
8088 			if (readprms) {
8089 				ipqos_msg(MT_ERROR,
8090 				    gettext("Second parameter clause not "
8091 				    "supported line %u.\n"), lineno);
8092 				goto fail;
8093 			}
8094 			res = readparams(cfp, tfp, (*action)->module,
8095 			    (*action)->params);
8096 			if (res != IPQOS_CONF_SUCCESS) {
8097 				goto fail;
8098 			}
8099 			readprms++;
8100 
8101 		/* something unexpected */
8102 		} else {
8103 			free(st);
8104 			ipqos_msg(MT_ERROR,
8105 			    gettext("Params/filter/class clause expected "
8106 			    "line %u.\n"), lineno);
8107 			goto fail;
8108 		}
8109 	}
8110 
8111 	(void) fclose(tfp);
8112 	return (IPQOS_CONF_SUCCESS);
8113 
8114 fail:
8115 	if (tfp)
8116 		(void) fclose(tfp);
8117 	if (*action) {
8118 		free_actions(*action);
8119 		*action = NULL;
8120 	}
8121 	return (IPQOS_CONF_ERR);
8122 }
8123 
8124 /*
8125  * check that each of the actions in actions is uniquely named. If one isn't
8126  * set *name to point at the name of the duplicate action.
8127  * RETURNS: IPQOS_CONF_ERR if a non-unique action, else IPQOS_CONF_SUCCESS.
8128  */
8129 static int
8130 actions_unique(ipqos_conf_action_t *actions, char **name)
8131 {
8132 
8133 	IPQOSCDBG0(L1, "In actions_unique.\n");
8134 
8135 	while (actions) {
8136 		if (actionexist(actions->name, actions->next)) {
8137 			*name = actions->name;
8138 			return (IPQOS_CONF_ERR);
8139 		}
8140 		actions = actions->next;
8141 	}
8142 
8143 	return (IPQOS_CONF_SUCCESS);
8144 }
8145 
8146 /*
8147  * checks whether the action parameter is involved in an action cycle.
8148  * RETURNS: 1 if involved in a cycle, 0 otherwise.
8149  */
8150 static int
8151 in_cycle(
8152 ipqos_conf_action_t *action)
8153 {
8154 
8155 	ipqos_conf_act_ref_t *aref;
8156 	ipqos_conf_class_t *c;
8157 
8158 	IPQOSCDBG1(L0, "in_cycle: visiting action %s\n", action->name);
8159 
8160 
8161 	/* have we visited this action before? */
8162 
8163 	if (action->visited == INCYCLE_VISITED) {
8164 		action->visited = 0;
8165 		return (1);
8166 	}
8167 	action->visited = INCYCLE_VISITED;
8168 
8169 	/*
8170 	 * recurse down the child actions of this action through the
8171 	 * classes next action and parameter actions.
8172 	 */
8173 
8174 	for (aref = action->params->actions; aref != NULL; aref = aref->next) {
8175 
8176 		/* skip virtual actions - they can't be in a cycle */
8177 
8178 		if (virtual_action(aref->name)) {
8179 			continue;
8180 		}
8181 
8182 		if (in_cycle(aref->action)) {
8183 			action->visited = 0;
8184 			return (1);
8185 		}
8186 	}
8187 
8188 	for (c = action->classes; c != NULL; c = c->next) {
8189 		aref = c->alist;
8190 
8191 		if (virtual_action(aref->name)) {
8192 			continue;
8193 		}
8194 
8195 		if (in_cycle(aref->action)) {
8196 			action->visited = 0;
8197 			return (1);
8198 		}
8199 	}
8200 
8201 	IPQOSCDBG0(L0, "in_cycle: return\n");
8202 	action->visited = 0;
8203 	return (0);
8204 }
8205 
8206 /*
8207  * checks that the configuration in actions is a valid whole, that
8208  * all actions are unique, all filters and classes are unique within
8209  * their action, that classes referenced by filters exist and actions
8210  * referenced by classes and params exist. Also checks that there are no root
8211  * actions but ipgpc and that no actions are involved in cycles. As
8212  * a consequence of checking that the actions exist two way pointers
8213  * are created between the dependee and dependant actions.
8214  *
8215  * In the case the the userconf flag is zero only this link creation is
8216  * set as we trust the kernel to return a valid configuration.
8217  *
8218  * RETURNS: IPQOS_CONF_ERR if config isn't valid, else IPQOS_CONF_SUCCESS.
8219  *
8220  */
8221 
8222 static int
8223 validconf(
8224 ipqos_conf_action_t *actions,
8225 int userconf)			/* are we checking a conf file ? */
8226 {
8227 	char *name;
8228 	ipqos_conf_action_t *act;
8229 	int res;
8230 	ipqos_conf_action_t *dact;
8231 	ipqos_conf_filter_t *flt;
8232 	ipqos_conf_class_t *cls;
8233 	ipqos_conf_params_t *params;
8234 	ipqos_conf_act_ref_t *aref;
8235 
8236 	IPQOSCDBG0(L0, "In validconf\n");
8237 
8238 	/* check actions are unique */
8239 
8240 	if (userconf && actions_unique(actions, &name) != IPQOS_CONF_SUCCESS) {
8241 		ipqos_msg(MT_ERROR, gettext("Duplicate named action %s.\n"),
8242 		    name);
8243 		return (IPQOS_CONF_ERR);
8244 	}
8245 
8246 	for (act = actions; act; act = act->next) {
8247 
8248 		/*
8249 		 * check filters (for user land configs only).
8250 		 * check they are unique in this action and their class exists.
8251 		 */
8252 		if (userconf) {
8253 			for (flt = act->filters; flt; flt = flt->next) {
8254 
8255 				/* check unique name */
8256 
8257 				if (filterexist(flt->name, flt->instance,
8258 				    flt->next)) {
8259 					ipqos_msg(MT_ERROR,
8260 					    gettext("Duplicate named filter "
8261 					    "%s in action %s.\n"), flt->name,
8262 					    act->name);
8263 					return (IPQOS_CONF_ERR);
8264 				}
8265 
8266 				/*
8267 				 * check existence of class and error if
8268 				 * class doesn't exist and not a perm class
8269 				 */
8270 
8271 				if (!classexist(flt->class_name,
8272 				    act->classes)) {
8273 					if (!in_string_table(act->perm_classes,
8274 					    act->num_perm_classes,
8275 					    flt->class_name)) {
8276 						ipqos_msg(MT_ERROR,
8277 						    gettext("Undefined "
8278 						    "class in filter %s, "
8279 						    "action %s.\n"), flt->name,
8280 						    act->name);
8281 						return (IPQOS_CONF_ERR);
8282 					}
8283 				}
8284 			}
8285 		}
8286 
8287 		/* check classes */
8288 
8289 		for (cls = act->classes; cls; cls = cls->next) {
8290 
8291 			/* check if class name unique (userland only) */
8292 
8293 			if (userconf && classexist(cls->name, cls->next)) {
8294 				ipqos_msg(MT_ERROR,
8295 				    gettext("Duplicate named class %s in "
8296 				    "action %s.\n"), cls->name, act->name);
8297 				return (IPQOS_CONF_ERR);
8298 			}
8299 
8300 			/*
8301 			 * virtual actions always exist so don't check for next
8302 			 * action.
8303 			 */
8304 			if (virtual_action(cls->alist->name)) {
8305 				continue;
8306 			}
8307 
8308 			/*
8309 			 * check existance of next action and create link to
8310 			 * it.
8311 			 */
8312 			if ((cls->alist->action =
8313 			    actionexist(cls->alist->name, actions)) == NULL) {
8314 				ipqos_msg(MT_ERROR,
8315 				    gettext("Undefined action in class %s, "
8316 				    "action %s.\n"), cls->name, act->name);
8317 				return (IPQOS_CONF_ERR);
8318 			}
8319 
8320 			/* create backwards link - used for deletions */
8321 
8322 			dact = cls->alist->action;
8323 			res = add_aref(&dact->dependencies, NULL, act->name);
8324 			if (res != IPQOS_CONF_SUCCESS) {
8325 				return (IPQOS_CONF_ERR);
8326 			}
8327 			dact->dependencies->action = act;
8328 		}
8329 
8330 
8331 		/* check actions exist for action type parameters */
8332 
8333 		params = act->params;
8334 		for (aref = params->actions; aref; aref = aref->next) {
8335 
8336 			/* skip virtuals */
8337 
8338 			if (virtual_action(aref->name)) {
8339 				continue;
8340 			}
8341 
8342 			/*
8343 			 * check existance of action in this ref
8344 			 * and if present create a ptr to it.
8345 			 */
8346 			aref->action = actionexist(aref->name, actions);
8347 			if (aref->action == NULL) {
8348 				ipqos_msg(MT_ERROR,
8349 				    gettext("Undefined action in parameter "
8350 				    "%s, action %s.\n"),
8351 				    SHORT_NAME(aref->field), act->name);
8352 				return (IPQOS_CONF_ERR);
8353 			}
8354 
8355 			/* create backwards link */
8356 
8357 			dact = aref->action;
8358 			res = add_aref(&dact->dependencies, NULL,
8359 			    act->name);
8360 			if (res != IPQOS_CONF_SUCCESS) {
8361 				return (IPQOS_CONF_ERR);
8362 			}
8363 			dact->dependencies->action = act;
8364 		}
8365 	}
8366 
8367 	/* for kernel retrieved configs we don't do the following checks. */
8368 	if (!userconf) {
8369 		return (IPQOS_CONF_SUCCESS);
8370 	}
8371 
8372 	/* check for cycles in config and orphaned actions other than ipgpc */
8373 
8374 	for (act = actions; act; act = act->next) {
8375 
8376 		/* check if involved in cycle */
8377 
8378 		if (in_cycle(act)) {
8379 			ipqos_msg(MT_ERROR,
8380 			    gettext("Action %s involved in cycle.\n"),
8381 			    act->name);
8382 			return (IPQOS_CONF_ERR);
8383 		}
8384 
8385 		/* check that this action has a parent (except ipgpc) */
8386 
8387 		if (act->dependencies == NULL &&
8388 		    strcmp(act->name, IPGPC_CLASSIFY) != 0) {
8389 			ipqos_msg(MT_ERROR, gettext("Action %s isn't "
8390 			    "referenced by any other actions.\n"), act->name);
8391 			return (IPQOS_CONF_ERR);
8392 		}
8393 	}
8394 
8395 	return (IPQOS_CONF_SUCCESS);
8396 }
8397 
8398 /*
8399  * Read the version from the config file with stream cfp with
8400  * the tag version_tag. The tag-value pair should be the first tokens
8401  * encountered.
8402  *
8403  * RETURNS: -1 if a missing or invalid version or a read error,
8404  * else an integer encoding of the version.
8405  */
8406 static int
8407 read_cfile_ver(
8408 FILE *cfp,
8409 char *version_tag)
8410 {
8411 	char *sp = NULL;
8412 	int res;
8413 	int version;
8414 
8415 	IPQOSCDBG0(L1, "In read_cfile_ver:\n");
8416 
8417 	/*
8418 	 * read version tag string.
8419 	 */
8420 	res = readtoken(cfp, &sp);
8421 	if (res != IPQOS_CONF_SUCCESS) {
8422 		goto fail;
8423 	} else if (strcasecmp(sp, version_tag) != 0) {
8424 		goto fail;
8425 	}
8426 	free(sp);
8427 	sp = NULL;
8428 
8429 	/*
8430 	 * read version number string.
8431 	 */
8432 	res = readtoken(cfp, &sp);
8433 	if (res != IPQOS_CONF_SUCCESS) {
8434 		goto fail;
8435 	}
8436 
8437 	/*
8438 	 * encode version into int.
8439 	 */
8440 	if ((version = ver_str_to_int(sp)) == -1) {
8441 		goto fail;
8442 	}
8443 	free(sp);
8444 
8445 	return (version);
8446 fail:
8447 	ipqos_msg(MT_ERROR,
8448 	    gettext("Missing/Invalid config file %s.\n"), version_tag);
8449 	if (sp != NULL)
8450 		free(sp);
8451 	return (-1);
8452 }
8453 
8454 /*
8455  * read the set of actions definitions from the stream cfp and store
8456  * them in a list pointed to by conf.
8457  * RETURNS: IPQOS_CONF_ERR if any errors, else IPQOS_CONF_SUCCESS.
8458  */
8459 static int
8460 readconf(
8461 FILE *cfp,
8462 ipqos_conf_action_t **conf)
8463 {
8464 
8465 	int res;
8466 	ipqos_conf_action_t *action;
8467 	boolean_t ipgpc_action = B_FALSE;
8468 	int fmt_ver;
8469 
8470 	IPQOSCDBG0(L0, "In readconf\n");
8471 
8472 	*conf = NULL;
8473 
8474 	/*
8475 	 * get config file format version.
8476 	 */
8477 	fmt_ver = read_cfile_ver(cfp, IPQOS_FMT_VERSION_STR);
8478 	if (fmt_ver == -1) {
8479 		return (IPQOS_CONF_ERR);
8480 	} else {
8481 		/*
8482 		 * check version is valid
8483 		 */
8484 		if ((IPP_MAJOR_MODULE_VER(fmt_ver) > 1) ||
8485 		    (IPP_MINOR_MODULE_VER(fmt_ver) > 0)) {
8486 			ipqos_msg(MT_ERROR, gettext("Unsupported config file "
8487 			    "format version.\n"));
8488 			return (IPQOS_CONF_ERR);
8489 		}
8490 	}
8491 
8492 	/* loop reading actions adding to conf till EOF */
8493 
8494 	for (;;) {
8495 		action = NULL;
8496 
8497 		/* readaction */
8498 
8499 		res = readaction(cfp, &action);
8500 		if (res == IPQOS_CONF_ERR) {
8501 			goto fail;
8502 		}
8503 
8504 		/* reached eof, finish */
8505 
8506 		if (res == IPQOS_CONF_EOF) {
8507 			break;
8508 		}
8509 
8510 		ADD_TO_LIST(conf, action);
8511 
8512 		/* check if we just read an ipgpc action */
8513 
8514 		if (strcmp(action->name, IPGPC_CLASSIFY) == 0)
8515 			ipgpc_action = B_TRUE;
8516 	}
8517 
8518 	/* check that there is one or more actions and that one is ipgpc */
8519 
8520 	if (ipgpc_action == B_FALSE) {
8521 		ipqos_msg(MT_ERROR, gettext("No %s action defined.\n"),
8522 		    IPGPC_NAME);
8523 		goto fail;
8524 	}
8525 
8526 	return (IPQOS_CONF_SUCCESS);
8527 fail:
8528 	free_actions(*conf);
8529 	*conf = NULL;
8530 	return (IPQOS_CONF_ERR);
8531 }
8532 
8533 /* ************************ kernel config retrieval ************************ */
8534 
8535 
8536 /*
8537  * read the current configuration from the kernel and make *conf a ptr to it.
8538  * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCES.
8539  */
8540 static int
8541 readkconf(ipqos_conf_action_t **conf)
8542 {
8543 
8544 	int res;
8545 	char **modnames = NULL;
8546 	int nmods;
8547 	char **actnames = NULL;
8548 	int nacts;
8549 	int x, y;
8550 	FILE *tfp;
8551 	int openerr;
8552 	ipqos_actinfo_prm_t ai_prm;
8553 
8554 
8555 	IPQOSCDBG0(L0, "In readkconf\n");
8556 
8557 	/* initialise conf to NULL */
8558 	*conf = NULL;
8559 
8560 	/* get list of modules currently loaded */
8561 
8562 	res = ipp_list_mods(&modnames, &nmods);
8563 	if (res != 0) {
8564 		ipqos_msg(MT_ENOSTR, "ipp_list_mods");
8565 		return (IPQOS_CONF_ERR);
8566 	}
8567 
8568 	/*
8569 	 * iterate through all loaded modules retrieving their list of actions
8570 	 * and then retrieving the configuration of each of these
8571 	 * and attatching it to conf.
8572 	 */
8573 	for (x = 0; x < nmods; x++) {
8574 
8575 		/* skip actions of modules that we can't open types file of */
8576 
8577 		if ((tfp = validmod(modnames[x], &openerr)) == NULL) {
8578 
8579 			/* mem error */
8580 
8581 			if (!openerr) {
8582 				goto fail;
8583 
8584 			/*
8585 			 * fopen fail - if we failed because the file didn't
8586 			 * exist we assume this is an unknown module and
8587 			 * ignore this module, otherwise error.
8588 			 */
8589 			} else {
8590 				if (errno == ENOENT) {
8591 					continue;
8592 				} else {
8593 					ipqos_msg(MT_ENOSTR, "fopen");
8594 					goto fail;
8595 				}
8596 			}
8597 		}
8598 		(void) fclose(tfp);
8599 
8600 		/* get action list for this module */
8601 
8602 		res = ipp_mod_list_actions(modnames[x], &actnames, &nacts);
8603 		if (res != 0) {
8604 			ipqos_msg(MT_ENOSTR, "ipp_mod_list_actions");
8605 			goto fail;
8606 		}
8607 
8608 		/* read config of each action of this module */
8609 
8610 		for (y = 0; y < nacts; y++) {
8611 			ai_prm.action = alloc_action();
8612 			if (ai_prm.action == NULL) {
8613 				goto fail;
8614 			}
8615 
8616 			/* copy action name into action struct */
8617 
8618 			(void) strlcpy(ai_prm.action->name, actnames[y],
8619 			    IPQOS_CONF_NAME_LEN);
8620 
8621 			/* copy module name into action struct */
8622 
8623 			(void) strlcpy(ai_prm.action->module, modnames[x],
8624 			    IPQOS_CONF_NAME_LEN);
8625 
8626 			/* get action info */
8627 
8628 			res = ipp_action_info(actnames[y],
8629 			    (int (*)(nvlist_t *, void *))parse_kaction,
8630 			    (void *)&ai_prm, 0);
8631 			if (res != 0) {
8632 				/* was this an ipp error */
8633 				if (ai_prm.intl_ret == IPQOS_CONF_SUCCESS) {
8634 					ipqos_msg(MT_ENOSTR,
8635 					    "ipp_action_info");
8636 				}
8637 				goto fail;
8638 			}
8639 
8640 			ADD_TO_LIST(conf, ai_prm.action);
8641 		}
8642 
8643 		cleanup_string_table(actnames, nacts);
8644 	}
8645 
8646 	cleanup_string_table(modnames, nmods);
8647 	return (IPQOS_CONF_SUCCESS);
8648 fail:
8649 	free_actions(*conf);
8650 	*conf = NULL;
8651 	cleanup_string_table(modnames, nmods);
8652 	cleanup_string_table(actnames, nacts);
8653 	return (IPQOS_CONF_ERR);
8654 }
8655 
8656 /*
8657  * This is passed as a parameter to ipp_action_info() in readkaction and
8658  * is called back one for each configuration element within the action
8659  * specified. This results in filters and classes being created and chained
8660  * off of action, and action having its params set.
8661  * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCESS.
8662  */
8663 static int
8664 parse_kaction(
8665 nvlist_t *nvl,
8666 ipqos_actinfo_prm_t *ai_prm)
8667 {
8668 
8669 	int ret;
8670 	uint8_t cfgtype;
8671 	ipqos_conf_filter_t *filter = NULL;
8672 	ipqos_conf_class_t *class = NULL;
8673 	ipqos_conf_action_t *action = ai_prm->action;
8674 
8675 
8676 	IPQOSCDBG1(KRET, "In parse_kaction: action_name: %s\n", action->name);
8677 
8678 	/* get config type */
8679 
8680 	(void) nvlist_lookup_byte(nvl, IPP_CONFIG_TYPE, &cfgtype);
8681 	(void) nvlist_remove_all(nvl, IPP_CONFIG_TYPE);
8682 
8683 	switch (cfgtype) {
8684 		case CLASSIFIER_ADD_FILTER: {
8685 			/*
8686 			 * parse the passed filter nvlist
8687 			 * and add result to action's filter list.
8688 			 */
8689 			filter = alloc_filter();
8690 			if (filter == NULL) {
8691 				ai_prm->intl_ret = IPQOS_CONF_ERR;
8692 				return (IPQOS_CONF_ERR);
8693 			}
8694 
8695 			ret = parse_kfilter(filter, nvl);
8696 			if (ret != IPQOS_CONF_SUCCESS) {
8697 				free_filter(filter);
8698 				ai_prm->intl_ret = IPQOS_CONF_ERR;
8699 				return (ret);
8700 			}
8701 
8702 			ADD_TO_LIST(&action->filters, filter);
8703 			break;
8704 		}
8705 		case CLASSIFIER_ADD_CLASS:
8706 		case CLASSIFIER_MODIFY_CLASS: {
8707 			/*
8708 			 * parse the passed class nvlist
8709 			 * and add result to action's class list.
8710 			 */
8711 			class = alloc_class();
8712 			if (class == NULL) {
8713 				ai_prm->intl_ret = IPQOS_CONF_ERR;
8714 				return (IPQOS_CONF_ERR);
8715 			}
8716 
8717 			ret = parse_kclass(class, nvl);
8718 			if (ret != IPQOS_CONF_SUCCESS) {
8719 				free_class(class);
8720 				ai_prm->intl_ret = IPQOS_CONF_ERR;
8721 				return (ret);
8722 			}
8723 
8724 			ADD_TO_LIST(&action->classes, class);
8725 			break;
8726 		}
8727 		case IPP_SET: {
8728 			/*
8729 			 * we don't alloc a params struct as it is created
8730 			 * as part of an action.
8731 			 */
8732 
8733 			/* parse the passed params nvlist */
8734 
8735 			ret = parse_kparams(action->module, action->params,
8736 			    nvl);
8737 			if (ret != IPQOS_CONF_SUCCESS) {
8738 				ai_prm->intl_ret = IPQOS_CONF_ERR;
8739 				return (ret);
8740 			}
8741 		}
8742 	}
8743 
8744 	ai_prm->intl_ret = IPQOS_CONF_SUCCESS;
8745 	return (IPQOS_CONF_SUCCESS);
8746 }
8747 
8748 /*
8749  * parses a params nvlist returned from the kernel.
8750  * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCES.
8751  */
8752 int
8753 parse_kparams(
8754 char *module,
8755 ipqos_conf_params_t *params,
8756 nvlist_t *nvl) {
8757 
8758 	int ret;
8759 	ipqos_nvtype_t type;
8760 	str_val_nd_t *tmp;
8761 	char *act;
8762 	uint32_t u32;
8763 	nvpair_t *nvp;
8764 	FILE *tfp;
8765 	char dfltst[IPQOS_VALST_MAXLEN];
8766 	char *param;
8767 	nvlist_t *nvlcp;
8768 	int openerr;
8769 	place_t place;
8770 
8771 	IPQOSCDBG0(KRET, "In parse_kparams:\n");
8772 
8773 	/* get stream to module types file */
8774 
8775 	tfp = validmod(module, &openerr);
8776 	if (tfp == NULL) {
8777 		if (openerr) {
8778 			ipqos_msg(MT_ENOSTR, "fopen");
8779 		}
8780 		return (IPQOS_CONF_ERR);
8781 	}
8782 
8783 	/* make copy of passed in nvlist as it is freed by the caller */
8784 
8785 	ret = nvlist_dup(nvl, &nvlcp, 0);
8786 	if (ret != 0) {
8787 		return (IPQOS_CONF_ERR);
8788 	}
8789 
8790 	/*
8791 	 * get config originator and remove from nvlist. If no owner we
8792 	 * assume ownership.
8793 	 */
8794 	ret = nvlist_lookup_uint32(nvlcp, IPP_CONFIG_ORIGINATOR, &u32);
8795 	if (ret == 0) {
8796 		params->originator = u32;
8797 		(void) nvlist_remove_all(nvlcp, IPP_CONFIG_ORIGINATOR);
8798 	} else {
8799 		params->originator = IPP_CONFIG_IPQOSCONF;
8800 	}
8801 
8802 	/* get action stats and remove from nvlist */
8803 
8804 	ret = nvlist_lookup_uint32(nvlcp, IPP_ACTION_STATS_ENABLE, &u32);
8805 	if (ret == 0) {
8806 		params->stats_enable = *(boolean_t *)&u32;
8807 		(void) nvlist_remove_all(nvlcp, IPP_ACTION_STATS_ENABLE);
8808 	}
8809 
8810 	/*
8811 	 * loop throught nvlist elements and for those that are actions create
8812 	 * action ref entrys for them.
8813 	 */
8814 	nvp = nvlist_next_nvpair(nvlcp, NULL);
8815 	while (nvp != NULL) {
8816 		param = SHORT_NAME(nvpair_name(nvp));
8817 		place = PL_ANY;
8818 		ret = readtype(tfp, module, param, &type, &tmp, dfltst,
8819 		    B_FALSE, &place);
8820 		if (ret != IPQOS_CONF_SUCCESS) {
8821 			goto fail;
8822 		}
8823 
8824 		if ((place == PL_PARAMS) &&	/* avoid map entries */
8825 		    (type == IPQOS_DATA_TYPE_ACTION)) {
8826 			(void) nvpair_value_string(nvp, &act);
8827 			ret = add_aref(&params->actions, nvpair_name(nvp), act);
8828 			if (ret != IPQOS_CONF_SUCCESS) {
8829 				goto fail;
8830 			}
8831 		}
8832 
8833 		nvp = nvlist_next_nvpair(nvlcp, nvp);
8834 	}
8835 
8836 	/* assign copied nvlist to params struct */
8837 
8838 	params->nvlist = nvlcp;
8839 
8840 	(void) fclose(tfp);
8841 	return (IPQOS_CONF_SUCCESS);
8842 fail:
8843 	(void) fclose(tfp);
8844 	free_arefs(params->actions);
8845 	params->actions = NULL;
8846 	nvlist_free(nvlcp);
8847 	return (IPQOS_CONF_ERR);
8848 }
8849 
8850 /*
8851  * parses a classes nvlist returned from the kernel.
8852  * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCES.
8853  */
8854 static int
8855 parse_kclass(
8856 ipqos_conf_class_t *class,
8857 nvlist_t *nvl)
8858 {
8859 
8860 	int ret;
8861 	uint32_t u32;
8862 	char *str;
8863 
8864 	IPQOSCDBG0(KRET, "In parse_kclass:\n");
8865 
8866 	/* lookup object originator */
8867 
8868 	ret = nvlist_lookup_uint32(nvl, IPP_CONFIG_ORIGINATOR, &u32);
8869 	if (ret == 0) {
8870 		class->originator = u32;
8871 	} else {
8872 		class->originator = IPP_CONFIG_IPQOSCONF;
8873 	}
8874 
8875 	/* lookup name */
8876 
8877 	(void) nvlist_lookup_string(nvl, CLASSIFIER_CLASS_NAME, &str);
8878 	(void) strlcpy(class->name, str, IPQOS_CONF_NAME_LEN);
8879 	IPQOSCDBG1(KRET, "reading class %s\n", class->name);
8880 
8881 	/* lookup next action */
8882 
8883 	(void) nvlist_lookup_string(nvl, CLASSIFIER_NEXT_ACTION, &str);
8884 	ret = add_aref(&class->alist, NULL, str);
8885 	if (ret != IPQOS_CONF_SUCCESS) {
8886 		return (IPQOS_CONF_ERR);
8887 	}
8888 
8889 	/* lookup stats enable */
8890 
8891 	ret = nvlist_lookup_uint32(nvl, CLASSIFIER_CLASS_STATS_ENABLE, &u32);
8892 	if (ret == 0) {
8893 		class->stats_enable = *(boolean_t *)&u32;
8894 	}
8895 
8896 	return (IPQOS_CONF_SUCCESS);
8897 }
8898 
8899 /*
8900  * parses a filters nvlist returned from the kernel.
8901  * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCES.
8902  */
8903 static int
8904 parse_kfilter(
8905 ipqos_conf_filter_t *filter,
8906 nvlist_t *nvl)
8907 {
8908 
8909 	int ret;
8910 	char *str;
8911 	uint32_t u32;
8912 	nvlist_t *nvlcp;
8913 	char *end;
8914 
8915 	IPQOSCDBG0(KRET, "In parse_kfilter:\n");
8916 
8917 	/* make copy of passed in nvlist as it is freed by the caller */
8918 
8919 	ret = nvlist_dup(nvl, &nvlcp, 0);
8920 	if (ret != 0) {
8921 		return (IPQOS_CONF_ERR);
8922 	}
8923 
8924 	/* lookup originator */
8925 
8926 	ret = nvlist_lookup_uint32(nvlcp, IPP_CONFIG_ORIGINATOR, &u32);
8927 	if (ret == 0) {
8928 		filter->originator = u32;
8929 		(void) nvlist_remove_all(nvlcp, IPP_CONFIG_ORIGINATOR);
8930 	} else {
8931 		filter->originator = IPP_CONFIG_IPQOSCONF;
8932 	}
8933 
8934 	/* lookup filter name */
8935 
8936 	(void) nvlist_lookup_string(nvlcp, CLASSIFIER_FILTER_NAME, &str);
8937 	(void) strlcpy(filter->name, str, IPQOS_CONF_NAME_LEN);
8938 	(void) nvlist_remove_all(nvlcp, CLASSIFIER_FILTER_NAME);
8939 
8940 	/* lookup class name */
8941 
8942 	(void) nvlist_lookup_string(nvlcp, CLASSIFIER_CLASS_NAME, &str);
8943 	(void) strlcpy(filter->class_name, str, IPQOS_CONF_NAME_LEN);
8944 	(void) nvlist_remove_all(nvlcp, CLASSIFIER_CLASS_NAME);
8945 
8946 	/* lookup src and dst host names if present */
8947 
8948 	if (nvlist_lookup_string(nvlcp, IPGPC_SADDR_HOSTNAME, &str) == 0) {
8949 		filter->src_nd_name = malloc(strlen(str) + 1);
8950 		if (filter->src_nd_name) {
8951 			(void) strcpy(filter->src_nd_name, str);
8952 			(void) nvlist_remove_all(nvlcp, IPGPC_SADDR_HOSTNAME);
8953 		} else {
8954 			ipqos_msg(MT_ENOSTR, "malloc");
8955 			nvlist_free(nvlcp);
8956 			return (IPQOS_CONF_ERR);
8957 		}
8958 	}
8959 	if (nvlist_lookup_string(nvlcp, IPGPC_DADDR_HOSTNAME, &str) == 0) {
8960 		filter->dst_nd_name = malloc(strlen(str) + 1);
8961 		if (filter->dst_nd_name) {
8962 			(void) strcpy(filter->dst_nd_name, str);
8963 			(void) nvlist_remove_all(nvlcp, IPGPC_DADDR_HOSTNAME);
8964 		} else {
8965 			ipqos_msg(MT_ENOSTR, "malloc");
8966 			nvlist_free(nvlcp);
8967 			return (IPQOS_CONF_ERR);
8968 		}
8969 	}
8970 
8971 	/* lookup ip_version if present */
8972 
8973 	if (nvlist_lookup_string(nvlcp, IPGPC_FILTER_PRIVATE, &str) == 0) {
8974 		filter->ip_versions = (uint32_t)strtol(str, &end, 0);
8975 		if (end != str) {
8976 			(void) nvlist_remove_all(nvlcp, IPGPC_FILTER_PRIVATE);
8977 		} else {
8978 			ipqos_msg(MT_ERROR,
8979 			    gettext("Corrupted ip_version returned from "
8980 			    "kernel.\n"));
8981 			nvlist_free(nvlcp);
8982 			return (IPQOS_CONF_ERR);
8983 		}
8984 	}
8985 
8986 	/* lookup filter instance if present */
8987 
8988 	ret = nvlist_lookup_int32(nvlcp, IPGPC_FILTER_INSTANCE,
8989 	    &filter->instance);
8990 	if (ret != 0) {
8991 		filter->instance = -1;
8992 	} else {
8993 		(void) nvlist_remove_all(nvlcp, IPGPC_FILTER_INSTANCE);
8994 	}
8995 
8996 	/* attach new trimmed nvlist to filter */
8997 	filter->nvlist = nvlcp;
8998 
8999 	return (IPQOS_CONF_SUCCESS);
9000 }
9001 
9002 
9003 /*
9004  * determines whether action_name is a virtual action name.
9005  * RETURNS: if virtual action 1, else 0.
9006  */
9007 static int
9008 virtual_action(char *action_name)
9009 {
9010 
9011 	if (strcmp(action_name, IPP_ANAME_CONT) == 0 ||
9012 	    strcmp(action_name, IPP_ANAME_DEFER) == 0 ||
9013 	    strcmp(action_name, IPP_ANAME_DROP) == 0) {
9014 		return (1);
9015 	}
9016 
9017 	return (0);
9018 }
9019 
9020 /*
9021  * remove all the actions within the kernel. If there is a failure
9022  * modified is set to represent whether the attempt to flush modified
9023  * the configuration in any way.
9024  * RETURNS: IPQOS_CONF_ERR if the ipp_* functions return any errors,
9025  * else IPQOS_CONF_SUCCESS.
9026  */
9027 static int
9028 flush(
9029 boolean_t *modified)
9030 {
9031 
9032 	int res;
9033 	char **modnames = NULL;
9034 	int nmods;
9035 	char **actnames = NULL;
9036 	int nacts;
9037 	int x, y;
9038 
9039 	IPQOSCDBG0(L0, "In flush\n");
9040 
9041 	*modified = B_FALSE;
9042 
9043 	/*
9044 	 * get list of modules currently loaded.
9045 	 */
9046 	res = ipp_list_mods(&modnames, &nmods);
9047 	if (res != 0) {
9048 		ipqos_msg(MT_ENOSTR, "ipp_list_mods");
9049 		return (IPQOS_CONF_ERR);
9050 	}
9051 
9052 	/*
9053 	 * iterate through all the modules listing their actions and
9054 	 * deleting all of them.
9055 	 */
9056 	for (x = 0; x < nmods; x++) {
9057 		IPQOSCDBG1(APPLY, "Getting actions of module %s.\n",
9058 		    modnames[x]);
9059 		res = ipp_mod_list_actions(modnames[x], &actnames, &nacts);
9060 		if (res != 0) {
9061 			ipqos_msg(MT_ENOSTR, "ipp_mod_list_actions");
9062 			cleanup_string_table(modnames, nmods);
9063 			return (IPQOS_CONF_ERR);
9064 		}
9065 
9066 		for (y = 0; y < nacts; y++) {
9067 			IPQOSCDBG1(APPLY, "deleting action %s\n", actnames[y]);
9068 			res = ipp_action_destroy(actnames[y], IPP_DESTROY_REF);
9069 			/*
9070 			 * if fails for reason other than action doesn't
9071 			 * exist or action has dependency.
9072 			 */
9073 			if (res != 0 && errno != ENOENT && errno != EBUSY) {
9074 				ipqos_msg(MT_ENOSTR, "ipp_action_destroy");
9075 				cleanup_string_table(modnames, nmods);
9076 				cleanup_string_table(actnames, nacts);
9077 				return (IPQOS_CONF_ERR);
9078 			}
9079 
9080 			if (res == 0)
9081 				*modified = B_TRUE;
9082 		}
9083 		cleanup_string_table(actnames, nacts);
9084 	}
9085 	cleanup_string_table(modnames, nmods);
9086 
9087 	return (IPQOS_CONF_SUCCESS);
9088 }
9089 
9090 /*
9091  * Trys to flush the configuration. If it fails and nothing has been modified
9092  * and force_flush is false just return an error, otherwise persist trying to
9093  * completion.
9094  * RETURNS: IPQOS_CONF_ERR if flush attempt failed without modifying anything
9095  * and force_flush was set to false, otherwise IPQOS_CONF_SUCCESS.
9096  */
9097 static int
9098 atomic_flush(
9099 boolean_t force_flush)
9100 {
9101 	int x = 0;
9102 	int res;
9103 	boolean_t modified = B_FALSE;
9104 
9105 	/*
9106 	 * attempt first flush of config.
9107 	 */
9108 	res = flush(&modified);
9109 	if ((force_flush == B_FALSE) && (res != IPQOS_CONF_SUCCESS) &&
9110 	    (modified == B_FALSE)) {
9111 		return (IPQOS_CONF_ERR);
9112 	} else if (res == IPQOS_CONF_SUCCESS) {
9113 		return (IPQOS_CONF_SUCCESS);
9114 	}
9115 
9116 	/*
9117 	 * failed flush that modified config, or force flush set; loop till
9118 	 * successful flush.
9119 	 */
9120 	while (res != IPQOS_CONF_SUCCESS) {
9121 		if (x == 5) {	/* 10 secs since start/last message. */
9122 			ipqos_msg(MT_ERROR,
9123 			    gettext("Retrying configuration flush.\n"));
9124 			x = 0;
9125 		}
9126 		(void) sleep(2);
9127 		x++;
9128 		res = flush(&modified);
9129 	}
9130 
9131 	return (IPQOS_CONF_SUCCESS);
9132 }
9133 
9134 /*
9135  * Performs a flush of the configuration within a signal blocking region
9136  * so that there's minimal chance of it being killed and the flush only
9137  * partially completing.
9138  * RETURNS: IPQOS_CONF_SUCCESS (for symmetry with the other main functions).
9139  */
9140 static int
9141 flushconf()
9142 {
9143 	int res;
9144 
9145 	/*
9146 	 * make sure that flush is as atomic as possible.
9147 	 */
9148 	if ((res = block_all_signals()) == -1)
9149 		return (IPQOS_CONF_ERR);
9150 
9151 	res = atomic_flush(B_FALSE);
9152 
9153 	/*
9154 	 * restore signals.
9155 	 */
9156 	(void) restore_all_signals();
9157 
9158 	if (res == IPQOS_CONF_SUCCESS) {
9159 		ipqos_msg(MT_LOG, gettext("Configuration flushed.\n"));
9160 	} else {
9161 		ipqos_msg(MT_ENOSTR, "atomic_flush");
9162 	}
9163 
9164 	return (res);
9165 }
9166 
9167 static int
9168 in_string_table(char *stable[], int size, char *string)
9169 {
9170 
9171 	IPQOSCDBG1(L1, "In in_string_table: search string %s\n", string);
9172 
9173 	for (--size; size >= 0; size--) {
9174 		if (strcmp(stable[size], string) == 0) {
9175 			IPQOSCDBG1(L1, "Found %s in string table\n", string);
9176 			return (1);
9177 		}
9178 	}
9179 
9180 	return (0);
9181 }
9182 
9183 /* free the memory occupied by the string table ctable and its contents. */
9184 static void
9185 cleanup_string_table(char *ctable[], int size)
9186 {
9187 
9188 	int x;
9189 
9190 	if (ctable) {
9191 		for (x = 0; x < size; x++) {
9192 			free(ctable[x]);
9193 		}
9194 		free(ctable);
9195 	}
9196 }
9197 
9198 #if 0
9199 
9200 /*
9201  * makes a copy of a string table and returns a ptr to it.
9202  * RETURNS: NULL on error or if size was 0, else ptr to copied table.
9203  */
9204 static char **
9205 copy_string_table(char *stable1[], int size)
9206 {
9207 
9208 	char **st = NULL;
9209 	int pos;
9210 
9211 	/* create char ptr array */
9212 
9213 	st = malloc(size * sizeof (char *));
9214 	if (st == NULL) {
9215 		ipqos_msg(MT_ENOSTR, "malloc");
9216 		return (st);
9217 	}
9218 
9219 	/* create copy of each string from stable1 in array */
9220 
9221 	for (pos = size - 1; pos >= 0; pos--) {
9222 		st[pos] = malloc(strlen(stable1[pos] + 1));
9223 		if (st[pos] == NULL) {
9224 			for (pos++; pos < size; pos++)
9225 				free(st[pos]);
9226 			free(st);
9227 			ipqos_msg(MT_ENOSTR, "malloc");
9228 			return (NULL);
9229 		}
9230 
9231 		(void) strcpy(st[pos], stable1[pos]);
9232 	}
9233 
9234 	return (st);
9235 }
9236 #endif	/* 0 */
9237 
9238 /*
9239  * retry lookups on filters that soft failed a previous lookup and
9240  * were put on the retry list.
9241  * RETURNS: IPQOS_CONF_ERR on any errors, else IPQOS_CONF_SUCCESS.
9242  */
9243 static int
9244 retry_name_lookups(
9245 ipqos_conf_action_t *actions)
9246 {
9247 
9248 	ipqos_conf_action_t *act;
9249 	ipqos_conf_filter_t **new_filters;
9250 	ipqos_conf_filter_t *flt;
9251 
9252 	IPQOSCDBG0(APPLY, "In retry_name_lookups:\n");
9253 
9254 	for (act = actions; act != NULL; act = act->next) {
9255 
9256 		/* store start of new resolved filters */
9257 		LIST_END(&act->filters, &new_filters);
9258 
9259 		/*
9260 		 * do name resolution on retry list adding resolved filters
9261 		 * to end of actions filters.
9262 		 */
9263 		for (flt = act->retry_filters; flt != NULL; flt = flt->next) {
9264 
9265 			if (domultihome(flt, new_filters, B_TRUE) !=
9266 			    IPQOS_CONF_SUCCESS) {
9267 
9268 				/* if resource failure */
9269 
9270 				if (flt->nlerr == 0) {
9271 					return (IPQOS_CONF_ERR);
9272 				}
9273 			}
9274 		}
9275 
9276 		/* add the newly resolved filters to the kernel action */
9277 
9278 		for (flt = *new_filters; flt != NULL; flt = flt->next) {
9279 			if (add_filter(act->name, flt, act->module_version) !=
9280 			    IPQOS_CONF_SUCCESS) {
9281 				return (IPQOS_CONF_ERR);
9282 			}
9283 		}
9284 	}
9285 
9286 	return (IPQOS_CONF_SUCCESS);
9287 }
9288 
9289 /*
9290  * write the configuration in conf to the file given in dstpath. This
9291  * is done by writing first to a temporary file and then renaming that
9292  * file to dstpath. This assures an atomic write.
9293  * RETURNS: IPQOS_CONF_ERR on any errors, else IPQOS_CONF_SUCCESS.
9294  */
9295 static int
9296 writeconf(
9297 ipqos_conf_action_t *conf,
9298 char *dstpath)
9299 {
9300 
9301 	FILE *tmpfp;
9302 	char *tmppath;
9303 	char *pathend;
9304 	ipqos_conf_action_t *act;
9305 	int res;
9306 
9307 	IPQOSCDBG0(L0, "in writeconf\n");
9308 
9309 	/* construct tmp file path so we can use rename() */
9310 
9311 	pathend = strrchr(dstpath, '/');
9312 
9313 	/* dstpath in current dir */
9314 
9315 	if (pathend == NULL) {
9316 		tmppath = malloc(strlen("ipqosconf.tmp") + 1);
9317 		if (tmppath == NULL) {
9318 			ipqos_msg(MT_ENOSTR, "malloc");
9319 			return (IPQOS_CONF_ERR);
9320 		}
9321 		(void) strcpy(tmppath, "ipqosconf.tmp");
9322 
9323 	/* dstpath in root dir */
9324 
9325 	} else if (pathend == dstpath) {
9326 		tmppath = malloc(strlen("/ipqosconf.tmp") + 1);
9327 		if (tmppath == NULL) {
9328 			ipqos_msg(MT_ENOSTR, "malloc");
9329 			return (IPQOS_CONF_ERR);
9330 		}
9331 		(void) strcpy(tmppath, "/ipqosconf.tmp");
9332 
9333 	/* not pwd or root */
9334 
9335 	} else {
9336 		*pathend = NULL;
9337 		tmppath = malloc(strlen(dstpath) + strlen("/ipqosconf.tmp") +
9338 		    1);
9339 		if (tmppath == NULL) {
9340 			ipqos_msg(MT_ENOSTR, "malloc");
9341 			return (IPQOS_CONF_ERR);
9342 		}
9343 		(void) strcpy(tmppath, dstpath);
9344 		(void) strcat(tmppath, "/ipqosconf.tmp");
9345 		*pathend = '/';
9346 	}
9347 
9348 
9349 	/* open tmp file */
9350 
9351 	tmpfp = fopen(tmppath, "w");
9352 	if (tmpfp == NULL) {
9353 		ipqos_msg(MT_ENOSTR, "fopen");
9354 		free(tmppath);
9355 		return (IPQOS_CONF_ERR);
9356 	}
9357 
9358 	/* write out format version */
9359 
9360 	(void) fprintf(tmpfp, "%s %d.%d\n\n", IPQOS_FMT_VERSION_STR,
9361 	    IPQOS_CUR_FMT_MAJOR_VER, IPQOS_CUR_FMT_MINOR_VER);
9362 
9363 	/*
9364 	 * loop through actions in list writing ipqosconf originated
9365 	 * ones out to the tmp file.
9366 	 */
9367 	for (act = conf; act != NULL; act = act->next) {
9368 		if (act->params->originator == IPP_CONFIG_IPQOSCONF) {
9369 			res = printaction(tmpfp, act, 0, 0);
9370 			if (res != IPQOS_CONF_SUCCESS) {
9371 				free(tmppath);
9372 				(void) fclose(tmpfp);
9373 				return (res);
9374 			}
9375 		}
9376 	}
9377 	(void) fclose(tmpfp);
9378 
9379 	/* rename tmp file to dst file */
9380 
9381 	if (rename(tmppath, dstpath) != 0) {
9382 		ipqos_msg(MT_ENOSTR, "rename");
9383 		free(tmppath);
9384 		return (IPQOS_CONF_ERR);
9385 	}
9386 	free(tmppath);
9387 
9388 	return (IPQOS_CONF_SUCCESS);
9389 }
9390 
9391 /*
9392  * read the configuration back from the kernel and then write each of the
9393  * actions read to IPQOS_CONF_INIT_PATH.
9394  * RETURNS: IPQOS_CONF_ERR if error, else IPQOS_CONF_SUCCESS.
9395  */
9396 static int
9397 commitconf()
9398 {
9399 
9400 	int ret;
9401 	ipqos_conf_action_t *conf;
9402 
9403 	IPQOSCDBG0(L0, "In commitconf\n");
9404 
9405 	/* read the configuration from the kernel */
9406 
9407 	ret = readkconf(&conf);
9408 	if (ret != IPQOS_CONF_SUCCESS) {
9409 		return (IPQOS_CONF_ERR);
9410 	}
9411 
9412 	/* dissallow a null config to be stored (we can't read one in) */
9413 
9414 	if (conf == NULL) {
9415 		ipqos_msg(MT_ERROR,
9416 		    gettext("Can't commit a null configuration.\n"));
9417 		return (IPQOS_CONF_ERR);
9418 	}
9419 
9420 	/* make sure if we create file that perms are 644 */
9421 
9422 	(void) umask(S_IXUSR | S_IWGRP | S_IXGRP | S_IWOTH | S_IXOTH);
9423 
9424 	/* write the configuration to the init file */
9425 
9426 	ret = writeconf(conf, IPQOS_CONF_INIT_PATH);
9427 	if (ret != IPQOS_CONF_SUCCESS) {
9428 		return (IPQOS_CONF_ERR);
9429 	}
9430 
9431 	ipqos_msg(MT_LOG,
9432 	    gettext("Current configuration saved to init file.\n"));
9433 
9434 	return (IPQOS_CONF_SUCCESS);
9435 }
9436 
9437 /*
9438  * Called in the event of a failed rollback. It first flushes the
9439  * current configuration, then attempts to apply the oconf (the old
9440  * one), and if that fails flushes again.
9441  *
9442  * RETURNS: IPQOS_CONF_ERR if the application of old config fails,
9443  * else IPQOS_CONF_SUCCESS.
9444  */
9445 static int
9446 rollback_recover(
9447 ipqos_conf_action_t *oconf)
9448 {
9449 	int res;
9450 
9451 	IPQOSCDBG0(RBK, "In rollback_recover\n");
9452 
9453 	/*
9454 	 * flush configuration.
9455 	 */
9456 	(void) atomic_flush(B_TRUE);
9457 
9458 	/*
9459 	 * mark all elements of old config for application.
9460 	 */
9461 	mark_config_new(oconf);
9462 
9463 	/*
9464 	 * attempt to apply old config.
9465 	 */
9466 	res = applydiff(oconf, NULL);
9467 	/*
9468 	 * if failed force flush of config.
9469 	 */
9470 	if (res != IPQOS_CONF_SUCCESS) {
9471 		(void) atomic_flush(B_TRUE);
9472 		return (IPQOS_CONF_ERR);
9473 	}
9474 
9475 	return (IPQOS_CONF_SUCCESS);
9476 }
9477 
9478 /*
9479  * read and apply the configuration contained if file ifile to the kernel.
9480  * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCES.
9481  */
9482 static int
9483 applyconf(char *ifile)
9484 {
9485 
9486 	FILE *ifp;
9487 	ipqos_conf_action_t *conf = NULL;
9488 	ipqos_conf_action_t *oconf = NULL;
9489 	ipqos_conf_action_t *act, *oact;
9490 	int res;
9491 
9492 	IPQOSCDBG0(L0, "In applyconf:\n");
9493 
9494 
9495 	/* if filename '-' read from stdin */
9496 
9497 	if (strcmp(ifile, "-") == 0) {
9498 		ifp = stdin;
9499 	} else {
9500 		ifp = fopen(ifile, "r");
9501 		if (ifp == NULL) {
9502 			ipqos_msg(MT_ERROR,
9503 			    gettext("Opening file %s for read: %s.\n"),
9504 			    ifile, strerror(errno));
9505 			return (IPQOS_CONF_ERR);
9506 		}
9507 	}
9508 
9509 	/* read in new configuration */
9510 
9511 	res = readconf(ifp, &conf);
9512 	if (res != IPQOS_CONF_SUCCESS) {
9513 		goto fail;
9514 	}
9515 
9516 	/* check configuration is valid */
9517 
9518 	res = validconf(conf, 1);
9519 	if (res != IPQOS_CONF_SUCCESS) {
9520 		goto fail;
9521 	}
9522 
9523 	/* read in kernel configuration */
9524 
9525 	res = readkconf(&oconf);
9526 	if (res != IPQOS_CONF_SUCCESS) {
9527 		goto fail;
9528 	}
9529 
9530 	/*
9531 	 * check there are no same named actions in both config file and the
9532 	 * the kernel that are for a different module. The application
9533 	 * system can't handle these as we would try to add the new
9534 	 * action before we deleted the old one and because actions
9535 	 * in the kernel are indexed solely on their name (their module
9536 	 * isn't included) the kernel would return an error. We want
9537 	 * to avoid this error and the resulting rollback.
9538 	 */
9539 	for (act = conf; act != NULL; act = act->next) {
9540 		for (oact = oconf; oact != NULL; oact = oact->next) {
9541 			/* found action */
9542 			if (strcmp(act->name, oact->name) == 0) {
9543 				/* different module */
9544 				if (strcmp(act->module, oact->module) != 0) {
9545 					ipqos_msg(MT_ERROR,
9546 					    gettext("Action at line %u has "
9547 					    "same name as currently "
9548 					    "installed action, but is for a "
9549 					    "different module.\n"),
9550 					    act->lineno);
9551 					goto fail;
9552 				/* same module - stop search */
9553 				} else {
9554 					break;
9555 				}
9556 			}
9557 		}
9558 	}
9559 
9560 
9561 	/* create links between actions for use with deletions etc.. */
9562 
9563 	res = validconf(oconf, 0);
9564 	if (res != IPQOS_CONF_SUCCESS) {
9565 		goto fail;
9566 	}
9567 
9568 	/* diff conf file against kernel */
9569 
9570 	res = diffconf(oconf, conf);
9571 	if (res != IPQOS_CONF_SUCCESS) {
9572 		goto fail;
9573 	}
9574 
9575 	/* make kernel mods as atomic as possible */
9576 
9577 	if ((res = block_all_signals()) == -1) {
9578 		res = IPQOS_CONF_ERR;
9579 		goto fail;
9580 	}
9581 
9582 	/* apply difference to kernel */
9583 
9584 	res = applydiff(conf, oconf);
9585 #ifdef	_IPQOS_CONF_DEBUG
9586 	if (force_rback || res != IPQOS_CONF_SUCCESS) {
9587 #else
9588 	if (res != IPQOS_CONF_SUCCESS) {
9589 #endif	/* _IPQOS_CONF_DEBUG */
9590 
9591 		res = rollback(conf, oconf);
9592 		if (res != IPQOS_CONF_SUCCESS) {
9593 			res = rollback_recover(oconf);
9594 			if (res != IPQOS_CONF_SUCCESS) {
9595 				/* system left flushed */
9596 				ipqos_msg(MT_ERROR,
9597 				    gettext("Failed to rollback from failed "
9598 				    "configuration, configuration flushed.\n"));
9599 				res = IPQOS_CONF_RECOVER_ERR;
9600 			} else {	/* old config re-applied */
9601 				ipqos_msg(MT_ERROR,
9602 				    gettext("Configuration failed, system "
9603 				    "state unchanged.\n"));
9604 				res = IPQOS_CONF_ERR;
9605 			}
9606 		} else {
9607 			ipqos_msg(MT_ERROR,
9608 			    gettext("Configuration failed, system "
9609 			    "state unchanged.\n"));
9610 			res = IPQOS_CONF_ERR;
9611 		}
9612 		goto fail;
9613 	}
9614 
9615 	/* retry any soft name lookup failures */
9616 
9617 	res = retry_name_lookups(conf);
9618 	if (res != IPQOS_CONF_SUCCESS) {
9619 		res = rollback(conf, oconf);
9620 		if (res != IPQOS_CONF_SUCCESS) {
9621 			res = rollback_recover(oconf);
9622 			if (res != IPQOS_CONF_SUCCESS) {
9623 			/* system left flushed */
9624 				ipqos_msg(MT_ERROR,
9625 				    gettext("Failed to rollback from failed "
9626 				    "configuration, configuration flushed.\n"));
9627 				res = IPQOS_CONF_RECOVER_ERR;
9628 			} else {	/* old config re-applied */
9629 				ipqos_msg(MT_ERROR,
9630 				    gettext("Configuration failed, system "
9631 				    "state unchanged.\n"));
9632 				res = IPQOS_CONF_ERR;
9633 			}
9634 		} else {
9635 			ipqos_msg(MT_ERROR,
9636 			    gettext("Configuration failed, system "
9637 			    "state unchanged.\n"));
9638 			res = IPQOS_CONF_ERR;
9639 		}
9640 		goto fail;
9641 
9642 	}
9643 
9644 	ipqos_msg(MT_LOG, gettext("IPQoS configuration applied.\n"));
9645 
9646 	/* re-enable signals */
9647 	(void) restore_all_signals();
9648 
9649 	(void) fclose(ifp);
9650 	free_actions(conf);
9651 	free_actions(oconf);
9652 	return (IPQOS_CONF_SUCCESS);
9653 fail:
9654 	(void) fclose(ifp);
9655 	(void) restore_all_signals();
9656 	if (conf)
9657 		free_actions(conf);
9658 	if (oconf)
9659 		free_actions(oconf);
9660 	if (res == IPQOS_CONF_RECOVER_ERR)
9661 		ipqos_msg(MT_LOG, gettext("Configuration flushed.\n"));
9662 	return (res);
9663 }
9664 
9665 static sigset_t set, oset;
9666 
9667 static int
9668 block_all_signals()
9669 {
9670 	if (sigfillset(&set) == -1) {
9671 		ipqos_msg(MT_ENOSTR, "sigfillset");
9672 		return (-1);
9673 	}
9674 	if (sigprocmask(SIG_SETMASK, &set, &oset) == -1) {
9675 		ipqos_msg(MT_ENOSTR, "sigprocmask");
9676 		return (-1);
9677 	}
9678 	return (0);
9679 }
9680 
9681 static int
9682 restore_all_signals()
9683 {
9684 	if (sigprocmask(SIG_SETMASK, &oset, NULL) == -1) {
9685 		ipqos_msg(MT_ENOSTR, "sigprocmask");
9686 		return (-1);
9687 	}
9688 	return (0);
9689 }
9690 
9691 static int
9692 unlock(int fd)
9693 {
9694 	if (lockf(fd, F_ULOCK, 0) == -1) {
9695 		ipqos_msg(MT_ENOSTR, "lockf");
9696 		return (-1);
9697 	}
9698 	return (0);
9699 }
9700 
9701 static int
9702 lock()
9703 {
9704 	int fd;
9705 	struct stat sbuf1;
9706 	struct stat sbuf2;
9707 
9708 	/*
9709 	 * Open the file with O_CREAT|O_EXCL. If it exists already, it
9710 	 * will fail. If it already exists, check whether it looks like
9711 	 * the one we created.
9712 	 */
9713 	(void) umask(0077);
9714 	if ((fd = open(IPQOS_CONF_LOCK_FILE, O_EXCL|O_CREAT|O_RDWR,
9715 	    S_IRUSR|S_IWUSR)) == -1) {
9716 		if (errno != EEXIST) {
9717 			/* Some other problem. */
9718 			ipqos_msg(MT_ENOSTR,
9719 			    gettext("Cannot open lock file %s"),
9720 			    IPQOS_CONF_LOCK_FILE);
9721 			return (-1);
9722 		}
9723 
9724 		/*
9725 		 * open() returned an EEXIST error. We don't fail yet
9726 		 * as it could be a residual from a previous
9727 		 * execution. However, we need to clear errno here.
9728 		 * If we don't and print_cmd_buf() is later invoked
9729 		 * as the result of a parsing error, it
9730 		 * will assume that the current error is EEXIST and
9731 		 * that a corresponding error message has already been
9732 		 * printed, which results in an incomplete error
9733 		 * message. If errno is zero, print_cmd_buf() will
9734 		 * assume that it is called as a result of a
9735 		 * parsing error and will print the appropriate
9736 		 * error message.
9737 		 */
9738 		errno = 0;
9739 
9740 		/*
9741 		 * File exists. make sure it is OK. We need to lstat()
9742 		 * as fstat() stats the file pointed to by the symbolic
9743 		 * link.
9744 		 */
9745 		if (lstat(IPQOS_CONF_LOCK_FILE, &sbuf1) == -1) {
9746 			ipqos_msg(MT_ENOSTR,
9747 			    gettext("Cannot lstat lock file %s\n"),
9748 			    IPQOS_CONF_LOCK_FILE);
9749 			return (-1);
9750 		}
9751 		/*
9752 		 * Check whether it is a regular file and not a symbolic
9753 		 * link. Its link count should be 1. The owner should be
9754 		 * root and the file should be empty.
9755 		 */
9756 		if (!S_ISREG(sbuf1.st_mode) ||
9757 		    sbuf1.st_nlink != 1 ||
9758 		    sbuf1.st_uid != 0 ||
9759 		    sbuf1.st_size != 0) {
9760 			ipqos_msg(MT_ERROR, gettext("Bad lock file %s.\n"),
9761 			    IPQOS_CONF_LOCK_FILE);
9762 			return (-1);
9763 		}
9764 		if ((fd = open(IPQOS_CONF_LOCK_FILE, O_CREAT|O_RDWR,
9765 		    S_IRUSR|S_IWUSR)) == -1) {
9766 			ipqos_msg(MT_ENOSTR,
9767 			    gettext("Cannot open lock file %s"),
9768 			    IPQOS_CONF_LOCK_FILE);
9769 			return (-1);
9770 		}
9771 
9772 		/* Check whether we opened the file that we lstat()ed. */
9773 		if (fstat(fd, &sbuf2) == -1) {
9774 			ipqos_msg(MT_ENOSTR,
9775 			    gettext("Cannot fstat lock file %s\n"),
9776 			    IPQOS_CONF_LOCK_FILE);
9777 			return (-1);
9778 		}
9779 		if (sbuf1.st_dev != sbuf2.st_dev ||
9780 		    sbuf1.st_ino != sbuf2.st_ino) {
9781 			/* File changed after we did the lstat() above */
9782 			ipqos_msg(MT_ERROR, gettext("Bad lock file %s.\n"),
9783 			    IPQOS_CONF_LOCK_FILE);
9784 			return (-1);
9785 		}
9786 	}
9787 	if (lockf(fd, F_LOCK, 0) == -1) {
9788 		ipqos_msg(MT_ENOSTR, "lockf");
9789 		return (-1);
9790 	}
9791 	return (fd);
9792 }
9793 
9794 /*
9795  * print the current kernel configuration out to stdout. If viewall
9796  * is set this causes more verbose configuration listing including
9797  * showing objects we didn't create, each instance of a mhome filter,
9798  * etc.. see printaction().
9799  * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCES.
9800  */
9801 
9802 static int
9803 viewconf(int viewall)
9804 {
9805 
9806 	ipqos_conf_action_t *conf = NULL;
9807 	ipqos_conf_action_t *act;
9808 	int ret;
9809 
9810 	IPQOSCDBG0(L0, "In viewconf\n");
9811 
9812 	/* get kernel configuration */
9813 
9814 	ret = readkconf(&conf);
9815 	if (ret != IPQOS_CONF_SUCCESS) {
9816 		return (IPQOS_CONF_ERR);
9817 	}
9818 
9819 	/* write out format version */
9820 
9821 	if (conf != NULL) {
9822 		(void) fprintf(stdout, "%s %d.%d\n\n", IPQOS_FMT_VERSION_STR,
9823 		    IPQOS_CUR_FMT_MAJOR_VER, IPQOS_CUR_FMT_MINOR_VER);
9824 	}
9825 
9826 	/* print each of the actions in the kernel config to stdout */
9827 
9828 	for (act = conf; act != NULL; act = act->next) {
9829 		ret = printaction(stdout, act, viewall, 0);
9830 		if (ret != IPQOS_CONF_SUCCESS) {
9831 			free_actions(conf);
9832 			return (ret);
9833 		}
9834 		(void) fprintf(stdout, "\n");
9835 	}
9836 
9837 	free_actions(conf);
9838 
9839 	return (IPQOS_CONF_SUCCESS);
9840 }
9841 
9842 
9843 /*
9844  * debug function that reads the config file and prints it out after
9845  * interpreting to stdout.
9846  */
9847 #ifdef	_IPQOS_CONF_DEBUG
9848 static int
9849 viewcfile(char *cfile)
9850 {
9851 
9852 	ipqos_conf_action_t *conf;
9853 	ipqos_conf_action_t *act;
9854 	int res;
9855 	FILE *ifp;
9856 	int viewall = 1;
9857 
9858 	IPQOSCDBG0(L0, "In viewcfile\n");
9859 	ifp = fopen(cfile, "r");
9860 	if (ifp == NULL) {
9861 		ipqos_msg(MT_ERROR, gettext("Opening file %s for read: %s.\n"),
9862 		    cfile, strerror(errno));
9863 		return (IPQOS_CONF_ERR);
9864 	}
9865 
9866 	res = readconf(ifp, &conf);
9867 	if (res != IPQOS_CONF_SUCCESS) {
9868 		free(ifp);
9869 		return (IPQOS_CONF_ERR);
9870 	}
9871 
9872 	/* print each of the actions in the kernel config to stdout */
9873 	for (act = conf; act != NULL; act = act->next) {
9874 		res = printaction(stdout, act, viewall, 0);
9875 		if (res != IPQOS_CONF_SUCCESS) {
9876 			free(ifp);
9877 			return (res);
9878 		}
9879 
9880 		(void) fprintf(stdout, "\n");
9881 	}
9882 
9883 	(void) fprintf(stdout, "\n");
9884 
9885 
9886 	return (IPQOS_CONF_SUCCESS);
9887 }
9888 #endif	/* _IPQOS_CONF_DEBUG */
9889 
9890 static void
9891 usage(void)
9892 {
9893 	(void) fprintf(stderr, gettext("usage:\n"
9894 	    "\tipqosconf [-sv] -a file|-\n"
9895 	    "\tipqosconf -c\n"
9896 	    "\tipqosconf -l\n"
9897 	    "\tipqosconf -L\n"
9898 	    "\tipqosconf -f\n"));
9899 }
9900 
9901 int
9902 main(int argc, char *argv[])
9903 {
9904 
9905 	int c;
9906 	char *ifile = NULL;
9907 	int args;
9908 	int ret;
9909 	int cmd;
9910 	int viewall = 0;
9911 	int lfp;
9912 
9913 	/* init global flags */
9914 	use_syslog = verbose = 0;
9915 
9916 	/* init current line number */
9917 	lineno = 0;
9918 
9919 	/* setup internationalisation */
9920 
9921 	(void) setlocale(LC_ALL, "");
9922 #if	!defined(TEXT_DOMAIN)
9923 #define	TEXT_DOMAIN "SYS_TEST"
9924 #endif
9925 	(void) textdomain(TEXT_DOMAIN);
9926 
9927 	/* setup syslog parameters */
9928 	openlog("ipqosconf", 0, LOG_USER);
9929 
9930 	args = 0;
9931 
9932 /* enable debug options */
9933 
9934 #ifdef	_IPQOS_CONF_DEBUG
9935 #define	DBGOPTS	"rz:"
9936 #else
9937 #define	DBGOPTS
9938 #endif	/* _IPQOS_CONF_DEBUG */
9939 
9940 	while ((c = getopt(argc, argv, "sca:vflL" DBGOPTS)) != EOF) {
9941 		switch (c) {
9942 #ifdef	_IPQOS_CONF_DEBUG
9943 			case 'z':
9944 				cmd = -1;
9945 				ifile = optarg;
9946 				if (*ifile == '\0') {
9947 					usage();
9948 					exit(1);
9949 				}
9950 				args++;
9951 				break;
9952 			case 'r':
9953 				force_rback++;
9954 				break;
9955 #endif	/* _IPQOS_CONF_DEBUG */
9956 			case 'c':
9957 				cmd = IPQOS_CONF_COMMIT;
9958 				args++;
9959 				break;
9960 			case 'a':
9961 				cmd = IPQOS_CONF_APPLY;
9962 				ifile = optarg;
9963 				if (*ifile == '\0') {
9964 					usage();
9965 					exit(1);
9966 				}
9967 				args++;
9968 				break;
9969 			case 'f':
9970 				cmd = IPQOS_CONF_FLUSH;
9971 				args++;
9972 				break;
9973 			case 'l':
9974 				cmd = IPQOS_CONF_VIEW;
9975 				args++;
9976 				break;
9977 			case 'L':
9978 				cmd = IPQOS_CONF_VIEW;
9979 				viewall++;
9980 				args++;
9981 				break;
9982 			case 'v':
9983 				verbose++;
9984 				break;
9985 			case 's':
9986 				use_syslog++;
9987 				break;
9988 			case '?':
9989 				usage();
9990 				return (1);
9991 		}
9992 	}
9993 
9994 	/*
9995 	 * dissallow non-option args, > 1 cmd args and syslog/verbose flags set
9996 	 * for anything but apply.
9997 	 */
9998 	if (optind != argc || args > 1 ||
9999 	    use_syslog && cmd != IPQOS_CONF_APPLY ||
10000 	    verbose && cmd != IPQOS_CONF_APPLY) {
10001 		usage();
10002 		exit(1);
10003 	}
10004 
10005 	/* if no cmd option then show config */
10006 
10007 	if (args == 0) {
10008 		cmd = IPQOS_CONF_VIEW;
10009 	}
10010 
10011 	/* stop concurrent ipqosconf invocations */
10012 	lfp = lock();
10013 	if (lfp == -1) {
10014 		exit(1);
10015 	}
10016 
10017 	switch (cmd) {
10018 #ifdef	_IPQOS_CONF_DEBUG
10019 		case -1:
10020 			ret = viewcfile(ifile);
10021 			break;
10022 #endif	/* _IPQOS_CONF_DEBUG */
10023 		case IPQOS_CONF_APPLY:
10024 			ret = applyconf(ifile);
10025 			break;
10026 		case IPQOS_CONF_COMMIT:
10027 			ret = commitconf();
10028 			break;
10029 		case IPQOS_CONF_VIEW:
10030 			ret = viewconf(viewall);
10031 			break;
10032 		case IPQOS_CONF_FLUSH:
10033 			ret = flushconf();
10034 			break;
10035 	}
10036 
10037 	(void) unlock(lfp);
10038 
10039 	return (ret);
10040 
10041 }
10042