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