Merge commit '0e8ce6ee7524ee6c2c24a2d4ed28ea71a5dcd85e'
[unleashed.git] / usr / src / cmd / cmd-inet / usr.sbin / ipqosconf / ipqosconf.c
blob9820f7be0d758bd495bd639d74818a68531ae13b
1 /*
2 * CDDL HEADER START
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.
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.
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]
20 * CDDL HEADER END
23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 /* enable debug output and some debug asserts */
28 #undef _IPQOS_CONF_DEBUG
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"
62 #if defined(_IPQOS_CONF_DEBUG)
64 /* debug level */
65 static int ipqosconf_dbg_flgs =
68 RBK |
69 MHME |
70 KRET |
71 DIFF |
72 APPLY |
73 L2 |
74 L1 |
75 L0 |
80 #define IPQOSCDBG0(lvl, x)\
81 if (lvl & ipqosconf_dbg_flgs)\
82 (void) fprintf(stderr, x)
84 #define IPQOSCDBG1(lvl, x, y)\
85 if (lvl & ipqosconf_dbg_flgs)\
86 (void) fprintf(stderr, x, y)
88 #define IPQOSCDBG2(lvl, x, y, z)\
89 if (lvl & ipqosconf_dbg_flgs)\
90 (void) fprintf(stderr, x, y, z)
92 #define IPQOSCDBG3(lvl, x, y, z, a)\
93 if (lvl & ipqosconf_dbg_flgs)\
94 (void) fprintf(stderr, x, y, z, a)
96 #define IPQOSCDBG4(lvl, x, y, z, a, b)\
97 if (lvl & ipqosconf_dbg_flgs)\
98 (void) fprintf(stderr, x, y, z, a, b)
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)
104 #else /* defined(_IPQOS_CONF_DEBUG) && !defined(lint) */
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)
113 #endif /* defined(_IPQOS_CONF_DEBUG) */
117 /* function prototypes */
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);
249 * macros to call list functions with the more complex list element type
250 * cast to the skeletal type iqpos_list_el_t.
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)
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.
263 #define QUOTE(x) #x
264 #define VAL2STR(x) QUOTE(x)
267 /* globals */
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}
290 /* table of name to id mappings for originator field */
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}
299 /* current parse line */
300 static int lineno;
302 /* verbose output flag */
303 static int verbose;
305 /* use syslog for msg reporting flag */
306 static int use_syslog;
308 #ifdef _IPQOS_CONF_DEBUG
310 * flag used to indicate that a rollback should be carried out regardless.
311 * Only settable during debug.
313 static int force_rback = 0;
314 #endif /* _IPQOS_CONF_DEBUG */
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.
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)
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.
333 /*PRINTFLIKE2*/
334 static void
335 ipqos_msg(enum msg_type msgt, char *format, ...)
337 va_list ap;
338 char str_buf[IPQOS_MSG_BUF_SZ];
339 char fmt_buf[IPQOS_MSG_BUF_SZ];
340 char *cp;
342 IPQOSCDBG0(L1, "In ipqos_msg:\n");
344 va_start(ap, format);
347 * send msgs to syslog if use_syslog set (except warning msgs),
348 * or a log msg.
350 if ((use_syslog && (msgt != MT_WARNING)) || msgt == MT_LOG) {
352 /* fill in format string */
353 (void) vsnprintf(str_buf, IPQOS_MSG_BUF_SZ, format, ap);
356 * print message to syslog with appropriate severity
358 if (msgt == MT_ERROR) {
359 syslog(LOG_ERR, str_buf);
360 } else if (msgt == MT_LOG) {
361 syslog(LOG_INFO, str_buf);
363 * for errno message type suffix with %m for syslog to
364 * interpret.
366 } else if (msgt == MT_ENOSTR) {
368 * remove any newline in message parameter.
369 * syslog will reapply a newline for us later.
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);
379 * send msgs to stderr if use_syslog not set (except log msgs), or
380 * if verbose set.
382 if ((!use_syslog && (msgt != MT_LOG)) || (verbose)) {
385 * prefix message with appropriate severity string
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;
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);
404 (void) strlcat(fmt_buf, format, IPQOS_MSG_BUF_SZ);
407 * for errno message type suffix message with errno string
409 if (msgt == MT_ENOSTR) {
411 * get rid of any newline in passed message.
412 * we'll apply another later.
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);
422 * append a newline to message if not one already.
424 if ((cp = strchr(fmt_buf, '\n')) == NULL)
425 (void) strlcat(fmt_buf, "\n", IPQOS_MSG_BUF_SZ);
427 (void) vfprintf(stderr, fmt_buf, ap);
430 va_end(ap);
433 /* **************** kernel filter/class/params manipulation fns *********** */
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.
442 static int
443 modify_params(
444 char *action_name,
445 nvlist_t **nvl,
446 int module_version,
447 boolean_t stats_enable)
450 int res;
451 int created = 0;
453 IPQOSCDBG1(APPLY, "In modify_params: action: %s\n", action_name);
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);
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;
473 * add module version
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;
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;
489 /* add ipqosconf as originator */
490 res = add_orig_ipqosconf(*nvl);
491 if (res != IPQOS_CONF_SUCCESS) {
492 goto fail;
495 /* call lib to do modify */
496 res = ipp_action_modify(action_name, nvl, 0);
497 if (res != 0) {
499 /* invalid parameters */
501 if (errno == EINVAL) {
502 ipqos_msg(MT_ERROR,
503 gettext("Invalid parameters for action %s.\n"),
504 action_name);
507 } else if (errno == ENOENT) {
508 ipqos_msg(MT_ERROR,
509 gettext("Mandatory parameter missing for "
510 "action %s.\n"), action_name);
513 } else { /* unexpected error */
514 ipqos_msg(MT_ERROR, gettext("Failed to modify action "
515 "%s parameters: %s.\n"), action_name,
516 strerror(errno));
519 goto fail;
522 return (IPQOS_CONF_SUCCESS);
523 fail:
524 if (created && *nvl != NULL) {
525 nvlist_free(*nvl);
526 *nvl = NULL;
528 return (IPQOS_CONF_ERR);
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.
537 static int
538 add_class(
539 char *action_name,
540 char *class_name,
541 int module_version,
542 boolean_t stats_enable,
543 char *first_action)
546 nvlist_t *nvl;
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"));
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);
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;
566 * add module version
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;
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;
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;
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;
593 /* add ipqosconf as originator */
594 if (add_orig_ipqosconf(nvl) != IPQOS_CONF_SUCCESS) {
595 goto fail;
598 /* call lib to do modify */
599 if (ipp_action_modify(action_name, &nvl, 0) != 0) {
601 /* ipgpc max classes */
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);
609 /* other errors */
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));
618 goto fail;
621 return (IPQOS_CONF_SUCCESS);
622 fail:
623 nvlist_free(nvl);
624 return (IPQOS_CONF_ERR);
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.
634 static int
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)
644 nvlist_t *nvl;
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);
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);
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;
665 * add module version
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;
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;
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;
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;
692 /* add originator ipqosconf */
693 if (add_orig_ipqosconf(nvl) != IPQOS_CONF_SUCCESS) {
694 goto fail;
697 /* call lib to do modify */
698 if (ipp_action_modify(action_name, &nvl, flags) != 0) {
700 /* generic error message */
702 ipqos_msg(MT_ERROR,
703 gettext("Modifying class %s in action %s failed: %s.\n"),
704 class_name, action_name, strerror(errno));
706 goto fail;
709 return (IPQOS_CONF_SUCCESS);
710 fail:
711 nvlist_free(nvl);
712 return (IPQOS_CONF_ERR);
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.
721 static int
722 remove_class(
723 char *action_name,
724 char *class_name,
725 int module_version,
726 enum ipp_flags flags)
729 nvlist_t *nvl;
731 IPQOSCDBG3(APPLY, "remove_class: action: %s, class: %s, "
732 "flags: %x\n", action_name, class_name, flags);
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);
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;
748 * add module version
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;
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;
762 if (ipp_action_modify(action_name, &nvl, flags) != 0) {
764 /* generic error message */
766 ipqos_msg(MT_ERROR,
767 gettext("Removing class %s in action %s failed: %s.\n"),
768 class_name, action_name, strerror(errno));
770 goto fail;
773 return (IPQOS_CONF_SUCCESS);
774 fail:
775 nvlist_free(nvl);
776 return (IPQOS_CONF_ERR);
780 * add the filter flt to the kernel action named action_name.
781 * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCES.
783 static int
784 add_filter(
785 char *action_name,
786 ipqos_conf_filter_t *flt,
787 int module_version)
790 nvlist_t *nvl = flt->nvlist;
791 char ipvsbuf[IPQOS_INT_STR_LEN];
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);
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);
805 * add module version
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);
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);
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);
826 /* add ipqosconf as originator to nvlist */
827 if (add_orig_ipqosconf(nvl) != IPQOS_CONF_SUCCESS) {
828 return (IPQOS_CONF_ERR);
831 /* add ipgpc specific nv entrys */
832 if (strcmp(action_name, IPGPC_CLASSIFY) == 0) {
834 /* add src and dst nodes to nvlist if present */
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);
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);
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.
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);
863 /* add filter instance if present */
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);
872 if (ipp_action_modify(action_name, &flt->nvlist, 0) != 0) {
874 /* invalid parameters */
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);
881 /* max ipgpc filters/classes */
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);
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));
896 return (IPQOS_CONF_ERR);
899 return (IPQOS_CONF_SUCCESS);
904 * modify the filter flt in the kernel action named action_name.
905 * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCES.
907 static int
908 modify_filter(
909 char *action_name,
910 ipqos_conf_filter_t *flt,
911 int module_version)
914 nvlist_t *nvl = flt->nvlist;
915 char ipvsbuf[IPQOS_INT_STR_LEN];
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);
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];
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));
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));
940 #endif /* _IPQOS_CONF_DEBUG */
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);
950 * add module version
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);
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);
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);
971 /* add originator ipqosconf to nvlist */
972 if (add_orig_ipqosconf(nvl) != IPQOS_CONF_SUCCESS) {
973 return (IPQOS_CONF_ERR);
976 /* add ipgpc specific nvpairs */
977 if (strcmp(action_name, IPGPC_CLASSIFY) == 0) {
979 /* add src and dst nodes to nvlist if present */
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);
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);
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.
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);
1008 /* add filter instance if present */
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);
1017 if (ipp_action_modify(action_name, &flt->nvlist, 0) != 0) {
1019 /* invalid parameters */
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);
1026 /* any other errnos */
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));
1034 return (IPQOS_CONF_ERR);
1037 return (IPQOS_CONF_SUCCESS);
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.
1045 static int
1046 remove_filter(
1047 char *action_name,
1048 char *filter_name,
1049 int instance,
1050 int module_version)
1053 nvlist_t *nvl;
1055 IPQOSCDBG2(APPLY, "remove_filter: action: %s, filter: %s\n",
1056 action_name, filter_name);
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);
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);
1072 * add module version
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);
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);
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);
1093 /* call into lib to remove */
1094 if (ipp_action_modify(action_name, &nvl, 0) != 0) {
1096 /* generic error message */
1098 ipqos_msg(MT_ERROR,
1099 gettext("Removing filter %s in action %s failed: %s.\n"),
1100 filter_name, action_name, strerror(errno));
1102 return (IPQOS_CONF_ERR);
1105 return (IPQOS_CONF_SUCCESS);
1108 /* ******************************************************************* */
1112 * add originator nvpair set to ipqosconf to nvl.
1113 * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCESS.
1115 static int
1116 add_orig_ipqosconf(nvlist_t *nvl)
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);
1125 return (IPQOS_CONF_SUCCESS);
1128 /* ************************* differencing functions ************************ */
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.
1136 static boolean_t
1137 arrays_equal(
1138 int array1[],
1139 int array2[],
1140 uint32_t size)
1142 int x;
1144 for (x = 0; x < size; x++) {
1145 if (array1[x] != array2[x])
1146 return (B_FALSE);
1148 return (B_TRUE);
1152 * difference class old against class new. It marks the new class as
1153 * modified if it is different.
1154 * RETURNS: IPQOS_CONF_SUCCESS.
1156 static int
1157 diffclass(
1158 ipqos_conf_class_t *old,
1159 ipqos_conf_class_t *new)
1162 IPQOSCDBG0(L0, "In diffclass:\n");
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);
1168 new->modified = B_TRUE;
1169 return (IPQOS_CONF_SUCCESS);
1172 /* different stats values */
1173 if (old->stats_enable != new->stats_enable) {
1174 IPQOSCDBG1(DIFF, "marking class %s as modified\n", new->name);
1176 new->modified = B_TRUE;
1177 return (IPQOS_CONF_SUCCESS);
1180 return (IPQOS_CONF_SUCCESS);
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.
1188 static int
1189 diffparams(
1190 ipqos_conf_params_t *old,
1191 ipqos_conf_params_t *new,
1192 char *module_name)
1195 int diff;
1196 int res;
1198 IPQOSCDBG0(L0, "In diffparams\n");
1200 /* diff stats */
1201 if (old->stats_enable != new->stats_enable) {
1203 new->modified = B_TRUE;
1204 return (IPQOS_CONF_SUCCESS);
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);
1213 if (diff) {
1215 new->modified = B_TRUE;
1218 return (IPQOS_CONF_SUCCESS);
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.
1226 static int
1227 difffilter(
1228 ipqos_conf_filter_t *old,
1229 ipqos_conf_filter_t *new,
1230 char *module_name)
1233 int res;
1234 int diff;
1236 IPQOSCDBG0(L0, "In difffilter\n");
1238 /* compare class name */
1240 if (strcmp(old->class_name, new->class_name) != 0) {
1241 IPQOSCDBG1(DIFF, "Marking filter %s as modified\n", new->name);
1243 new->modified = B_TRUE;
1244 return (IPQOS_CONF_SUCCESS);
1247 /* compare module specific params */
1249 res = diffnvlists(old->nvlist, new->nvlist, module_name, &diff,
1250 PL_FILTER);
1251 if (res != IPQOS_CONF_SUCCESS) {
1252 return (res);
1255 if (diff) {
1256 IPQOSCDBG1(DIFF, "Marking filter %s as modified\n", new->name);
1257 new->modified = B_TRUE;
1260 return (IPQOS_CONF_SUCCESS);
1265 * mark all the filters and classes in parameter action either
1266 * for deletion (if they are ipqosconf originated) or for modification.
1268 static void
1269 mark_classes_filters_del(ipqos_conf_action_t *action)
1272 ipqos_conf_filter_t *flt;
1273 ipqos_conf_class_t *cls;
1275 IPQOSCDBG1(L1, "In mark_classes_filters_del: action: %s\n",
1276 action->name);
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);
1284 flt->modified = B_TRUE;
1285 } else {
1286 IPQOSCDBG1(DIFF, "Marking filter %s as del.\n",
1287 flt->name);
1289 flt->todel = B_TRUE;
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);
1299 cls->modified = B_TRUE;
1300 } else {
1301 IPQOSCDBG1(DIFF, "Marking class %s as del.\n",
1302 cls->name);
1304 cls->todel = B_TRUE;
1310 * mark all classes and filters either new (non-permanent) or modified.
1312 static void
1313 mark_classes_filters_new(ipqos_conf_action_t *action)
1316 ipqos_conf_filter_t *flt;
1317 ipqos_conf_class_t *cls;
1319 IPQOSCDBG1(L1, "In mark_classes_filters_new: action: %s\n",
1320 action->name);
1322 /* mark all permanent filters as modified and all others new */
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);
1329 flt->modified = B_TRUE;
1330 action->modified = B_TRUE;
1331 } else {
1332 IPQOSCDBG1(DIFF, "Marking filter %s as new.\n",
1333 flt->name);
1335 flt->new = B_TRUE;
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);
1345 cls->modified = B_TRUE;
1346 action->modified = B_TRUE;
1347 } else {
1348 IPQOSCDBG1(DIFF, "Marking class %s as new.\n",
1349 cls->name);
1351 cls->new = B_TRUE;
1357 * Marks all the actions and their constituent elements in conf
1358 * as new.
1360 static void
1361 mark_config_new(
1362 ipqos_conf_action_t *conf)
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;
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.
1378 static int
1379 diffconf(
1380 ipqos_conf_action_t *old,
1381 ipqos_conf_action_t *new)
1384 int res;
1385 ipqos_conf_action_t *act;
1386 ipqos_conf_action_t *tmp;
1388 IPQOSCDBG0((L0 | DIFF), "In diffconf\n");
1390 /* check the new actions against the old */
1392 for (act = new; act; act = act->next) {
1394 /* if action not in old mark it and it's contents as new */
1396 if ((tmp = actionexist(act->name, old)) == NULL) {
1397 IPQOSCDBG1(DIFF, "marking act %s as new\n", act->name);
1399 act->new = B_TRUE;
1400 mark_classes_filters_new(act);
1401 continue;
1404 /* if action in old diff old against new */
1406 res = diffaction(tmp, act);
1407 if (res != IPQOS_CONF_SUCCESS) {
1408 return (res);
1413 * mark actions, and their contents, in old but not new that were
1414 * created by us for del.
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);
1422 act->todel = B_TRUE;
1423 mark_classes_filters_del(act);
1427 return (IPQOS_CONF_SUCCESS);
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.
1436 static int
1437 diffaction(
1438 ipqos_conf_action_t *old,
1439 ipqos_conf_action_t *new)
1442 int res;
1444 IPQOSCDBG0(L0, "In diffaction\n");
1446 /* compare and mark classes */
1447 res = diffclasses(old, new);
1448 if (res != IPQOS_CONF_SUCCESS) {
1449 return (res);
1452 /* compare and mark filters */
1453 res = difffilters(old, new);
1454 if (res != IPQOS_CONF_SUCCESS) {
1455 return (res);
1458 /* compare and mark parameters */
1459 res = diffparams(old->params, new->params, old->module);
1460 if (res != IPQOS_CONF_SUCCESS) {
1461 return (res);
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);
1469 new->modified = B_TRUE;
1472 return (IPQOS_CONF_SUCCESS);
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.
1483 static int
1484 diffclasses(
1485 ipqos_conf_action_t *old,
1486 ipqos_conf_action_t *new)
1490 ipqos_conf_class_t *cls;
1491 ipqos_conf_class_t *tmpc;
1492 ipqos_conf_class_t *ncls;
1493 int res;
1496 /* loop through old classes checking for classes not present in new */
1498 for (cls = old->classes; cls; cls = cls->next) {
1500 if (classexist(cls->name, new->classes) == NULL) {
1502 /* if we created original class mark for deletion */
1504 if (cls->originator == IPP_CONFIG_IPQOSCONF) {
1505 IPQOSCDBG1(DIFF, "marking class %s for del\n",
1506 cls->name);
1508 cls->todel = B_TRUE;
1510 /* mark old action */
1511 old->modified = B_TRUE;
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.
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) {
1528 /* copy class */
1530 res = dup_class(cls, &ncls);
1531 if (res != IPQOS_CONF_SUCCESS) {
1532 return (IPQOS_CONF_ERR);
1535 /* set next action to continue */
1537 (void) strcpy(ncls->alist->name,
1538 IPP_ANAME_CONT);
1540 /* add to news classes to be diffed below */
1541 ADD_TO_LIST(&new->classes, ncls);
1546 /* loop through new classes checking for new / modified classes */
1548 for (cls = new->classes; cls; cls = cls->next) {
1550 /* new ipqosconf class */
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);
1558 cls->new = B_TRUE;
1560 new->modified = B_TRUE; /* mark new action */
1561 continue;
1563 /* existing ipqosconf/perm class */
1564 } else {
1565 res = diffclass(tmpc, cls);
1566 if (res != IPQOS_CONF_SUCCESS) {
1567 return (res);
1570 if (cls->modified == B_TRUE) {
1571 new->modified = B_TRUE;
1576 return (IPQOS_CONF_SUCCESS);
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).
1587 static int
1588 difffilters(
1589 ipqos_conf_action_t *old,
1590 ipqos_conf_action_t *new)
1593 ipqos_conf_filter_t *flt;
1594 ipqos_conf_filter_t *tmpf;
1595 int maxi;
1596 int newi;
1597 int res;
1599 /* check for new/modified filters */
1601 for (flt = new->filters; flt; flt = flt->next) {
1603 /* new ipqosconf filter */
1605 if ((tmpf = filterexist(flt->name, -1, old->filters)) == NULL) {
1607 /* mark all instances of this filter as new */
1608 for (;;) {
1609 IPQOSCDBG1(DIFF, "Marking filter %s as "
1610 "new\n", flt->name);
1612 flt->new = B_TRUE;
1615 if (flt->next == NULL ||
1616 strcmp(flt->next->name, flt->name) != 0) {
1617 break;
1619 flt = flt->next;
1621 new->modified = B_TRUE; /* mark new action */
1623 /* ipqosconf/permanent filter existed */
1624 } else {
1626 * if ip node name force filter refresh - ie. mark
1627 * all old filter instances as todel and all new new.
1629 if (tmpf->src_nd_name || tmpf->dst_nd_name ||
1630 flt->src_nd_name || flt->dst_nd_name) {
1632 /* init max previous filter instance */
1633 maxi = tmpf->instance;
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);
1641 tmpf->todel = B_TRUE;
1644 * check and update previous instance
1645 * max.
1647 if (tmpf->instance > maxi) {
1648 maxi = tmpf->instance;
1651 tmpf = tmpf->next;
1652 } while (tmpf != NULL &&
1653 strcmp(tmpf->name, flt->name) == 0);
1656 * use the max previous instance + 1 for
1657 * the start of the new instance numbers.
1659 newi = (uint32_t)++maxi % INT_MAX;
1662 * mark new instances for addition and
1663 * give new instance number.
1665 for (;;) {
1666 IPQOSCDBG2(DIFF, "Marking filter "
1667 "%s, instance %d as new\n",
1668 flt->name, newi);
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;
1677 flt = flt->next;
1679 new->modified = B_TRUE; /* mark new action */
1681 /* mark old action */
1682 old->modified = B_TRUE;
1684 /* non-node name filter */
1685 } else {
1686 /* compare and mark as modified if diff */
1688 res = difffilter(tmpf, flt, new->module);
1689 if (res != IPQOS_CONF_SUCCESS) {
1690 return (res);
1692 if (flt->modified == B_TRUE) {
1693 /* mark action if diff */
1694 new->modified = B_TRUE;
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.
1707 for (flt = old->filters; flt; flt = flt->next) {
1709 if (flt->originator == IPP_CONFIG_IPQOSCONF &&
1710 filterexist(flt->name, -1, new->filters) == NULL) {
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);
1717 flt->todel = B_TRUE;
1718 old->modified = B_TRUE; /* mark old action */
1720 if (flt->next == NULL ||
1721 strcmp(flt->next->name, flt->name) != 0) {
1722 break;
1724 flt = flt->next;
1729 return (IPQOS_CONF_SUCCESS);
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.
1739 static int
1740 diffnvlists(
1741 nvlist_t *old,
1742 nvlist_t *new,
1743 char *module_name,
1744 int *pdiff,
1745 place_t place)
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;
1762 IPQOSCDBG0(L0, "In diffnvlists\n");
1764 /* open stream to types file */
1766 tfp = validmod(module_name, &openerr);
1767 if (tfp == NULL) {
1768 if (openerr) {
1769 ipqos_msg(MT_ENOSTR, "fopen");
1771 return (IPQOS_CONF_ERR);
1773 start:
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.
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.
1785 nvp = nvlist_next_nvpair(new, NULL);
1786 while (nvp != NULL) {
1788 /* get name */
1789 nme = nvpair_name(nvp);
1792 * get type.
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);
1801 /* init diff to 1 */
1802 diff = 1;
1804 switch (type) {
1806 /* interface name */
1807 case IPQOS_DATA_TYPE_IFINDEX: {
1808 uint32_t ifidx;
1809 uint32_t oifidx;
1811 /* get new value */
1812 (void) nvpair_value_uint32(nvp, &ifidx);
1814 /* compare against old if present */
1816 res = nvlist_lookup_uint32(old, nme, &oifidx);
1817 if (res == 0) {
1818 /* diff values */
1819 diff = (ifidx != oifidx);
1821 /* not in old so see if new value is default */
1823 } else {
1824 diff = (ifidx != 0);
1826 break;
1828 /* protocol */
1829 case IPQOS_DATA_TYPE_PROTO: {
1830 uchar_t proto;
1831 uchar_t oproto;
1833 (void) nvpair_value_byte(nvp, &proto);
1835 res = nvlist_lookup_byte(old, nme, &oproto);
1836 if (res == 0) {
1837 diff = (proto != oproto);
1838 } else {
1839 diff = (proto != 0);
1841 break;
1843 /* port */
1844 case IPQOS_DATA_TYPE_PORT: {
1845 uint16_t port;
1846 uint16_t oport;
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);
1855 break;
1857 /* action name / string */
1858 case IPQOS_DATA_TYPE_ACTION:
1859 case IPQOS_DATA_TYPE_STRING: {
1860 char *str;
1861 char *ostr;
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);
1870 break;
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;
1880 * all addresses are stored as v6 addresses, so
1881 * a uint32_t[4] array is used.
1884 /* lookup new value */
1886 (void) nvpair_value_uint32_array(nvp,
1887 (uint32_t **)&in6, &x);
1889 /* see if there's an old value and diff it */
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 */
1896 for (x = 0; x < 16; x++) {
1897 if (in6->s6_addr[x] !=
1898 oin6->s6_addr[x]) {
1899 diff++;
1900 break;
1904 break;
1906 /* boolean */
1907 case IPQOS_DATA_TYPE_BOOLEAN: {
1908 boolean_t bl;
1909 boolean_t obl;
1911 (void) nvpair_value_uint32(nvp, (uint32_t *)&bl);
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);
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);
1925 break;
1927 /* uint 8 */
1928 case IPQOS_DATA_TYPE_UINT8: {
1929 uint8_t u8;
1930 uint8_t ou8;
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);
1942 break;
1944 /* int 16 */
1945 case IPQOS_DATA_TYPE_INT16: {
1946 int16_t i16;
1947 int16_t oi16;
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);
1959 break;
1961 /* uint16 */
1962 case IPQOS_DATA_TYPE_UINT16: {
1963 uint16_t ui16;
1964 uint16_t oui16;
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);
1976 break;
1979 * int32 and user.
1980 * Since user uids are stored in an int32 nvpair we can use
1981 * the same comparison code.
1983 case IPQOS_DATA_TYPE_USER:
1984 case IPQOS_DATA_TYPE_INT32: {
1985 int32_t i32;
1986 int32_t oi32;
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);
1998 break;
2000 /* uint32 */
2001 case IPQOS_DATA_TYPE_UINT32: {
2002 uint32_t ui32;
2003 uint32_t oui32;
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);
2015 break;
2017 /* enumeration */
2018 case IPQOS_DATA_TYPE_ENUM: {
2019 uint32_t eval;
2020 uint32_t oeval;
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);
2032 break;
2034 case IPQOS_DATA_TYPE_M_INDEX: {
2035 uint8_t idx, oidx;
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;
2043 case IPQOS_DATA_TYPE_INT_ARRAY: {
2044 int *oarr, *arr;
2045 uint32_t osize, size;
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;
2055 #ifdef _IPQOS_CONF_DEBUG
2056 default: {
2057 /* shouldn't get here as all types should be covered */
2058 assert(1);
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);
2070 nvp = nvlist_next_nvpair(new, nvp);
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;
2083 (void) fclose(tfp);
2085 *pdiff = 0;
2086 return (IPQOS_CONF_SUCCESS);
2091 /* ************************** difference application *********************** */
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.
2100 static int
2101 applydiff(
2102 ipqos_conf_action_t *actions,
2103 ipqos_conf_action_t *old_actions)
2106 int res;
2108 IPQOSCDBG0(L1, "In applydiff:\n");
2111 /* add each item marked as new */
2113 res = add_items(actions, B_FALSE);
2114 if (res != IPQOS_CONF_SUCCESS) {
2115 return (res);
2118 /* modify items marked for modification */
2120 res = modify_items(actions);
2121 if (res != IPQOS_CONF_SUCCESS) {
2122 return (res);
2125 /* delete items marked for deletion */
2127 res = remove_items(old_actions, B_FALSE);
2128 if (res != IPQOS_CONF_SUCCESS) {
2129 return (res);
2132 return (IPQOS_CONF_SUCCESS);
2135 static int
2136 add_items(
2137 ipqos_conf_action_t *actions,
2138 boolean_t rem_undo)
2141 int res;
2142 ipqos_conf_action_t *act;
2144 IPQOSCDBG1(L1, "In add_items, rem_undo: %u\n", rem_undo);
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.
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)) {
2156 res = add_action(act);
2157 if (res != IPQOS_CONF_SUCCESS) {
2158 return (res);
2163 * loop though action list and add any actions marked as
2164 * new/modified action and apply any additions there, then return.
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);
2174 return (IPQOS_CONF_SUCCESS);
2180 * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCESS.
2182 static int
2183 add_item(
2184 ipqos_conf_action_t *actions,
2185 boolean_t rem_undo)
2188 ipqos_conf_action_t *act = actions;
2189 int res;
2190 ipqos_conf_class_t *cls;
2191 ipqos_conf_act_ref_t *pact;
2193 IPQOSCDBG2(L1, "In add_item: action: %s, rem_undo: %u\n",
2194 actions->name, rem_undo);
2196 /* if already visited return immediately */
2198 if (act->visited == ADD_VISITED) {
2199 IPQOSCDBG0(L1, "Early exit due to visited\n");
2200 return (IPQOS_CONF_SUCCESS);
2202 act->visited = ADD_VISITED;
2205 /* recurse to last action in tree */
2207 for (cls = act->classes; cls; cls = cls->next) {
2209 /* if not virtual action */
2211 if (cls->alist->action) {
2212 res = add_item(cls->alist->action, rem_undo);
2213 if (res != IPQOS_CONF_SUCCESS) {
2214 return (res);
2219 for (pact = act->params->actions; pact; pact = pact->next) {
2221 /* if not virtual */
2223 if (pact->action) {
2224 res = add_item(pact->action, rem_undo);
2225 if (res != IPQOS_CONF_SUCCESS) {
2226 return (res);
2232 /* if action marked as new and not ipgpc, create */
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);
2243 /* add any classes and filters marked as new */
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);
2252 return (IPQOS_CONF_SUCCESS);
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.
2263 static int
2264 add_action(ipqos_conf_action_t *act)
2267 int res;
2268 nvlist_t **nvl;
2270 IPQOSCDBG2(APPLY, "add_action: action: %s, module: %s\n", act->name,
2271 act->module);
2273 nvl = &act->params->nvlist;
2275 /* alloc params nvlist if not already one */
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);
2286 * add module version
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);
2294 /* add action stats */
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);
2302 /* add ipqosconf originator id */
2304 if (add_orig_ipqosconf(*nvl) != IPQOS_CONF_SUCCESS) {
2305 return (IPQOS_CONF_ERR);
2308 /* call into lib to create action */
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);
2315 /* invalid params */
2317 if (errno == EINVAL) {
2318 ipqos_msg(MT_ERROR,
2319 gettext("Invalid Parameters for action %s.\n"),
2320 act->name);
2322 } else if (errno == ENOENT) {
2323 ipqos_msg(MT_ERROR,
2324 gettext("Missing required parameter for action "
2325 "%s.\n"), act->name);
2327 } else { /* unexpected error */
2328 ipqos_msg(MT_ERROR, gettext("Failed to create action "
2329 "%s: %s.\n"), act->name, strerror(errno));
2332 return (IPQOS_CONF_ERR);
2335 /* mark action as created */
2336 act->cr_mod = B_TRUE;
2338 return (IPQOS_CONF_SUCCESS);
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.
2348 static int
2349 add_filters(
2350 ipqos_conf_filter_t *filters,
2351 char *action,
2352 int module_version,
2353 boolean_t rem_undo)
2356 ipqos_conf_filter_t *flt;
2358 IPQOSCDBG0(L1, "In add_filters\n");
2360 /* loop through filters in filters param */
2361 for (flt = filters; flt; flt = flt->next) {
2363 * skip filter if in normal mode and not new filter or
2364 * if doing rollback and filter wasn't previously deleted.
2366 if ((rem_undo == B_FALSE && flt->new == B_FALSE) ||
2367 (rem_undo == B_TRUE && flt->deleted == B_FALSE)) {
2368 continue;
2371 /* add filter to action */
2372 if (add_filter(action, flt, module_version) !=
2373 IPQOS_CONF_SUCCESS) {
2374 return (IPQOS_CONF_ERR);
2377 /* mark as created */
2378 flt->cr_mod = B_TRUE;
2381 return (IPQOS_CONF_SUCCESS);
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.
2392 add_classes(
2393 ipqos_conf_class_t *classes,
2394 char *action,
2395 int module_version,
2396 boolean_t rem_undo) {
2398 int res;
2399 ipqos_conf_class_t *cls;
2401 IPQOSCDBG0(L1, "In add_classes\n");
2403 /* for each class */
2404 for (cls = classes; cls; cls = cls->next) {
2406 * skip class if in normal mode and not new class or
2407 * if doing rollback and class wasn't deleted.
2409 if ((rem_undo == B_FALSE && cls->new == B_FALSE) ||
2410 (rem_undo == B_TRUE && cls->deleted == B_FALSE)) {
2411 continue;
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);
2421 /* mark class as created */
2422 cls->cr_mod = B_TRUE;
2425 return (IPQOS_CONF_SUCCESS);
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.
2433 static int
2434 remove_items(
2435 ipqos_conf_action_t *actions,
2436 boolean_t add_undo)
2439 int res;
2440 ipqos_conf_action_t *act;
2442 IPQOSCDBG1(L0, "In remove_items, add_undo: %u\n", add_undo);
2445 * loop through actions removing any actions, or action contents
2446 * that are marked as such.
2448 for (act = actions; act; act = act->next) {
2449 res = remove_item(act, add_undo);
2450 if (res != IPQOS_CONF_SUCCESS) {
2451 return (res);
2455 return (IPQOS_CONF_SUCCESS);
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.
2465 static int
2466 remove_item(
2467 ipqos_conf_action_t *act,
2468 boolean_t add_undo)
2471 ipqos_conf_class_t *cls;
2472 ipqos_conf_filter_t *flt;
2473 ipqos_conf_act_ref_t *dep;
2474 int res;
2476 IPQOSCDBG3(L1, "In remove_item: action: %s, add_undo: %u, mod: %u\n",
2477 act->name, add_undo, act->modified);
2480 /* return immmediately if previously visited in remove phase */
2482 if (act->visited == REM_VISITED) {
2483 IPQOSCDBG0(L1, "Exit due to REM_VISITED set\n");
2484 return (IPQOS_CONF_SUCCESS);
2486 act->visited = REM_VISITED;
2489 /* if this action is to be deleted */
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) {
2495 /* modify parent actions first */
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);
2504 /* delete this action */
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);
2514 /* flag as deleted */
2516 act->deleted = B_TRUE;
2518 /* if modified action */
2520 } else if (act->modified == B_TRUE) {
2522 /* loop through removing any filters marked for del */
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)) {
2529 /* do deletion */
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);
2538 return (IPQOS_CONF_ERR);
2541 /* flag deleted */
2543 flt->deleted = B_TRUE;
2547 /* remove any classes marked for del */
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)) {
2554 /* do deletion */
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);
2562 return (IPQOS_CONF_ERR);
2565 /* flag deleted */
2567 cls->deleted = B_TRUE;
2571 /* mark action as having been modified */
2573 act->cr_mod = B_TRUE;
2576 return (IPQOS_CONF_SUCCESS);
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.
2584 static int
2585 modify_items(ipqos_conf_action_t *actions)
2588 ipqos_conf_action_t *act;
2589 int res;
2590 ipqos_conf_filter_t *flt;
2591 ipqos_conf_class_t *cls;
2594 IPQOSCDBG0(L1, "In modify_items\n");
2596 /* loop through actions in parameter actions */
2598 for (act = actions; act; act = act->next) {
2600 /* skip unchanged actions */
2602 if (act->modified == B_FALSE) {
2603 continue;
2606 /* apply any parameter mods */
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);
2616 act->params->cr_mod = B_TRUE;
2619 /* apply any class mods */
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);
2630 /* mark modification done */
2631 cls->cr_mod = B_TRUE;
2635 /* apply any filter mods */
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);
2645 /* mark modification done */
2646 flt->cr_mod = B_TRUE;
2650 /* mark action modified */
2652 act->cr_mod = B_TRUE;
2655 return (IPQOS_CONF_SUCCESS);
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.
2664 static int
2665 undo_modifys(
2666 ipqos_conf_action_t *oactions,
2667 ipqos_conf_action_t *nactions)
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;
2678 IPQOSCDBG0(L1, "In undo_modifys:\n");
2680 /* loop throught new actions */
2682 for (act = nactions; act; act = act->next) {
2683 oldact = actionexist(act->name, oactions);
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.
2690 if (oldact == NULL) {
2691 continue;
2694 /* if parameters were modified switch them back */
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);
2706 /* for each filter in action if filter modified switch back */
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);
2721 /* for each class in action if class modified switch back */
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);
2733 if (res != IPQOS_CONF_SUCCESS) {
2734 return (res);
2741 * Go through the old actions modifying perm filters and classes
2742 * whose action was deleted.
2745 for (act = oactions; act != NULL; act = act->next) {
2747 if (act->deleted == B_FALSE) {
2748 continue;
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);
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);
2774 return (IPQOS_CONF_SUCCESS);
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.
2783 static int
2784 rollback(
2785 ipqos_conf_action_t *actions,
2786 ipqos_conf_action_t *old_actions)
2789 int res;
2791 IPQOSCDBG0(RBK, "In rollback:\n");
2793 /* re-add items that were deleted */
2795 res = add_items(old_actions, B_TRUE);
2796 if (res != IPQOS_CONF_SUCCESS) {
2797 return (res);
2800 /* change modified items back how they were */
2802 res = undo_modifys(old_actions, actions);
2803 if (res != IPQOS_CONF_SUCCESS) {
2804 return (res);
2807 /* remove new items that were added */
2809 res = remove_items(actions, B_TRUE);
2810 if (res != IPQOS_CONF_SUCCESS) {
2811 return (res);
2814 return (IPQOS_CONF_SUCCESS);
2817 /* ******************************* print config **************************** */
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'.
2823 static void
2824 printuser(
2825 FILE *fp,
2826 uid_t uid)
2828 struct passwd *pwd;
2830 IPQOSCDBG0(L0, "In printuser\n");
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);
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.
2845 static void
2846 printrange(
2847 FILE *fp,
2848 uint32_t start,
2849 uint32_t end)
2851 uint32_t tmp;
2853 if (start > end) {
2854 tmp = start;
2855 start = end;
2856 end = tmp;
2859 (void) fprintf(fp, "%u", start);
2860 if (end != start)
2861 (void) fprintf(fp, "-%u", end);
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.
2876 static void
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)
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 ? */
2892 IPQOSCDBG4(L0, "In print_int_array: size: %u, llimit: %u, ulimit: %u, "
2893 "enum_nvs: %x \n", size, llimit, ulimit, enum_nvs);
2896 * if an enumeration retrieve value range.
2898 if (enum_nvs != NULL)
2899 get_str_val_value_range(enum_nvs, &llimit, &ulimit);
2902 * print opening curl.
2904 (void) fprintf(fp, "%c\n", CURL_BEGIN);
2905 PRINT_TABS(fp, tab_inserts + 1);
2907 first_entry = B_TRUE;
2909 * for each value in range.
2911 for (x = llimit; x <= ulimit; x++) {
2912 found_range = B_FALSE;
2913 first_range = B_TRUE;
2914 y = 0;
2916 * scan array and print ranges of indexes with value x.
2918 while (y < size) {
2920 * get first occurence of value for this range.
2922 while ((arr[y] != x) && (y < size))
2923 y++;
2924 if (y == size) {
2925 break;
2926 } else {
2927 found_range = B_TRUE;
2929 first = y;
2932 * get last occurence of value for this range.
2934 while ((arr[y] == x) && (y < size))
2935 y++;
2936 last = y - 1;
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.
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;
2951 * print comma (range delimeter) only if there was
2952 * a previous range for this value.
2954 if (!first_range) {
2955 (void) fprintf(fp, ",");
2956 } else {
2957 first_range = B_FALSE;
2961 * print range.
2963 printrange(fp, first, last);
2966 * only print a colon and value if we found a range with
2967 * this value.
2969 if (found_range) {
2970 (void) fprintf(fp, ":");
2973 * print numeric/symbolic value.
2975 if (enum_nvs) {
2976 printenum(fp, x, enum_nvs);
2977 } else {
2978 (void) fprintf(fp, "%d", x);
2984 * print closing curl.
2986 (void) fprintf(fp, "\n");
2987 PRINT_TABS(fp, tab_inserts);
2988 (void) fprintf(fp, "%c\n", CURL_END);
2991 /* print the protocol name for proto, or if unknown protocol number proto. */
2992 static void
2993 printproto(
2994 FILE *fp,
2995 uint8_t proto)
2998 struct protoent *pent;
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);
3009 * prints the name associated with interface with index ifindex to fp.
3010 * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCESS.
3012 static int
3013 printifname(
3014 FILE *fp,
3015 int ifindex)
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;
3029 /* open socket */
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);
3036 /* get number of lifreq structs that need to be alloc'd for */
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);
3047 /* allocate buffer for SIOGLIFCONF ioctl */
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);
3057 /* setup lifconf params for ioctl */
3059 lc.lifc_family = AF_UNSPEC;
3060 lc.lifc_flags = 0;
3061 lc.lifc_len = len;
3062 lc.lifc_buf = buf;
3064 /* do SIOCGLIFCONF ioctl */
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);
3073 (void) close(s);
3076 * for each interface name given in the returned lifreq list get
3077 * it's index and compare with ifindex param. Break if equal.
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);
3085 if (idx == ifindex) {
3086 break;
3089 free(buf);
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);
3096 /* truncate any logical suffix */
3098 if ((cp = strchr(lr->lifr_name, '@')) != NULL) {
3099 *cp = '\0';
3102 /* print interface name */
3103 (void) fprintf(fp, "%s\n", lr->lifr_name);
3105 return (IPQOS_CONF_SUCCESS);
3109 * print to fp the enumeration clause evaluating to the value val using the
3110 * names/values given in enum_nvs.
3112 static void
3113 printenum(
3114 FILE *fp,
3115 uint32_t val,
3116 str_val_nd_t *enum_nvs)
3119 boolean_t isfirstval = B_TRUE;
3120 str_val_nd_t *name_val = enum_nvs;
3122 /* for each value in enum_nvs if same bit set in val print name */
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);
3133 name_val = name_val->next;
3138 /* prints the service name of port, or if unknown the number to fp. */
3139 static void
3140 printport(
3141 FILE *fp,
3142 uint16_t port)
3145 struct servent *sent;
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));
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.
3160 static int
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)
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;
3181 IPQOSCDBG0(L1, "In printnvlist\n");
3184 /* open stream to types file */
3186 tfp = validmod(module, &openerr);
3187 if (tfp == NULL) {
3188 if (openerr) {
3189 ipqos_msg(MT_ENOSTR, "fopen");
3191 return (IPQOS_CONF_ERR);
3195 /* go through list getting param name and type and printing it */
3197 nvp = nvlist_next_nvpair(nvl, NULL);
3198 while (nvp) {
3200 /* get nvpair name */
3201 name = nvpair_name(nvp);
3202 IPQOSCDBG1(L0, "processing element %s.\n", name);
3204 /* skip ipgpc params that are not explicitly user settable */
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;
3215 param = SHORT_NAME(name);
3218 * get parameter type from types file.
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);
3228 * for map entries we don't print the map value, only
3229 * the index value it was derived from.
3231 if (place == PL_MAP) {
3232 nvp = nvlist_next_nvpair(nvl, nvp);
3233 continue;
3237 * the ifindex is converted to the name and printed out
3238 * so print the parameter name as ifname.
3240 if (strcmp(name, IPGPC_IF_INDEX) == 0) {
3241 PRINT_TABS(fp, tab_inserts);
3242 (void) fprintf(fp, "%s ", IPQOS_IFNAME_STR);
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.
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);
3254 switch (type) {
3255 case IPQOS_DATA_TYPE_IFINDEX: {
3256 uint32_t ifidx;
3258 (void) nvpair_value_uint32(nvp, &ifidx);
3259 (void) printifname(fp, ifidx);
3260 break;
3262 case IPQOS_DATA_TYPE_BOOLEAN: {
3263 boolean_t bl;
3265 (void) nvpair_value_uint32(nvp,
3266 (uint32_t *)&bl);
3267 (void) fprintf(fp, "%s\n",
3268 bl == B_TRUE ? "true" : "false");
3269 break;
3271 case IPQOS_DATA_TYPE_ACTION: {
3272 char *strval;
3274 (void) nvpair_value_string(nvp, &strval);
3275 print_action_nm(fp, strval);
3276 break;
3278 case IPQOS_DATA_TYPE_STRING: {
3279 char *strval;
3281 (void) nvpair_value_string(nvp, &strval);
3282 (void) fprintf(fp, "%s\n",
3283 quote_ws_string(strval));
3284 break;
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;
3295 * skip addresses that have node names for
3296 * non printall listings.
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;
3306 /* we skipped this above */
3308 PRINT_TABS(fp, tab_inserts);
3309 (void) fprintf(fp, "%s ", param);
3311 (void) nvpair_value_uint32_array(nvp,
3312 (uint32_t **)&addr, &tmp);
3314 /* get filter type */
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;
3325 /* get mask */
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);
3338 /* print address/mask to fp */
3340 (void) fprintf(fp, "%s/%u\n",
3341 inet_ntop(af, addr, addrstr,
3342 INET6_ADDRSTRLEN), masktocidr(af, mask));
3343 break;
3345 case IPQOS_DATA_TYPE_ENUM: {
3346 uint32_t val;
3348 (void) nvpair_value_uint32(nvp, &val);
3351 * print list of tokens resulting in val
3353 (void) fprintf(fp, "{ ");
3354 printenum(fp, val, enum_nvs);
3355 (void) fprintf(fp, " }\n");
3356 break;
3358 case IPQOS_DATA_TYPE_PORT: {
3359 uint16_t port;
3361 (void) nvpair_value_uint16(nvp, &port);
3362 printport(fp, port);
3363 break;
3365 case IPQOS_DATA_TYPE_PROTO: {
3366 uint8_t proto;
3368 (void) nvpair_value_byte(nvp, &proto);
3369 printproto(fp, proto);
3370 break;
3372 case IPQOS_DATA_TYPE_M_INDEX:
3373 case IPQOS_DATA_TYPE_UINT8: {
3374 uchar_t u8;
3376 (void) nvpair_value_byte(nvp, &u8);
3377 (void) fprintf(fp, "%u\n", u8);
3378 break;
3380 case IPQOS_DATA_TYPE_UINT16: {
3381 uint16_t u16;
3383 (void) nvpair_value_uint16(nvp, &u16);
3384 (void) fprintf(fp, "%u\n", u16);
3385 break;
3387 case IPQOS_DATA_TYPE_INT16: {
3388 int16_t i16;
3390 (void) nvpair_value_int16(nvp, &i16);
3391 (void) fprintf(fp, "%d\n", i16);
3392 break;
3394 case IPQOS_DATA_TYPE_UINT32: {
3395 uint32_t u32;
3397 (void) nvpair_value_uint32(nvp, &u32);
3398 (void) fprintf(fp, "%u\n", u32);
3399 break;
3401 case IPQOS_DATA_TYPE_INT32: {
3402 int i32;
3404 (void) nvpair_value_int32(nvp, &i32);
3405 (void) fprintf(fp, "%d\n", i32);
3406 break;
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;
3414 (void) nvpair_value_int32_array(nvp, &arr,
3415 &size);
3418 * read array info from types file.
3420 res = read_int_array_info(dfltst,
3421 &arr_enum_nvs, &size, &llimit, &ulimit,
3422 module);
3425 * print array with numbers, or symbols
3426 * if enumerated.
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);
3437 break;
3439 case IPQOS_DATA_TYPE_USER: {
3440 uid_t uid;
3442 (void) nvpair_value_int32(nvp, (int *)&uid);
3443 printuser(fp, uid);
3444 break;
3446 #ifdef _IPQOS_CONF_DEBUG
3447 default: {
3449 * we should have catered for all used data
3450 * types that readtype returns.
3452 assert(1);
3454 #endif
3457 nvp = nvlist_next_nvpair(nvl, nvp);
3460 (void) fclose(tfp);
3461 return (IPQOS_CONF_SUCCESS);
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.
3469 static int
3470 printparams(
3471 FILE *fp,
3472 char *module,
3473 ipqos_conf_params_t *params,
3474 int printall,
3475 int tab_inserts)
3478 int res;
3480 /* print opening clause */
3482 PRINT_TABS(fp, tab_inserts);
3483 (void) fprintf(fp, IPQOS_CONF_PARAMS_STR " {\n");
3485 /* print originator name if printall flag set */
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)));
3493 /* print global stats */
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");
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);
3506 PRINT_TABS(fp, tab_inserts);
3507 (void) fprintf(fp, "}\n");
3509 return (IPQOS_CONF_SUCCESS);
3513 * print the interpreted name of the action_nm parameter if it is a special
3514 * action, else action_nm verbatim to fp parameter.
3516 static void
3517 print_action_nm(FILE *fp, char *action_nm)
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));
3532 * print a class clause for class to fp. If printall is set the originator
3533 * is printed.
3535 static void
3536 printclass(
3537 FILE *fp,
3538 ipqos_conf_class_t *class,
3539 int printall,
3540 int tab_inserts)
3543 /* print opening clause */
3545 PRINT_TABS(fp, tab_inserts);
3546 (void) fprintf(fp, IPQOS_CONF_CLASS_STR " {\n");
3549 /* if printall flag print originator name */
3551 if (printall) {
3552 PRINT_TABS(fp, tab_inserts + 1);
3553 (void) fprintf(stdout, "Originator %s\n",
3554 get_originator_nm(class->originator));
3557 /* print name, next action and stats enable */
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");
3569 PRINT_TABS(fp, tab_inserts);
3570 (void) fprintf(fp, "}\n");
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".
3578 static char *
3579 get_originator_nm(uint32_t origid)
3582 int x;
3584 /* scan originators table for origid */
3586 for (x = 0; originators[x].value != -1 &&
3587 originators[x].value != origid; x++) {}
3589 /* if we've reached end of array due to unknown type return "unknown" */
3591 if (originators[x].value == -1) {
3592 return ("unknown");
3595 return (originators[x].string);
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.
3605 static int
3606 printfilter(
3607 FILE *fp,
3608 char *module,
3609 ipqos_conf_filter_t **filter,
3610 int printall,
3611 int tab_inserts)
3614 int res;
3616 /* print opening clause */
3618 PRINT_TABS(fp, tab_inserts);
3619 (void) fprintf(fp, IPQOS_CONF_FILTER_STR " {\n");
3621 /* print originator if printall flag set */
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)));
3629 /* print name and class */
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));
3638 /* print the instance if printall and potential mhomed addresses */
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);
3645 /* print node names if any */
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);
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);
3658 /* print ip_version enumeration if set */
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");
3666 if (VERSION_IS_V6(*filter)) {
3667 (void) fprintf(fp, " V6");
3669 (void) fprintf(fp, " }\n");
3672 /* print other module specific parameters parameters */
3674 res = printnvlist(fp, module, (*filter)->nvlist, printall, *filter,
3675 tab_inserts + 1, PL_FILTER);
3676 if (res != IPQOS_CONF_SUCCESS) {
3677 return (res);
3680 PRINT_TABS(fp, tab_inserts);
3681 (void) fprintf(fp, "}\n");
3684 * if not printall advance filter parameter to last instance of this
3685 * filter.
3688 if (!printall) {
3689 for (;;) {
3690 if ((*filter)->next == NULL ||
3691 strcmp((*filter)->name, (*filter)->next->name) !=
3692 0) {
3693 break;
3695 *filter = (*filter)->next;
3699 return (IPQOS_CONF_SUCCESS);
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.
3708 static char *
3709 quote_ws_string(const char *str)
3711 static char *buf = NULL;
3712 const char *cp; /* we don't modify the contents of str so const */
3714 IPQOSCDBG0(L0, "In quote_ws_string\n");
3717 * Just return str if no whitespace.
3719 for (cp = str; (*cp != '\0') && !isspace(*cp); cp++)
3721 if (*cp == '\0')
3722 return ((char *)str);
3724 if (buf == NULL) {
3726 * if first run just allocate buffer of
3727 * strlen(str) + 2 quote characters + NULL terminator.
3729 buf = malloc(strlen(str) + 3);
3730 } else if ((strlen(str) + 2) > strlen(buf)) {
3732 * Not first run, so check if we have a big enough buffer
3733 * and if not reallocate the buffer to a sufficient size.
3735 buf = realloc(buf, strlen(str) + 3);
3737 if (buf == NULL)
3738 return ("");
3741 * copy string into buffer with quotes.
3743 (void) strcpy(buf, "\"");
3744 (void) strcat(buf, str);
3745 (void) strcat(buf, "\"");
3747 return (buf);
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.
3756 static int
3757 printaction(
3758 FILE *fp,
3759 ipqos_conf_action_t *action,
3760 int printall,
3761 int tab_inserts)
3764 ipqos_conf_filter_t *flt;
3765 ipqos_conf_class_t *cls;
3766 int res;
3768 /* print opening clause, module and name */
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));
3778 /* print params clause */
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);
3788 * print classes clause for each class if printall is set, else
3789 * just ipqosconf created or permanent classes.
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);
3801 * print filter clause for each filter if printall is set, else
3802 * just ipqosconf created or permanent filters.
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);
3817 PRINT_TABS(fp, tab_inserts);
3818 (void) fprintf(fp, "}\n");
3820 return (IPQOS_CONF_SUCCESS);
3825 /* *************************************************************** */
3828 static void
3829 list_end(
3830 ipqos_list_el_t **listp,
3831 ipqos_list_el_t ***lendpp)
3833 *lendpp = listp;
3834 while (**lendpp != NULL) {
3835 *lendpp = &(**lendpp)->next;
3839 static void
3840 add_to_list(
3841 ipqos_list_el_t **listp,
3842 ipqos_list_el_t *el)
3844 el->next = *listp;
3845 *listp = el;
3849 * given mask calculates the number of bits it spans. The mask must be
3850 * continuous.
3851 * RETURNS: number of bits spanned.
3853 static int
3854 masktocidr(
3855 int af,
3856 in6_addr_t *mask)
3858 int zeros = 0;
3859 int byte;
3860 int cidr;
3863 * loop through from lowest byte to highest byte counting the
3864 * number of zero bits till hitting a one bit.
3866 for (byte = 15; byte >= 0; byte--) {
3868 * zero byte, so add 8 to zeros.
3870 if (mask->s6_addr[byte] == 0) {
3871 zeros += 8;
3873 * non-zero byte, add zero count to zeros.
3875 } else {
3876 zeros += (ffs((int)mask->s6_addr[byte]) - 1);
3877 break;
3881 * translate zero bits to 32 or 128 bit mask based on af.
3883 if (af == AF_INET) {
3884 cidr = 32 - zeros;
3885 } else {
3886 cidr = 128 - zeros;
3889 return (cidr);
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.
3896 static void
3897 setmask(int prefix_len, in6_addr_t *addr, int af)
3900 int i;
3901 int shift;
3902 int maskstartbit = 128 - prefix_len;
3903 int end_u32;
3905 IPQOSCDBG2(L1, "In setmask, prefix_len: %u, af: %s\n", prefix_len,
3906 af == AF_INET ? "AF_INET" : "AF_INET6");
3908 /* zero addr */
3909 bzero(addr, sizeof (in6_addr_t));
3912 /* set which 32bits in *addr are relevant to this af */
3914 if (af == AF_INET) {
3915 end_u32 = 3;
3916 maskstartbit = 32 - prefix_len;
3917 /* AF_INET6 */
3918 } else {
3919 end_u32 = 0;
3922 * go through each of the 32bit quantities in 128 bit in6_addr_t
3923 * and set appropriate bits according to prefix_len.
3925 for (i = 3; i >= end_u32; i--) {
3927 /* does the prefix apply to this 32bits? */
3929 if (maskstartbit < ((4 - i) * 32)) {
3931 /* is this 32bits fully masked? */
3933 if (maskstartbit <= ((3 - i) * 32)) {
3934 shift = 0;
3935 } else {
3936 shift = maskstartbit % 32;
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;
3945 /* translate to NBO */
3946 addr->_S6_un._S6_u32[i] = htonl(addr->_S6_un._S6_u32[i]);
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.
3955 static nvpair_t *
3956 find_nvpair(nvlist_t *nvl, char *name)
3959 nvpair_t *nvp;
3960 nvpair_t *match = NULL;
3961 char *nvp_name;
3963 IPQOSCDBG0(L1, "In find_nvpair\n");
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;
3971 nvp = nvlist_next_nvpair(nvl, nvp);
3974 return (match);
3978 * returns a string containing module_name '.' name.
3979 * RETURNS: IPQOS_CONF_ERR if error, else IPQOS_CONF_SUCCESS.
3981 static char *
3982 prepend_module_name(
3983 char *name,
3984 char *module)
3987 char *ret;
3989 IPQOSCDBG0(L2, "In prepend_module_name\n");
3991 ret = malloc(strlen(module) + strlen(".") + strlen(name) + 1);
3992 if (ret == NULL) {
3993 ipqos_msg(MT_ENOSTR, "malloc");
3994 return (NULL);
3997 (void) strcpy(ret, module);
3998 (void) strcat(ret, ".");
3999 (void) strcat(ret, name);
4001 return (ret);
4004 #if 0
4007 * check if element with matching s1 and s2 string is in table table.
4008 * RETURNS: 1 if found else 0.
4010 static int
4011 in_str_str_table(
4012 str_str_t *table,
4013 char *s1,
4014 char *s2)
4017 str_str_t *ss = table;
4019 /* loop through table till matched or end */
4021 while (ss->s1[0] != '\0' &&
4022 (strcmp(ss->s1, s1) != 0 || strcmp(ss->s2, s2) != 0)) {
4023 ss++;
4026 if (ss->s1[0] != '\0') {
4027 return (1);
4030 return (0);
4032 #endif /* 0 */
4035 * check whether name is a valid action/class/filter name.
4036 * RETURNS: IPQOS_CONF_ERR if invalid name else IPQOS_CONF_SUCCESS.
4038 static int
4039 valid_name(char *name)
4042 IPQOSCDBG1(L1, "In valid_name: name: %s\n", name);
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);
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);
4058 return (IPQOS_CONF_SUCCESS);
4061 /* ********************* string value manip fns ************************** */
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.
4069 static void
4070 get_str_val_value_range(
4071 str_val_nd_t *svnp,
4072 int *min,
4073 int *max)
4075 if (svnp != NULL) {
4076 *min = *max = svnp->sv.value;
4077 svnp = svnp->next;
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;
4089 * add an entry with string string and value val to sv_entrys.
4090 * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCESS.
4092 static int
4093 add_str_val_entry(
4094 str_val_nd_t **sv_entrys,
4095 char *string,
4096 uint32_t val)
4099 str_val_nd_t *sv_entry;
4101 IPQOSCDBG2(L1, "In add_str_val_entry: string: %s, val: %u\n", string,
4102 val);
4104 /* alloc new node */
4106 sv_entry = malloc(sizeof (str_val_nd_t));
4107 if (sv_entry == NULL) {
4108 return (IPQOS_CONF_ERR);
4111 /* populate node */
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);
4121 sv_entry->sv.value = val;
4123 /* place at start of sv_entrys list */
4125 sv_entry->next = *sv_entrys;
4126 *sv_entrys = sv_entry;
4128 return (IPQOS_CONF_SUCCESS);
4132 /* frees all the elements of sv_entrys. */
4133 static void
4134 free_str_val_entrys(
4135 str_val_nd_t *sv_entrys)
4138 str_val_nd_t *sve = sv_entrys;
4139 str_val_nd_t *tmp;
4141 IPQOSCDBG0(L1, "In free_str_val_entrys\n");
4143 while (sve) {
4144 free(sve->sv.string);
4145 tmp = sve->next;
4146 free(sve);
4147 sve = tmp;
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.
4156 static int
4157 str_val_list_lookup(
4158 str_val_nd_t *svs,
4159 char *string,
4160 uint32_t *val)
4163 str_val_nd_t *sv = svs;
4165 IPQOSCDBG1(L1, "In str_val_list_lookup: %s\n", string);
4167 /* loop through list and exit when found or list end */
4169 while (sv != NULL) {
4170 if (strcmp(sv->sv.string, string) == 0) {
4171 break;
4173 sv = sv->next;
4176 /* ret error if not found */
4178 if (sv == NULL) {
4179 return (IPQOS_CONF_ERR);
4182 *val = sv->sv.value;
4184 IPQOSCDBG1(L1, "svll: Value returned is %u\n", *val);
4185 return (IPQOS_CONF_SUCCESS);
4189 /* ************************ conf file read fns ***************************** */
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.
4201 static int
4202 readuser(
4203 char *str,
4204 uid_t *uid)
4206 struct passwd *pwd;
4207 char *lo;
4209 IPQOSCDBG1(L0, "In readuser, str: %s\n", str);
4211 if (str == NULL)
4212 return (IPQOS_CONF_ERR);
4214 * Check if this appears to be a uid, and if so check that a
4215 * corresponding user exists.
4217 if (isdigit((int)str[0])) {
4219 * Read a 32bit integer and check in doing so that
4220 * we have consumed the whole string.
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);
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;
4236 return (IPQOS_CONF_SUCCESS);
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.
4246 static int
4247 readrange(
4248 char *range_st,
4249 int *lower,
4250 int *upper)
4252 char *cp;
4253 char *end, *end2;
4255 IPQOSCDBG1(L0, "In readrange: string: %s\n", range_st);
4258 * get range boundarys.
4260 cp = strchr(range_st, '-');
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);
4274 } else { /* single value */
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);
4284 return (IPQOS_CONF_SUCCESS);
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.
4297 * RETURNS: IPQOS_CONF_ERR on resource or parse error, else IPQOS_CONF_SUCCESS.
4299 static int
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)
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;
4324 IPQOSCDBG4(L0, "In read_int_array: size: %u, lower: %u, upper: %u, "
4325 "first_token: %s\n", arr_size, llimit, ulimit, first_token);
4328 * read beginning curl.
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);
4337 * allocate and initialise array for holding read values.
4339 *arrp = malloc(arr_size * sizeof (int));
4340 if (*arrp == NULL) {
4341 ipqos_msg(MT_ENOSTR, "malloc");
4342 return (IPQOS_CONF_ERR);
4344 (void) memset(*arrp, -1, arr_size * sizeof (int));
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.
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);
4361 if (res != IPQOS_CONF_CURL_END) {
4362 goto array_err;
4364 IPQOSCDBG1(L0, "array declaration buffer contains: %s\n", buf);
4367 * loop reading "ranges ':' value;" till end of buffer.
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;
4376 *svalue++ = '\0';
4377 ranges = entry;
4380 * get value of number or enumerated symbol.
4382 if (enum_nvs) {
4384 * get rid of surrounding whitespace so as not to
4385 * confuse read_enum_value.
4387 SKIPWS(svalue);
4388 tmp = svalue;
4389 while (*tmp != '\0') {
4390 if (isspace(*tmp)) {
4391 *tmp = '\0';
4392 break;
4393 } else {
4394 tmp++;
4399 * read enumeration value.
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;
4412 IPQOSCDBG1(L0, "value: %u\n", value);
4415 * check value within valid range.
4417 if ((value < llimit) || (value > ulimit)) {
4418 IPQOSCDBG0(L0, "value out of range\n");
4419 goto array_err;
4424 * loop reading ranges for this value.
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);
4434 if (upper < lower) {
4435 uint32_t u = lower;
4436 lower = upper;
4437 upper = u;
4441 * check range valid for array size.
4443 if ((lower < 0) || (upper > arr_size)) {
4444 IPQOSCDBG0(L0, "Range out of array "
4445 "dimensions\n");
4446 goto array_err;
4450 * add this value to array indexes within range.
4452 for (x = lower; x <= upper; x++)
4453 (*arrp)[x] = value;
4456 * get next range.
4458 range = strtok_r(NULL, ",", &tmp);
4461 entry = strtok(NULL, ";");
4464 return (IPQOS_CONF_SUCCESS);
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);
4473 static int
4474 readllong(char *str, long long *llp, char **lo)
4477 *llp = strtoll(str, lo, 0);
4478 if (*lo == str) {
4479 return (IPQOS_CONF_ERR);
4481 return (IPQOS_CONF_SUCCESS);
4484 static int
4485 readuint8(char *str, uint8_t *ui8, char **lo)
4488 long long tmp;
4490 if (readllong(str, &tmp, lo) != 0) {
4491 return (IPQOS_CONF_ERR);
4493 if (tmp > UCHAR_MAX || tmp < 0) {
4494 return (IPQOS_CONF_ERR);
4496 *ui8 = (uint8_t)tmp;
4497 return (IPQOS_CONF_SUCCESS);
4500 static int
4501 readuint16(char *str, uint16_t *ui16, char **lo)
4503 long long tmp;
4505 if (readllong(str, &tmp, lo) != IPQOS_CONF_SUCCESS) {
4506 return (IPQOS_CONF_ERR);
4508 if (tmp > USHRT_MAX || tmp < 0) {
4509 return (IPQOS_CONF_ERR);
4511 *ui16 = (uint16_t)tmp;
4512 return (IPQOS_CONF_SUCCESS);
4515 static int
4516 readint16(char *str, int16_t *i16, char **lo)
4518 long long tmp;
4520 if (readllong(str, &tmp, lo) != 0) {
4521 return (IPQOS_CONF_ERR);
4523 if (tmp > SHRT_MAX || tmp < SHRT_MIN) {
4524 return (IPQOS_CONF_ERR);
4526 *i16 = (int16_t)tmp;
4527 return (IPQOS_CONF_SUCCESS);
4530 static int
4531 readint32(char *str, int *i32, char **lo)
4533 long long tmp;
4535 if (readllong(str, &tmp, lo) != IPQOS_CONF_SUCCESS) {
4536 return (IPQOS_CONF_ERR);
4538 if (tmp > INT_MAX || tmp < INT_MIN) {
4539 return (IPQOS_CONF_ERR);
4541 *i32 = tmp;
4542 return (IPQOS_CONF_SUCCESS);
4545 static int
4546 readuint32(char *str, uint32_t *ui32, char **lo)
4548 long long tmp;
4550 if (readllong(str, &tmp, lo) != IPQOS_CONF_SUCCESS) {
4551 return (IPQOS_CONF_ERR);
4553 if (tmp > UINT_MAX || tmp < 0) {
4554 return (IPQOS_CONF_ERR);
4556 *ui32 = (uint32_t)tmp;
4557 return (IPQOS_CONF_SUCCESS);
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.
4565 static int
4566 readifindex(
4567 char *ifname,
4568 int *ifindex)
4571 int s;
4572 struct lifreq lifrq;
4575 /* open socket */
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);
4582 /* copy ifname into lifreq */
4584 (void) strlcpy(lifrq.lifr_name, ifname, LIFNAMSIZ);
4586 /* do SIOGLIFINDEX ioctl */
4588 if (ioctl(s, SIOCGLIFINDEX, (caddr_t)&lifrq) == -1) {
4589 (void) close(s);
4590 return (IPQOS_CONF_ERR);
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"));
4598 (void) close(s);
4599 *ifindex = lifrq.lifr_index;
4600 return (IPQOS_CONF_SUCCESS);
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.
4608 static int
4609 readbool(char *str, boolean_t *bool)
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);
4620 return (IPQOS_CONF_SUCCESS);
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.
4629 static int
4630 readproto(char *proto_str, uint8_t *proto)
4633 struct protoent *pent;
4634 char *lo;
4635 int res;
4637 IPQOSCDBG1(L1, "In readproto: string: %s\n", proto_str);
4639 /* try name lookup */
4641 pent = getprotobyname(proto_str);
4642 if (pent) {
4643 *proto = pent->p_proto;
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);
4653 return (IPQOS_CONF_SUCCESS);
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.
4661 static int
4662 readport(char *port_str, uint16_t *port)
4665 struct servent *sent;
4666 char *tmp;
4668 IPQOSCDBG1(L1, "In readport: string: %s\n", port_str);
4670 /* try service name lookup */
4671 sent = getservbyname(port_str, NULL);
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);
4679 *port = htons(*port);
4680 } else {
4681 *port = sent->s_port;
4684 return (IPQOS_CONF_SUCCESS);
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.
4699 static int
4700 readtoken(
4701 FILE *fp,
4702 char **token)
4705 char *st, *tmp;
4706 int len;
4707 int quoted = 0;
4708 char *cmnt;
4709 char *bpos;
4710 int rembuf;
4712 static char *lo;
4713 static char *buf = NULL;
4714 static int bufsize;
4716 /* if first call initialize line buf to default size */
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);
4727 /* set buffer postition and size to use whole buffer */
4729 bpos = buf;
4730 rembuf = bufsize;
4734 * loop reading lines until we've read a line with a non-whitespace
4735 * char.
4738 do {
4739 /* if no leftover from previous invocation */
4741 if (lo == NULL) {
4744 * loop reading into buffer doubling if necessary until
4745 * we have either read a complete line or reached the
4746 * end of file.
4748 for (;;) {
4749 st = fgets(bpos, rembuf, fp);
4751 if (st == NULL) {
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);
4761 /* end of file */
4762 } else {
4763 free(buf);
4764 buf = NULL;
4765 *token = NULL;
4766 return (IPQOS_CONF_EOF);
4768 } else {
4769 /* if read a newline */
4771 if (buf[strlen(buf) - 1] == '\n') {
4772 lineno++;
4773 break;
4775 /* if read the last line */
4777 } else if (feof(fp)) {
4778 break;
4781 * not read a full line so buffer size
4782 * is too small, double it and retry.
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;
4797 * make parameters to fgets read
4798 * into centre of doubled buffer
4799 * so we retain what we've
4800 * already read.
4802 bpos = &buf[(bufsize / 2) - 1];
4803 rembuf = (bufsize / 2) + 1;
4808 st = buf;
4810 /* previous leftover, assign to st */
4812 } else {
4813 st = lo;
4814 lo = NULL;
4817 /* truncate at comment */
4819 cmnt = strchr(st, '#');
4820 if (cmnt) {
4821 *cmnt = '\0';
4824 /* Skip any whitespace */
4826 while (isspace(*st) && *st != '\0') {
4827 st++;
4830 } while (*st == '\0');
4833 /* find end of token */
4835 tmp = st;
4837 /* if curl advance 1 char */
4839 if (*tmp == CURL_BEGIN || *tmp == CURL_END) {
4840 tmp++;
4843 /* if dbl quote read until matching quote */
4845 } else if (*tmp == '"') {
4846 quoted++;
4847 tmp = ++st;
4849 while (*tmp != '"' && *tmp != '\n' && *tmp != '\0') {
4850 tmp++;
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);
4859 /* normal token */
4860 } else {
4861 /* find first whitespace, curl, newline or string end */
4863 while (!isspace(*tmp) && *tmp != CURL_BEGIN &&
4864 *tmp != CURL_END && *tmp != '\n' && *tmp != '\0') {
4865 tmp++;
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);
4877 bcopy(st, *token, len);
4878 (*token)[len] = '\0';
4880 /* if just read quoted string remove quote from remaining string */
4882 if (quoted) {
4883 tmp++;
4886 /* if not end of string, store rest for latter parsing */
4888 if (*tmp != '\0' && *tmp != '\n') {
4889 lo = tmp;
4892 /* for curl_end and curl_begin return special ret codes */
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);
4902 return (IPQOS_CONF_SUCCESS);
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.
4910 static str_val_nd_t *
4911 read_enum_nvs(char *line, char *module_name)
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;
4923 IPQOSCDBG1(L1, "In read_enum_nvs, line: %s\n", line);
4925 /* read opening brace */
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;
4936 * loop reading 'name = value' entrys seperated by comma until
4937 * reach closing brace.
4940 for (;;) {
4941 SKIPWS(start);
4942 if (*start == '\0') {
4943 IPQOSCDBG0(L1, "missing closing bracket\n");
4944 goto fail;
4948 * read name - read until whitespace, '=', closing curl,
4949 * or string end.
4952 for (cp = start;
4953 !isspace(*cp) && *cp != '=' && *cp != CURL_END &&
4954 *cp != '\0'; cp++) {}
4956 if (*cp == '\0') {
4957 IPQOSCDBG0(L1, "Unexpected line end in enum def'n\n");
4958 goto fail;
4960 /* finished definition, exit loop */
4961 } else if (*cp == CURL_END) {
4962 break;
4965 /* store name */
4967 len = cp - start;
4968 name = malloc(len + 1);
4969 if (name == NULL) {
4970 ipqos_msg(MT_ENOSTR, "malloc");
4971 goto fail;
4973 bcopy(start, name, len);
4974 name[len] = '\0';
4975 IPQOSCDBG1(L0, "Stored name: %s\n", name);
4977 /* read assignment */
4979 start = strchr(cp, '=');
4980 if (start == NULL) {
4981 IPQOSCDBG0(L1, "Missing = in enum def'n\n");
4982 goto fail;
4985 /* read value */
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;
4994 /* add name value to set */
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;
5001 free(name);
5002 name = NULL;
5004 /* try reading comma */
5005 cp = strchr(start, ',');
5007 if (cp != NULL) {
5008 start = cp + 1;
5010 /* no comma, advance to char past value last read */
5011 } else {
5012 start += readc;
5016 return (enum_vals);
5017 fail:
5018 free_str_val_entrys(enum_vals);
5019 free(name);
5021 /* if a parse error */
5023 if (errno == 0) {
5024 ipqos_msg(MT_ERROR, gettext("Types file for module %s is "
5025 "corrupt.\n"), module_name);
5028 return (NULL);
5032 * Given mapped_list with is a comma seperated list of map names, and value,
5033 * which is used to index into these maps, the function creates x new entries
5034 * in nvpp, where x is the number of map names specified. Each of these
5035 * entries has the value from the map in the position indexed by value and
5036 * with name module.${MAP_NAME}. The maps are contained in the modules config
5037 * file and have the form:
5038 * map map1 uint32 1,23,32,45,3
5039 * As you can see the map values are uint32, and along with uint8 are the
5040 * only supported types at the moment.
5042 * RETURNS: IPQOS_CONF_ERR if one of the maps specified in mapped_list
5043 * doesn't exist, if value is not a valid map position for a map, or if
5044 * there's a resource failure. otherwise IPQOS_CONF_SUCCESS is returned.
5046 static int
5047 read_mapped_values(
5048 FILE *tfp,
5049 nvlist_t **nvlp,
5050 char *module,
5051 char *mapped_list,
5052 int value)
5054 char *map_name, *lastparam, *tmpname;
5055 int res;
5056 ipqos_nvtype_t type;
5057 char dfltst[IPQOS_VALST_MAXLEN+1] = "";
5058 str_val_nd_t *enum_nvs;
5059 place_t place;
5061 IPQOSCDBG0(L1, "In read_mapped_values\n");
5063 map_name = (char *)strtok_r(mapped_list, ",", &lastparam);
5064 while (map_name != NULL) {
5065 char *tokval, *lastval;
5066 int index = 0;
5069 * get map info from types file.
5071 place = PL_MAP;
5072 res = readtype(tfp, module, map_name, &type, &enum_nvs,
5073 dfltst, B_FALSE, &place);
5074 if (res != IPQOS_CONF_SUCCESS) {
5075 return (IPQOS_CONF_ERR);
5079 * Just keep browsing the list till we get to the element
5080 * with the index from the value parameter or the end.
5082 tokval = (char *)strtok_r(dfltst, ",", &lastval);
5083 for (;;) {
5084 if (tokval == NULL) {
5085 ipqos_msg(MT_ERROR,
5086 gettext("Invalid value, %u, line %u.\n"),
5087 value, lineno);
5088 return (IPQOS_CONF_ERR);
5090 if (index++ == value) {
5091 break;
5093 tokval = (char *)strtok_r(NULL, ",", &lastval);
5098 * create fully qualified parameter name for map value.
5100 tmpname = prepend_module_name(map_name, module);
5101 if (tmpname == NULL) {
5102 return (IPQOS_CONF_ERR);
5106 * add map value with fqn to parameter nvlist.
5108 IPQOSCDBG2(L0, "Adding map %s, value %u to nvlist\n",
5109 tmpname, atoi(tokval));
5110 switch (type) {
5111 case IPQOS_DATA_TYPE_UINT8: {
5112 res = nvlist_add_byte(*nvlp, tmpname,
5113 (uint8_t)atoi(tokval));
5114 if (res != 0) {
5115 free(tmpname);
5116 ipqos_msg(MT_ENOSTR,
5117 "nvlist_add_uint8");
5118 return (IPQOS_CONF_ERR);
5120 break;
5122 case IPQOS_DATA_TYPE_UINT32: {
5123 res = nvlist_add_uint32(*nvlp, tmpname,
5124 (uint32_t)atoi(tokval));
5125 if (res != 0) {
5126 free(tmpname);
5127 ipqos_msg(MT_ENOSTR,
5128 "nvlist_add_uint32");
5129 return (IPQOS_CONF_ERR);
5131 break;
5133 default: {
5134 ipqos_msg(MT_ERROR,
5135 gettext("Types file for module %s is "
5136 "corrupt.\n"), module);
5137 IPQOSCDBG1(L0, "Unsupported map type for "
5138 "parameter %s given in types file.\n",
5139 map_name);
5140 return (IPQOS_CONF_ERR);
5143 free(tmpname);
5145 map_name = (char *)strtok_r(NULL, ",", &lastparam);
5148 return (IPQOS_CONF_SUCCESS);
5152 * Parses the string info_str into it's components. Its format is:
5153 * SIZE','[ENUM_DEF | RANGE], where SIZE is the size of the array,
5154 * ENUM_DEF is the definition of the enumeration for this array,
5155 * and RANGE is the set of values this array can accept. In
5156 * the event this array has an enumeration definition enum_nvs is
5157 * set to point at a str_val_nd_t structure which stores the names
5158 * and values associated with this enumeration. Otherwise, if this
5159 * is not an enumerated array, lower and upper are set to the lower
5160 * and upper values of RANGE.
5161 * RETURNS: IPQOS_CONF_ERR due to unexpected parse errors, else
5162 * IPQOS_CONF_SUCCESS.
5164 static int
5165 read_int_array_info(
5166 char *info_str,
5167 str_val_nd_t **enum_nvs,
5168 uint32_t *size,
5169 int *lower,
5170 int *upper,
5171 char *module)
5173 int res;
5174 char *end;
5175 char *token;
5176 char *tmp;
5178 IPQOSCDBG1(L0, "In read_array_info: info_str: %s\n",
5179 (info_str != NULL) ? info_str : "NULL");
5181 if (info_str == NULL) {
5182 IPQOSCDBG0(L0, "Null info string\n");
5183 goto fail;
5187 * read size.
5189 token = strtok(info_str, ",");
5190 *size = (uint32_t)strtol(token, &end, 10);
5191 SKIPWS(end);
5192 if ((end == token) || (*end != '\0')) {
5193 IPQOSCDBG0(L0, "Invalid size\n");
5194 goto fail;
5196 IPQOSCDBG1(L0, "read size: %u\n", *size);
5199 * check we have another string.
5201 token = strtok(NULL, "\n");
5202 if (token == NULL) {
5203 IPQOSCDBG0(L0, "Missing range/enum def\n");
5204 goto fail;
5206 IPQOSCDBG1(L0, "range/enum def: %s\n", token);
5209 * check if enumeration set or integer set and read enumeration
5210 * definition or integer range respectively.
5212 tmp = strchr(token, CURL_BEGIN);
5213 if (tmp == NULL) { /* a numeric range */
5214 res = readrange(token, lower, upper);
5215 if (res != IPQOS_CONF_SUCCESS) {
5216 IPQOSCDBG0(L0, "Failed reading range\n");
5217 goto fail;
5219 } else { /* an enumeration */
5220 *enum_nvs = read_enum_nvs(token, module);
5221 if (*enum_nvs == NULL) {
5222 IPQOSCDBG0(L0, "Failed reading enum def\n");
5223 goto fail;
5227 return (IPQOS_CONF_SUCCESS);
5228 fail:
5229 ipqos_msg(MT_ERROR,
5230 gettext("Types file for module %s is corrupt.\n"), module);
5231 return (IPQOS_CONF_ERR);
5235 * reads the value of an enumeration parameter from first_token and fp.
5236 * first_token is the first token of the value.
5237 * The format expected is NAME | { NAME1 [, NAME2 ] [, NAME3 ] }.
5238 * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCESS.
5240 static int
5241 read_enum_value(
5242 FILE *fp,
5243 char *first_token,
5244 str_val_nd_t *enum_vals,
5245 uint32_t *val)
5248 uint32_t u32;
5249 int ret;
5250 char *tk;
5251 char *lo = NULL;
5252 char *cm;
5253 int name_expected = 0;
5255 IPQOSCDBG0(L1, "In read_enum_value\n");
5257 /* init param val */
5258 *val = 0;
5260 /* first token not curl_begin, so lookup its value */
5262 if (*first_token != CURL_BEGIN) {
5263 ret = str_val_list_lookup(enum_vals, first_token, val);
5264 if (ret != IPQOS_CONF_SUCCESS) {
5265 ipqos_msg(MT_ERROR,
5266 gettext("Unrecognized value, %s, line %u.\n"),
5267 first_token, lineno);
5268 return (ret);
5271 /* curl_begin, so read values till curl_end, dicing at ',' */
5272 } else {
5274 name_expected++;
5276 for (;;) {
5279 * no leftover from pervious iteration so read new
5280 * token. This leftover happens because readtoken
5281 * doesn't interpret comma's as special characters
5282 * and thus could return 'val1,val2' as one token.
5283 * If this happens the val1 will be used in the
5284 * current iteration and what follows saved in lo
5285 * for processing by successive iterations.
5288 if (lo == NULL) {
5289 ret = readtoken(fp, &tk);
5290 if (ret == IPQOS_CONF_ERR) {
5291 return (ret);
5292 } else if (ret == IPQOS_CONF_EOF) {
5293 ipqos_msg(MT_ERROR,
5294 gettext("Unexpected EOF.\n"));
5295 return (IPQOS_CONF_ERR);
5298 } else { /* previous leftover, so use it */
5300 IPQOSCDBG1(L1, "Using leftover %s.\n", lo);
5301 tk = lo;
5302 lo = NULL;
5305 if (name_expected) {
5306 if (ret == IPQOS_CONF_CURL_END ||
5307 tk[0] == ',') {
5308 ipqos_msg(MT_ERROR,
5309 gettext("Malformed value list "
5310 "line %u.\n"), lineno);
5311 free(tk);
5312 return (IPQOS_CONF_ERR);
5316 * check if this token contains a ',' and
5317 * if so store it and what follows for next
5318 * iteration.
5320 cm = strchr(tk, ',');
5321 if (cm != NULL) {
5322 lo = malloc(strlen(cm) + 1);
5323 if (lo == NULL) {
5324 ipqos_msg(MT_ENOSTR, "malloc");
5325 free(tk);
5326 return (IPQOS_CONF_ERR);
5329 (void) strcpy(lo, cm);
5330 *cm = '\0';
5334 /* get name value and add to total val */
5336 ret = str_val_list_lookup(enum_vals, tk, &u32);
5337 if (ret != IPQOS_CONF_SUCCESS) {
5338 ipqos_msg(MT_ERROR,
5339 gettext("Unrecognized value, %s, "
5340 "line %u.\n"), tk, lineno);
5341 free(tk);
5342 return (IPQOS_CONF_ERR);
5345 *val = *val | u32;
5346 name_expected--;
5348 /* comma or curl end accepted */
5349 } else {
5351 /* we've reached curl_end so break */
5353 if (ret == IPQOS_CONF_CURL_END) {
5354 free(tk);
5355 break;
5357 /* not curl end and not comma */
5359 } else if (tk[0] != ',') {
5360 ipqos_msg(MT_ERROR,
5361 gettext("Malformed value list "
5362 "line %u.\n"), lineno);
5363 free(tk);
5364 return (IPQOS_CONF_ERR);
5368 * store anything after the comma for next
5369 * iteration.
5371 if (tk[1] != '\0') {
5372 lo = malloc(strlen(&tk[1]) + 1);
5373 if (lo == NULL) {
5374 ipqos_msg(MT_ENOSTR, "malloc");
5375 free(tk);
5376 return (IPQOS_CONF_ERR);
5378 (void) strcpy(lo, &tk[1]);
5381 name_expected++;
5384 free(tk);
5388 IPQOSCDBG1(L1, "value returned is: %u\n", *val);
5390 return (IPQOS_CONF_SUCCESS);
5394 * read the set of permanent classes/filter from the types file ref'd by tfp
5395 * and store them in a string table pointed to by perm_items,
5396 * with *nitems getting set to number of items read. perm_filters is set
5397 * to 1 if we're searching for permanent filters, else 0 for classes.
5398 * RETURNS: IPQOS_CONF_ERR if any errors, else IPQOS_CONF_SUCCESS.
5400 static int
5401 read_perm_items(
5402 int perm_filters,
5403 FILE *tfp,
5404 char *module_name,
5405 char ***perm_items,
5406 int *nitems)
5409 char lbuf[IPQOS_CONF_TYPE_LINE_LEN];
5410 int cnt = 0;
5411 char name[IPQOS_CONF_NAME_LEN+1];
5412 char foo[IPQOS_CONF_NAME_LEN+1];
5413 int res;
5414 char **items = NULL;
5415 char **tmp;
5416 char *marker;
5418 IPQOSCDBG0(L1, "In read_perm_items\n");
5421 /* seek to start of types file */
5423 if (fseek(tfp, 0, SEEK_SET) != 0) {
5424 ipqos_msg(MT_ENOSTR, "fseek");
5425 return (IPQOS_CONF_ERR);
5428 /* select which marker were looking for */
5430 if (perm_filters) {
5431 marker = IPQOS_CONF_PERM_FILTER_MK;
5432 } else {
5433 marker = IPQOS_CONF_PERM_CLASS_MK;
5436 /* scan file line by line till end */
5438 while (fgets(lbuf, IPQOS_CONF_TYPE_LINE_LEN, tfp) != NULL) {
5441 * if the line is marked as containing a default item name
5442 * read the name, extend the items string array
5443 * and store the string off the array.
5445 if (strncmp(lbuf, marker, strlen(marker)) == 0) {
5447 res = sscanf(lbuf,
5448 "%" VAL2STR(IPQOS_CONF_NAME_LEN) "s"
5449 "%" VAL2STR(IPQOS_CONF_NAME_LEN) "s",
5450 foo, name);
5451 if (res < 2) {
5452 ipqos_msg(MT_ERROR,
5453 gettext("Types file for module %s is "
5454 "corrupt.\n"), module_name);
5455 IPQOSCDBG1(L0, "Missing name with a %s.\n",
5456 marker);
5457 goto fail;
5460 /* extend items array to accomodate new item */
5462 tmp = reallocarray(items, cnt + 1, sizeof (char *));
5463 if (tmp == NULL) {
5464 ipqos_msg(MT_ENOSTR, "realloc");
5465 goto fail;
5466 } else {
5467 items = tmp;
5470 /* copy and store item name */
5472 items[cnt] = malloc(strlen(name) + 1);
5473 if (items[cnt] == NULL) {
5474 ipqos_msg(MT_ENOSTR, "malloc");
5475 goto fail;
5478 (void) strcpy(items[cnt], name);
5479 cnt++;
5482 IPQOSCDBG1(L1, "stored %s in perm items array\n",
5483 name);
5487 *perm_items = items;
5488 *nitems = cnt;
5490 return (IPQOS_CONF_SUCCESS);
5491 fail:
5492 for (cnt--; cnt >= 0; cnt--)
5493 free(items[cnt]);
5494 free(items);
5495 return (IPQOS_CONF_ERR);
5499 * Searches types file ref'd by tfp for the parameter named name
5500 * with the place corresponding with place parameter. The format
5501 * of the lines in the file are:
5502 * PLACE NAME TYPE [ ENUM_DEF ] [ DEFAULT_STR ]
5503 * The ENUM_DEF is an enumeration definition and is only present
5504 * for parameters of type enum. DEFAULT_STR is a default value for
5505 * this parameter. If present type is set to the appropriate type
5506 * enumeration and dfltst filled with DEFAULT_STR if one was set.
5507 * Also if the type is enum enum_nvps is made to point at a
5508 * set of name value pairs representing ENUM_DEF.
5510 * RETURNS: If any resource errors occur, or a matching parameter
5511 * isn't found IPQOS_CONF_ERR is returned, else IPQOS_CONF_SUCCESS.
5513 static int
5514 readtype(
5515 FILE *tfp,
5516 char *module_name,
5517 char *name,
5518 ipqos_nvtype_t *type,
5519 str_val_nd_t **enum_nvps,
5520 char *dfltst,
5521 boolean_t allow_ipgpc_priv,
5522 place_t *place)
5525 int ac;
5526 char lbuf[IPQOS_CONF_TYPE_LINE_LEN];
5527 char param[IPQOS_CONF_PNAME_LEN+1];
5528 char typest[IPQOS_CONF_TYPE_LEN+1];
5529 char place_st[IPQOS_CONF_TYPE_LEN+1];
5530 char *cp;
5531 int x;
5532 char *ipgpc_nm;
5533 int found = 0;
5535 IPQOSCDBG1(L1, "In readtype: param: %s\n", name);
5539 * if allow_ipgpc_priv is true then we allow ipgpc parameters that are
5540 * private between ipqosconf and ipgpc. eg. address masks, port masks.
5542 if (allow_ipgpc_priv && strcmp(module_name, IPGPC_NAME) == 0) {
5543 ipgpc_nm = prepend_module_name(name, IPGPC_NAME);
5544 if (ipgpc_nm == NULL) {
5545 return (IPQOS_CONF_ERR);
5548 if (strcmp(ipgpc_nm, IPGPC_SADDR_MASK) == 0 ||
5549 strcmp(ipgpc_nm, IPGPC_DADDR_MASK) == 0) {
5550 *type = IPQOS_DATA_TYPE_ADDRESS_MASK;
5551 return (IPQOS_CONF_SUCCESS);
5552 } else if (strcmp(ipgpc_nm, IPGPC_SPORT_MASK) == 0 ||
5553 strcmp(ipgpc_nm, IPGPC_DPORT_MASK) == 0) {
5554 *type = IPQOS_DATA_TYPE_UINT16;
5555 return (IPQOS_CONF_SUCCESS);
5556 } else if (strcmp(ipgpc_nm, IPGPC_FILTER_TYPE) == 0) {
5557 *type = IPQOS_DATA_TYPE_UINT32;
5558 return (IPQOS_CONF_SUCCESS);
5559 } else if (strcmp(ipgpc_nm, IPGPC_IF_INDEX) == 0) {
5560 *type = IPQOS_DATA_TYPE_IFINDEX;
5561 return (IPQOS_CONF_SUCCESS);
5564 free(ipgpc_nm);
5568 * read upto and including module version line.
5570 if (read_tfile_ver(tfp, IPQOS_MOD_STR, module_name) == -1)
5571 return (IPQOS_CONF_ERR);
5575 * loop reading lines of the types file until named parameter
5576 * found or EOF.
5578 while (fgets(lbuf, IPQOS_CONF_TYPE_LINE_LEN, tfp) != NULL) {
5581 * check whether blank or commented line; if so skip
5583 for (cp = lbuf; isspace(*cp) && *cp != '\0'; cp++) {}
5584 if (*cp == '\0' || *cp == '#') {
5585 continue;
5588 dfltst[0] = '\0';
5591 * read place, param, type and if present default str
5592 * from line.
5594 ac = sscanf(lbuf,
5595 "%" VAL2STR(IPQOS_CONF_TYPE_LEN) "s "
5596 "%" VAL2STR(IPQOS_CONF_PNAME_LEN) "s "
5597 "%" VAL2STR(IPQOS_CONF_TYPE_LEN) "s "
5598 "%" VAL2STR(IPQOS_VALST_MAXLEN) "s",
5599 place_st, param, typest, dfltst);
5600 if (ac < 3) {
5601 ipqos_msg(MT_ERROR,
5602 gettext("Types file for module %s is corrupt.\n"),
5603 module_name);
5604 IPQOSCDBG0(L0, "sscanf failed to read 3 strings.\n");
5605 return (IPQOS_CONF_ERR);
5609 * if the place and name match no need to look any further.
5611 if ((*place == PL_ANY) ||
5612 ((*place == PL_PARAMS) &&
5613 strcmp(place_st, IPQOS_PLACE_PRM_STR) == 0) ||
5614 ((*place == PL_FILTER) &&
5615 strcmp(place_st, IPQOS_PLACE_FILTER_STR) == 0) ||
5616 ((*place == PL_MAP) &&
5617 strcmp(place_st, IPQOS_PLACE_MAP_STR) == 0)) {
5618 if (strcmp(param, name) == 0) {
5619 found++;
5620 break;
5624 if (found == 0) {
5625 ipqos_msg(MT_ERROR,
5626 gettext("Invalid parameter, %s, line %u.\n"), name,
5627 lineno);
5628 return (IPQOS_CONF_ERR);
5632 * set the place parameter to the actual place when the PL_ANY flag
5633 * was set.
5635 if (*place == PL_ANY) {
5636 if (strcmp(place_st, IPQOS_PLACE_PRM_STR) == 0) {
5637 *place = PL_PARAMS;
5638 } else if (strcmp(place_st, IPQOS_PLACE_FILTER_STR) == 0) {
5639 *place = PL_FILTER;
5640 } else if (strcmp(place_st, IPQOS_PLACE_MAP_STR) == 0) {
5641 *place = PL_MAP;
5646 * get type enumeration
5648 for (x = 0; nv_types[x].string[0]; x++) {
5649 if (strcmp(nv_types[x].string, typest) == 0) {
5650 break;
5654 * check that we have a type corresponding with the one the types
5655 * file specifies.
5657 if (nv_types[x].string[0] == '\0') {
5658 ipqos_msg(MT_ERROR,
5659 gettext("Types file for module %s is corrupt.\n"),
5660 module_name);
5661 return (IPQOS_CONF_ERR);
5663 *type = nv_types[x].value;
5666 * if enumeration type get set of name/vals and any default value
5668 if (*type == IPQOS_DATA_TYPE_ENUM) {
5669 *enum_nvps = read_enum_nvs(lbuf, module_name);
5670 if (*enum_nvps == NULL) {
5671 return (IPQOS_CONF_ERR);
5674 dfltst[0] = '\0';
5675 cp = strchr(lbuf, CURL_END);
5676 (void) sscanf(++cp,
5677 "%" VAL2STR(IPQOS_VALST_MAXLEN) "s", dfltst);
5681 IPQOSCDBG2(L1, "read type: %s default: %s\n", nv_types[x].string,
5682 *dfltst ? dfltst : "None");
5683 return (IPQOS_CONF_SUCCESS);
5688 * Reads a name and a value from file ref'd by cfp into list indirectly
5689 * ref'd by nvlp; If this list is NULL it will be created to accomodate
5690 * the name/value. The name must be either a special token for
5691 * for the place, or be present in the module types file ref'd by tfp.
5692 * *type is set to the enumeration of the type of the parameter and
5693 * nvp to point at the element with the nvlp ref'd list.
5694 * RETURNS: IPQOS_CONF_CURL_END if read CURL_END as name,
5695 * IPQOS_CONF_ERR on errors, else IPQOS_CONF_SUCCESS.
5697 static int
5698 readnvpair(
5699 FILE *cfp,
5700 FILE *tfp,
5701 nvlist_t **nvlp,
5702 nvpair_t **nvp,
5703 ipqos_nvtype_t *type,
5704 place_t place,
5705 char *module_name)
5708 char *name = NULL;
5709 char *valst = NULL;
5710 int res;
5711 char *tmp;
5712 str_val_nd_t *enum_nvs = NULL;
5713 char dfltst[IPQOS_VALST_MAXLEN+1];
5715 IPQOSCDBG0(L1, "in readnvpair\n");
5718 * read nvpair name
5720 res = readtoken(cfp, &name);
5723 * if reached eof, curl end or error encountered return to caller
5725 if (res == IPQOS_CONF_EOF) {
5726 ipqos_msg(MT_ERROR, gettext("Unexpected EOF.\n"));
5727 return (IPQOS_CONF_ERR);
5728 } else if (res == IPQOS_CONF_ERR) {
5729 return (res);
5730 } else if (res == IPQOS_CONF_CURL_END) {
5731 free(name);
5732 return (res);
5736 * read nvpair value
5738 res = readtoken(cfp, &valst);
5741 * check we've read a valid value
5743 if (res != IPQOS_CONF_SUCCESS && res != IPQOS_CONF_CURL_BEGIN) {
5744 if (res == IPQOS_CONF_EOF) {
5745 ipqos_msg(MT_ERROR, gettext("Unexpected EOF.\n"));
5746 } else if (res == IPQOS_CONF_CURL_END) {
5747 ipqos_msg(MT_ERROR,
5748 gettext("Missing parameter value line %u.\n"),
5749 lineno);
5750 free(valst);
5751 } /* we do nothing special for IPQOS_CONF_ERR */
5752 free(name);
5753 return (IPQOS_CONF_ERR);
5757 * check for generic parameters.
5760 if ((place == PL_CLASS) &&
5761 strcmp(name, IPQOS_CONF_NEXT_ACTION_STR) == 0) {
5762 *type = IPQOS_DATA_TYPE_ACTION;
5764 } else if (place == PL_PARAMS &&
5765 strcmp(name, IPQOS_CONF_GLOBAL_STATS_STR) == 0 ||
5766 place == PL_CLASS &&
5767 strcmp(name, IPQOS_CONF_STATS_ENABLE_STR) == 0) {
5768 *type = IPQOS_DATA_TYPE_BOOLEAN;
5770 } else if (tfp == NULL ||
5771 ((place != PL_PARAMS) && strcmp(name, IPQOS_CONF_NAME_STR) == 0) ||
5772 (place == PL_FILTER) && (strcmp(name, IPQOS_CONF_CLASS_STR) ==
5773 0) ||
5774 (place == PL_ACTION) && (strcmp(name, IPQOS_CONF_MODULE_STR) ==
5775 0)) {
5776 *type = IPQOS_DATA_TYPE_STRING;
5778 } else { /* if not generic parameter */
5780 * get type from types file
5782 if (readtype(tfp, module_name, name, type, &enum_nvs, dfltst,
5783 B_FALSE, &place) != IPQOS_CONF_SUCCESS) {
5784 free(name);
5785 free(valst);
5786 return (IPQOS_CONF_ERR);
5790 * get full module prefix parameter name
5792 tmp = name;
5793 if ((name = prepend_module_name(name, module_name)) == NULL) {
5794 name = tmp;
5795 goto fail;
5797 free(tmp);
5800 IPQOSCDBG3(L1, "NVP, name: %s, str_value: %s, type: %s\n", name,
5801 valst, nv_types[*type].string);
5805 * create nvlist if not present already
5807 if (*nvlp == NULL) {
5808 res = nvlist_alloc(nvlp, NV_UNIQUE_NAME, 0);
5809 if (res != 0) {
5810 ipqos_msg(MT_ENOSTR, "nvlist_alloc");
5811 free(name);
5812 free(valst);
5813 return (IPQOS_CONF_ERR);
5818 * check we haven't already read this parameter
5820 if (find_nvpair(*nvlp, name)) {
5821 ipqos_msg(MT_ERROR, gettext("Duplicate parameter line %u.\n"),
5822 lineno);
5823 goto fail;
5827 * convert value string to appropriate type and add to nvlist
5830 switch (*type) {
5831 case IPQOS_DATA_TYPE_IFNAME: {
5832 uint32_t ifidx;
5834 res = readifindex(valst, (int *)&ifidx);
5835 if (res == IPQOS_CONF_SUCCESS) {
5836 res = nvlist_add_uint32(*nvlp, IPGPC_IF_INDEX,
5837 ifidx);
5838 if (res != 0) {
5839 ipqos_msg(MT_ENOSTR,
5840 "nvlist_add_uint32");
5841 goto fail;
5843 (void) nvlist_remove_all(*nvlp, name);
5845 * change name to point at the name of the
5846 * new ifindex nvlist entry as name is used
5847 * later in the function.
5849 free(name);
5850 name = malloc(strlen(IPGPC_IF_INDEX) + 1);
5851 if (name == NULL) {
5852 ipqos_msg(MT_ENOSTR, "malloc");
5853 goto fail;
5855 (void) strcpy(name, IPGPC_IF_INDEX);
5857 break;
5859 case IPQOS_DATA_TYPE_PROTO: {
5860 uint8_t proto;
5862 res = readproto(valst, &proto);
5863 if (res == IPQOS_CONF_SUCCESS) {
5864 res = nvlist_add_byte(*nvlp, name, proto);
5865 if (res != 0) {
5866 ipqos_msg(MT_ENOSTR, "nvlist_add_byte");
5867 goto fail;
5870 break;
5872 case IPQOS_DATA_TYPE_PORT: {
5873 uint16_t port;
5875 res = readport(valst, &port);
5876 if (res == IPQOS_CONF_SUCCESS) {
5878 /* add port */
5880 res = nvlist_add_uint16(*nvlp, name, port);
5881 if (res != 0) {
5882 ipqos_msg(MT_ENOSTR,
5883 "nvlist_add_uint16");
5884 goto fail;
5887 /* add appropriate all ones port mask */
5889 if (strcmp(name, IPGPC_DPORT) == 0) {
5890 res = nvlist_add_uint16(*nvlp,
5891 IPGPC_DPORT_MASK, ~0);
5893 } else if (strcmp(name, IPGPC_SPORT) == 0) {
5894 res = nvlist_add_uint16(*nvlp,
5895 IPGPC_SPORT_MASK, ~0);
5897 if (res != 0) {
5898 ipqos_msg(MT_ENOSTR,
5899 "nvlist_add_uint16");
5900 goto fail;
5903 break;
5905 case IPQOS_DATA_TYPE_ADDRESS:
5906 case IPQOS_DATA_TYPE_ACTION:
5907 case IPQOS_DATA_TYPE_STRING:
5908 res = nvlist_add_string(*nvlp, name, valst);
5909 if (res != 0) {
5910 ipqos_msg(MT_ENOSTR, "nvlist_add_string");
5911 goto fail;
5913 break;
5914 case IPQOS_DATA_TYPE_BOOLEAN: {
5915 boolean_t b;
5917 res = readbool(valst, &b);
5918 if (res == IPQOS_CONF_SUCCESS) {
5919 res = nvlist_add_uint32(*nvlp, name,
5920 (uint32_t)b);
5921 if (res != 0) {
5922 ipqos_msg(MT_ENOSTR,
5923 "nvlist_add_uint32");
5924 goto fail;
5927 break;
5929 case IPQOS_DATA_TYPE_UINT8: {
5930 uint8_t u8;
5932 res = readuint8(valst, &u8, &tmp);
5933 if (res == IPQOS_CONF_SUCCESS) {
5934 res = nvlist_add_byte(*nvlp, name, u8);
5935 if (res != 0) {
5936 ipqos_msg(MT_ENOSTR, "nvlist_add_byte");
5937 goto fail;
5940 break;
5942 case IPQOS_DATA_TYPE_INT16: {
5943 int16_t i16;
5945 res = readint16(valst, &i16, &tmp);
5946 if (res == IPQOS_CONF_SUCCESS) {
5947 res = nvlist_add_int16(*nvlp, name, i16);
5948 if (res != 0) {
5949 ipqos_msg(MT_ENOSTR,
5950 "nvlist_add_int16");
5951 goto fail;
5954 break;
5956 case IPQOS_DATA_TYPE_UINT16: {
5957 uint16_t u16;
5959 res = readuint16(valst, &u16, &tmp);
5960 if (res == IPQOS_CONF_SUCCESS) {
5961 res = nvlist_add_uint16(*nvlp, name, u16);
5962 if (res != 0) {
5963 ipqos_msg(MT_ENOSTR,
5964 "nvlist_add_int16");
5965 goto fail;
5968 break;
5970 case IPQOS_DATA_TYPE_INT32: {
5971 int i32;
5973 res = readint32(valst, &i32, &tmp);
5974 if (res == IPQOS_CONF_SUCCESS) {
5975 res = nvlist_add_int32(*nvlp, name, i32);
5976 if (res != 0) {
5977 ipqos_msg(MT_ENOSTR,
5978 "nvlist_add_int32");
5979 goto fail;
5982 break;
5984 case IPQOS_DATA_TYPE_UINT32: {
5985 uint32_t u32;
5987 res = readuint32(valst, &u32, &tmp);
5988 if (res == IPQOS_CONF_SUCCESS) {
5989 res = nvlist_add_uint32(*nvlp, name, u32);
5990 if (res != 0) {
5991 ipqos_msg(MT_ENOSTR,
5992 "nvlist_add_uint32");
5993 goto fail;
5996 break;
5998 case IPQOS_DATA_TYPE_ENUM: {
5999 uint32_t val;
6001 res = read_enum_value(cfp, valst, enum_nvs, &val);
6002 if (res == IPQOS_CONF_SUCCESS) {
6003 res = nvlist_add_uint32(*nvlp, name, val);
6004 if (res != 0) {
6005 ipqos_msg(MT_ENOSTR,
6006 "nvlist_add_uint32");
6007 goto fail;
6009 } else {
6010 goto fail;
6012 break;
6015 * For now the dfltst contains a comma separated list of the
6016 * type we need this parameter to be mapped to.
6017 * read_mapped_values will fill in all the mapped parameters
6018 * and their values in the nvlist.
6020 case IPQOS_DATA_TYPE_M_INDEX: {
6021 uint8_t u8;
6023 res = readuint8(valst, &u8, &tmp);
6024 if (res == IPQOS_CONF_SUCCESS) {
6025 res = nvlist_add_byte(*nvlp, name, u8);
6026 if (res != 0) {
6027 ipqos_msg(MT_ENOSTR,
6028 "nvlist_add_uint8");
6029 goto fail;
6031 } else {
6032 *type = IPQOS_DATA_TYPE_UINT8;
6033 break;
6035 res = read_mapped_values(tfp, nvlp, module_name,
6036 dfltst, u8);
6037 if (res != IPQOS_CONF_SUCCESS) {
6038 goto fail;
6040 break;
6042 case IPQOS_DATA_TYPE_INT_ARRAY: {
6043 str_val_nd_t *arr_enum_nvs = NULL;
6044 uint32_t size;
6045 int llimit = 0, ulimit = 0;
6046 int *arr;
6049 * read array info from types file.
6051 res = read_int_array_info(dfltst, &arr_enum_nvs, &size,
6052 &llimit, &ulimit, module_name);
6053 if (res != IPQOS_CONF_SUCCESS) {
6054 goto fail;
6058 * read array contents from config file and construct
6059 * array with them.
6061 res = read_int_array(cfp, valst, &arr, size, llimit,
6062 ulimit, arr_enum_nvs);
6063 if (res != IPQOS_CONF_SUCCESS) {
6064 goto fail;
6068 * add array to nvlist.
6070 res = nvlist_add_int32_array(*nvlp, name, arr, size);
6071 if (res != 0) {
6072 ipqos_msg(MT_ENOSTR, "nvlist_add_int32");
6073 goto fail;
6077 * free uneeded resources.
6079 free(arr);
6080 if (arr_enum_nvs)
6081 free_str_val_entrys(arr_enum_nvs);
6083 break;
6085 case IPQOS_DATA_TYPE_USER: {
6086 uid_t uid;
6088 res = readuser(valst, &uid);
6089 if (res == IPQOS_CONF_SUCCESS) {
6090 res = nvlist_add_int32(*nvlp, name, (int)uid);
6091 if (res != 0) {
6092 ipqos_msg(MT_ENOSTR,
6093 "nvlist_add_int32");
6094 goto fail;
6097 break;
6099 #ifdef _IPQOS_CONF_DEBUG
6100 default: {
6102 * we shouldn't have a type that doesn't have a switch
6103 * entry.
6105 assert(1);
6107 #endif
6109 if (res != 0) {
6110 ipqos_msg(MT_ERROR, gettext("Invalid %s, line %u.\n"),
6111 nv_types[*type].string, lineno);
6112 goto fail;
6115 /* set the nvp parameter to point at the newly added nvlist entry */
6117 *nvp = find_nvpair(*nvlp, name);
6119 free(name);
6120 free(valst);
6121 if (enum_nvs)
6122 free_str_val_entrys(enum_nvs);
6123 return (IPQOS_CONF_SUCCESS);
6124 fail:
6125 free(name);
6126 free(valst);
6127 if (enum_nvs != NULL)
6128 free_str_val_entrys(enum_nvs);
6129 return (IPQOS_CONF_ERR);
6133 * read a parameter clause from cfp into *params.
6134 * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCES.
6136 static int
6137 readparams(
6138 FILE *cfp,
6139 FILE *tfp,
6140 char *module_name,
6141 ipqos_conf_params_t *params)
6144 int res;
6145 nvpair_t *nvp;
6146 ipqos_nvtype_t type;
6147 boolean_t bl;
6148 char *nm;
6149 char *action;
6150 char tmp[IPQOS_CONF_PNAME_LEN];
6151 int read_stats = 0;
6153 IPQOSCDBG0(L0, "in readparams\n");
6155 /* read beginning curl */
6157 res = read_curl_begin(cfp);
6158 if (res != IPQOS_CONF_SUCCESS) {
6159 return (res);
6163 * loop reading nvpairs, adding to params nvlist until encounter
6164 * CURL_END.
6166 for (;;) {
6167 /* read nvpair */
6169 res = readnvpair(cfp, tfp, &params->nvlist,
6170 &nvp, &type, PL_PARAMS, module_name);
6171 if (res == IPQOS_CONF_ERR) {
6172 goto fail;
6174 /* we have finished reading params */
6176 } else if (res == IPQOS_CONF_CURL_END) {
6177 break;
6181 * read global stats - place into params struct and remove
6182 * from nvlist.
6184 if (strcmp(nvpair_name(nvp), IPQOS_CONF_GLOBAL_STATS_STR) ==
6185 0) {
6186 /* check we haven't read stats before */
6188 if (read_stats) {
6189 ipqos_msg(MT_ERROR,
6190 gettext("Duplicate parameter line %u.\n"),
6191 lineno);
6192 goto fail;
6194 read_stats++;
6196 (void) nvpair_value_uint32(nvp, (uint32_t *)&bl);
6197 params->stats_enable = bl;
6198 (void) nvlist_remove_all(params->nvlist,
6199 IPQOS_CONF_GLOBAL_STATS_STR);
6203 * read action type parameter - add it to list of action refs.
6204 * also, if it's one of continue or drop virtual actions
6205 * change the action name to their special ipp names in
6206 * the action ref list and the nvlist.
6208 } else if (type == IPQOS_DATA_TYPE_ACTION) {
6210 /* get name and value from nvlist */
6212 nm = nvpair_name(nvp);
6213 (void) nvpair_value_string(nvp, &action);
6215 /* if virtual action names change to ipp name */
6217 if ((strcmp(action, IPQOS_CONF_CONT_STR) == 0) ||
6218 strcmp(action, IPQOS_CONF_DROP_STR) == 0) {
6220 * we copy nm to a seperate buffer as nv_pair
6221 * name above gave us a ptr to internal
6222 * memory which causes strange behaviour
6223 * when we re-value that nvlist element.
6225 (void) strlcpy(tmp, nm, sizeof (tmp));
6226 nm = tmp;
6229 /* modify nvlist entry and change action */
6231 if (strcmp(action, IPQOS_CONF_CONT_STR) == 0) {
6232 action = IPP_ANAME_CONT;
6233 res = nvlist_add_string(params->nvlist,
6234 nm, action);
6235 } else {
6236 action = IPP_ANAME_DROP;
6237 res = nvlist_add_string(params->nvlist,
6238 nm, action);
6240 if (res != 0) {
6241 ipqos_msg(MT_ENOSTR,
6242 "nvlist_add_string");
6243 goto fail;
6247 /* add action reference to params */
6249 res = add_aref(&params->actions, nm, action);
6253 return (IPQOS_CONF_SUCCESS);
6254 fail:
6256 if (params->nvlist) {
6257 nvlist_free(params->nvlist);
6258 params->nvlist = NULL;
6260 if (params->actions) {
6261 free_arefs(params->actions);
6262 params->actions = NULL;
6264 return (IPQOS_CONF_ERR);
6267 /* ************************* class manip fns ****************************** */
6272 * make dst point at a dupicate class struct with duplicate elements to src.
6273 * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCESS.
6275 static int
6276 dup_class(
6277 ipqos_conf_class_t *src,
6278 ipqos_conf_class_t **dst)
6281 ipqos_conf_class_t *cls;
6282 int res;
6284 IPQOSCDBG1(DIFF, "In dup_class: class: %s\n", src->name);
6285 cls = alloc_class();
6286 if (cls == NULL) {
6287 return (IPQOS_CONF_ERR);
6290 /* struct copy */
6291 *cls = *src;
6293 /* we're not interested in the nvlist for a class */
6294 cls->nvlist = NULL;
6297 /* copy first action reference */
6298 cls->alist = NULL;
6299 res = add_aref(&cls->alist, src->alist->field, src->alist->name);
6300 if (res != IPQOS_CONF_SUCCESS) {
6301 free(cls);
6302 return (res);
6305 *dst = cls;
6307 return (IPQOS_CONF_SUCCESS);
6311 * create a zero'd class struct and return a ptr to it.
6312 * RETURNS: ptr to struct on success, NULL otherwise.
6314 static ipqos_conf_class_t *
6315 alloc_class()
6318 ipqos_conf_class_t *class;
6320 class = malloc(sizeof (ipqos_conf_class_t));
6321 if (class) {
6322 bzero(class, sizeof (ipqos_conf_class_t));
6323 } else {
6324 ipqos_msg(MT_ENOSTR, "malloc");
6327 return (class);
6330 /* frees up all memory occupied by a filter struct and its contents. */
6331 static void
6332 free_class(ipqos_conf_class_t *cls)
6335 if (cls == NULL)
6336 return;
6338 /* free its nvlist if present */
6340 nvlist_free(cls->nvlist);
6342 /* free its action refs if present */
6344 if (cls->alist)
6345 free_arefs(cls->alist);
6347 /* finally free class itself */
6348 free(cls);
6352 * Checks whether there is a class called class_nm in classes list.
6353 * RETURNS: ptr to first matched class, else if not matched NULL.
6355 static ipqos_conf_class_t *
6356 classexist(
6357 char *class_nm,
6358 ipqos_conf_class_t *classes)
6361 ipqos_conf_class_t *cls;
6363 IPQOSCDBG1(L1, "In classexist: name: %s\n", class_nm);
6365 for (cls = classes; cls; cls = cls->next) {
6366 if (strcmp(class_nm, cls->name) == 0) {
6367 break;
6371 return (cls);
6376 /* ************************** filter manip fns **************************** */
6381 * Checks whether there is a filter called filter_nm with instance number
6382 * instance in filters list created by us or permanent. Instance value -1
6383 * is a wildcard.
6384 * RETURNS: ptr to first matched filter, else if not matched NULL.
6386 static ipqos_conf_filter_t *
6387 filterexist(
6388 char *filter_nm,
6389 int instance,
6390 ipqos_conf_filter_t *filters)
6393 IPQOSCDBG2(L1, "In filterexist: name :%s, inst: %d\n", filter_nm,
6394 instance);
6396 while (filters) {
6397 if (strcmp(filters->name, filter_nm) == 0 &&
6398 (instance == -1 || filters->instance == instance) &&
6399 (filters->originator == IPP_CONFIG_IPQOSCONF ||
6400 filters->originator == IPP_CONFIG_PERMANENT)) {
6401 break;
6403 filters = filters->next;
6405 return (filters);
6409 * allocate and zero a filter structure.
6410 * RETURNS: NULL on error, else ptr to filter struct.
6412 static ipqos_conf_filter_t *
6413 alloc_filter()
6416 ipqos_conf_filter_t *flt;
6418 flt = malloc(sizeof (ipqos_conf_filter_t));
6419 if (flt) {
6420 bzero(flt, sizeof (ipqos_conf_filter_t));
6421 flt->instance = -1;
6422 } else {
6423 ipqos_msg(MT_ENOSTR, "malloc");
6426 return (flt);
6429 /* free flt and all it's contents. */
6431 static void
6432 free_filter(ipqos_conf_filter_t *flt)
6435 IPQOSCDBG2(L1, "In free_filter: filter: %s, inst: %d\n", flt->name,
6436 flt->instance);
6438 if (flt == NULL)
6439 return;
6441 free(flt->src_nd_name);
6442 free(flt->dst_nd_name);
6443 nvlist_free(flt->nvlist);
6444 free(flt);
6448 * makes a copy of ofilter and its contents and points nfilter at it. It
6449 * also adds an instance number to the filter and if either saddr or
6450 * daddr are non-null that address to the filters nvlist along with
6451 * an all 1s address mask and the af.
6452 * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCES.
6454 static int
6455 dup_filter(
6456 ipqos_conf_filter_t *ofilter,
6457 ipqos_conf_filter_t **nfilter,
6458 int af,
6459 int inv6, /* if saddr or daddr set and v4 filter are they in v6 addr */
6460 void *saddr,
6461 void *daddr,
6462 int inst)
6465 ipqos_conf_filter_t *nf;
6466 int res;
6467 in6_addr_t v6addr;
6468 in6_addr_t all_1s_v6;
6470 IPQOSCDBG4(MHME, "In dup_filter: name: %s, af: %u, inv6: %u, ins: %d\n",
6471 ofilter->name, af, inv6, inst);
6473 /* show src address and dst address if present */
6474 #ifdef _IPQOS_CONF_DEBUG
6475 if (ipqosconf_dbg_flgs & MHME) {
6476 char st[100];
6478 if (saddr) {
6479 (void) fprintf(stderr, "saddr: %s\n",
6480 inet_ntop(inv6 ? AF_INET6 : AF_INET, saddr, st,
6481 100));
6484 if (daddr) {
6485 (void) fprintf(stderr, "daddr: %s\n",
6486 inet_ntop(inv6 ? AF_INET6 : AF_INET, daddr, st,
6487 100));
6490 #endif /* _IPQOS_CONF_DEBUG */
6492 /* init local v6 address to 0 */
6493 (void) bzero(&v6addr, sizeof (in6_addr_t));
6495 /* create an all 1s address for use as mask */
6496 (void) memset(&all_1s_v6, ~0, sizeof (in6_addr_t));
6498 /* create a new filter */
6500 nf = alloc_filter();
6501 if (nf == NULL) {
6502 return (IPQOS_CONF_ERR);
6505 /* struct copy old filter to new */
6506 *nf = *ofilter;
6508 /* copy src filters nvlist if there is one to copy */
6510 if (ofilter->nvlist) {
6511 res = nvlist_dup(ofilter->nvlist, &nf->nvlist, 0);
6512 if (res != 0) {
6513 ipqos_msg(MT_ENOSTR, "nvlist_dup");
6514 goto fail;
6518 /* copy src and dst node names if present */
6520 if (ofilter->src_nd_name) {
6521 nf->src_nd_name = malloc(strlen(ofilter->src_nd_name) + 1);
6522 if (nf->src_nd_name == NULL) {
6523 ipqos_msg(MT_ENOSTR, "malloc");
6524 goto fail;
6526 (void) strcpy(nf->src_nd_name, ofilter->src_nd_name);
6528 if (ofilter->dst_nd_name) {
6529 nf->dst_nd_name = malloc(strlen(ofilter->dst_nd_name) + 1);
6530 if (nf->dst_nd_name == NULL) {
6531 ipqos_msg(MT_ENOSTR, "malloc");
6532 goto fail;
6534 (void) strcpy(nf->dst_nd_name, ofilter->dst_nd_name);
6537 /* add filter addresses type */
6539 res = nvlist_add_byte(nf->nvlist, IPGPC_FILTER_TYPE,
6540 af == AF_INET ? IPGPC_V4_FLTR : IPGPC_V6_FLTR);
6541 if (res != 0) {
6542 ipqos_msg(MT_ENOSTR, "nvlist_add_byte");
6543 goto fail;
6545 IPQOSCDBG1(MHME, "adding address type %s in dup filter\n",
6546 af == AF_INET ? "AF_INET" : "AF_INET6");
6548 /* add saddr if present */
6550 if (saddr) {
6551 if (af == AF_INET && !inv6) {
6552 V4_PART_OF_V6(v6addr) = *(uint32_t *)saddr;
6553 saddr = &v6addr;
6556 /* add address and all 1's mask */
6558 if (nvlist_add_uint32_array(nf->nvlist, IPGPC_SADDR,
6559 (uint32_t *)saddr, 4) != 0 ||
6560 nvlist_add_uint32_array(nf->nvlist, IPGPC_SADDR_MASK,
6561 (uint32_t *)&all_1s_v6, 4) != 0) {
6562 ipqos_msg(MT_ENOSTR, "nvlist_add_uint32_array");
6563 goto fail;
6568 /* add daddr if present */
6570 if (daddr) {
6571 if (af == AF_INET && !inv6) {
6572 V4_PART_OF_V6(v6addr) = *(uint32_t *)daddr;
6573 daddr = &v6addr;
6576 /* add address and all 1's mask */
6578 if (nvlist_add_uint32_array(nf->nvlist, IPGPC_DADDR,
6579 (uint32_t *)daddr, 4) != 0 ||
6580 nvlist_add_uint32_array(nf->nvlist, IPGPC_DADDR_MASK,
6581 (uint32_t *)&all_1s_v6, 4) != 0) {
6582 ipqos_msg(MT_ENOSTR, "nvlist_add_uint32_array");
6583 goto fail;
6587 /* add filter instance */
6589 nf->instance = inst;
6591 *nfilter = nf;
6592 return (IPQOS_CONF_SUCCESS);
6593 fail:
6594 free_filter(nf);
6595 return (IPQOS_CONF_ERR);
6600 /* ************************* action manip fns ********************** */
6605 * create and zero action structure and a params structure hung off of it.
6606 * RETURNS: ptr to allocated action on success, else NULL.
6608 static ipqos_conf_action_t *
6609 alloc_action()
6612 ipqos_conf_action_t *action;
6614 action = (ipqos_conf_action_t *)malloc(sizeof (ipqos_conf_action_t));
6615 if (action == NULL) {
6616 ipqos_msg(MT_ENOSTR, "malloc");
6617 return (action);
6619 bzero(action, sizeof (ipqos_conf_action_t));
6621 action->params = (ipqos_conf_params_t *)
6622 malloc(sizeof (ipqos_conf_params_t));
6623 if (action->params == NULL) {
6624 free(action);
6625 return (NULL);
6627 bzero(action->params, sizeof (ipqos_conf_params_t));
6628 action->params->stats_enable = B_FALSE;
6630 return (action);
6634 * free all the memory used in all the actions in actions list.
6636 static void
6637 free_actions(
6638 ipqos_conf_action_t *actions)
6641 ipqos_conf_action_t *act = actions;
6642 ipqos_conf_action_t *next;
6643 ipqos_conf_filter_t *flt, *nf;
6644 ipqos_conf_class_t *cls, *nc;
6646 while (act != NULL) {
6647 /* free parameters */
6649 if (act->params != NULL) {
6650 free_arefs(act->params->actions);
6651 nvlist_free(act->params->nvlist);
6652 free(act->params);
6655 /* free action nvlist */
6657 free(act->nvlist);
6659 /* free filters */
6661 flt = act->filters;
6662 while (flt != NULL) {
6663 nf = flt->next;
6664 free_filter(flt);
6665 flt = nf;
6668 /* free classes */
6670 cls = act->classes;
6671 while (cls != NULL) {
6672 nc = cls->next;
6673 free_class(cls);
6674 cls = nc;
6677 /* free permanent classes table */
6678 cleanup_string_table(act->perm_classes, act->num_perm_classes);
6680 /* free filters to retry */
6682 flt = act->retry_filters;
6683 while (flt != NULL) {
6684 nf = flt->next;
6685 free_filter(flt);
6686 flt = nf;
6689 /* free dependency pointers */
6690 free_arefs(act->dependencies);
6692 next = act->next;
6693 free(act);
6694 act = next;
6699 * Checks whether there is an action called action_name in actions list.
6700 * RETURNS: ptr to first matched action, else if not matched NULL.
6703 static ipqos_conf_action_t *
6704 actionexist(
6705 char *action_name,
6706 ipqos_conf_action_t *actions)
6709 IPQOSCDBG1(L1, "In actionexist: name: %s\n", action_name);
6711 while (actions) {
6712 if (strcmp(action_name, actions->name) == 0) {
6713 break;
6715 actions = actions->next;
6718 return (actions);
6721 /* **************************** act ref manip fns ******************** */
6725 * add an action reference element with parameter field and action
6726 * action_name to arefs.
6727 * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCES.
6729 static int
6730 add_aref(
6731 ipqos_conf_act_ref_t **arefs,
6732 char *field,
6733 char *action_name)
6736 ipqos_conf_act_ref_t *aref;
6738 IPQOSCDBG1(L1, "add_aref: action: %s.\n", action_name);
6740 /* allocate zero'd aref */
6742 aref = malloc(sizeof (ipqos_conf_act_ref_t));
6743 if (aref == NULL) {
6744 ipqos_msg(MT_ENOSTR, "malloc");
6745 return (IPQOS_CONF_ERR);
6747 (void) bzero(aref, sizeof (ipqos_conf_act_ref_t));
6749 /* copy parameter name if present */
6751 if (field)
6752 (void) strlcpy(aref->field, field, IPQOS_CONF_PNAME_LEN);
6754 /* copy action name */
6755 (void) strlcpy(aref->name, action_name, IPQOS_CONF_NAME_LEN);
6757 /* place at head of list */
6759 aref->next = *arefs;
6760 *arefs = aref;
6762 return (IPQOS_CONF_SUCCESS);
6766 * free all the memory used by the action references in arefs.
6768 static void
6769 free_arefs(
6770 ipqos_conf_act_ref_t *arefs)
6773 ipqos_conf_act_ref_t *aref = arefs;
6774 ipqos_conf_act_ref_t *next;
6776 while (aref) {
6777 nvlist_free(aref->nvlist);
6778 next = aref->next;
6779 free(aref);
6780 aref = next;
6786 /* *************************************************************** */
6791 * checks whether aname is a valid action name.
6792 * RETURNS: IPQOS_CONF_ERR if invalid, else IPQOS_CONF_SUCCESS.
6794 static int
6795 valid_aname(char *aname)
6799 * dissallow the use of the name of a virtual action, either
6800 * the ipqosconf name, or the longer ipp names.
6802 if (strcmp(aname, IPQOS_CONF_CONT_STR) == 0 ||
6803 strcmp(aname, IPQOS_CONF_DEFER_STR) == 0 ||
6804 strcmp(aname, IPQOS_CONF_DROP_STR) == 0 ||
6805 virtual_action(aname)) {
6806 ipqos_msg(MT_ERROR, gettext("Invalid action name line %u.\n"),
6807 lineno);
6808 return (IPQOS_CONF_ERR);
6811 return (IPQOS_CONF_SUCCESS);
6815 * Opens a stream to the types file for module module_name (assuming
6816 * that the file path is TYPES_FILE_DIR/module_name.types). if
6817 * a file open failure occurs, *openerr is set to 1.
6818 * RETURNS: NULL on error, else stream ptr to module types file.
6820 static FILE *
6821 validmod(
6822 char *module_name,
6823 int *openerr)
6826 FILE *fp;
6827 char *path;
6829 IPQOSCDBG1(L1, "In validmod: module_name: %s\n", module_name);
6831 *openerr = 0;
6833 /* create modules type file path */
6835 path = malloc(strlen(TYPES_FILE_DIR) + strlen(module_name) +
6836 strlen(".types") + 1);
6837 if (path == NULL) {
6838 ipqos_msg(MT_ENOSTR, "malloc");
6839 return (NULL);
6841 (void) strcpy(path, TYPES_FILE_DIR);
6842 (void) strcat(path, module_name);
6843 (void) strcat(path, ".types");
6846 IPQOSCDBG1(L1, "opening file %s\n", path);
6848 /* open stream to types file */
6850 fp = fopen(path, "r");
6851 if (fp == NULL) {
6852 (*openerr)++;
6855 free(path);
6856 return (fp);
6861 * read a class clause from cfp into a class struct and point class at this.
6862 * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCESS.
6864 static int
6865 readclass(
6866 FILE *cfp,
6867 char *module_name,
6868 ipqos_conf_class_t **class,
6869 char **perm_classes,
6870 int num_perm_classes)
6873 int nm, act;
6874 int res;
6875 nvpair_t *nvp;
6876 ipqos_nvtype_t type;
6877 char *name;
6878 char *action;
6879 int stats;
6881 IPQOSCDBG0(L0, "in readclass\n");
6883 /* create and zero class struct */
6885 *class = alloc_class();
6886 if (!*class) {
6887 return (IPQOS_CONF_ERR);
6889 (*class)->originator = IPP_CONFIG_IPQOSCONF;
6891 /* get starting line for error reporting */
6892 (*class)->lineno = lineno;
6894 /* read curl_begin */
6896 res = read_curl_begin(cfp);
6897 if (res != IPQOS_CONF_SUCCESS) {
6898 goto fail;
6901 /* loop reading parameters till read curl_end */
6903 stats = nm = act = 0;
6904 for (;;) {
6905 /* read nvpair */
6906 res = readnvpair(cfp, NULL, &(*class)->nvlist,
6907 &nvp, &type, PL_CLASS, module_name);
6908 if (res == IPQOS_CONF_ERR) {
6909 goto fail;
6911 /* reached end of class clause */
6912 } else if (res == IPQOS_CONF_CURL_END) {
6913 break;
6917 * catch name and action nv pairs and stats if present
6918 * and place values in class structure.
6921 /* name */
6923 if (nm == 0 &&
6924 strcmp(nvpair_name(nvp), IPQOS_CONF_NAME_STR) == 0) {
6926 (void) nvpair_value_string(nvp, &name);
6928 if (valid_name(name) != IPQOS_CONF_SUCCESS) {
6929 goto fail;
6931 (void) strcpy((*class)->name, name);
6932 nm++;
6934 /* next action */
6936 } else if (act == 0 &&
6937 strcmp(nvpair_name(nvp), IPQOS_CONF_NEXT_ACTION_STR) == 0) {
6939 (void) nvpair_value_string(nvp, &action);
6942 * if next action string continue string set action to
6943 * IPP_ANAME_CONT, else if drop string IPP_ANAME_DROP
6945 if (strcmp(action, IPQOS_CONF_CONT_STR) == 0) {
6946 action = IPP_ANAME_CONT;
6947 } else if (strcmp(action, IPQOS_CONF_DROP_STR) == 0) {
6948 action = IPP_ANAME_DROP;
6951 /* add an action reference to action list */
6953 res = add_aref(&(*class)->alist,
6954 IPQOS_CONF_NEXT_ACTION_STR, action);
6955 if (res != IPQOS_CONF_SUCCESS) {
6956 goto fail;
6958 act++;
6960 /* class stats enable */
6962 } else if (stats == 0 &&
6963 strcmp(nvpair_name(nvp), IPQOS_CONF_STATS_ENABLE_STR) ==
6964 0) {
6965 boolean_t bl;
6967 (void) nvpair_value_uint32(nvp, (uint32_t *)&bl);
6968 (*class)->stats_enable = bl;
6970 stats++;
6972 /* no other / duplicate parameters allowed */
6974 } else {
6975 ipqos_msg(MT_ERROR,
6976 gettext("Unexpected parameter line %u.\n"), lineno);
6977 goto fail;
6980 if (nm == 0 || act == 0) {
6981 ipqos_msg(MT_ERROR,
6982 gettext("Missing class name/next action before line %u.\n"),
6983 lineno);
6984 goto fail;
6987 /* change class originator field to permanent if permanent class */
6989 if (in_string_table(perm_classes, num_perm_classes, (*class)->name)) {
6990 IPQOSCDBG1(L0, "Setting class %s as permanent.\n", (*class)->name);
6991 (*class)->originator = IPP_CONFIG_PERMANENT;
6994 return (IPQOS_CONF_SUCCESS);
6995 fail:
6996 if (*class)
6997 free_class(*class);
6998 return (IPQOS_CONF_ERR);
7002 * This function assumes either src_nd_name or dst_node_nm are set in filter.
7004 * Creates one of more copies of filter according to the ip versions
7005 * requested (or assumed) and the resolution of the src and dst address
7006 * node names if spec'd. If both node names are spec'd then a filter is
7007 * created for each pair of addresses (one from each node name) that is
7008 * compatible with the chosen address family, otherwise a filter copy is
7009 * created for just each address of the single node name that is
7010 * compatible.
7011 * If filter->ip_versions has been set that is used to determine the
7012 * af's we will create filters for, else if a numeric address was
7013 * added the family of that will be used, otherwise we fall back
7014 * to both v4 and v6 addresses.
7016 * Any name lookup failures that occur are checked to see whether the failure
7017 * was a soft or hard failure and the nlerr field of filter set accordingly
7018 * before the error is returned.
7020 * RETURNS: IPQOS_CONF_ERR on any error, else IPQOS_CONF_SUCCESS.
7023 static int
7024 domultihome(
7025 ipqos_conf_filter_t *filter,
7026 ipqos_conf_filter_t **flist,
7027 boolean_t last_retry)
7030 uint32_t ftype;
7031 int v4 = 1, v6 = 1; /* default lookup family is v4 and v6 */
7032 int saf, daf;
7033 struct hostent *shp = NULL;
7034 struct hostent *dhp = NULL;
7035 in6_addr_t daddr, saddr;
7036 int idx = 0;
7037 ipqos_conf_filter_t *nfilter;
7038 int res;
7039 int ernum;
7040 int in32b = 0;
7041 char **sp, **dp;
7043 IPQOSCDBG3(MHME, "In domultihome: filter: %s, src_node: %s, "
7044 "dst_node: %s\n", filter->name,
7045 (filter->src_nd_name ? filter->src_nd_name : "NULL"),
7046 (filter->dst_nd_name ? filter->dst_nd_name : "NULL"));
7048 /* check if we've read an ip_version request to get the versions */
7050 if (filter->ip_versions != 0) {
7051 v4 = VERSION_IS_V4(filter);
7052 v6 = VERSION_IS_V6(filter);
7054 /* otherwise check if we've read a numeric address and get versions */
7056 } else if (nvlist_lookup_uint32(filter->nvlist, IPGPC_FILTER_TYPE,
7057 &ftype) == 0) {
7058 if (ftype == IPGPC_V4_FLTR) {
7059 v6--;
7060 } else {
7061 v4--;
7065 /* read saddrs if src node name */
7067 if (filter->src_nd_name) {
7069 /* v4 only address */
7071 if (v4 && !v6) {
7072 in32b++;
7073 shp = getipnodebyname(filter->src_nd_name, AF_INET,
7074 AI_ADDRCONFIG, &ernum);
7076 /* v6 only */
7078 } else if (v6 && !v4) {
7079 shp = getipnodebyname(filter->src_nd_name, AF_INET6,
7080 AI_DEFAULT, &ernum);
7082 /* v4 and v6 */
7084 } else if (v6 && v4) {
7085 shp = getipnodebyname(filter->src_nd_name, AF_INET6,
7086 AI_DEFAULT|AI_ALL, &ernum);
7089 #ifdef TESTING_RETRY
7090 if (!last_retry) {
7091 filter->nlerr = IPQOS_LOOKUP_RETRY;
7092 goto fail;
7094 #endif
7097 * if lookup error determine whether it was a soft or hard
7098 * failure and mark as such in filter.
7100 if (shp == NULL) {
7101 if (ernum != TRY_AGAIN) {
7102 ipqos_msg(MT_ERROR, gettext("Failed to "
7103 "resolve src host name for filter at "
7104 "line %u, ignoring filter.\n"),
7105 filter->lineno);
7106 filter->nlerr = IPQOS_LOOKUP_FAIL;
7107 } else {
7108 if (last_retry) {
7109 ipqos_msg(MT_ERROR, gettext("Failed "
7110 "to resolve src host name for "
7111 "filter at line %u, ignoring "
7112 "filter.\n"), filter->lineno);
7114 filter->nlerr = IPQOS_LOOKUP_RETRY;
7116 goto fail;
7120 /* read daddrs if dst node name */
7121 if (filter->dst_nd_name) {
7123 /* v4 only address */
7125 if (v4 && !v6) {
7126 in32b++;
7127 dhp = getipnodebyname(filter->dst_nd_name, AF_INET,
7128 AI_ADDRCONFIG, &ernum);
7130 /* v6 only */
7132 } else if (v6 && !v4) {
7133 dhp = getipnodebyname(filter->dst_nd_name, AF_INET6,
7134 AI_DEFAULT, &ernum);
7136 /* v6 and v4 addresses */
7138 } else {
7139 dhp = getipnodebyname(filter->dst_nd_name, AF_INET6,
7140 AI_DEFAULT|AI_ALL, &ernum);
7143 if (dhp == NULL) {
7144 if (ernum != TRY_AGAIN) {
7145 ipqos_msg(MT_ERROR, gettext("Failed to "
7146 "resolve dst host name for filter at "
7147 "line %u, ignoring filter.\n"),
7148 filter->lineno);
7149 filter->nlerr = IPQOS_LOOKUP_FAIL;
7150 } else {
7151 if (last_retry) {
7152 ipqos_msg(MT_ERROR, gettext("Failed "
7153 "to resolve dst host name for "
7154 "filter at line %u, ignoring "
7155 "filter.\n"), filter->lineno);
7157 filter->nlerr = IPQOS_LOOKUP_RETRY;
7159 goto fail;
7164 * if src and dst node name, create set of filters; one for each
7165 * src and dst address of matching types.
7167 if (filter->src_nd_name && filter->dst_nd_name) {
7169 for (sp = shp->h_addr_list; *sp != NULL; sp++) {
7170 (void) bcopy(*sp, &saddr, shp->h_length);
7172 /* get saddr family */
7174 if (in32b || IN6_IS_ADDR_V4MAPPED(&saddr)) {
7175 saf = AF_INET;
7176 } else {
7177 saf = AF_INET6;
7180 for (dp = dhp->h_addr_list; *dp != NULL; dp++) {
7181 (void) bcopy(*dp, &daddr, dhp->h_length);
7183 /* get daddr family */
7185 if (in32b || IN6_IS_ADDR_V4MAPPED(&daddr)) {
7186 daf = AF_INET;
7187 } else {
7188 daf = AF_INET6;
7192 * if saddr and daddr same af duplicate
7193 * filter adding addresses and new instance
7194 * number and add to flist filter list.
7197 if (daf == saf) {
7199 res = dup_filter(filter, &nfilter, saf,
7200 !in32b, &saddr, &daddr, ++idx);
7201 if (res != IPQOS_CONF_SUCCESS) {
7202 goto fail;
7204 ADD_TO_LIST(flist, nfilter);
7209 /* if src name only create set of filters, one for each node address */
7211 } else if (filter->src_nd_name) {
7213 for (sp = shp->h_addr_list; *sp != NULL; sp++) {
7214 (void) bcopy(*sp, &saddr, shp->h_length);
7216 /* get af */
7218 if (in32b || IN6_IS_ADDR_V4MAPPED(&saddr)) {
7219 saf = AF_INET;
7220 } else {
7221 saf = AF_INET6;
7226 * dup filter adding saddr and new instance num and
7227 * add to flist filter list.
7229 res = dup_filter(filter, &nfilter, saf, !in32b, &saddr,
7230 NULL, ++idx);
7231 if (res != IPQOS_CONF_SUCCESS) {
7232 goto fail;
7235 ADD_TO_LIST(flist, nfilter);
7239 /* if dname only create set of filters, one for each node address */
7241 } else {
7242 for (dp = dhp->h_addr_list; *dp != NULL; dp++) {
7243 (void) bcopy(*dp, &daddr, dhp->h_length);
7245 /* get af */
7247 if (in32b || IN6_IS_ADDR_V4MAPPED(&daddr)) {
7248 daf = AF_INET;
7249 } else {
7250 daf = AF_INET6;
7254 * dup filter adding daddr and new instance num and
7255 * add to flist filter list.
7257 res = dup_filter(filter, &nfilter, daf, !in32b, NULL,
7258 &daddr, ++idx);
7259 if (res != IPQOS_CONF_SUCCESS) {
7260 goto fail;
7263 ADD_TO_LIST(flist, nfilter);
7267 if (shp)
7268 freehostent(shp);
7269 if (dhp)
7270 freehostent(dhp);
7271 return (IPQOS_CONF_SUCCESS);
7272 fail:
7274 * should really clean up any filters that we have created,
7275 * however, free_actions called from readaction will cleam them up.
7277 if (shp)
7278 freehostent(shp);
7279 if (dhp)
7280 freehostent(dhp);
7281 return (IPQOS_CONF_ERR);
7286 * read a filter clause from cfp into a filter struct and point filter
7287 * at this.
7288 * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCES.
7290 static int
7291 readfilter(
7292 FILE *cfp,
7293 FILE *tfp,
7294 char *module_name,
7295 ipqos_conf_filter_t **filter,
7296 char **perm_filters,
7297 int num_perm_filters)
7300 int res;
7301 int nm, cls, ipv;
7302 in6_addr_t mask;
7303 char *addr_str;
7304 char *sl = NULL;
7305 in6_addr_t addr;
7306 int sa;
7307 struct hostent *hp;
7308 int err_num;
7309 int v4 = 0, v6 = 0;
7310 uchar_t mlen;
7311 char *tmp;
7312 nvpair_t *nvp;
7313 ipqos_nvtype_t type;
7314 char *name;
7315 char *class;
7316 uchar_t b;
7317 in6_addr_t v6addr;
7319 IPQOSCDBG0(L0, "in readfilter\n");
7322 /* create and zero filter struct */
7324 *filter = alloc_filter();
7325 if (*filter == NULL) {
7326 return (IPQOS_CONF_ERR);
7328 (*filter)->originator = IPP_CONFIG_IPQOSCONF;
7330 /* get starting line for error reporting */
7331 (*filter)->lineno = lineno;
7333 /* read beginning curl */
7335 res = read_curl_begin(cfp);
7336 if (res != IPQOS_CONF_SUCCESS) {
7337 goto fail;
7342 * loop reading nvpairs onto nvlist until encounter CURL_END
7344 ipv = nm = cls = 0;
7345 for (;;) {
7346 /* read nvpair */
7348 res = readnvpair(cfp, tfp, &(*filter)->nvlist,
7349 &nvp, &type, PL_FILTER, module_name);
7350 if (res == IPQOS_CONF_ERR) {
7351 goto fail;
7353 /* reached the end of filter definition */
7355 } else if (res == IPQOS_CONF_CURL_END) {
7356 break;
7360 * catch name and class and place value into filter
7361 * structure.
7364 /* read filter name */
7366 if (strcmp(nvpair_name(nvp), IPQOS_CONF_NAME_STR) == 0) {
7367 if (nm != 0) {
7368 ipqos_msg(MT_ERROR,
7369 gettext("Duplicate parameter line %u.\n"),
7370 lineno);
7371 goto fail;
7374 (void) nvpair_value_string(nvp, &name);
7375 if (valid_name(name) != IPQOS_CONF_SUCCESS) {
7376 goto fail;
7379 (void) strcpy((*filter)->name, name);
7380 (void) nvlist_remove_all((*filter)->nvlist,
7381 IPQOS_CONF_NAME_STR);
7382 nm++;
7384 /* read class name */
7386 } else if (strcmp(nvpair_name(nvp), IPQOS_CONF_CLASS_STR) ==
7387 0) {
7388 if (cls != 0) {
7389 ipqos_msg(MT_ERROR,
7390 gettext("Duplicate parameter line %u.\n"),
7391 lineno);
7392 goto fail;
7395 if (nvpair_value_string(nvp, &class) != 0) {
7396 ipqos_msg(MT_ENOSTR, "nvpair_value_string");
7397 break;
7399 if (valid_name(class) != IPQOS_CONF_SUCCESS) {
7400 goto fail;
7402 (void) strcpy((*filter)->class_name, class);
7403 (void) nvlist_remove_all((*filter)->nvlist,
7404 IPQOS_CONF_CLASS_STR);
7405 cls++;
7408 * if a src or dst ip node name/address. For those that
7409 * are determined to be addresses we convert them from
7410 * strings here and add to the filter nvlist; for node names
7411 * we add the name to the filter struct for readaction to
7412 * process.
7414 } else if (strcmp(nvpair_name(nvp), IPGPC_SADDR) == 0 ||
7415 strcmp(nvpair_name(nvp), IPGPC_DADDR) == 0) {
7417 sa = 0;
7419 if (strcmp(nvpair_name(nvp), IPGPC_SADDR) == 0) {
7420 sa++;
7423 (void) nvpair_value_string(nvp, &addr_str);
7426 * get the address mask if present.
7427 * make a copy so that the nvlist element that
7428 * it is part of doesn't dissapear and causes probs.
7430 sl = strchr(addr_str, '/');
7431 if (sl) {
7432 *sl = '\0';
7433 tmp = malloc(strlen(++sl) + 1);
7434 if (tmp == NULL) {
7435 ipqos_msg(MT_ENOSTR, "malloc");
7436 goto fail;
7438 (void) strcpy(tmp, sl);
7439 sl = tmp;
7443 /* if a numeric address */
7445 if (inet_pton(AF_INET, addr_str, &addr) == 1 ||
7446 inet_pton(AF_INET6, addr_str, &addr) == 1) {
7448 /* get address */
7450 hp = getipnodebyname(addr_str, AF_INET6,
7451 AI_DEFAULT, &err_num);
7452 if (hp == NULL) {
7453 ipqos_msg(MT_ENOSTR,
7454 "getipnodebyname");
7455 goto fail;
7458 (void) bcopy(hp->h_addr_list[0], &v6addr,
7459 hp->h_length);
7460 freehostent(hp);
7462 /* determine address type */
7464 v4 = IN6_IS_ADDR_V4MAPPED(&v6addr);
7465 if (!v4) {
7466 v6++;
7470 * check any previous addresses have same
7471 * version.
7473 if (nvlist_lookup_byte((*filter)->nvlist,
7474 IPGPC_FILTER_TYPE, &b) == 0) {
7475 if (v4 && b != IPGPC_V4_FLTR ||
7476 v6 && b != IPGPC_V6_FLTR) {
7477 ipqos_msg(MT_ERROR,
7478 gettext("Incompatible "
7479 "address version line "
7480 "%u.\n"), lineno);
7481 goto fail;
7486 * check that if ip_version spec'd it
7487 * corresponds.
7489 if ((*filter)->ip_versions != 0) {
7490 if (v4 && !VERSION_IS_V4(*filter) ||
7491 v6 && !VERSION_IS_V6(*filter)) {
7492 ipqos_msg(MT_ERROR,
7493 gettext("Incompatible "
7494 "address version line %u"
7495 ".\n"), lineno);
7496 goto fail;
7500 /* add the address type */
7502 res = nvlist_add_byte(
7503 (*filter)->nvlist, IPGPC_FILTER_TYPE,
7504 v4 ? IPGPC_V4_FLTR : IPGPC_V6_FLTR);
7505 if (res != 0) {
7506 ipqos_msg(MT_ENOSTR,
7507 "nvlist_add_byte");
7508 goto fail;
7511 /* add address to list */
7513 res = nvlist_add_uint32_array((*filter)->nvlist,
7514 sa ? IPGPC_SADDR : IPGPC_DADDR,
7515 (uint32_t *)&v6addr, 4);
7516 if (res != 0) {
7517 ipqos_msg(MT_ENOSTR,
7518 "nvlist_add_uint32_array");
7519 goto fail;
7524 * add mask entry in list.
7527 if (sl) { /* have CIDR mask */
7528 char *lo;
7529 res = readuint8(sl, &mlen, &lo);
7530 if (res != IPQOS_CONF_SUCCESS ||
7531 v4 && mlen > 32 ||
7532 !v4 && mlen > 128 ||
7533 mlen == 0) {
7534 ipqos_msg(MT_ERROR,
7535 gettext("Invalid CIDR "
7536 "mask line %u.\n"), lineno);
7537 goto fail;
7539 setmask(mlen, &mask,
7540 v4 ? AF_INET : AF_INET6);
7541 free(sl);
7542 } else {
7543 /* no CIDR mask spec'd - use all 1s */
7545 (void) memset(&mask, ~0,
7546 sizeof (in6_addr_t));
7548 res = nvlist_add_uint32_array((*filter)->nvlist,
7549 sa ? IPGPC_SADDR_MASK : IPGPC_DADDR_MASK,
7550 (uint32_t *)&mask, 4);
7551 if (res != 0) {
7552 ipqos_msg(MT_ENOSTR,
7553 "nvlist_add_uint32_arr");
7554 goto fail;
7557 /* inet_pton returns fail - we assume a node name */
7559 } else {
7561 * doesn't make sense to have a mask
7562 * with a node name.
7564 if (sl) {
7565 ipqos_msg(MT_ERROR,
7566 gettext("Address masks aren't "
7567 "allowed for host names line "
7568 "%u.\n"), lineno);
7569 goto fail;
7573 * store node name in filter struct for
7574 * later resolution.
7576 if (sa) {
7577 (*filter)->src_nd_name =
7578 malloc(strlen(addr_str) + 1);
7579 (void) strcpy((*filter)->src_nd_name,
7580 addr_str);
7581 } else {
7582 (*filter)->dst_nd_name =
7583 malloc(strlen(addr_str) + 1);
7584 (void) strcpy((*filter)->dst_nd_name,
7585 addr_str);
7589 /* ip_version enumeration */
7591 } else if (strcmp(nvpair_name(nvp), IPQOS_CONF_IP_VERSION) ==
7592 0) {
7593 /* check we haven't read ip_version before */
7594 if (ipv) {
7595 ipqos_msg(MT_ERROR,
7596 gettext("Duplicate parameter line %u.\n"),
7597 lineno);
7598 goto fail;
7600 ipv++;
7602 /* get bitmask value */
7604 (void) nvpair_value_uint32(nvp,
7605 &(*filter)->ip_versions);
7608 * check that if either ip address is spec'd it
7609 * corresponds.
7611 if (v4 && !VERSION_IS_V4(*filter) ||
7612 v6 && !VERSION_IS_V6(*filter)) {
7613 ipqos_msg(MT_ERROR, gettext("Incompatible "
7614 "address version line %u.\n"), lineno);
7615 goto fail;
7618 /* remove ip_version from nvlist */
7620 (void) nvlist_remove_all((*filter)->nvlist,
7621 IPQOS_CONF_IP_VERSION);
7624 if (nm == 0 || cls == 0) {
7625 ipqos_msg(MT_ERROR, gettext("Missing filter/class name "
7626 "before line %u.\n"), lineno);
7627 goto fail;
7630 if (in_string_table(perm_filters, num_perm_filters, (*filter)->name)) {
7631 IPQOSCDBG1(L0, "Setting filter %s as permanent.\n",
7632 (*filter)->name);
7634 (*filter)->originator = IPP_CONFIG_PERMANENT;
7637 return (IPQOS_CONF_SUCCESS);
7638 fail:
7639 if (*filter)
7640 free_filter(*filter);
7641 if (hp)
7642 freehostent(hp);
7643 free(sl);
7645 return (IPQOS_CONF_ERR);
7649 * reads the curl begin token from cfp stream.
7650 * RETURNS: IPQOS_CONF_ERR if not read successfully, else IPQOS_CONF_SUCCES.
7652 static int
7653 read_curl_begin(FILE *cfp)
7656 int res;
7657 char *st;
7659 res = readtoken(cfp, &st);
7661 if (res != IPQOS_CONF_CURL_BEGIN) {
7662 if (res == IPQOS_CONF_EOF) {
7663 ipqos_msg(MT_ERROR, gettext("Unexpected EOF.\n"));
7665 /* if CURL_END or something else */
7666 } else if (res != IPQOS_CONF_ERR) {
7667 free(st);
7668 ipqos_msg(MT_ERROR, gettext("\'{\' missing at line "
7669 "%u.\n"), lineno);
7671 return (IPQOS_CONF_ERR);
7674 free(st);
7675 return (IPQOS_CONF_SUCCESS);
7679 * This function parses the parameter string version into a version of the
7680 * form "%u.%u" (as a sscanf format string). It then encodes this into an
7681 * int and returns this encoding.
7682 * RETURNS: -1 if an invalid string, else the integer encoding.
7684 static int
7685 ver_str_to_int(
7686 char *version)
7688 uint32_t major, minor;
7689 int ver;
7691 if (sscanf(version, "%u.%u", &major, &minor) != 2) {
7692 IPQOSCDBG0(L0, "Failed to process version number string\n");
7693 return (-1);
7696 ver = (int)((major * 10000) + minor);
7697 return (ver);
7701 * This function scans through the stream fp line by line looking for
7702 * a line beginning with version_tag and returns a integer encoding of
7703 * the version following it.
7705 * RETURNS: If the version definition isn't found or the version is not
7706 * a valid version (%u.%u) then -1 is returned, else an integer encoding
7707 * of the read version.
7709 static int
7710 read_tfile_ver(
7711 FILE *fp,
7712 char *version_tag,
7713 char *module_name)
7715 char lbuf[IPQOS_CONF_LINEBUF_SZ];
7716 char buf[IPQOS_CONF_LINEBUF_SZ+1];
7717 char buf2[IPQOS_CONF_LINEBUF_SZ+1];
7718 int found = 0;
7719 int version;
7722 * reset to file start
7724 if (fseek(fp, 0, SEEK_SET) != 0) {
7725 ipqos_msg(MT_ENOSTR, "fseek");
7726 return (-1);
7730 * loop reading lines till found the one beginning with version_tag.
7732 while (fgets(lbuf, IPQOS_CONF_LINEBUF_SZ, fp) != NULL) {
7733 if ((sscanf(lbuf,
7734 "%" VAL2STR(IPQOS_CONF_LINEBUF_SZ) "s"
7735 "%" VAL2STR(IPQOS_CONF_LINEBUF_SZ) "s",
7736 buf, buf2) == 2) &&
7737 (strcmp(buf, version_tag) == 0)) {
7738 found++;
7739 break;
7742 if (found == 0) {
7743 ipqos_msg(MT_ERROR, gettext("Types file for module %s is "
7744 "corrupt.\n"), module_name);
7745 IPQOSCDBG1(L1, "Couldn't find %s in types file\n",
7746 version_tag);
7747 return (-1);
7751 * convert version string into int.
7753 if ((version = ver_str_to_int(buf2)) == -1) {
7754 ipqos_msg(MT_ERROR, gettext("Types file for module %s is "
7755 "corrupt.\n"), module_name);
7756 return (-1);
7759 return (version);
7763 * read action clause and params/classes/filters clauses within and
7764 * store in and hang off an action structure, and point action at it.
7765 * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCES.
7767 static int
7768 readaction(
7769 FILE *cfp,
7770 ipqos_conf_action_t **action)
7773 char *st;
7774 FILE *tfp = NULL;
7775 int nm, md;
7776 int readprms = 0;
7777 int res;
7778 char *strval;
7779 char *name;
7780 nvpair_t *nvp;
7781 ipqos_nvtype_t type;
7782 ipqos_conf_filter_t *filter;
7783 ipqos_conf_class_t *class;
7784 int oe;
7785 char **perm_filters;
7786 int num_perm_filters;
7787 int tf_fmt_ver;
7789 IPQOSCDBG0(L0, "in readaction\n");
7791 res = readtoken(cfp, &st);
7792 if (res == IPQOS_CONF_ERR || res == IPQOS_CONF_EOF) {
7793 return (res);
7794 } else if (strcmp(st, IPQOS_CONF_ACTION_STR) != 0) {
7795 ipqos_msg(MT_ERROR, gettext("Missing %s token line "
7796 "%u.\n"), IPQOS_CONF_ACTION_STR, lineno);
7797 free(st);
7798 return (IPQOS_CONF_ERR);
7800 free(st);
7802 /* create action structure */
7804 *action = alloc_action();
7805 if (*action == NULL) {
7806 return (IPQOS_CONF_ERR);
7808 (*action)->params->originator = IPP_CONFIG_IPQOSCONF;
7811 /* get starting line for error reporting */
7812 (*action)->lineno = lineno;
7814 /* read beginning curl */
7816 res = read_curl_begin(cfp);
7817 if (res != IPQOS_CONF_SUCCESS) {
7818 goto fail;
7821 /* loop till read both action name and module */
7823 nm = md = 0;
7824 do {
7825 /* read nvpair */
7827 res = readnvpair(cfp, NULL, &(*action)->nvlist, &nvp, &type,
7828 PL_ACTION, NULL);
7829 if (res == IPQOS_CONF_ERR) {
7830 goto fail;
7832 /* read curl_end */
7834 } else if (res == IPQOS_CONF_CURL_END) {
7835 if (nm == 0 || md == 0) {
7836 ipqos_msg(MT_ERROR,
7837 gettext("Missing action name/ module "
7838 "before line %u.\n"), lineno);
7839 goto fail;
7844 /* store name and module in action structure */
7846 name = nvpair_name(nvp);
7848 /* read action name */
7850 if (nm == 0 && strcmp(name, IPQOS_CONF_NAME_STR) == 0) {
7852 (void) nvpair_value_string(nvp, &strval);
7854 /* check name is valid */
7856 if (valid_name(strval) != IPQOS_CONF_SUCCESS ||
7857 valid_aname(strval) != IPQOS_CONF_SUCCESS) {
7858 goto fail;
7861 /* store and remove from list */
7863 (void) strcpy((*action)->name, strval);
7864 /* remove name from nvlist */
7865 (void) nvlist_remove_all((*action)->nvlist,
7866 IPQOS_CONF_NAME_STR);
7868 nm++;
7870 /* read module name */
7872 } else if (md == 0 &&
7873 strcmp(name, IPQOS_CONF_MODULE_STR) == 0) {
7875 * check that module has a type file and get
7876 * open stream to it.
7878 (void) nvpair_value_string(nvp, &strval);
7879 if ((tfp = validmod(strval, &oe)) == NULL) {
7880 if (oe) {
7881 if (errno == ENOENT) {
7882 ipqos_msg(MT_ERROR,
7883 gettext("Invalid "
7884 "module name line %u.\n"),
7885 lineno);
7886 } else {
7887 ipqos_msg(MT_ENOSTR, "fopen");
7890 goto fail;
7894 * move module name to action struct
7896 (void) strlcpy((*action)->module, strval,
7897 IPQOS_CONF_NAME_LEN);
7898 (void) nvlist_remove_all((*action)->nvlist,
7899 IPQOS_CONF_MODULE_STR);
7900 md++;
7902 /* duplicate/other parameter */
7904 } else {
7905 ipqos_msg(MT_ERROR,
7906 gettext("Unexpected parameter line %u.\n"),
7907 lineno);
7908 goto fail;
7911 } while (nm == 0 || md == 0);
7914 * check that if the ipgpc action it is named correctly
7916 if ((strcmp((*action)->module, IPGPC_NAME) == 0) &&
7917 (strcmp((*action)->name, IPGPC_CLASSIFY) != 0)) {
7918 ipqos_msg(MT_ERROR,
7919 gettext("%s action has incorrect name line %u.\n"),
7920 IPGPC_NAME, (*action)->lineno);
7921 goto fail;
7924 /* get list of permanent classes */
7926 res = read_perm_items(0, tfp, (*action)->module,
7927 &(*action)->perm_classes, &(*action)->num_perm_classes);
7928 if (res != IPQOS_CONF_SUCCESS) {
7929 goto fail;
7932 /* get list of permanent filters */
7934 res = read_perm_items(1, tfp, (*action)->module,
7935 &perm_filters, &num_perm_filters);
7936 if (res != IPQOS_CONF_SUCCESS) {
7937 goto fail;
7941 * get types file format version and check its supported.
7943 if ((tf_fmt_ver = read_tfile_ver(tfp, IPQOS_FMT_STR,
7944 (*action)->module)) == -1)
7945 goto fail;
7946 if (IPP_MAJOR_MODULE_VER(tf_fmt_ver) > 1 ||
7947 IPP_MINOR_MODULE_VER(tf_fmt_ver) > 0) {
7948 ipqos_msg(MT_ERROR, gettext("Types file for module %s is "
7949 "incompatible.\n"), (*action)->module);
7950 IPQOSCDBG0(L1, "Unsupported fmt major/minor version\n");
7951 goto fail;
7955 * get module version
7957 if (((*action)->module_version = read_tfile_ver(tfp, IPQOS_MOD_STR,
7958 (*action)->module)) == -1)
7959 goto fail;
7961 /* read filter/class/params blocks until CURL_END */
7963 for (;;) {
7964 /* read token */
7965 res = readtoken(cfp, &st);
7967 if (res == IPQOS_CONF_ERR) {
7968 goto fail;
7969 } else if (res == IPQOS_CONF_EOF) {
7970 ipqos_msg(MT_ERROR, gettext("Unexpected EOF.\n"));
7971 goto fail;
7973 /* read CURL_END - end of action definition */
7975 } else if (res == IPQOS_CONF_CURL_END) {
7976 free(st);
7977 break;
7982 * read in either a filter/class or parameter block.
7985 /* read filter */
7987 if (strcmp(st, IPQOS_CONF_FILTER_STR) == 0) {
7988 free(st);
7990 res = readfilter(cfp, tfp, (*action)->module, &filter,
7991 perm_filters, num_perm_filters);
7992 if (res != IPQOS_CONF_SUCCESS) {
7993 goto fail;
7997 * if we read a host name for either src or dst addr
7998 * resolve the hostnames and create the appropriate
7999 * number of filters.
8002 if (filter->src_nd_name || filter->dst_nd_name) {
8004 res = domultihome(filter, &(*action)->filters,
8005 B_FALSE);
8007 * if a lookup fails and the filters
8008 * marked as retry we add it to a list
8009 * for another attempt later, otherwise
8010 * it is thrown away.
8012 if (res != IPQOS_CONF_SUCCESS) {
8014 /* if not name lookup problem */
8016 if (filter->nlerr == 0) {
8017 free_filter(filter);
8018 goto fail;
8020 /* name lookup problem */
8023 * if intermitent lookup failure
8024 * add to list of filters to
8025 * retry later.
8027 } else if (filter->nlerr ==
8028 IPQOS_LOOKUP_RETRY) {
8029 filter->nlerr = 0;
8030 ADD_TO_LIST(
8031 &(*action)->retry_filters,
8032 filter);
8034 * for non-existing names
8035 * ignore the filter.
8037 } else {
8038 free_filter(filter);
8041 /* creation of new filters successful */
8043 } else {
8044 free_filter(filter);
8047 /* non-node name filter */
8049 } else {
8050 ADD_TO_LIST(&(*action)->filters, filter);
8053 /* read class */
8055 } else if (strcmp(st, IPQOS_CONF_CLASS_STR) == 0) {
8056 free(st);
8057 res = readclass(cfp, (*action)->module, &class,
8058 (*action)->perm_classes,
8059 (*action)->num_perm_classes);
8060 if (res != IPQOS_CONF_SUCCESS) {
8061 goto fail;
8064 ADD_TO_LIST(&(*action)->classes, class);
8066 /* read params */
8068 } else if (strcmp(st, IPQOS_CONF_PARAMS_STR) == 0) {
8069 free(st);
8070 if (readprms) {
8071 ipqos_msg(MT_ERROR,
8072 gettext("Second parameter clause not "
8073 "supported line %u.\n"), lineno);
8074 goto fail;
8076 res = readparams(cfp, tfp, (*action)->module,
8077 (*action)->params);
8078 if (res != IPQOS_CONF_SUCCESS) {
8079 goto fail;
8081 readprms++;
8083 /* something unexpected */
8084 } else {
8085 free(st);
8086 ipqos_msg(MT_ERROR,
8087 gettext("Params/filter/class clause expected "
8088 "line %u.\n"), lineno);
8089 goto fail;
8093 (void) fclose(tfp);
8094 return (IPQOS_CONF_SUCCESS);
8096 fail:
8097 if (tfp)
8098 (void) fclose(tfp);
8099 if (*action) {
8100 free_actions(*action);
8101 *action = NULL;
8103 return (IPQOS_CONF_ERR);
8107 * check that each of the actions in actions is uniquely named. If one isn't
8108 * set *name to point at the name of the duplicate action.
8109 * RETURNS: IPQOS_CONF_ERR if a non-unique action, else IPQOS_CONF_SUCCESS.
8111 static int
8112 actions_unique(ipqos_conf_action_t *actions, char **name)
8115 IPQOSCDBG0(L1, "In actions_unique.\n");
8117 while (actions) {
8118 if (actionexist(actions->name, actions->next)) {
8119 *name = actions->name;
8120 return (IPQOS_CONF_ERR);
8122 actions = actions->next;
8125 return (IPQOS_CONF_SUCCESS);
8129 * checks whether the action parameter is involved in an action cycle.
8130 * RETURNS: 1 if involved in a cycle, 0 otherwise.
8132 static int
8133 in_cycle(
8134 ipqos_conf_action_t *action)
8137 ipqos_conf_act_ref_t *aref;
8138 ipqos_conf_class_t *c;
8140 IPQOSCDBG1(L0, "in_cycle: visiting action %s\n", action->name);
8143 /* have we visited this action before? */
8145 if (action->visited == INCYCLE_VISITED) {
8146 action->visited = 0;
8147 return (1);
8149 action->visited = INCYCLE_VISITED;
8152 * recurse down the child actions of this action through the
8153 * classes next action and parameter actions.
8156 for (aref = action->params->actions; aref != NULL; aref = aref->next) {
8158 /* skip virtual actions - they can't be in a cycle */
8160 if (virtual_action(aref->name)) {
8161 continue;
8164 if (in_cycle(aref->action)) {
8165 action->visited = 0;
8166 return (1);
8170 for (c = action->classes; c != NULL; c = c->next) {
8171 aref = c->alist;
8173 if (virtual_action(aref->name)) {
8174 continue;
8177 if (in_cycle(aref->action)) {
8178 action->visited = 0;
8179 return (1);
8183 IPQOSCDBG0(L0, "in_cycle: return\n");
8184 action->visited = 0;
8185 return (0);
8189 * checks that the configuration in actions is a valid whole, that
8190 * all actions are unique, all filters and classes are unique within
8191 * their action, that classes referenced by filters exist and actions
8192 * referenced by classes and params exist. Also checks that there are no root
8193 * actions but ipgpc and that no actions are involved in cycles. As
8194 * a consequence of checking that the actions exist two way pointers
8195 * are created between the dependee and dependant actions.
8197 * In the case the the userconf flag is zero only this link creation is
8198 * set as we trust the kernel to return a valid configuration.
8200 * RETURNS: IPQOS_CONF_ERR if config isn't valid, else IPQOS_CONF_SUCCESS.
8204 static int
8205 validconf(
8206 ipqos_conf_action_t *actions,
8207 int userconf) /* are we checking a conf file ? */
8209 char *name;
8210 ipqos_conf_action_t *act;
8211 int res;
8212 ipqos_conf_action_t *dact;
8213 ipqos_conf_filter_t *flt;
8214 ipqos_conf_class_t *cls;
8215 ipqos_conf_params_t *params;
8216 ipqos_conf_act_ref_t *aref;
8218 IPQOSCDBG0(L0, "In validconf\n");
8220 /* check actions are unique */
8222 if (userconf && actions_unique(actions, &name) != IPQOS_CONF_SUCCESS) {
8223 ipqos_msg(MT_ERROR, gettext("Duplicate named action %s.\n"),
8224 name);
8225 return (IPQOS_CONF_ERR);
8228 for (act = actions; act; act = act->next) {
8231 * check filters (for user land configs only).
8232 * check they are unique in this action and their class exists.
8234 if (userconf) {
8235 for (flt = act->filters; flt; flt = flt->next) {
8237 /* check unique name */
8239 if (filterexist(flt->name, flt->instance,
8240 flt->next)) {
8241 ipqos_msg(MT_ERROR,
8242 gettext("Duplicate named filter "
8243 "%s in action %s.\n"), flt->name,
8244 act->name);
8245 return (IPQOS_CONF_ERR);
8249 * check existence of class and error if
8250 * class doesn't exist and not a perm class
8253 if (!classexist(flt->class_name,
8254 act->classes)) {
8255 if (!in_string_table(act->perm_classes,
8256 act->num_perm_classes,
8257 flt->class_name)) {
8258 ipqos_msg(MT_ERROR,
8259 gettext("Undefined "
8260 "class in filter %s, "
8261 "action %s.\n"), flt->name,
8262 act->name);
8263 return (IPQOS_CONF_ERR);
8269 /* check classes */
8271 for (cls = act->classes; cls; cls = cls->next) {
8273 /* check if class name unique (userland only) */
8275 if (userconf && classexist(cls->name, cls->next)) {
8276 ipqos_msg(MT_ERROR,
8277 gettext("Duplicate named class %s in "
8278 "action %s.\n"), cls->name, act->name);
8279 return (IPQOS_CONF_ERR);
8283 * virtual actions always exist so don't check for next
8284 * action.
8286 if (virtual_action(cls->alist->name)) {
8287 continue;
8291 * check existance of next action and create link to
8292 * it.
8294 if ((cls->alist->action =
8295 actionexist(cls->alist->name, actions)) == NULL) {
8296 ipqos_msg(MT_ERROR,
8297 gettext("Undefined action in class %s, "
8298 "action %s.\n"), cls->name, act->name);
8299 return (IPQOS_CONF_ERR);
8302 /* create backwards link - used for deletions */
8304 dact = cls->alist->action;
8305 res = add_aref(&dact->dependencies, NULL, act->name);
8306 if (res != IPQOS_CONF_SUCCESS) {
8307 return (IPQOS_CONF_ERR);
8309 dact->dependencies->action = act;
8313 /* check actions exist for action type parameters */
8315 params = act->params;
8316 for (aref = params->actions; aref; aref = aref->next) {
8318 /* skip virtuals */
8320 if (virtual_action(aref->name)) {
8321 continue;
8325 * check existance of action in this ref
8326 * and if present create a ptr to it.
8328 aref->action = actionexist(aref->name, actions);
8329 if (aref->action == NULL) {
8330 ipqos_msg(MT_ERROR,
8331 gettext("Undefined action in parameter "
8332 "%s, action %s.\n"),
8333 SHORT_NAME(aref->field), act->name);
8334 return (IPQOS_CONF_ERR);
8337 /* create backwards link */
8339 dact = aref->action;
8340 res = add_aref(&dact->dependencies, NULL,
8341 act->name);
8342 if (res != IPQOS_CONF_SUCCESS) {
8343 return (IPQOS_CONF_ERR);
8345 dact->dependencies->action = act;
8349 /* for kernel retrieved configs we don't do the following checks. */
8350 if (!userconf) {
8351 return (IPQOS_CONF_SUCCESS);
8354 /* check for cycles in config and orphaned actions other than ipgpc */
8356 for (act = actions; act; act = act->next) {
8358 /* check if involved in cycle */
8360 if (in_cycle(act)) {
8361 ipqos_msg(MT_ERROR,
8362 gettext("Action %s involved in cycle.\n"),
8363 act->name);
8364 return (IPQOS_CONF_ERR);
8367 /* check that this action has a parent (except ipgpc) */
8369 if (act->dependencies == NULL &&
8370 strcmp(act->name, IPGPC_CLASSIFY) != 0) {
8371 ipqos_msg(MT_ERROR, gettext("Action %s isn't "
8372 "referenced by any other actions.\n"), act->name);
8373 return (IPQOS_CONF_ERR);
8377 return (IPQOS_CONF_SUCCESS);
8381 * Read the version from the config file with stream cfp with
8382 * the tag version_tag. The tag-value pair should be the first tokens
8383 * encountered.
8385 * RETURNS: -1 if a missing or invalid version or a read error,
8386 * else an integer encoding of the version.
8388 static int
8389 read_cfile_ver(
8390 FILE *cfp,
8391 char *version_tag)
8393 char *sp = NULL;
8394 int res;
8395 int version;
8397 IPQOSCDBG0(L1, "In read_cfile_ver:\n");
8400 * read version tag string.
8402 res = readtoken(cfp, &sp);
8403 if (res != IPQOS_CONF_SUCCESS) {
8404 goto fail;
8405 } else if (strcasecmp(sp, version_tag) != 0) {
8406 goto fail;
8408 free(sp);
8409 sp = NULL;
8412 * read version number string.
8414 res = readtoken(cfp, &sp);
8415 if (res != IPQOS_CONF_SUCCESS) {
8416 goto fail;
8420 * encode version into int.
8422 if ((version = ver_str_to_int(sp)) == -1) {
8423 goto fail;
8425 free(sp);
8427 return (version);
8428 fail:
8429 ipqos_msg(MT_ERROR,
8430 gettext("Missing/Invalid config file %s.\n"), version_tag);
8431 free(sp);
8432 return (-1);
8436 * read the set of actions definitions from the stream cfp and store
8437 * them in a list pointed to by conf.
8438 * RETURNS: IPQOS_CONF_ERR if any errors, else IPQOS_CONF_SUCCESS.
8440 static int
8441 readconf(
8442 FILE *cfp,
8443 ipqos_conf_action_t **conf)
8446 int res;
8447 ipqos_conf_action_t *action;
8448 boolean_t ipgpc_action = B_FALSE;
8449 int fmt_ver;
8451 IPQOSCDBG0(L0, "In readconf\n");
8453 *conf = NULL;
8456 * get config file format version.
8458 fmt_ver = read_cfile_ver(cfp, IPQOS_FMT_VERSION_STR);
8459 if (fmt_ver == -1) {
8460 return (IPQOS_CONF_ERR);
8461 } else {
8463 * check version is valid
8465 if ((IPP_MAJOR_MODULE_VER(fmt_ver) > 1) ||
8466 (IPP_MINOR_MODULE_VER(fmt_ver) > 0)) {
8467 ipqos_msg(MT_ERROR, gettext("Unsupported config file "
8468 "format version.\n"));
8469 return (IPQOS_CONF_ERR);
8473 /* loop reading actions adding to conf till EOF */
8475 for (;;) {
8476 action = NULL;
8478 /* readaction */
8480 res = readaction(cfp, &action);
8481 if (res == IPQOS_CONF_ERR) {
8482 goto fail;
8485 /* reached eof, finish */
8487 if (res == IPQOS_CONF_EOF) {
8488 break;
8491 ADD_TO_LIST(conf, action);
8493 /* check if we just read an ipgpc action */
8495 if (strcmp(action->name, IPGPC_CLASSIFY) == 0)
8496 ipgpc_action = B_TRUE;
8499 /* check that there is one or more actions and that one is ipgpc */
8501 if (ipgpc_action == B_FALSE) {
8502 ipqos_msg(MT_ERROR, gettext("No %s action defined.\n"),
8503 IPGPC_NAME);
8504 goto fail;
8507 return (IPQOS_CONF_SUCCESS);
8508 fail:
8509 free_actions(*conf);
8510 *conf = NULL;
8511 return (IPQOS_CONF_ERR);
8514 /* ************************ kernel config retrieval ************************ */
8518 * read the current configuration from the kernel and make *conf a ptr to it.
8519 * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCES.
8521 static int
8522 readkconf(ipqos_conf_action_t **conf)
8525 int res;
8526 char **modnames = NULL;
8527 int nmods;
8528 char **actnames = NULL;
8529 int nacts;
8530 int x, y;
8531 FILE *tfp;
8532 int openerr;
8533 ipqos_actinfo_prm_t ai_prm;
8536 IPQOSCDBG0(L0, "In readkconf\n");
8538 /* initialise conf to NULL */
8539 *conf = NULL;
8541 /* get list of modules currently loaded */
8543 res = ipp_list_mods(&modnames, &nmods);
8544 if (res != 0) {
8545 ipqos_msg(MT_ENOSTR, "ipp_list_mods");
8546 return (IPQOS_CONF_ERR);
8550 * iterate through all loaded modules retrieving their list of actions
8551 * and then retrieving the configuration of each of these
8552 * and attatching it to conf.
8554 for (x = 0; x < nmods; x++) {
8556 /* skip actions of modules that we can't open types file of */
8558 if ((tfp = validmod(modnames[x], &openerr)) == NULL) {
8560 /* mem error */
8562 if (!openerr) {
8563 goto fail;
8566 * fopen fail - if we failed because the file didn't
8567 * exist we assume this is an unknown module and
8568 * ignore this module, otherwise error.
8570 } else {
8571 if (errno == ENOENT) {
8572 continue;
8573 } else {
8574 ipqos_msg(MT_ENOSTR, "fopen");
8575 goto fail;
8579 (void) fclose(tfp);
8581 /* get action list for this module */
8583 res = ipp_mod_list_actions(modnames[x], &actnames, &nacts);
8584 if (res != 0) {
8585 ipqos_msg(MT_ENOSTR, "ipp_mod_list_actions");
8586 goto fail;
8589 /* read config of each action of this module */
8591 for (y = 0; y < nacts; y++) {
8592 ai_prm.action = alloc_action();
8593 if (ai_prm.action == NULL) {
8594 goto fail;
8597 /* copy action name into action struct */
8599 (void) strlcpy(ai_prm.action->name, actnames[y],
8600 IPQOS_CONF_NAME_LEN);
8602 /* copy module name into action struct */
8604 (void) strlcpy(ai_prm.action->module, modnames[x],
8605 IPQOS_CONF_NAME_LEN);
8607 /* get action info */
8609 res = ipp_action_info(actnames[y],
8610 (int (*)(nvlist_t *, void *))parse_kaction,
8611 (void *)&ai_prm, 0);
8612 if (res != 0) {
8613 /* was this an ipp error */
8614 if (ai_prm.intl_ret == IPQOS_CONF_SUCCESS) {
8615 ipqos_msg(MT_ENOSTR,
8616 "ipp_action_info");
8618 goto fail;
8621 ADD_TO_LIST(conf, ai_prm.action);
8624 cleanup_string_table(actnames, nacts);
8627 cleanup_string_table(modnames, nmods);
8628 return (IPQOS_CONF_SUCCESS);
8629 fail:
8630 free_actions(*conf);
8631 *conf = NULL;
8632 cleanup_string_table(modnames, nmods);
8633 cleanup_string_table(actnames, nacts);
8634 return (IPQOS_CONF_ERR);
8638 * This is passed as a parameter to ipp_action_info() in readkaction and
8639 * is called back one for each configuration element within the action
8640 * specified. This results in filters and classes being created and chained
8641 * off of action, and action having its params set.
8642 * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCESS.
8644 static int
8645 parse_kaction(
8646 nvlist_t *nvl,
8647 ipqos_actinfo_prm_t *ai_prm)
8650 int ret;
8651 uint8_t cfgtype;
8652 ipqos_conf_filter_t *filter = NULL;
8653 ipqos_conf_class_t *class = NULL;
8654 ipqos_conf_action_t *action = ai_prm->action;
8657 IPQOSCDBG1(KRET, "In parse_kaction: action_name: %s\n", action->name);
8659 /* get config type */
8661 (void) nvlist_lookup_byte(nvl, IPP_CONFIG_TYPE, &cfgtype);
8662 (void) nvlist_remove_all(nvl, IPP_CONFIG_TYPE);
8664 switch (cfgtype) {
8665 case CLASSIFIER_ADD_FILTER: {
8667 * parse the passed filter nvlist
8668 * and add result to action's filter list.
8670 filter = alloc_filter();
8671 if (filter == NULL) {
8672 ai_prm->intl_ret = IPQOS_CONF_ERR;
8673 return (IPQOS_CONF_ERR);
8676 ret = parse_kfilter(filter, nvl);
8677 if (ret != IPQOS_CONF_SUCCESS) {
8678 free_filter(filter);
8679 ai_prm->intl_ret = IPQOS_CONF_ERR;
8680 return (ret);
8683 ADD_TO_LIST(&action->filters, filter);
8684 break;
8686 case CLASSIFIER_ADD_CLASS:
8687 case CLASSIFIER_MODIFY_CLASS: {
8689 * parse the passed class nvlist
8690 * and add result to action's class list.
8692 class = alloc_class();
8693 if (class == NULL) {
8694 ai_prm->intl_ret = IPQOS_CONF_ERR;
8695 return (IPQOS_CONF_ERR);
8698 ret = parse_kclass(class, nvl);
8699 if (ret != IPQOS_CONF_SUCCESS) {
8700 free_class(class);
8701 ai_prm->intl_ret = IPQOS_CONF_ERR;
8702 return (ret);
8705 ADD_TO_LIST(&action->classes, class);
8706 break;
8708 case IPP_SET: {
8710 * we don't alloc a params struct as it is created
8711 * as part of an action.
8714 /* parse the passed params nvlist */
8716 ret = parse_kparams(action->module, action->params,
8717 nvl);
8718 if (ret != IPQOS_CONF_SUCCESS) {
8719 ai_prm->intl_ret = IPQOS_CONF_ERR;
8720 return (ret);
8725 ai_prm->intl_ret = IPQOS_CONF_SUCCESS;
8726 return (IPQOS_CONF_SUCCESS);
8730 * parses a params nvlist returned from the kernel.
8731 * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCES.
8734 parse_kparams(
8735 char *module,
8736 ipqos_conf_params_t *params,
8737 nvlist_t *nvl) {
8739 int ret;
8740 ipqos_nvtype_t type;
8741 str_val_nd_t *tmp;
8742 char *act;
8743 uint32_t u32;
8744 nvpair_t *nvp;
8745 FILE *tfp;
8746 char dfltst[IPQOS_VALST_MAXLEN];
8747 char *param;
8748 nvlist_t *nvlcp;
8749 int openerr;
8750 place_t place;
8752 IPQOSCDBG0(KRET, "In parse_kparams:\n");
8754 /* get stream to module types file */
8756 tfp = validmod(module, &openerr);
8757 if (tfp == NULL) {
8758 if (openerr) {
8759 ipqos_msg(MT_ENOSTR, "fopen");
8761 return (IPQOS_CONF_ERR);
8764 /* make copy of passed in nvlist as it is freed by the caller */
8766 ret = nvlist_dup(nvl, &nvlcp, 0);
8767 if (ret != 0) {
8768 return (IPQOS_CONF_ERR);
8772 * get config originator and remove from nvlist. If no owner we
8773 * assume ownership.
8775 ret = nvlist_lookup_uint32(nvlcp, IPP_CONFIG_ORIGINATOR, &u32);
8776 if (ret == 0) {
8777 params->originator = u32;
8778 (void) nvlist_remove_all(nvlcp, IPP_CONFIG_ORIGINATOR);
8779 } else {
8780 params->originator = IPP_CONFIG_IPQOSCONF;
8783 /* get action stats and remove from nvlist */
8785 ret = nvlist_lookup_uint32(nvlcp, IPP_ACTION_STATS_ENABLE, &u32);
8786 if (ret == 0) {
8787 params->stats_enable = *(boolean_t *)&u32;
8788 (void) nvlist_remove_all(nvlcp, IPP_ACTION_STATS_ENABLE);
8792 * loop throught nvlist elements and for those that are actions create
8793 * action ref entrys for them.
8795 nvp = nvlist_next_nvpair(nvlcp, NULL);
8796 while (nvp != NULL) {
8797 param = SHORT_NAME(nvpair_name(nvp));
8798 place = PL_ANY;
8799 ret = readtype(tfp, module, param, &type, &tmp, dfltst,
8800 B_FALSE, &place);
8801 if (ret != IPQOS_CONF_SUCCESS) {
8802 goto fail;
8805 if ((place == PL_PARAMS) && /* avoid map entries */
8806 (type == IPQOS_DATA_TYPE_ACTION)) {
8807 (void) nvpair_value_string(nvp, &act);
8808 ret = add_aref(&params->actions, nvpair_name(nvp), act);
8809 if (ret != IPQOS_CONF_SUCCESS) {
8810 goto fail;
8814 nvp = nvlist_next_nvpair(nvlcp, nvp);
8817 /* assign copied nvlist to params struct */
8819 params->nvlist = nvlcp;
8821 (void) fclose(tfp);
8822 return (IPQOS_CONF_SUCCESS);
8823 fail:
8824 (void) fclose(tfp);
8825 free_arefs(params->actions);
8826 params->actions = NULL;
8827 nvlist_free(nvlcp);
8828 return (IPQOS_CONF_ERR);
8832 * parses a classes nvlist returned from the kernel.
8833 * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCES.
8835 static int
8836 parse_kclass(
8837 ipqos_conf_class_t *class,
8838 nvlist_t *nvl)
8841 int ret;
8842 uint32_t u32;
8843 char *str;
8845 IPQOSCDBG0(KRET, "In parse_kclass:\n");
8847 /* lookup object originator */
8849 ret = nvlist_lookup_uint32(nvl, IPP_CONFIG_ORIGINATOR, &u32);
8850 if (ret == 0) {
8851 class->originator = u32;
8852 } else {
8853 class->originator = IPP_CONFIG_IPQOSCONF;
8856 /* lookup name */
8858 (void) nvlist_lookup_string(nvl, CLASSIFIER_CLASS_NAME, &str);
8859 (void) strlcpy(class->name, str, IPQOS_CONF_NAME_LEN);
8860 IPQOSCDBG1(KRET, "reading class %s\n", class->name);
8862 /* lookup next action */
8864 (void) nvlist_lookup_string(nvl, CLASSIFIER_NEXT_ACTION, &str);
8865 ret = add_aref(&class->alist, NULL, str);
8866 if (ret != IPQOS_CONF_SUCCESS) {
8867 return (IPQOS_CONF_ERR);
8870 /* lookup stats enable */
8872 ret = nvlist_lookup_uint32(nvl, CLASSIFIER_CLASS_STATS_ENABLE, &u32);
8873 if (ret == 0) {
8874 class->stats_enable = *(boolean_t *)&u32;
8877 return (IPQOS_CONF_SUCCESS);
8881 * parses a filters nvlist returned from the kernel.
8882 * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCES.
8884 static int
8885 parse_kfilter(
8886 ipqos_conf_filter_t *filter,
8887 nvlist_t *nvl)
8890 int ret;
8891 char *str;
8892 uint32_t u32;
8893 nvlist_t *nvlcp;
8894 char *end;
8896 IPQOSCDBG0(KRET, "In parse_kfilter:\n");
8898 /* make copy of passed in nvlist as it is freed by the caller */
8900 ret = nvlist_dup(nvl, &nvlcp, 0);
8901 if (ret != 0) {
8902 return (IPQOS_CONF_ERR);
8905 /* lookup originator */
8907 ret = nvlist_lookup_uint32(nvlcp, IPP_CONFIG_ORIGINATOR, &u32);
8908 if (ret == 0) {
8909 filter->originator = u32;
8910 (void) nvlist_remove_all(nvlcp, IPP_CONFIG_ORIGINATOR);
8911 } else {
8912 filter->originator = IPP_CONFIG_IPQOSCONF;
8915 /* lookup filter name */
8917 (void) nvlist_lookup_string(nvlcp, CLASSIFIER_FILTER_NAME, &str);
8918 (void) strlcpy(filter->name, str, IPQOS_CONF_NAME_LEN);
8919 (void) nvlist_remove_all(nvlcp, CLASSIFIER_FILTER_NAME);
8921 /* lookup class name */
8923 (void) nvlist_lookup_string(nvlcp, CLASSIFIER_CLASS_NAME, &str);
8924 (void) strlcpy(filter->class_name, str, IPQOS_CONF_NAME_LEN);
8925 (void) nvlist_remove_all(nvlcp, CLASSIFIER_CLASS_NAME);
8927 /* lookup src and dst host names if present */
8929 if (nvlist_lookup_string(nvlcp, IPGPC_SADDR_HOSTNAME, &str) == 0) {
8930 filter->src_nd_name = malloc(strlen(str) + 1);
8931 if (filter->src_nd_name) {
8932 (void) strcpy(filter->src_nd_name, str);
8933 (void) nvlist_remove_all(nvlcp, IPGPC_SADDR_HOSTNAME);
8934 } else {
8935 ipqos_msg(MT_ENOSTR, "malloc");
8936 nvlist_free(nvlcp);
8937 return (IPQOS_CONF_ERR);
8940 if (nvlist_lookup_string(nvlcp, IPGPC_DADDR_HOSTNAME, &str) == 0) {
8941 filter->dst_nd_name = malloc(strlen(str) + 1);
8942 if (filter->dst_nd_name) {
8943 (void) strcpy(filter->dst_nd_name, str);
8944 (void) nvlist_remove_all(nvlcp, IPGPC_DADDR_HOSTNAME);
8945 } else {
8946 ipqos_msg(MT_ENOSTR, "malloc");
8947 nvlist_free(nvlcp);
8948 return (IPQOS_CONF_ERR);
8952 /* lookup ip_version if present */
8954 if (nvlist_lookup_string(nvlcp, IPGPC_FILTER_PRIVATE, &str) == 0) {
8955 filter->ip_versions = (uint32_t)strtol(str, &end, 0);
8956 if (end != str) {
8957 (void) nvlist_remove_all(nvlcp, IPGPC_FILTER_PRIVATE);
8958 } else {
8959 ipqos_msg(MT_ERROR,
8960 gettext("Corrupted ip_version returned from "
8961 "kernel.\n"));
8962 nvlist_free(nvlcp);
8963 return (IPQOS_CONF_ERR);
8967 /* lookup filter instance if present */
8969 ret = nvlist_lookup_int32(nvlcp, IPGPC_FILTER_INSTANCE,
8970 &filter->instance);
8971 if (ret != 0) {
8972 filter->instance = -1;
8973 } else {
8974 (void) nvlist_remove_all(nvlcp, IPGPC_FILTER_INSTANCE);
8977 /* attach new trimmed nvlist to filter */
8978 filter->nvlist = nvlcp;
8980 return (IPQOS_CONF_SUCCESS);
8985 * determines whether action_name is a virtual action name.
8986 * RETURNS: if virtual action 1, else 0.
8988 static int
8989 virtual_action(char *action_name)
8992 if (strcmp(action_name, IPP_ANAME_CONT) == 0 ||
8993 strcmp(action_name, IPP_ANAME_DEFER) == 0 ||
8994 strcmp(action_name, IPP_ANAME_DROP) == 0) {
8995 return (1);
8998 return (0);
9002 * remove all the actions within the kernel. If there is a failure
9003 * modified is set to represent whether the attempt to flush modified
9004 * the configuration in any way.
9005 * RETURNS: IPQOS_CONF_ERR if the ipp_* functions return any errors,
9006 * else IPQOS_CONF_SUCCESS.
9008 static int
9009 flush(
9010 boolean_t *modified)
9013 int res;
9014 char **modnames = NULL;
9015 int nmods;
9016 char **actnames = NULL;
9017 int nacts;
9018 int x, y;
9020 IPQOSCDBG0(L0, "In flush\n");
9022 *modified = B_FALSE;
9025 * get list of modules currently loaded.
9027 res = ipp_list_mods(&modnames, &nmods);
9028 if (res != 0) {
9029 ipqos_msg(MT_ENOSTR, "ipp_list_mods");
9030 return (IPQOS_CONF_ERR);
9034 * iterate through all the modules listing their actions and
9035 * deleting all of them.
9037 for (x = 0; x < nmods; x++) {
9038 IPQOSCDBG1(APPLY, "Getting actions of module %s.\n",
9039 modnames[x]);
9040 res = ipp_mod_list_actions(modnames[x], &actnames, &nacts);
9041 if (res != 0) {
9042 ipqos_msg(MT_ENOSTR, "ipp_mod_list_actions");
9043 cleanup_string_table(modnames, nmods);
9044 return (IPQOS_CONF_ERR);
9047 for (y = 0; y < nacts; y++) {
9048 IPQOSCDBG1(APPLY, "deleting action %s\n", actnames[y]);
9049 res = ipp_action_destroy(actnames[y], IPP_DESTROY_REF);
9051 * if fails for reason other than action doesn't
9052 * exist or action has dependency.
9054 if (res != 0 && errno != ENOENT && errno != EBUSY) {
9055 ipqos_msg(MT_ENOSTR, "ipp_action_destroy");
9056 cleanup_string_table(modnames, nmods);
9057 cleanup_string_table(actnames, nacts);
9058 return (IPQOS_CONF_ERR);
9061 if (res == 0)
9062 *modified = B_TRUE;
9064 cleanup_string_table(actnames, nacts);
9066 cleanup_string_table(modnames, nmods);
9068 return (IPQOS_CONF_SUCCESS);
9072 * Trys to flush the configuration. If it fails and nothing has been modified
9073 * and force_flush is false just return an error, otherwise persist trying to
9074 * completion.
9075 * RETURNS: IPQOS_CONF_ERR if flush attempt failed without modifying anything
9076 * and force_flush was set to false, otherwise IPQOS_CONF_SUCCESS.
9078 static int
9079 atomic_flush(
9080 boolean_t force_flush)
9082 int x = 0;
9083 int res;
9084 boolean_t modified = B_FALSE;
9087 * attempt first flush of config.
9089 res = flush(&modified);
9090 if ((force_flush == B_FALSE) && (res != IPQOS_CONF_SUCCESS) &&
9091 (modified == B_FALSE)) {
9092 return (IPQOS_CONF_ERR);
9093 } else if (res == IPQOS_CONF_SUCCESS) {
9094 return (IPQOS_CONF_SUCCESS);
9098 * failed flush that modified config, or force flush set; loop till
9099 * successful flush.
9101 while (res != IPQOS_CONF_SUCCESS) {
9102 if (x == 5) { /* 10 secs since start/last message. */
9103 ipqos_msg(MT_ERROR,
9104 gettext("Retrying configuration flush.\n"));
9105 x = 0;
9107 (void) sleep(2);
9108 x++;
9109 res = flush(&modified);
9112 return (IPQOS_CONF_SUCCESS);
9116 * Performs a flush of the configuration within a signal blocking region
9117 * so that there's minimal chance of it being killed and the flush only
9118 * partially completing.
9119 * RETURNS: IPQOS_CONF_SUCCESS (for symmetry with the other main functions).
9121 static int
9122 flushconf()
9124 int res;
9127 * make sure that flush is as atomic as possible.
9129 if ((res = block_all_signals()) == -1)
9130 return (IPQOS_CONF_ERR);
9132 res = atomic_flush(B_FALSE);
9135 * restore signals.
9137 (void) restore_all_signals();
9139 if (res == IPQOS_CONF_SUCCESS) {
9140 ipqos_msg(MT_LOG, gettext("Configuration flushed.\n"));
9141 } else {
9142 ipqos_msg(MT_ENOSTR, "atomic_flush");
9145 return (res);
9148 static int
9149 in_string_table(char *stable[], int size, char *string)
9152 IPQOSCDBG1(L1, "In in_string_table: search string %s\n", string);
9154 for (--size; size >= 0; size--) {
9155 if (strcmp(stable[size], string) == 0) {
9156 IPQOSCDBG1(L1, "Found %s in string table\n", string);
9157 return (1);
9161 return (0);
9164 /* free the memory occupied by the string table ctable and its contents. */
9165 static void
9166 cleanup_string_table(char *ctable[], int size)
9169 int x;
9171 if (ctable) {
9172 for (x = 0; x < size; x++) {
9173 free(ctable[x]);
9175 free(ctable);
9179 #if 0
9182 * makes a copy of a string table and returns a ptr to it.
9183 * RETURNS: NULL on error or if size was 0, else ptr to copied table.
9185 static char **
9186 copy_string_table(char *stable1[], int size)
9189 char **st = NULL;
9190 int pos;
9192 /* create char ptr array */
9194 st = malloc(size * sizeof (char *));
9195 if (st == NULL) {
9196 ipqos_msg(MT_ENOSTR, "malloc");
9197 return (st);
9200 /* create copy of each string from stable1 in array */
9202 for (pos = size - 1; pos >= 0; pos--) {
9203 st[pos] = malloc(strlen(stable1[pos] + 1));
9204 if (st[pos] == NULL) {
9205 for (pos++; pos < size; pos++)
9206 free(st[pos]);
9207 free(st);
9208 ipqos_msg(MT_ENOSTR, "malloc");
9209 return (NULL);
9212 (void) strcpy(st[pos], stable1[pos]);
9215 return (st);
9217 #endif /* 0 */
9220 * retry lookups on filters that soft failed a previous lookup and
9221 * were put on the retry list.
9222 * RETURNS: IPQOS_CONF_ERR on any errors, else IPQOS_CONF_SUCCESS.
9224 static int
9225 retry_name_lookups(
9226 ipqos_conf_action_t *actions)
9229 ipqos_conf_action_t *act;
9230 ipqos_conf_filter_t **new_filters;
9231 ipqos_conf_filter_t *flt;
9233 IPQOSCDBG0(APPLY, "In retry_name_lookups:\n");
9235 for (act = actions; act != NULL; act = act->next) {
9237 /* store start of new resolved filters */
9238 GET_LIST_END(&act->filters, &new_filters);
9241 * do name resolution on retry list adding resolved filters
9242 * to end of actions filters.
9244 for (flt = act->retry_filters; flt != NULL; flt = flt->next) {
9246 if (domultihome(flt, new_filters, B_TRUE) !=
9247 IPQOS_CONF_SUCCESS) {
9249 /* if resource failure */
9251 if (flt->nlerr == 0) {
9252 return (IPQOS_CONF_ERR);
9257 /* add the newly resolved filters to the kernel action */
9259 for (flt = *new_filters; flt != NULL; flt = flt->next) {
9260 if (add_filter(act->name, flt, act->module_version) !=
9261 IPQOS_CONF_SUCCESS) {
9262 return (IPQOS_CONF_ERR);
9267 return (IPQOS_CONF_SUCCESS);
9271 * write the configuration in conf to the file given in dstpath. This
9272 * is done by writing first to a temporary file and then renaming that
9273 * file to dstpath. This assures an atomic write.
9274 * RETURNS: IPQOS_CONF_ERR on any errors, else IPQOS_CONF_SUCCESS.
9276 static int
9277 writeconf(
9278 ipqos_conf_action_t *conf,
9279 char *dstpath)
9282 FILE *tmpfp;
9283 char *tmppath;
9284 char *pathend;
9285 ipqos_conf_action_t *act;
9286 int res;
9288 IPQOSCDBG0(L0, "in writeconf\n");
9290 /* construct tmp file path so we can use rename() */
9292 pathend = strrchr(dstpath, '/');
9294 /* dstpath in current dir */
9296 if (pathend == NULL) {
9297 tmppath = malloc(strlen("ipqosconf.tmp") + 1);
9298 if (tmppath == NULL) {
9299 ipqos_msg(MT_ENOSTR, "malloc");
9300 return (IPQOS_CONF_ERR);
9302 (void) strcpy(tmppath, "ipqosconf.tmp");
9304 /* dstpath in root dir */
9306 } else if (pathend == dstpath) {
9307 tmppath = malloc(strlen("/ipqosconf.tmp") + 1);
9308 if (tmppath == NULL) {
9309 ipqos_msg(MT_ENOSTR, "malloc");
9310 return (IPQOS_CONF_ERR);
9312 (void) strcpy(tmppath, "/ipqosconf.tmp");
9314 /* not pwd or root */
9316 } else {
9317 *pathend = '\0';
9318 tmppath = malloc(strlen(dstpath) + strlen("/ipqosconf.tmp") +
9320 if (tmppath == NULL) {
9321 ipqos_msg(MT_ENOSTR, "malloc");
9322 return (IPQOS_CONF_ERR);
9324 (void) strcpy(tmppath, dstpath);
9325 (void) strcat(tmppath, "/ipqosconf.tmp");
9326 *pathend = '/';
9330 /* open tmp file */
9332 tmpfp = fopen(tmppath, "w");
9333 if (tmpfp == NULL) {
9334 ipqos_msg(MT_ENOSTR, "fopen");
9335 free(tmppath);
9336 return (IPQOS_CONF_ERR);
9339 /* write out format version */
9341 (void) fprintf(tmpfp, "%s %d.%d\n\n", IPQOS_FMT_VERSION_STR,
9342 IPQOS_CUR_FMT_MAJOR_VER, IPQOS_CUR_FMT_MINOR_VER);
9345 * loop through actions in list writing ipqosconf originated
9346 * ones out to the tmp file.
9348 for (act = conf; act != NULL; act = act->next) {
9349 if (act->params->originator == IPP_CONFIG_IPQOSCONF) {
9350 res = printaction(tmpfp, act, 0, 0);
9351 if (res != IPQOS_CONF_SUCCESS) {
9352 free(tmppath);
9353 (void) fclose(tmpfp);
9354 return (res);
9358 (void) fclose(tmpfp);
9360 /* rename tmp file to dst file */
9362 if (rename(tmppath, dstpath) != 0) {
9363 ipqos_msg(MT_ENOSTR, "rename");
9364 free(tmppath);
9365 return (IPQOS_CONF_ERR);
9367 free(tmppath);
9369 return (IPQOS_CONF_SUCCESS);
9373 * read the configuration back from the kernel and then write each of the
9374 * actions read to IPQOS_CONF_INIT_PATH.
9375 * RETURNS: IPQOS_CONF_ERR if error, else IPQOS_CONF_SUCCESS.
9377 static int
9378 commitconf()
9381 int ret;
9382 ipqos_conf_action_t *conf;
9384 IPQOSCDBG0(L0, "In commitconf\n");
9386 /* read the configuration from the kernel */
9388 ret = readkconf(&conf);
9389 if (ret != IPQOS_CONF_SUCCESS) {
9390 return (IPQOS_CONF_ERR);
9393 /* dissallow a null config to be stored (we can't read one in) */
9395 if (conf == NULL) {
9396 ipqos_msg(MT_ERROR,
9397 gettext("Can't commit a null configuration.\n"));
9398 return (IPQOS_CONF_ERR);
9401 /* make sure if we create file that perms are 644 */
9403 (void) umask(S_IXUSR | S_IWGRP | S_IXGRP | S_IWOTH | S_IXOTH);
9405 /* write the configuration to the init file */
9407 ret = writeconf(conf, IPQOS_CONF_INIT_PATH);
9408 if (ret != IPQOS_CONF_SUCCESS) {
9409 return (IPQOS_CONF_ERR);
9412 ipqos_msg(MT_LOG,
9413 gettext("Current configuration saved to init file.\n"));
9415 return (IPQOS_CONF_SUCCESS);
9419 * Called in the event of a failed rollback. It first flushes the
9420 * current configuration, then attempts to apply the oconf (the old
9421 * one), and if that fails flushes again.
9423 * RETURNS: IPQOS_CONF_ERR if the application of old config fails,
9424 * else IPQOS_CONF_SUCCESS.
9426 static int
9427 rollback_recover(
9428 ipqos_conf_action_t *oconf)
9430 int res;
9432 IPQOSCDBG0(RBK, "In rollback_recover\n");
9435 * flush configuration.
9437 (void) atomic_flush(B_TRUE);
9440 * mark all elements of old config for application.
9442 mark_config_new(oconf);
9445 * attempt to apply old config.
9447 res = applydiff(oconf, NULL);
9449 * if failed force flush of config.
9451 if (res != IPQOS_CONF_SUCCESS) {
9452 (void) atomic_flush(B_TRUE);
9453 return (IPQOS_CONF_ERR);
9456 return (IPQOS_CONF_SUCCESS);
9460 * read and apply the configuration contained if file ifile to the kernel.
9461 * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCES.
9463 static int
9464 applyconf(char *ifile)
9467 FILE *ifp;
9468 ipqos_conf_action_t *conf = NULL;
9469 ipqos_conf_action_t *oconf = NULL;
9470 ipqos_conf_action_t *act, *oact;
9471 int res;
9473 IPQOSCDBG0(L0, "In applyconf:\n");
9476 /* if filename '-' read from stdin */
9478 if (strcmp(ifile, "-") == 0) {
9479 ifp = stdin;
9480 } else {
9481 ifp = fopen(ifile, "r");
9482 if (ifp == NULL) {
9483 ipqos_msg(MT_ERROR,
9484 gettext("Opening file %s for read: %s.\n"),
9485 ifile, strerror(errno));
9486 return (IPQOS_CONF_ERR);
9490 /* read in new configuration */
9492 res = readconf(ifp, &conf);
9493 if (res != IPQOS_CONF_SUCCESS) {
9494 goto fail;
9497 /* check configuration is valid */
9499 res = validconf(conf, 1);
9500 if (res != IPQOS_CONF_SUCCESS) {
9501 goto fail;
9504 /* read in kernel configuration */
9506 res = readkconf(&oconf);
9507 if (res != IPQOS_CONF_SUCCESS) {
9508 goto fail;
9512 * check there are no same named actions in both config file and the
9513 * the kernel that are for a different module. The application
9514 * system can't handle these as we would try to add the new
9515 * action before we deleted the old one and because actions
9516 * in the kernel are indexed solely on their name (their module
9517 * isn't included) the kernel would return an error. We want
9518 * to avoid this error and the resulting rollback.
9520 for (act = conf; act != NULL; act = act->next) {
9521 for (oact = oconf; oact != NULL; oact = oact->next) {
9522 /* found action */
9523 if (strcmp(act->name, oact->name) == 0) {
9524 /* different module */
9525 if (strcmp(act->module, oact->module) != 0) {
9526 ipqos_msg(MT_ERROR,
9527 gettext("Action at line %u has "
9528 "same name as currently "
9529 "installed action, but is for a "
9530 "different module.\n"),
9531 act->lineno);
9532 goto fail;
9533 /* same module - stop search */
9534 } else {
9535 break;
9542 /* create links between actions for use with deletions etc.. */
9544 res = validconf(oconf, 0);
9545 if (res != IPQOS_CONF_SUCCESS) {
9546 goto fail;
9549 /* diff conf file against kernel */
9551 res = diffconf(oconf, conf);
9552 if (res != IPQOS_CONF_SUCCESS) {
9553 goto fail;
9556 /* make kernel mods as atomic as possible */
9558 if ((res = block_all_signals()) == -1) {
9559 res = IPQOS_CONF_ERR;
9560 goto fail;
9563 /* apply difference to kernel */
9565 res = applydiff(conf, oconf);
9566 #ifdef _IPQOS_CONF_DEBUG
9567 if (force_rback || res != IPQOS_CONF_SUCCESS) {
9568 #else
9569 if (res != IPQOS_CONF_SUCCESS) {
9570 #endif /* _IPQOS_CONF_DEBUG */
9572 res = rollback(conf, oconf);
9573 if (res != IPQOS_CONF_SUCCESS) {
9574 res = rollback_recover(oconf);
9575 if (res != IPQOS_CONF_SUCCESS) {
9576 /* system left flushed */
9577 ipqos_msg(MT_ERROR,
9578 gettext("Failed to rollback from failed "
9579 "configuration, configuration flushed.\n"));
9580 res = IPQOS_CONF_RECOVER_ERR;
9581 } else { /* old config re-applied */
9582 ipqos_msg(MT_ERROR,
9583 gettext("Configuration failed, system "
9584 "state unchanged.\n"));
9585 res = IPQOS_CONF_ERR;
9587 } else {
9588 ipqos_msg(MT_ERROR,
9589 gettext("Configuration failed, system "
9590 "state unchanged.\n"));
9591 res = IPQOS_CONF_ERR;
9593 goto fail;
9596 /* retry any soft name lookup failures */
9598 res = retry_name_lookups(conf);
9599 if (res != IPQOS_CONF_SUCCESS) {
9600 res = rollback(conf, oconf);
9601 if (res != IPQOS_CONF_SUCCESS) {
9602 res = rollback_recover(oconf);
9603 if (res != IPQOS_CONF_SUCCESS) {
9604 /* system left flushed */
9605 ipqos_msg(MT_ERROR,
9606 gettext("Failed to rollback from failed "
9607 "configuration, configuration flushed.\n"));
9608 res = IPQOS_CONF_RECOVER_ERR;
9609 } else { /* old config re-applied */
9610 ipqos_msg(MT_ERROR,
9611 gettext("Configuration failed, system "
9612 "state unchanged.\n"));
9613 res = IPQOS_CONF_ERR;
9615 } else {
9616 ipqos_msg(MT_ERROR,
9617 gettext("Configuration failed, system "
9618 "state unchanged.\n"));
9619 res = IPQOS_CONF_ERR;
9621 goto fail;
9625 ipqos_msg(MT_LOG, gettext("IPQoS configuration applied.\n"));
9627 /* re-enable signals */
9628 (void) restore_all_signals();
9630 (void) fclose(ifp);
9631 free_actions(conf);
9632 free_actions(oconf);
9633 return (IPQOS_CONF_SUCCESS);
9634 fail:
9635 (void) fclose(ifp);
9636 (void) restore_all_signals();
9637 if (conf)
9638 free_actions(conf);
9639 if (oconf)
9640 free_actions(oconf);
9641 if (res == IPQOS_CONF_RECOVER_ERR)
9642 ipqos_msg(MT_LOG, gettext("Configuration flushed.\n"));
9643 return (res);
9646 static sigset_t set, oset;
9648 static int
9649 block_all_signals()
9651 if (sigfillset(&set) == -1) {
9652 ipqos_msg(MT_ENOSTR, "sigfillset");
9653 return (-1);
9655 if (sigprocmask(SIG_SETMASK, &set, &oset) == -1) {
9656 ipqos_msg(MT_ENOSTR, "sigprocmask");
9657 return (-1);
9659 return (0);
9662 static int
9663 restore_all_signals()
9665 if (sigprocmask(SIG_SETMASK, &oset, NULL) == -1) {
9666 ipqos_msg(MT_ENOSTR, "sigprocmask");
9667 return (-1);
9669 return (0);
9672 static int
9673 unlock(int fd)
9675 if (lockf(fd, F_ULOCK, 0) == -1) {
9676 ipqos_msg(MT_ENOSTR, "lockf");
9677 return (-1);
9679 return (0);
9682 static int
9683 lock()
9685 int fd;
9686 struct stat sbuf1;
9687 struct stat sbuf2;
9690 * Open the file with O_CREAT|O_EXCL. If it exists already, it
9691 * will fail. If it already exists, check whether it looks like
9692 * the one we created.
9694 (void) umask(0077);
9695 if ((fd = open(IPQOS_CONF_LOCK_FILE, O_EXCL|O_CREAT|O_RDWR,
9696 S_IRUSR|S_IWUSR)) == -1) {
9697 if (errno != EEXIST) {
9698 /* Some other problem. */
9699 ipqos_msg(MT_ENOSTR,
9700 gettext("Cannot open lock file %s"),
9701 IPQOS_CONF_LOCK_FILE);
9702 return (-1);
9706 * open() returned an EEXIST error. We don't fail yet
9707 * as it could be a residual from a previous
9708 * execution. However, we need to clear errno here.
9709 * If we don't and print_cmd_buf() is later invoked
9710 * as the result of a parsing error, it
9711 * will assume that the current error is EEXIST and
9712 * that a corresponding error message has already been
9713 * printed, which results in an incomplete error
9714 * message. If errno is zero, print_cmd_buf() will
9715 * assume that it is called as a result of a
9716 * parsing error and will print the appropriate
9717 * error message.
9719 errno = 0;
9722 * File exists. make sure it is OK. We need to lstat()
9723 * as fstat() stats the file pointed to by the symbolic
9724 * link.
9726 if (lstat(IPQOS_CONF_LOCK_FILE, &sbuf1) == -1) {
9727 ipqos_msg(MT_ENOSTR,
9728 gettext("Cannot lstat lock file %s\n"),
9729 IPQOS_CONF_LOCK_FILE);
9730 return (-1);
9733 * Check whether it is a regular file and not a symbolic
9734 * link. Its link count should be 1. The owner should be
9735 * root and the file should be empty.
9737 if (!S_ISREG(sbuf1.st_mode) ||
9738 sbuf1.st_nlink != 1 ||
9739 sbuf1.st_uid != 0 ||
9740 sbuf1.st_size != 0) {
9741 ipqos_msg(MT_ERROR, gettext("Bad lock file %s.\n"),
9742 IPQOS_CONF_LOCK_FILE);
9743 return (-1);
9745 if ((fd = open(IPQOS_CONF_LOCK_FILE, O_CREAT|O_RDWR,
9746 S_IRUSR|S_IWUSR)) == -1) {
9747 ipqos_msg(MT_ENOSTR,
9748 gettext("Cannot open lock file %s"),
9749 IPQOS_CONF_LOCK_FILE);
9750 return (-1);
9753 /* Check whether we opened the file that we lstat()ed. */
9754 if (fstat(fd, &sbuf2) == -1) {
9755 ipqos_msg(MT_ENOSTR,
9756 gettext("Cannot fstat lock file %s\n"),
9757 IPQOS_CONF_LOCK_FILE);
9758 return (-1);
9760 if (sbuf1.st_dev != sbuf2.st_dev ||
9761 sbuf1.st_ino != sbuf2.st_ino) {
9762 /* File changed after we did the lstat() above */
9763 ipqos_msg(MT_ERROR, gettext("Bad lock file %s.\n"),
9764 IPQOS_CONF_LOCK_FILE);
9765 return (-1);
9768 if (lockf(fd, F_LOCK, 0) == -1) {
9769 ipqos_msg(MT_ENOSTR, "lockf");
9770 return (-1);
9772 return (fd);
9776 * print the current kernel configuration out to stdout. If viewall
9777 * is set this causes more verbose configuration listing including
9778 * showing objects we didn't create, each instance of a mhome filter,
9779 * etc.. see printaction().
9780 * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCES.
9783 static int
9784 viewconf(int viewall)
9787 ipqos_conf_action_t *conf = NULL;
9788 ipqos_conf_action_t *act;
9789 int ret;
9791 IPQOSCDBG0(L0, "In viewconf\n");
9793 /* get kernel configuration */
9795 ret = readkconf(&conf);
9796 if (ret != IPQOS_CONF_SUCCESS) {
9797 return (IPQOS_CONF_ERR);
9800 /* write out format version */
9802 if (conf != NULL) {
9803 (void) fprintf(stdout, "%s %d.%d\n\n", IPQOS_FMT_VERSION_STR,
9804 IPQOS_CUR_FMT_MAJOR_VER, IPQOS_CUR_FMT_MINOR_VER);
9807 /* print each of the actions in the kernel config to stdout */
9809 for (act = conf; act != NULL; act = act->next) {
9810 ret = printaction(stdout, act, viewall, 0);
9811 if (ret != IPQOS_CONF_SUCCESS) {
9812 free_actions(conf);
9813 return (ret);
9815 (void) fprintf(stdout, "\n");
9818 free_actions(conf);
9820 return (IPQOS_CONF_SUCCESS);
9825 * debug function that reads the config file and prints it out after
9826 * interpreting to stdout.
9828 #ifdef _IPQOS_CONF_DEBUG
9829 static int
9830 viewcfile(char *cfile)
9833 ipqos_conf_action_t *conf;
9834 ipqos_conf_action_t *act;
9835 int res;
9836 FILE *ifp;
9837 int viewall = 1;
9839 IPQOSCDBG0(L0, "In viewcfile\n");
9840 ifp = fopen(cfile, "r");
9841 if (ifp == NULL) {
9842 ipqos_msg(MT_ERROR, gettext("Opening file %s for read: %s.\n"),
9843 cfile, strerror(errno));
9844 return (IPQOS_CONF_ERR);
9847 res = readconf(ifp, &conf);
9848 if (res != IPQOS_CONF_SUCCESS) {
9849 free(ifp);
9850 return (IPQOS_CONF_ERR);
9853 /* print each of the actions in the kernel config to stdout */
9854 for (act = conf; act != NULL; act = act->next) {
9855 res = printaction(stdout, act, viewall, 0);
9856 if (res != IPQOS_CONF_SUCCESS) {
9857 free(ifp);
9858 return (res);
9861 (void) fprintf(stdout, "\n");
9864 (void) fprintf(stdout, "\n");
9867 return (IPQOS_CONF_SUCCESS);
9869 #endif /* _IPQOS_CONF_DEBUG */
9871 static void
9872 usage(void)
9874 (void) fprintf(stderr, gettext("usage:\n"
9875 "\tipqosconf [-sv] -a file|-\n"
9876 "\tipqosconf -c\n"
9877 "\tipqosconf -l\n"
9878 "\tipqosconf -L\n"
9879 "\tipqosconf -f\n"));
9883 main(int argc, char *argv[])
9886 int c;
9887 char *ifile = NULL;
9888 int args;
9889 int ret;
9890 int cmd;
9891 int viewall = 0;
9892 int lfp;
9894 /* init global flags */
9895 use_syslog = verbose = 0;
9897 /* init current line number */
9898 lineno = 0;
9900 /* setup internationalisation */
9902 (void) setlocale(LC_ALL, "");
9903 #if !defined(TEXT_DOMAIN)
9904 #define TEXT_DOMAIN "SYS_TEST"
9905 #endif
9906 (void) textdomain(TEXT_DOMAIN);
9908 /* setup syslog parameters */
9909 openlog("ipqosconf", 0, LOG_USER);
9911 args = 0;
9913 /* enable debug options */
9915 #ifdef _IPQOS_CONF_DEBUG
9916 #define DBGOPTS "rz:"
9917 #else
9918 #define DBGOPTS
9919 #endif /* _IPQOS_CONF_DEBUG */
9921 while ((c = getopt(argc, argv, "sca:vflL" DBGOPTS)) != EOF) {
9922 switch (c) {
9923 #ifdef _IPQOS_CONF_DEBUG
9924 case 'z':
9925 cmd = -1;
9926 ifile = optarg;
9927 if (*ifile == '\0') {
9928 usage();
9929 exit(1);
9931 args++;
9932 break;
9933 case 'r':
9934 force_rback++;
9935 break;
9936 #endif /* _IPQOS_CONF_DEBUG */
9937 case 'c':
9938 cmd = IPQOS_CONF_COMMIT;
9939 args++;
9940 break;
9941 case 'a':
9942 cmd = IPQOS_CONF_APPLY;
9943 ifile = optarg;
9944 if (*ifile == '\0') {
9945 usage();
9946 exit(1);
9948 args++;
9949 break;
9950 case 'f':
9951 cmd = IPQOS_CONF_FLUSH;
9952 args++;
9953 break;
9954 case 'l':
9955 cmd = IPQOS_CONF_VIEW;
9956 args++;
9957 break;
9958 case 'L':
9959 cmd = IPQOS_CONF_VIEW;
9960 viewall++;
9961 args++;
9962 break;
9963 case 'v':
9964 verbose++;
9965 break;
9966 case 's':
9967 use_syslog++;
9968 break;
9969 case '?':
9970 usage();
9971 return (1);
9976 * dissallow non-option args, > 1 cmd args and syslog/verbose flags set
9977 * for anything but apply.
9979 if (optind != argc || args > 1 ||
9980 use_syslog && cmd != IPQOS_CONF_APPLY ||
9981 verbose && cmd != IPQOS_CONF_APPLY) {
9982 usage();
9983 exit(1);
9986 /* if no cmd option then show config */
9988 if (args == 0) {
9989 cmd = IPQOS_CONF_VIEW;
9992 /* stop concurrent ipqosconf invocations */
9993 lfp = lock();
9994 if (lfp == -1) {
9995 exit(1);
9998 switch (cmd) {
9999 #ifdef _IPQOS_CONF_DEBUG
10000 case -1:
10001 ret = viewcfile(ifile);
10002 break;
10003 #endif /* _IPQOS_CONF_DEBUG */
10004 case IPQOS_CONF_APPLY:
10005 ret = applyconf(ifile);
10006 break;
10007 case IPQOS_CONF_COMMIT:
10008 ret = commitconf();
10009 break;
10010 case IPQOS_CONF_VIEW:
10011 ret = viewconf(viewall);
10012 break;
10013 case IPQOS_CONF_FLUSH:
10014 ret = flushconf();
10015 break;
10018 (void) unlock(lfp);
10020 return (ret);