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