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