6659 nvlist_free(NULL) is a no-op
[illumos-gate.git] / usr / src / cmd / cmd-inet / usr.sbin / ipqosconf / ipqosconf.c
blob975adc4a7b9f6810eb3a7785475a231bca1d754c
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
20 * CDDL HEADER END
23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 /* enable debug output and some debug asserts */
28 #undef _IPQOS_CONF_DEBUG
30 #include <stdlib.h>
31 #include <unistd.h>
32 #include <libintl.h>
33 #include <signal.h>
34 #include <strings.h>
35 #include <sys/nvpair.h>
36 #include <stdio.h>
37 #include <netinet/in.h>
38 #include <arpa/inet.h>
39 #include <ctype.h>
40 #include <sys/socket.h>
41 #include <limits.h>
42 #include <netdb.h>
43 #include <fcntl.h>
44 #include <sys/types.h>
45 #include <sys/stat.h>
46 #include <errno.h>
47 #include <libipp.h>
48 #include <ipp/ipp_config.h>
49 #include <ipp/ipgpc/ipgpc.h>
50 #include <ipp/ipp.h>
51 #ifdef _IPQOS_CONF_DEBUG
52 #include <assert.h>
53 #endif
54 #include <sys/sockio.h>
55 #include <syslog.h>
56 #include <stdarg.h>
57 #include <libintl.h>
58 #include <locale.h>
59 #include <pwd.h>
60 #include "ipqosconf.h"
62 #if defined(_IPQOS_CONF_DEBUG)
64 /* debug level */
65 static int ipqosconf_dbg_flgs =
68 RBK |
69 MHME |
70 KRET |
71 DIFF |
72 APPLY |
73 L2 |
74 L1 |
75 L0 |
80 #define IPQOSCDBG0(lvl, x)\
81 if (lvl & ipqosconf_dbg_flgs)\
82 (void) fprintf(stderr, x)
84 #define IPQOSCDBG1(lvl, x, y)\
85 if (lvl & ipqosconf_dbg_flgs)\
86 (void) fprintf(stderr, x, y)
88 #define IPQOSCDBG2(lvl, x, y, z)\
89 if (lvl & ipqosconf_dbg_flgs)\
90 (void) fprintf(stderr, x, y, z)
92 #define IPQOSCDBG3(lvl, x, y, z, a)\
93 if (lvl & ipqosconf_dbg_flgs)\
94 (void) fprintf(stderr, x, y, z, a)
96 #define IPQOSCDBG4(lvl, x, y, z, a, b)\
97 if (lvl & ipqosconf_dbg_flgs)\
98 (void) fprintf(stderr, x, y, z, a, b)
100 #define IPQOSCDBG5(lvl, x, y, z, a, b, c)\
101 if (lvl & ipqosconf_dbg_flgs)\
102 (void) fprintf(stderr, x, y, z, a, b, c)
104 #else /* defined(_IPQOS_CONF_DEBUG) && !defined(lint) */
106 #define IPQOSCDBG0(lvl, x)
107 #define IPQOSCDBG1(lvl, x, y)
108 #define IPQOSCDBG2(lvl, x, y, z)
109 #define IPQOSCDBG3(lvl, x, y, z, a)
110 #define IPQOSCDBG4(lvl, x, y, z, a, b)
111 #define IPQOSCDBG5(lvl, x, y, z, a, b, c)
113 #endif /* defined(_IPQOS_CONF_DEBUG) */
117 /* function prototypes */
119 static int modify_params(char *, nvlist_t **, int, boolean_t);
120 static int add_class(char *, char *, int, boolean_t, char *);
121 static int modify_class(char *, char *, int, boolean_t, char *,
122 enum ipp_flags);
123 static int remove_class(char *, char *, int, enum ipp_flags);
124 static int add_filter(char *, ipqos_conf_filter_t *, int);
125 static int modify_filter(char *, ipqos_conf_filter_t *, int);
126 static int remove_filter(char *, char *, int, int);
127 static boolean_t arrays_equal(int *, int *, uint32_t);
128 static int diffclass(ipqos_conf_class_t *, ipqos_conf_class_t *);
129 static int diffparams(ipqos_conf_params_t *, ipqos_conf_params_t *, char *);
130 static int difffilter(ipqos_conf_filter_t *, ipqos_conf_filter_t *, char *);
131 static int add_filters(ipqos_conf_filter_t *, char *, int, boolean_t);
132 static int add_classes(ipqos_conf_class_t *, char *, int, boolean_t);
133 static int modify_items(ipqos_conf_action_t *);
134 static int add_items(ipqos_conf_action_t *, boolean_t);
135 static int add_item(ipqos_conf_action_t *, boolean_t);
136 static int remove_items(ipqos_conf_action_t *, boolean_t);
137 static int remove_item(ipqos_conf_action_t *, boolean_t);
138 static int undo_modifys(ipqos_conf_action_t *, ipqos_conf_action_t *);
139 static int applydiff(ipqos_conf_action_t *, ipqos_conf_action_t *);
140 static int rollback(ipqos_conf_action_t *, ipqos_conf_action_t *);
141 static int rollback_recover(ipqos_conf_action_t *);
142 static ipqos_conf_class_t *classexist(char *, ipqos_conf_class_t *);
143 static ipqos_conf_filter_t *filterexist(char *, int, ipqos_conf_filter_t *);
144 static ipqos_conf_action_t *actionexist(char *, ipqos_conf_action_t *);
145 static int diffnvlists(nvlist_t *, nvlist_t *, char *, int *, place_t);
146 static int diffaction(ipqos_conf_action_t *, ipqos_conf_action_t *);
147 static int diffconf(ipqos_conf_action_t *, ipqos_conf_action_t *);
148 static int readllong(char *, long long *, char **);
149 static int readuint8(char *, uint8_t *, char **);
150 static int readuint16(char *, uint16_t *, char **);
151 static int readint16(char *, int16_t *, char **);
152 static int readint32(char *, int *, char **);
153 static int readuint32(char *, uint32_t *, char **);
154 static int readbool(char *, boolean_t *);
155 static void setmask(int, in6_addr_t *, int);
156 static int readtoken(FILE *, char **);
157 static nvpair_t *find_nvpair(nvlist_t *, char *);
158 static char *prepend_module_name(char *, char *);
159 static int readnvpair(FILE *, FILE *, nvlist_t **, nvpair_t **,
160 ipqos_nvtype_t *, place_t, char *);
161 static int add_aref(ipqos_conf_act_ref_t **, char *, char *);
162 static int readparams(FILE *, FILE *, char *, ipqos_conf_params_t *);
163 static int readclass(FILE *, char *, ipqos_conf_class_t **, char **, int);
164 static int readfilter(FILE *, FILE *, char *, ipqos_conf_filter_t **, char **,
165 int);
166 static FILE *validmod(char *, int *);
167 static int readaction(FILE *, ipqos_conf_action_t **);
168 static int actions_unique(ipqos_conf_action_t *, char **);
169 static int validconf(ipqos_conf_action_t *, int);
170 static int readconf(FILE *, ipqos_conf_action_t **);
171 static int flush(boolean_t *);
172 static int atomic_flush(boolean_t);
173 static int flushconf();
174 static int writeconf(ipqos_conf_action_t *, char *);
175 static int commitconf();
176 static int applyconf(char *ifile);
177 static int block_all_signals();
178 static int restore_all_signals();
179 static int unlock(int fd);
180 static int lock();
181 static int viewconf(int);
182 static void usage();
183 static int valid_name(char *);
184 static int in_cycle(ipqos_conf_action_t *);
185 static int readtype(FILE *, char *, char *, ipqos_nvtype_t *, str_val_nd_t **,
186 char *, boolean_t, place_t *);
187 static int read_int_array_info(char *, str_val_nd_t **, uint32_t *, int *,
188 int *, char *);
189 static str_val_nd_t *read_enum_nvs(char *, char *);
190 static int add_str_val_entry(str_val_nd_t **, char *, uint32_t);
191 static void free_str_val_entrys(str_val_nd_t *);
192 static void get_str_val_value_range(str_val_nd_t *, int *, int *);
193 static int read_enum_value(FILE *, char *, str_val_nd_t *, uint32_t *);
194 static int read_mapped_values(FILE *, nvlist_t **, char *, char *,
195 int);
196 static int read_int_array(FILE *, char *, int **, uint32_t, int, int,
197 str_val_nd_t *);
198 static int str_val_list_lookup(str_val_nd_t *, char *, uint32_t *);
199 static int parse_kparams(char *, ipqos_conf_params_t *, nvlist_t *);
200 static int parse_kclass(ipqos_conf_class_t *, nvlist_t *);
201 static int parse_kfilter(ipqos_conf_filter_t *, nvlist_t *);
202 static int parse_kaction(nvlist_t *, ipqos_actinfo_prm_t *);
203 static int readkconf(ipqos_conf_action_t **);
204 static void print_int_array(FILE *, int *, uint32_t, int, int, str_val_nd_t *,
205 int);
206 static void printrange(FILE *fp, uint32_t, uint32_t);
207 static void printenum(FILE *, uint32_t, str_val_nd_t *);
208 static void printproto(FILE *, uint8_t);
209 static void printport(FILE *, uint16_t);
210 static int printnvlist(FILE *, char *, nvlist_t *, int, ipqos_conf_filter_t *,
211 int, place_t);
212 static int virtual_action(char *);
213 static void free_arefs(ipqos_conf_act_ref_t *);
214 static void print_action_nm(FILE *, char *);
215 static int add_orig_ipqosconf(nvlist_t *);
216 static char *get_originator_nm(uint32_t);
217 static void mark_classes_filters_new(ipqos_conf_action_t *);
218 static void mark_classes_filters_del(ipqos_conf_action_t *);
219 static void mark_config_new(ipqos_conf_action_t *);
220 static int printifname(FILE *, int);
221 static int readifindex(char *, int *);
222 static void cleanup_string_table(char **, int);
223 static int domultihome(ipqos_conf_filter_t *, ipqos_conf_filter_t **,
224 boolean_t);
225 static int dup_filter(ipqos_conf_filter_t *, ipqos_conf_filter_t **, int, int,
226 void *, void *, int);
227 static void free_actions(ipqos_conf_action_t *);
228 static ipqos_conf_filter_t *alloc_filter();
229 static void free_filter(ipqos_conf_filter_t *);
230 static int read_curl_begin(FILE *);
231 static ipqos_conf_class_t *alloc_class(void);
232 static int diffclasses(ipqos_conf_action_t *old, ipqos_conf_action_t *new);
233 static int difffilters(ipqos_conf_action_t *old, ipqos_conf_action_t *new);
234 static int dup_class(ipqos_conf_class_t *src, ipqos_conf_class_t **dst);
235 static int add_action(ipqos_conf_action_t *act);
236 static int masktocidr(int af, in6_addr_t *mask);
237 static int read_perm_items(int, FILE *, char *, char ***, int *);
238 static int in_string_table(char *stable[], int size, char *string);
239 static void list_end(ipqos_list_el_t **listp, ipqos_list_el_t ***lendpp);
240 static void add_to_list(ipqos_list_el_t **listp, ipqos_list_el_t *el);
241 static int read_cfile_ver(FILE *, char *);
242 static char *quote_ws_string(const char *);
243 static int read_tfile_ver(FILE *, char *, char *);
244 static int ver_str_to_int(char *);
245 static void printuser(FILE *fp, uid_t uid);
246 static int readuser(char *str, uid_t *uid);
249 * macros to call list functions with the more complex list element type
250 * cast to the skeletal type iqpos_list_el_t.
252 #define LIST_END(list, end)\
253 list_end((ipqos_list_el_t **)list, (ipqos_list_el_t ***)end)
254 #define ADD_TO_LIST(list, el)\
255 add_to_list((ipqos_list_el_t **)list, (ipqos_list_el_t *)el)
258 * Macros to produce a quoted string containing the value of a
259 * preprocessor macro. For example, if SIZE is defined to be 256,
260 * VAL2STR(SIZE) is "256". This is used to construct format
261 * strings for scanf-family functions below.
263 #define QUOTE(x) #x
264 #define VAL2STR(x) QUOTE(x)
267 /* globals */
269 /* table of supported parameter types and enum value */
270 static str_val_t nv_types[] = {
271 {"uint8", IPQOS_DATA_TYPE_UINT8},
272 {"int16", IPQOS_DATA_TYPE_INT16},
273 {"uint16", IPQOS_DATA_TYPE_UINT16},
274 {"int32", IPQOS_DATA_TYPE_INT32},
275 {"uint32", IPQOS_DATA_TYPE_UINT32},
276 {"boolean", IPQOS_DATA_TYPE_BOOLEAN},
277 {"string", IPQOS_DATA_TYPE_STRING},
278 {"action", IPQOS_DATA_TYPE_ACTION},
279 {"address", IPQOS_DATA_TYPE_ADDRESS},
280 {"port", IPQOS_DATA_TYPE_PORT},
281 {"protocol", IPQOS_DATA_TYPE_PROTO},
282 {"enum", IPQOS_DATA_TYPE_ENUM},
283 {"ifname", IPQOS_DATA_TYPE_IFNAME},
284 {"mindex", IPQOS_DATA_TYPE_M_INDEX},
285 {"int_array", IPQOS_DATA_TYPE_INT_ARRAY},
286 {"user", IPQOS_DATA_TYPE_USER},
287 {"", 0}
290 /* table of name to id mappings for originator field */
292 static str_val_t originators[] = {
293 {IPP_CONFIG_NAME_PERMANENT, IPP_CONFIG_PERMANENT},
294 {IPP_CONFIG_NAME_IPQOSCONF, IPP_CONFIG_IPQOSCONF},
295 {IPP_CONFIG_NAME_FTPCL, IPP_CONFIG_FTPCL},
296 {"", -1}
299 /* current parse line */
300 static int lineno;
302 /* verbose output flag */
303 static int verbose;
305 /* use syslog for msg reporting flag */
306 static int use_syslog;
308 #ifdef _IPQOS_CONF_DEBUG
310 * flag used to indicate that a rollback should be carried out regardless.
311 * Only settable during debug.
313 static int force_rback = 0;
314 #endif /* _IPQOS_CONF_DEBUG */
317 * delivers messages to either syslog or stderr, dependant upon the
318 * the state of the flags use_syslog and verbose. The type
319 * of the msg as given in msg_type is indicated in the output msg.
321 * valid message types are:
322 * o MT_ERROR (standard error message)
323 * o MT_ENOSTR (error message with system error string appended)
324 * o MT_WARNING (warning message)
325 * o MT_LOG (logging message)
327 * Log messages only go to syslog. Warning messages only go to stderr
328 * and only when the verbose flag is set. All other messages go by default
329 * to the console; to syslog if syslog flag set, and to both if both
330 * syslog and verbose are set.
333 /*PRINTFLIKE2*/
334 static void
335 ipqos_msg(enum msg_type msgt, char *format, ...)
337 va_list ap;
338 char str_buf[IPQOS_MSG_BUF_SZ];
339 char fmt_buf[IPQOS_MSG_BUF_SZ];
340 char *cp;
342 IPQOSCDBG0(L1, "In ipqos_msg:\n");
344 va_start(ap, format);
347 * send msgs to syslog if use_syslog set (except warning msgs),
348 * or a log msg.
350 if ((use_syslog && (msgt != MT_WARNING)) || msgt == MT_LOG) {
352 /* fill in format string */
353 (void) vsnprintf(str_buf, IPQOS_MSG_BUF_SZ, format, ap);
356 * print message to syslog with appropriate severity
358 if (msgt == MT_ERROR) {
359 syslog(LOG_ERR, str_buf);
360 } else if (msgt == MT_LOG) {
361 syslog(LOG_INFO, str_buf);
363 * for errno message type suffix with %m for syslog to
364 * interpret.
366 } else if (msgt == MT_ENOSTR) {
368 * remove any newline in message parameter.
369 * syslog will reapply a newline for us later.
371 if ((cp = strchr(str_buf, '\n')) != NULL)
372 *cp = '\0';
373 (void) strlcat(str_buf, ": %m", IPQOS_MSG_BUF_SZ);
374 syslog(LOG_ERR, str_buf);
379 * send msgs to stderr if use_syslog not set (except log msgs), or
380 * if verbose set.
382 if ((!use_syslog && (msgt != MT_LOG)) || (verbose)) {
385 * prefix message with appropriate severity string
387 if (msgt == MT_ERROR) {
388 (void) strlcpy(fmt_buf, gettext("Error: "),
389 IPQOS_MSG_BUF_SZ);
390 } else if (msgt == MT_WARNING) {
391 if (!verbose) { /* don't show warn msg if !verbose */
392 va_end(ap);
393 return;
395 (void) strlcpy(fmt_buf, gettext("Warning: "),
396 IPQOS_MSG_BUF_SZ);
397 } else if (msgt == MT_ENOSTR) {
398 (void) strlcpy(fmt_buf, gettext("Error: "),
399 IPQOS_MSG_BUF_SZ);
400 } else if (msgt == MT_LOG) {
401 (void) strlcpy(fmt_buf, gettext("Notice: "),
402 IPQOS_MSG_BUF_SZ);
404 (void) strlcat(fmt_buf, format, IPQOS_MSG_BUF_SZ);
407 * for errno message type suffix message with errno string
409 if (msgt == MT_ENOSTR) {
411 * get rid of any newline in passed message.
412 * we'll apply another later.
414 if ((cp = strchr(fmt_buf, '\n')) != NULL)
415 *cp = '\0';
416 (void) strlcat(fmt_buf, ": ", IPQOS_MSG_BUF_SZ);
417 (void) strlcat(fmt_buf, strerror(errno),
418 IPQOS_MSG_BUF_SZ);
422 * append a newline to message if not one already.
424 if ((cp = strchr(fmt_buf, '\n')) == NULL)
425 (void) strlcat(fmt_buf, "\n", IPQOS_MSG_BUF_SZ);
427 (void) vfprintf(stderr, fmt_buf, ap);
430 va_end(ap);
433 /* **************** kernel filter/class/params manipulation fns *********** */
437 * modify the kernel parameters of the action action_nm using the nvlist
438 * parameter nvl and setting the stats according to stats_enable.
439 * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCES.
442 static int
443 modify_params(
444 char *action_name,
445 nvlist_t **nvl,
446 int module_version,
447 boolean_t stats_enable)
450 int res;
451 int created = 0;
453 IPQOSCDBG1(APPLY, "In modify_params: action: %s\n", action_name);
455 /* create nvlist if NULL */
456 if (*nvl == NULL) {
457 created++;
458 res = nvlist_alloc(nvl, NV_UNIQUE_NAME, 0);
459 if (res != 0) {
460 ipqos_msg(MT_ENOSTR, "nvlist_alloc");
461 return (IPQOS_CONF_ERR);
465 /* add params modify config type */
466 res = nvlist_add_byte(*nvl, IPP_CONFIG_TYPE, IPP_SET);
467 if (res != 0) {
468 ipqos_msg(MT_ENOSTR, "nvlist_add_byte");
469 goto fail;
473 * add module version
475 if (nvlist_add_uint32(*nvl, IPP_MODULE_VERSION,
476 (uint32_t)module_version) != 0) {
477 ipqos_msg(MT_ENOSTR, "nvlist_add_uint32");
478 goto fail;
481 /* add stats_enable */
482 res = nvlist_add_uint32(*nvl, IPP_ACTION_STATS_ENABLE,
483 (uint32_t)stats_enable);
484 if (res != 0) {
485 ipqos_msg(MT_ENOSTR, "nvlist_add_uint32");
486 goto fail;
489 /* add ipqosconf as originator */
490 res = add_orig_ipqosconf(*nvl);
491 if (res != IPQOS_CONF_SUCCESS) {
492 goto fail;
495 /* call lib to do modify */
496 res = ipp_action_modify(action_name, nvl, 0);
497 if (res != 0) {
499 /* invalid parameters */
501 if (errno == EINVAL) {
502 ipqos_msg(MT_ERROR,
503 gettext("Invalid parameters for action %s.\n"),
504 action_name);
507 } else if (errno == ENOENT) {
508 ipqos_msg(MT_ERROR,
509 gettext("Mandatory parameter missing for "
510 "action %s.\n"), action_name);
513 } else { /* unexpected error */
514 ipqos_msg(MT_ERROR, gettext("Failed to modify action "
515 "%s parameters: %s.\n"), action_name,
516 strerror(errno));
519 goto fail;
522 return (IPQOS_CONF_SUCCESS);
523 fail:
524 if (created && *nvl != NULL) {
525 nvlist_free(*nvl);
526 *nvl = NULL;
528 return (IPQOS_CONF_ERR);
532 * add a class to the kernel action action_name called class_name with
533 * stats set according to stats_enable and the first action set to
534 * first_action.
535 * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCES.
537 static int
538 add_class(
539 char *action_name,
540 char *class_name,
541 int module_version,
542 boolean_t stats_enable,
543 char *first_action)
546 nvlist_t *nvl;
548 IPQOSCDBG4(APPLY, "add_class: action: %s, class: %s, "
549 "first_action: %s, stats: %s\n", action_name, class_name,
550 first_action, (stats_enable == B_TRUE ? "true" : "false"));
553 /* create nvlist */
554 if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) {
555 ipqos_msg(MT_ENOSTR, "nvlist_alloc");
556 return (IPQOS_CONF_ERR);
559 /* add 'add class' config type */
560 if (nvlist_add_byte(nvl, IPP_CONFIG_TYPE, CLASSIFIER_ADD_CLASS) != 0) {
561 ipqos_msg(MT_ENOSTR, "nvlist_add_byte");
562 goto fail;
566 * add module version
568 if (nvlist_add_uint32(nvl, IPP_MODULE_VERSION,
569 (uint32_t)module_version) != 0) {
570 ipqos_msg(MT_ENOSTR, "nvlist_add_uint32");
571 goto fail;
574 /* add class name */
575 if (nvlist_add_string(nvl, CLASSIFIER_CLASS_NAME, class_name) != 0) {
576 ipqos_msg(MT_ENOSTR, "nvlist_add_string");
577 goto fail;
580 /* add next action */
581 if (nvlist_add_string(nvl, CLASSIFIER_NEXT_ACTION, first_action) != 0) {
582 ipqos_msg(MT_ENOSTR, "nvlist_add_string");
583 goto fail;
586 /* add stats_enable */
587 if (nvlist_add_uint32(nvl, CLASSIFIER_CLASS_STATS_ENABLE,
588 (uint32_t)stats_enable) != 0) {
589 ipqos_msg(MT_ENOSTR, "nvlist_add_uint32");
590 goto fail;
593 /* add ipqosconf as originator */
594 if (add_orig_ipqosconf(nvl) != IPQOS_CONF_SUCCESS) {
595 goto fail;
598 /* call lib to do modify */
599 if (ipp_action_modify(action_name, &nvl, 0) != 0) {
601 /* ipgpc max classes */
603 if (errno == ENOSPC &&
604 strcmp(action_name, IPGPC_CLASSIFY) == 0) {
605 ipqos_msg(MT_ERROR,
606 gettext("Max number of classes reached in %s.\n"),
607 IPGPC_NAME);
609 /* other errors */
611 } else {
612 ipqos_msg(MT_ERROR,
613 gettext("Failed to create class %s in action "
614 "%s: %s.\n"), class_name, action_name,
615 strerror(errno));
618 goto fail;
621 return (IPQOS_CONF_SUCCESS);
622 fail:
623 nvlist_free(nvl);
624 return (IPQOS_CONF_ERR);
629 * modify the class in the kernel action action_name called class_name with
630 * stats set according to stats_enable and the first action set to
631 * first_action.
632 * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCES.
634 static int
635 modify_class(
636 char *action_name,
637 char *class_name,
638 int module_version,
639 boolean_t stats_enable,
640 char *first_action,
641 enum ipp_flags flags)
644 nvlist_t *nvl;
646 IPQOSCDBG5(APPLY, "modify_class: action: %s, class: %s, first: %s, "
647 "stats: %s, flags: %x\n", action_name, class_name, first_action,
648 stats_enable == B_TRUE ? "true" : "false", flags);
651 /* create nvlist */
652 if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) {
653 ipqos_msg(MT_ENOSTR, "nvlist_alloc");
654 return (IPQOS_CONF_ERR);
657 /* add 'modify class' config type */
658 if (nvlist_add_byte(nvl, IPP_CONFIG_TYPE, CLASSIFIER_MODIFY_CLASS) !=
659 0) {
660 ipqos_msg(MT_ENOSTR, "nvlist_add_byte");
661 goto fail;
665 * add module version
667 if (nvlist_add_uint32(nvl, IPP_MODULE_VERSION,
668 (uint32_t)module_version) != 0) {
669 ipqos_msg(MT_ENOSTR, "nvlist_add_uint32");
670 goto fail;
673 /* add class name */
674 if (nvlist_add_string(nvl, CLASSIFIER_CLASS_NAME, class_name) != 0) {
675 ipqos_msg(MT_ENOSTR, "nvlist_add_string");
676 goto fail;
679 /* add next action */
680 if (nvlist_add_string(nvl, CLASSIFIER_NEXT_ACTION, first_action) != 0) {
681 ipqos_msg(MT_ENOSTR, "nvlist_add_string");
682 goto fail;
685 /* add stats enable */
686 if (nvlist_add_uint32(nvl, CLASSIFIER_CLASS_STATS_ENABLE,
687 (uint32_t)stats_enable) != 0) {
688 ipqos_msg(MT_ENOSTR, "nvlist_add_uint32");
689 goto fail;
692 /* add originator ipqosconf */
693 if (add_orig_ipqosconf(nvl) != IPQOS_CONF_SUCCESS) {
694 goto fail;
697 /* call lib to do modify */
698 if (ipp_action_modify(action_name, &nvl, flags) != 0) {
700 /* generic error message */
702 ipqos_msg(MT_ERROR,
703 gettext("Modifying class %s in action %s failed: %s.\n"),
704 class_name, action_name, strerror(errno));
706 goto fail;
709 return (IPQOS_CONF_SUCCESS);
710 fail:
711 nvlist_free(nvl);
712 return (IPQOS_CONF_ERR);
716 * removes the class class_name from the kernel action action_name. The
717 * flags argument can currently be set to IPP_ACTION_DESTROY which will
718 * result in the action this class references being destroyed.
719 * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCES.
721 static int
722 remove_class(
723 char *action_name,
724 char *class_name,
725 int module_version,
726 enum ipp_flags flags)
729 nvlist_t *nvl;
731 IPQOSCDBG3(APPLY, "remove_class: action: %s, class: %s, "
732 "flags: %x\n", action_name, class_name, flags);
734 /* allocate nvlist */
735 if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) {
736 ipqos_msg(MT_ENOSTR, "nvlist_alloc");
737 return (IPQOS_CONF_ERR);
740 /* add 'remove class' config type */
741 if (nvlist_add_byte(nvl, IPP_CONFIG_TYPE, CLASSIFIER_REMOVE_CLASS) !=
742 0) {
743 ipqos_msg(MT_ENOSTR, "nvlist_add_byte");
744 goto fail;
748 * add module version
750 if (nvlist_add_uint32(nvl, IPP_MODULE_VERSION,
751 (uint32_t)module_version) != 0) {
752 ipqos_msg(MT_ENOSTR, "nvlist_add_uint32");
753 goto fail;
756 /* add class name */
757 if (nvlist_add_string(nvl, CLASSIFIER_CLASS_NAME, class_name) != 0) {
758 ipqos_msg(MT_ENOSTR, "nvlist_add_string");
759 goto fail;
762 if (ipp_action_modify(action_name, &nvl, flags) != 0) {
764 /* generic error message */
766 ipqos_msg(MT_ERROR,
767 gettext("Removing class %s in action %s failed: %s.\n"),
768 class_name, action_name, strerror(errno));
770 goto fail;
773 return (IPQOS_CONF_SUCCESS);
774 fail:
775 nvlist_free(nvl);
776 return (IPQOS_CONF_ERR);
780 * add the filter flt to the kernel action named action_name.
781 * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCES.
783 static int
784 add_filter(
785 char *action_name,
786 ipqos_conf_filter_t *flt,
787 int module_version)
790 nvlist_t *nvl = flt->nvlist;
791 char ipvsbuf[IPQOS_INT_STR_LEN];
793 IPQOSCDBG4(APPLY, "add_filter: action: %s, filter: %s, "
794 "instance: %d, class: %s\n", action_name, flt->name,
795 flt->instance, flt->class_name);
798 /* add 'add filter' config type to filter nvlist */
799 if (nvlist_add_byte(nvl, IPP_CONFIG_TYPE, CLASSIFIER_ADD_FILTER) != 0) {
800 ipqos_msg(MT_ENOSTR, "nvlist_add_byte");
801 return (IPQOS_CONF_ERR);
805 * add module version
807 if (nvlist_add_uint32(nvl, IPP_MODULE_VERSION,
808 (uint32_t)module_version) != 0) {
809 ipqos_msg(MT_ENOSTR, "nvlist_add_uint32");
810 return (IPQOS_CONF_ERR);
813 /* add filter name to nvlist */
814 if (nvlist_add_string(nvl, CLASSIFIER_FILTER_NAME, flt->name) != 0) {
815 ipqos_msg(MT_ENOSTR, "nvlist_add_string");
816 return (IPQOS_CONF_ERR);
819 /* add class name to nvlist */
820 if (nvlist_add_string(nvl, CLASSIFIER_CLASS_NAME, flt->class_name) !=
821 0) {
822 ipqos_msg(MT_ENOSTR, "nvlist_add_string");
823 return (IPQOS_CONF_ERR);
826 /* add ipqosconf as originator to nvlist */
827 if (add_orig_ipqosconf(nvl) != IPQOS_CONF_SUCCESS) {
828 return (IPQOS_CONF_ERR);
831 /* add ipgpc specific nv entrys */
832 if (strcmp(action_name, IPGPC_CLASSIFY) == 0) {
834 /* add src and dst nodes to nvlist if present */
836 if (flt->src_nd_name != NULL &&
837 nvlist_add_string(nvl, IPGPC_SADDR_HOSTNAME,
838 flt->src_nd_name) != 0) {
839 ipqos_msg(MT_ENOSTR, "nvlist_add_string");
840 return (IPQOS_CONF_ERR);
842 if (flt->dst_nd_name != NULL &&
843 nvlist_add_string(nvl, IPGPC_DADDR_HOSTNAME,
844 flt->dst_nd_name) != 0) {
845 ipqos_msg(MT_ENOSTR, "nvlist_add_string");
846 return (IPQOS_CONF_ERR);
850 * add ip_version to private list element if present.
851 * NOTE: this value is of only real use to ipqosconf so
852 * it is placed in this opaque private field.
854 if (flt->ip_versions != 0) {
855 (void) sprintf(ipvsbuf, "%d", flt->ip_versions);
856 if (nvlist_add_string(nvl, IPGPC_FILTER_PRIVATE,
857 ipvsbuf) != 0) {
858 ipqos_msg(MT_ENOSTR, "nvlist_add_string");
859 return (IPQOS_CONF_ERR);
863 /* add filter instance if present */
865 if (nvlist_add_int32(nvl, IPGPC_FILTER_INSTANCE,
866 flt->instance) != 0) {
867 ipqos_msg(MT_ENOSTR, "nvlist_add_int32");
868 return (IPQOS_CONF_ERR);
872 if (ipp_action_modify(action_name, &flt->nvlist, 0) != 0) {
874 /* invalid parameters */
876 if (errno == EINVAL) {
877 ipqos_msg(MT_ERROR,
878 gettext("Invalid/missing parameters for filter "
879 "%s in action %s.\n"), flt->name, action_name);
881 /* max ipgpc filters/classes */
883 } else if (errno == ENOSPC &&
884 strcmp(action_name, IPGPC_CLASSIFY) == 0) {
885 ipqos_msg(MT_ERROR, gettext("Max number of filters "
886 "reached in action %s.\n"), IPGPC_NAME);
888 /* anything other errnos */
889 } else {
890 ipqos_msg(MT_ERROR,
891 gettext("Failed to create filter %s in action "
892 "%s: %s.\n"), flt->name, action_name,
893 strerror(errno));
896 return (IPQOS_CONF_ERR);
899 return (IPQOS_CONF_SUCCESS);
904 * modify the filter flt in the kernel action named action_name.
905 * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCES.
907 static int
908 modify_filter(
909 char *action_name,
910 ipqos_conf_filter_t *flt,
911 int module_version)
914 nvlist_t *nvl = flt->nvlist;
915 char ipvsbuf[IPQOS_INT_STR_LEN];
917 IPQOSCDBG4(APPLY, "modify_filter: action: %s, filter: %s, "
918 "instance: %d, class: %s\n", action_name, flt->name,
919 flt->instance, flt->class_name);
921 /* show src address and dst address if present */
922 #ifdef _IPQOS_CONF_DEBUG
923 if (ipqosconf_dbg_flgs & APPLY) {
924 uint_t tmp;
925 in6_addr_t *add;
926 char st[100];
928 if (nvlist_lookup_uint32_array(nvl, IPGPC_SADDR,
929 (uint32_t **)&add, &tmp) == 0) {
930 (void) fprintf(stderr, "saddr: %s\n",
931 inet_ntop(AF_INET6, add, st, 100));
934 if (nvlist_lookup_uint32_array(nvl, IPGPC_DADDR,
935 (uint32_t **)&add, &tmp) == 0) {
936 (void) fprintf(stderr, "daddr: %s\n",
937 inet_ntop(AF_INET6, add, st, 100));
940 #endif /* _IPQOS_CONF_DEBUG */
942 /* add 'modify filter' config type to filters nvlist */
943 if (nvlist_add_byte(nvl, IPP_CONFIG_TYPE,
944 CLASSIFIER_MODIFY_FILTER) != 0) {
945 ipqos_msg(MT_ENOSTR, "nvlist_add_byte");
946 return (IPQOS_CONF_ERR);
950 * add module version
952 if (nvlist_add_uint32(nvl, IPP_MODULE_VERSION,
953 (uint32_t)module_version) != 0) {
954 ipqos_msg(MT_ENOSTR, "nvlist_add_uint32");
955 return (IPQOS_CONF_ERR);
958 /* add filter name to nvlist */
959 if (nvlist_add_string(nvl, CLASSIFIER_FILTER_NAME, flt->name) != 0) {
960 ipqos_msg(MT_ENOSTR, "nvlist_add_string");
961 return (IPQOS_CONF_ERR);
964 /* add class name to nvlist */
965 if (nvlist_add_string(nvl, CLASSIFIER_CLASS_NAME, flt->class_name) !=
966 0) {
967 ipqos_msg(MT_ENOSTR, "nvlist_add_string");
968 return (IPQOS_CONF_ERR);
971 /* add originator ipqosconf to nvlist */
972 if (add_orig_ipqosconf(nvl) != IPQOS_CONF_SUCCESS) {
973 return (IPQOS_CONF_ERR);
976 /* add ipgpc specific nvpairs */
977 if (strcmp(action_name, IPGPC_CLASSIFY) == 0) {
979 /* add src and dst nodes to nvlist if present */
981 if (flt->src_nd_name &&
982 nvlist_add_string(nvl, IPGPC_SADDR_HOSTNAME,
983 flt->src_nd_name) != 0) {
984 ipqos_msg(MT_ENOSTR, "nvlist_add_string");
985 return (IPQOS_CONF_ERR);
987 if (flt->dst_nd_name &&
988 nvlist_add_string(nvl, IPGPC_DADDR_HOSTNAME,
989 flt->dst_nd_name) != 0) {
990 ipqos_msg(MT_ENOSTR, "nvlist_add_string");
991 return (IPQOS_CONF_ERR);
995 * add ip_version to private list element if present.
996 * NOTE: this value is of only real use to ipqosconf so
997 * it is placed in this opaque private field.
999 if (flt->ip_versions != 0) {
1000 (void) sprintf(ipvsbuf, "%d", flt->ip_versions);
1001 if (nvlist_add_string(nvl, IPGPC_FILTER_PRIVATE,
1002 ipvsbuf) != 0) {
1003 ipqos_msg(MT_ENOSTR, "nvlist_add_string");
1004 return (IPQOS_CONF_ERR);
1008 /* add filter instance if present */
1010 if (nvlist_add_int32(nvl, IPGPC_FILTER_INSTANCE,
1011 flt->instance) != 0) {
1012 ipqos_msg(MT_ENOSTR, "nvlist_add_int32");
1013 return (IPQOS_CONF_ERR);
1017 if (ipp_action_modify(action_name, &flt->nvlist, 0) != 0) {
1019 /* invalid parameters */
1021 if (errno == EINVAL) {
1022 ipqos_msg(MT_ERROR, gettext("Missing/Invalid "
1023 "parameter for filter %s in action %s.\n"),
1024 flt->name, action_name);
1026 /* any other errnos */
1028 } else {
1029 ipqos_msg(MT_ERROR,
1030 gettext("Failed to modify filter %s in action %s: "
1031 "%s.\n"), flt->name, action_name, strerror(errno));
1034 return (IPQOS_CONF_ERR);
1037 return (IPQOS_CONF_SUCCESS);
1041 * remove the filter named filter_name instance number instance from the
1042 * kernel action action_name.
1043 * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCES.
1045 static int
1046 remove_filter(
1047 char *action_name,
1048 char *filter_name,
1049 int instance,
1050 int module_version)
1053 nvlist_t *nvl;
1055 IPQOSCDBG2(APPLY, "remove_filter: action: %s, filter: %s\n",
1056 action_name, filter_name);
1058 /* create nvlist */
1059 if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) {
1060 ipqos_msg(MT_ENOSTR, "nvlist_alloc");
1061 return (IPQOS_CONF_ERR);
1064 /* add 'remove filter' config type to list */
1065 if (nvlist_add_byte(nvl, IPP_CONFIG_TYPE, CLASSIFIER_REMOVE_FILTER)
1066 != 0) {
1067 ipqos_msg(MT_ENOSTR, "nvlist_add_byte");
1068 return (IPQOS_CONF_ERR);
1072 * add module version
1074 if (nvlist_add_uint32(nvl, IPP_MODULE_VERSION,
1075 (uint32_t)module_version) != 0) {
1076 ipqos_msg(MT_ENOSTR, "nvlist_add_uint32");
1077 return (IPQOS_CONF_ERR);
1080 /* add filter name to list */
1081 if (nvlist_add_string(nvl, CLASSIFIER_FILTER_NAME, filter_name) != 0) {
1082 ipqos_msg(MT_ENOSTR, "nvlist_add_string");
1083 return (IPQOS_CONF_ERR);
1086 /* add instance number if part of multi-instance filter */
1087 if (instance != -1 && nvlist_add_int32(nvl, IPGPC_FILTER_INSTANCE,
1088 instance) != 0) {
1089 ipqos_msg(MT_ENOSTR, "nvlist_add_int32");
1090 return (IPQOS_CONF_ERR);
1093 /* call into lib to remove */
1094 if (ipp_action_modify(action_name, &nvl, 0) != 0) {
1096 /* generic error message */
1098 ipqos_msg(MT_ERROR,
1099 gettext("Removing filter %s in action %s failed: %s.\n"),
1100 filter_name, action_name, strerror(errno));
1102 return (IPQOS_CONF_ERR);
1105 return (IPQOS_CONF_SUCCESS);
1108 /* ******************************************************************* */
1112 * add originator nvpair set to ipqosconf to nvl.
1113 * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCESS.
1115 static int
1116 add_orig_ipqosconf(nvlist_t *nvl)
1119 if (nvlist_add_uint32(nvl, IPP_CONFIG_ORIGINATOR,
1120 IPP_CONFIG_IPQOSCONF) != 0) {
1121 ipqos_msg(MT_ENOSTR, "nvlist_add_uint32: originator:");
1122 return (IPQOS_CONF_ERR);
1125 return (IPQOS_CONF_SUCCESS);
1128 /* ************************* differencing functions ************************ */
1132 * compares the contents of arrays array1 and array2, both of size size, and
1133 * returns B_TRUE or B_FALSE if they're equal or not respectively.
1134 * RETURNS: B_TRUE if equal, else B_FALSE.
1136 static boolean_t
1137 arrays_equal(
1138 int array1[],
1139 int array2[],
1140 uint32_t size)
1142 int x;
1144 for (x = 0; x < size; x++) {
1145 if (array1[x] != array2[x])
1146 return (B_FALSE);
1148 return (B_TRUE);
1152 * difference class old against class new. It marks the new class as
1153 * modified if it is different.
1154 * RETURNS: IPQOS_CONF_SUCCESS.
1156 static int
1157 diffclass(
1158 ipqos_conf_class_t *old,
1159 ipqos_conf_class_t *new)
1162 IPQOSCDBG0(L0, "In diffclass:\n");
1164 /* two different spec'd actions */
1165 if (strcmp(old->alist->name, new->alist->name) != 0) {
1166 IPQOSCDBG1(DIFF, "marking class %s as modified\n", new->name);
1168 new->modified = B_TRUE;
1169 return (IPQOS_CONF_SUCCESS);
1172 /* different stats values */
1173 if (old->stats_enable != new->stats_enable) {
1174 IPQOSCDBG1(DIFF, "marking class %s as modified\n", new->name);
1176 new->modified = B_TRUE;
1177 return (IPQOS_CONF_SUCCESS);
1180 return (IPQOS_CONF_SUCCESS);
1184 * difference params set old against params set new of module module_name. It
1185 * marks the new params as modified if different.
1186 * RETURNS: if error IPQOS_CONF_ERR, else IPQOS_CONF_SUCCESS.
1188 static int
1189 diffparams(
1190 ipqos_conf_params_t *old,
1191 ipqos_conf_params_t *new,
1192 char *module_name)
1195 int diff;
1196 int res;
1198 IPQOSCDBG0(L0, "In diffparams\n");
1200 /* diff stats */
1201 if (old->stats_enable != new->stats_enable) {
1203 new->modified = B_TRUE;
1204 return (IPQOS_CONF_SUCCESS);
1207 /* diff module specific params */
1208 res = diffnvlists(old->nvlist, new->nvlist, module_name, &diff,
1209 PL_PARAMS);
1210 if (res != IPQOS_CONF_SUCCESS) {
1211 return (res);
1213 if (diff) {
1215 new->modified = B_TRUE;
1218 return (IPQOS_CONF_SUCCESS);
1222 * differences filter old against filter new of module module_name. It marks
1223 * filter new as different if so.
1224 * RETURNS: if error IPQOS_CONF_ERR, else IPQOS_CONF_SUCCESS.
1226 static int
1227 difffilter(
1228 ipqos_conf_filter_t *old,
1229 ipqos_conf_filter_t *new,
1230 char *module_name)
1233 int res;
1234 int diff;
1236 IPQOSCDBG0(L0, "In difffilter\n");
1238 /* compare class name */
1240 if (strcmp(old->class_name, new->class_name) != 0) {
1241 IPQOSCDBG1(DIFF, "Marking filter %s as modified\n", new->name);
1243 new->modified = B_TRUE;
1244 return (IPQOS_CONF_SUCCESS);
1247 /* compare module specific params */
1249 res = diffnvlists(old->nvlist, new->nvlist, module_name, &diff,
1250 PL_FILTER);
1251 if (res != IPQOS_CONF_SUCCESS) {
1252 return (res);
1255 if (diff) {
1256 IPQOSCDBG1(DIFF, "Marking filter %s as modified\n", new->name);
1257 new->modified = B_TRUE;
1260 return (IPQOS_CONF_SUCCESS);
1265 * mark all the filters and classes in parameter action either
1266 * for deletion (if they are ipqosconf originated) or for modification.
1268 static void
1269 mark_classes_filters_del(ipqos_conf_action_t *action)
1272 ipqos_conf_filter_t *flt;
1273 ipqos_conf_class_t *cls;
1275 IPQOSCDBG1(L1, "In mark_classes_filters_del: action: %s\n",
1276 action->name);
1278 /* mark all non-permanent filters for del and permanent to modify */
1279 for (flt = action->filters; flt; flt = flt->next) {
1280 if (flt->originator == IPP_CONFIG_PERMANENT) {
1281 IPQOSCDBG1(DIFF, "Marking prm filter %s as modified.\n",
1282 flt->name);
1284 flt->modified = B_TRUE;
1285 } else {
1286 IPQOSCDBG1(DIFF, "Marking filter %s as del.\n",
1287 flt->name);
1289 flt->todel = B_TRUE;
1293 /* mark all non-permanent classes for del and permanent to modify */
1294 for (cls = action->classes; cls; cls = cls->next) {
1295 if (cls->originator == IPP_CONFIG_PERMANENT) {
1296 IPQOSCDBG1(DIFF, "Marking prm class %s as modified.\n",
1297 cls->name);
1299 cls->modified = B_TRUE;
1300 } else {
1301 IPQOSCDBG1(DIFF, "Marking class %s as del.\n",
1302 cls->name);
1304 cls->todel = B_TRUE;
1310 * mark all classes and filters either new (non-permanent) or modified.
1312 static void
1313 mark_classes_filters_new(ipqos_conf_action_t *action)
1316 ipqos_conf_filter_t *flt;
1317 ipqos_conf_class_t *cls;
1319 IPQOSCDBG1(L1, "In mark_classes_filters_new: action: %s\n",
1320 action->name);
1322 /* mark all permanent filters as modified and all others new */
1324 for (flt = action->filters; flt; flt = flt->next) {
1325 if (flt->originator == IPP_CONFIG_PERMANENT) {
1326 IPQOSCDBG1(DIFF, "Marking prm filter %s as modified.\n",
1327 flt->name);
1329 flt->modified = B_TRUE;
1330 action->modified = B_TRUE;
1331 } else {
1332 IPQOSCDBG1(DIFF, "Marking filter %s as new.\n",
1333 flt->name);
1335 flt->new = B_TRUE;
1339 /* mark all permanent classes as modified and all others new */
1340 for (cls = action->classes; cls; cls = cls->next) {
1341 if (cls->originator == IPP_CONFIG_PERMANENT) {
1342 IPQOSCDBG1(DIFF, "Marking prm class %s as modified.\n",
1343 cls->name);
1345 cls->modified = B_TRUE;
1346 action->modified = B_TRUE;
1347 } else {
1348 IPQOSCDBG1(DIFF, "Marking class %s as new.\n",
1349 cls->name);
1351 cls->new = B_TRUE;
1357 * Marks all the actions and their constituent elements in conf
1358 * as new.
1360 static void
1361 mark_config_new(
1362 ipqos_conf_action_t *conf)
1364 while (conf != NULL) {
1365 IPQOSCDBG1(DIFF, "Marking action %s as new\n", conf->name);
1366 mark_classes_filters_new(conf);
1367 conf->new = B_TRUE;
1368 conf->visited = 0;
1369 conf = conf->next;
1374 * differences the configuration in new against old marking the actions
1375 * and their contents appropriately.
1376 * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCESS.
1378 static int
1379 diffconf(
1380 ipqos_conf_action_t *old,
1381 ipqos_conf_action_t *new)
1384 int res;
1385 ipqos_conf_action_t *act;
1386 ipqos_conf_action_t *tmp;
1388 IPQOSCDBG0((L0 | DIFF), "In diffconf\n");
1390 /* check the new actions against the old */
1392 for (act = new; act; act = act->next) {
1394 /* if action not in old mark it and it's contents as new */
1396 if ((tmp = actionexist(act->name, old)) == NULL) {
1397 IPQOSCDBG1(DIFF, "marking act %s as new\n", act->name);
1399 act->new = B_TRUE;
1400 mark_classes_filters_new(act);
1401 continue;
1404 /* if action in old diff old against new */
1406 res = diffaction(tmp, act);
1407 if (res != IPQOS_CONF_SUCCESS) {
1408 return (res);
1413 * mark actions, and their contents, in old but not new that were
1414 * created by us for del.
1417 for (act = old; act; act = act->next) {
1418 if (act->params->originator == IPP_CONFIG_IPQOSCONF &&
1419 actionexist(act->name, new) == NULL) {
1420 IPQOSCDBG1(DIFF, "marking act %s for del\n", act->name);
1422 act->todel = B_TRUE;
1423 mark_classes_filters_del(act);
1427 return (IPQOS_CONF_SUCCESS);
1431 * differences action old against action new, comparing its classes, filters
1432 * and parameters. If it is different the new action is marked as modified
1433 * and it's different sub-objects are also marked approriately.
1434 * RETURNS: IPQOS_CONF_ERR if error, else IPQOS_CONF_SUCCESS.
1436 static int
1437 diffaction(
1438 ipqos_conf_action_t *old,
1439 ipqos_conf_action_t *new)
1442 int res;
1444 IPQOSCDBG0(L0, "In diffaction\n");
1446 /* compare and mark classes */
1447 res = diffclasses(old, new);
1448 if (res != IPQOS_CONF_SUCCESS) {
1449 return (res);
1452 /* compare and mark filters */
1453 res = difffilters(old, new);
1454 if (res != IPQOS_CONF_SUCCESS) {
1455 return (res);
1458 /* compare and mark parameters */
1459 res = diffparams(old->params, new->params, old->module);
1460 if (res != IPQOS_CONF_SUCCESS) {
1461 return (res);
1464 /* mark action as modified if params are */
1465 if (new->params->modified == B_TRUE) {
1466 IPQOSCDBG1(DIFF, "Marking params for action %s modified\n",
1467 new->name);
1469 new->modified = B_TRUE;
1472 return (IPQOS_CONF_SUCCESS);
1476 * differences the set of classes in new against those in old, marking any
1477 * that are new/modified, approriately in the new class, and any removed
1478 * in the old class appropriately. Also marks the action which has had an
1479 * object within marked, as modified.
1480 * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCESS.
1483 static int
1484 diffclasses(
1485 ipqos_conf_action_t *old,
1486 ipqos_conf_action_t *new)
1490 ipqos_conf_class_t *cls;
1491 ipqos_conf_class_t *tmpc;
1492 ipqos_conf_class_t *ncls;
1493 int res;
1496 /* loop through old classes checking for classes not present in new */
1498 for (cls = old->classes; cls; cls = cls->next) {
1500 if (classexist(cls->name, new->classes) == NULL) {
1502 /* if we created original class mark for deletion */
1504 if (cls->originator == IPP_CONFIG_IPQOSCONF) {
1505 IPQOSCDBG1(DIFF, "marking class %s for del\n",
1506 cls->name);
1508 cls->todel = B_TRUE;
1510 /* mark old action */
1511 old->modified = B_TRUE;
1514 * if permanent class and next action created by us
1515 * copy it, set it's next action to continue and
1516 * add it to new action. This will cause the class
1517 * to be marked as and modified. This returns the class
1518 * to an assumed default state and prevents the
1519 * case where the class is pointing at an action
1520 * we want to remove and therefore couldn't without
1521 * this forced modify.
1523 } else if (cls->originator == IPP_CONFIG_PERMANENT &&
1524 cls->alist->action && /* not virtual action */
1525 cls->alist->action->params->originator ==
1526 IPP_CONFIG_IPQOSCONF) {
1528 /* copy class */
1530 res = dup_class(cls, &ncls);
1531 if (res != IPQOS_CONF_SUCCESS) {
1532 return (IPQOS_CONF_ERR);
1535 /* set next action to continue */
1537 (void) strcpy(ncls->alist->name,
1538 IPP_ANAME_CONT);
1540 /* add to news classes to be diffed below */
1541 ADD_TO_LIST(&new->classes, ncls);
1546 /* loop through new classes checking for new / modified classes */
1548 for (cls = new->classes; cls; cls = cls->next) {
1550 /* new ipqosconf class */
1552 if ((tmpc = classexist(cls->name, old->classes)) == NULL ||
1553 (tmpc->originator != IPP_CONFIG_IPQOSCONF &&
1554 tmpc->originator != IPP_CONFIG_PERMANENT)) {
1555 IPQOSCDBG1(DIFF, "marking class %s new\n",
1556 cls->name);
1558 cls->new = B_TRUE;
1560 new->modified = B_TRUE; /* mark new action */
1561 continue;
1563 /* existing ipqosconf/perm class */
1564 } else {
1565 res = diffclass(tmpc, cls);
1566 if (res != IPQOS_CONF_SUCCESS) {
1567 return (res);
1570 if (cls->modified == B_TRUE) {
1571 new->modified = B_TRUE;
1576 return (IPQOS_CONF_SUCCESS);
1580 * differences the set of filters in new against those in old, marking any
1581 * that are new/modified, approriately in the new filter/s, and any removed
1582 * in the old filter appropriately. Also marks the action which has had an
1583 * object within marked, as modified.
1584 * RETURNS: IPQOS_CONF_SUCCESS (we return an int for symmetry with diffclasses
1585 * and difffparams).
1587 static int
1588 difffilters(
1589 ipqos_conf_action_t *old,
1590 ipqos_conf_action_t *new)
1593 ipqos_conf_filter_t *flt;
1594 ipqos_conf_filter_t *tmpf;
1595 int maxi;
1596 int newi;
1597 int res;
1599 /* check for new/modified filters */
1601 for (flt = new->filters; flt; flt = flt->next) {
1603 /* new ipqosconf filter */
1605 if ((tmpf = filterexist(flt->name, -1, old->filters)) == NULL) {
1607 /* mark all instances of this filter as new */
1608 for (;;) {
1609 IPQOSCDBG1(DIFF, "Marking filter %s as "
1610 "new\n", flt->name);
1612 flt->new = B_TRUE;
1615 if (flt->next == NULL ||
1616 strcmp(flt->next->name, flt->name) != 0) {
1617 break;
1619 flt = flt->next;
1621 new->modified = B_TRUE; /* mark new action */
1623 /* ipqosconf/permanent filter existed */
1624 } else {
1626 * if ip node name force filter refresh - ie. mark
1627 * all old filter instances as todel and all new new.
1629 if (tmpf->src_nd_name || tmpf->dst_nd_name ||
1630 flt->src_nd_name || flt->dst_nd_name) {
1632 /* init max previous filter instance */
1633 maxi = tmpf->instance;
1635 /* mark old instances for deletion */
1636 do {
1637 IPQOSCDBG2(DIFF, "Marking filter "
1638 "%s, instance %d for del\n",
1639 tmpf->name, tmpf->instance);
1641 tmpf->todel = B_TRUE;
1644 * check and update previous instance
1645 * max.
1647 if (tmpf->instance > maxi) {
1648 maxi = tmpf->instance;
1651 tmpf = tmpf->next;
1652 } while (tmpf != NULL &&
1653 strcmp(tmpf->name, flt->name) == 0);
1656 * use the max previous instance + 1 for
1657 * the start of the new instance numbers.
1659 newi = (uint32_t)++maxi % INT_MAX;
1662 * mark new instances for addition and
1663 * give new instance number.
1665 for (;;) {
1666 IPQOSCDBG2(DIFF, "Marking filter "
1667 "%s, instance %d as new\n",
1668 flt->name, newi);
1670 flt->new = B_TRUE;
1671 flt->instance = newi++;
1672 if (flt->next == NULL ||
1673 strcmp(flt->next->name,
1674 flt->name) != 0) {
1675 break;
1677 flt = flt->next;
1679 new->modified = B_TRUE; /* mark new action */
1681 /* mark old action */
1682 old->modified = B_TRUE;
1684 /* non-node name filter */
1685 } else {
1686 /* compare and mark as modified if diff */
1688 res = difffilter(tmpf, flt, new->module);
1689 if (res != IPQOS_CONF_SUCCESS) {
1690 return (res);
1692 if (flt->modified == B_TRUE) {
1693 /* mark action if diff */
1694 new->modified = B_TRUE;
1701 * Check for deleted ipqosconf created filters and mark
1702 * any found for deletion.
1703 * For non-ipqosconf generated filters, including permanent
1704 * ones (none of these exist at the moment) we just leave
1705 * the filter unmarked.
1707 for (flt = old->filters; flt; flt = flt->next) {
1709 if (flt->originator == IPP_CONFIG_IPQOSCONF &&
1710 filterexist(flt->name, -1, new->filters) == NULL) {
1712 /* mark all old instances for deletions */
1713 for (;;) {
1714 IPQOSCDBG2(DIFF, "marking flt %s, inst %d "
1715 "for del\n", flt->name, flt->instance);
1717 flt->todel = B_TRUE;
1718 old->modified = B_TRUE; /* mark old action */
1720 if (flt->next == NULL ||
1721 strcmp(flt->next->name, flt->name) != 0) {
1722 break;
1724 flt = flt->next;
1729 return (IPQOS_CONF_SUCCESS);
1734 * differences the elements of nvlists old and new using the types file
1735 * for module name to interpret the element types. It sets pdiff to either
1736 * 0 or 1 if they are the same or different respectively.
1737 * RETURNS: IPQOS_CONF_ERR if any errors, else IPQOS_CONF_SUCCESS.
1739 static int
1740 diffnvlists(
1741 nvlist_t *old,
1742 nvlist_t *new,
1743 char *module_name,
1744 int *pdiff,
1745 place_t place)
1748 int first_pass = 1;
1749 nvlist_t *tmp;
1750 int res;
1751 nvpair_t *nvp;
1752 FILE *tfp;
1753 str_val_nd_t *enum_nvs;
1754 char dfltst[IPQOS_VALST_MAXLEN+1] = "";
1755 char *lo;
1756 ipqos_nvtype_t type;
1757 char *nme;
1758 int diff;
1759 int openerr;
1762 IPQOSCDBG0(L0, "In diffnvlists\n");
1764 /* open stream to types file */
1766 tfp = validmod(module_name, &openerr);
1767 if (tfp == NULL) {
1768 if (openerr) {
1769 ipqos_msg(MT_ENOSTR, "fopen");
1771 return (IPQOS_CONF_ERR);
1773 start:
1775 * loop through each of the elements of the new list comparing
1776 * it with the old one if present. If the old one isn't present
1777 * then it is compared with the default value for that type (if
1778 * set). Any time the values are determined to be different
1779 * or the default value is to be used but isn't present the diff
1780 * param is set to 1 and we return.
1782 * If the loop runs its course then the new and old nvlists are
1783 * reversed and the loop is entered for a second time.
1785 nvp = nvlist_next_nvpair(new, NULL);
1786 while (nvp != NULL) {
1788 /* get name */
1789 nme = nvpair_name(nvp);
1792 * get type.
1794 place = PL_ANY;
1795 res = readtype(tfp, module_name, SHORT_NAME(nme), &type,
1796 &enum_nvs, dfltst, B_TRUE, &place);
1797 if (res != IPQOS_CONF_SUCCESS) {
1798 return (res);
1801 /* init diff to 1 */
1802 diff = 1;
1804 switch (type) {
1806 /* interface name */
1807 case IPQOS_DATA_TYPE_IFINDEX: {
1808 uint32_t ifidx;
1809 uint32_t oifidx;
1811 /* get new value */
1812 (void) nvpair_value_uint32(nvp, &ifidx);
1814 /* compare against old if present */
1816 res = nvlist_lookup_uint32(old, nme, &oifidx);
1817 if (res == 0) {
1818 /* diff values */
1819 diff = (ifidx != oifidx);
1821 /* not in old so see if new value is default */
1823 } else {
1824 diff = (ifidx != 0);
1826 break;
1828 /* protocol */
1829 case IPQOS_DATA_TYPE_PROTO: {
1830 uchar_t proto;
1831 uchar_t oproto;
1833 (void) nvpair_value_byte(nvp, &proto);
1835 res = nvlist_lookup_byte(old, nme, &oproto);
1836 if (res == 0) {
1837 diff = (proto != oproto);
1838 } else {
1839 diff = (proto != 0);
1841 break;
1843 /* port */
1844 case IPQOS_DATA_TYPE_PORT: {
1845 uint16_t port;
1846 uint16_t oport;
1848 (void) nvpair_value_uint16(nvp, &port);
1849 res = nvlist_lookup_uint16(old, nme, &oport);
1850 if (res == 0) {
1851 diff = (port != oport);
1852 } else {
1853 diff = (port != 0);
1855 break;
1857 /* action name / string */
1858 case IPQOS_DATA_TYPE_ACTION:
1859 case IPQOS_DATA_TYPE_STRING: {
1860 char *str;
1861 char *ostr;
1863 (void) nvpair_value_string(nvp, &str);
1864 res = nvlist_lookup_string(old, nme, &ostr);
1865 if (res == 0) {
1866 diff = strcmp(str, ostr);
1867 } else if (*dfltst) {
1868 diff = strcmp(str, dfltst);
1870 break;
1872 /* address mask / address */
1873 case IPQOS_DATA_TYPE_ADDRESS_MASK:
1874 case IPQOS_DATA_TYPE_ADDRESS: {
1875 in6_addr_t *in6;
1876 in6_addr_t *oin6;
1877 uint_t x;
1880 * all addresses are stored as v6 addresses, so
1881 * a uint32_t[4] array is used.
1884 /* lookup new value */
1886 (void) nvpair_value_uint32_array(nvp,
1887 (uint32_t **)&in6, &x);
1889 /* see if there's an old value and diff it */
1891 res = nvlist_lookup_uint32_array(old, nme,
1892 (uint32_t **)&oin6, &x);
1893 if (res == 0) {
1894 /* diff each of the 16 v6 address bytes */
1896 for (x = 0; x < 16; x++) {
1897 if (in6->s6_addr[x] !=
1898 oin6->s6_addr[x]) {
1899 diff++;
1900 break;
1904 break;
1906 /* boolean */
1907 case IPQOS_DATA_TYPE_BOOLEAN: {
1908 boolean_t bl;
1909 boolean_t obl;
1911 (void) nvpair_value_uint32(nvp, (uint32_t *)&bl);
1913 /* see if there's an old value and diff it */
1914 res = nvlist_lookup_uint32(old, nme, (uint32_t *)&obl);
1915 if (res == 0) {
1916 diff = (bl != obl);
1918 /* compare against default if present */
1919 } else if (*dfltst) {
1920 res = readbool(dfltst, &obl);
1921 if (res == IPQOS_CONF_SUCCESS) {
1922 diff = (bl != obl);
1925 break;
1927 /* uint 8 */
1928 case IPQOS_DATA_TYPE_UINT8: {
1929 uint8_t u8;
1930 uint8_t ou8;
1932 (void) nvpair_value_byte(nvp, (uchar_t *)&u8);
1933 res = nvlist_lookup_byte(old, nme, (uchar_t *)&ou8);
1934 if (res == 0) {
1935 diff = (u8 != ou8);
1936 } else if (*dfltst) {
1937 res = readuint8(dfltst, &ou8, &lo);
1938 if (res == IPQOS_CONF_SUCCESS) {
1939 diff = (u8 != ou8);
1942 break;
1944 /* int 16 */
1945 case IPQOS_DATA_TYPE_INT16: {
1946 int16_t i16;
1947 int16_t oi16;
1949 (void) nvpair_value_int16(nvp, &i16);
1950 res = nvlist_lookup_int16(old, nme, &oi16);
1951 if (res == 0) {
1952 diff = (i16 != oi16);
1953 } else if (*dfltst) {
1954 res = readint16(dfltst, &oi16, &lo);
1955 if (res == IPQOS_CONF_SUCCESS) {
1956 diff = (i16 != oi16);
1959 break;
1961 /* uint16 */
1962 case IPQOS_DATA_TYPE_UINT16: {
1963 uint16_t ui16;
1964 uint16_t oui16;
1966 (void) nvpair_value_uint16(nvp, &ui16);
1967 res = nvlist_lookup_uint16(old, nme, &oui16);
1968 if (res == 0) {
1969 diff = (ui16 != oui16);
1970 } else if (*dfltst) {
1971 res = readuint16(dfltst, &oui16, &lo);
1972 if (res == IPQOS_CONF_SUCCESS) {
1973 diff = (ui16 != oui16);
1976 break;
1979 * int32 and user.
1980 * Since user uids are stored in an int32 nvpair we can use
1981 * the same comparison code.
1983 case IPQOS_DATA_TYPE_USER:
1984 case IPQOS_DATA_TYPE_INT32: {
1985 int32_t i32;
1986 int32_t oi32;
1988 (void) nvpair_value_int32(nvp, &i32);
1989 res = nvlist_lookup_int32(old, nme, &oi32);
1990 if (res == 0) {
1991 diff = (i32 != oi32);
1992 } else if (*dfltst) {
1993 res = readint32(dfltst, &oi32, &lo);
1994 if (res == IPQOS_CONF_SUCCESS) {
1995 diff = (i32 != oi32);
1998 break;
2000 /* uint32 */
2001 case IPQOS_DATA_TYPE_UINT32: {
2002 uint32_t ui32;
2003 uint32_t oui32;
2005 (void) nvpair_value_uint32(nvp, &ui32);
2006 res = nvlist_lookup_uint32(old, nme, &oui32);
2007 if (res == 0) {
2008 diff = (ui32 != oui32);
2009 } else if (*dfltst) {
2010 res = readuint32(dfltst, &oui32, &lo);
2011 if (res == IPQOS_CONF_SUCCESS) {
2012 diff = (ui32 != oui32);
2015 break;
2017 /* enumeration */
2018 case IPQOS_DATA_TYPE_ENUM: {
2019 uint32_t eval;
2020 uint32_t oeval;
2022 (void) nvpair_value_uint32(nvp, &eval);
2023 res = nvlist_lookup_uint32(old, nme, &oeval);
2024 if (res == 0) {
2025 diff = (eval != oeval);
2026 } else if (*dfltst) {
2027 res = readuint32(dfltst, &oeval, &lo);
2028 if (res == IPQOS_CONF_SUCCESS) {
2029 diff = (eval != oeval);
2032 break;
2034 case IPQOS_DATA_TYPE_M_INDEX: {
2035 uint8_t idx, oidx;
2037 (void) nvpair_value_byte(nvp, &idx);
2038 res = nvlist_lookup_byte(old, nme, &oidx);
2039 if (res == 0)
2040 diff = (idx != oidx);
2041 break;
2043 case IPQOS_DATA_TYPE_INT_ARRAY: {
2044 int *oarr, *arr;
2045 uint32_t osize, size;
2047 (void) nvpair_value_int32_array(nvp, &arr, &size);
2048 res = nvlist_lookup_int32_array(old, nme, &oarr,
2049 &osize);
2050 if (res == 0)
2051 diff = (arrays_equal(arr, oarr, size) ==
2052 B_FALSE);
2053 break;
2055 #ifdef _IPQOS_CONF_DEBUG
2056 default: {
2057 /* shouldn't get here as all types should be covered */
2058 assert(1);
2060 #endif
2061 } /* switch */
2062 if (diff != 0) {
2063 IPQOSCDBG1(DIFF, "parameter %s different\n", nme);
2064 *pdiff = 1;
2065 (void) fclose(tfp);
2066 return (IPQOS_CONF_SUCCESS);
2070 nvp = nvlist_next_nvpair(new, nvp);
2074 /* now compare all the stuff in the second list with the first */
2075 if (first_pass) {
2076 tmp = old;
2077 old = new;
2078 new = tmp;
2079 first_pass = 0;
2080 goto start;
2083 (void) fclose(tfp);
2085 *pdiff = 0;
2086 return (IPQOS_CONF_SUCCESS);
2091 /* ************************** difference application *********************** */
2096 * causes all items marked as requiring change in actions and old_actions
2097 * to have the change applied.
2098 * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCESS.
2100 static int
2101 applydiff(
2102 ipqos_conf_action_t *actions,
2103 ipqos_conf_action_t *old_actions)
2106 int res;
2108 IPQOSCDBG0(L1, "In applydiff:\n");
2111 /* add each item marked as new */
2113 res = add_items(actions, B_FALSE);
2114 if (res != IPQOS_CONF_SUCCESS) {
2115 return (res);
2118 /* modify items marked for modification */
2120 res = modify_items(actions);
2121 if (res != IPQOS_CONF_SUCCESS) {
2122 return (res);
2125 /* delete items marked for deletion */
2127 res = remove_items(old_actions, B_FALSE);
2128 if (res != IPQOS_CONF_SUCCESS) {
2129 return (res);
2132 return (IPQOS_CONF_SUCCESS);
2135 static int
2136 add_items(
2137 ipqos_conf_action_t *actions,
2138 boolean_t rem_undo)
2141 int res;
2142 ipqos_conf_action_t *act;
2144 IPQOSCDBG1(L1, "In add_items, rem_undo: %u\n", rem_undo);
2147 * we need to create ipgpc action before any others as some actions
2148 * such as ftpcl which make calls to it depend on it being there on
2149 * their creation.
2151 act = actionexist(IPGPC_CLASSIFY, actions);
2152 if (act &&
2153 (rem_undo == B_FALSE && act->new == B_TRUE ||
2154 rem_undo == B_TRUE && act->deleted == B_TRUE)) {
2156 res = add_action(act);
2157 if (res != IPQOS_CONF_SUCCESS) {
2158 return (res);
2163 * loop though action list and add any actions marked as
2164 * new/modified action and apply any additions there, then return.
2167 for (act = actions; act; act = act->next) {
2168 res = add_item(act, rem_undo);
2169 if (res != IPQOS_CONF_SUCCESS) {
2170 return (IPQOS_CONF_ERR);
2174 return (IPQOS_CONF_SUCCESS);
2180 * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCESS.
2182 static int
2183 add_item(
2184 ipqos_conf_action_t *actions,
2185 boolean_t rem_undo)
2188 ipqos_conf_action_t *act = actions;
2189 int res;
2190 ipqos_conf_class_t *cls;
2191 ipqos_conf_act_ref_t *pact;
2193 IPQOSCDBG2(L1, "In add_item: action: %s, rem_undo: %u\n",
2194 actions->name, rem_undo);
2196 /* if already visited return immediately */
2198 if (act->visited == ADD_VISITED) {
2199 IPQOSCDBG0(L1, "Early exit due to visited\n");
2200 return (IPQOS_CONF_SUCCESS);
2202 act->visited = ADD_VISITED;
2205 /* recurse to last action in tree */
2207 for (cls = act->classes; cls; cls = cls->next) {
2209 /* if not virtual action */
2211 if (cls->alist->action) {
2212 res = add_item(cls->alist->action, rem_undo);
2213 if (res != IPQOS_CONF_SUCCESS) {
2214 return (res);
2219 for (pact = act->params->actions; pact; pact = pact->next) {
2221 /* if not virtual */
2223 if (pact->action) {
2224 res = add_item(pact->action, rem_undo);
2225 if (res != IPQOS_CONF_SUCCESS) {
2226 return (res);
2232 /* if action marked as new and not ipgpc, create */
2234 if (((rem_undo == B_FALSE && act->new == B_TRUE) ||
2235 (rem_undo == B_TRUE && act->deleted == B_TRUE)) &&
2236 strcmp(act->name, IPGPC_CLASSIFY) != 0) {
2237 res = add_action(act);
2238 if (res != IPQOS_CONF_SUCCESS) {
2239 return (res);
2243 /* add any classes and filters marked as new */
2245 if (add_classes(act->classes, act->name, act->module_version,
2246 rem_undo) != IPQOS_CONF_SUCCESS ||
2247 add_filters(act->filters, act->name, act->module_version,
2248 rem_undo) != IPQOS_CONF_SUCCESS) {
2249 return (IPQOS_CONF_ERR);
2252 return (IPQOS_CONF_SUCCESS);
2257 * Uses the contents of acts params nvlist and adds an originator
2258 * element set to ipqosconf and the stats parameter. This list
2259 * is then used as the parameter to a call to ipp_action_create to create
2260 * this action in the kernel.
2261 * RETURNS: IPQOS_CONF_ERR on err, else IPQOS_CONF_SUCCESS.
2263 static int
2264 add_action(ipqos_conf_action_t *act)
2267 int res;
2268 nvlist_t **nvl;
2270 IPQOSCDBG2(APPLY, "add_action: action: %s, module: %s\n", act->name,
2271 act->module);
2273 nvl = &act->params->nvlist;
2275 /* alloc params nvlist if not already one */
2277 if (*nvl == NULL) {
2278 res = nvlist_alloc(nvl, NV_UNIQUE_NAME, 0);
2279 if (res != 0) {
2280 ipqos_msg(MT_ENOSTR, "nvlist_alloc");
2281 return (IPQOS_CONF_ERR);
2286 * add module version
2288 if (nvlist_add_uint32(*nvl, IPP_MODULE_VERSION,
2289 (uint32_t)act->module_version) != 0) {
2290 ipqos_msg(MT_ENOSTR, "nvlist_add_uint32");
2291 return (IPQOS_CONF_ERR);
2294 /* add action stats */
2296 if (nvlist_add_uint32(*nvl, IPP_ACTION_STATS_ENABLE,
2297 (uint32_t)act->params->stats_enable) != 0) {
2298 ipqos_msg(MT_ENOSTR, "nvlist_add_uint32: action stats");
2299 return (IPQOS_CONF_ERR);
2302 /* add ipqosconf originator id */
2304 if (add_orig_ipqosconf(*nvl) != IPQOS_CONF_SUCCESS) {
2305 return (IPQOS_CONF_ERR);
2308 /* call into lib to create action */
2310 res = ipp_action_create(act->module, act->name, nvl, 0);
2311 if (res != 0) {
2312 IPQOSCDBG2(APPLY, "Create action %s, module %s failed\n",
2313 act->name, act->module);
2315 /* invalid params */
2317 if (errno == EINVAL) {
2318 ipqos_msg(MT_ERROR,
2319 gettext("Invalid Parameters for action %s.\n"),
2320 act->name);
2322 } else if (errno == ENOENT) {
2323 ipqos_msg(MT_ERROR,
2324 gettext("Missing required parameter for action "
2325 "%s.\n"), act->name);
2327 } else { /* unexpected error */
2328 ipqos_msg(MT_ERROR, gettext("Failed to create action "
2329 "%s: %s.\n"), act->name, strerror(errno));
2332 return (IPQOS_CONF_ERR);
2335 /* mark action as created */
2336 act->cr_mod = B_TRUE;
2338 return (IPQOS_CONF_SUCCESS);
2342 * for each of the filters in parameter filters if rem_undo is false and
2343 * the filter is marked as new or if rem_undo is true and the filter is
2344 * marked as deleted then add the filter to the kernel action named by action
2345 * and if successful mark as created.
2346 * RETURNS: IPQOS_CONF_ERR on errors, else IPQOS_CONF_SUCCESS.
2348 static int
2349 add_filters(
2350 ipqos_conf_filter_t *filters,
2351 char *action,
2352 int module_version,
2353 boolean_t rem_undo)
2356 ipqos_conf_filter_t *flt;
2358 IPQOSCDBG0(L1, "In add_filters\n");
2360 /* loop through filters in filters param */
2361 for (flt = filters; flt; flt = flt->next) {
2363 * skip filter if in normal mode and not new filter or
2364 * if doing rollback and filter wasn't previously deleted.
2366 if ((rem_undo == B_FALSE && flt->new == B_FALSE) ||
2367 (rem_undo == B_TRUE && flt->deleted == B_FALSE)) {
2368 continue;
2371 /* add filter to action */
2372 if (add_filter(action, flt, module_version) !=
2373 IPQOS_CONF_SUCCESS) {
2374 return (IPQOS_CONF_ERR);
2377 /* mark as created */
2378 flt->cr_mod = B_TRUE;
2381 return (IPQOS_CONF_SUCCESS);
2385 * for each of the classes in parameter classes if rem_undo is false and
2386 * the class is marked as new or if rem_undo is true and the class is
2387 * marked as deleted then add the class to the kernel action named by action
2388 * and if successful mark as created.
2389 * RETURNS: IPQOS_CONF_ERR on errors, else IPQOS_CONF_SUCCESS.
2392 add_classes(
2393 ipqos_conf_class_t *classes,
2394 char *action,
2395 int module_version,
2396 boolean_t rem_undo) {
2398 int res;
2399 ipqos_conf_class_t *cls;
2401 IPQOSCDBG0(L1, "In add_classes\n");
2403 /* for each class */
2404 for (cls = classes; cls; cls = cls->next) {
2406 * skip class if in normal mode and not new class or
2407 * if doing rollback and class wasn't deleted.
2409 if ((rem_undo == B_FALSE && cls->new == B_FALSE) ||
2410 (rem_undo == B_TRUE && cls->deleted == B_FALSE)) {
2411 continue;
2414 /* add class to action */
2415 res = add_class(action, cls->name, module_version,
2416 cls->stats_enable, cls->alist->name);
2417 if (res != IPQOS_CONF_SUCCESS) {
2418 return (IPQOS_CONF_ERR);
2421 /* mark class as created */
2422 cls->cr_mod = B_TRUE;
2425 return (IPQOS_CONF_SUCCESS);
2429 * For each of the actions in actions remove the action if marked as
2430 * such or remove any objects within marked as such.
2431 * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCESS.
2433 static int
2434 remove_items(
2435 ipqos_conf_action_t *actions,
2436 boolean_t add_undo)
2439 int res;
2440 ipqos_conf_action_t *act;
2442 IPQOSCDBG1(L0, "In remove_items, add_undo: %u\n", add_undo);
2445 * loop through actions removing any actions, or action contents
2446 * that are marked as such.
2448 for (act = actions; act; act = act->next) {
2449 res = remove_item(act, add_undo);
2450 if (res != IPQOS_CONF_SUCCESS) {
2451 return (res);
2455 return (IPQOS_CONF_SUCCESS);
2459 * Deletes this action if marked for deletion or any of it's contents marked
2460 * for deletion. If the action is marked for deletion any actions referencing
2461 * this action are destroyed first if marked or have their contents destroyed
2462 * if marked. This is recursive.
2463 * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCESS.
2465 static int
2466 remove_item(
2467 ipqos_conf_action_t *act,
2468 boolean_t add_undo)
2471 ipqos_conf_class_t *cls;
2472 ipqos_conf_filter_t *flt;
2473 ipqos_conf_act_ref_t *dep;
2474 int res;
2476 IPQOSCDBG3(L1, "In remove_item: action: %s, add_undo: %u, mod: %u\n",
2477 act->name, add_undo, act->modified);
2480 /* return immmediately if previously visited in remove phase */
2482 if (act->visited == REM_VISITED) {
2483 IPQOSCDBG0(L1, "Exit due to REM_VISITED set\n");
2484 return (IPQOS_CONF_SUCCESS);
2486 act->visited = REM_VISITED;
2489 /* if this action is to be deleted */
2491 if (add_undo == B_FALSE && act->todel == B_TRUE ||
2492 add_undo == B_TRUE && act->new == B_TRUE &&
2493 act->cr_mod == B_TRUE) {
2495 /* modify parent actions first */
2497 for (dep = act->dependencies; dep; dep = dep->next) {
2498 res = remove_item(dep->action, add_undo);
2499 if (res != IPQOS_CONF_SUCCESS) {
2500 return (res);
2504 /* delete this action */
2506 IPQOSCDBG1(APPLY, "deleting action %s\n", act->name);
2507 res = ipp_action_destroy(act->name, 0);
2508 if (res != 0) {
2509 IPQOSCDBG1(APPLY, "failed to destroy action %s\n",
2510 act->name);
2511 return (IPQOS_CONF_ERR);
2514 /* flag as deleted */
2516 act->deleted = B_TRUE;
2518 /* if modified action */
2520 } else if (act->modified == B_TRUE) {
2522 /* loop through removing any filters marked for del */
2524 for (flt = act->filters; flt; flt = flt->next) {
2525 if ((add_undo == B_FALSE && flt->todel == B_TRUE) ||
2526 (add_undo == B_TRUE && flt->new == B_TRUE &&
2527 flt->cr_mod == B_TRUE)) {
2529 /* do deletion */
2531 res = remove_filter(act->name, flt->name,
2532 flt->instance, act->module_version);
2533 if (res != IPQOS_CONF_SUCCESS) {
2534 IPQOSCDBG2(APPLY, "failed to destroy "
2535 "filter %s, inst: %d\n", flt->name,
2536 flt->instance);
2538 return (IPQOS_CONF_ERR);
2541 /* flag deleted */
2543 flt->deleted = B_TRUE;
2547 /* remove any classes marked for del */
2549 for (cls = act->classes; cls; cls = cls->next) {
2550 if ((add_undo == B_FALSE && cls->todel == B_TRUE) ||
2551 (add_undo == B_TRUE && cls->new == B_TRUE &&
2552 cls->cr_mod == B_TRUE)) {
2554 /* do deletion */
2556 res = remove_class(act->name, cls->name,
2557 act->module_version, 0);
2558 if (res != IPQOS_CONF_SUCCESS) {
2559 IPQOSCDBG1(APPLY, "failed to destroy "
2560 "class %s\n", cls->name);
2562 return (IPQOS_CONF_ERR);
2565 /* flag deleted */
2567 cls->deleted = B_TRUE;
2571 /* mark action as having been modified */
2573 act->cr_mod = B_TRUE;
2576 return (IPQOS_CONF_SUCCESS);
2580 * for each of the actions in parameter actions apply any objects marked as
2581 * modified as a modification to the kernel action represented.
2582 * RETURNS: IPQOS_CONF_ERR on err, else IPQOS_CONF_SUCCESS.
2584 static int
2585 modify_items(ipqos_conf_action_t *actions)
2588 ipqos_conf_action_t *act;
2589 int res;
2590 ipqos_conf_filter_t *flt;
2591 ipqos_conf_class_t *cls;
2594 IPQOSCDBG0(L1, "In modify_items\n");
2596 /* loop through actions in parameter actions */
2598 for (act = actions; act; act = act->next) {
2600 /* skip unchanged actions */
2602 if (act->modified == B_FALSE) {
2603 continue;
2606 /* apply any parameter mods */
2608 if (act->params->modified) {
2609 res = modify_params(act->name,
2610 &act->params->nvlist,
2611 act->module_version, act->params->stats_enable);
2612 if (res != IPQOS_CONF_SUCCESS) {
2613 return (IPQOS_CONF_ERR);
2616 act->params->cr_mod = B_TRUE;
2619 /* apply any class mods */
2621 for (cls = act->classes; cls; cls = cls->next) {
2622 if (cls->modified) {
2623 res = modify_class(act->name, cls->name,
2624 act->module_version, cls->stats_enable,
2625 cls->alist->name, 0);
2626 if (res != IPQOS_CONF_SUCCESS) {
2627 return (IPQOS_CONF_ERR);
2630 /* mark modification done */
2631 cls->cr_mod = B_TRUE;
2635 /* apply any filter mods */
2637 for (flt = act->filters; flt; flt = flt->next) {
2638 if (flt->modified) {
2639 res = modify_filter(act->name, flt,
2640 act->module_version);
2641 if (res != 0) {
2642 return (IPQOS_CONF_ERR);
2645 /* mark modification done */
2646 flt->cr_mod = B_TRUE;
2650 /* mark action modified */
2652 act->cr_mod = B_TRUE;
2655 return (IPQOS_CONF_SUCCESS);
2659 * For each of the objects of each of the actions in nactions that are
2660 * marked as having been modified the object modification is done in
2661 * reverse using the same named object from oactions.
2662 * RETURNS: IPQOS_CONF_ERR on error, IPQOS_CONF_SUCCESS otherwise.
2664 static int
2665 undo_modifys(
2666 ipqos_conf_action_t *oactions,
2667 ipqos_conf_action_t *nactions)
2670 ipqos_conf_filter_t *flt;
2671 ipqos_conf_class_t *cls;
2672 ipqos_conf_action_t *act;
2673 ipqos_conf_action_t *oldact;
2674 ipqos_conf_filter_t *oldflt;
2675 ipqos_conf_class_t *oldcls;
2676 int res;
2678 IPQOSCDBG0(L1, "In undo_modifys:\n");
2680 /* loop throught new actions */
2682 for (act = nactions; act; act = act->next) {
2683 oldact = actionexist(act->name, oactions);
2686 * if the action was new then it will be removed and
2687 * any permamanent items that were marked for modify
2688 * will dissappear, so ignore action.
2690 if (oldact == NULL) {
2691 continue;
2694 /* if parameters were modified switch them back */
2696 if (act->params->modified == B_TRUE &&
2697 act->params->cr_mod == B_TRUE) {
2698 res = modify_params(act->name,
2699 &oldact->params->nvlist,
2700 act->module_version, act->params->stats_enable);
2701 if (res != IPQOS_CONF_SUCCESS) {
2702 return (res);
2706 /* for each filter in action if filter modified switch back */
2708 for (flt = act->filters; flt; flt = flt->next) {
2709 if (flt->modified == B_TRUE &&
2710 flt->cr_mod == B_TRUE) {
2711 oldflt = filterexist(flt->name, -1,
2712 oldact->filters);
2713 res = modify_filter(act->name, oldflt,
2714 act->module_version);
2715 if (res != IPQOS_CONF_SUCCESS) {
2716 return (res);
2721 /* for each class in action if class modified switch back */
2723 for (cls = act->classes; cls; cls = cls->next) {
2724 if (cls->modified == B_TRUE &&
2725 cls->cr_mod == B_TRUE) {
2726 oldcls = classexist(cls->name, oldact->classes);
2727 if (oldcls->alist) {
2728 res = modify_class(act->name,
2729 cls->name, act->module_version,
2730 oldcls->stats_enable,
2731 oldcls->alist->name, 0);
2733 if (res != IPQOS_CONF_SUCCESS) {
2734 return (res);
2741 * Go through the old actions modifying perm filters and classes
2742 * whose action was deleted.
2745 for (act = oactions; act != NULL; act = act->next) {
2747 if (act->deleted == B_FALSE) {
2748 continue;
2751 for (flt = act->filters; flt != NULL; flt = flt->next) {
2752 if (flt->originator == IPP_CONFIG_PERMANENT) {
2753 res = modify_filter(act->name, flt,
2754 act->module_version);
2755 if (res != IPQOS_CONF_SUCCESS) {
2756 return (res);
2761 for (cls = act->classes; cls != NULL; cls = cls->next) {
2762 if (cls->originator == IPP_CONFIG_PERMANENT) {
2763 res = modify_class(act->name, cls->name,
2764 act->module_version, cls->stats_enable,
2765 cls->alist->name, 0);
2766 if (res != IPQOS_CONF_SUCCESS) {
2767 return (res);
2774 return (IPQOS_CONF_SUCCESS);
2779 * causes all changes marked as being done in actions and old_actions
2780 * to be undone.
2781 * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCESS.
2783 static int
2784 rollback(
2785 ipqos_conf_action_t *actions,
2786 ipqos_conf_action_t *old_actions)
2789 int res;
2791 IPQOSCDBG0(RBK, "In rollback:\n");
2793 /* re-add items that were deleted */
2795 res = add_items(old_actions, B_TRUE);
2796 if (res != IPQOS_CONF_SUCCESS) {
2797 return (res);
2800 /* change modified items back how they were */
2802 res = undo_modifys(old_actions, actions);
2803 if (res != IPQOS_CONF_SUCCESS) {
2804 return (res);
2807 /* remove new items that were added */
2809 res = remove_items(actions, B_TRUE);
2810 if (res != IPQOS_CONF_SUCCESS) {
2811 return (res);
2814 return (IPQOS_CONF_SUCCESS);
2817 /* ******************************* print config **************************** */
2820 * Prints the username of the user with uid 'uid' to 'fp' if the uid belongs
2821 * to a known user on the system, otherwise just print 'uid'.
2823 static void
2824 printuser(
2825 FILE *fp,
2826 uid_t uid)
2828 struct passwd *pwd;
2830 IPQOSCDBG0(L0, "In printuser\n");
2832 pwd = getpwuid(uid);
2833 if (pwd != NULL) {
2834 (void) fprintf(fp, "%s\n", pwd->pw_name);
2835 } else {
2836 (void) fprintf(fp, "%u\n", (int)uid);
2841 * print either a single value of start to fp (if start equals end), else
2842 * print start'-'end if start is the smaller of the two values, otherwise
2843 * print end'-'start.
2845 static void
2846 printrange(
2847 FILE *fp,
2848 uint32_t start,
2849 uint32_t end)
2851 uint32_t tmp;
2853 if (start > end) {
2854 tmp = start;
2855 start = end;
2856 end = tmp;
2859 (void) fprintf(fp, "%u", start);
2860 if (end != start)
2861 (void) fprintf(fp, "-%u", end);
2865 * print the contents of the array arr to fp in the form:
2866 * {0-6:1;7-12:2;13:3.....} or {0-6:GREEN;7-12:YELLOW:...}
2867 * dependant upon whether this is an integer or enumerated array resectively
2868 * (if enum_nvs isn't set to NULL this is assumed to be an enumerated array);
2869 * where 0-6 is the range of indexes with value 1 (or GREEN), 7-12 the range
2870 * with value 2 (or YELLOW), and so forth. size is the array size and llimit
2871 * and ulimit are the lower and upper limits of the array values printed
2872 * respectively. For enumerated arrays enum_nvs carries the list of name
2873 * and value pairs and ulimit and llimit parameters are ignored and instead
2874 * determined from the enum_nvs list.
2876 static void
2877 print_int_array(
2878 FILE *fp,
2879 int arr[],
2880 uint32_t size,
2881 int llimit,
2882 int ulimit,
2883 str_val_nd_t *enum_nvs,
2884 int tab_inserts)
2886 int x, y;
2887 uint32_t first, last;
2888 boolean_t first_entry; /* first 'ranges:value' to be printed ? */
2889 boolean_t first_range; /* first range for a value to be printed ? */
2890 boolean_t found_range; /* did we find a range for this value ? */
2892 IPQOSCDBG4(L0, "In print_int_array: size: %u, llimit: %u, ulimit: %u, "
2893 "enum_nvs: %x \n", size, llimit, ulimit, enum_nvs);
2896 * if an enumeration retrieve value range.
2898 if (enum_nvs != NULL)
2899 get_str_val_value_range(enum_nvs, &llimit, &ulimit);
2902 * print opening curl.
2904 (void) fprintf(fp, "%c\n", CURL_BEGIN);
2905 PRINT_TABS(fp, tab_inserts + 1);
2907 first_entry = B_TRUE;
2909 * for each value in range.
2911 for (x = llimit; x <= ulimit; x++) {
2912 found_range = B_FALSE;
2913 first_range = B_TRUE;
2914 y = 0;
2916 * scan array and print ranges of indexes with value x.
2918 while (y < size) {
2920 * get first occurence of value for this range.
2922 while ((arr[y] != x) && (y < size))
2923 y++;
2924 if (y == size) {
2925 break;
2926 } else {
2927 found_range = B_TRUE;
2929 first = y;
2932 * get last occurence of value for this range.
2934 while ((arr[y] == x) && (y < size))
2935 y++;
2936 last = y - 1;
2939 * print entry delimiter (semi-colon)? It must be
2940 * the first range for this value and this mustn't
2941 * be the first 'ranges:value' entry.
2943 if (!first_entry && first_range) {
2944 (void) fprintf(fp, ";\n");
2945 PRINT_TABS(fp, tab_inserts + 1);
2946 } else {
2947 first_entry = B_FALSE;
2951 * print comma (range delimeter) only if there was
2952 * a previous range for this value.
2954 if (!first_range) {
2955 (void) fprintf(fp, ",");
2956 } else {
2957 first_range = B_FALSE;
2961 * print range.
2963 printrange(fp, first, last);
2966 * only print a colon and value if we found a range with
2967 * this value.
2969 if (found_range) {
2970 (void) fprintf(fp, ":");
2973 * print numeric/symbolic value.
2975 if (enum_nvs) {
2976 printenum(fp, x, enum_nvs);
2977 } else {
2978 (void) fprintf(fp, "%d", x);
2984 * print closing curl.
2986 (void) fprintf(fp, "\n");
2987 PRINT_TABS(fp, tab_inserts);
2988 (void) fprintf(fp, "%c\n", CURL_END);
2991 /* print the protocol name for proto, or if unknown protocol number proto. */
2992 static void
2993 printproto(
2994 FILE *fp,
2995 uint8_t proto)
2998 struct protoent *pent;
3000 pent = getprotobynumber(proto);
3001 if (pent != NULL) {
3002 (void) fprintf(fp, "%s\n", pent->p_name);
3003 } else {
3004 (void) fprintf(fp, "%u\n", proto);
3009 * prints the name associated with interface with index ifindex to fp.
3010 * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCESS.
3012 static int
3013 printifname(
3014 FILE *fp,
3015 int ifindex)
3018 int s;
3019 struct lifconf lc;
3020 struct lifnum ln;
3021 struct lifreq *lr;
3022 char *buf;
3023 int len;
3024 char *cp;
3025 int ret;
3026 int x;
3027 int idx;
3029 /* open socket */
3031 if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
3032 ipqos_msg(MT_ENOSTR, gettext("opening AF_INET socket"));
3033 return (IPQOS_CONF_ERR);
3036 /* get number of lifreq structs that need to be alloc'd for */
3038 ln.lifn_family = AF_UNSPEC;
3039 ln.lifn_flags = 0;
3040 ret = ioctl(s, SIOCGLIFNUM, &ln);
3041 if (ret < 0) {
3042 ipqos_msg(MT_ENOSTR, "SIOCLIFNUM ioctl");
3043 (void) close(s);
3044 return (IPQOS_CONF_ERR);
3047 /* allocate buffer for SIOGLIFCONF ioctl */
3049 len = ln.lifn_count * sizeof (struct lifreq);
3050 buf = malloc(len);
3051 if (buf == NULL) {
3052 ipqos_msg(MT_ENOSTR, "malloc");
3053 (void) close(s);
3054 return (IPQOS_CONF_ERR);
3057 /* setup lifconf params for ioctl */
3059 lc.lifc_family = AF_UNSPEC;
3060 lc.lifc_flags = 0;
3061 lc.lifc_len = len;
3062 lc.lifc_buf = buf;
3064 /* do SIOCGLIFCONF ioctl */
3066 ret = ioctl(s, SIOCGLIFCONF, &lc);
3067 if (ret < 0) {
3068 ipqos_msg(MT_ENOSTR, "SIGLIFCONF");
3069 (void) close(s);
3070 free(buf);
3071 return (IPQOS_CONF_ERR);
3073 (void) close(s);
3076 * for each interface name given in the returned lifreq list get
3077 * it's index and compare with ifindex param. Break if equal.
3079 for (x = ln.lifn_count, lr = lc.lifc_req; x > 0; x--, lr++) {
3080 ret = readifindex(lr->lifr_name, &idx);
3081 if (ret != IPQOS_CONF_SUCCESS) {
3082 free(buf);
3083 return (IPQOS_CONF_ERR);
3085 if (idx == ifindex) {
3086 break;
3089 free(buf);
3091 if (x == 0) {
3092 IPQOSCDBG1(L1, "Failed to find if index %u in returned "
3093 "if list.\n", ifindex);
3094 return (IPQOS_CONF_ERR);
3096 /* truncate any logical suffix */
3098 if ((cp = strchr(lr->lifr_name, '@')) != NULL) {
3099 *cp = NULL;
3102 /* print interface name */
3103 (void) fprintf(fp, "%s\n", lr->lifr_name);
3105 return (IPQOS_CONF_SUCCESS);
3109 * print to fp the enumeration clause evaluating to the value val using the
3110 * names/values given in enum_nvs.
3112 static void
3113 printenum(
3114 FILE *fp,
3115 uint32_t val,
3116 str_val_nd_t *enum_nvs)
3119 boolean_t isfirstval = B_TRUE;
3120 str_val_nd_t *name_val = enum_nvs;
3122 /* for each value in enum_nvs if same bit set in val print name */
3124 while (name_val) {
3125 if ((name_val->sv.value & val) == name_val->sv.value) {
3126 if (isfirstval == B_TRUE) {
3127 (void) fprintf(fp, "%s", name_val->sv.string);
3128 isfirstval = B_FALSE;
3129 } else {
3130 (void) fprintf(fp, ", %s", name_val->sv.string);
3133 name_val = name_val->next;
3138 /* prints the service name of port, or if unknown the number to fp. */
3139 static void
3140 printport(
3141 FILE *fp,
3142 uint16_t port)
3145 struct servent *sent;
3147 sent = getservbyport(port, NULL);
3148 if (sent != NULL) {
3149 (void) fprintf(fp, "%s\n", sent->s_name);
3150 } else {
3151 (void) fprintf(fp, "%u\n", ntohs(port));
3156 * prints tp fp the name and value of all user specifiable parameters in the
3157 * nvlist.
3158 * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCESS.
3160 static int
3161 printnvlist(
3162 FILE *fp,
3163 char *module,
3164 nvlist_t *nvl,
3165 int printall, /* are we want ip addresses printing if node name */
3166 ipqos_conf_filter_t *flt, /* used to determine if node name set */
3167 int tab_inserts,
3168 place_t place)
3170 FILE *tfp;
3171 nvpair_t *nvp;
3172 char *name;
3173 ipqos_nvtype_t type;
3174 str_val_nd_t *enum_nvs;
3175 int ret;
3176 char dfltst[IPQOS_VALST_MAXLEN+1];
3177 char *param;
3178 int openerr;
3179 int res;
3181 IPQOSCDBG0(L1, "In printnvlist\n");
3184 /* open stream to types file */
3186 tfp = validmod(module, &openerr);
3187 if (tfp == NULL) {
3188 if (openerr) {
3189 ipqos_msg(MT_ENOSTR, "fopen");
3191 return (IPQOS_CONF_ERR);
3195 /* go through list getting param name and type and printing it */
3197 nvp = nvlist_next_nvpair(nvl, NULL);
3198 while (nvp) {
3200 /* get nvpair name */
3201 name = nvpair_name(nvp);
3202 IPQOSCDBG1(L0, "processing element %s.\n", name);
3204 /* skip ipgpc params that are not explicitly user settable */
3206 if (strcmp(name, IPGPC_FILTER_TYPE) == 0 ||
3207 strcmp(name, IPGPC_SADDR_MASK) == 0 ||
3208 strcmp(name, IPGPC_DADDR_MASK) == 0 ||
3209 strcmp(name, IPGPC_SPORT_MASK) == 0 ||
3210 strcmp(name, IPGPC_DPORT_MASK) == 0) {
3211 nvp = nvlist_next_nvpair(nvl, nvp);
3212 continue;
3215 param = SHORT_NAME(name);
3218 * get parameter type from types file.
3220 place = PL_ANY;
3221 ret = readtype(tfp, module, param, &type, &enum_nvs, dfltst,
3222 B_TRUE, &place);
3223 if (ret != IPQOS_CONF_SUCCESS) {
3224 return (ret);
3228 * for map entries we don't print the map value, only
3229 * the index value it was derived from.
3231 if (place == PL_MAP) {
3232 nvp = nvlist_next_nvpair(nvl, nvp);
3233 continue;
3237 * the ifindex is converted to the name and printed out
3238 * so print the parameter name as ifname.
3240 if (strcmp(name, IPGPC_IF_INDEX) == 0) {
3241 PRINT_TABS(fp, tab_inserts);
3242 (void) fprintf(fp, "%s ", IPQOS_IFNAME_STR);
3244 * we may not print the address due to us instead printing
3245 * the node name in printfilter, therefore we leave the
3246 * printing of the parameter in the addresses switch case code.
3248 } else if ((strcmp(name, IPGPC_SADDR) != 0 &&
3249 strcmp(name, IPGPC_DADDR) != 0)) {
3250 PRINT_TABS(fp, tab_inserts);
3251 (void) fprintf(fp, "%s ", param);
3254 switch (type) {
3255 case IPQOS_DATA_TYPE_IFINDEX: {
3256 uint32_t ifidx;
3258 (void) nvpair_value_uint32(nvp, &ifidx);
3259 (void) printifname(fp, ifidx);
3260 break;
3262 case IPQOS_DATA_TYPE_BOOLEAN: {
3263 boolean_t bl;
3265 (void) nvpair_value_uint32(nvp,
3266 (uint32_t *)&bl);
3267 (void) fprintf(fp, "%s\n",
3268 bl == B_TRUE ? "true" : "false");
3269 break;
3271 case IPQOS_DATA_TYPE_ACTION: {
3272 char *strval;
3274 (void) nvpair_value_string(nvp, &strval);
3275 print_action_nm(fp, strval);
3276 break;
3278 case IPQOS_DATA_TYPE_STRING: {
3279 char *strval;
3281 (void) nvpair_value_string(nvp, &strval);
3282 (void) fprintf(fp, "%s\n",
3283 quote_ws_string(strval));
3284 break;
3286 case IPQOS_DATA_TYPE_ADDRESS: {
3287 uint_t tmp;
3288 in6_addr_t *addr;
3289 char addrstr[INET6_ADDRSTRLEN];
3290 uchar_t ftype;
3291 int af;
3292 in6_addr_t *mask;
3295 * skip addresses that have node names for
3296 * non printall listings.
3298 if (printall == 0 &&
3299 (strcmp(nvpair_name(nvp), IPGPC_SADDR) ==
3300 0 && flt->src_nd_name ||
3301 strcmp(nvpair_name(nvp), IPGPC_DADDR) ==
3302 0 && flt->dst_nd_name)) {
3303 break;
3306 /* we skipped this above */
3308 PRINT_TABS(fp, tab_inserts);
3309 (void) fprintf(fp, "%s ", param);
3311 (void) nvpair_value_uint32_array(nvp,
3312 (uint32_t **)&addr, &tmp);
3314 /* get filter type */
3316 (void) nvlist_lookup_byte(nvl,
3317 IPGPC_FILTER_TYPE, &ftype);
3318 if (ftype == IPGPC_V4_FLTR) {
3319 af = AF_INET;
3320 addr = (in6_addr_t *)
3321 &V4_PART_OF_V6((*addr));
3322 } else {
3323 af = AF_INET6;
3325 /* get mask */
3327 if (strcmp(nvpair_name(nvp), IPGPC_SADDR) ==
3328 0) {
3329 ret = nvlist_lookup_uint32_array(nvl,
3330 IPGPC_SADDR_MASK,
3331 (uint32_t **)&mask, &tmp);
3332 } else {
3333 ret = nvlist_lookup_uint32_array(nvl,
3334 IPGPC_DADDR_MASK,
3335 (uint32_t **)&mask, &tmp);
3338 /* print address/mask to fp */
3340 (void) fprintf(fp, "%s/%u\n",
3341 inet_ntop(af, addr, addrstr,
3342 INET6_ADDRSTRLEN), masktocidr(af, mask));
3343 break;
3345 case IPQOS_DATA_TYPE_ENUM: {
3346 uint32_t val;
3348 (void) nvpair_value_uint32(nvp, &val);
3351 * print list of tokens resulting in val
3353 (void) fprintf(fp, "{ ");
3354 printenum(fp, val, enum_nvs);
3355 (void) fprintf(fp, " }\n");
3356 break;
3358 case IPQOS_DATA_TYPE_PORT: {
3359 uint16_t port;
3361 (void) nvpair_value_uint16(nvp, &port);
3362 printport(fp, port);
3363 break;
3365 case IPQOS_DATA_TYPE_PROTO: {
3366 uint8_t proto;
3368 (void) nvpair_value_byte(nvp, &proto);
3369 printproto(fp, proto);
3370 break;
3372 case IPQOS_DATA_TYPE_M_INDEX:
3373 case IPQOS_DATA_TYPE_UINT8: {
3374 uchar_t u8;
3376 (void) nvpair_value_byte(nvp, &u8);
3377 (void) fprintf(fp, "%u\n", u8);
3378 break;
3380 case IPQOS_DATA_TYPE_UINT16: {
3381 uint16_t u16;
3383 (void) nvpair_value_uint16(nvp, &u16);
3384 (void) fprintf(fp, "%u\n", u16);
3385 break;
3387 case IPQOS_DATA_TYPE_INT16: {
3388 int16_t i16;
3390 (void) nvpair_value_int16(nvp, &i16);
3391 (void) fprintf(fp, "%d\n", i16);
3392 break;
3394 case IPQOS_DATA_TYPE_UINT32: {
3395 uint32_t u32;
3397 (void) nvpair_value_uint32(nvp, &u32);
3398 (void) fprintf(fp, "%u\n", u32);
3399 break;
3401 case IPQOS_DATA_TYPE_INT32: {
3402 int i32;
3404 (void) nvpair_value_int32(nvp, &i32);
3405 (void) fprintf(fp, "%d\n", i32);
3406 break;
3408 case IPQOS_DATA_TYPE_INT_ARRAY: {
3409 str_val_nd_t *arr_enum_nvs = NULL;
3410 uint32_t size;
3411 int llimit, ulimit;
3412 int *arr;
3414 (void) nvpair_value_int32_array(nvp, &arr,
3415 &size);
3418 * read array info from types file.
3420 res = read_int_array_info(dfltst,
3421 &arr_enum_nvs, &size, &llimit, &ulimit,
3422 module);
3425 * print array with numbers, or symbols
3426 * if enumerated.
3428 if (res == IPQOS_CONF_SUCCESS) {
3429 print_int_array(fp, arr, size,
3430 llimit, ulimit, arr_enum_nvs,
3431 tab_inserts);
3432 if (arr_enum_nvs != NULL) {
3433 free_str_val_entrys(
3434 arr_enum_nvs);
3437 break;
3439 case IPQOS_DATA_TYPE_USER: {
3440 uid_t uid;
3442 (void) nvpair_value_int32(nvp, (int *)&uid);
3443 printuser(fp, uid);
3444 break;
3446 #ifdef _IPQOS_CONF_DEBUG
3447 default: {
3449 * we should have catered for all used data
3450 * types that readtype returns.
3452 assert(1);
3454 #endif
3457 nvp = nvlist_next_nvpair(nvl, nvp);
3460 (void) fclose(tfp);
3461 return (IPQOS_CONF_SUCCESS);
3465 * print a parameter clause for the parmeters given in params to fp.
3466 * If printall is set, then the originator of the parameter object is printed.
3467 * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCESS.
3469 static int
3470 printparams(
3471 FILE *fp,
3472 char *module,
3473 ipqos_conf_params_t *params,
3474 int printall,
3475 int tab_inserts)
3478 int res;
3480 /* print opening clause */
3482 PRINT_TABS(fp, tab_inserts);
3483 (void) fprintf(fp, IPQOS_CONF_PARAMS_STR " {\n");
3485 /* print originator name if printall flag set */
3487 if (printall) {
3488 PRINT_TABS(fp, tab_inserts + 1);
3489 (void) fprintf(stdout, "Originator %s\n",
3490 quote_ws_string(get_originator_nm(params->originator)));
3493 /* print global stats */
3495 PRINT_TABS(fp, tab_inserts + 1);
3496 (void) fprintf(fp, IPQOS_CONF_GLOBAL_STATS_STR " %s\n",
3497 params->stats_enable == B_TRUE ? "true" : "false");
3499 /* print module specific parameters */
3500 res = printnvlist(fp, module, params->nvlist, printall, NULL,
3501 tab_inserts + 1, PL_PARAMS);
3502 if (res != IPQOS_CONF_SUCCESS) {
3503 return (res);
3506 PRINT_TABS(fp, tab_inserts);
3507 (void) fprintf(fp, "}\n");
3509 return (IPQOS_CONF_SUCCESS);
3513 * print the interpreted name of the action_nm parameter if it is a special
3514 * action, else action_nm verbatim to fp parameter.
3516 static void
3517 print_action_nm(FILE *fp, char *action_nm)
3520 if (strcmp(action_nm, IPP_ANAME_CONT) == 0) {
3521 (void) fprintf(fp, IPQOS_CONF_CONT_STR "\n");
3522 } else if (strcmp(action_nm, IPP_ANAME_DEFER) == 0) {
3523 (void) fprintf(fp, IPQOS_CONF_DEFER_STR "\n");
3524 } else if (strcmp(action_nm, IPP_ANAME_DROP) == 0) {
3525 (void) fprintf(fp, IPQOS_CONF_DROP_STR "\n");
3526 } else {
3527 (void) fprintf(fp, "%s\n", quote_ws_string(action_nm));
3532 * print a class clause for class to fp. If printall is set the originator
3533 * is printed.
3535 static void
3536 printclass(
3537 FILE *fp,
3538 ipqos_conf_class_t *class,
3539 int printall,
3540 int tab_inserts)
3543 /* print opening clause */
3545 PRINT_TABS(fp, tab_inserts);
3546 (void) fprintf(fp, IPQOS_CONF_CLASS_STR " {\n");
3549 /* if printall flag print originator name */
3551 if (printall) {
3552 PRINT_TABS(fp, tab_inserts + 1);
3553 (void) fprintf(stdout, "Originator %s\n",
3554 get_originator_nm(class->originator));
3557 /* print name, next action and stats enable */
3559 PRINT_TABS(fp, tab_inserts + 1);
3560 (void) fprintf(fp, IPQOS_CONF_NAME_STR " %s\n",
3561 quote_ws_string(class->name));
3562 PRINT_TABS(fp, tab_inserts + 1);
3563 (void) fprintf(fp, IPQOS_CONF_NEXT_ACTION_STR " ");
3564 print_action_nm(fp, class->alist->name);
3565 PRINT_TABS(fp, tab_inserts + 1);
3566 (void) fprintf(fp, IPQOS_CONF_STATS_ENABLE_STR " %s\n",
3567 class->stats_enable == B_TRUE ? "true" : "false");
3569 PRINT_TABS(fp, tab_inserts);
3570 (void) fprintf(fp, "}\n");
3574 * Returns a ptr to the originator name associated with origid. If unknown
3575 * id returns ptr to "unknown".
3576 * RETURNS: ptr to originator name, or if id not known "unknown".
3578 static char *
3579 get_originator_nm(uint32_t origid)
3582 int x;
3584 /* scan originators table for origid */
3586 for (x = 0; originators[x].value != -1 &&
3587 originators[x].value != origid; x++) {}
3589 /* if we've reached end of array due to unknown type return "unknown" */
3591 if (originators[x].value == -1) {
3592 return ("unknown");
3595 return (originators[x].string);
3599 * print a filter clause for filter pointed to by filter out to fp. If printall
3600 * is set then the originator is printed, for filters with node names instance
3601 * numbers are printed, and the filter pointer isn't advanced to point at the
3602 * last instance of the printed filter.
3603 * RETURNS: IPQOS_CONF_ERR on errors, else IPQOS_CONF_SUCCESS.
3605 static int
3606 printfilter(
3607 FILE *fp,
3608 char *module,
3609 ipqos_conf_filter_t **filter,
3610 int printall,
3611 int tab_inserts)
3614 int res;
3616 /* print opening clause */
3618 PRINT_TABS(fp, tab_inserts);
3619 (void) fprintf(fp, IPQOS_CONF_FILTER_STR " {\n");
3621 /* print originator if printall flag set */
3623 if (printall) {
3624 PRINT_TABS(fp, tab_inserts + 1);
3625 (void) fprintf(stdout, "Originator %s\n",
3626 quote_ws_string(get_originator_nm((*filter)->originator)));
3629 /* print name and class */
3631 PRINT_TABS(fp, tab_inserts + 1);
3632 (void) fprintf(fp, IPQOS_CONF_NAME_STR " %s\n",
3633 quote_ws_string((*filter)->name));
3634 PRINT_TABS(fp, tab_inserts + 1);
3635 (void) fprintf(fp, IPQOS_CONF_CLASS_STR " %s\n",
3636 quote_ws_string((*filter)->class_name));
3638 /* print the instance if printall and potential mhomed addresses */
3640 if (printall && ((*filter)->src_nd_name || (*filter)->dst_nd_name)) {
3641 PRINT_TABS(fp, tab_inserts + 1);
3642 (void) fprintf(fp, "Instance %u\n", (*filter)->instance);
3645 /* print node names if any */
3647 if ((*filter)->src_nd_name) {
3648 PRINT_TABS(fp, tab_inserts + 1);
3649 (void) fprintf(fp, "%s %s\n", strchr(IPGPC_SADDR, '.') + 1,
3650 (*filter)->src_nd_name);
3652 if ((*filter)->dst_nd_name) {
3653 PRINT_TABS(fp, tab_inserts + 1);
3654 (void) fprintf(fp, "%s %s\n", strchr(IPGPC_DADDR, '.') + 1,
3655 (*filter)->dst_nd_name);
3658 /* print ip_version enumeration if set */
3660 if ((*filter)->ip_versions != 0) {
3661 PRINT_TABS(fp, tab_inserts + 1);
3662 (void) fprintf(fp, IPQOS_CONF_IP_VERSION_STR " {");
3663 if (VERSION_IS_V4(*filter)) {
3664 (void) fprintf(fp, " V4");
3666 if (VERSION_IS_V6(*filter)) {
3667 (void) fprintf(fp, " V6");
3669 (void) fprintf(fp, " }\n");
3672 /* print other module specific parameters parameters */
3674 res = printnvlist(fp, module, (*filter)->nvlist, printall, *filter,
3675 tab_inserts + 1, PL_FILTER);
3676 if (res != IPQOS_CONF_SUCCESS) {
3677 return (res);
3680 PRINT_TABS(fp, tab_inserts);
3681 (void) fprintf(fp, "}\n");
3684 * if not printall advance filter parameter to last instance of this
3685 * filter.
3688 if (!printall) {
3689 for (;;) {
3690 if ((*filter)->next == NULL ||
3691 strcmp((*filter)->name, (*filter)->next->name) !=
3692 0) {
3693 break;
3695 *filter = (*filter)->next;
3699 return (IPQOS_CONF_SUCCESS);
3703 * Returns a pointer to str if no whitespace is present, else it returns
3704 * a pointer to a string with the contents of str enclose in double quotes.
3705 * This returned strings contents may change in subsequent calls so a copy
3706 * should be made of it if the caller wishes to retain it.
3708 static char *
3709 quote_ws_string(const char *str)
3711 static char *buf = NULL;
3712 const char *cp; /* we don't modify the contents of str so const */
3714 IPQOSCDBG0(L0, "In quote_ws_string\n");
3717 * Just return str if no whitespace.
3719 for (cp = str; (*cp != '\0') && !isspace(*cp); cp++)
3721 if (*cp == '\0')
3722 return ((char *)str);
3724 if (buf == NULL) {
3726 * if first run just allocate buffer of
3727 * strlen(str) + 2 quote characters + NULL terminator.
3729 buf = malloc(strlen(str) + 3);
3730 } else if ((strlen(str) + 2) > strlen(buf)) {
3732 * Not first run, so check if we have a big enough buffer
3733 * and if not reallocate the buffer to a sufficient size.
3735 buf = realloc(buf, strlen(str) + 3);
3737 if (buf == NULL)
3738 return ("");
3741 * copy string into buffer with quotes.
3743 (void) strcpy(buf, "\"");
3744 (void) strcat(buf, str);
3745 (void) strcat(buf, "\"");
3747 return (buf);
3751 * print an action clause for action to fp. If the printall flag is set
3752 * then all filters and classes (regardless of their originator) and
3753 * their originators are displayed.
3754 * RETURNS: IPQOS_CONF_ERR on errors, else IPQOS_CONF_SUCCESS.
3756 static int
3757 printaction(
3758 FILE *fp,
3759 ipqos_conf_action_t *action,
3760 int printall,
3761 int tab_inserts)
3764 ipqos_conf_filter_t *flt;
3765 ipqos_conf_class_t *cls;
3766 int res;
3768 /* print opening clause, module and name */
3770 PRINT_TABS(fp, tab_inserts);
3771 (void) fprintf(fp, IPQOS_CONF_ACTION_STR " {\n");
3772 PRINT_TABS(fp, tab_inserts + 1);
3773 (void) fprintf(fp, IPQOS_CONF_MODULE_STR " %s\n",
3774 quote_ws_string(action->module));
3775 PRINT_TABS(fp, tab_inserts + 1);
3776 (void) fprintf(fp, "name %s\n", quote_ws_string(action->name));
3778 /* print params clause */
3780 (void) fprintf(fp, "\n");
3781 res = printparams(fp, action->module, action->params, printall,
3782 tab_inserts + 1);
3783 if (res != IPQOS_CONF_SUCCESS) {
3784 return (res);
3788 * print classes clause for each class if printall is set, else
3789 * just ipqosconf created or permanent classes.
3791 for (cls = action->classes; cls != NULL; cls = cls->next) {
3792 if (printall ||
3793 cls->originator == IPP_CONFIG_IPQOSCONF ||
3794 cls->originator == IPP_CONFIG_PERMANENT) {
3795 (void) fprintf(fp, "\n");
3796 printclass(fp, cls, printall, tab_inserts + 1);
3801 * print filter clause for each filter if printall is set, else
3802 * just ipqosconf created or permanent filters.
3804 for (flt = action->filters; flt != NULL; flt = flt->next) {
3805 if (printall ||
3806 flt->originator == IPP_CONFIG_IPQOSCONF ||
3807 flt->originator == IPP_CONFIG_PERMANENT) {
3808 (void) fprintf(fp, "\n");
3809 res = printfilter(fp, action->module, &flt, printall,
3810 tab_inserts + 1);
3811 if (res != IPQOS_CONF_SUCCESS) {
3812 return (res);
3817 PRINT_TABS(fp, tab_inserts);
3818 (void) fprintf(fp, "}\n");
3820 return (IPQOS_CONF_SUCCESS);
3825 /* *************************************************************** */
3828 static void
3829 list_end(
3830 ipqos_list_el_t **listp,
3831 ipqos_list_el_t ***lendpp)
3833 *lendpp = listp;
3834 while (**lendpp != NULL) {
3835 *lendpp = &(**lendpp)->next;
3839 static void
3840 add_to_list(
3841 ipqos_list_el_t **listp,
3842 ipqos_list_el_t *el)
3844 el->next = *listp;
3845 *listp = el;
3849 * given mask calculates the number of bits it spans. The mask must be
3850 * continuous.
3851 * RETURNS: number of bits spanned.
3853 static int
3854 masktocidr(
3855 int af,
3856 in6_addr_t *mask)
3858 int zeros = 0;
3859 int byte;
3860 int cidr;
3863 * loop through from lowest byte to highest byte counting the
3864 * number of zero bits till hitting a one bit.
3866 for (byte = 15; byte >= 0; byte--) {
3868 * zero byte, so add 8 to zeros.
3870 if (mask->s6_addr[byte] == 0) {
3871 zeros += 8;
3873 * non-zero byte, add zero count to zeros.
3875 } else {
3876 zeros += (ffs((int)mask->s6_addr[byte]) - 1);
3877 break;
3881 * translate zero bits to 32 or 128 bit mask based on af.
3883 if (af == AF_INET) {
3884 cidr = 32 - zeros;
3885 } else {
3886 cidr = 128 - zeros;
3889 return (cidr);
3893 * Sets the first prefix_len bits in the v4 or v6 address (based upon af)
3894 * contained in the v6 address referenced by addr to 1.
3896 static void
3897 setmask(int prefix_len, in6_addr_t *addr, int af)
3900 int i;
3901 int shift;
3902 int maskstartbit = 128 - prefix_len;
3903 int end_u32;
3905 IPQOSCDBG2(L1, "In setmask, prefix_len: %u, af: %s\n", prefix_len,
3906 af == AF_INET ? "AF_INET" : "AF_INET6");
3908 /* zero addr */
3909 bzero(addr, sizeof (in6_addr_t));
3912 /* set which 32bits in *addr are relevant to this af */
3914 if (af == AF_INET) {
3915 end_u32 = 3;
3916 maskstartbit = 32 - prefix_len;
3917 /* AF_INET6 */
3918 } else {
3919 end_u32 = 0;
3922 * go through each of the 32bit quantities in 128 bit in6_addr_t
3923 * and set appropriate bits according to prefix_len.
3925 for (i = 3; i >= end_u32; i--) {
3927 /* does the prefix apply to this 32bits? */
3929 if (maskstartbit < ((4 - i) * 32)) {
3931 /* is this 32bits fully masked? */
3933 if (maskstartbit <= ((3 - i) * 32)) {
3934 shift = 0;
3935 } else {
3936 shift = maskstartbit % 32;
3938 addr->_S6_un._S6_u32[i] = (uint32_t)~0;
3939 addr->_S6_un._S6_u32[i] =
3940 addr->_S6_un._S6_u32[i] >> shift;
3941 addr->_S6_un._S6_u32[i] =
3942 addr->_S6_un._S6_u32[i] << shift;
3945 /* translate to NBO */
3946 addr->_S6_un._S6_u32[i] = htonl(addr->_S6_un._S6_u32[i]);
3951 * search nvlist for an element with the name specified and return a ptr
3952 * to it if found.
3953 * RETURNS: pointer to nvpair named name if found, else NULL.
3955 static nvpair_t *
3956 find_nvpair(nvlist_t *nvl, char *name)
3959 nvpair_t *nvp;
3960 nvpair_t *match = NULL;
3961 char *nvp_name;
3963 IPQOSCDBG0(L1, "In find_nvpair\n");
3965 nvp = nvlist_next_nvpair(nvl, NULL);
3966 while (nvp) {
3967 nvp_name = nvpair_name(nvp);
3968 if (strcmp(name, nvp_name) == 0) {
3969 match = nvp;
3971 nvp = nvlist_next_nvpair(nvl, nvp);
3974 return (match);
3978 * returns a string containing module_name '.' name.
3979 * RETURNS: IPQOS_CONF_ERR if error, else IPQOS_CONF_SUCCESS.
3981 static char *
3982 prepend_module_name(
3983 char *name,
3984 char *module)
3987 char *ret;
3989 IPQOSCDBG0(L2, "In prepend_module_name\n");
3991 ret = malloc(strlen(module) + strlen(".") + strlen(name) + 1);
3992 if (ret == NULL) {
3993 ipqos_msg(MT_ENOSTR, "malloc");
3994 return (NULL);
3997 (void) strcpy(ret, module);
3998 (void) strcat(ret, ".");
3999 (void) strcat(ret, name);
4001 return (ret);
4004 #if 0
4007 * check if element with matching s1 and s2 string is in table table.
4008 * RETURNS: 1 if found else 0.
4010 static int
4011 in_str_str_table(
4012 str_str_t *table,
4013 char *s1,
4014 char *s2)
4017 str_str_t *ss = table;
4019 /* loop through table till matched or end */
4021 while (ss->s1[0] != '\0' &&
4022 (strcmp(ss->s1, s1) != 0 || strcmp(ss->s2, s2) != 0)) {
4023 ss++;
4026 if (ss->s1[0] != '\0') {
4027 return (1);
4030 return (0);
4032 #endif /* 0 */
4035 * check whether name is a valid action/class/filter name.
4036 * RETURNS: IPQOS_CONF_ERR if invalid name else IPQOS_CONF_SUCCESS.
4038 static int
4039 valid_name(char *name)
4042 IPQOSCDBG1(L1, "In valid_name: name: %s\n", name);
4044 /* first char can't be '!' */
4045 if (name[0] == '!') {
4046 ipqos_msg(MT_ERROR, gettext("Name not allowed to start with "
4047 "'!', line %u.\n"), lineno);
4048 return (IPQOS_CONF_ERR);
4051 /* can't exceed IPQOS_CONF_NAME_LEN size */
4052 if (strlen(name) >= IPQOS_CONF_NAME_LEN) {
4053 ipqos_msg(MT_ERROR, gettext("Name exceeds maximum name length "
4054 "line %u.\n"), lineno);
4055 return (IPQOS_CONF_ERR);
4058 return (IPQOS_CONF_SUCCESS);
4061 /* ********************* string value manip fns ************************** */
4065 * searches through the str_val_nd_t list of string value pairs finding
4066 * the minimum and maximum values for value and places them in the
4067 * integers pointed at by min and max.
4069 static void
4070 get_str_val_value_range(
4071 str_val_nd_t *svnp,
4072 int *min,
4073 int *max)
4075 if (svnp != NULL) {
4076 *min = *max = svnp->sv.value;
4077 svnp = svnp->next;
4079 while (svnp != NULL) {
4080 if (svnp->sv.value > *max)
4081 *max = svnp->sv.value;
4082 if (svnp->sv.value < *min)
4083 *min = svnp->sv.value;
4084 svnp = svnp->next;
4089 * add an entry with string string and value val to sv_entrys.
4090 * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCESS.
4092 static int
4093 add_str_val_entry(
4094 str_val_nd_t **sv_entrys,
4095 char *string,
4096 uint32_t val)
4099 str_val_nd_t *sv_entry;
4101 IPQOSCDBG2(L1, "In add_str_val_entry: string: %s, val: %u\n", string,
4102 val);
4104 /* alloc new node */
4106 sv_entry = malloc(sizeof (str_val_nd_t));
4107 if (sv_entry == NULL) {
4108 return (IPQOS_CONF_ERR);
4111 /* populate node */
4113 sv_entry->sv.string = malloc(strlen(string) + 1);
4114 if (sv_entry->sv.string == NULL) {
4115 free(sv_entry);
4116 ipqos_msg(MT_ENOSTR, "malloc");
4117 return (IPQOS_CONF_ERR);
4118 } else {
4119 (void) strcpy(sv_entry->sv.string, string);
4121 sv_entry->sv.value = val;
4123 /* place at start of sv_entrys list */
4125 sv_entry->next = *sv_entrys;
4126 *sv_entrys = sv_entry;
4128 return (IPQOS_CONF_SUCCESS);
4132 /* frees all the elements of sv_entrys. */
4133 static void
4134 free_str_val_entrys(
4135 str_val_nd_t *sv_entrys)
4138 str_val_nd_t *sve = sv_entrys;
4139 str_val_nd_t *tmp;
4141 IPQOSCDBG0(L1, "In free_str_val_entrys\n");
4143 while (sve) {
4144 free(sve->sv.string);
4145 tmp = sve->next;
4146 free(sve);
4147 sve = tmp;
4152 * finds the value associated with string and assigns it to value ref'd by
4153 * val.
4154 * RETURNS: IPQOS_CONF_ERR if string not found, else IPQOS_CONF_SUCCESS.
4156 static int
4157 str_val_list_lookup(
4158 str_val_nd_t *svs,
4159 char *string,
4160 uint32_t *val)
4163 str_val_nd_t *sv = svs;
4165 IPQOSCDBG1(L1, "In str_val_list_lookup: %s\n", string);
4167 /* loop through list and exit when found or list end */
4169 while (sv != NULL) {
4170 if (strcmp(sv->sv.string, string) == 0) {
4171 break;
4173 sv = sv->next;
4176 /* ret error if not found */
4178 if (sv == NULL) {
4179 return (IPQOS_CONF_ERR);
4182 *val = sv->sv.value;
4184 IPQOSCDBG1(L1, "svll: Value returned is %u\n", *val);
4185 return (IPQOS_CONF_SUCCESS);
4189 /* ************************ conf file read fns ***************************** */
4192 * Reads a uid or username from string 'str' and assigns either the uid
4193 * or associated uid respectively to storage pointed at by 'uid'. The
4194 * function determines whether to read a uid by checking whether the first
4195 * character of 'str' is numeric, in which case it reads a uid; otherwise it
4196 * assumes a username.
4197 * RETURNS: IPQOS_CONF_ERR if a NULL string pointer is passed, the read uid
4198 * doesn't have an entry on the system, or the read username doesn't have an
4199 * entry on the system.
4201 static int
4202 readuser(
4203 char *str,
4204 uid_t *uid)
4206 struct passwd *pwd;
4207 char *lo;
4209 IPQOSCDBG1(L0, "In readuser, str: %s\n", str);
4211 if (str == NULL)
4212 return (IPQOS_CONF_ERR);
4214 * Check if this appears to be a uid, and if so check that a
4215 * corresponding user exists.
4217 if (isdigit((int)str[0])) {
4219 * Read a 32bit integer and check in doing so that
4220 * we have consumed the whole string.
4222 if (readint32(str, (int *)uid, &lo) != IPQOS_CONF_SUCCESS ||
4223 *lo != '\0')
4224 return (IPQOS_CONF_ERR);
4225 if (getpwuid(*uid) == NULL)
4226 return (IPQOS_CONF_ERR);
4228 } else { /* This must be a username, so lookup the uid. */
4229 pwd = getpwnam(str);
4230 if (pwd == NULL) {
4231 return (IPQOS_CONF_ERR);
4232 } else {
4233 *uid = pwd->pw_uid;
4236 return (IPQOS_CONF_SUCCESS);
4240 * Reads a range from range_st, either of form 'a-b' or simply 'a'.
4241 * In the former case lower and upper have their values set to a
4242 * and b respectively; in the later lower and upper have both
4243 * their values set to a.
4244 * RETURNS: IPQOS_CONF_ERR if there's a parse error, else IPQOS_CONF_SUCCESS.
4246 static int
4247 readrange(
4248 char *range_st,
4249 int *lower,
4250 int *upper)
4252 char *cp;
4253 char *end, *end2;
4255 IPQOSCDBG1(L0, "In readrange: string: %s\n", range_st);
4258 * get range boundarys.
4260 cp = strchr(range_st, '-');
4262 if (cp != NULL) { /* we have a range */
4263 *cp++ = '\0';
4264 *lower = (int)strtol(range_st, &end, 10);
4265 *upper = (int)strtol(cp, &end2, 10);
4266 SKIPWS(end);
4267 SKIPWS(end2);
4268 if ((range_st == end) || (*end != NULL) ||
4269 (cp == end) || (*end2 != NULL)) {
4270 IPQOSCDBG0(L0, "Failed reading a-b\n");
4271 return (IPQOS_CONF_ERR);
4274 } else { /* single value */
4276 *lower = *upper = (int)strtol(range_st, &end, 10);
4277 SKIPWS(end);
4278 if ((range_st == end) || (*end != NULL)) {
4279 IPQOSCDBG0(L0, "Failed reading a\n");
4280 return (IPQOS_CONF_ERR);
4284 return (IPQOS_CONF_SUCCESS);
4288 * Reads the values of an integer array from fp whose format is:
4289 * '{'RANGE[,RANGE[..]]:VALUE[;RANGE:VALUE[..]]'}', creates an array of size
4290 * arr_size, applies the values to it and points arrp at this array.
4291 * RANGE is one set of array indexes over which this value is to
4292 * be applied, and VALUE either an integer within the range
4293 * llimit - ulimit, or if enum_nvs isn't NULL, an enumeration value
4294 * found in the list enum_nvs. Those values which aren't explicity set
4295 * will be set to -1.
4297 * RETURNS: IPQOS_CONF_ERR on resource or parse error, else IPQOS_CONF_SUCCESS.
4299 static int
4300 read_int_array(
4301 FILE *fp,
4302 char *first_token,
4303 int **arrp,
4304 uint32_t arr_size,
4305 int llimit,
4306 int ulimit,
4307 str_val_nd_t *enum_nvs)
4310 char buf[5 * IPQOS_CONF_LINEBUF_SZ];
4311 char *token;
4312 char *range;
4313 char *ranges;
4314 char *svalue;
4315 int value;
4316 int res;
4317 char *entry;
4318 char *tmp;
4319 char *end;
4320 int lower, upper;
4321 int x;
4322 uint32_t startln;
4324 IPQOSCDBG4(L0, "In read_int_array: size: %u, lower: %u, upper: %u, "
4325 "first_token: %s\n", arr_size, llimit, ulimit, first_token);
4328 * read beginning curl.
4330 if (first_token[0] != CURL_BEGIN) {
4331 ipqos_msg(MT_ERROR, gettext("\'{\' missing at line "
4332 "%u.\n"), lineno);
4333 return (IPQOS_CONF_ERR);
4337 * allocate and initialise array for holding read values.
4339 *arrp = malloc(arr_size * sizeof (int));
4340 if (*arrp == NULL) {
4341 ipqos_msg(MT_ENOSTR, "malloc");
4342 return (IPQOS_CONF_ERR);
4344 (void) memset(*arrp, -1, arr_size * sizeof (int));
4347 * read whole array declaration string into buffer.
4348 * this is because readtoken doesn't interpret our
4349 * delimeter values specially and may return them
4350 * within another string.
4352 startln = lineno; /* store starting lineno for error reports */
4353 buf[0] = '\0';
4354 res = readtoken(fp, &token);
4355 while ((res != IPQOS_CONF_CURL_END) && (res != IPQOS_CONF_ERR) &&
4356 (res != IPQOS_CONF_EOF)) {
4357 (void) strlcat(buf, token, sizeof (buf));
4358 free(token);
4359 res = readtoken(fp, &token);
4361 if (res != IPQOS_CONF_CURL_END) {
4362 goto array_err;
4364 IPQOSCDBG1(L0, "array declaration buffer contains: %s\n", buf);
4367 * loop reading "ranges ':' value;" till end of buffer.
4369 entry = strtok(buf, ";");
4370 while (entry != NULL) {
4371 svalue = strchr(entry, ':');
4372 if (svalue == NULL) { /* missing value string */
4373 IPQOSCDBG0(L0, "Missing value string\n");
4374 goto array_err;
4376 *svalue++ = '\0';
4377 ranges = entry;
4380 * get value of number or enumerated symbol.
4382 if (enum_nvs) {
4384 * get rid of surrounding whitespace so as not to
4385 * confuse read_enum_value.
4387 SKIPWS(svalue);
4388 tmp = svalue;
4389 while (*tmp != '\0') {
4390 if (isspace(*tmp)) {
4391 *tmp = '\0';
4392 break;
4393 } else {
4394 tmp++;
4399 * read enumeration value.
4401 res = read_enum_value(NULL, svalue, enum_nvs,
4402 (uint32_t *)&value);
4403 if (res != IPQOS_CONF_SUCCESS)
4404 goto array_err;
4405 } else {
4406 value = (int)strtol(svalue, &end, 10);
4407 SKIPWS(end);
4408 if ((svalue == end) || (*end != NULL)) {
4409 IPQOSCDBG0(L0, "Invalid value\n");
4410 goto array_err;
4412 IPQOSCDBG1(L0, "value: %u\n", value);
4415 * check value within valid range.
4417 if ((value < llimit) || (value > ulimit)) {
4418 IPQOSCDBG0(L0, "value out of range\n");
4419 goto array_err;
4424 * loop reading ranges for this value.
4426 range = strtok_r(ranges, ",", &tmp);
4427 while (range != NULL) {
4428 res = readrange(range, &lower, &upper);
4429 if (res != IPQOS_CONF_SUCCESS)
4430 goto array_err;
4431 IPQOSCDBG2(L0, "range: %u - %u\n", lower, upper);
4434 if (upper < lower) {
4435 uint32_t u = lower;
4436 lower = upper;
4437 upper = u;
4441 * check range valid for array size.
4443 if ((lower < 0) || (upper > arr_size)) {
4444 IPQOSCDBG0(L0, "Range out of array "
4445 "dimensions\n");
4446 goto array_err;
4450 * add this value to array indexes within range.
4452 for (x = lower; x <= upper; x++)
4453 (*arrp)[x] = value;
4456 * get next range.
4458 range = strtok_r(NULL, ",", &tmp);
4461 entry = strtok(NULL, ";");
4464 return (IPQOS_CONF_SUCCESS);
4466 array_err:
4467 ipqos_msg(MT_ERROR,
4468 gettext("Array declaration line %u is invalid.\n"), startln);
4469 free(*arrp);
4470 return (IPQOS_CONF_ERR);
4473 static int
4474 readllong(char *str, long long *llp, char **lo)
4477 *llp = strtoll(str, lo, 0);
4478 if (*lo == str) {
4479 return (IPQOS_CONF_ERR);
4481 return (IPQOS_CONF_SUCCESS);
4484 static int
4485 readuint8(char *str, uint8_t *ui8, char **lo)
4488 long long tmp;
4490 if (readllong(str, &tmp, lo) != 0) {
4491 return (IPQOS_CONF_ERR);
4493 if (tmp > UCHAR_MAX || tmp < 0) {
4494 return (IPQOS_CONF_ERR);
4496 *ui8 = (uint8_t)tmp;
4497 return (IPQOS_CONF_SUCCESS);
4500 static int
4501 readuint16(char *str, uint16_t *ui16, char **lo)
4503 long long tmp;
4505 if (readllong(str, &tmp, lo) != IPQOS_CONF_SUCCESS) {
4506 return (IPQOS_CONF_ERR);
4508 if (tmp > USHRT_MAX || tmp < 0) {
4509 return (IPQOS_CONF_ERR);
4511 *ui16 = (uint16_t)tmp;
4512 return (IPQOS_CONF_SUCCESS);
4515 static int
4516 readint16(char *str, int16_t *i16, char **lo)
4518 long long tmp;
4520 if (readllong(str, &tmp, lo) != 0) {
4521 return (IPQOS_CONF_ERR);
4523 if (tmp > SHRT_MAX || tmp < SHRT_MIN) {
4524 return (IPQOS_CONF_ERR);
4526 *i16 = (int16_t)tmp;
4527 return (IPQOS_CONF_SUCCESS);
4530 static int
4531 readint32(char *str, int *i32, char **lo)
4533 long long tmp;
4535 if (readllong(str, &tmp, lo) != IPQOS_CONF_SUCCESS) {
4536 return (IPQOS_CONF_ERR);
4538 if (tmp > INT_MAX || tmp < INT_MIN) {
4539 return (IPQOS_CONF_ERR);
4541 *i32 = tmp;
4542 return (IPQOS_CONF_SUCCESS);
4545 static int
4546 readuint32(char *str, uint32_t *ui32, char **lo)
4548 long long tmp;
4550 if (readllong(str, &tmp, lo) != IPQOS_CONF_SUCCESS) {
4551 return (IPQOS_CONF_ERR);
4553 if (tmp > UINT_MAX || tmp < 0) {
4554 return (IPQOS_CONF_ERR);
4556 *ui32 = (uint32_t)tmp;
4557 return (IPQOS_CONF_SUCCESS);
4561 * retrieves the index associated with the interface named ifname and assigns
4562 * it to the int pointed to by ifindex.
4563 * RETURNS: IPQOS_CONF_ERR on errors, else IPQOS_CONF_SUCCESS.
4565 static int
4566 readifindex(
4567 char *ifname,
4568 int *ifindex)
4571 int s;
4572 struct lifreq lifrq;
4575 /* open socket */
4577 if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
4578 ipqos_msg(MT_ENOSTR, gettext("opening AF_INET socket"));
4579 return (IPQOS_CONF_ERR);
4582 /* copy ifname into lifreq */
4584 (void) strlcpy(lifrq.lifr_name, ifname, LIFNAMSIZ);
4586 /* do SIOGLIFINDEX ioctl */
4588 if (ioctl(s, SIOCGLIFINDEX, (caddr_t)&lifrq) == -1) {
4589 (void) close(s);
4590 return (IPQOS_CONF_ERR);
4593 /* Warn if a virtual interface is specified */
4594 if ((ioctl(s, SIOCGLIFFLAGS, (caddr_t)&lifrq) != -1) &&
4595 (lifrq.lifr_flags & IFF_VIRTUAL)) {
4596 ipqos_msg(MT_WARNING, gettext("Invalid interface"));
4598 (void) close(s);
4599 *ifindex = lifrq.lifr_index;
4600 return (IPQOS_CONF_SUCCESS);
4604 * Case insensitively compares the string in str with IPQOS_CONF_TRUE_STR
4605 * and IPQOS_CONF_FALSE_STR and sets boolean pointed to by bool accordingly.
4606 * RETURNS: if failure to match either IPQOS_CONF_ERR, else IPQOS_CONF_SUCCESS.
4608 static int
4609 readbool(char *str, boolean_t *bool)
4612 if (strcasecmp(str, IPQOS_CONF_TRUE_STR) == 0) {
4613 *bool = B_TRUE;
4614 } else if (strcasecmp(str, IPQOS_CONF_FALSE_STR) == 0) {
4615 *bool = B_FALSE;
4616 } else {
4617 return (IPQOS_CONF_ERR);
4620 return (IPQOS_CONF_SUCCESS);
4624 * reads a protocol name/number from proto_str and assigns the number
4625 * to the uint8 ref'd by proto.
4626 * RETURNS: If not a valid name or protocol number IPQOS_CONF_ERR, else
4627 * IPQOS_CONF_SUCCESS.
4629 static int
4630 readproto(char *proto_str, uint8_t *proto)
4633 struct protoent *pent;
4634 char *lo;
4635 int res;
4637 IPQOSCDBG1(L1, "In readproto: string: %s\n", proto_str);
4639 /* try name lookup */
4641 pent = getprotobyname(proto_str);
4642 if (pent) {
4643 *proto = pent->p_proto;
4645 /* check valid protocol number */
4646 } else {
4647 res = readuint8(proto_str, proto, &lo);
4648 if (res != IPQOS_CONF_SUCCESS || proto == 0) {
4649 return (IPQOS_CONF_ERR);
4653 return (IPQOS_CONF_SUCCESS);
4657 * reads either a port service, or a port number from port_str and assigns
4658 * the associated port number to short ref'd by port.
4659 * RETURNS: If invalid name and number IPQOS_CONF_ERR, else IPQOS_CONF_SUCCESS.
4661 static int
4662 readport(char *port_str, uint16_t *port)
4665 struct servent *sent;
4666 char *tmp;
4668 IPQOSCDBG1(L1, "In readport: string: %s\n", port_str);
4670 /* try service name lookup */
4671 sent = getservbyname(port_str, NULL);
4673 /* failed name lookup so read port number */
4674 if (sent == NULL) {
4675 if (readuint16(port_str, port, &tmp) != IPQOS_CONF_SUCCESS ||
4676 *port == 0) {
4677 return (IPQOS_CONF_ERR);
4679 *port = htons(*port);
4680 } else {
4681 *port = sent->s_port;
4684 return (IPQOS_CONF_SUCCESS);
4689 * Reads a curly brace, a string enclosed in double quotes, or a whitespace/
4690 * curly brace delimited string. If a double quote enclosed string the
4691 * closing quotes need to be on the same line.
4692 * RETURNS:
4693 * on reading a CURL_BEGIN token it returns IPQOS_CONF_CURL_BEGIN,
4694 * on reading a CURL_END token it returns IPQOS_CONF_CURL_END,
4695 * on reading another valid token it returns IPQOS_CONF_SUCCESS.
4696 * for each of these token is set to point at the read string.
4697 * at EOF it returns IPQOS_CONF_EOF and if errors it returns IPQOS_CONF_ERR.
4699 static int
4700 readtoken(
4701 FILE *fp,
4702 char **token)
4705 char *st, *tmp;
4706 int len;
4707 int quoted = 0;
4708 char *cmnt;
4709 char *bpos;
4710 int rembuf;
4712 static char *lo;
4713 static char *buf = NULL;
4714 static int bufsize;
4716 /* if first call initialize line buf to default size */
4718 if (buf == NULL) {
4719 bufsize = IPQOS_CONF_LINEBUF_SZ;
4720 buf = malloc(bufsize);
4721 if (buf == NULL) {
4722 ipqos_msg(MT_ENOSTR, "malloc");
4723 return (IPQOS_CONF_ERR);
4727 /* set buffer postition and size to use whole buffer */
4729 bpos = buf;
4730 rembuf = bufsize;
4734 * loop reading lines until we've read a line with a non-whitespace
4735 * char.
4738 do {
4739 /* if no leftover from previous invocation */
4741 if (lo == NULL) {
4744 * loop reading into buffer doubling if necessary until
4745 * we have either read a complete line or reached the
4746 * end of file.
4748 for (;;) {
4749 st = fgets(bpos, rembuf, fp);
4751 if (st == NULL) {
4753 /* if read error */
4754 if (ferror(fp)) {
4755 free(buf);
4756 buf = NULL;
4757 ipqos_msg(MT_ENOSTR,
4758 "fgets");
4759 return (IPQOS_CONF_ERR);
4761 /* end of file */
4762 } else {
4763 free(buf);
4764 buf = NULL;
4765 *token = NULL;
4766 return (IPQOS_CONF_EOF);
4768 } else {
4769 /* if read a newline */
4771 if (buf[strlen(buf) - 1] == '\n') {
4772 lineno++;
4773 break;
4775 /* if read the last line */
4777 } else if (feof(fp)) {
4778 break;
4781 * not read a full line so buffer size
4782 * is too small, double it and retry.
4784 } else {
4785 bufsize *= 2;
4786 tmp = realloc(buf, bufsize);
4787 if (tmp == NULL) {
4788 ipqos_msg(MT_ENOSTR,
4789 "realloc");
4790 free(buf);
4791 return (IPQOS_CONF_ERR);
4792 } else {
4793 buf = tmp;
4797 * make parameters to fgets read
4798 * into centre of doubled buffer
4799 * so we retain what we've
4800 * already read.
4802 bpos = &buf[(bufsize / 2) - 1];
4803 rembuf = (bufsize / 2) + 1;
4808 st = buf;
4810 /* previous leftover, assign to st */
4812 } else {
4813 st = lo;
4814 lo = NULL;
4817 /* truncate at comment */
4819 cmnt = strchr(st, '#');
4820 if (cmnt) {
4821 *cmnt = '\0';
4824 /* Skip any whitespace */
4826 while (isspace(*st) && st != '\0') {
4827 st++;
4830 } while (*st == '\0');
4833 /* find end of token */
4835 tmp = st;
4837 /* if curl advance 1 char */
4839 if (*tmp == CURL_BEGIN || *tmp == CURL_END) {
4840 tmp++;
4843 /* if dbl quote read until matching quote */
4845 } else if (*tmp == '"') {
4846 quoted++;
4847 tmp = ++st;
4849 while (*tmp != '"' && *tmp != '\n' && *tmp != '\0') {
4850 tmp++;
4852 if (*tmp != '"') {
4853 ipqos_msg(MT_ERROR, gettext("Quoted string exceeds "
4854 "line, line %u.\n"), lineno);
4855 free(buf);
4856 return (IPQOS_CONF_ERR);
4859 /* normal token */
4860 } else {
4861 /* find first whitespace, curl, newline or string end */
4863 while (!isspace(*tmp) && *tmp != CURL_BEGIN &&
4864 *tmp != CURL_END && *tmp != '\n' && *tmp != '\0') {
4865 tmp++;
4869 /* copy token to return */
4870 len = tmp - st;
4871 *token = malloc(len + 1);
4872 if (!*token) {
4873 free(buf);
4874 ipqos_msg(MT_ENOSTR, "malloc");
4875 return (IPQOS_CONF_ERR);
4877 bcopy(st, *token, len);
4878 (*token)[len] = '\0';
4880 /* if just read quoted string remove quote from remaining string */
4882 if (quoted) {
4883 tmp++;
4886 /* if not end of string, store rest for latter parsing */
4888 if (*tmp != '\0' && *tmp != '\n') {
4889 lo = tmp;
4892 /* for curl_end and curl_begin return special ret codes */
4894 if ((*token)[1] == '\0') {
4895 if (**token == CURL_BEGIN) {
4896 return (IPQOS_CONF_CURL_BEGIN);
4897 } else if (**token == CURL_END) {
4898 return (IPQOS_CONF_CURL_END);
4902 return (IPQOS_CONF_SUCCESS);
4906 * Reads an enumeration bitmask definition from line. The format is:
4907 * { NAME=VAL, NAME2=VAL2 }. The resulting names and values are returned.
4908 * RETURNS: NULL on error, else ptr to name/values.
4910 static str_val_nd_t *
4911 read_enum_nvs(char *line, char *module_name)
4914 str_val_nd_t *enum_vals = NULL;
4915 char *cp;
4916 char *start;
4917 char *name = NULL;
4918 int len;
4919 uint32_t val;
4920 int ret;
4921 int readc;
4923 IPQOSCDBG1(L1, "In read_enum_nvs, line: %s\n", line);
4925 /* read opening brace */
4927 cp = strchr(line, CURL_BEGIN);
4928 if (cp == NULL) {
4929 IPQOSCDBG0(L1, "missing curl begin\n");
4930 goto fail;
4931 } else {
4932 start = cp + 1;
4936 * loop reading 'name = value' entrys seperated by comma until
4937 * reach closing brace.
4940 for (;;) {
4941 SKIPWS(start);
4942 if (*start == '\0') {
4943 IPQOSCDBG0(L1, "missing closing bracket\n");
4944 goto fail;
4948 * read name - read until whitespace, '=', closing curl,
4949 * or string end.
4952 for (cp = start;
4953 !isspace(*cp) && *cp != '=' && *cp != CURL_END &&
4954 *cp != '\0'; cp++) {}
4956 if (*cp == '\0') {
4957 IPQOSCDBG0(L1, "Unexpected line end in enum def'n\n");
4958 goto fail;
4960 /* finished definition, exit loop */
4961 } else if (*cp == CURL_END) {
4962 break;
4965 /* store name */
4967 len = cp - start;
4968 name = malloc(len + 1);
4969 if (name == NULL) {
4970 ipqos_msg(MT_ENOSTR, "malloc");
4971 goto fail;
4973 bcopy(start, name, len);
4974 name[len] = NULL;
4975 IPQOSCDBG1(L0, "Stored name: %s\n", name);
4977 /* read assignment */
4979 start = strchr(cp, '=');
4980 if (start == NULL) {
4981 IPQOSCDBG0(L1, "Missing = in enum def'n\n");
4982 goto fail;
4985 /* read value */
4987 ret = sscanf(++start, "%x%n", &val, &readc);
4988 if (ret != 1) {
4989 IPQOSCDBG1(L1, "sscanf of value failed, string: %s\n",
4990 cp);
4991 goto fail;
4994 /* add name value to set */
4996 ret = add_str_val_entry(&enum_vals, name, val);
4997 if (ret != IPQOS_CONF_SUCCESS) {
4998 IPQOSCDBG0(L1, "Failed to add str_val entry\n");
4999 goto fail;
5001 free(name);
5002 name = NULL;
5004 /* try reading comma */
5005 cp = strchr(start, ',');
5007 if (cp != NULL) {
5008 start = cp + 1;
5010 /* no comma, advance to char past value last read */
5011 } else {
5012 start += readc;
5016 return (enum_vals);
5017 fail:
5018 free_str_val_entrys(enum_vals);
5019 if (name != NULL)
5020 free(name);
5022 /* if a parse error */
5024 if (errno == 0) {
5025 ipqos_msg(MT_ERROR, gettext("Types file for module %s is "
5026 "corrupt.\n"), module_name);
5029 return (NULL);
5033 * Given mapped_list with is a comma seperated list of map names, and value,
5034 * which is used to index into these maps, the function creates x new entries
5035 * in nvpp, where x is the number of map names specified. Each of these
5036 * entries has the value from the map in the position indexed by value and
5037 * with name module.${MAP_NAME}. The maps are contained in the modules config
5038 * file and have the form:
5039 * map map1 uint32 1,23,32,45,3
5040 * As you can see the map values are uint32, and along with uint8 are the
5041 * only supported types at the moment.
5043 * RETURNS: IPQOS_CONF_ERR if one of the maps specified in mapped_list
5044 * doesn't exist, if value is not a valid map position for a map, or if
5045 * there's a resource failure. otherwise IPQOS_CONF_SUCCESS is returned.
5047 static int
5048 read_mapped_values(
5049 FILE *tfp,
5050 nvlist_t **nvlp,
5051 char *module,
5052 char *mapped_list,
5053 int value)
5055 char *map_name, *lastparam, *tmpname;
5056 int res;
5057 ipqos_nvtype_t type;
5058 char dfltst[IPQOS_VALST_MAXLEN+1] = "";
5059 str_val_nd_t *enum_nvs;
5060 place_t place;
5062 IPQOSCDBG0(L1, "In read_mapped_values\n");
5064 map_name = (char *)strtok_r(mapped_list, ",", &lastparam);
5065 while (map_name != NULL) {
5066 char *tokval, *lastval;
5067 int index = 0;
5070 * get map info from types file.
5072 place = PL_MAP;
5073 res = readtype(tfp, module, map_name, &type, &enum_nvs,
5074 dfltst, B_FALSE, &place);
5075 if (res != IPQOS_CONF_SUCCESS) {
5076 return (IPQOS_CONF_ERR);
5080 * Just keep browsing the list till we get to the element
5081 * with the index from the value parameter or the end.
5083 tokval = (char *)strtok_r(dfltst, ",", &lastval);
5084 for (;;) {
5085 if (tokval == NULL) {
5086 ipqos_msg(MT_ERROR,
5087 gettext("Invalid value, %u, line %u.\n"),
5088 value, lineno);
5089 return (IPQOS_CONF_ERR);
5091 if (index++ == value) {
5092 break;
5094 tokval = (char *)strtok_r(NULL, ",", &lastval);
5099 * create fully qualified parameter name for map value.
5101 tmpname = prepend_module_name(map_name, module);
5102 if (tmpname == NULL) {
5103 return (IPQOS_CONF_ERR);
5107 * add map value with fqn to parameter nvlist.
5109 IPQOSCDBG2(L0, "Adding map %s, value %u to nvlist\n",
5110 tmpname, atoi(tokval));
5111 switch (type) {
5112 case IPQOS_DATA_TYPE_UINT8: {
5113 res = nvlist_add_byte(*nvlp, tmpname,
5114 (uint8_t)atoi(tokval));
5115 if (res != 0) {
5116 free(tmpname);
5117 ipqos_msg(MT_ENOSTR,
5118 "nvlist_add_uint8");
5119 return (IPQOS_CONF_ERR);
5121 break;
5123 case IPQOS_DATA_TYPE_UINT32: {
5124 res = nvlist_add_uint32(*nvlp, tmpname,
5125 (uint32_t)atoi(tokval));
5126 if (res != 0) {
5127 free(tmpname);
5128 ipqos_msg(MT_ENOSTR,
5129 "nvlist_add_uint32");
5130 return (IPQOS_CONF_ERR);
5132 break;
5134 default: {
5135 ipqos_msg(MT_ERROR,
5136 gettext("Types file for module %s is "
5137 "corrupt.\n"), module);
5138 IPQOSCDBG1(L0, "Unsupported map type for "
5139 "parameter %s given in types file.\n",
5140 map_name);
5141 return (IPQOS_CONF_ERR);
5144 free(tmpname);
5146 map_name = (char *)strtok_r(NULL, ",", &lastparam);
5149 return (IPQOS_CONF_SUCCESS);
5153 * Parses the string info_str into it's components. Its format is:
5154 * SIZE','[ENUM_DEF | RANGE], where SIZE is the size of the array,
5155 * ENUM_DEF is the definition of the enumeration for this array,
5156 * and RANGE is the set of values this array can accept. In
5157 * the event this array has an enumeration definition enum_nvs is
5158 * set to point at a str_val_nd_t structure which stores the names
5159 * and values associated with this enumeration. Otherwise, if this
5160 * is not an enumerated array, lower and upper are set to the lower
5161 * and upper values of RANGE.
5162 * RETURNS: IPQOS_CONF_ERR due to unexpected parse errors, else
5163 * IPQOS_CONF_SUCCESS.
5165 static int
5166 read_int_array_info(
5167 char *info_str,
5168 str_val_nd_t **enum_nvs,
5169 uint32_t *size,
5170 int *lower,
5171 int *upper,
5172 char *module)
5174 int res;
5175 char *end;
5176 char *token;
5177 char *tmp;
5179 IPQOSCDBG1(L0, "In read_array_info: info_str: %s\n",
5180 (info_str != NULL) ? info_str : "NULL");
5182 if (info_str == NULL) {
5183 IPQOSCDBG0(L0, "Null info string\n");
5184 goto fail;
5188 * read size.
5190 token = strtok(info_str, ",");
5191 *size = (uint32_t)strtol(token, &end, 10);
5192 SKIPWS(end);
5193 if ((end == token) || (*end != NULL)) {
5194 IPQOSCDBG0(L0, "Invalid size\n");
5195 goto fail;
5197 IPQOSCDBG1(L0, "read size: %u\n", *size);
5200 * check we have another string.
5202 token = strtok(NULL, "\n");
5203 if (token == NULL) {
5204 IPQOSCDBG0(L0, "Missing range/enum def\n");
5205 goto fail;
5207 IPQOSCDBG1(L0, "range/enum def: %s\n", token);
5210 * check if enumeration set or integer set and read enumeration
5211 * definition or integer range respectively.
5213 tmp = strchr(token, CURL_BEGIN);
5214 if (tmp == NULL) { /* a numeric range */
5215 res = readrange(token, lower, upper);
5216 if (res != IPQOS_CONF_SUCCESS) {
5217 IPQOSCDBG0(L0, "Failed reading range\n");
5218 goto fail;
5220 } else { /* an enumeration */
5221 *enum_nvs = read_enum_nvs(token, module);
5222 if (*enum_nvs == NULL) {
5223 IPQOSCDBG0(L0, "Failed reading enum def\n");
5224 goto fail;
5228 return (IPQOS_CONF_SUCCESS);
5229 fail:
5230 ipqos_msg(MT_ERROR,
5231 gettext("Types file for module %s is corrupt.\n"), module);
5232 return (IPQOS_CONF_ERR);
5236 * reads the value of an enumeration parameter from first_token and fp.
5237 * first_token is the first token of the value.
5238 * The format expected is NAME | { NAME1 [, NAME2 ] [, NAME3 ] }.
5239 * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCESS.
5241 static int
5242 read_enum_value(
5243 FILE *fp,
5244 char *first_token,
5245 str_val_nd_t *enum_vals,
5246 uint32_t *val)
5249 uint32_t u32;
5250 int ret;
5251 char *tk;
5252 char *lo = NULL;
5253 char *cm;
5254 int name_expected = 0;
5256 IPQOSCDBG0(L1, "In read_enum_value\n");
5258 /* init param val */
5259 *val = 0;
5261 /* first token not curl_begin, so lookup its value */
5263 if (*first_token != CURL_BEGIN) {
5264 ret = str_val_list_lookup(enum_vals, first_token, val);
5265 if (ret != IPQOS_CONF_SUCCESS) {
5266 ipqos_msg(MT_ERROR,
5267 gettext("Unrecognized value, %s, line %u.\n"),
5268 first_token, lineno);
5269 return (ret);
5272 /* curl_begin, so read values till curl_end, dicing at ',' */
5273 } else {
5275 name_expected++;
5277 for (;;) {
5280 * no leftover from pervious iteration so read new
5281 * token. This leftover happens because readtoken
5282 * doesn't interpret comma's as special characters
5283 * and thus could return 'val1,val2' as one token.
5284 * If this happens the val1 will be used in the
5285 * current iteration and what follows saved in lo
5286 * for processing by successive iterations.
5289 if (lo == NULL) {
5290 ret = readtoken(fp, &tk);
5291 if (ret == IPQOS_CONF_ERR) {
5292 return (ret);
5293 } else if (ret == IPQOS_CONF_EOF) {
5294 ipqos_msg(MT_ERROR,
5295 gettext("Unexpected EOF.\n"));
5296 return (IPQOS_CONF_ERR);
5299 } else { /* previous leftover, so use it */
5301 IPQOSCDBG1(L1, "Using leftover %s.\n", lo);
5302 tk = lo;
5303 lo = NULL;
5306 if (name_expected) {
5307 if (ret == IPQOS_CONF_CURL_END ||
5308 tk[0] == ',') {
5309 ipqos_msg(MT_ERROR,
5310 gettext("Malformed value list "
5311 "line %u.\n"), lineno);
5312 free(tk);
5313 return (IPQOS_CONF_ERR);
5317 * check if this token contains a ',' and
5318 * if so store it and what follows for next
5319 * iteration.
5321 cm = strchr(tk, ',');
5322 if (cm != NULL) {
5323 lo = malloc(strlen(cm) + 1);
5324 if (lo == NULL) {
5325 ipqos_msg(MT_ENOSTR, "malloc");
5326 free(tk);
5327 return (IPQOS_CONF_ERR);
5330 (void) strcpy(lo, cm);
5331 *cm = '\0';
5335 /* get name value and add to total val */
5337 ret = str_val_list_lookup(enum_vals, tk, &u32);
5338 if (ret != IPQOS_CONF_SUCCESS) {
5339 ipqos_msg(MT_ERROR,
5340 gettext("Unrecognized value, %s, "
5341 "line %u.\n"), tk, lineno);
5342 free(tk);
5343 return (IPQOS_CONF_ERR);
5346 *val = *val | u32;
5347 name_expected--;
5349 /* comma or curl end accepted */
5350 } else {
5352 /* we've reached curl_end so break */
5354 if (ret == IPQOS_CONF_CURL_END) {
5355 free(tk);
5356 break;
5358 /* not curl end and not comma */
5360 } else if (tk[0] != ',') {
5361 ipqos_msg(MT_ERROR,
5362 gettext("Malformed value list "
5363 "line %u.\n"), lineno);
5364 free(tk);
5365 return (IPQOS_CONF_ERR);
5369 * store anything after the comma for next
5370 * iteration.
5372 if (tk[1] != '\0') {
5373 lo = malloc(strlen(&tk[1]) + 1);
5374 if (lo == NULL) {
5375 ipqos_msg(MT_ENOSTR, "malloc");
5376 free(tk);
5377 return (IPQOS_CONF_ERR);
5379 (void) strcpy(lo, &tk[1]);
5382 name_expected++;
5385 free(tk);
5389 IPQOSCDBG1(L1, "value returned is: %u\n", *val);
5391 return (IPQOS_CONF_SUCCESS);
5395 * read the set of permanent classes/filter from the types file ref'd by tfp
5396 * and store them in a string table pointed to by perm_items,
5397 * with *nitems getting set to number of items read. perm_filters is set
5398 * to 1 if we're searching for permanent filters, else 0 for classes.
5399 * RETURNS: IPQOS_CONF_ERR if any errors, else IPQOS_CONF_SUCCESS.
5401 static int
5402 read_perm_items(
5403 int perm_filters,
5404 FILE *tfp,
5405 char *module_name,
5406 char ***perm_items,
5407 int *nitems)
5410 char lbuf[IPQOS_CONF_TYPE_LINE_LEN];
5411 int cnt = 0;
5412 char name[IPQOS_CONF_NAME_LEN+1];
5413 char foo[IPQOS_CONF_NAME_LEN+1];
5414 int res;
5415 char **items = NULL;
5416 char **tmp;
5417 char *marker;
5419 IPQOSCDBG0(L1, "In read_perm_items\n");
5422 /* seek to start of types file */
5424 if (fseek(tfp, 0, SEEK_SET) != 0) {
5425 ipqos_msg(MT_ENOSTR, "fseek");
5426 return (IPQOS_CONF_ERR);
5429 /* select which marker were looking for */
5431 if (perm_filters) {
5432 marker = IPQOS_CONF_PERM_FILTER_MK;
5433 } else {
5434 marker = IPQOS_CONF_PERM_CLASS_MK;
5437 /* scan file line by line till end */
5439 while (fgets(lbuf, IPQOS_CONF_TYPE_LINE_LEN, tfp) != NULL) {
5442 * if the line is marked as containing a default item name
5443 * read the name, extend the items string array
5444 * and store the string off the array.
5446 if (strncmp(lbuf, marker, strlen(marker)) == 0) {
5448 res = sscanf(lbuf,
5449 "%" VAL2STR(IPQOS_CONF_NAME_LEN) "s"
5450 "%" VAL2STR(IPQOS_CONF_NAME_LEN) "s",
5451 foo, name);
5452 if (res < 2) {
5453 ipqos_msg(MT_ERROR,
5454 gettext("Types file for module %s is "
5455 "corrupt.\n"), module_name);
5456 IPQOSCDBG1(L0, "Missing name with a %s.\n",
5457 marker);
5458 goto fail;
5461 /* extend items array to accomodate new item */
5463 tmp = realloc(items, (cnt + 1) * sizeof (char *));
5464 if (tmp == NULL) {
5465 ipqos_msg(MT_ENOSTR, "realloc");
5466 goto fail;
5467 } else {
5468 items = tmp;
5471 /* copy and store item name */
5473 items[cnt] = malloc(strlen(name) + 1);
5474 if (items[cnt] == NULL) {
5475 ipqos_msg(MT_ENOSTR, "malloc");
5476 goto fail;
5479 (void) strcpy(items[cnt], name);
5480 cnt++;
5483 IPQOSCDBG1(L1, "stored %s in perm items array\n",
5484 name);
5488 *perm_items = items;
5489 *nitems = cnt;
5491 return (IPQOS_CONF_SUCCESS);
5492 fail:
5493 for (cnt--; cnt >= 0; cnt--)
5494 free(items[cnt]);
5495 free(items);
5496 return (IPQOS_CONF_ERR);
5500 * Searches types file ref'd by tfp for the parameter named name
5501 * with the place corresponding with place parameter. The format
5502 * of the lines in the file are:
5503 * PLACE NAME TYPE [ ENUM_DEF ] [ DEFAULT_STR ]
5504 * The ENUM_DEF is an enumeration definition and is only present
5505 * for parameters of type enum. DEFAULT_STR is a default value for
5506 * this parameter. If present type is set to the appropriate type
5507 * enumeration and dfltst filled with DEFAULT_STR if one was set.
5508 * Also if the type is enum enum_nvps is made to point at a
5509 * set of name value pairs representing ENUM_DEF.
5511 * RETURNS: If any resource errors occur, or a matching parameter
5512 * isn't found IPQOS_CONF_ERR is returned, else IPQOS_CONF_SUCCESS.
5514 static int
5515 readtype(
5516 FILE *tfp,
5517 char *module_name,
5518 char *name,
5519 ipqos_nvtype_t *type,
5520 str_val_nd_t **enum_nvps,
5521 char *dfltst,
5522 boolean_t allow_ipgpc_priv,
5523 place_t *place)
5526 int ac;
5527 char lbuf[IPQOS_CONF_TYPE_LINE_LEN];
5528 char param[IPQOS_CONF_PNAME_LEN+1];
5529 char typest[IPQOS_CONF_TYPE_LEN+1];
5530 char place_st[IPQOS_CONF_TYPE_LEN+1];
5531 char *cp;
5532 int x;
5533 char *ipgpc_nm;
5534 int found = 0;
5536 IPQOSCDBG1(L1, "In readtype: param: %s\n", name);
5540 * if allow_ipgpc_priv is true then we allow ipgpc parameters that are
5541 * private between ipqosconf and ipgpc. eg. address masks, port masks.
5543 if (allow_ipgpc_priv && strcmp(module_name, IPGPC_NAME) == 0) {
5544 ipgpc_nm = prepend_module_name(name, IPGPC_NAME);
5545 if (ipgpc_nm == NULL) {
5546 return (IPQOS_CONF_ERR);
5549 if (strcmp(ipgpc_nm, IPGPC_SADDR_MASK) == 0 ||
5550 strcmp(ipgpc_nm, IPGPC_DADDR_MASK) == 0) {
5551 *type = IPQOS_DATA_TYPE_ADDRESS_MASK;
5552 return (IPQOS_CONF_SUCCESS);
5553 } else if (strcmp(ipgpc_nm, IPGPC_SPORT_MASK) == 0 ||
5554 strcmp(ipgpc_nm, IPGPC_DPORT_MASK) == 0) {
5555 *type = IPQOS_DATA_TYPE_UINT16;
5556 return (IPQOS_CONF_SUCCESS);
5557 } else if (strcmp(ipgpc_nm, IPGPC_FILTER_TYPE) == 0) {
5558 *type = IPQOS_DATA_TYPE_UINT32;
5559 return (IPQOS_CONF_SUCCESS);
5560 } else if (strcmp(ipgpc_nm, IPGPC_IF_INDEX) == 0) {
5561 *type = IPQOS_DATA_TYPE_IFINDEX;
5562 return (IPQOS_CONF_SUCCESS);
5565 free(ipgpc_nm);
5569 * read upto and including module version line.
5571 if (read_tfile_ver(tfp, IPQOS_MOD_STR, module_name) == -1)
5572 return (IPQOS_CONF_ERR);
5576 * loop reading lines of the types file until named parameter
5577 * found or EOF.
5579 while (fgets(lbuf, IPQOS_CONF_TYPE_LINE_LEN, tfp) != NULL) {
5582 * check whether blank or commented line; if so skip
5584 for (cp = lbuf; isspace(*cp) && *cp != '\0'; cp++) {}
5585 if (*cp == '\0' || *cp == '#') {
5586 continue;
5589 dfltst[0] = '\0';
5592 * read place, param, type and if present default str
5593 * from line.
5595 ac = sscanf(lbuf,
5596 "%" VAL2STR(IPQOS_CONF_TYPE_LEN) "s "
5597 "%" VAL2STR(IPQOS_CONF_PNAME_LEN) "s "
5598 "%" VAL2STR(IPQOS_CONF_TYPE_LEN) "s "
5599 "%" VAL2STR(IPQOS_VALST_MAXLEN) "s",
5600 place_st, param, typest, dfltst);
5601 if (ac < 3) {
5602 ipqos_msg(MT_ERROR,
5603 gettext("Types file for module %s is corrupt.\n"),
5604 module_name);
5605 IPQOSCDBG0(L0, "sscanf failed to read 3 strings.\n");
5606 return (IPQOS_CONF_ERR);
5610 * if the place and name match no need to look any further.
5612 if ((*place == PL_ANY) ||
5613 ((*place == PL_PARAMS) &&
5614 strcmp(place_st, IPQOS_PLACE_PRM_STR) == 0) ||
5615 ((*place == PL_FILTER) &&
5616 strcmp(place_st, IPQOS_PLACE_FILTER_STR) == 0) ||
5617 ((*place == PL_MAP) &&
5618 strcmp(place_st, IPQOS_PLACE_MAP_STR) == 0)) {
5619 if (strcmp(param, name) == 0) {
5620 found++;
5621 break;
5625 if (found == 0) {
5626 ipqos_msg(MT_ERROR,
5627 gettext("Invalid parameter, %s, line %u.\n"), name,
5628 lineno);
5629 return (IPQOS_CONF_ERR);
5633 * set the place parameter to the actual place when the PL_ANY flag
5634 * was set.
5636 if (*place == PL_ANY) {
5637 if (strcmp(place_st, IPQOS_PLACE_PRM_STR) == 0) {
5638 *place = PL_PARAMS;
5639 } else if (strcmp(place_st, IPQOS_PLACE_FILTER_STR) == 0) {
5640 *place = PL_FILTER;
5641 } else if (strcmp(place_st, IPQOS_PLACE_MAP_STR) == 0) {
5642 *place = PL_MAP;
5647 * get type enumeration
5649 for (x = 0; nv_types[x].string[0]; x++) {
5650 if (strcmp(nv_types[x].string, typest) == 0) {
5651 break;
5655 * check that we have a type corresponding with the one the types
5656 * file specifies.
5658 if (nv_types[x].string[0] == '\0') {
5659 ipqos_msg(MT_ERROR,
5660 gettext("Types file for module %s is corrupt.\n"),
5661 module_name);
5662 return (IPQOS_CONF_ERR);
5664 *type = nv_types[x].value;
5667 * if enumeration type get set of name/vals and any default value
5669 if (*type == IPQOS_DATA_TYPE_ENUM) {
5670 *enum_nvps = read_enum_nvs(lbuf, module_name);
5671 if (*enum_nvps == NULL) {
5672 return (IPQOS_CONF_ERR);
5675 dfltst[0] = '\0';
5676 cp = strchr(lbuf, CURL_END);
5677 (void) sscanf(++cp,
5678 "%" VAL2STR(IPQOS_VALST_MAXLEN) "s", dfltst);
5682 IPQOSCDBG2(L1, "read type: %s default: %s\n", nv_types[x].string,
5683 *dfltst ? dfltst : "None");
5684 return (IPQOS_CONF_SUCCESS);
5689 * Reads a name and a value from file ref'd by cfp into list indirectly
5690 * ref'd by nvlp; If this list is NULL it will be created to accomodate
5691 * the name/value. The name must be either a special token for
5692 * for the place, or be present in the module types file ref'd by tfp.
5693 * *type is set to the enumeration of the type of the parameter and
5694 * nvp to point at the element with the nvlp ref'd list.
5695 * RETURNS: IPQOS_CONF_CURL_END if read CURL_END as name,
5696 * IPQOS_CONF_ERR on errors, else IPQOS_CONF_SUCCESS.
5698 static int
5699 readnvpair(
5700 FILE *cfp,
5701 FILE *tfp,
5702 nvlist_t **nvlp,
5703 nvpair_t **nvp,
5704 ipqos_nvtype_t *type,
5705 place_t place,
5706 char *module_name)
5709 char *name = NULL;
5710 char *valst = NULL;
5711 int res;
5712 char *tmp;
5713 str_val_nd_t *enum_nvs = NULL;
5714 char dfltst[IPQOS_VALST_MAXLEN+1];
5716 IPQOSCDBG0(L1, "in readnvpair\n");
5719 * read nvpair name
5721 res = readtoken(cfp, &name);
5724 * if reached eof, curl end or error encountered return to caller
5726 if (res == IPQOS_CONF_EOF) {
5727 ipqos_msg(MT_ERROR, gettext("Unexpected EOF.\n"));
5728 return (IPQOS_CONF_ERR);
5729 } else if (res == IPQOS_CONF_ERR) {
5730 return (res);
5731 } else if (res == IPQOS_CONF_CURL_END) {
5732 free(name);
5733 return (res);
5737 * read nvpair value
5739 res = readtoken(cfp, &valst);
5742 * check we've read a valid value
5744 if (res != IPQOS_CONF_SUCCESS && res != IPQOS_CONF_CURL_BEGIN) {
5745 if (res == IPQOS_CONF_EOF) {
5746 ipqos_msg(MT_ERROR, gettext("Unexpected EOF.\n"));
5747 } else if (res == IPQOS_CONF_CURL_END) {
5748 ipqos_msg(MT_ERROR,
5749 gettext("Missing parameter value line %u.\n"),
5750 lineno);
5751 free(valst);
5752 } /* we do nothing special for IPQOS_CONF_ERR */
5753 free(name);
5754 return (IPQOS_CONF_ERR);
5758 * check for generic parameters.
5761 if ((place == PL_CLASS) &&
5762 strcmp(name, IPQOS_CONF_NEXT_ACTION_STR) == 0) {
5763 *type = IPQOS_DATA_TYPE_ACTION;
5765 } else if (place == PL_PARAMS &&
5766 strcmp(name, IPQOS_CONF_GLOBAL_STATS_STR) == 0 ||
5767 place == PL_CLASS &&
5768 strcmp(name, IPQOS_CONF_STATS_ENABLE_STR) == 0) {
5769 *type = IPQOS_DATA_TYPE_BOOLEAN;
5771 } else if (tfp == NULL ||
5772 ((place != PL_PARAMS) && strcmp(name, IPQOS_CONF_NAME_STR) == 0) ||
5773 (place == PL_FILTER) && (strcmp(name, IPQOS_CONF_CLASS_STR) ==
5774 0) ||
5775 (place == PL_ACTION) && (strcmp(name, IPQOS_CONF_MODULE_STR) ==
5776 0)) {
5777 *type = IPQOS_DATA_TYPE_STRING;
5779 } else { /* if not generic parameter */
5781 * get type from types file
5783 if (readtype(tfp, module_name, name, type, &enum_nvs, dfltst,
5784 B_FALSE, &place) != IPQOS_CONF_SUCCESS) {
5785 free(name);
5786 free(valst);
5787 return (IPQOS_CONF_ERR);
5791 * get full module prefix parameter name
5793 tmp = name;
5794 if ((name = prepend_module_name(name, module_name)) == NULL) {
5795 name = tmp;
5796 goto fail;
5798 free(tmp);
5801 IPQOSCDBG3(L1, "NVP, name: %s, str_value: %s, type: %s\n", name,
5802 valst, nv_types[*type].string);
5806 * create nvlist if not present already
5808 if (*nvlp == NULL) {
5809 res = nvlist_alloc(nvlp, NV_UNIQUE_NAME, 0);
5810 if (res != 0) {
5811 ipqos_msg(MT_ENOSTR, "nvlist_alloc");
5812 free(name);
5813 free(valst);
5814 return (IPQOS_CONF_ERR);
5819 * check we haven't already read this parameter
5821 if (find_nvpair(*nvlp, name)) {
5822 ipqos_msg(MT_ERROR, gettext("Duplicate parameter line %u.\n"),
5823 lineno);
5824 goto fail;
5828 * convert value string to appropriate type and add to nvlist
5831 switch (*type) {
5832 case IPQOS_DATA_TYPE_IFNAME: {
5833 uint32_t ifidx;
5835 res = readifindex(valst, (int *)&ifidx);
5836 if (res == IPQOS_CONF_SUCCESS) {
5837 res = nvlist_add_uint32(*nvlp, IPGPC_IF_INDEX,
5838 ifidx);
5839 if (res != 0) {
5840 ipqos_msg(MT_ENOSTR,
5841 "nvlist_add_uint32");
5842 goto fail;
5844 (void) nvlist_remove_all(*nvlp, name);
5846 * change name to point at the name of the
5847 * new ifindex nvlist entry as name is used
5848 * later in the function.
5850 free(name);
5851 name = malloc(strlen(IPGPC_IF_INDEX) + 1);
5852 if (name == NULL) {
5853 ipqos_msg(MT_ENOSTR, "malloc");
5854 goto fail;
5856 (void) strcpy(name, IPGPC_IF_INDEX);
5858 break;
5860 case IPQOS_DATA_TYPE_PROTO: {
5861 uint8_t proto;
5863 res = readproto(valst, &proto);
5864 if (res == IPQOS_CONF_SUCCESS) {
5865 res = nvlist_add_byte(*nvlp, name, proto);
5866 if (res != 0) {
5867 ipqos_msg(MT_ENOSTR, "nvlist_add_byte");
5868 goto fail;
5871 break;
5873 case IPQOS_DATA_TYPE_PORT: {
5874 uint16_t port;
5876 res = readport(valst, &port);
5877 if (res == IPQOS_CONF_SUCCESS) {
5879 /* add port */
5881 res = nvlist_add_uint16(*nvlp, name, port);
5882 if (res != 0) {
5883 ipqos_msg(MT_ENOSTR,
5884 "nvlist_add_uint16");
5885 goto fail;
5888 /* add appropriate all ones port mask */
5890 if (strcmp(name, IPGPC_DPORT) == 0) {
5891 res = nvlist_add_uint16(*nvlp,
5892 IPGPC_DPORT_MASK, ~0);
5894 } else if (strcmp(name, IPGPC_SPORT) == 0) {
5895 res = nvlist_add_uint16(*nvlp,
5896 IPGPC_SPORT_MASK, ~0);
5898 if (res != 0) {
5899 ipqos_msg(MT_ENOSTR,
5900 "nvlist_add_uint16");
5901 goto fail;
5904 break;
5906 case IPQOS_DATA_TYPE_ADDRESS:
5907 case IPQOS_DATA_TYPE_ACTION:
5908 case IPQOS_DATA_TYPE_STRING:
5909 res = nvlist_add_string(*nvlp, name, valst);
5910 if (res != 0) {
5911 ipqos_msg(MT_ENOSTR, "nvlist_add_string");
5912 goto fail;
5914 break;
5915 case IPQOS_DATA_TYPE_BOOLEAN: {
5916 boolean_t b;
5918 res = readbool(valst, &b);
5919 if (res == IPQOS_CONF_SUCCESS) {
5920 res = nvlist_add_uint32(*nvlp, name,
5921 (uint32_t)b);
5922 if (res != 0) {
5923 ipqos_msg(MT_ENOSTR,
5924 "nvlist_add_uint32");
5925 goto fail;
5928 break;
5930 case IPQOS_DATA_TYPE_UINT8: {
5931 uint8_t u8;
5933 res = readuint8(valst, &u8, &tmp);
5934 if (res == IPQOS_CONF_SUCCESS) {
5935 res = nvlist_add_byte(*nvlp, name, u8);
5936 if (res != 0) {
5937 ipqos_msg(MT_ENOSTR, "nvlist_add_byte");
5938 goto fail;
5941 break;
5943 case IPQOS_DATA_TYPE_INT16: {
5944 int16_t i16;
5946 res = readint16(valst, &i16, &tmp);
5947 if (res == IPQOS_CONF_SUCCESS) {
5948 res = nvlist_add_int16(*nvlp, name, i16);
5949 if (res != 0) {
5950 ipqos_msg(MT_ENOSTR,
5951 "nvlist_add_int16");
5952 goto fail;
5955 break;
5957 case IPQOS_DATA_TYPE_UINT16: {
5958 uint16_t u16;
5960 res = readuint16(valst, &u16, &tmp);
5961 if (res == IPQOS_CONF_SUCCESS) {
5962 res = nvlist_add_uint16(*nvlp, name, u16);
5963 if (res != 0) {
5964 ipqos_msg(MT_ENOSTR,
5965 "nvlist_add_int16");
5966 goto fail;
5969 break;
5971 case IPQOS_DATA_TYPE_INT32: {
5972 int i32;
5974 res = readint32(valst, &i32, &tmp);
5975 if (res == IPQOS_CONF_SUCCESS) {
5976 res = nvlist_add_int32(*nvlp, name, i32);
5977 if (res != 0) {
5978 ipqos_msg(MT_ENOSTR,
5979 "nvlist_add_int32");
5980 goto fail;
5983 break;
5985 case IPQOS_DATA_TYPE_UINT32: {
5986 uint32_t u32;
5988 res = readuint32(valst, &u32, &tmp);
5989 if (res == IPQOS_CONF_SUCCESS) {
5990 res = nvlist_add_uint32(*nvlp, name, u32);
5991 if (res != 0) {
5992 ipqos_msg(MT_ENOSTR,
5993 "nvlist_add_uint32");
5994 goto fail;
5997 break;
5999 case IPQOS_DATA_TYPE_ENUM: {
6000 uint32_t val;
6002 res = read_enum_value(cfp, valst, enum_nvs, &val);
6003 if (res == IPQOS_CONF_SUCCESS) {
6004 res = nvlist_add_uint32(*nvlp, name, val);
6005 if (res != 0) {
6006 ipqos_msg(MT_ENOSTR,
6007 "nvlist_add_uint32");
6008 goto fail;
6010 } else {
6011 goto fail;
6013 break;
6016 * For now the dfltst contains a comma separated list of the
6017 * type we need this parameter to be mapped to.
6018 * read_mapped_values will fill in all the mapped parameters
6019 * and their values in the nvlist.
6021 case IPQOS_DATA_TYPE_M_INDEX: {
6022 uint8_t u8;
6024 res = readuint8(valst, &u8, &tmp);
6025 if (res == IPQOS_CONF_SUCCESS) {
6026 res = nvlist_add_byte(*nvlp, name, u8);
6027 if (res != 0) {
6028 ipqos_msg(MT_ENOSTR,
6029 "nvlist_add_uint8");
6030 goto fail;
6032 } else {
6033 *type = IPQOS_DATA_TYPE_UINT8;
6034 break;
6036 res = read_mapped_values(tfp, nvlp, module_name,
6037 dfltst, u8);
6038 if (res != IPQOS_CONF_SUCCESS) {
6039 goto fail;
6041 break;
6043 case IPQOS_DATA_TYPE_INT_ARRAY: {
6044 str_val_nd_t *arr_enum_nvs = NULL;
6045 uint32_t size;
6046 int llimit = 0, ulimit = 0;
6047 int *arr;
6050 * read array info from types file.
6052 res = read_int_array_info(dfltst, &arr_enum_nvs, &size,
6053 &llimit, &ulimit, module_name);
6054 if (res != IPQOS_CONF_SUCCESS) {
6055 goto fail;
6059 * read array contents from config file and construct
6060 * array with them.
6062 res = read_int_array(cfp, valst, &arr, size, llimit,
6063 ulimit, arr_enum_nvs);
6064 if (res != IPQOS_CONF_SUCCESS) {
6065 goto fail;
6069 * add array to nvlist.
6071 res = nvlist_add_int32_array(*nvlp, name, arr, size);
6072 if (res != 0) {
6073 ipqos_msg(MT_ENOSTR, "nvlist_add_int32");
6074 goto fail;
6078 * free uneeded resources.
6080 free(arr);
6081 if (arr_enum_nvs)
6082 free_str_val_entrys(arr_enum_nvs);
6084 break;
6086 case IPQOS_DATA_TYPE_USER: {
6087 uid_t uid;
6089 res = readuser(valst, &uid);
6090 if (res == IPQOS_CONF_SUCCESS) {
6091 res = nvlist_add_int32(*nvlp, name, (int)uid);
6092 if (res != 0) {
6093 ipqos_msg(MT_ENOSTR,
6094 "nvlist_add_int32");
6095 goto fail;
6098 break;
6100 #ifdef _IPQOS_CONF_DEBUG
6101 default: {
6103 * we shouldn't have a type that doesn't have a switch
6104 * entry.
6106 assert(1);
6108 #endif
6110 if (res != 0) {
6111 ipqos_msg(MT_ERROR, gettext("Invalid %s, line %u.\n"),
6112 nv_types[*type].string, lineno);
6113 goto fail;
6116 /* set the nvp parameter to point at the newly added nvlist entry */
6118 *nvp = find_nvpair(*nvlp, name);
6120 free(name);
6121 free(valst);
6122 if (enum_nvs)
6123 free_str_val_entrys(enum_nvs);
6124 return (IPQOS_CONF_SUCCESS);
6125 fail:
6126 if (name != NULL)
6127 free(name);
6128 if (valst != NULL)
6129 free(valst);
6130 if (enum_nvs != NULL)
6131 free_str_val_entrys(enum_nvs);
6132 return (IPQOS_CONF_ERR);
6136 * read a parameter clause from cfp into *params.
6137 * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCES.
6139 static int
6140 readparams(
6141 FILE *cfp,
6142 FILE *tfp,
6143 char *module_name,
6144 ipqos_conf_params_t *params)
6147 int res;
6148 nvpair_t *nvp;
6149 ipqos_nvtype_t type;
6150 boolean_t bl;
6151 char *nm;
6152 char *action;
6153 char tmp[IPQOS_CONF_PNAME_LEN];
6154 int read_stats = 0;
6156 IPQOSCDBG0(L0, "in readparams\n");
6158 /* read beginning curl */
6160 res = read_curl_begin(cfp);
6161 if (res != IPQOS_CONF_SUCCESS) {
6162 return (res);
6166 * loop reading nvpairs, adding to params nvlist until encounter
6167 * CURL_END.
6169 for (;;) {
6170 /* read nvpair */
6172 res = readnvpair(cfp, tfp, &params->nvlist,
6173 &nvp, &type, PL_PARAMS, module_name);
6174 if (res == IPQOS_CONF_ERR) {
6175 goto fail;
6177 /* we have finished reading params */
6179 } else if (res == IPQOS_CONF_CURL_END) {
6180 break;
6184 * read global stats - place into params struct and remove
6185 * from nvlist.
6187 if (strcmp(nvpair_name(nvp), IPQOS_CONF_GLOBAL_STATS_STR) ==
6188 0) {
6189 /* check we haven't read stats before */
6191 if (read_stats) {
6192 ipqos_msg(MT_ERROR,
6193 gettext("Duplicate parameter line %u.\n"),
6194 lineno);
6195 goto fail;
6197 read_stats++;
6199 (void) nvpair_value_uint32(nvp, (uint32_t *)&bl);
6200 params->stats_enable = bl;
6201 (void) nvlist_remove_all(params->nvlist,
6202 IPQOS_CONF_GLOBAL_STATS_STR);
6206 * read action type parameter - add it to list of action refs.
6207 * also, if it's one of continue or drop virtual actions
6208 * change the action name to their special ipp names in
6209 * the action ref list and the nvlist.
6211 } else if (type == IPQOS_DATA_TYPE_ACTION) {
6213 /* get name and value from nvlist */
6215 nm = nvpair_name(nvp);
6216 (void) nvpair_value_string(nvp, &action);
6218 /* if virtual action names change to ipp name */
6220 if ((strcmp(action, IPQOS_CONF_CONT_STR) == 0) ||
6221 strcmp(action, IPQOS_CONF_DROP_STR) == 0) {
6223 * we copy nm to a seperate buffer as nv_pair
6224 * name above gave us a ptr to internal
6225 * memory which causes strange behaviour
6226 * when we re-value that nvlist element.
6228 (void) strlcpy(tmp, nm, sizeof (tmp));
6229 nm = tmp;
6232 /* modify nvlist entry and change action */
6234 if (strcmp(action, IPQOS_CONF_CONT_STR) == 0) {
6235 action = IPP_ANAME_CONT;
6236 res = nvlist_add_string(params->nvlist,
6237 nm, action);
6238 } else {
6239 action = IPP_ANAME_DROP;
6240 res = nvlist_add_string(params->nvlist,
6241 nm, action);
6243 if (res != 0) {
6244 ipqos_msg(MT_ENOSTR,
6245 "nvlist_add_string");
6246 goto fail;
6250 /* add action reference to params */
6252 res = add_aref(&params->actions, nm, action);
6256 return (IPQOS_CONF_SUCCESS);
6257 fail:
6259 if (params->nvlist) {
6260 nvlist_free(params->nvlist);
6261 params->nvlist = NULL;
6263 if (params->actions) {
6264 free_arefs(params->actions);
6265 params->actions = NULL;
6267 return (IPQOS_CONF_ERR);
6270 /* ************************* class manip fns ****************************** */
6275 * make dst point at a dupicate class struct with duplicate elements to src.
6276 * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCESS.
6278 static int
6279 dup_class(
6280 ipqos_conf_class_t *src,
6281 ipqos_conf_class_t **dst)
6284 ipqos_conf_class_t *cls;
6285 int res;
6287 IPQOSCDBG1(DIFF, "In dup_class: class: %s\n", src->name);
6288 cls = alloc_class();
6289 if (cls == NULL) {
6290 return (IPQOS_CONF_ERR);
6293 /* struct copy */
6294 *cls = *src;
6296 /* we're not interested in the nvlist for a class */
6297 cls->nvlist = NULL;
6300 /* copy first action reference */
6301 cls->alist = NULL;
6302 res = add_aref(&cls->alist, src->alist->field, src->alist->name);
6303 if (res != IPQOS_CONF_SUCCESS) {
6304 free(cls);
6305 return (res);
6308 *dst = cls;
6310 return (IPQOS_CONF_SUCCESS);
6314 * create a zero'd class struct and return a ptr to it.
6315 * RETURNS: ptr to struct on success, NULL otherwise.
6317 static ipqos_conf_class_t *
6318 alloc_class()
6321 ipqos_conf_class_t *class;
6323 class = malloc(sizeof (ipqos_conf_class_t));
6324 if (class) {
6325 bzero(class, sizeof (ipqos_conf_class_t));
6326 } else {
6327 ipqos_msg(MT_ENOSTR, "malloc");
6330 return (class);
6333 /* frees up all memory occupied by a filter struct and its contents. */
6334 static void
6335 free_class(ipqos_conf_class_t *cls)
6338 if (cls == NULL)
6339 return;
6341 /* free its nvlist if present */
6343 nvlist_free(cls->nvlist);
6345 /* free its action refs if present */
6347 if (cls->alist)
6348 free_arefs(cls->alist);
6350 /* finally free class itself */
6351 free(cls);
6355 * Checks whether there is a class called class_nm in classes list.
6356 * RETURNS: ptr to first matched class, else if not matched NULL.
6358 static ipqos_conf_class_t *
6359 classexist(
6360 char *class_nm,
6361 ipqos_conf_class_t *classes)
6364 ipqos_conf_class_t *cls;
6366 IPQOSCDBG1(L1, "In classexist: name: %s\n", class_nm);
6368 for (cls = classes; cls; cls = cls->next) {
6369 if (strcmp(class_nm, cls->name) == 0) {
6370 break;
6374 return (cls);
6379 /* ************************** filter manip fns **************************** */
6384 * Checks whether there is a filter called filter_nm with instance number
6385 * instance in filters list created by us or permanent. Instance value -1
6386 * is a wildcard.
6387 * RETURNS: ptr to first matched filter, else if not matched NULL.
6389 static ipqos_conf_filter_t *
6390 filterexist(
6391 char *filter_nm,
6392 int instance,
6393 ipqos_conf_filter_t *filters)
6396 IPQOSCDBG2(L1, "In filterexist: name :%s, inst: %d\n", filter_nm,
6397 instance);
6399 while (filters) {
6400 if (strcmp(filters->name, filter_nm) == 0 &&
6401 (instance == -1 || filters->instance == instance) &&
6402 (filters->originator == IPP_CONFIG_IPQOSCONF ||
6403 filters->originator == IPP_CONFIG_PERMANENT)) {
6404 break;
6406 filters = filters->next;
6408 return (filters);
6412 * allocate and zero a filter structure.
6413 * RETURNS: NULL on error, else ptr to filter struct.
6415 static ipqos_conf_filter_t *
6416 alloc_filter()
6419 ipqos_conf_filter_t *flt;
6421 flt = malloc(sizeof (ipqos_conf_filter_t));
6422 if (flt) {
6423 bzero(flt, sizeof (ipqos_conf_filter_t));
6424 flt->instance = -1;
6425 } else {
6426 ipqos_msg(MT_ENOSTR, "malloc");
6429 return (flt);
6432 /* free flt and all it's contents. */
6434 static void
6435 free_filter(ipqos_conf_filter_t *flt)
6438 IPQOSCDBG2(L1, "In free_filter: filter: %s, inst: %d\n", flt->name,
6439 flt->instance);
6441 if (flt == NULL)
6442 return;
6444 if (flt->src_nd_name)
6445 free(flt->src_nd_name);
6446 if (flt->dst_nd_name)
6447 free(flt->dst_nd_name);
6448 if (flt->nvlist) {
6449 nvlist_free(flt->nvlist);
6451 free(flt);
6455 * makes a copy of ofilter and its contents and points nfilter at it. It
6456 * also adds an instance number to the filter and if either saddr or
6457 * daddr are non-null that address to the filters nvlist along with
6458 * an all 1s address mask and the af.
6459 * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCES.
6461 static int
6462 dup_filter(
6463 ipqos_conf_filter_t *ofilter,
6464 ipqos_conf_filter_t **nfilter,
6465 int af,
6466 int inv6, /* if saddr or daddr set and v4 filter are they in v6 addr */
6467 void *saddr,
6468 void *daddr,
6469 int inst)
6472 ipqos_conf_filter_t *nf;
6473 int res;
6474 in6_addr_t v6addr;
6475 in6_addr_t all_1s_v6;
6477 IPQOSCDBG4(MHME, "In dup_filter: name: %s, af: %u, inv6: %u, ins: %d\n",
6478 ofilter->name, af, inv6, inst);
6480 /* show src address and dst address if present */
6481 #ifdef _IPQOS_CONF_DEBUG
6482 if (ipqosconf_dbg_flgs & MHME) {
6483 char st[100];
6485 if (saddr) {
6486 (void) fprintf(stderr, "saddr: %s\n",
6487 inet_ntop(inv6 ? AF_INET6 : AF_INET, saddr, st,
6488 100));
6491 if (daddr) {
6492 (void) fprintf(stderr, "daddr: %s\n",
6493 inet_ntop(inv6 ? AF_INET6 : AF_INET, daddr, st,
6494 100));
6497 #endif /* _IPQOS_CONF_DEBUG */
6499 /* init local v6 address to 0 */
6500 (void) bzero(&v6addr, sizeof (in6_addr_t));
6502 /* create an all 1s address for use as mask */
6503 (void) memset(&all_1s_v6, ~0, sizeof (in6_addr_t));
6505 /* create a new filter */
6507 nf = alloc_filter();
6508 if (nf == NULL) {
6509 return (IPQOS_CONF_ERR);
6512 /* struct copy old filter to new */
6513 *nf = *ofilter;
6515 /* copy src filters nvlist if there is one to copy */
6517 if (ofilter->nvlist) {
6518 res = nvlist_dup(ofilter->nvlist, &nf->nvlist, 0);
6519 if (res != 0) {
6520 ipqos_msg(MT_ENOSTR, "nvlist_dup");
6521 goto fail;
6525 /* copy src and dst node names if present */
6527 if (ofilter->src_nd_name) {
6528 nf->src_nd_name = malloc(strlen(ofilter->src_nd_name) + 1);
6529 if (nf->src_nd_name == NULL) {
6530 ipqos_msg(MT_ENOSTR, "malloc");
6531 goto fail;
6533 (void) strcpy(nf->src_nd_name, ofilter->src_nd_name);
6535 if (ofilter->dst_nd_name) {
6536 nf->dst_nd_name = malloc(strlen(ofilter->dst_nd_name) + 1);
6537 if (nf->dst_nd_name == NULL) {
6538 ipqos_msg(MT_ENOSTR, "malloc");
6539 goto fail;
6541 (void) strcpy(nf->dst_nd_name, ofilter->dst_nd_name);
6544 /* add filter addresses type */
6546 res = nvlist_add_byte(nf->nvlist, IPGPC_FILTER_TYPE,
6547 af == AF_INET ? IPGPC_V4_FLTR : IPGPC_V6_FLTR);
6548 if (res != 0) {
6549 ipqos_msg(MT_ENOSTR, "nvlist_add_byte");
6550 goto fail;
6552 IPQOSCDBG1(MHME, "adding address type %s in dup filter\n",
6553 af == AF_INET ? "AF_INET" : "AF_INET6");
6555 /* add saddr if present */
6557 if (saddr) {
6558 if (af == AF_INET && !inv6) {
6559 V4_PART_OF_V6(v6addr) = *(uint32_t *)saddr;
6560 saddr = &v6addr;
6563 /* add address and all 1's mask */
6565 if (nvlist_add_uint32_array(nf->nvlist, IPGPC_SADDR,
6566 (uint32_t *)saddr, 4) != 0 ||
6567 nvlist_add_uint32_array(nf->nvlist, IPGPC_SADDR_MASK,
6568 (uint32_t *)&all_1s_v6, 4) != 0) {
6569 ipqos_msg(MT_ENOSTR, "nvlist_add_uint32_array");
6570 goto fail;
6575 /* add daddr if present */
6577 if (daddr) {
6578 if (af == AF_INET && !inv6) {
6579 V4_PART_OF_V6(v6addr) = *(uint32_t *)daddr;
6580 daddr = &v6addr;
6583 /* add address and all 1's mask */
6585 if (nvlist_add_uint32_array(nf->nvlist, IPGPC_DADDR,
6586 (uint32_t *)daddr, 4) != 0 ||
6587 nvlist_add_uint32_array(nf->nvlist, IPGPC_DADDR_MASK,
6588 (uint32_t *)&all_1s_v6, 4) != 0) {
6589 ipqos_msg(MT_ENOSTR, "nvlist_add_uint32_array");
6590 goto fail;
6594 /* add filter instance */
6596 nf->instance = inst;
6598 *nfilter = nf;
6599 return (IPQOS_CONF_SUCCESS);
6600 fail:
6601 free_filter(nf);
6602 return (IPQOS_CONF_ERR);
6607 /* ************************* action manip fns ********************** */
6612 * create and zero action structure and a params structure hung off of it.
6613 * RETURNS: ptr to allocated action on success, else NULL.
6615 static ipqos_conf_action_t *
6616 alloc_action()
6619 ipqos_conf_action_t *action;
6621 action = (ipqos_conf_action_t *)malloc(sizeof (ipqos_conf_action_t));
6622 if (action == NULL) {
6623 ipqos_msg(MT_ENOSTR, "malloc");
6624 return (action);
6626 bzero(action, sizeof (ipqos_conf_action_t));
6628 action->params = (ipqos_conf_params_t *)
6629 malloc(sizeof (ipqos_conf_params_t));
6630 if (action->params == NULL) {
6631 free(action);
6632 return (NULL);
6634 bzero(action->params, sizeof (ipqos_conf_params_t));
6635 action->params->stats_enable = B_FALSE;
6637 return (action);
6641 * free all the memory used in all the actions in actions list.
6643 static void
6644 free_actions(
6645 ipqos_conf_action_t *actions)
6648 ipqos_conf_action_t *act = actions;
6649 ipqos_conf_action_t *next;
6650 ipqos_conf_filter_t *flt, *nf;
6651 ipqos_conf_class_t *cls, *nc;
6653 while (act != NULL) {
6654 /* free parameters */
6656 if (act->params != NULL) {
6657 free_arefs(act->params->actions);
6658 if (act->params->nvlist != NULL) {
6659 nvlist_free(act->params->nvlist);
6661 free(act->params);
6664 /* free action nvlist */
6666 if (act->nvlist != NULL)
6667 free(act->nvlist);
6669 /* free filters */
6671 flt = act->filters;
6672 while (flt != NULL) {
6673 nf = flt->next;
6674 free_filter(flt);
6675 flt = nf;
6678 /* free classes */
6680 cls = act->classes;
6681 while (cls != NULL) {
6682 nc = cls->next;
6683 free_class(cls);
6684 cls = nc;
6687 /* free permanent classes table */
6688 cleanup_string_table(act->perm_classes, act->num_perm_classes);
6690 /* free filters to retry */
6692 flt = act->retry_filters;
6693 while (flt != NULL) {
6694 nf = flt->next;
6695 free_filter(flt);
6696 flt = nf;
6699 /* free dependency pointers */
6700 free_arefs(act->dependencies);
6702 next = act->next;
6703 free(act);
6704 act = next;
6709 * Checks whether there is an action called action_name in actions list.
6710 * RETURNS: ptr to first matched action, else if not matched NULL.
6713 static ipqos_conf_action_t *
6714 actionexist(
6715 char *action_name,
6716 ipqos_conf_action_t *actions)
6719 IPQOSCDBG1(L1, "In actionexist: name: %s\n", action_name);
6721 while (actions) {
6722 if (strcmp(action_name, actions->name) == 0) {
6723 break;
6725 actions = actions->next;
6728 return (actions);
6731 /* **************************** act ref manip fns ******************** */
6735 * add an action reference element with parameter field and action
6736 * action_name to arefs.
6737 * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCES.
6739 static int
6740 add_aref(
6741 ipqos_conf_act_ref_t **arefs,
6742 char *field,
6743 char *action_name)
6746 ipqos_conf_act_ref_t *aref;
6748 IPQOSCDBG1(L1, "add_aref: action: %s.\n", action_name);
6750 /* allocate zero'd aref */
6752 aref = malloc(sizeof (ipqos_conf_act_ref_t));
6753 if (aref == NULL) {
6754 ipqos_msg(MT_ENOSTR, "malloc");
6755 return (IPQOS_CONF_ERR);
6757 (void) bzero(aref, sizeof (ipqos_conf_act_ref_t));
6759 /* copy parameter name if present */
6761 if (field)
6762 (void) strlcpy(aref->field, field, IPQOS_CONF_PNAME_LEN);
6764 /* copy action name */
6765 (void) strlcpy(aref->name, action_name, IPQOS_CONF_NAME_LEN);
6767 /* place at head of list */
6769 aref->next = *arefs;
6770 *arefs = aref;
6772 return (IPQOS_CONF_SUCCESS);
6776 * free all the memory used by the action references in arefs.
6778 static void
6779 free_arefs(
6780 ipqos_conf_act_ref_t *arefs)
6783 ipqos_conf_act_ref_t *aref = arefs;
6784 ipqos_conf_act_ref_t *next;
6786 while (aref) {
6787 nvlist_free(aref->nvlist);
6788 next = aref->next;
6789 free(aref);
6790 aref = next;
6796 /* *************************************************************** */
6801 * checks whether aname is a valid action name.
6802 * RETURNS: IPQOS_CONF_ERR if invalid, else IPQOS_CONF_SUCCESS.
6804 static int
6805 valid_aname(char *aname)
6809 * dissallow the use of the name of a virtual action, either
6810 * the ipqosconf name, or the longer ipp names.
6812 if (strcmp(aname, IPQOS_CONF_CONT_STR) == 0 ||
6813 strcmp(aname, IPQOS_CONF_DEFER_STR) == 0 ||
6814 strcmp(aname, IPQOS_CONF_DROP_STR) == 0 ||
6815 virtual_action(aname)) {
6816 ipqos_msg(MT_ERROR, gettext("Invalid action name line %u.\n"),
6817 lineno);
6818 return (IPQOS_CONF_ERR);
6821 return (IPQOS_CONF_SUCCESS);
6825 * Opens a stream to the types file for module module_name (assuming
6826 * that the file path is TYPES_FILE_DIR/module_name.types). if
6827 * a file open failure occurs, *openerr is set to 1.
6828 * RETURNS: NULL on error, else stream ptr to module types file.
6830 static FILE *
6831 validmod(
6832 char *module_name,
6833 int *openerr)
6836 FILE *fp;
6837 char *path;
6839 IPQOSCDBG1(L1, "In validmod: module_name: %s\n", module_name);
6841 *openerr = 0;
6843 /* create modules type file path */
6845 path = malloc(strlen(TYPES_FILE_DIR) + strlen(module_name) +
6846 strlen(".types") + 1);
6847 if (path == NULL) {
6848 ipqos_msg(MT_ENOSTR, "malloc");
6849 return (NULL);
6851 (void) strcpy(path, TYPES_FILE_DIR);
6852 (void) strcat(path, module_name);
6853 (void) strcat(path, ".types");
6856 IPQOSCDBG1(L1, "opening file %s\n", path);
6858 /* open stream to types file */
6860 fp = fopen(path, "r");
6861 if (fp == NULL) {
6862 (*openerr)++;
6865 free(path);
6866 return (fp);
6871 * read a class clause from cfp into a class struct and point class at this.
6872 * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCESS.
6874 static int
6875 readclass(
6876 FILE *cfp,
6877 char *module_name,
6878 ipqos_conf_class_t **class,
6879 char **perm_classes,
6880 int num_perm_classes)
6883 int nm, act;
6884 int res;
6885 nvpair_t *nvp;
6886 ipqos_nvtype_t type;
6887 char *name;
6888 char *action;
6889 int stats;
6891 IPQOSCDBG0(L0, "in readclass\n");
6893 /* create and zero class struct */
6895 *class = alloc_class();
6896 if (!*class) {
6897 return (IPQOS_CONF_ERR);
6899 (*class)->originator = IPP_CONFIG_IPQOSCONF;
6901 /* get starting line for error reporting */
6902 (*class)->lineno = lineno;
6904 /* read curl_begin */
6906 res = read_curl_begin(cfp);
6907 if (res != IPQOS_CONF_SUCCESS) {
6908 goto fail;
6911 /* loop reading parameters till read curl_end */
6913 stats = nm = act = 0;
6914 for (;;) {
6915 /* read nvpair */
6916 res = readnvpair(cfp, NULL, &(*class)->nvlist,
6917 &nvp, &type, PL_CLASS, module_name);
6918 if (res == IPQOS_CONF_ERR) {
6919 goto fail;
6921 /* reached end of class clause */
6922 } else if (res == IPQOS_CONF_CURL_END) {
6923 break;
6927 * catch name and action nv pairs and stats if present
6928 * and place values in class structure.
6931 /* name */
6933 if (nm == 0 &&
6934 strcmp(nvpair_name(nvp), IPQOS_CONF_NAME_STR) == 0) {
6936 (void) nvpair_value_string(nvp, &name);
6938 if (valid_name(name) != IPQOS_CONF_SUCCESS) {
6939 goto fail;
6941 (void) strcpy((*class)->name, name);
6942 nm++;
6944 /* next action */
6946 } else if (act == 0 &&
6947 strcmp(nvpair_name(nvp), IPQOS_CONF_NEXT_ACTION_STR) == 0) {
6949 (void) nvpair_value_string(nvp, &action);
6952 * if next action string continue string set action to
6953 * IPP_ANAME_CONT, else if drop string IPP_ANAME_DROP
6955 if (strcmp(action, IPQOS_CONF_CONT_STR) == 0) {
6956 action = IPP_ANAME_CONT;
6957 } else if (strcmp(action, IPQOS_CONF_DROP_STR) == 0) {
6958 action = IPP_ANAME_DROP;
6961 /* add an action reference to action list */
6963 res = add_aref(&(*class)->alist,
6964 IPQOS_CONF_NEXT_ACTION_STR, action);
6965 if (res != IPQOS_CONF_SUCCESS) {
6966 goto fail;
6968 act++;
6970 /* class stats enable */
6972 } else if (stats == 0 &&
6973 strcmp(nvpair_name(nvp), IPQOS_CONF_STATS_ENABLE_STR) ==
6974 0) {
6975 boolean_t bl;
6977 (void) nvpair_value_uint32(nvp, (uint32_t *)&bl);
6978 (*class)->stats_enable = bl;
6980 stats++;
6982 /* no other / duplicate parameters allowed */
6984 } else {
6985 ipqos_msg(MT_ERROR,
6986 gettext("Unexpected parameter line %u.\n"), lineno);
6987 goto fail;
6990 if (nm == 0 || act == 0) {
6991 ipqos_msg(MT_ERROR,
6992 gettext("Missing class name/next action before line %u.\n"),
6993 lineno);
6994 goto fail;
6997 /* change class originator field to permanent if permanent class */
6999 if (in_string_table(perm_classes, num_perm_classes, (*class)->name)) {
7000 IPQOSCDBG1(L0, "Setting class %s as permanent.\n", (*class)->name);
7001 (*class)->originator = IPP_CONFIG_PERMANENT;
7004 return (IPQOS_CONF_SUCCESS);
7005 fail:
7006 if (*class)
7007 free_class(*class);
7008 return (IPQOS_CONF_ERR);
7012 * This function assumes either src_nd_name or dst_node_nm are set in filter.
7014 * Creates one of more copies of filter according to the ip versions
7015 * requested (or assumed) and the resolution of the src and dst address
7016 * node names if spec'd. If both node names are spec'd then a filter is
7017 * created for each pair of addresses (one from each node name) that is
7018 * compatible with the chosen address family, otherwise a filter copy is
7019 * created for just each address of the single node name that is
7020 * compatible.
7021 * If filter->ip_versions has been set that is used to determine the
7022 * af's we will create filters for, else if a numeric address was
7023 * added the family of that will be used, otherwise we fall back
7024 * to both v4 and v6 addresses.
7026 * Any name lookup failures that occur are checked to see whether the failure
7027 * was a soft or hard failure and the nlerr field of filter set accordingly
7028 * before the error is returned.
7030 * RETURNS: IPQOS_CONF_ERR on any error, else IPQOS_CONF_SUCCESS.
7033 static int
7034 domultihome(
7035 ipqos_conf_filter_t *filter,
7036 ipqos_conf_filter_t **flist,
7037 boolean_t last_retry)
7040 uint32_t ftype;
7041 int v4 = 1, v6 = 1; /* default lookup family is v4 and v6 */
7042 int saf, daf;
7043 struct hostent *shp = NULL;
7044 struct hostent *dhp = NULL;
7045 in6_addr_t daddr, saddr;
7046 int idx = 0;
7047 ipqos_conf_filter_t *nfilter;
7048 int res;
7049 int ernum;
7050 int in32b = 0;
7051 char **sp, **dp;
7053 IPQOSCDBG3(MHME, "In domultihome: filter: %s, src_node: %s, "
7054 "dst_node: %s\n", filter->name,
7055 (filter->src_nd_name ? filter->src_nd_name : "NULL"),
7056 (filter->dst_nd_name ? filter->dst_nd_name : "NULL"));
7058 /* check if we've read an ip_version request to get the versions */
7060 if (filter->ip_versions != 0) {
7061 v4 = VERSION_IS_V4(filter);
7062 v6 = VERSION_IS_V6(filter);
7064 /* otherwise check if we've read a numeric address and get versions */
7066 } else if (nvlist_lookup_uint32(filter->nvlist, IPGPC_FILTER_TYPE,
7067 &ftype) == 0) {
7068 if (ftype == IPGPC_V4_FLTR) {
7069 v6--;
7070 } else {
7071 v4--;
7075 /* read saddrs if src node name */
7077 if (filter->src_nd_name) {
7079 /* v4 only address */
7081 if (v4 && !v6) {
7082 in32b++;
7083 shp = getipnodebyname(filter->src_nd_name, AF_INET,
7084 AI_ADDRCONFIG, &ernum);
7086 /* v6 only */
7088 } else if (v6 && !v4) {
7089 shp = getipnodebyname(filter->src_nd_name, AF_INET6,
7090 AI_DEFAULT, &ernum);
7092 /* v4 and v6 */
7094 } else if (v6 && v4) {
7095 shp = getipnodebyname(filter->src_nd_name, AF_INET6,
7096 AI_DEFAULT|AI_ALL, &ernum);
7099 #ifdef TESTING_RETRY
7100 if (!last_retry) {
7101 filter->nlerr = IPQOS_LOOKUP_RETRY;
7102 goto fail;
7104 #endif
7107 * if lookup error determine whether it was a soft or hard
7108 * failure and mark as such in filter.
7110 if (shp == NULL) {
7111 if (ernum != TRY_AGAIN) {
7112 ipqos_msg(MT_ERROR, gettext("Failed to "
7113 "resolve src host name for filter at "
7114 "line %u, ignoring filter.\n"),
7115 filter->lineno);
7116 filter->nlerr = IPQOS_LOOKUP_FAIL;
7117 } else {
7118 if (last_retry) {
7119 ipqos_msg(MT_ERROR, gettext("Failed "
7120 "to resolve src host name for "
7121 "filter at line %u, ignoring "
7122 "filter.\n"), filter->lineno);
7124 filter->nlerr = IPQOS_LOOKUP_RETRY;
7126 goto fail;
7130 /* read daddrs if dst node name */
7131 if (filter->dst_nd_name) {
7133 /* v4 only address */
7135 if (v4 && !v6) {
7136 in32b++;
7137 dhp = getipnodebyname(filter->dst_nd_name, AF_INET,
7138 AI_ADDRCONFIG, &ernum);
7140 /* v6 only */
7142 } else if (v6 && !v4) {
7143 dhp = getipnodebyname(filter->dst_nd_name, AF_INET6,
7144 AI_DEFAULT, &ernum);
7146 /* v6 and v4 addresses */
7148 } else {
7149 dhp = getipnodebyname(filter->dst_nd_name, AF_INET6,
7150 AI_DEFAULT|AI_ALL, &ernum);
7153 if (dhp == NULL) {
7154 if (ernum != TRY_AGAIN) {
7155 ipqos_msg(MT_ERROR, gettext("Failed to "
7156 "resolve dst host name for filter at "
7157 "line %u, ignoring filter.\n"),
7158 filter->lineno);
7159 filter->nlerr = IPQOS_LOOKUP_FAIL;
7160 } else {
7161 if (last_retry) {
7162 ipqos_msg(MT_ERROR, gettext("Failed "
7163 "to resolve dst host name for "
7164 "filter at line %u, ignoring "
7165 "filter.\n"), filter->lineno);
7167 filter->nlerr = IPQOS_LOOKUP_RETRY;
7169 goto fail;
7174 * if src and dst node name, create set of filters; one for each
7175 * src and dst address of matching types.
7177 if (filter->src_nd_name && filter->dst_nd_name) {
7179 for (sp = shp->h_addr_list; *sp != NULL; sp++) {
7180 (void) bcopy(*sp, &saddr, shp->h_length);
7182 /* get saddr family */
7184 if (in32b || IN6_IS_ADDR_V4MAPPED(&saddr)) {
7185 saf = AF_INET;
7186 } else {
7187 saf = AF_INET6;
7190 for (dp = dhp->h_addr_list; *dp != NULL; dp++) {
7191 (void) bcopy(*dp, &daddr, dhp->h_length);
7193 /* get daddr family */
7195 if (in32b || IN6_IS_ADDR_V4MAPPED(&daddr)) {
7196 daf = AF_INET;
7197 } else {
7198 daf = AF_INET6;
7202 * if saddr and daddr same af duplicate
7203 * filter adding addresses and new instance
7204 * number and add to flist filter list.
7207 if (daf == saf) {
7209 res = dup_filter(filter, &nfilter, saf,
7210 !in32b, &saddr, &daddr, ++idx);
7211 if (res != IPQOS_CONF_SUCCESS) {
7212 goto fail;
7214 ADD_TO_LIST(flist, nfilter);
7219 /* if src name only create set of filters, one for each node address */
7221 } else if (filter->src_nd_name) {
7223 for (sp = shp->h_addr_list; *sp != NULL; sp++) {
7224 (void) bcopy(*sp, &saddr, shp->h_length);
7226 /* get af */
7228 if (in32b || IN6_IS_ADDR_V4MAPPED(&saddr)) {
7229 saf = AF_INET;
7230 } else {
7231 saf = AF_INET6;
7236 * dup filter adding saddr and new instance num and
7237 * add to flist filter list.
7239 res = dup_filter(filter, &nfilter, saf, !in32b, &saddr,
7240 NULL, ++idx);
7241 if (res != IPQOS_CONF_SUCCESS) {
7242 goto fail;
7245 ADD_TO_LIST(flist, nfilter);
7249 /* if dname only create set of filters, one for each node address */
7251 } else {
7252 for (dp = dhp->h_addr_list; *dp != NULL; dp++) {
7253 (void) bcopy(*dp, &daddr, dhp->h_length);
7255 /* get af */
7257 if (in32b || IN6_IS_ADDR_V4MAPPED(&daddr)) {
7258 daf = AF_INET;
7259 } else {
7260 daf = AF_INET6;
7264 * dup filter adding daddr and new instance num and
7265 * add to flist filter list.
7267 res = dup_filter(filter, &nfilter, daf, !in32b, NULL,
7268 &daddr, ++idx);
7269 if (res != IPQOS_CONF_SUCCESS) {
7270 goto fail;
7273 ADD_TO_LIST(flist, nfilter);
7277 if (shp)
7278 freehostent(shp);
7279 if (dhp)
7280 freehostent(dhp);
7281 return (IPQOS_CONF_SUCCESS);
7282 fail:
7284 * should really clean up any filters that we have created,
7285 * however, free_actions called from readaction will cleam them up.
7287 if (shp)
7288 freehostent(shp);
7289 if (dhp)
7290 freehostent(dhp);
7291 return (IPQOS_CONF_ERR);
7296 * read a filter clause from cfp into a filter struct and point filter
7297 * at this.
7298 * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCES.
7300 static int
7301 readfilter(
7302 FILE *cfp,
7303 FILE *tfp,
7304 char *module_name,
7305 ipqos_conf_filter_t **filter,
7306 char **perm_filters,
7307 int num_perm_filters)
7310 int res;
7311 int nm, cls, ipv;
7312 in6_addr_t mask;
7313 char *addr_str;
7314 char *sl = NULL;
7315 in6_addr_t addr;
7316 int sa;
7317 struct hostent *hp;
7318 int err_num;
7319 int v4 = 0, v6 = 0;
7320 uchar_t mlen;
7321 char *tmp;
7322 nvpair_t *nvp;
7323 ipqos_nvtype_t type;
7324 char *name;
7325 char *class;
7326 uchar_t b;
7327 in6_addr_t v6addr;
7329 IPQOSCDBG0(L0, "in readfilter\n");
7332 /* create and zero filter struct */
7334 *filter = alloc_filter();
7335 if (*filter == NULL) {
7336 return (IPQOS_CONF_ERR);
7338 (*filter)->originator = IPP_CONFIG_IPQOSCONF;
7340 /* get starting line for error reporting */
7341 (*filter)->lineno = lineno;
7343 /* read beginning curl */
7345 res = read_curl_begin(cfp);
7346 if (res != IPQOS_CONF_SUCCESS) {
7347 goto fail;
7352 * loop reading nvpairs onto nvlist until encounter CURL_END
7354 ipv = nm = cls = 0;
7355 for (;;) {
7356 /* read nvpair */
7358 res = readnvpair(cfp, tfp, &(*filter)->nvlist,
7359 &nvp, &type, PL_FILTER, module_name);
7360 if (res == IPQOS_CONF_ERR) {
7361 goto fail;
7363 /* reached the end of filter definition */
7365 } else if (res == IPQOS_CONF_CURL_END) {
7366 break;
7370 * catch name and class and place value into filter
7371 * structure.
7374 /* read filter name */
7376 if (strcmp(nvpair_name(nvp), IPQOS_CONF_NAME_STR) == 0) {
7377 if (nm != 0) {
7378 ipqos_msg(MT_ERROR,
7379 gettext("Duplicate parameter line %u.\n"),
7380 lineno);
7381 goto fail;
7384 (void) nvpair_value_string(nvp, &name);
7385 if (valid_name(name) != IPQOS_CONF_SUCCESS) {
7386 goto fail;
7389 (void) strcpy((*filter)->name, name);
7390 (void) nvlist_remove_all((*filter)->nvlist,
7391 IPQOS_CONF_NAME_STR);
7392 nm++;
7394 /* read class name */
7396 } else if (strcmp(nvpair_name(nvp), IPQOS_CONF_CLASS_STR) ==
7397 0) {
7398 if (cls != 0) {
7399 ipqos_msg(MT_ERROR,
7400 gettext("Duplicate parameter line %u.\n"),
7401 lineno);
7402 goto fail;
7405 if (nvpair_value_string(nvp, &class) != 0) {
7406 ipqos_msg(MT_ENOSTR, "nvpair_value_string");
7407 break;
7409 if (valid_name(class) != IPQOS_CONF_SUCCESS) {
7410 goto fail;
7412 (void) strcpy((*filter)->class_name, class);
7413 (void) nvlist_remove_all((*filter)->nvlist,
7414 IPQOS_CONF_CLASS_STR);
7415 cls++;
7418 * if a src or dst ip node name/address. For those that
7419 * are determined to be addresses we convert them from
7420 * strings here and add to the filter nvlist; for node names
7421 * we add the name to the filter struct for readaction to
7422 * process.
7424 } else if (strcmp(nvpair_name(nvp), IPGPC_SADDR) == 0 ||
7425 strcmp(nvpair_name(nvp), IPGPC_DADDR) == 0) {
7427 sa = 0;
7429 if (strcmp(nvpair_name(nvp), IPGPC_SADDR) == 0) {
7430 sa++;
7433 (void) nvpair_value_string(nvp, &addr_str);
7436 * get the address mask if present.
7437 * make a copy so that the nvlist element that
7438 * it is part of doesn't dissapear and causes probs.
7440 sl = strchr(addr_str, '/');
7441 if (sl) {
7442 *sl = '\0';
7443 tmp = malloc(strlen(++sl) + 1);
7444 if (tmp == NULL) {
7445 ipqos_msg(MT_ENOSTR, "malloc");
7446 goto fail;
7448 (void) strcpy(tmp, sl);
7449 sl = tmp;
7453 /* if a numeric address */
7455 if (inet_pton(AF_INET, addr_str, &addr) == 1 ||
7456 inet_pton(AF_INET6, addr_str, &addr) == 1) {
7458 /* get address */
7460 hp = getipnodebyname(addr_str, AF_INET6,
7461 AI_DEFAULT, &err_num);
7462 if (hp == NULL) {
7463 ipqos_msg(MT_ENOSTR,
7464 "getipnodebyname");
7465 goto fail;
7468 (void) bcopy(hp->h_addr_list[0], &v6addr,
7469 hp->h_length);
7470 freehostent(hp);
7472 /* determine address type */
7474 v4 = IN6_IS_ADDR_V4MAPPED(&v6addr);
7475 if (!v4) {
7476 v6++;
7480 * check any previous addresses have same
7481 * version.
7483 if (nvlist_lookup_byte((*filter)->nvlist,
7484 IPGPC_FILTER_TYPE, &b) == 0) {
7485 if (v4 && b != IPGPC_V4_FLTR ||
7486 v6 && b != IPGPC_V6_FLTR) {
7487 ipqos_msg(MT_ERROR,
7488 gettext("Incompatible "
7489 "address version line "
7490 "%u.\n"), lineno);
7491 goto fail;
7496 * check that if ip_version spec'd it
7497 * corresponds.
7499 if ((*filter)->ip_versions != 0) {
7500 if (v4 && !VERSION_IS_V4(*filter) ||
7501 v6 && !VERSION_IS_V6(*filter)) {
7502 ipqos_msg(MT_ERROR,
7503 gettext("Incompatible "
7504 "address version line %u"
7505 ".\n"), lineno);
7506 goto fail;
7510 /* add the address type */
7512 res = nvlist_add_byte(
7513 (*filter)->nvlist, IPGPC_FILTER_TYPE,
7514 v4 ? IPGPC_V4_FLTR : IPGPC_V6_FLTR);
7515 if (res != 0) {
7516 ipqos_msg(MT_ENOSTR,
7517 "nvlist_add_byte");
7518 goto fail;
7521 /* add address to list */
7523 res = nvlist_add_uint32_array((*filter)->nvlist,
7524 sa ? IPGPC_SADDR : IPGPC_DADDR,
7525 (uint32_t *)&v6addr, 4);
7526 if (res != 0) {
7527 ipqos_msg(MT_ENOSTR,
7528 "nvlist_add_uint32_array");
7529 goto fail;
7534 * add mask entry in list.
7537 if (sl) { /* have CIDR mask */
7538 char *lo;
7539 res = readuint8(sl, &mlen, &lo);
7540 if (res != IPQOS_CONF_SUCCESS ||
7541 v4 && mlen > 32 ||
7542 !v4 && mlen > 128 ||
7543 mlen == 0) {
7544 ipqos_msg(MT_ERROR,
7545 gettext("Invalid CIDR "
7546 "mask line %u.\n"), lineno);
7547 goto fail;
7549 setmask(mlen, &mask,
7550 v4 ? AF_INET : AF_INET6);
7551 free(sl);
7552 } else {
7553 /* no CIDR mask spec'd - use all 1s */
7555 (void) memset(&mask, ~0,
7556 sizeof (in6_addr_t));
7558 res = nvlist_add_uint32_array((*filter)->nvlist,
7559 sa ? IPGPC_SADDR_MASK : IPGPC_DADDR_MASK,
7560 (uint32_t *)&mask, 4);
7561 if (res != 0) {
7562 ipqos_msg(MT_ENOSTR,
7563 "nvlist_add_uint32_arr");
7564 goto fail;
7567 /* inet_pton returns fail - we assume a node name */
7569 } else {
7571 * doesn't make sense to have a mask
7572 * with a node name.
7574 if (sl) {
7575 ipqos_msg(MT_ERROR,
7576 gettext("Address masks aren't "
7577 "allowed for host names line "
7578 "%u.\n"), lineno);
7579 goto fail;
7583 * store node name in filter struct for
7584 * later resolution.
7586 if (sa) {
7587 (*filter)->src_nd_name =
7588 malloc(strlen(addr_str) + 1);
7589 (void) strcpy((*filter)->src_nd_name,
7590 addr_str);
7591 } else {
7592 (*filter)->dst_nd_name =
7593 malloc(strlen(addr_str) + 1);
7594 (void) strcpy((*filter)->dst_nd_name,
7595 addr_str);
7599 /* ip_version enumeration */
7601 } else if (strcmp(nvpair_name(nvp), IPQOS_CONF_IP_VERSION) ==
7602 0) {
7603 /* check we haven't read ip_version before */
7604 if (ipv) {
7605 ipqos_msg(MT_ERROR,
7606 gettext("Duplicate parameter line %u.\n"),
7607 lineno);
7608 goto fail;
7610 ipv++;
7612 /* get bitmask value */
7614 (void) nvpair_value_uint32(nvp,
7615 &(*filter)->ip_versions);
7618 * check that if either ip address is spec'd it
7619 * corresponds.
7621 if (v4 && !VERSION_IS_V4(*filter) ||
7622 v6 && !VERSION_IS_V6(*filter)) {
7623 ipqos_msg(MT_ERROR, gettext("Incompatible "
7624 "address version line %u.\n"), lineno);
7625 goto fail;
7628 /* remove ip_version from nvlist */
7630 (void) nvlist_remove_all((*filter)->nvlist,
7631 IPQOS_CONF_IP_VERSION);
7634 if (nm == 0 || cls == 0) {
7635 ipqos_msg(MT_ERROR, gettext("Missing filter/class name "
7636 "before line %u.\n"), lineno);
7637 goto fail;
7640 if (in_string_table(perm_filters, num_perm_filters, (*filter)->name)) {
7641 IPQOSCDBG1(L0, "Setting filter %s as permanent.\n",
7642 (*filter)->name);
7644 (*filter)->originator = IPP_CONFIG_PERMANENT;
7647 return (IPQOS_CONF_SUCCESS);
7648 fail:
7649 if (*filter)
7650 free_filter(*filter);
7651 if (hp)
7652 freehostent(hp);
7653 if (sl)
7654 free(sl);
7656 return (IPQOS_CONF_ERR);
7660 * reads the curl begin token from cfp stream.
7661 * RETURNS: IPQOS_CONF_ERR if not read successfully, else IPQOS_CONF_SUCCES.
7663 static int
7664 read_curl_begin(FILE *cfp)
7667 int res;
7668 char *st;
7670 res = readtoken(cfp, &st);
7672 if (res != IPQOS_CONF_CURL_BEGIN) {
7673 if (res == IPQOS_CONF_EOF) {
7674 ipqos_msg(MT_ERROR, gettext("Unexpected EOF.\n"));
7676 /* if CURL_END or something else */
7677 } else if (res != IPQOS_CONF_ERR) {
7678 free(st);
7679 ipqos_msg(MT_ERROR, gettext("\'{\' missing at line "
7680 "%u.\n"), lineno);
7682 return (IPQOS_CONF_ERR);
7685 free(st);
7686 return (IPQOS_CONF_SUCCESS);
7690 * This function parses the parameter string version into a version of the
7691 * form "%u.%u" (as a sscanf format string). It then encodes this into an
7692 * int and returns this encoding.
7693 * RETURNS: -1 if an invalid string, else the integer encoding.
7695 static int
7696 ver_str_to_int(
7697 char *version)
7699 uint32_t major, minor;
7700 int ver;
7702 if (sscanf(version, "%u.%u", &major, &minor) != 2) {
7703 IPQOSCDBG0(L0, "Failed to process version number string\n");
7704 return (-1);
7707 ver = (int)((major * 10000) + minor);
7708 return (ver);
7712 * This function scans through the stream fp line by line looking for
7713 * a line beginning with version_tag and returns a integer encoding of
7714 * the version following it.
7716 * RETURNS: If the version definition isn't found or the version is not
7717 * a valid version (%u.%u) then -1 is returned, else an integer encoding
7718 * of the read version.
7720 static int
7721 read_tfile_ver(
7722 FILE *fp,
7723 char *version_tag,
7724 char *module_name)
7726 char lbuf[IPQOS_CONF_LINEBUF_SZ];
7727 char buf[IPQOS_CONF_LINEBUF_SZ+1];
7728 char buf2[IPQOS_CONF_LINEBUF_SZ+1];
7729 int found = 0;
7730 int version;
7733 * reset to file start
7735 if (fseek(fp, 0, SEEK_SET) != 0) {
7736 ipqos_msg(MT_ENOSTR, "fseek");
7737 return (-1);
7741 * loop reading lines till found the one beginning with version_tag.
7743 while (fgets(lbuf, IPQOS_CONF_LINEBUF_SZ, fp) != NULL) {
7744 if ((sscanf(lbuf,
7745 "%" VAL2STR(IPQOS_CONF_LINEBUF_SZ) "s"
7746 "%" VAL2STR(IPQOS_CONF_LINEBUF_SZ) "s",
7747 buf, buf2) == 2) &&
7748 (strcmp(buf, version_tag) == 0)) {
7749 found++;
7750 break;
7753 if (found == 0) {
7754 ipqos_msg(MT_ERROR, gettext("Types file for module %s is "
7755 "corrupt.\n"), module_name);
7756 IPQOSCDBG1(L1, "Couldn't find %s in types file\n",
7757 version_tag);
7758 return (-1);
7762 * convert version string into int.
7764 if ((version = ver_str_to_int(buf2)) == -1) {
7765 ipqos_msg(MT_ERROR, gettext("Types file for module %s is "
7766 "corrupt.\n"), module_name);
7767 return (-1);
7770 return (version);
7774 * read action clause and params/classes/filters clauses within and
7775 * store in and hang off an action structure, and point action at it.
7776 * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCES.
7778 static int
7779 readaction(
7780 FILE *cfp,
7781 ipqos_conf_action_t **action)
7784 char *st;
7785 FILE *tfp = NULL;
7786 int nm, md;
7787 int readprms = 0;
7788 int res;
7789 char *strval;
7790 char *name;
7791 nvpair_t *nvp;
7792 ipqos_nvtype_t type;
7793 ipqos_conf_filter_t *filter;
7794 ipqos_conf_class_t *class;
7795 int oe;
7796 char **perm_filters;
7797 int num_perm_filters;
7798 int tf_fmt_ver;
7800 IPQOSCDBG0(L0, "in readaction\n");
7802 res = readtoken(cfp, &st);
7803 if (res == IPQOS_CONF_ERR || res == IPQOS_CONF_EOF) {
7804 return (res);
7805 } else if (strcmp(st, IPQOS_CONF_ACTION_STR) != 0) {
7806 ipqos_msg(MT_ERROR, gettext("Missing %s token line "
7807 "%u.\n"), IPQOS_CONF_ACTION_STR, lineno);
7808 free(st);
7809 return (IPQOS_CONF_ERR);
7811 free(st);
7813 /* create action structure */
7815 *action = alloc_action();
7816 if (*action == NULL) {
7817 return (IPQOS_CONF_ERR);
7819 (*action)->params->originator = IPP_CONFIG_IPQOSCONF;
7822 /* get starting line for error reporting */
7823 (*action)->lineno = lineno;
7825 /* read beginning curl */
7827 res = read_curl_begin(cfp);
7828 if (res != IPQOS_CONF_SUCCESS) {
7829 goto fail;
7832 /* loop till read both action name and module */
7834 nm = md = 0;
7835 do {
7836 /* read nvpair */
7838 res = readnvpair(cfp, NULL, &(*action)->nvlist, &nvp, &type,
7839 PL_ACTION, NULL);
7840 if (res == IPQOS_CONF_ERR) {
7841 goto fail;
7843 /* read curl_end */
7845 } else if (res == IPQOS_CONF_CURL_END) {
7846 if (nm == 0 || md == 0) {
7847 ipqos_msg(MT_ERROR,
7848 gettext("Missing action name/ module "
7849 "before line %u.\n"), lineno);
7850 goto fail;
7855 /* store name and module in action structure */
7857 name = nvpair_name(nvp);
7859 /* read action name */
7861 if (nm == 0 && strcmp(name, IPQOS_CONF_NAME_STR) == 0) {
7863 (void) nvpair_value_string(nvp, &strval);
7865 /* check name is valid */
7867 if (valid_name(strval) != IPQOS_CONF_SUCCESS ||
7868 valid_aname(strval) != IPQOS_CONF_SUCCESS) {
7869 goto fail;
7872 /* store and remove from list */
7874 (void) strcpy((*action)->name, strval);
7875 /* remove name from nvlist */
7876 (void) nvlist_remove_all((*action)->nvlist,
7877 IPQOS_CONF_NAME_STR);
7879 nm++;
7881 /* read module name */
7883 } else if (md == 0 &&
7884 strcmp(name, IPQOS_CONF_MODULE_STR) == 0) {
7886 * check that module has a type file and get
7887 * open stream to it.
7889 (void) nvpair_value_string(nvp, &strval);
7890 if ((tfp = validmod(strval, &oe)) == NULL) {
7891 if (oe) {
7892 if (errno == ENOENT) {
7893 ipqos_msg(MT_ERROR,
7894 gettext("Invalid "
7895 "module name line %u.\n"),
7896 lineno);
7897 } else {
7898 ipqos_msg(MT_ENOSTR, "fopen");
7901 goto fail;
7905 * move module name to action struct
7907 (void) strlcpy((*action)->module, strval,
7908 IPQOS_CONF_NAME_LEN);
7909 (void) nvlist_remove_all((*action)->nvlist,
7910 IPQOS_CONF_MODULE_STR);
7911 md++;
7913 /* duplicate/other parameter */
7915 } else {
7916 ipqos_msg(MT_ERROR,
7917 gettext("Unexpected parameter line %u.\n"),
7918 lineno);
7919 goto fail;
7922 } while (nm == 0 || md == 0);
7925 * check that if the ipgpc action it is named correctly
7927 if ((strcmp((*action)->module, IPGPC_NAME) == 0) &&
7928 (strcmp((*action)->name, IPGPC_CLASSIFY) != 0)) {
7929 ipqos_msg(MT_ERROR,
7930 gettext("%s action has incorrect name line %u.\n"),
7931 IPGPC_NAME, (*action)->lineno);
7932 goto fail;
7935 /* get list of permanent classes */
7937 res = read_perm_items(0, tfp, (*action)->module,
7938 &(*action)->perm_classes, &(*action)->num_perm_classes);
7939 if (res != IPQOS_CONF_SUCCESS) {
7940 goto fail;
7943 /* get list of permanent filters */
7945 res = read_perm_items(1, tfp, (*action)->module,
7946 &perm_filters, &num_perm_filters);
7947 if (res != IPQOS_CONF_SUCCESS) {
7948 goto fail;
7952 * get types file format version and check its supported.
7954 if ((tf_fmt_ver = read_tfile_ver(tfp, IPQOS_FMT_STR,
7955 (*action)->module)) == -1)
7956 goto fail;
7957 if (IPP_MAJOR_MODULE_VER(tf_fmt_ver) > 1 ||
7958 IPP_MINOR_MODULE_VER(tf_fmt_ver) > 0) {
7959 ipqos_msg(MT_ERROR, gettext("Types file for module %s is "
7960 "incompatible.\n"), (*action)->module);
7961 IPQOSCDBG0(L1, "Unsupported fmt major/minor version\n");
7962 goto fail;
7966 * get module version
7968 if (((*action)->module_version = read_tfile_ver(tfp, IPQOS_MOD_STR,
7969 (*action)->module)) == -1)
7970 goto fail;
7972 /* read filter/class/params blocks until CURL_END */
7974 for (;;) {
7975 /* read token */
7976 res = readtoken(cfp, &st);
7978 if (res == IPQOS_CONF_ERR) {
7979 goto fail;
7980 } else if (res == IPQOS_CONF_EOF) {
7981 ipqos_msg(MT_ERROR, gettext("Unexpected EOF.\n"));
7982 goto fail;
7984 /* read CURL_END - end of action definition */
7986 } else if (res == IPQOS_CONF_CURL_END) {
7987 free(st);
7988 break;
7993 * read in either a filter/class or parameter block.
7996 /* read filter */
7998 if (strcmp(st, IPQOS_CONF_FILTER_STR) == 0) {
7999 free(st);
8001 res = readfilter(cfp, tfp, (*action)->module, &filter,
8002 perm_filters, num_perm_filters);
8003 if (res != IPQOS_CONF_SUCCESS) {
8004 goto fail;
8008 * if we read a host name for either src or dst addr
8009 * resolve the hostnames and create the appropriate
8010 * number of filters.
8013 if (filter->src_nd_name || filter->dst_nd_name) {
8015 res = domultihome(filter, &(*action)->filters,
8016 B_FALSE);
8018 * if a lookup fails and the filters
8019 * marked as retry we add it to a list
8020 * for another attempt later, otherwise
8021 * it is thrown away.
8023 if (res != IPQOS_CONF_SUCCESS) {
8025 /* if not name lookup problem */
8027 if (filter->nlerr == 0) {
8028 free_filter(filter);
8029 goto fail;
8031 /* name lookup problem */
8034 * if intermitent lookup failure
8035 * add to list of filters to
8036 * retry later.
8038 } else if (filter->nlerr ==
8039 IPQOS_LOOKUP_RETRY) {
8040 filter->nlerr = 0;
8041 ADD_TO_LIST(
8042 &(*action)->retry_filters,
8043 filter);
8045 * for non-existing names
8046 * ignore the filter.
8048 } else {
8049 free_filter(filter);
8052 /* creation of new filters successful */
8054 } else {
8055 free_filter(filter);
8058 /* non-node name filter */
8060 } else {
8061 ADD_TO_LIST(&(*action)->filters, filter);
8064 /* read class */
8066 } else if (strcmp(st, IPQOS_CONF_CLASS_STR) == 0) {
8067 free(st);
8068 res = readclass(cfp, (*action)->module, &class,
8069 (*action)->perm_classes,
8070 (*action)->num_perm_classes);
8071 if (res != IPQOS_CONF_SUCCESS) {
8072 goto fail;
8075 ADD_TO_LIST(&(*action)->classes, class);
8077 /* read params */
8079 } else if (strcmp(st, IPQOS_CONF_PARAMS_STR) == 0) {
8080 free(st);
8081 if (readprms) {
8082 ipqos_msg(MT_ERROR,
8083 gettext("Second parameter clause not "
8084 "supported line %u.\n"), lineno);
8085 goto fail;
8087 res = readparams(cfp, tfp, (*action)->module,
8088 (*action)->params);
8089 if (res != IPQOS_CONF_SUCCESS) {
8090 goto fail;
8092 readprms++;
8094 /* something unexpected */
8095 } else {
8096 free(st);
8097 ipqos_msg(MT_ERROR,
8098 gettext("Params/filter/class clause expected "
8099 "line %u.\n"), lineno);
8100 goto fail;
8104 (void) fclose(tfp);
8105 return (IPQOS_CONF_SUCCESS);
8107 fail:
8108 if (tfp)
8109 (void) fclose(tfp);
8110 if (*action) {
8111 free_actions(*action);
8112 *action = NULL;
8114 return (IPQOS_CONF_ERR);
8118 * check that each of the actions in actions is uniquely named. If one isn't
8119 * set *name to point at the name of the duplicate action.
8120 * RETURNS: IPQOS_CONF_ERR if a non-unique action, else IPQOS_CONF_SUCCESS.
8122 static int
8123 actions_unique(ipqos_conf_action_t *actions, char **name)
8126 IPQOSCDBG0(L1, "In actions_unique.\n");
8128 while (actions) {
8129 if (actionexist(actions->name, actions->next)) {
8130 *name = actions->name;
8131 return (IPQOS_CONF_ERR);
8133 actions = actions->next;
8136 return (IPQOS_CONF_SUCCESS);
8140 * checks whether the action parameter is involved in an action cycle.
8141 * RETURNS: 1 if involved in a cycle, 0 otherwise.
8143 static int
8144 in_cycle(
8145 ipqos_conf_action_t *action)
8148 ipqos_conf_act_ref_t *aref;
8149 ipqos_conf_class_t *c;
8151 IPQOSCDBG1(L0, "in_cycle: visiting action %s\n", action->name);
8154 /* have we visited this action before? */
8156 if (action->visited == INCYCLE_VISITED) {
8157 action->visited = 0;
8158 return (1);
8160 action->visited = INCYCLE_VISITED;
8163 * recurse down the child actions of this action through the
8164 * classes next action and parameter actions.
8167 for (aref = action->params->actions; aref != NULL; aref = aref->next) {
8169 /* skip virtual actions - they can't be in a cycle */
8171 if (virtual_action(aref->name)) {
8172 continue;
8175 if (in_cycle(aref->action)) {
8176 action->visited = 0;
8177 return (1);
8181 for (c = action->classes; c != NULL; c = c->next) {
8182 aref = c->alist;
8184 if (virtual_action(aref->name)) {
8185 continue;
8188 if (in_cycle(aref->action)) {
8189 action->visited = 0;
8190 return (1);
8194 IPQOSCDBG0(L0, "in_cycle: return\n");
8195 action->visited = 0;
8196 return (0);
8200 * checks that the configuration in actions is a valid whole, that
8201 * all actions are unique, all filters and classes are unique within
8202 * their action, that classes referenced by filters exist and actions
8203 * referenced by classes and params exist. Also checks that there are no root
8204 * actions but ipgpc and that no actions are involved in cycles. As
8205 * a consequence of checking that the actions exist two way pointers
8206 * are created between the dependee and dependant actions.
8208 * In the case the the userconf flag is zero only this link creation is
8209 * set as we trust the kernel to return a valid configuration.
8211 * RETURNS: IPQOS_CONF_ERR if config isn't valid, else IPQOS_CONF_SUCCESS.
8215 static int
8216 validconf(
8217 ipqos_conf_action_t *actions,
8218 int userconf) /* are we checking a conf file ? */
8220 char *name;
8221 ipqos_conf_action_t *act;
8222 int res;
8223 ipqos_conf_action_t *dact;
8224 ipqos_conf_filter_t *flt;
8225 ipqos_conf_class_t *cls;
8226 ipqos_conf_params_t *params;
8227 ipqos_conf_act_ref_t *aref;
8229 IPQOSCDBG0(L0, "In validconf\n");
8231 /* check actions are unique */
8233 if (userconf && actions_unique(actions, &name) != IPQOS_CONF_SUCCESS) {
8234 ipqos_msg(MT_ERROR, gettext("Duplicate named action %s.\n"),
8235 name);
8236 return (IPQOS_CONF_ERR);
8239 for (act = actions; act; act = act->next) {
8242 * check filters (for user land configs only).
8243 * check they are unique in this action and their class exists.
8245 if (userconf) {
8246 for (flt = act->filters; flt; flt = flt->next) {
8248 /* check unique name */
8250 if (filterexist(flt->name, flt->instance,
8251 flt->next)) {
8252 ipqos_msg(MT_ERROR,
8253 gettext("Duplicate named filter "
8254 "%s in action %s.\n"), flt->name,
8255 act->name);
8256 return (IPQOS_CONF_ERR);
8260 * check existence of class and error if
8261 * class doesn't exist and not a perm class
8264 if (!classexist(flt->class_name,
8265 act->classes)) {
8266 if (!in_string_table(act->perm_classes,
8267 act->num_perm_classes,
8268 flt->class_name)) {
8269 ipqos_msg(MT_ERROR,
8270 gettext("Undefined "
8271 "class in filter %s, "
8272 "action %s.\n"), flt->name,
8273 act->name);
8274 return (IPQOS_CONF_ERR);
8280 /* check classes */
8282 for (cls = act->classes; cls; cls = cls->next) {
8284 /* check if class name unique (userland only) */
8286 if (userconf && classexist(cls->name, cls->next)) {
8287 ipqos_msg(MT_ERROR,
8288 gettext("Duplicate named class %s in "
8289 "action %s.\n"), cls->name, act->name);
8290 return (IPQOS_CONF_ERR);
8294 * virtual actions always exist so don't check for next
8295 * action.
8297 if (virtual_action(cls->alist->name)) {
8298 continue;
8302 * check existance of next action and create link to
8303 * it.
8305 if ((cls->alist->action =
8306 actionexist(cls->alist->name, actions)) == NULL) {
8307 ipqos_msg(MT_ERROR,
8308 gettext("Undefined action in class %s, "
8309 "action %s.\n"), cls->name, act->name);
8310 return (IPQOS_CONF_ERR);
8313 /* create backwards link - used for deletions */
8315 dact = cls->alist->action;
8316 res = add_aref(&dact->dependencies, NULL, act->name);
8317 if (res != IPQOS_CONF_SUCCESS) {
8318 return (IPQOS_CONF_ERR);
8320 dact->dependencies->action = act;
8324 /* check actions exist for action type parameters */
8326 params = act->params;
8327 for (aref = params->actions; aref; aref = aref->next) {
8329 /* skip virtuals */
8331 if (virtual_action(aref->name)) {
8332 continue;
8336 * check existance of action in this ref
8337 * and if present create a ptr to it.
8339 aref->action = actionexist(aref->name, actions);
8340 if (aref->action == NULL) {
8341 ipqos_msg(MT_ERROR,
8342 gettext("Undefined action in parameter "
8343 "%s, action %s.\n"),
8344 SHORT_NAME(aref->field), act->name);
8345 return (IPQOS_CONF_ERR);
8348 /* create backwards link */
8350 dact = aref->action;
8351 res = add_aref(&dact->dependencies, NULL,
8352 act->name);
8353 if (res != IPQOS_CONF_SUCCESS) {
8354 return (IPQOS_CONF_ERR);
8356 dact->dependencies->action = act;
8360 /* for kernel retrieved configs we don't do the following checks. */
8361 if (!userconf) {
8362 return (IPQOS_CONF_SUCCESS);
8365 /* check for cycles in config and orphaned actions other than ipgpc */
8367 for (act = actions; act; act = act->next) {
8369 /* check if involved in cycle */
8371 if (in_cycle(act)) {
8372 ipqos_msg(MT_ERROR,
8373 gettext("Action %s involved in cycle.\n"),
8374 act->name);
8375 return (IPQOS_CONF_ERR);
8378 /* check that this action has a parent (except ipgpc) */
8380 if (act->dependencies == NULL &&
8381 strcmp(act->name, IPGPC_CLASSIFY) != 0) {
8382 ipqos_msg(MT_ERROR, gettext("Action %s isn't "
8383 "referenced by any other actions.\n"), act->name);
8384 return (IPQOS_CONF_ERR);
8388 return (IPQOS_CONF_SUCCESS);
8392 * Read the version from the config file with stream cfp with
8393 * the tag version_tag. The tag-value pair should be the first tokens
8394 * encountered.
8396 * RETURNS: -1 if a missing or invalid version or a read error,
8397 * else an integer encoding of the version.
8399 static int
8400 read_cfile_ver(
8401 FILE *cfp,
8402 char *version_tag)
8404 char *sp = NULL;
8405 int res;
8406 int version;
8408 IPQOSCDBG0(L1, "In read_cfile_ver:\n");
8411 * read version tag string.
8413 res = readtoken(cfp, &sp);
8414 if (res != IPQOS_CONF_SUCCESS) {
8415 goto fail;
8416 } else if (strcasecmp(sp, version_tag) != 0) {
8417 goto fail;
8419 free(sp);
8420 sp = NULL;
8423 * read version number string.
8425 res = readtoken(cfp, &sp);
8426 if (res != IPQOS_CONF_SUCCESS) {
8427 goto fail;
8431 * encode version into int.
8433 if ((version = ver_str_to_int(sp)) == -1) {
8434 goto fail;
8436 free(sp);
8438 return (version);
8439 fail:
8440 ipqos_msg(MT_ERROR,
8441 gettext("Missing/Invalid config file %s.\n"), version_tag);
8442 if (sp != NULL)
8443 free(sp);
8444 return (-1);
8448 * read the set of actions definitions from the stream cfp and store
8449 * them in a list pointed to by conf.
8450 * RETURNS: IPQOS_CONF_ERR if any errors, else IPQOS_CONF_SUCCESS.
8452 static int
8453 readconf(
8454 FILE *cfp,
8455 ipqos_conf_action_t **conf)
8458 int res;
8459 ipqos_conf_action_t *action;
8460 boolean_t ipgpc_action = B_FALSE;
8461 int fmt_ver;
8463 IPQOSCDBG0(L0, "In readconf\n");
8465 *conf = NULL;
8468 * get config file format version.
8470 fmt_ver = read_cfile_ver(cfp, IPQOS_FMT_VERSION_STR);
8471 if (fmt_ver == -1) {
8472 return (IPQOS_CONF_ERR);
8473 } else {
8475 * check version is valid
8477 if ((IPP_MAJOR_MODULE_VER(fmt_ver) > 1) ||
8478 (IPP_MINOR_MODULE_VER(fmt_ver) > 0)) {
8479 ipqos_msg(MT_ERROR, gettext("Unsupported config file "
8480 "format version.\n"));
8481 return (IPQOS_CONF_ERR);
8485 /* loop reading actions adding to conf till EOF */
8487 for (;;) {
8488 action = NULL;
8490 /* readaction */
8492 res = readaction(cfp, &action);
8493 if (res == IPQOS_CONF_ERR) {
8494 goto fail;
8497 /* reached eof, finish */
8499 if (res == IPQOS_CONF_EOF) {
8500 break;
8503 ADD_TO_LIST(conf, action);
8505 /* check if we just read an ipgpc action */
8507 if (strcmp(action->name, IPGPC_CLASSIFY) == 0)
8508 ipgpc_action = B_TRUE;
8511 /* check that there is one or more actions and that one is ipgpc */
8513 if (ipgpc_action == B_FALSE) {
8514 ipqos_msg(MT_ERROR, gettext("No %s action defined.\n"),
8515 IPGPC_NAME);
8516 goto fail;
8519 return (IPQOS_CONF_SUCCESS);
8520 fail:
8521 free_actions(*conf);
8522 *conf = NULL;
8523 return (IPQOS_CONF_ERR);
8526 /* ************************ kernel config retrieval ************************ */
8530 * read the current configuration from the kernel and make *conf a ptr to it.
8531 * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCES.
8533 static int
8534 readkconf(ipqos_conf_action_t **conf)
8537 int res;
8538 char **modnames = NULL;
8539 int nmods;
8540 char **actnames = NULL;
8541 int nacts;
8542 int x, y;
8543 FILE *tfp;
8544 int openerr;
8545 ipqos_actinfo_prm_t ai_prm;
8548 IPQOSCDBG0(L0, "In readkconf\n");
8550 /* initialise conf to NULL */
8551 *conf = NULL;
8553 /* get list of modules currently loaded */
8555 res = ipp_list_mods(&modnames, &nmods);
8556 if (res != 0) {
8557 ipqos_msg(MT_ENOSTR, "ipp_list_mods");
8558 return (IPQOS_CONF_ERR);
8562 * iterate through all loaded modules retrieving their list of actions
8563 * and then retrieving the configuration of each of these
8564 * and attatching it to conf.
8566 for (x = 0; x < nmods; x++) {
8568 /* skip actions of modules that we can't open types file of */
8570 if ((tfp = validmod(modnames[x], &openerr)) == NULL) {
8572 /* mem error */
8574 if (!openerr) {
8575 goto fail;
8578 * fopen fail - if we failed because the file didn't
8579 * exist we assume this is an unknown module and
8580 * ignore this module, otherwise error.
8582 } else {
8583 if (errno == ENOENT) {
8584 continue;
8585 } else {
8586 ipqos_msg(MT_ENOSTR, "fopen");
8587 goto fail;
8591 (void) fclose(tfp);
8593 /* get action list for this module */
8595 res = ipp_mod_list_actions(modnames[x], &actnames, &nacts);
8596 if (res != 0) {
8597 ipqos_msg(MT_ENOSTR, "ipp_mod_list_actions");
8598 goto fail;
8601 /* read config of each action of this module */
8603 for (y = 0; y < nacts; y++) {
8604 ai_prm.action = alloc_action();
8605 if (ai_prm.action == NULL) {
8606 goto fail;
8609 /* copy action name into action struct */
8611 (void) strlcpy(ai_prm.action->name, actnames[y],
8612 IPQOS_CONF_NAME_LEN);
8614 /* copy module name into action struct */
8616 (void) strlcpy(ai_prm.action->module, modnames[x],
8617 IPQOS_CONF_NAME_LEN);
8619 /* get action info */
8621 res = ipp_action_info(actnames[y],
8622 (int (*)(nvlist_t *, void *))parse_kaction,
8623 (void *)&ai_prm, 0);
8624 if (res != 0) {
8625 /* was this an ipp error */
8626 if (ai_prm.intl_ret == IPQOS_CONF_SUCCESS) {
8627 ipqos_msg(MT_ENOSTR,
8628 "ipp_action_info");
8630 goto fail;
8633 ADD_TO_LIST(conf, ai_prm.action);
8636 cleanup_string_table(actnames, nacts);
8639 cleanup_string_table(modnames, nmods);
8640 return (IPQOS_CONF_SUCCESS);
8641 fail:
8642 free_actions(*conf);
8643 *conf = NULL;
8644 cleanup_string_table(modnames, nmods);
8645 cleanup_string_table(actnames, nacts);
8646 return (IPQOS_CONF_ERR);
8650 * This is passed as a parameter to ipp_action_info() in readkaction and
8651 * is called back one for each configuration element within the action
8652 * specified. This results in filters and classes being created and chained
8653 * off of action, and action having its params set.
8654 * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCESS.
8656 static int
8657 parse_kaction(
8658 nvlist_t *nvl,
8659 ipqos_actinfo_prm_t *ai_prm)
8662 int ret;
8663 uint8_t cfgtype;
8664 ipqos_conf_filter_t *filter = NULL;
8665 ipqos_conf_class_t *class = NULL;
8666 ipqos_conf_action_t *action = ai_prm->action;
8669 IPQOSCDBG1(KRET, "In parse_kaction: action_name: %s\n", action->name);
8671 /* get config type */
8673 (void) nvlist_lookup_byte(nvl, IPP_CONFIG_TYPE, &cfgtype);
8674 (void) nvlist_remove_all(nvl, IPP_CONFIG_TYPE);
8676 switch (cfgtype) {
8677 case CLASSIFIER_ADD_FILTER: {
8679 * parse the passed filter nvlist
8680 * and add result to action's filter list.
8682 filter = alloc_filter();
8683 if (filter == NULL) {
8684 ai_prm->intl_ret = IPQOS_CONF_ERR;
8685 return (IPQOS_CONF_ERR);
8688 ret = parse_kfilter(filter, nvl);
8689 if (ret != IPQOS_CONF_SUCCESS) {
8690 free_filter(filter);
8691 ai_prm->intl_ret = IPQOS_CONF_ERR;
8692 return (ret);
8695 ADD_TO_LIST(&action->filters, filter);
8696 break;
8698 case CLASSIFIER_ADD_CLASS:
8699 case CLASSIFIER_MODIFY_CLASS: {
8701 * parse the passed class nvlist
8702 * and add result to action's class list.
8704 class = alloc_class();
8705 if (class == NULL) {
8706 ai_prm->intl_ret = IPQOS_CONF_ERR;
8707 return (IPQOS_CONF_ERR);
8710 ret = parse_kclass(class, nvl);
8711 if (ret != IPQOS_CONF_SUCCESS) {
8712 free_class(class);
8713 ai_prm->intl_ret = IPQOS_CONF_ERR;
8714 return (ret);
8717 ADD_TO_LIST(&action->classes, class);
8718 break;
8720 case IPP_SET: {
8722 * we don't alloc a params struct as it is created
8723 * as part of an action.
8726 /* parse the passed params nvlist */
8728 ret = parse_kparams(action->module, action->params,
8729 nvl);
8730 if (ret != IPQOS_CONF_SUCCESS) {
8731 ai_prm->intl_ret = IPQOS_CONF_ERR;
8732 return (ret);
8737 ai_prm->intl_ret = IPQOS_CONF_SUCCESS;
8738 return (IPQOS_CONF_SUCCESS);
8742 * parses a params nvlist returned from the kernel.
8743 * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCES.
8746 parse_kparams(
8747 char *module,
8748 ipqos_conf_params_t *params,
8749 nvlist_t *nvl) {
8751 int ret;
8752 ipqos_nvtype_t type;
8753 str_val_nd_t *tmp;
8754 char *act;
8755 uint32_t u32;
8756 nvpair_t *nvp;
8757 FILE *tfp;
8758 char dfltst[IPQOS_VALST_MAXLEN];
8759 char *param;
8760 nvlist_t *nvlcp;
8761 int openerr;
8762 place_t place;
8764 IPQOSCDBG0(KRET, "In parse_kparams:\n");
8766 /* get stream to module types file */
8768 tfp = validmod(module, &openerr);
8769 if (tfp == NULL) {
8770 if (openerr) {
8771 ipqos_msg(MT_ENOSTR, "fopen");
8773 return (IPQOS_CONF_ERR);
8776 /* make copy of passed in nvlist as it is freed by the caller */
8778 ret = nvlist_dup(nvl, &nvlcp, 0);
8779 if (ret != 0) {
8780 return (IPQOS_CONF_ERR);
8784 * get config originator and remove from nvlist. If no owner we
8785 * assume ownership.
8787 ret = nvlist_lookup_uint32(nvlcp, IPP_CONFIG_ORIGINATOR, &u32);
8788 if (ret == 0) {
8789 params->originator = u32;
8790 (void) nvlist_remove_all(nvlcp, IPP_CONFIG_ORIGINATOR);
8791 } else {
8792 params->originator = IPP_CONFIG_IPQOSCONF;
8795 /* get action stats and remove from nvlist */
8797 ret = nvlist_lookup_uint32(nvlcp, IPP_ACTION_STATS_ENABLE, &u32);
8798 if (ret == 0) {
8799 params->stats_enable = *(boolean_t *)&u32;
8800 (void) nvlist_remove_all(nvlcp, IPP_ACTION_STATS_ENABLE);
8804 * loop throught nvlist elements and for those that are actions create
8805 * action ref entrys for them.
8807 nvp = nvlist_next_nvpair(nvlcp, NULL);
8808 while (nvp != NULL) {
8809 param = SHORT_NAME(nvpair_name(nvp));
8810 place = PL_ANY;
8811 ret = readtype(tfp, module, param, &type, &tmp, dfltst,
8812 B_FALSE, &place);
8813 if (ret != IPQOS_CONF_SUCCESS) {
8814 goto fail;
8817 if ((place == PL_PARAMS) && /* avoid map entries */
8818 (type == IPQOS_DATA_TYPE_ACTION)) {
8819 (void) nvpair_value_string(nvp, &act);
8820 ret = add_aref(&params->actions, nvpair_name(nvp), act);
8821 if (ret != IPQOS_CONF_SUCCESS) {
8822 goto fail;
8826 nvp = nvlist_next_nvpair(nvlcp, nvp);
8829 /* assign copied nvlist to params struct */
8831 params->nvlist = nvlcp;
8833 (void) fclose(tfp);
8834 return (IPQOS_CONF_SUCCESS);
8835 fail:
8836 (void) fclose(tfp);
8837 free_arefs(params->actions);
8838 params->actions = NULL;
8839 nvlist_free(nvlcp);
8840 return (IPQOS_CONF_ERR);
8844 * parses a classes nvlist returned from the kernel.
8845 * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCES.
8847 static int
8848 parse_kclass(
8849 ipqos_conf_class_t *class,
8850 nvlist_t *nvl)
8853 int ret;
8854 uint32_t u32;
8855 char *str;
8857 IPQOSCDBG0(KRET, "In parse_kclass:\n");
8859 /* lookup object originator */
8861 ret = nvlist_lookup_uint32(nvl, IPP_CONFIG_ORIGINATOR, &u32);
8862 if (ret == 0) {
8863 class->originator = u32;
8864 } else {
8865 class->originator = IPP_CONFIG_IPQOSCONF;
8868 /* lookup name */
8870 (void) nvlist_lookup_string(nvl, CLASSIFIER_CLASS_NAME, &str);
8871 (void) strlcpy(class->name, str, IPQOS_CONF_NAME_LEN);
8872 IPQOSCDBG1(KRET, "reading class %s\n", class->name);
8874 /* lookup next action */
8876 (void) nvlist_lookup_string(nvl, CLASSIFIER_NEXT_ACTION, &str);
8877 ret = add_aref(&class->alist, NULL, str);
8878 if (ret != IPQOS_CONF_SUCCESS) {
8879 return (IPQOS_CONF_ERR);
8882 /* lookup stats enable */
8884 ret = nvlist_lookup_uint32(nvl, CLASSIFIER_CLASS_STATS_ENABLE, &u32);
8885 if (ret == 0) {
8886 class->stats_enable = *(boolean_t *)&u32;
8889 return (IPQOS_CONF_SUCCESS);
8893 * parses a filters nvlist returned from the kernel.
8894 * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCES.
8896 static int
8897 parse_kfilter(
8898 ipqos_conf_filter_t *filter,
8899 nvlist_t *nvl)
8902 int ret;
8903 char *str;
8904 uint32_t u32;
8905 nvlist_t *nvlcp;
8906 char *end;
8908 IPQOSCDBG0(KRET, "In parse_kfilter:\n");
8910 /* make copy of passed in nvlist as it is freed by the caller */
8912 ret = nvlist_dup(nvl, &nvlcp, 0);
8913 if (ret != 0) {
8914 return (IPQOS_CONF_ERR);
8917 /* lookup originator */
8919 ret = nvlist_lookup_uint32(nvlcp, IPP_CONFIG_ORIGINATOR, &u32);
8920 if (ret == 0) {
8921 filter->originator = u32;
8922 (void) nvlist_remove_all(nvlcp, IPP_CONFIG_ORIGINATOR);
8923 } else {
8924 filter->originator = IPP_CONFIG_IPQOSCONF;
8927 /* lookup filter name */
8929 (void) nvlist_lookup_string(nvlcp, CLASSIFIER_FILTER_NAME, &str);
8930 (void) strlcpy(filter->name, str, IPQOS_CONF_NAME_LEN);
8931 (void) nvlist_remove_all(nvlcp, CLASSIFIER_FILTER_NAME);
8933 /* lookup class name */
8935 (void) nvlist_lookup_string(nvlcp, CLASSIFIER_CLASS_NAME, &str);
8936 (void) strlcpy(filter->class_name, str, IPQOS_CONF_NAME_LEN);
8937 (void) nvlist_remove_all(nvlcp, CLASSIFIER_CLASS_NAME);
8939 /* lookup src and dst host names if present */
8941 if (nvlist_lookup_string(nvlcp, IPGPC_SADDR_HOSTNAME, &str) == 0) {
8942 filter->src_nd_name = malloc(strlen(str) + 1);
8943 if (filter->src_nd_name) {
8944 (void) strcpy(filter->src_nd_name, str);
8945 (void) nvlist_remove_all(nvlcp, IPGPC_SADDR_HOSTNAME);
8946 } else {
8947 ipqos_msg(MT_ENOSTR, "malloc");
8948 nvlist_free(nvlcp);
8949 return (IPQOS_CONF_ERR);
8952 if (nvlist_lookup_string(nvlcp, IPGPC_DADDR_HOSTNAME, &str) == 0) {
8953 filter->dst_nd_name = malloc(strlen(str) + 1);
8954 if (filter->dst_nd_name) {
8955 (void) strcpy(filter->dst_nd_name, str);
8956 (void) nvlist_remove_all(nvlcp, IPGPC_DADDR_HOSTNAME);
8957 } else {
8958 ipqos_msg(MT_ENOSTR, "malloc");
8959 nvlist_free(nvlcp);
8960 return (IPQOS_CONF_ERR);
8964 /* lookup ip_version if present */
8966 if (nvlist_lookup_string(nvlcp, IPGPC_FILTER_PRIVATE, &str) == 0) {
8967 filter->ip_versions = (uint32_t)strtol(str, &end, 0);
8968 if (end != str) {
8969 (void) nvlist_remove_all(nvlcp, IPGPC_FILTER_PRIVATE);
8970 } else {
8971 ipqos_msg(MT_ERROR,
8972 gettext("Corrupted ip_version returned from "
8973 "kernel.\n"));
8974 nvlist_free(nvlcp);
8975 return (IPQOS_CONF_ERR);
8979 /* lookup filter instance if present */
8981 ret = nvlist_lookup_int32(nvlcp, IPGPC_FILTER_INSTANCE,
8982 &filter->instance);
8983 if (ret != 0) {
8984 filter->instance = -1;
8985 } else {
8986 (void) nvlist_remove_all(nvlcp, IPGPC_FILTER_INSTANCE);
8989 /* attach new trimmed nvlist to filter */
8990 filter->nvlist = nvlcp;
8992 return (IPQOS_CONF_SUCCESS);
8997 * determines whether action_name is a virtual action name.
8998 * RETURNS: if virtual action 1, else 0.
9000 static int
9001 virtual_action(char *action_name)
9004 if (strcmp(action_name, IPP_ANAME_CONT) == 0 ||
9005 strcmp(action_name, IPP_ANAME_DEFER) == 0 ||
9006 strcmp(action_name, IPP_ANAME_DROP) == 0) {
9007 return (1);
9010 return (0);
9014 * remove all the actions within the kernel. If there is a failure
9015 * modified is set to represent whether the attempt to flush modified
9016 * the configuration in any way.
9017 * RETURNS: IPQOS_CONF_ERR if the ipp_* functions return any errors,
9018 * else IPQOS_CONF_SUCCESS.
9020 static int
9021 flush(
9022 boolean_t *modified)
9025 int res;
9026 char **modnames = NULL;
9027 int nmods;
9028 char **actnames = NULL;
9029 int nacts;
9030 int x, y;
9032 IPQOSCDBG0(L0, "In flush\n");
9034 *modified = B_FALSE;
9037 * get list of modules currently loaded.
9039 res = ipp_list_mods(&modnames, &nmods);
9040 if (res != 0) {
9041 ipqos_msg(MT_ENOSTR, "ipp_list_mods");
9042 return (IPQOS_CONF_ERR);
9046 * iterate through all the modules listing their actions and
9047 * deleting all of them.
9049 for (x = 0; x < nmods; x++) {
9050 IPQOSCDBG1(APPLY, "Getting actions of module %s.\n",
9051 modnames[x]);
9052 res = ipp_mod_list_actions(modnames[x], &actnames, &nacts);
9053 if (res != 0) {
9054 ipqos_msg(MT_ENOSTR, "ipp_mod_list_actions");
9055 cleanup_string_table(modnames, nmods);
9056 return (IPQOS_CONF_ERR);
9059 for (y = 0; y < nacts; y++) {
9060 IPQOSCDBG1(APPLY, "deleting action %s\n", actnames[y]);
9061 res = ipp_action_destroy(actnames[y], IPP_DESTROY_REF);
9063 * if fails for reason other than action doesn't
9064 * exist or action has dependency.
9066 if (res != 0 && errno != ENOENT && errno != EBUSY) {
9067 ipqos_msg(MT_ENOSTR, "ipp_action_destroy");
9068 cleanup_string_table(modnames, nmods);
9069 cleanup_string_table(actnames, nacts);
9070 return (IPQOS_CONF_ERR);
9073 if (res == 0)
9074 *modified = B_TRUE;
9076 cleanup_string_table(actnames, nacts);
9078 cleanup_string_table(modnames, nmods);
9080 return (IPQOS_CONF_SUCCESS);
9084 * Trys to flush the configuration. If it fails and nothing has been modified
9085 * and force_flush is false just return an error, otherwise persist trying to
9086 * completion.
9087 * RETURNS: IPQOS_CONF_ERR if flush attempt failed without modifying anything
9088 * and force_flush was set to false, otherwise IPQOS_CONF_SUCCESS.
9090 static int
9091 atomic_flush(
9092 boolean_t force_flush)
9094 int x = 0;
9095 int res;
9096 boolean_t modified = B_FALSE;
9099 * attempt first flush of config.
9101 res = flush(&modified);
9102 if ((force_flush == B_FALSE) && (res != IPQOS_CONF_SUCCESS) &&
9103 (modified == B_FALSE)) {
9104 return (IPQOS_CONF_ERR);
9105 } else if (res == IPQOS_CONF_SUCCESS) {
9106 return (IPQOS_CONF_SUCCESS);
9110 * failed flush that modified config, or force flush set; loop till
9111 * successful flush.
9113 while (res != IPQOS_CONF_SUCCESS) {
9114 if (x == 5) { /* 10 secs since start/last message. */
9115 ipqos_msg(MT_ERROR,
9116 gettext("Retrying configuration flush.\n"));
9117 x = 0;
9119 (void) sleep(2);
9120 x++;
9121 res = flush(&modified);
9124 return (IPQOS_CONF_SUCCESS);
9128 * Performs a flush of the configuration within a signal blocking region
9129 * so that there's minimal chance of it being killed and the flush only
9130 * partially completing.
9131 * RETURNS: IPQOS_CONF_SUCCESS (for symmetry with the other main functions).
9133 static int
9134 flushconf()
9136 int res;
9139 * make sure that flush is as atomic as possible.
9141 if ((res = block_all_signals()) == -1)
9142 return (IPQOS_CONF_ERR);
9144 res = atomic_flush(B_FALSE);
9147 * restore signals.
9149 (void) restore_all_signals();
9151 if (res == IPQOS_CONF_SUCCESS) {
9152 ipqos_msg(MT_LOG, gettext("Configuration flushed.\n"));
9153 } else {
9154 ipqos_msg(MT_ENOSTR, "atomic_flush");
9157 return (res);
9160 static int
9161 in_string_table(char *stable[], int size, char *string)
9164 IPQOSCDBG1(L1, "In in_string_table: search string %s\n", string);
9166 for (--size; size >= 0; size--) {
9167 if (strcmp(stable[size], string) == 0) {
9168 IPQOSCDBG1(L1, "Found %s in string table\n", string);
9169 return (1);
9173 return (0);
9176 /* free the memory occupied by the string table ctable and its contents. */
9177 static void
9178 cleanup_string_table(char *ctable[], int size)
9181 int x;
9183 if (ctable) {
9184 for (x = 0; x < size; x++) {
9185 free(ctable[x]);
9187 free(ctable);
9191 #if 0
9194 * makes a copy of a string table and returns a ptr to it.
9195 * RETURNS: NULL on error or if size was 0, else ptr to copied table.
9197 static char **
9198 copy_string_table(char *stable1[], int size)
9201 char **st = NULL;
9202 int pos;
9204 /* create char ptr array */
9206 st = malloc(size * sizeof (char *));
9207 if (st == NULL) {
9208 ipqos_msg(MT_ENOSTR, "malloc");
9209 return (st);
9212 /* create copy of each string from stable1 in array */
9214 for (pos = size - 1; pos >= 0; pos--) {
9215 st[pos] = malloc(strlen(stable1[pos] + 1));
9216 if (st[pos] == NULL) {
9217 for (pos++; pos < size; pos++)
9218 free(st[pos]);
9219 free(st);
9220 ipqos_msg(MT_ENOSTR, "malloc");
9221 return (NULL);
9224 (void) strcpy(st[pos], stable1[pos]);
9227 return (st);
9229 #endif /* 0 */
9232 * retry lookups on filters that soft failed a previous lookup and
9233 * were put on the retry list.
9234 * RETURNS: IPQOS_CONF_ERR on any errors, else IPQOS_CONF_SUCCESS.
9236 static int
9237 retry_name_lookups(
9238 ipqos_conf_action_t *actions)
9241 ipqos_conf_action_t *act;
9242 ipqos_conf_filter_t **new_filters;
9243 ipqos_conf_filter_t *flt;
9245 IPQOSCDBG0(APPLY, "In retry_name_lookups:\n");
9247 for (act = actions; act != NULL; act = act->next) {
9249 /* store start of new resolved filters */
9250 LIST_END(&act->filters, &new_filters);
9253 * do name resolution on retry list adding resolved filters
9254 * to end of actions filters.
9256 for (flt = act->retry_filters; flt != NULL; flt = flt->next) {
9258 if (domultihome(flt, new_filters, B_TRUE) !=
9259 IPQOS_CONF_SUCCESS) {
9261 /* if resource failure */
9263 if (flt->nlerr == 0) {
9264 return (IPQOS_CONF_ERR);
9269 /* add the newly resolved filters to the kernel action */
9271 for (flt = *new_filters; flt != NULL; flt = flt->next) {
9272 if (add_filter(act->name, flt, act->module_version) !=
9273 IPQOS_CONF_SUCCESS) {
9274 return (IPQOS_CONF_ERR);
9279 return (IPQOS_CONF_SUCCESS);
9283 * write the configuration in conf to the file given in dstpath. This
9284 * is done by writing first to a temporary file and then renaming that
9285 * file to dstpath. This assures an atomic write.
9286 * RETURNS: IPQOS_CONF_ERR on any errors, else IPQOS_CONF_SUCCESS.
9288 static int
9289 writeconf(
9290 ipqos_conf_action_t *conf,
9291 char *dstpath)
9294 FILE *tmpfp;
9295 char *tmppath;
9296 char *pathend;
9297 ipqos_conf_action_t *act;
9298 int res;
9300 IPQOSCDBG0(L0, "in writeconf\n");
9302 /* construct tmp file path so we can use rename() */
9304 pathend = strrchr(dstpath, '/');
9306 /* dstpath in current dir */
9308 if (pathend == NULL) {
9309 tmppath = malloc(strlen("ipqosconf.tmp") + 1);
9310 if (tmppath == NULL) {
9311 ipqos_msg(MT_ENOSTR, "malloc");
9312 return (IPQOS_CONF_ERR);
9314 (void) strcpy(tmppath, "ipqosconf.tmp");
9316 /* dstpath in root dir */
9318 } else if (pathend == dstpath) {
9319 tmppath = malloc(strlen("/ipqosconf.tmp") + 1);
9320 if (tmppath == NULL) {
9321 ipqos_msg(MT_ENOSTR, "malloc");
9322 return (IPQOS_CONF_ERR);
9324 (void) strcpy(tmppath, "/ipqosconf.tmp");
9326 /* not pwd or root */
9328 } else {
9329 *pathend = NULL;
9330 tmppath = malloc(strlen(dstpath) + strlen("/ipqosconf.tmp") +
9332 if (tmppath == NULL) {
9333 ipqos_msg(MT_ENOSTR, "malloc");
9334 return (IPQOS_CONF_ERR);
9336 (void) strcpy(tmppath, dstpath);
9337 (void) strcat(tmppath, "/ipqosconf.tmp");
9338 *pathend = '/';
9342 /* open tmp file */
9344 tmpfp = fopen(tmppath, "w");
9345 if (tmpfp == NULL) {
9346 ipqos_msg(MT_ENOSTR, "fopen");
9347 free(tmppath);
9348 return (IPQOS_CONF_ERR);
9351 /* write out format version */
9353 (void) fprintf(tmpfp, "%s %d.%d\n\n", IPQOS_FMT_VERSION_STR,
9354 IPQOS_CUR_FMT_MAJOR_VER, IPQOS_CUR_FMT_MINOR_VER);
9357 * loop through actions in list writing ipqosconf originated
9358 * ones out to the tmp file.
9360 for (act = conf; act != NULL; act = act->next) {
9361 if (act->params->originator == IPP_CONFIG_IPQOSCONF) {
9362 res = printaction(tmpfp, act, 0, 0);
9363 if (res != IPQOS_CONF_SUCCESS) {
9364 free(tmppath);
9365 (void) fclose(tmpfp);
9366 return (res);
9370 (void) fclose(tmpfp);
9372 /* rename tmp file to dst file */
9374 if (rename(tmppath, dstpath) != 0) {
9375 ipqos_msg(MT_ENOSTR, "rename");
9376 free(tmppath);
9377 return (IPQOS_CONF_ERR);
9379 free(tmppath);
9381 return (IPQOS_CONF_SUCCESS);
9385 * read the configuration back from the kernel and then write each of the
9386 * actions read to IPQOS_CONF_INIT_PATH.
9387 * RETURNS: IPQOS_CONF_ERR if error, else IPQOS_CONF_SUCCESS.
9389 static int
9390 commitconf()
9393 int ret;
9394 ipqos_conf_action_t *conf;
9396 IPQOSCDBG0(L0, "In commitconf\n");
9398 /* read the configuration from the kernel */
9400 ret = readkconf(&conf);
9401 if (ret != IPQOS_CONF_SUCCESS) {
9402 return (IPQOS_CONF_ERR);
9405 /* dissallow a null config to be stored (we can't read one in) */
9407 if (conf == NULL) {
9408 ipqos_msg(MT_ERROR,
9409 gettext("Can't commit a null configuration.\n"));
9410 return (IPQOS_CONF_ERR);
9413 /* make sure if we create file that perms are 644 */
9415 (void) umask(S_IXUSR | S_IWGRP | S_IXGRP | S_IWOTH | S_IXOTH);
9417 /* write the configuration to the init file */
9419 ret = writeconf(conf, IPQOS_CONF_INIT_PATH);
9420 if (ret != IPQOS_CONF_SUCCESS) {
9421 return (IPQOS_CONF_ERR);
9424 ipqos_msg(MT_LOG,
9425 gettext("Current configuration saved to init file.\n"));
9427 return (IPQOS_CONF_SUCCESS);
9431 * Called in the event of a failed rollback. It first flushes the
9432 * current configuration, then attempts to apply the oconf (the old
9433 * one), and if that fails flushes again.
9435 * RETURNS: IPQOS_CONF_ERR if the application of old config fails,
9436 * else IPQOS_CONF_SUCCESS.
9438 static int
9439 rollback_recover(
9440 ipqos_conf_action_t *oconf)
9442 int res;
9444 IPQOSCDBG0(RBK, "In rollback_recover\n");
9447 * flush configuration.
9449 (void) atomic_flush(B_TRUE);
9452 * mark all elements of old config for application.
9454 mark_config_new(oconf);
9457 * attempt to apply old config.
9459 res = applydiff(oconf, NULL);
9461 * if failed force flush of config.
9463 if (res != IPQOS_CONF_SUCCESS) {
9464 (void) atomic_flush(B_TRUE);
9465 return (IPQOS_CONF_ERR);
9468 return (IPQOS_CONF_SUCCESS);
9472 * read and apply the configuration contained if file ifile to the kernel.
9473 * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCES.
9475 static int
9476 applyconf(char *ifile)
9479 FILE *ifp;
9480 ipqos_conf_action_t *conf = NULL;
9481 ipqos_conf_action_t *oconf = NULL;
9482 ipqos_conf_action_t *act, *oact;
9483 int res;
9485 IPQOSCDBG0(L0, "In applyconf:\n");
9488 /* if filename '-' read from stdin */
9490 if (strcmp(ifile, "-") == 0) {
9491 ifp = stdin;
9492 } else {
9493 ifp = fopen(ifile, "r");
9494 if (ifp == NULL) {
9495 ipqos_msg(MT_ERROR,
9496 gettext("Opening file %s for read: %s.\n"),
9497 ifile, strerror(errno));
9498 return (IPQOS_CONF_ERR);
9502 /* read in new configuration */
9504 res = readconf(ifp, &conf);
9505 if (res != IPQOS_CONF_SUCCESS) {
9506 goto fail;
9509 /* check configuration is valid */
9511 res = validconf(conf, 1);
9512 if (res != IPQOS_CONF_SUCCESS) {
9513 goto fail;
9516 /* read in kernel configuration */
9518 res = readkconf(&oconf);
9519 if (res != IPQOS_CONF_SUCCESS) {
9520 goto fail;
9524 * check there are no same named actions in both config file and the
9525 * the kernel that are for a different module. The application
9526 * system can't handle these as we would try to add the new
9527 * action before we deleted the old one and because actions
9528 * in the kernel are indexed solely on their name (their module
9529 * isn't included) the kernel would return an error. We want
9530 * to avoid this error and the resulting rollback.
9532 for (act = conf; act != NULL; act = act->next) {
9533 for (oact = oconf; oact != NULL; oact = oact->next) {
9534 /* found action */
9535 if (strcmp(act->name, oact->name) == 0) {
9536 /* different module */
9537 if (strcmp(act->module, oact->module) != 0) {
9538 ipqos_msg(MT_ERROR,
9539 gettext("Action at line %u has "
9540 "same name as currently "
9541 "installed action, but is for a "
9542 "different module.\n"),
9543 act->lineno);
9544 goto fail;
9545 /* same module - stop search */
9546 } else {
9547 break;
9554 /* create links between actions for use with deletions etc.. */
9556 res = validconf(oconf, 0);
9557 if (res != IPQOS_CONF_SUCCESS) {
9558 goto fail;
9561 /* diff conf file against kernel */
9563 res = diffconf(oconf, conf);
9564 if (res != IPQOS_CONF_SUCCESS) {
9565 goto fail;
9568 /* make kernel mods as atomic as possible */
9570 if ((res = block_all_signals()) == -1) {
9571 res = IPQOS_CONF_ERR;
9572 goto fail;
9575 /* apply difference to kernel */
9577 res = applydiff(conf, oconf);
9578 #ifdef _IPQOS_CONF_DEBUG
9579 if (force_rback || res != IPQOS_CONF_SUCCESS) {
9580 #else
9581 if (res != IPQOS_CONF_SUCCESS) {
9582 #endif /* _IPQOS_CONF_DEBUG */
9584 res = rollback(conf, oconf);
9585 if (res != IPQOS_CONF_SUCCESS) {
9586 res = rollback_recover(oconf);
9587 if (res != IPQOS_CONF_SUCCESS) {
9588 /* system left flushed */
9589 ipqos_msg(MT_ERROR,
9590 gettext("Failed to rollback from failed "
9591 "configuration, configuration flushed.\n"));
9592 res = IPQOS_CONF_RECOVER_ERR;
9593 } else { /* old config re-applied */
9594 ipqos_msg(MT_ERROR,
9595 gettext("Configuration failed, system "
9596 "state unchanged.\n"));
9597 res = IPQOS_CONF_ERR;
9599 } else {
9600 ipqos_msg(MT_ERROR,
9601 gettext("Configuration failed, system "
9602 "state unchanged.\n"));
9603 res = IPQOS_CONF_ERR;
9605 goto fail;
9608 /* retry any soft name lookup failures */
9610 res = retry_name_lookups(conf);
9611 if (res != IPQOS_CONF_SUCCESS) {
9612 res = rollback(conf, oconf);
9613 if (res != IPQOS_CONF_SUCCESS) {
9614 res = rollback_recover(oconf);
9615 if (res != IPQOS_CONF_SUCCESS) {
9616 /* system left flushed */
9617 ipqos_msg(MT_ERROR,
9618 gettext("Failed to rollback from failed "
9619 "configuration, configuration flushed.\n"));
9620 res = IPQOS_CONF_RECOVER_ERR;
9621 } else { /* old config re-applied */
9622 ipqos_msg(MT_ERROR,
9623 gettext("Configuration failed, system "
9624 "state unchanged.\n"));
9625 res = IPQOS_CONF_ERR;
9627 } else {
9628 ipqos_msg(MT_ERROR,
9629 gettext("Configuration failed, system "
9630 "state unchanged.\n"));
9631 res = IPQOS_CONF_ERR;
9633 goto fail;
9637 ipqos_msg(MT_LOG, gettext("IPQoS configuration applied.\n"));
9639 /* re-enable signals */
9640 (void) restore_all_signals();
9642 (void) fclose(ifp);
9643 free_actions(conf);
9644 free_actions(oconf);
9645 return (IPQOS_CONF_SUCCESS);
9646 fail:
9647 (void) fclose(ifp);
9648 (void) restore_all_signals();
9649 if (conf)
9650 free_actions(conf);
9651 if (oconf)
9652 free_actions(oconf);
9653 if (res == IPQOS_CONF_RECOVER_ERR)
9654 ipqos_msg(MT_LOG, gettext("Configuration flushed.\n"));
9655 return (res);
9658 static sigset_t set, oset;
9660 static int
9661 block_all_signals()
9663 if (sigfillset(&set) == -1) {
9664 ipqos_msg(MT_ENOSTR, "sigfillset");
9665 return (-1);
9667 if (sigprocmask(SIG_SETMASK, &set, &oset) == -1) {
9668 ipqos_msg(MT_ENOSTR, "sigprocmask");
9669 return (-1);
9671 return (0);
9674 static int
9675 restore_all_signals()
9677 if (sigprocmask(SIG_SETMASK, &oset, NULL) == -1) {
9678 ipqos_msg(MT_ENOSTR, "sigprocmask");
9679 return (-1);
9681 return (0);
9684 static int
9685 unlock(int fd)
9687 if (lockf(fd, F_ULOCK, 0) == -1) {
9688 ipqos_msg(MT_ENOSTR, "lockf");
9689 return (-1);
9691 return (0);
9694 static int
9695 lock()
9697 int fd;
9698 struct stat sbuf1;
9699 struct stat sbuf2;
9702 * Open the file with O_CREAT|O_EXCL. If it exists already, it
9703 * will fail. If it already exists, check whether it looks like
9704 * the one we created.
9706 (void) umask(0077);
9707 if ((fd = open(IPQOS_CONF_LOCK_FILE, O_EXCL|O_CREAT|O_RDWR,
9708 S_IRUSR|S_IWUSR)) == -1) {
9709 if (errno != EEXIST) {
9710 /* Some other problem. */
9711 ipqos_msg(MT_ENOSTR,
9712 gettext("Cannot open lock file %s"),
9713 IPQOS_CONF_LOCK_FILE);
9714 return (-1);
9718 * open() returned an EEXIST error. We don't fail yet
9719 * as it could be a residual from a previous
9720 * execution. However, we need to clear errno here.
9721 * If we don't and print_cmd_buf() is later invoked
9722 * as the result of a parsing error, it
9723 * will assume that the current error is EEXIST and
9724 * that a corresponding error message has already been
9725 * printed, which results in an incomplete error
9726 * message. If errno is zero, print_cmd_buf() will
9727 * assume that it is called as a result of a
9728 * parsing error and will print the appropriate
9729 * error message.
9731 errno = 0;
9734 * File exists. make sure it is OK. We need to lstat()
9735 * as fstat() stats the file pointed to by the symbolic
9736 * link.
9738 if (lstat(IPQOS_CONF_LOCK_FILE, &sbuf1) == -1) {
9739 ipqos_msg(MT_ENOSTR,
9740 gettext("Cannot lstat lock file %s\n"),
9741 IPQOS_CONF_LOCK_FILE);
9742 return (-1);
9745 * Check whether it is a regular file and not a symbolic
9746 * link. Its link count should be 1. The owner should be
9747 * root and the file should be empty.
9749 if (!S_ISREG(sbuf1.st_mode) ||
9750 sbuf1.st_nlink != 1 ||
9751 sbuf1.st_uid != 0 ||
9752 sbuf1.st_size != 0) {
9753 ipqos_msg(MT_ERROR, gettext("Bad lock file %s.\n"),
9754 IPQOS_CONF_LOCK_FILE);
9755 return (-1);
9757 if ((fd = open(IPQOS_CONF_LOCK_FILE, O_CREAT|O_RDWR,
9758 S_IRUSR|S_IWUSR)) == -1) {
9759 ipqos_msg(MT_ENOSTR,
9760 gettext("Cannot open lock file %s"),
9761 IPQOS_CONF_LOCK_FILE);
9762 return (-1);
9765 /* Check whether we opened the file that we lstat()ed. */
9766 if (fstat(fd, &sbuf2) == -1) {
9767 ipqos_msg(MT_ENOSTR,
9768 gettext("Cannot fstat lock file %s\n"),
9769 IPQOS_CONF_LOCK_FILE);
9770 return (-1);
9772 if (sbuf1.st_dev != sbuf2.st_dev ||
9773 sbuf1.st_ino != sbuf2.st_ino) {
9774 /* File changed after we did the lstat() above */
9775 ipqos_msg(MT_ERROR, gettext("Bad lock file %s.\n"),
9776 IPQOS_CONF_LOCK_FILE);
9777 return (-1);
9780 if (lockf(fd, F_LOCK, 0) == -1) {
9781 ipqos_msg(MT_ENOSTR, "lockf");
9782 return (-1);
9784 return (fd);
9788 * print the current kernel configuration out to stdout. If viewall
9789 * is set this causes more verbose configuration listing including
9790 * showing objects we didn't create, each instance of a mhome filter,
9791 * etc.. see printaction().
9792 * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCES.
9795 static int
9796 viewconf(int viewall)
9799 ipqos_conf_action_t *conf = NULL;
9800 ipqos_conf_action_t *act;
9801 int ret;
9803 IPQOSCDBG0(L0, "In viewconf\n");
9805 /* get kernel configuration */
9807 ret = readkconf(&conf);
9808 if (ret != IPQOS_CONF_SUCCESS) {
9809 return (IPQOS_CONF_ERR);
9812 /* write out format version */
9814 if (conf != NULL) {
9815 (void) fprintf(stdout, "%s %d.%d\n\n", IPQOS_FMT_VERSION_STR,
9816 IPQOS_CUR_FMT_MAJOR_VER, IPQOS_CUR_FMT_MINOR_VER);
9819 /* print each of the actions in the kernel config to stdout */
9821 for (act = conf; act != NULL; act = act->next) {
9822 ret = printaction(stdout, act, viewall, 0);
9823 if (ret != IPQOS_CONF_SUCCESS) {
9824 free_actions(conf);
9825 return (ret);
9827 (void) fprintf(stdout, "\n");
9830 free_actions(conf);
9832 return (IPQOS_CONF_SUCCESS);
9837 * debug function that reads the config file and prints it out after
9838 * interpreting to stdout.
9840 #ifdef _IPQOS_CONF_DEBUG
9841 static int
9842 viewcfile(char *cfile)
9845 ipqos_conf_action_t *conf;
9846 ipqos_conf_action_t *act;
9847 int res;
9848 FILE *ifp;
9849 int viewall = 1;
9851 IPQOSCDBG0(L0, "In viewcfile\n");
9852 ifp = fopen(cfile, "r");
9853 if (ifp == NULL) {
9854 ipqos_msg(MT_ERROR, gettext("Opening file %s for read: %s.\n"),
9855 cfile, strerror(errno));
9856 return (IPQOS_CONF_ERR);
9859 res = readconf(ifp, &conf);
9860 if (res != IPQOS_CONF_SUCCESS) {
9861 free(ifp);
9862 return (IPQOS_CONF_ERR);
9865 /* print each of the actions in the kernel config to stdout */
9866 for (act = conf; act != NULL; act = act->next) {
9867 res = printaction(stdout, act, viewall, 0);
9868 if (res != IPQOS_CONF_SUCCESS) {
9869 free(ifp);
9870 return (res);
9873 (void) fprintf(stdout, "\n");
9876 (void) fprintf(stdout, "\n");
9879 return (IPQOS_CONF_SUCCESS);
9881 #endif /* _IPQOS_CONF_DEBUG */
9883 static void
9884 usage(void)
9886 (void) fprintf(stderr, gettext("usage:\n"
9887 "\tipqosconf [-sv] -a file|-\n"
9888 "\tipqosconf -c\n"
9889 "\tipqosconf -l\n"
9890 "\tipqosconf -L\n"
9891 "\tipqosconf -f\n"));
9895 main(int argc, char *argv[])
9898 int c;
9899 char *ifile = NULL;
9900 int args;
9901 int ret;
9902 int cmd;
9903 int viewall = 0;
9904 int lfp;
9906 /* init global flags */
9907 use_syslog = verbose = 0;
9909 /* init current line number */
9910 lineno = 0;
9912 /* setup internationalisation */
9914 (void) setlocale(LC_ALL, "");
9915 #if !defined(TEXT_DOMAIN)
9916 #define TEXT_DOMAIN "SYS_TEST"
9917 #endif
9918 (void) textdomain(TEXT_DOMAIN);
9920 /* setup syslog parameters */
9921 openlog("ipqosconf", 0, LOG_USER);
9923 args = 0;
9925 /* enable debug options */
9927 #ifdef _IPQOS_CONF_DEBUG
9928 #define DBGOPTS "rz:"
9929 #else
9930 #define DBGOPTS
9931 #endif /* _IPQOS_CONF_DEBUG */
9933 while ((c = getopt(argc, argv, "sca:vflL" DBGOPTS)) != EOF) {
9934 switch (c) {
9935 #ifdef _IPQOS_CONF_DEBUG
9936 case 'z':
9937 cmd = -1;
9938 ifile = optarg;
9939 if (*ifile == '\0') {
9940 usage();
9941 exit(1);
9943 args++;
9944 break;
9945 case 'r':
9946 force_rback++;
9947 break;
9948 #endif /* _IPQOS_CONF_DEBUG */
9949 case 'c':
9950 cmd = IPQOS_CONF_COMMIT;
9951 args++;
9952 break;
9953 case 'a':
9954 cmd = IPQOS_CONF_APPLY;
9955 ifile = optarg;
9956 if (*ifile == '\0') {
9957 usage();
9958 exit(1);
9960 args++;
9961 break;
9962 case 'f':
9963 cmd = IPQOS_CONF_FLUSH;
9964 args++;
9965 break;
9966 case 'l':
9967 cmd = IPQOS_CONF_VIEW;
9968 args++;
9969 break;
9970 case 'L':
9971 cmd = IPQOS_CONF_VIEW;
9972 viewall++;
9973 args++;
9974 break;
9975 case 'v':
9976 verbose++;
9977 break;
9978 case 's':
9979 use_syslog++;
9980 break;
9981 case '?':
9982 usage();
9983 return (1);
9988 * dissallow non-option args, > 1 cmd args and syslog/verbose flags set
9989 * for anything but apply.
9991 if (optind != argc || args > 1 ||
9992 use_syslog && cmd != IPQOS_CONF_APPLY ||
9993 verbose && cmd != IPQOS_CONF_APPLY) {
9994 usage();
9995 exit(1);
9998 /* if no cmd option then show config */
10000 if (args == 0) {
10001 cmd = IPQOS_CONF_VIEW;
10004 /* stop concurrent ipqosconf invocations */
10005 lfp = lock();
10006 if (lfp == -1) {
10007 exit(1);
10010 switch (cmd) {
10011 #ifdef _IPQOS_CONF_DEBUG
10012 case -1:
10013 ret = viewcfile(ifile);
10014 break;
10015 #endif /* _IPQOS_CONF_DEBUG */
10016 case IPQOS_CONF_APPLY:
10017 ret = applyconf(ifile);
10018 break;
10019 case IPQOS_CONF_COMMIT:
10020 ret = commitconf();
10021 break;
10022 case IPQOS_CONF_VIEW:
10023 ret = viewconf(viewall);
10024 break;
10025 case IPQOS_CONF_FLUSH:
10026 ret = flushconf();
10027 break;
10030 (void) unlock(lfp);
10032 return (ret);