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