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