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