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