xref: /illumos-gate/usr/src/uts/common/ipp/ipgpc/filters.c (revision 15deec58)
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 (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <sys/atomic.h>
29 #include <sys/types.h>
30 #include <sys/systm.h>
31 #include <netinet/in.h>
32 #include <netinet/ip6.h>
33 #include <inet/common.h>
34 #include <inet/ip.h>
35 #include <inet/ip6.h>
36 #include <ipp/ipp_config.h>
37 #include <ipp/ipgpc/filters.h>
38 #include <ipp/ipgpc/trie.h>
39 #include <ipp/ipgpc/table.h>
40 #include <ipp/ipgpc/ba_table.h>
41 #include <ipp/ipgpc/classifier.h>
42 
43 /* Implementation for filter management and configuration support of ipgpc */
44 
45 #define	BITLENGTH(x) (sizeof (x) * NBBY)
46 
47 /* Globals */
48 kmutex_t ipgpc_table_list_lock; /* table list lock */
49 kmutex_t ipgpc_fid_list_lock;	/* filter id list lock */
50 kmutex_t ipgpc_cid_list_lock;	/* class id list lock */
51 trie_id_t ipgpc_trie_list[NUM_TRIES]; /* list of all trie structures ids */
52 table_id_t ipgpc_table_list[NUM_TABLES]; /* list of all table ids */
53 ba_table_id_t ipgpc_ds_table_id;	/* DiffServ field table id */
54 fid_t *ipgpc_fid_list = NULL;		/* filter id list */
55 cid_t *ipgpc_cid_list = NULL;		/* class id list */
56 kmem_cache_t *ht_node_cache = NULL;	/* hashtable cache */
57 kmem_cache_t *ht_match_cache = NULL;	/* ht_match cache */
58 kmem_cache_t *trie_node_cache = NULL;	/* trie node cache */
59 kmem_cache_t *element_node_cache = NULL; /* element node cache */
60 boolean_t ipgpc_gather_stats;	/* should stats be performed for ipgpc */
61 uint64_t ipgpc_npackets;	/* number of packets stat */
62 uint64_t ipgpc_nbytes;		/* number of bytes stat */
63 uint64_t ipgpc_epackets;	/* number of packets in error */
64 int ipgpc_def_class_id = -1;	/* class id of default class */
65 size_t ipgpc_num_fltrs;		/* number of loaded filter */
66 size_t ipgpc_num_cls;		/* number of loaded classes */
67 /* max number of allowable filters */
68 size_t ipgpc_max_num_filters = IPGPC_DEFAULT_MAX_FILTERS;
69 /* max number of allowable classes */
70 size_t ipgpc_max_num_classes = IPGPC_DEFAULT_MAX_CLASSES;
71 size_t ipgpc_max_filters = 0;	/* set in /etc/system */
72 size_t ipgpc_max_classes = 0;	/* set in /etc/system */
73 ipp_stat_t *ipgpc_global_stats = NULL; /* global stats structure */
74 
75 /* Statics */
76 static trie saddr_trie;		/* IPv4 source address trie */
77 static trie daddr_trie;		/* IPv4 destination address trie */
78 static trie sport_trie;		/* source port trie */
79 static trie dport_trie;		/* destination port trie */
80 static trie saddr6_trie;	/* IPv6 source address trie */
81 static trie daddr6_trie;	/* IPv6 destination address trie */
82 static ht_node_t proto_table[TABLE_SIZE]; /* protocol table */
83 static ht_node_t uid_table[TABLE_SIZE]; /* IPGPC_UID table */
84 static ht_node_t projid_table[TABLE_SIZE]; /* IPGPC_PROJID table */
85 static ht_node_t if_table[TABLE_SIZE]; /* Interface ID table */
86 static ht_node_t if_grpnm_table[TABLE_SIZE]; /* Interface Group Name table */
87 static ht_node_t dir_table[TABLE_SIZE]; /* packet direction table */
88 static ipp_action_id_t ipgpc_aid; /* the action id for ipgpc */
89 
90 static int global_statinit(void);
91 static void insert_ipgpc_trie_list_info(int, size_t, trie, uint16_t);
92 static int initialize_tries(void);
93 static void insert_ipgpc_table_list_info(int, hash_table, int, uint16_t);
94 static void initialize_tables(void);
95 static void initialize_ba_tables(void);
96 static void element_node_ref(element_node_t *);
97 static void element_node_unref(element_node_t *);
98 static int element_node_cache_constructor(void *, void *, int);
99 static int filter_name2id(unsigned *, char[], int32_t, int);
100 static int class_name2id(unsigned *, char[], int);
101 static boolean_t iscontinuousmask(uint32_t, uint8_t);
102 static void insertfid(int, ipgpc_filter_t *, uint_t);
103 static void common_addfilter(fid_t *, int);
104 static void v4_addfilter(fid_t *, int);
105 static void v6_addfilter(fid_t *, int);
106 static void reset_dontcare_stats(void);
107 static int class_statinit(ipgpc_class_t *, int);
108 static int insertcid(ipgpc_class_t *, int *);
109 static void common_removefilter(int, fid_t *);
110 static void v4_removefilter(int, fid_t *);
111 static void v6_removefilter(int, fid_t *);
112 static void removecid(int);
113 static void remove_from_cid_filter_list(int, int);
114 static void removeclasses(ipp_flags_t);
115 static void freetriev6nodes(node_t **);
116 static int ht_match_insert(ht_match_t *, int, uint16_t);
117 static int update_class_stats(ipp_stat_t *, void *, int);
118 static int update_global_stats(ipp_stat_t *, void *, int);
119 static int build_class_nvlist(nvlist_t **, ipgpc_class_t *, boolean_t);
120 static int build_filter_nvlist(nvlist_t **, ipgpc_filter_t *, char *);
121 
122 
123 /*
124  * Module initialization code
125  */
126 
127 /*
128  * global_statinit()
129  *
130  * initializes global stats for ipgpc action module.
131  * global include:
132  * - number of filters loaded
133  * - number of classes loaded
134  * - number of packets that have passed through ipgpc since action create
135  * - number of bytes that have passed through ipgpc since action create
136  * if ipp_stat_create fails, an error code is returned
137  * if ipp_stat_named_init fails, an error code is returned
138  * 0 is returned on success
139  */
140 static int
141 global_statinit(void)
142 {
143 	int rc;
144 	globalstats_t *gblsnames = NULL;
145 
146 	/* create stat structure */
147 	if ((rc = ipp_stat_create(ipgpc_aid, "ipgpc_global_stats", 5,
148 	    update_global_stats, NULL, &ipgpc_global_stats)) != 0) {
149 		ipgpc0dbg(("global_statinit: error creating ipp_stat entry"));
150 		return (rc);
151 	}
152 
153 	ASSERT(ipgpc_global_stats != NULL);
154 	gblsnames = (globalstats_t *)ipgpc_global_stats->ipps_data;
155 	ASSERT(gblsnames != NULL);
156 
157 	/* add stat name entries */
158 	if ((rc = ipp_stat_named_init(ipgpc_global_stats, "nfilters",
159 	    IPP_STAT_UINT32, &gblsnames->nfilters)) != 0) {
160 		return (rc);
161 	}
162 	if ((rc = ipp_stat_named_init(ipgpc_global_stats, "nclasses",
163 	    IPP_STAT_UINT32, &gblsnames->nclasses)) != 0) {
164 		return (rc);
165 	}
166 	if ((rc = ipp_stat_named_init(ipgpc_global_stats, "nbytes",
167 	    IPP_STAT_UINT64, &gblsnames->nbytes)) != 0) {
168 		return (rc);
169 	}
170 	if ((rc = ipp_stat_named_init(ipgpc_global_stats, "npackets",
171 	    IPP_STAT_UINT64, &gblsnames->npackets)) != 0) {
172 		return (rc);
173 	}
174 	if ((rc = ipp_stat_named_init(ipgpc_global_stats, "epackets",
175 	    IPP_STAT_UINT64, &gblsnames->epackets)) != 0) {
176 		return (rc);
177 	}
178 	ipp_stat_install(ipgpc_global_stats);
179 	return (0);
180 }
181 
182 static void
183 insert_ipgpc_trie_list_info(int trie_id, size_t key_len, trie in_trie,
184     uint16_t mask)
185 {
186 	ipgpc_trie_list[trie_id].trie = in_trie;
187 	rw_init(&ipgpc_trie_list[trie_id].rw_lock, NULL, RW_DEFAULT, NULL);
188 	ipgpc_trie_list[trie_id].key_len = key_len;
189 	ipgpc_trie_list[trie_id].info.mask = mask;
190 	ipgpc_trie_list[trie_id].info.dontcareonly = B_TRUE;
191 }
192 
193 static int
194 initialize_tries(void)
195 {
196 	/* IPv4 Source Address field structure */
197 	if ((saddr_trie = create_node(KM_NOSLEEP)) == NULL) {
198 		return (ENOMEM);
199 	}
200 	saddr_trie->isroot = 1;
201 	insert_ipgpc_trie_list_info(IPGPC_TRIE_SADDRID, IP_ABITS, saddr_trie,
202 	    SADDR_MASK);
203 	/* IPv4 Destination Address field structure */
204 	if ((daddr_trie = create_node(KM_NOSLEEP)) == NULL) {
205 		return (ENOMEM);
206 	}
207 	daddr_trie->isroot = 1;
208 	insert_ipgpc_trie_list_info(IPGPC_TRIE_DADDRID, IP_ABITS, daddr_trie,
209 	    DADDR_MASK);
210 	/* TCP Source Port field structure */
211 	if ((sport_trie = create_node(KM_NOSLEEP)) == NULL) {
212 		return (ENOMEM);
213 	}
214 	sport_trie->isroot = 1;
215 	insert_ipgpc_trie_list_info(IPGPC_TRIE_SPORTID, BITLENGTH(uint16_t),
216 	    sport_trie, SPORT_MASK);
217 	/* TCP Destination Port field structure */
218 	if ((dport_trie = create_node(KM_NOSLEEP)) == NULL) {
219 		return (ENOMEM);
220 	}
221 	dport_trie->isroot = 1;
222 	insert_ipgpc_trie_list_info(IPGPC_TRIE_DPORTID, BITLENGTH(uint16_t),
223 	    dport_trie, DPORT_MASK);
224 	/* IPv6 Source Address field structure */
225 	if ((saddr6_trie = create_node(KM_NOSLEEP)) == NULL) {
226 		return (ENOMEM);
227 	}
228 	saddr6_trie->isroot = 1;
229 	insert_ipgpc_trie_list_info(IPGPC_TRIE_SADDRID6, IPV6_ABITS,
230 	    saddr6_trie, SADDR6_MASK);
231 	/* IPv6 Destination Address field structure */
232 	if ((daddr6_trie = create_node(KM_NOSLEEP)) == NULL) {
233 		return (ENOMEM);
234 	}
235 	daddr6_trie->isroot = 1;
236 	insert_ipgpc_trie_list_info(IPGPC_TRIE_DADDRID6, IPV6_ABITS,
237 	    daddr6_trie, DADDR6_MASK);
238 	return (0);
239 }
240 
241 static void
242 insert_ipgpc_table_list_info(int table_id, hash_table table, int wildcard,
243     uint16_t mask)
244 {
245 	ipgpc_table_list[table_id].table = table;
246 	ipgpc_table_list[table_id].wildcard = wildcard;
247 	ipgpc_table_list[table_id].info.mask = mask;
248 	ipgpc_table_list[table_id].info.dontcareonly = B_TRUE;
249 }
250 static void
251 initialize_tables(void)
252 {
253 	/* Protocol selector structure */
254 	insert_ipgpc_table_list_info(PROTOID_IDX, proto_table,
255 	    IPGPC_UNSPECIFIED, PROTO_MASK);
256 	/* UID selector structure */
257 	insert_ipgpc_table_list_info(UID_IDX, uid_table, IPGPC_WILDCARD,
258 	    UID_MASK);
259 	/* PROJID selector structure */
260 	insert_ipgpc_table_list_info(PROJID_IDX, projid_table, IPGPC_WILDCARD,
261 	    PROJID_MASK);
262 	/* IF_INDEX selector structure */
263 	insert_ipgpc_table_list_info(IF_IDX, if_table, IPGPC_UNSPECIFIED,
264 	    IF_MASK);
265 	/* IF_GRPNM_INDEX selector structure */
266 	insert_ipgpc_table_list_info(IF_GRPNM_IDX, if_grpnm_table,
267 	    IPGPC_WILDCARD, IF_GRPNM_MASK);
268 	/* DIR selector structure */
269 	insert_ipgpc_table_list_info(DIR_IDX, dir_table, IPGPC_UNSPECIFIED,
270 	    DIR_MASK);
271 }
272 
273 static void
274 initialize_ba_tables(void)
275 {
276 	/* DS (ToS/Traffic Class) field structure */
277 	ipgpc_ds_table_id.info.mask = DS_MASK;
278 	ipgpc_ds_table_id.info.dontcareonly = B_TRUE;
279 }
280 
281 static void
282 element_node_ref(element_node_t *element)
283 {
284 	atomic_add_32(&element->element_refcnt, 1);
285 	ASSERT(element->element_refcnt > 1);
286 }
287 
288 static void
289 element_node_unref(element_node_t *element)
290 {
291 	ASSERT(element->element_refcnt > 0);
292 	if (atomic_add_32_nv(&element->element_refcnt, -1) == 0) {
293 		kmem_cache_free(element_node_cache, element);
294 	}
295 }
296 
297 /* ARGSUSED1 */
298 static int
299 element_node_cache_constructor(void *buf, void *cdrarg, int kmflags)
300 {
301 	element_node_t *node = buf;
302 
303 	node->element_ref = element_node_ref;
304 	node->element_unref = element_node_unref;
305 	return (0);
306 }
307 
308 /* prime values to be used for hashing of filter and class tables */
309 #define	IPGPC_PRIMES()	{0, 0, 0, 5, 11, 23, 47, 89, 191, 383, 503, 761, \
310 			1009, 1531, 2003, 2503, 3067, 3511, 4001, 5003, 6143, \
311 			10007, 12281, 15013, 20011, 24571, 49139, 98299, \
312 			100003, 196597, 393209, 786431, 1000003, 1251409, \
313 			1572853, 3145721, 0}
314 
315 /*
316  * ipgpc_initialize(in_aid)
317  *
318  * initializes locks, data structures, configuration variables used and
319  * sets globals.  Will fail on memory or initialization error.
320  */
321 int
322 ipgpc_initialize(ipp_action_id_t in_aid)
323 {
324 	ipgpc_class_t def_class;
325 	int i;
326 	int rc;
327 	int sizes[] = IPGPC_PRIMES();
328 
329 	/* initialize globals */
330 	ipgpc_aid = in_aid;	/* store away action id for ipgpc */
331 	ipgpc_num_fltrs = 0;
332 	ipgpc_num_cls = 0;
333 	ipgpc_npackets = 0;
334 	ipgpc_nbytes = 0;
335 	ipgpc_epackets = 0;
336 
337 	/* check for user tunable maximums (set in /etc/system) */
338 	if (ipgpc_max_filters > 0) {
339 		/* start with a reasonably small value to find closest prime */
340 		for (i = 3; i < sizeof (sizes) / sizeof (*sizes) - 1; ++i) {
341 			if (sizes[i] >= ipgpc_max_filters) {
342 				break;
343 			}
344 		}
345 		if (sizes[i] == 0) {
346 			ipgpc0dbg(("ipgpc_initialize: ipgpc_max_filters " \
347 			    "out of range"));
348 			/* use the largest allowable value */
349 			ipgpc_max_num_filters = sizes[(i - 1)];
350 		} else {
351 			ipgpc_max_num_filters = sizes[i];
352 		}
353 	}
354 	if (ipgpc_max_classes > 0) {
355 		/* start with a reasonably small value to find closest prime */
356 		for (i = 3; i < sizeof (sizes) / sizeof (*sizes) - 1; ++i) {
357 			if (sizes[i] >= ipgpc_max_classes) {
358 				break;
359 			}
360 		}
361 		if (sizes[i] == 0) {
362 			ipgpc0dbg(("ipgpc_initialize: ipgpc_max_classes " \
363 			    "out of range"));
364 			/* use the largest allowable value */
365 			ipgpc_max_num_classes = sizes[(i - 1)];
366 		} else {
367 			ipgpc_max_num_classes = sizes[i];
368 		}
369 	}
370 
371 	/* create filter id list */
372 	ipgpc_fid_list =
373 	    kmem_zalloc(sizeof (fid_t) * ipgpc_max_num_filters, KM_NOSLEEP);
374 	if (ipgpc_fid_list == NULL) {
375 		ipgpc0dbg(("ipgpc_initialize: failed to create fid list"));
376 		return (ENOMEM);
377 	}
378 
379 	/* create class id list */
380 	ipgpc_cid_list = kmem_zalloc(sizeof (cid_t) * ipgpc_max_num_classes,
381 	    KM_NOSLEEP);
382 	if (ipgpc_cid_list == NULL) {
383 		ipgpc0dbg(("ipgpc_initialize: failed to create cid list"));
384 		return (ENOMEM);
385 	}
386 
387 	/* create object caches */
388 	element_node_cache = kmem_cache_create("element_node_cache",
389 	    sizeof (element_node_t), 0, element_node_cache_constructor,
390 	    NULL, NULL, NULL, NULL, 0);
391 	trie_node_cache = kmem_cache_create("trie_node_cache",
392 	    sizeof (node_t), 0, NULL, NULL, NULL, NULL, NULL, 0);
393 	ht_node_cache = kmem_cache_create("ht_node_cache",
394 	    sizeof (ht_node_t), 0, NULL, NULL, NULL, NULL, NULL, 0);
395 	ht_match_cache = kmem_cache_create("ht_match_cache",
396 	    sizeof (ht_match_t), 0, NULL, NULL, NULL, NULL, NULL, 0);
397 
398 	/* initialize tries, catch memory errors */
399 	if ((rc = initialize_tries()) != 0) {
400 		return (rc);
401 	}
402 
403 	initialize_tables();	/* no memory is allocated here */
404 	initialize_ba_tables();	/* no memory is allocated here */
405 
406 	if ((rc = global_statinit()) != 0) { /* init global stats */
407 		ipgpc0dbg(("ipgpc_initialize: global_statinit error " \
408 		    "%d", rc));
409 		return (rc);
410 	}
411 
412 	/* create default class */
413 	bzero(&def_class, sizeof (ipgpc_class_t));
414 	def_class.next_action = IPP_ACTION_CONT;
415 	def_class.gather_stats = B_FALSE; /* don't gather stats by default */
416 	(void) strcpy(def_class.class_name, "default");
417 	def_class.originator = IPP_CONFIG_PERMANENT; /* label as permanent */
418 
419 	/* add default class and record default class id */
420 	if ((rc = insertcid(&def_class, &ipgpc_def_class_id)) != ENOENT) {
421 		ipgpc0dbg(("ipgpc_initialize: insert of default class failed" \
422 		    " with error %d", rc));
423 		return (rc);
424 	}
425 	return (0);
426 }
427 
428 /*
429  * Module modify code
430  */
431 
432 /*
433  * name_hash(name, M)
434  *
435  * hash function for a string (name) of lenght M
436  */
437 unsigned
438 name_hash(char *name, size_t M)
439 {
440 	unsigned h;
441 
442 	for (h = 0; *name != '\0'; name++) {
443 		h = ((64 * h) + *name);
444 	}
445 	return ((h % M));
446 }
447 
448 
449 /*
450  * ipgpc_filter_destructor(filter)
451  *
452  * frees any allocated memory pointed to in the filter structure
453  * this function should be run before freeing an ipgpc_filter_t
454  */
455 void
456 ipgpc_filter_destructor(ipgpc_filter_t *filter)
457 {
458 	if (filter->filter_comment != NULL) {
459 		kmem_free(filter->filter_comment,
460 		    (strlen(filter->filter_comment) + 1));
461 	}
462 	if (filter->saddr_hostname != NULL) {
463 		kmem_free(filter->saddr_hostname,
464 		    (strlen(filter->saddr_hostname) + 1));
465 	}
466 	if (filter->daddr_hostname != NULL) {
467 		kmem_free(filter->daddr_hostname,
468 		    (strlen(filter->daddr_hostname) + 1));
469 	}
470 }
471 
472 /*
473  * filter_name2id(*out_id, name, filter_instance, in_num_filters)
474  *
475  * looks up name and instance in filter id table
476  * checks in_num_filters against max filter boundary
477  * if found, returns EEXIST and places the id in out_id
478  * if not found, returns ENOENT and places the new id in out_id
479  * if no additional filter ids are available, ENOMEM is returned
480  */
481 static int
482 filter_name2id(unsigned *out_id, char name[], int32_t filter_instance,
483     int in_num_filters)
484 {
485 	unsigned h;
486 	int dirty = -1;		/* set dirty to not found */
487 
488 	if (in_num_filters >= ipgpc_max_num_filters) {
489 		return (ENOSPC); /* will exceed maximum number of filters */
490 	}
491 
492 	/*
493 	 * search until fid w/ matching name is found or clean space is found
494 	 * if clean space is found, return first dirty space found or if
495 	 * none werer found, return clean space
496 	 */
497 	h = name_hash(name, ipgpc_max_num_filters);
498 	while ((ipgpc_fid_list[h].info != 0) &&
499 	    ((ipgpc_fid_list[h].filter.filter_instance != filter_instance) ||
500 	    (strcmp(name, ipgpc_fid_list[h].filter.filter_name) != 0))) {
501 		if (dirty == -1) { /* this is the first dirty space */
502 			if (ipgpc_fid_list[h].info == -1) { /* dirty */
503 				dirty = h;
504 			}
505 		}
506 		h = (h + 1) % ipgpc_max_num_filters;
507 	}
508 	/*
509 	 * check to see if searching stopped because a clean spot was found
510 	 * and a dirty space was seen before
511 	 */
512 	if ((dirty != -1) && (ipgpc_fid_list[h].info == 0)) {
513 		*out_id = dirty;
514 		return (ENOENT); /* name does not exist in table */
515 	} else if (ipgpc_fid_list[h].info == 0) {
516 		*out_id = h;
517 		return (ENOENT); /* name does not exist in table */
518 	} else {
519 		*out_id = h;
520 		if (ipgpc_fid_list[h].info == -1) {
521 			return (ENOENT);
522 		} else {
523 			return (EEXIST); /* name exists in table */
524 		}
525 	}
526 }
527 
528 /*
529  * class_name2id(*out_id, name, in_num_classes)
530  *
531  * looks up name in class id table
532  * checks in_num_classes against max class boundry
533  * if found, returns EEXIST and places the id in out_id
534  * if not found, returns ENOENT and places the new id in out_id
535  * if no additional class ids are available, ENOSPC is returned
536  */
537 static int
538 class_name2id(unsigned *out_id, char name[], int in_num_classes)
539 {
540 	unsigned h;
541 	int dirty = -1;		/* set dirty to not found */
542 
543 	if (in_num_classes >= ipgpc_max_num_classes) {
544 		return (ENOSPC); /* will exceed maximum number of classes */
545 	}
546 
547 	/*
548 	 * search until cid w/ matching name is found or clean space is found
549 	 * if clean space is found, return first dirty space found or if
550 	 * none were found, return clean space
551 	 */
552 	h = name_hash(name, ipgpc_max_num_classes);
553 	while ((ipgpc_cid_list[h].info != 0) &&
554 	    (strcmp(name, ipgpc_cid_list[h].aclass.class_name) != 0)) {
555 		if (dirty == -1) { /* this is the first dirty space */
556 			if (ipgpc_cid_list[h].info == -1) { /* dirty */
557 				dirty = h;
558 			}
559 		}
560 		h = (h + 1) % ipgpc_max_num_classes;
561 	}
562 	/*
563 	 * check to see if searching stopped because a clean spot was found
564 	 * and a dirty space was seen before
565 	 */
566 	if ((dirty != -1) && (ipgpc_cid_list[h].info == 0)) {
567 		*out_id = dirty;
568 		return (ENOENT); /* name does not exist in table */
569 	} else if (ipgpc_cid_list[h].info == 0) {
570 		*out_id = h;
571 		return (ENOENT); /* name does not exist in table */
572 	} else {
573 		*out_id = h;
574 		if (ipgpc_cid_list[h].info == -1) { /* name did exist */
575 			return (ENOENT); /* name does not exist in table */
576 		} else {
577 			return (EEXIST); /* name exists in table */
578 		}
579 	}
580 }
581 
582 /*
583  * ipgpc_parse_filter(filter, nvlp)
584  *
585  * given a name value pair list, a filter structure is parsed.  A valid
586  * filter must have a filter_name and originator id.  Any value that is not
587  * present, will be given the default wildcard value for that selector
588  */
589 int
590 ipgpc_parse_filter(ipgpc_filter_t *filter, nvlist_t *nvlp)
591 {
592 	uint_t nelem = 4;	/* an IPv6 address is an uint32_t array[4] */
593 	uint32_t *mask;
594 	uint32_t *addr;
595 	char *s;
596 	int i;
597 	in6_addr_t zeroaddr = IN6ADDR_ANY_INIT;
598 
599 	/* parse filter name */
600 	if (nvlist_lookup_string(nvlp, CLASSIFIER_FILTER_NAME, &s) != 0) {
601 		return (EINVAL); /* filter name is missing, error */
602 	}
603 
604 	/* parse originator */
605 	if (nvlist_lookup_uint32(nvlp, IPP_CONFIG_ORIGINATOR,
606 	    &filter->originator) != 0) {
607 		ipgpc0dbg(("ipgpc_parse_filter: originator missing"));
608 		return (EINVAL);
609 	}
610 
611 	/* check for max name length */
612 	if ((strlen(s) + 1) > MAXNAMELEN) {
613 		ipgpc0dbg(("ipgpc_parse_filter: filter name length > " \
614 		    "MAXNAMELEN"));
615 		return (EINVAL);
616 	}
617 
618 	bcopy(s, filter->filter_name, (strlen(s) + 1));
619 
620 	/* parse interface group name */
621 	if (nvlist_lookup_string(nvlp, IPGPC_IF_GROUPNAME, &s) != 0) {
622 		filter->if_groupname[0] = '\0';
623 	} else {
624 		/* check max interface group name lenght */
625 		if ((strlen(s) + 1) > LIFNAMSIZ) {
626 			ipgpc0dbg(("ipgpc_parse_filter: interface group name" \
627 			    " > LIFNAMSIZ"));
628 			return (EINVAL);
629 		}
630 		bcopy(s, filter->if_groupname, (strlen(s) + 1));
631 	}
632 
633 	/* parse uid */
634 	if (nvlist_lookup_uint32(nvlp, IPGPC_UID, &filter->uid) != 0) {
635 		filter->uid = (uid_t)IPGPC_WILDCARD;
636 	}
637 
638 	/* parse projid */
639 	if (nvlist_lookup_int32(nvlp, IPGPC_PROJID, &filter->projid) != 0) {
640 		filter->projid = IPGPC_WILDCARD;
641 	}
642 
643 	/* parse if_index */
644 	if (nvlist_lookup_uint32(nvlp, IPGPC_IF_INDEX, &filter->if_index)
645 	    != 0) {
646 		filter->if_index = 0;
647 	}
648 
649 	/* parse direction */
650 	if (nvlist_lookup_uint32(nvlp, IPGPC_DIR, &filter->direction) != 0) {
651 		filter->direction = 0;
652 	}
653 
654 	/* parse proto */
655 	if (nvlist_lookup_byte(nvlp, IPGPC_PROTO, &filter->proto) != 0) {
656 		filter->proto = 0;
657 	}
658 
659 	/*
660 	 * parse dsfield mask, if mask is present and dsfield value is not,
661 	 * then this is an invalid filter configuration
662 	 */
663 	if (nvlist_lookup_byte(nvlp, IPGPC_DSFIELD_MASK, &filter->dsfield_mask)
664 	    == 0) {
665 		/* parse dsfield */
666 		if (nvlist_lookup_byte(nvlp, IPGPC_DSFIELD, &filter->dsfield)
667 		    != 0) {
668 			ipgpc0dbg(("ipgpc_parse_filter: dsfield missing" \
669 			    " when dsfield_mask 0x%x is present",
670 			    filter->dsfield_mask));
671 			return (EINVAL);
672 		}
673 	} else {
674 		filter->dsfield_mask = 0;
675 		/* check to see if user added dsfield, but not dsfield_mask */
676 		if (nvlist_lookup_byte(nvlp, IPGPC_DSFIELD, &filter->dsfield)
677 		    == 0) {
678 			ipgpc0dbg(("ipgpc_parse_filter: dsfield_mask missing" \
679 			    " when dsfield 0x%x is present",
680 			    filter->dsfield));
681 			return (EINVAL);
682 		}
683 		filter->dsfield = 0;
684 	}
685 
686 	/* parse source port */
687 	if (nvlist_lookup_uint16(nvlp, IPGPC_SPORT, &filter->sport) != 0) {
688 		filter->sport = 0;
689 	}
690 
691 	/*
692 	 * parse source port mask, mask and value must be present, or neither
693 	 */
694 	if (nvlist_lookup_uint16(nvlp, IPGPC_SPORT_MASK, &filter->sport_mask)
695 	    != 0) {
696 		if (filter->sport != 0) {
697 			ipgpc0dbg(("ipgpc_parse_filter: sport_mask missing " \
698 			    "to mask sport %u", filter->sport));
699 			return (EINVAL);
700 		}
701 		filter->sport_mask = 0;
702 	} else {		/* sport mask is present */
703 		if (filter->sport == 0) {
704 			ipgpc0dbg(("ipgpc_parse_filter: sport missing " \
705 			    "when sport_mask %u is present",
706 			    filter->sport_mask));
707 			return (EINVAL);
708 		}
709 	}
710 
711 	/* check for non-continuous mask */
712 	if (!iscontinuousmask(filter->sport_mask, BITLENGTH(uint16_t))) {
713 		ipgpc0dbg(("ipgpc_parse_filter: sport_mask is " \
714 		    "non-continuous"));
715 		return (EINVAL);
716 	}
717 
718 	/* parse destination port */
719 	if (nvlist_lookup_uint16(nvlp, IPGPC_DPORT, &filter->dport) != 0) {
720 		filter->dport = 0;
721 	}
722 
723 	/*
724 	 * parse destination port mask, mask and value must be present,
725 	 * or neither
726 	 */
727 	if (nvlist_lookup_uint16(nvlp, IPGPC_DPORT_MASK, &filter->dport_mask)
728 	    != 0) {
729 		if (filter->dport != 0) {
730 			ipgpc0dbg(("ipgpc_parse_filter: dport_mask missing " \
731 			    "to mask dport %u", filter->dport));
732 			return (EINVAL);
733 		}
734 		filter->dport_mask = 0;
735 	} else {		/* dport mask is present */
736 		if (filter->dport == 0) {
737 			ipgpc0dbg(("ipgpc_parse_filter: dport missing " \
738 			    "when dport_mask %u is present",
739 			    filter->dport_mask));
740 			return (EINVAL);
741 		}
742 	}
743 
744 	/* check for non-continuous mask */
745 	if (!iscontinuousmask(filter->dport_mask, BITLENGTH(uint16_t))) {
746 		ipgpc0dbg(("ipgpc_parse_filter: dport_mask is " \
747 		    "non-continuous"));
748 		return (EINVAL);
749 	}
750 
751 	/* parse precedence */
752 	if (nvlist_lookup_uint32(nvlp, IPGPC_PRECEDENCE, &filter->precedence)
753 	    != 0) {
754 		filter->precedence = UINT_MAX; /* worst precedence */
755 	}
756 
757 	/* parse priority */
758 	if (nvlist_lookup_uint32(nvlp, IPGPC_PRIORITY, &filter->priority)
759 	    != 0) {
760 		filter->priority = 0; /* worst priority */
761 	}
762 
763 	/* parse filter type */
764 	if (nvlist_lookup_byte(nvlp, IPGPC_FILTER_TYPE, &filter->filter_type)
765 	    != 0) {
766 		filter->filter_type = IPGPC_GENERIC_FLTR;
767 	}
768 
769 	/* parse filter instance */
770 	if (nvlist_lookup_int32(nvlp, IPGPC_FILTER_INSTANCE,
771 	    &filter->filter_instance) != 0) {
772 		filter->filter_instance = -1;
773 	}
774 
775 	/* parse filter private field */
776 	if (nvlist_lookup_string(nvlp, IPGPC_FILTER_PRIVATE, &s) != 0) {
777 		filter->filter_comment = NULL;
778 	} else {
779 		filter->filter_comment = kmem_alloc((strlen(s) + 1), KM_SLEEP);
780 		(void) strcpy(filter->filter_comment, s);
781 	}
782 
783 	/*
784 	 * parse source address mask, if address is present, mask must be
785 	 * present
786 	 */
787 	if (nvlist_lookup_uint32_array(nvlp, IPGPC_SADDR_MASK, &mask, &nelem)
788 	    != 0) {
789 		/* check if source address is present */
790 		if (nvlist_lookup_uint32_array(nvlp, IPGPC_SADDR, &addr,
791 		    &nelem) == 0) {
792 			ipgpc0dbg(("ipgpc_parse_filter: source address mask " \
793 			    "missing"));
794 			return (EINVAL);
795 		} else {	/* both saddr and saddr_mask absent */
796 			bcopy(zeroaddr.s6_addr32, filter->saddr.s6_addr32,
797 			    sizeof (filter->saddr.s6_addr32));
798 		}
799 		bcopy(zeroaddr.s6_addr32, filter->saddr_mask.s6_addr32,
800 		    sizeof (filter->saddr_mask.s6_addr32));
801 	} else {		/* saddr_mask present */
802 		/* parse source address */
803 		if (nvlist_lookup_uint32_array(nvlp, IPGPC_SADDR, &addr,
804 		    &nelem) != 0) {
805 			ipgpc0dbg(("ipgpc_parse_filter: source address " \
806 			    "missing"));
807 			return (EINVAL);
808 		} else {	/* saddr present */
809 			bcopy(addr, filter->saddr.s6_addr32,
810 			    sizeof (filter->saddr.s6_addr32));
811 		}
812 		bcopy(mask, filter->saddr_mask.s6_addr32,
813 		    sizeof (filter->saddr_mask.s6_addr32));
814 	}
815 
816 	/* check for non-continuous mask */
817 	if ((filter->filter_type == IPGPC_V6_FLTR) ||
818 	    (filter->filter_type == IPGPC_GENERIC_FLTR)) {
819 		boolean_t zero_found = B_FALSE;
820 		for (i = 0; i < 4; ++i) {
821 			if (filter->saddr_mask.s6_addr32[i] == 0) {
822 				zero_found = B_TRUE;
823 			} else {
824 				if (zero_found) {
825 					ipgpc0dbg(("ipgpc_parse_filter: "
826 					    "saddr_mask is non-continuous"));
827 					return (EINVAL);
828 				}
829 			}
830 			if (!iscontinuousmask(filter->saddr_mask.s6_addr32[i],
831 			    IP_ABITS)) {
832 				ipgpc0dbg(("ipgpc_parse_filter: saddr_mask " \
833 				    "is non-continuous"));
834 				return (EINVAL);
835 			}
836 		}
837 	} else {		/* IPGPC_V4_FLTR */
838 		if (!iscontinuousmask((V4_PART_OF_V6(filter->saddr_mask)),
839 		    IP_ABITS)) {
840 			ipgpc0dbg(("ipgpc_parse_filter: saddr_mask is " \
841 			    "non-continuous"));
842 			return (EINVAL);
843 		}
844 	}
845 
846 	/* parse source address hostname */
847 	if (nvlist_lookup_string(nvlp, IPGPC_SADDR_HOSTNAME, &s) != 0) {
848 		filter->saddr_hostname = NULL;
849 	} else {
850 		filter->saddr_hostname = kmem_alloc((strlen(s) + 1), KM_SLEEP);
851 		(void) strcpy(filter->saddr_hostname, s);
852 	}
853 
854 	/*
855 	 * parse destination address mask, if address is present, mask must be
856 	 * present
857 	 */
858 	if (nvlist_lookup_uint32_array(nvlp, IPGPC_DADDR_MASK, &mask, &nelem)
859 	    != 0) {
860 		/* check if destination address is present */
861 		if (nvlist_lookup_uint32_array(nvlp, IPGPC_DADDR, &addr,
862 		    &nelem) == 0) {
863 			ipgpc0dbg(("ipgpc_parse_filter: destination address " \
864 			    "mask missing"));
865 			return (EINVAL);
866 		} else {	/* both daddr and daddr_mask absent */
867 			bcopy(zeroaddr.s6_addr32, filter->daddr.s6_addr32,
868 			    sizeof (filter->daddr.s6_addr32));
869 		}
870 		bcopy(zeroaddr.s6_addr32, filter->daddr_mask.s6_addr32,
871 		    sizeof (filter->daddr_mask.s6_addr32));
872 	} else {		/* daddr_mask present */
873 		/* parse destination address */
874 		if (nvlist_lookup_uint32_array(nvlp, IPGPC_DADDR, &addr,
875 		    &nelem) != 0) {
876 			ipgpc0dbg(("ipgpc_parse_filter: destination address " \
877 			    "missing"));
878 			return (EINVAL);
879 		} else {	/* daddr present */
880 			bcopy(addr, filter->daddr.s6_addr32,
881 			    sizeof (filter->daddr.s6_addr32));
882 		}
883 		bcopy(mask, filter->daddr_mask.s6_addr32,
884 		    sizeof (filter->daddr_mask.s6_addr32));
885 	}
886 
887 	/* check for non-continuous mask */
888 	if ((filter->filter_type == IPGPC_V6_FLTR) ||
889 	    (filter->filter_type == IPGPC_GENERIC_FLTR)) {
890 		boolean_t zero_found = B_FALSE;
891 		for (i = 0; i < 4; ++i) {
892 			if (filter->daddr_mask.s6_addr32[i] == 0) {
893 				zero_found = B_TRUE;
894 			} else {
895 				if (zero_found) {
896 					ipgpc0dbg(("ipgpc_parse_filter: "
897 					    "daddr_mask is non-continuous"));
898 					return (EINVAL);
899 				}
900 			}
901 			if (!iscontinuousmask(filter->daddr_mask.s6_addr32[i],
902 			    IP_ABITS)) {
903 				ipgpc0dbg(("ipgpc_parse_filter: daddr_mask " \
904 				    "is non-continuous"));
905 				return (EINVAL);
906 			}
907 		}
908 	} else {		/* IPGPC_V4_FLTR */
909 		if (!iscontinuousmask((V4_PART_OF_V6(filter->daddr_mask)),
910 		    IP_ABITS)) {
911 			ipgpc0dbg(("ipgpc_parse_filter: daddr_mask is " \
912 			    "non-continuous"));
913 			return (EINVAL);
914 		}
915 	}
916 
917 	/* parse destination address hostname */
918 	if (nvlist_lookup_string(nvlp, IPGPC_DADDR_HOSTNAME, &s) != 0) {
919 		filter->daddr_hostname = NULL;
920 	} else {
921 		filter->daddr_hostname = kmem_alloc((strlen(s) + 1), KM_SLEEP);
922 		(void) strcpy(filter->daddr_hostname, s);
923 	}
924 
925 	return (0);
926 }
927 
928 /*
929  * iscontinuousmask(mask, len)
930  *
931  * Searches a given mask of length len from MSB to LSB looking for a zero
932  * bit followed by one bit.  A continuous mask must be a string of zero or
933  * more ones followed by a string of zero or more zeros, which would return
934  * B_TRUE.  Otherwise, it is not continuous and this function returns B_FALSE.
935  */
936 static boolean_t
937 iscontinuousmask(uint32_t mask, uint8_t len)
938 {
939 	uint8_t pos;
940 	boolean_t zero_found = B_FALSE;
941 
942 	for (pos = len; pos > 0; --pos) {
943 		if (EXTRACTBIT(mask, (pos - 1), len) == 0) {
944 			zero_found = B_TRUE;
945 		} else {
946 			if (zero_found) {
947 				return (B_FALSE);
948 			}
949 		}
950 	}
951 	return (B_TRUE);
952 }
953 
954 
955 /*
956  * insertfid(filter_id, filter, class_id)
957  *
958  * creates a filter id (fid) structure for filter with filter_id.
959  * filter is associated with the input class id
960  * it is assumed that a fid will not be inserted for a filter that already
961  * exists by the same name.
962  */
963 static void
964 insertfid(int filter_id, ipgpc_filter_t *filter, uint_t class_id)
965 {
966 	ipgpc_fid_list[filter_id].info = 1;
967 	ipgpc3dbg(("insert_fid: adding filter %s to class %s",
968 	    filter->filter_name,
969 	    ipgpc_cid_list[class_id].aclass.class_name));
970 	ipgpc_fid_list[filter_id].class_id = class_id;
971 	ipgpc_fid_list[filter_id].filter = *filter;
972 	ipgpc_fid_list[filter_id].insert_map = 0;
973 }
974 
975 
976 static void
977 common_addfilter(fid_t *fid, int filter_id)
978 {
979 	int if_grpnm_hv;
980 
981 	/* start trie inserts */
982 	/* add source port selector */
983 	if (t_insert(&ipgpc_trie_list[IPGPC_TRIE_SPORTID], filter_id,
984 	    fid->filter.sport, fid->filter.sport_mask) == NORMAL_VALUE) {
985 		fid->insert_map |= SPORT_MASK;
986 	}
987 	/* add destination port selector */
988 	if (t_insert(&ipgpc_trie_list[IPGPC_TRIE_DPORTID], filter_id,
989 	    fid->filter.dport, fid->filter.dport_mask) == NORMAL_VALUE) {
990 		fid->insert_map |= DPORT_MASK;
991 	}
992 	/* end trie inserts */
993 
994 	/* add diffserv field selector */
995 	mutex_enter(&ipgpc_ds_table_id.lock);
996 	if (ba_insert(&ipgpc_ds_table_id, filter_id, fid->filter.dsfield,
997 	    fid->filter.dsfield_mask) == NORMAL_VALUE) {
998 		fid->insert_map |= DS_MASK;
999 	}
1000 	mutex_exit(&ipgpc_ds_table_id.lock);
1001 
1002 	/* start table inserts */
1003 	mutex_enter(&ipgpc_table_list_lock);
1004 	/* add protocol selector */
1005 	if (ht_insert(&ipgpc_table_list[PROTOID_IDX], filter_id,
1006 	    fid->filter.proto) == NORMAL_VALUE) {
1007 		fid->insert_map |= PROTO_MASK;
1008 	}
1009 
1010 	/* add UID selector */
1011 	if (ht_insert(&ipgpc_table_list[UID_IDX], filter_id, fid->filter.uid)
1012 	    == NORMAL_VALUE) {
1013 		fid->insert_map |= UID_MASK;
1014 	}
1015 
1016 	/* add PROJID selector */
1017 	if (ht_insert(&ipgpc_table_list[PROJID_IDX], filter_id,
1018 	    fid->filter.projid) == NORMAL_VALUE) {
1019 		fid->insert_map |= PROJID_MASK;
1020 	}
1021 
1022 	/* add interface index selector */
1023 	if (ht_insert(&ipgpc_table_list[IF_IDX], filter_id,
1024 	    fid->filter.if_index) == NORMAL_VALUE) {
1025 		fid->insert_map |= IF_MASK;
1026 	}
1027 
1028 	/* add interface groupname selector */
1029 	if (fid->filter.if_groupname[0] == '\0') {
1030 		if_grpnm_hv = IPGPC_WILDCARD;
1031 	} else {
1032 		if_grpnm_hv = name_hash(fid->filter.if_groupname, TABLE_SIZE);
1033 	}
1034 	if (ht_insert(&ipgpc_table_list[IF_GRPNM_IDX], filter_id, if_grpnm_hv)
1035 	    == NORMAL_VALUE) {
1036 		fid->insert_map |= IF_GRPNM_MASK;
1037 	}
1038 
1039 	/* add direction selector */
1040 	if (ht_insert(&ipgpc_table_list[DIR_IDX], filter_id,
1041 	    fid->filter.direction) == NORMAL_VALUE) {
1042 		fid->insert_map |= DIR_MASK;
1043 	}
1044 	mutex_exit(&ipgpc_table_list_lock);
1045 	/* end table inserts */
1046 }
1047 
1048 static void
1049 v4_addfilter(fid_t *fid, int filter_id)
1050 {
1051 	/* add IPv4 source address selector */
1052 	if (t_insert(&ipgpc_trie_list[IPGPC_TRIE_SADDRID], filter_id,
1053 	    V4_PART_OF_V6(fid->filter.saddr),
1054 	    V4_PART_OF_V6(fid->filter.saddr_mask)) == NORMAL_VALUE) {
1055 		fid->insert_map |= SADDR_MASK;
1056 	}
1057 
1058 	/* add IPv4 destination address selector */
1059 	if (t_insert(&ipgpc_trie_list[IPGPC_TRIE_DADDRID], filter_id,
1060 	    V4_PART_OF_V6(fid->filter.daddr),
1061 	    V4_PART_OF_V6(fid->filter.daddr_mask)) == NORMAL_VALUE) {
1062 		fid->insert_map |= DADDR_MASK;
1063 	}
1064 }
1065 
1066 static void
1067 v6_addfilter(fid_t *fid, int filter_id)
1068 {
1069 	/* add IPv6 source address selector */
1070 	if (t_insert6(&ipgpc_trie_list[IPGPC_TRIE_SADDRID6], filter_id,
1071 	    fid->filter.saddr, fid->filter.saddr_mask) == NORMAL_VALUE) {
1072 		fid->insert_map |= SADDR6_MASK;
1073 	}
1074 
1075 	/* add IPv6 destination address selector */
1076 	if (t_insert6(&ipgpc_trie_list[IPGPC_TRIE_DADDRID6], filter_id,
1077 	    fid->filter.daddr, fid->filter.daddr_mask) == NORMAL_VALUE) {
1078 		fid->insert_map |= DADDR6_MASK;
1079 	}
1080 }
1081 
1082 /*
1083  * ipgpc_addfilter(filter, class_name, flags)
1084  *
1085  * add the specified filter and associate it with the specified class
1086  * name
1087  * - add filter id to filter list
1088  * - add filter keys to selector structures
1089  * - ENOENT is returned if class does not exist
1090  * - EEXIST is returned if add failed because filter name exists
1091  * - ENOMEM is returned if no memory is available to add a new filter
1092  * - EINVAL if filter.filter_type is invalid
1093  * - 0 is returned on success
1094  * flags is unused currently
1095  */
1096 /* ARGSUSED1 */
1097 int
1098 ipgpc_addfilter(ipgpc_filter_t *filter, char *class_name, ipp_flags_t flags)
1099 {
1100 	unsigned filter_id;
1101 	int err = 0;
1102 	fid_t *fid;
1103 	unsigned class_id;
1104 
1105 	if ((err = class_name2id(&class_id, class_name, ipgpc_num_cls)) !=
1106 		EEXIST) {
1107 		ipgpc0dbg(("ipgpc_addfilter: class lookup error %d", err));
1108 		return (err);
1109 	}
1110 	mutex_enter(&ipgpc_fid_list_lock);
1111 	/* make sure filter does not already exist */
1112 	if ((err = filter_name2id(&filter_id, filter->filter_name,
1113 	    filter->filter_instance, ipgpc_num_fltrs + 1)) == EEXIST) {
1114 		ipgpc0dbg(("ipgpc_addfilter: filter name %s already exists",
1115 		    filter->filter_name));
1116 		mutex_exit(&ipgpc_fid_list_lock);
1117 		return (err);
1118 	} else if (err == ENOSPC) {
1119 		ipgpc0dbg(("ipgpc_addfilter: can not add filter %s, " \
1120 		    "ipgpc_max_num_filteres has been reached",
1121 		    filter->filter_name));
1122 		mutex_exit(&ipgpc_fid_list_lock);
1123 		return (err);
1124 	}
1125 	insertfid(filter_id, filter, class_id);
1126 
1127 	fid = &ipgpc_fid_list[filter_id];
1128 	/* add filter id to selector structures */
1129 	switch (fid->filter.filter_type) {
1130 	case IPGPC_GENERIC_FLTR:
1131 		/* add filter id to all selectors */
1132 		common_addfilter(fid, filter_id);
1133 		v4_addfilter(fid, filter_id);
1134 		v6_addfilter(fid, filter_id);
1135 		break;
1136 	case IPGPC_V4_FLTR:
1137 		/* add filter to common and V4 selectors */
1138 		common_addfilter(fid, filter_id);
1139 		v4_addfilter(fid, filter_id);
1140 		break;
1141 	case IPGPC_V6_FLTR:
1142 		/* add filter to common and V6 selectors */
1143 		common_addfilter(fid, filter_id);
1144 		v6_addfilter(fid, filter_id);
1145 		break;
1146 	default:
1147 		ipgpc0dbg(("ipgpc_addfilter(): invalid filter type %d",
1148 		    fid->filter.filter_type));
1149 		mutex_exit(&ipgpc_fid_list_lock);
1150 		return (EINVAL);
1151 	}
1152 	/* check to see if this is a catch all filter, which we reject */
1153 	if (fid->insert_map == 0) {
1154 		ipgpc0dbg(("ipgpc_addfilter(): filter %s rejected because " \
1155 		    "catch all filters are not supported\n",
1156 		    filter->filter_name));
1157 		/* cleanup what we allocated */
1158 		/* remove filter from filter list */
1159 		ipgpc_fid_list[filter_id].info = -1;
1160 		ipgpc_fid_list[filter_id].filter.filter_name[0] = '\0';
1161 		reset_dontcare_stats();	/* need to fixup stats */
1162 		mutex_exit(&ipgpc_fid_list_lock);
1163 		return (EINVAL);
1164 	} else {		/* associate filter with class */
1165 		mutex_enter(&ipgpc_cid_list_lock);
1166 		(void) ipgpc_list_insert(&ipgpc_cid_list[class_id].filter_list,
1167 		    filter_id);
1168 		mutex_exit(&ipgpc_cid_list_lock);
1169 	}
1170 	mutex_exit(&ipgpc_fid_list_lock);
1171 	atomic_add_long(&ipgpc_num_fltrs, 1);
1172 	ipgpc3dbg(("ipgpc_addfilter: adding filter %s", filter->filter_name));
1173 	return (0);
1174 }
1175 
1176 /*
1177  * reset_dontcare_stats()
1178  *
1179  * when an insertion fails because zero selectors are specified in a filter
1180  * the number of dontcare's recorded for each selector structure needs to be
1181  * decremented
1182  */
1183 static void
1184 reset_dontcare_stats(void)
1185 {
1186 	int i;
1187 
1188 	for (i = 0; i < NUM_TRIES; ++i) {
1189 		atomic_add_32(&ipgpc_trie_list[i].stats.num_dontcare, -1);
1190 	}
1191 	for (i = 0; i < NUM_TABLES; ++i) {
1192 		atomic_add_32(&ipgpc_table_list[i].stats.num_dontcare, -1);
1193 	}
1194 	atomic_add_32(&ipgpc_ds_table_id.stats.num_dontcare, -1);
1195 }
1196 
1197 /*
1198  * ipgpc_parse_class(out_class, nvlp)
1199  *
1200  * Given a name value pair list, a class structure will be parsed.
1201  * To be a valid class, the class name, originator id and next action name
1202  * must be present. gather_stats is optional, if absent default value is used
1203  */
1204 int
1205 ipgpc_parse_class(ipgpc_class_t *out_class, nvlist_t *nvlp)
1206 {
1207 	char *name;
1208 	size_t name_len;
1209 	uint32_t gather_stats;
1210 
1211 	/* parse class name */
1212 	if (nvlist_lookup_string(nvlp, CLASSIFIER_CLASS_NAME, &name) != 0) {
1213 		return (EINVAL); /* class name missing, error */
1214 	}
1215 
1216 	name_len = strlen(name);
1217 	/* check for max name length */
1218 	if ((name_len + 1) > MAXNAMELEN) {
1219 		ipgpc0dbg(("ipgpc_parse_class: class name length > " \
1220 		    "MAXNAMELEN"));
1221 		return (EINVAL);
1222 	}
1223 
1224 	bcopy(name, out_class->class_name, (name_len + 1));
1225 
1226 	/* parse originator */
1227 	if (nvlist_lookup_uint32(nvlp, IPP_CONFIG_ORIGINATOR,
1228 	    &out_class->originator) != 0) {
1229 		ipgpc0dbg(("ipgpc_parse_class: originator missing"));
1230 		return (EINVAL);
1231 	}
1232 
1233 	/* parse action name */
1234 	if (nvlist_lookup_string(nvlp, CLASSIFIER_NEXT_ACTION, &name) != 0) {
1235 		return (EINVAL); /* action name missing, error */
1236 	}
1237 	if ((out_class->next_action = ipp_action_lookup(name))
1238 	    == IPP_ACTION_INVAL) {
1239 		ipgpc0dbg(("ipgpc_parse_class: invalid action name %s", name));
1240 		return (EINVAL);
1241 	}
1242 
1243 	/* parse gather stats boolean */
1244 	if (nvlist_lookup_uint32(nvlp, CLASSIFIER_CLASS_STATS_ENABLE,
1245 	    &gather_stats) != 0) {
1246 		/* stats turned off by default */
1247 		out_class->gather_stats = B_FALSE;
1248 	} else {
1249 		out_class->gather_stats = (boolean_t)gather_stats;
1250 	}
1251 	return (0);
1252 }
1253 
1254 
1255 /*
1256  * ipgpc_addclass(in_class, flags)
1257  *
1258  * adds the given class to the class id list.
1259  * - EEXIST is returned if class of same name already exists
1260  * - ENOSPC if there is no more available memory to add class
1261  * - 0 for success
1262  * flags is currently unused
1263  */
1264 /* ARGSUSED */
1265 int
1266 ipgpc_addclass(ipgpc_class_t *in_class, ipp_flags_t flags) {
1267 	int class_id;
1268 	int err;
1269 
1270 	if ((err = insertcid(in_class, &class_id)) == EEXIST) {
1271 		ipgpc0dbg(("ipgpc_addclass: class name %s already exists",
1272 		    in_class->class_name));
1273 		return (err);
1274 	} else if (err == ENOSPC) {
1275 		ipgpc0dbg(("ipgpc_addclass: can not add class %s, " \
1276 		    "ipgpc_max_num_classes has been reached",
1277 		    in_class->class_name));
1278 		return (err);
1279 	}
1280 	/* add reference to next action */
1281 	if ((err = ipp_action_ref(ipgpc_aid, in_class->next_action, 0)) != 0) {
1282 		/*
1283 		 * the action id we want to reference must have been
1284 		 * destroyed before we could reference it. remove class
1285 		 * and fail.
1286 		 */
1287 		removecid(class_id);
1288 		return (err);
1289 	}
1290 	return (0);
1291 }
1292 
1293 
1294 
1295 /*
1296  * class_statinit(in_class, in_class_id)
1297  *
1298  * for the given class, create stats entries to record
1299  * - next action id
1300  * - number of bytes that matched this class
1301  * - number of packets that matched this class
1302  * - time in hrtime of last match for this class
1303  * any failures are returned, zero on sucess
1304  */
1305 static int
1306 class_statinit(ipgpc_class_t *in_class, int in_class_id)
1307 {
1308 	int rc;
1309 	ipp_stat_t *ipp_cl_stats;
1310 	classstats_t *clsnames = NULL;
1311 
1312 	/* create stat structure */
1313 	if ((rc = ipp_stat_create(ipgpc_aid, in_class->class_name, 3,
1314 	    update_class_stats, &ipgpc_cid_list[in_class_id].stats,
1315 	    &ipp_cl_stats)) != 0) {
1316 		ipgpc0dbg(("class_statinit: error creating ipp_stat entry"));
1317 		return (rc);
1318 	}
1319 
1320 	ASSERT(ipp_cl_stats != NULL);
1321 	clsnames = (classstats_t *)ipp_cl_stats->ipps_data;
1322 	ASSERT(clsnames != NULL);
1323 
1324 	/* create stats entry */
1325 	bzero(&ipgpc_cid_list[in_class_id].stats,
1326 	    sizeof (ipgpc_class_stats_t));
1327 
1328 	/* set next action id */
1329 	ipgpc_cid_list[in_class_id].stats.next_action =
1330 	    ipgpc_cid_list[in_class_id].aclass.next_action;
1331 
1332 	if ((rc = ipp_stat_named_init(ipp_cl_stats, "nbytes",
1333 	    IPP_STAT_UINT64, &clsnames->nbytes)) != 0) {
1334 		return (rc);
1335 	}
1336 	if ((rc = ipp_stat_named_init(ipp_cl_stats, "npackets",
1337 	    IPP_STAT_UINT64, &clsnames->npackets)) != 0) {
1338 		return (rc);
1339 	}
1340 	if ((rc = ipp_stat_named_init(ipp_cl_stats, "last_match",
1341 	    IPP_STAT_INT64, &clsnames->last_match)) != 0) {
1342 		return (rc);
1343 	}
1344 
1345 	/* make reference to kstat structure, for removal */
1346 	ipgpc_cid_list[in_class_id].cl_stats = ipp_cl_stats;
1347 	ipp_stat_install(ipp_cl_stats);
1348 	return (0);
1349 }
1350 
1351 /*
1352  * insertcid(in_class, out_class_id)
1353  *
1354  * creates a class id (cid) structure for in_class, if in_class name
1355  * does not exist already.  id is associated with in_class. the internal
1356  * id of the cid associated with in_class is returned in out_class_id
1357  * - ENOENT is returned if in_class->class_name does not already exist
1358  * - EEXIST is returned if in_class->class_name does already exist
1359  * - ENOSPC is returned if by adding this class, the ipgpc_max_num_classes
1360  *   will be exceeded.
1361  */
1362 static int
1363 insertcid(ipgpc_class_t *in_class, int *out_class_id)
1364 {
1365 	int err, rc;
1366 	unsigned class_id;
1367 
1368 	mutex_enter(&ipgpc_cid_list_lock);
1369 	/* see if entry already exists for class */
1370 	if ((err = class_name2id(&class_id, in_class->class_name,
1371 	    ipgpc_num_cls + 1)) == ENOENT) {
1372 		/* create new filter list for new class */
1373 		ipgpc_cid_list[class_id].info = 1;
1374 		ipgpc_cid_list[class_id].aclass = *in_class;
1375 		if (in_class->gather_stats == B_TRUE) {
1376 			/* init kstat entry */
1377 			if ((rc = class_statinit(in_class, class_id)) != 0) {
1378 				ipgpc_cid_list[class_id].info = -1;
1379 				ipgpc0dbg(("insertcid: " \
1380 					    "class_statinit failed with " \
1381 					    "error %d", rc));
1382 				mutex_exit(&ipgpc_cid_list_lock);
1383 				return (rc);
1384 			}
1385 		} else {
1386 			ipgpc_cid_list[class_id].cl_stats = NULL;
1387 		}
1388 		ipgpc3dbg(("insertcid: adding class %s",
1389 		    in_class->class_name));
1390 		bcopy(in_class->class_name,
1391 		    ipgpc_cid_list[class_id].aclass.class_name, MAXNAMELEN);
1392 		ipgpc_cid_list[class_id].filter_list = NULL;
1393 		atomic_add_long(&ipgpc_num_cls, 1);
1394 	} else {
1395 		ipgpc0dbg(("insertcid: class name lookup error %d", err));
1396 		mutex_exit(&ipgpc_cid_list_lock);
1397 		return (err);
1398 	}
1399 	mutex_exit(&ipgpc_cid_list_lock);
1400 	*out_class_id = class_id;
1401 	return (err);
1402 }
1403 
1404 /*
1405  * common_removefilter(in_filter_id, fid)
1406  *
1407  * removes in_filter_id from each of the common selector structures
1408  */
1409 static void
1410 common_removefilter(int in_filter_id, fid_t *fid)
1411 {
1412 	int if_grpnm_hv;
1413 
1414 	/* start trie removes */
1415 	t_remove(&ipgpc_trie_list[IPGPC_TRIE_SPORTID], in_filter_id,
1416 	    fid->filter.sport, fid->filter.sport_mask);
1417 	/* remove id from destination port trie */
1418 	t_remove(&ipgpc_trie_list[IPGPC_TRIE_DPORTID], in_filter_id,
1419 	    fid->filter.dport, fid->filter.dport_mask);
1420 	/* end trie revmoves */
1421 
1422 	/* remove id from DiffServ field ba table */
1423 	mutex_enter(&ipgpc_ds_table_id.lock);
1424 	ba_remove(&ipgpc_ds_table_id, in_filter_id, fid->filter.dsfield,
1425 	    fid->filter.dsfield_mask);
1426 	mutex_exit(&ipgpc_ds_table_id.lock);
1427 
1428 	/* start table removes */
1429 	mutex_enter(&ipgpc_table_list_lock);
1430 	/* remove id from protocol table */
1431 	ht_remove(&ipgpc_table_list[PROTOID_IDX], in_filter_id,
1432 	    fid->filter.proto);
1433 	/* remove id from UID table */
1434 	ht_remove(&ipgpc_table_list[UID_IDX], in_filter_id, fid->filter.uid);
1435 	/* remove id from PROJID table */
1436 	ht_remove(&ipgpc_table_list[PROJID_IDX], in_filter_id,
1437 	    fid->filter.projid);
1438 	/* remove id from interface id table */
1439 	ht_remove(&ipgpc_table_list[IF_IDX], in_filter_id,
1440 	    fid->filter.if_index);
1441 
1442 	/* remove id from interface group name table */
1443 	if (fid->filter.if_groupname[0] == '\0') {
1444 		if_grpnm_hv = IPGPC_WILDCARD;
1445 	} else {
1446 		if_grpnm_hv = name_hash(fid->filter.if_groupname, TABLE_SIZE);
1447 	}
1448 	ht_remove(&ipgpc_table_list[IF_GRPNM_IDX], in_filter_id, if_grpnm_hv);
1449 	/* remove id from direction table */
1450 	ht_remove(&ipgpc_table_list[DIR_IDX], in_filter_id,
1451 	    fid->filter.direction);
1452 	mutex_exit(&ipgpc_table_list_lock);
1453 	/* end table removes */
1454 }
1455 
1456 /*
1457  * v4_removefilter(in_filter_id, fid)
1458  *
1459  * removes id from IPV4 specific structures
1460  */
1461 static void
1462 v4_removefilter(int in_filter_id, fid_t *fid)
1463 {
1464 	/* remove id from source address trie */
1465 	t_remove(&ipgpc_trie_list[IPGPC_TRIE_SADDRID], in_filter_id,
1466 	    V4_PART_OF_V6(fid->filter.saddr),
1467 	    V4_PART_OF_V6(fid->filter.saddr_mask));
1468 	/* remove id from destination address trie */
1469 	t_remove(&ipgpc_trie_list[IPGPC_TRIE_DADDRID], in_filter_id,
1470 	    V4_PART_OF_V6(fid->filter.daddr),
1471 	    V4_PART_OF_V6(fid->filter.daddr_mask));
1472 }
1473 
1474 /*
1475  * v6_removefilter(in_filter_id, fid)
1476  *
1477  * removes id from IPV6 specific structures
1478  */
1479 static void
1480 v6_removefilter(int in_filter_id, fid_t *fid)
1481 {
1482 	/* remove id from source address trie */
1483 	t_remove6(&ipgpc_trie_list[IPGPC_TRIE_SADDRID6], in_filter_id,
1484 	    fid->filter.saddr, fid->filter.saddr_mask);
1485 	/* remove id from destination address trie */
1486 	t_remove6(&ipgpc_trie_list[IPGPC_TRIE_DADDRID6], in_filter_id,
1487 	    fid->filter.daddr, fid->filter.daddr_mask);
1488 }
1489 
1490 /*
1491  * ipgpc_removefilter(filter_name, filter_instance, flags)
1492  *
1493  * remove the filter associated with the specified name and instance
1494  * - remove filter keys from all search tries
1495  * - remove from filter id list
1496  * - ENOENT is returned if filter name does not exist
1497  * - returns 0 on success
1498  */
1499 /* ARGSUSED */
1500 int
1501 ipgpc_removefilter(char *filter_name, int32_t filter_instance,
1502     ipp_flags_t flags)
1503 {
1504 	unsigned filter_id;
1505 	fid_t *fid;
1506 	int rc;
1507 
1508 	/* check to see if any filters are loaded */
1509 	if (ipgpc_num_fltrs == 0) {
1510 		return (ENOENT);
1511 	}
1512 
1513 	mutex_enter(&ipgpc_fid_list_lock);
1514 	/* lookup filter name, only existing filters can be removed */
1515 	if ((rc = filter_name2id(&filter_id, filter_name, filter_instance,
1516 	    ipgpc_num_fltrs)) != EEXIST) {
1517 		mutex_exit(&ipgpc_fid_list_lock);
1518 		return (rc);
1519 	}
1520 	fid = &ipgpc_fid_list[filter_id];
1521 	switch (fid->filter.filter_type) {
1522 	case IPGPC_GENERIC_FLTR:
1523 		common_removefilter(filter_id, fid);
1524 		v4_removefilter(filter_id, fid);
1525 		v6_removefilter(filter_id, fid);
1526 		break;
1527 	case IPGPC_V4_FLTR:
1528 		common_removefilter(filter_id, fid);
1529 		v4_removefilter(filter_id, fid);
1530 		break;
1531 	case IPGPC_V6_FLTR:
1532 		common_removefilter(filter_id, fid);
1533 		v6_removefilter(filter_id, fid);
1534 		break;
1535 	default:
1536 		ipgpc0dbg(("ipgpc_removefilter(): invalid filter type %d",
1537 		    fid->filter.filter_type));
1538 		mutex_exit(&ipgpc_fid_list_lock);
1539 		return (EINVAL);
1540 	}
1541 	/* remove filter from filter list */
1542 	ipgpc_fid_list[filter_id].info = -1;
1543 	ipgpc_fid_list[filter_id].insert_map = 0;
1544 	ipgpc_fid_list[filter_id].filter.filter_name[0] = '\0';
1545 	ipgpc_filter_destructor(&ipgpc_fid_list[filter_id].filter);
1546 	mutex_exit(&ipgpc_fid_list_lock);
1547 	/* remove filter id from class' list of filters */
1548 	remove_from_cid_filter_list(ipgpc_fid_list[filter_id].class_id,
1549 	    filter_id);
1550 	atomic_add_long(&ipgpc_num_fltrs, -1);
1551 	return (0);
1552 }
1553 
1554 /*
1555  * removecid(in_class_id)
1556  *
1557  * removes the cid entry from the cid list and frees allocated structures
1558  */
1559 static void
1560 removecid(int in_class_id)
1561 {
1562 	ipgpc_cid_list[in_class_id].info = -1;
1563 	ipgpc_cid_list[in_class_id].aclass.class_name[0] = '\0';
1564 	ipgpc_cid_list[in_class_id].aclass.next_action = -1;
1565 	/* delete kstat entry */
1566 	if (ipgpc_cid_list[in_class_id].cl_stats != NULL) {
1567 		ipp_stat_destroy(ipgpc_cid_list[in_class_id].cl_stats);
1568 		ipgpc_cid_list[in_class_id].cl_stats = NULL;
1569 	}
1570 	/* decrement total number of classes loaded */
1571 	atomic_add_long(&ipgpc_num_cls, -1);
1572 }
1573 
1574 /*
1575  * remove_from_cid_filter_list(in_class_id, in_filter_id)
1576  *
1577  * removes the input filter_id from the filter_list of the class associated
1578  * with the input class_id
1579  */
1580 static void
1581 remove_from_cid_filter_list(int in_class_id, int in_filter_id)
1582 {
1583 	cid_t *cid = &ipgpc_cid_list[in_class_id];
1584 
1585 	if (cid->filter_list != NULL) {
1586 		(void) ipgpc_list_remove(&cid->filter_list, in_filter_id);
1587 	}
1588 }
1589 
1590 /*
1591  * ipgpc_removeclass(class_name)
1592  *
1593  * removes a class and all the filters that point to it (ouch!)
1594  * - returns 0 on success
1595  * - ENOENT if class name does not exist
1596  * - ENOTSUP if class name equals 'default'
1597  */
1598 int
1599 ipgpc_removeclass(char *class_name, ipp_flags_t flags)
1600 {
1601 	unsigned class_id;
1602 	element_node_t *anode = NULL;
1603 	element_node_t *tnode = NULL;
1604 	fid_t *fid = NULL;
1605 	ipp_action_id_t old_next_action;
1606 	int rc;
1607 
1608 	/* check to see if any classes are loaded */
1609 	if (ipgpc_num_cls == 0) {
1610 		return (ENOENT);
1611 	}
1612 
1613 	mutex_enter(&ipgpc_cid_list_lock); /* set lock */
1614 	/* lookup class name, only classes that exist can be removed */
1615 	if ((rc = class_name2id(&class_id, class_name, (ipgpc_num_cls - 1)))
1616 	    != EEXIST) {
1617 		mutex_exit(&ipgpc_cid_list_lock); /* release lock */
1618 		return (rc);
1619 	}
1620 	if (class_id == ipgpc_def_class_id) {
1621 		ipgpc0dbg(("ipgpc_removeclass(): default class may not be " \
1622 		    "removed"));
1623 		mutex_exit(&ipgpc_cid_list_lock); /* release lock */
1624 		return (ENOTSUP);
1625 	}
1626 
1627 	old_next_action = ipgpc_cid_list[class_id].aclass.next_action;
1628 	anode = ipgpc_cid_list[class_id].filter_list;
1629 	while (anode != NULL) {
1630 		fid = &ipgpc_fid_list[anode->id];
1631 		if (ipgpc_fid_list[anode->id].info > 0) {
1632 			anode = anode->next;
1633 			(void) ipgpc_removefilter(fid->filter.filter_name,
1634 			    fid->filter.filter_instance, flags);
1635 		} else {
1636 			tnode = anode;
1637 			anode = anode->next;
1638 			/* free this node */
1639 			kmem_cache_free(element_node_cache, tnode);
1640 		}
1641 	}
1642 	/* remove cid from ipgpc_cid_list and decrement ipgpc_num_cls */
1643 	ipgpc3dbg(("ipgpc_removeclass: class %s has been removed",
1644 	    class_name));
1645 	removecid(class_id);
1646 	mutex_exit(&ipgpc_cid_list_lock); /* release lock */
1647 	rc = ipp_action_unref(ipgpc_aid, old_next_action, flags);
1648 	ASSERT(rc == 0);
1649 	return (0);
1650 }
1651 
1652 /*
1653  * ipgpc_modifyfilter(nvlist, flags)
1654  *
1655  * modifies the input filter
1656  * - if in_class != NULL, filter is associated with that class
1657  * - EINVAL is returned if filter name does not exist in nvlist
1658  * - if filter->filter_name does not exist ENOENT is returned
1659  * - if a class name to associate with is not present in nvlist, then the
1660  *   previous class association is used
1661  */
1662 int
1663 ipgpc_modifyfilter(nvlist_t **nvlpp, ipp_flags_t flags)
1664 {
1665 	unsigned filter_id;
1666 	int ret = 0;
1667 	int rc;
1668 	ipgpc_filter_t *filter;
1669 	ipgpc_filter_t old_filter;
1670 	char *name;
1671 	char *s;
1672 	uint_t class_id;
1673 
1674 	filter = kmem_zalloc(sizeof (ipgpc_filter_t), KM_SLEEP);
1675 	if ((ret = ipgpc_parse_filter(filter, *nvlpp)) != 0) {
1676 		ipgpc0dbg(("ipgpc_modifyfilter: error %d parsing filter",
1677 		    ret));
1678 		ipgpc_filter_destructor(filter);
1679 		kmem_free(filter, sizeof (ipgpc_filter_t));
1680 		return (ret);
1681 	}
1682 
1683 	/* parse class name */
1684 	if (nvlist_lookup_string(*nvlpp, CLASSIFIER_CLASS_NAME, &name)
1685 	    != 0) {
1686 		name = NULL;	/* no class specified */
1687 	}
1688 
1689 	/* modify filter entry */
1690 	if ((rc = filter_name2id(&filter_id, filter->filter_name,
1691 	    filter->filter_instance, ipgpc_num_fltrs)) == EEXIST) {
1692 		if (name == NULL) {
1693 			/* set class_name to previous class_name association */
1694 			class_id = ipgpc_fid_list[filter_id].class_id;
1695 			name = ipgpc_cid_list[class_id].aclass.class_name;
1696 		} else {
1697 			if ((ret = class_name2id(&class_id, name,
1698 			    ipgpc_num_cls)) != EEXIST) {
1699 				ipgpc0dbg(("ipgpc_modifyfilter: class does " \
1700 				    "not exist"));
1701 				ipgpc_filter_destructor(filter);
1702 				kmem_free(filter, sizeof (ipgpc_filter_t));
1703 				return (ret);
1704 			}
1705 		}
1706 		/* copy out old filter just in case we need to revert */
1707 		old_filter = ipgpc_fid_list[filter_id].filter;
1708 
1709 		/* make copy of filter_comment */
1710 		if (ipgpc_fid_list[filter_id].filter.filter_comment != NULL) {
1711 			s = ipgpc_fid_list[filter_id].filter.filter_comment;
1712 			old_filter.filter_comment =
1713 			    kmem_alloc((strlen(s) + 1), KM_SLEEP);
1714 			(void) strcpy(old_filter.filter_comment, s);
1715 		} else {
1716 			old_filter.filter_comment = NULL;
1717 		}
1718 
1719 		/* make copy of saddr_hostname */
1720 		if (ipgpc_fid_list[filter_id].filter.saddr_hostname != NULL) {
1721 			s = ipgpc_fid_list[filter_id].filter.saddr_hostname;
1722 			old_filter.saddr_hostname =
1723 			    kmem_alloc((strlen(s) + 1), KM_SLEEP);
1724 			(void) strcpy(old_filter.saddr_hostname, s);
1725 		} else {
1726 			old_filter.saddr_hostname = NULL;
1727 		}
1728 
1729 		/* make copy of daddr_hostname */
1730 		if (ipgpc_fid_list[filter_id].filter.daddr_hostname != NULL) {
1731 			s = ipgpc_fid_list[filter_id].filter.daddr_hostname;
1732 			old_filter.daddr_hostname =
1733 			    kmem_alloc((strlen(s) + 1), KM_SLEEP);
1734 			(void) strcpy(old_filter.daddr_hostname, s);
1735 		} else {
1736 			old_filter.daddr_hostname = NULL;
1737 		}
1738 
1739 		/* remove old filter entry */
1740 		ret = ipgpc_removefilter(filter->filter_name,
1741 		    filter->filter_instance, flags);
1742 		if (ret == 0) {	/* no error, add filter */
1743 			ret = ipgpc_addfilter(filter, name, flags);
1744 			if (ret != 0) {
1745 				/* error occured, free filter fields */
1746 				ipgpc0dbg(("ipgpc_modifyfilter: invalid " \
1747 				    "filter given, unable to modify " \
1748 				    "existing filter %s",
1749 				    filter->filter_name));
1750 				ipgpc_filter_destructor(filter);
1751 				kmem_free(filter, sizeof (ipgpc_filter_t));
1752 				/* revert back to old filter */
1753 				(void) ipgpc_addfilter(&old_filter, name,
1754 				    flags);
1755 				return (ret);
1756 			}
1757 			ipgpc_filter_destructor(&old_filter);
1758 		} else {
1759 			ipgpc0dbg(("ipgpc_modifyfilter: error %d occured " \
1760 			    "when modifying filter", ret));
1761 			ipgpc_filter_destructor(&old_filter);
1762 			ipgpc_filter_destructor(filter);
1763 			kmem_free(filter, sizeof (ipgpc_filter_t));
1764 			return (ret);
1765 		}
1766 	} else {
1767 		ipgpc0dbg(("ipgpc_modifyfilter: filter lookup error %d", rc));
1768 		return (rc); /* filter name does not exist */
1769 	}
1770 	kmem_free(filter, sizeof (ipgpc_filter_t));
1771 	return (0);
1772 }
1773 
1774 /*
1775  * ipgpc_modifyclass(in_class)
1776  *
1777  * if the input class exists, then the action list is modified
1778  * if the input class does not exist, ENOENT is returned
1779  */
1780 /* ARGSUSED */
1781 int
1782 ipgpc_modifyclass(nvlist_t **nvlpp, ipp_flags_t flags)
1783 {
1784 	unsigned class_id;
1785 	ipp_stat_t *cl_stats;
1786 	ipgpc_class_t in_class;
1787 	char *name;
1788 	int rc;
1789 	uint32_t gather_stats;
1790 	boolean_t ref_action = B_FALSE;
1791 	ipp_action_id_t old_next_action;
1792 	size_t name_len;
1793 
1794 	/* parse class name */
1795 	if (nvlist_lookup_string(*nvlpp, CLASSIFIER_CLASS_NAME, &name) != 0) {
1796 		return (EINVAL); /* class name missing, error */
1797 	}
1798 	name_len = strlen(name);
1799 	/* check for max name length */
1800 	if ((name_len + 1) > MAXNAMELEN) {
1801 		ipgpc0dbg(("ipgpc_modifyclass: class name length > " \
1802 		    "MAXNAMELEN"));
1803 		return (EINVAL);
1804 	}
1805 	bcopy(name, in_class.class_name, (name_len + 1));
1806 
1807 	mutex_enter(&ipgpc_cid_list_lock);
1808 	/* look up class name, only existing classes can be modified */
1809 	if ((rc = class_name2id(&class_id, in_class.class_name,
1810 	    ipgpc_num_cls)) == EEXIST) {
1811 		/* preserve previous config if values are absent */
1812 		/* parse action name */
1813 		old_next_action = ipgpc_cid_list[class_id].aclass.next_action;
1814 		if (nvlist_lookup_string(*nvlpp, CLASSIFIER_NEXT_ACTION, &name)
1815 		    != 0) {
1816 			/* use previous config */
1817 			in_class.next_action = old_next_action;
1818 		} else {	/* next action name present */
1819 			if ((in_class.next_action = ipp_action_lookup(name))
1820 			    == IPP_ACTION_INVAL) {
1821 				ipgpc0dbg(("ipgpc_modifyclass: invalid " \
1822 				    "action name %s", name));
1823 				mutex_exit(&ipgpc_cid_list_lock);
1824 				return (EINVAL); /* this is an error */
1825 			}
1826 			ref_action = B_TRUE;
1827 		}
1828 		/* parse gather stats byte */
1829 		if (nvlist_lookup_uint32(*nvlpp, CLASSIFIER_CLASS_STATS_ENABLE,
1830 		    &gather_stats) != 0) {
1831 			/* use previous config */
1832 			in_class.gather_stats =
1833 			    ipgpc_cid_list[class_id].aclass.gather_stats;
1834 		} else {
1835 			in_class.gather_stats = (boolean_t)gather_stats;
1836 		}
1837 		/* check to see if gather_stats booleans differ */
1838 		if ((ipgpc_cid_list[class_id].aclass.gather_stats !=
1839 		    in_class.gather_stats)) {
1840 			if (ipgpc_cid_list[class_id].aclass.gather_stats ==
1841 			    B_TRUE) {
1842 			    /* delete kstat entry */
1843 			    if (ipgpc_cid_list[class_id].cl_stats != NULL) {
1844 				    cl_stats =
1845 					ipgpc_cid_list[class_id].cl_stats;
1846 				    ipp_stat_destroy(cl_stats);
1847 				    ipgpc_cid_list[class_id].cl_stats = NULL;
1848 			    }
1849 			} else { /* gather_stats == B_FALSE */
1850 				if ((rc = class_statinit(&in_class, class_id))
1851 				    != 0) {
1852 					ipgpc0dbg(("ipgpc_modifyclass: " \
1853 					    "class_statinit failed with " \
1854 					    "error %d", rc));
1855 					mutex_exit(&ipgpc_cid_list_lock);
1856 					return (rc);
1857 				}
1858 			}
1859 		}
1860 		mutex_exit(&ipgpc_cid_list_lock);
1861 		/* check if next_action was modified */
1862 		if (ref_action == B_TRUE) {
1863 			if ((rc = ipp_action_ref(ipgpc_aid,
1864 			    in_class.next_action, 0)) != 0) {
1865 				ipgpc0dbg(("ipgpc_modifyclass: error " \
1866 				    "occured while adding a reference to " \
1867 				    "the new next_action %d",
1868 				    in_class.next_action));
1869 				mutex_exit(&ipgpc_cid_list_lock);
1870 				return (rc);
1871 			}
1872 			/* fix up references */
1873 			rc = ipp_action_unref(ipgpc_aid, old_next_action,
1874 			    flags);
1875 			ASSERT(rc == 0);
1876 		}
1877 		/* preserve originator id */
1878 		in_class.originator =
1879 		    ipgpc_cid_list[class_id].aclass.originator;
1880 		ipgpc_cid_list[class_id].aclass = in_class;
1881 		ipgpc_cid_list[class_id].stats.next_action =
1882 		    in_class.next_action;
1883 	} else {
1884 		ipgpc0dbg(("ipgpc_modifyclass: class name lookup error %d",
1885 		    rc));
1886 		mutex_exit(&ipgpc_cid_list_lock);
1887 		return (rc);
1888 	}
1889 	return (0);
1890 }
1891 
1892 
1893 /*
1894  * ipgpc_list_insert(listpp, id)
1895  *
1896  * inserts an item, id, into the list, if item exists EEXIST is returned
1897  */
1898 int
1899 ipgpc_list_insert(linked_list *listpp, key_t id)
1900 {
1901 	element_node_t *p;
1902 
1903 	if (*listpp == NULL) {
1904 		*listpp = kmem_cache_alloc(element_node_cache, KM_SLEEP);
1905 		(*listpp)->element_refcnt = 1;
1906 		(*listpp)->next = NULL;
1907 		(*listpp)->id = id;
1908 	} else {
1909 		for (p = *listpp; p->next != NULL; p = p->next) {
1910 			if (p->id == id) {
1911 				(*p->element_ref)(p);
1912 				return (EEXIST);
1913 			}
1914 		}
1915 		if (p->id == id) {
1916 			(*p->element_ref)(p);
1917 			return (EEXIST);
1918 		} else {
1919 			p->next =
1920 			    kmem_cache_alloc(element_node_cache, KM_SLEEP);
1921 			p->next->element_refcnt = 1;
1922 			p->next->next = NULL;
1923 			p = p->next;
1924 			p->id = id;
1925 		}
1926 	}
1927 	return (0);
1928 }
1929 
1930 /*
1931  * ipgpc_list_remove(listpp, id)
1932  *
1933  * removes an item, id, from the list if it exists and returns TRUE or FALSE
1934  * if not removed
1935  */
1936 boolean_t
1937 ipgpc_list_remove(element_node_t **listpp, key_t id)
1938 {
1939 	element_node_t *p = NULL;
1940 	element_node_t *t = NULL;
1941 
1942 	if (*listpp == NULL) {
1943 		return (B_FALSE);
1944 	}
1945 	if ((*listpp)->id == id) {
1946 		p = *listpp;
1947 		if ((*listpp)->element_refcnt == 1) {
1948 			*listpp = (*listpp)->next;
1949 		}
1950 		(*p->element_unref)(p);
1951 		return (B_TRUE);
1952 	} else if ((*listpp)->next != NULL) {
1953 		/* linear search for matching id */
1954 		for (p = *listpp; p->next != NULL; p = p->next) {
1955 			if (p->next->id == id) {
1956 				t = p->next;
1957 				if (p->next->element_refcnt == 1) {
1958 					p->next = p->next->next;
1959 				}
1960 				(*t->element_unref)(t);
1961 				return (B_TRUE);
1962 			}
1963 		}
1964 	}
1965 	return (B_FALSE);
1966 }
1967 
1968 /*
1969  * Module destroy code
1970  */
1971 
1972 static void
1973 removeclasses(ipp_flags_t flags)
1974 {
1975 	int i;
1976 
1977 	for (i = 0; i < ipgpc_max_num_classes; ++i) {
1978 		if (ipgpc_cid_list[i].info > 0) {
1979 			(void) ipgpc_removeclass(
1980 			    ipgpc_cid_list[i].aclass.class_name, flags);
1981 		}
1982 	}
1983 }
1984 
1985 static void
1986 freetriev6nodes(node_t **inNode)
1987 {
1988 	node_t *anode = *inNode;
1989 	node_t *tnode;
1990 	node_t *s[130];		/* stack of previous nodes */
1991 	int prev_link[130];	/* stack of what the previous link was */
1992 	int sp = 0;
1993 	node_t *root = *inNode;	/* pointer to root node */
1994 
1995 	s[sp] = NULL;
1996 	prev_link[sp] = -1;
1997 	/* loop until only the root node remains */
1998 	while (!((root->zero == NULL) && (root->one == NULL))) {
1999 		if (anode->zero != NULL) { /* check zero node */
2000 			tnode = anode;
2001 			anode = anode->zero;
2002 			s[++sp] = tnode; /* put node on stack */
2003 			prev_link[sp] = 0;
2004 		} else if (anode->one != NULL) { /* check one node */
2005 			tnode = anode;
2006 			anode = anode->one;
2007 			s[++sp] = tnode; /* put node on stack */
2008 			prev_link[sp] = 1;
2009 		} else {	/* leaf node reached */
2010 			/* free leaf node and pop the stack */
2011 			kmem_cache_free(trie_node_cache, anode);
2012 			anode = s[sp];
2013 			if (prev_link[sp--] == 0) {
2014 				anode->zero = NULL;
2015 			} else {
2016 				anode->one = NULL;
2017 			}
2018 			if (anode == NULL) {
2019 				return;
2020 			}
2021 		}
2022 	}
2023 }
2024 
2025 
2026 void
2027 ipgpc_destroy(ipp_flags_t flags)
2028 {
2029 	int i;
2030 	int rc;
2031 	element_node_t *anode = NULL;
2032 	element_node_t *tnode = NULL;
2033 	fid_t *fid = NULL;
2034 
2035 	/* check to see if default class id was set */
2036 	if (ipgpc_def_class_id != -1) {
2037 		ipp_action_id_t next_action =
2038 		    ipgpc_cid_list[ipgpc_def_class_id].aclass.next_action;
2039 
2040 		/* unreference default_class->next_action */
2041 		rc = ipp_action_unref(ipgpc_aid, next_action, flags);
2042 		ASSERT(rc == 0);
2043 		/* removing filter associated with the default class */
2044 		anode = ipgpc_cid_list[ipgpc_def_class_id].filter_list;
2045 		while (anode != NULL) {
2046 			fid = &ipgpc_fid_list[anode->id];
2047 			if (ipgpc_fid_list[anode->id].info > 0) {
2048 				anode = anode->next;
2049 				(void) ipgpc_removefilter(
2050 				    fid->filter.filter_name,
2051 				    fid->filter.filter_instance, flags);
2052 			} else {
2053 				tnode = anode;
2054 				anode = anode->next;
2055 				/* free this node */
2056 				kmem_cache_free(element_node_cache, tnode);
2057 			}
2058 		}
2059 		ASSERT(ipgpc_cid_list[ipgpc_def_class_id].filter_list == NULL);
2060 		removecid(ipgpc_def_class_id);
2061 		ASSERT(ipgpc_cid_list[ipgpc_def_class_id].info == -1);
2062 		ipgpc_def_class_id = -1;
2063 	}
2064 	/* remove stats entries */
2065 	if (ipgpc_global_stats != NULL) {
2066 		/* destroy global stats */
2067 		ipp_stat_destroy(ipgpc_global_stats);
2068 		ipgpc_global_stats = NULL;
2069 	}
2070 
2071 	/*
2072 	 * remove all classes, which will remove all filters, stats and
2073 	 * selectors
2074 	 */
2075 	if (ipgpc_cid_list != NULL) {
2076 		removeclasses(flags);
2077 		kmem_free(ipgpc_cid_list,
2078 		    sizeof (cid_t) * ipgpc_max_num_classes);
2079 		ipgpc_cid_list = NULL;
2080 	}
2081 	/* all filters and classes should have been removed at this point */
2082 	ASSERT((ipgpc_num_cls == 0) && (ipgpc_num_fltrs == 0));
2083 
2084 	/* free filter id list structure */
2085 	if (ipgpc_fid_list != NULL) {
2086 		kmem_free(ipgpc_fid_list,
2087 		    sizeof (fid_t) * ipgpc_max_num_filters);
2088 		ipgpc_fid_list = NULL;
2089 	}
2090 
2091 	/*
2092 	 * IPv6 address tries don't implement path compression or node
2093 	 * deletions, like v4/port tries.  All allocated nodes must be freed
2094 	 * before trie root node is destroyed
2095 	 */
2096 	if (ipgpc_trie_list[IPGPC_TRIE_SADDRID6].trie != NULL) {
2097 		freetriev6nodes(&ipgpc_trie_list[IPGPC_TRIE_SADDRID6].trie);
2098 		/* free trie root */
2099 		kmem_cache_free(trie_node_cache,
2100 		    ipgpc_trie_list[IPGPC_TRIE_SADDRID6].trie);
2101 		/* destroy lock */
2102 		rw_destroy(&ipgpc_trie_list[IPGPC_TRIE_SADDRID6].rw_lock);
2103 		ipgpc_trie_list[IPGPC_TRIE_SADDRID6].trie = NULL;
2104 	}
2105 	if (ipgpc_trie_list[IPGPC_TRIE_DADDRID6].trie != NULL) {
2106 		freetriev6nodes(&ipgpc_trie_list[IPGPC_TRIE_DADDRID6].trie);
2107 		/* free trie root */
2108 		kmem_cache_free(trie_node_cache,
2109 		    ipgpc_trie_list[IPGPC_TRIE_DADDRID6].trie);
2110 		/* destroy lock */
2111 		rw_destroy(&ipgpc_trie_list[IPGPC_TRIE_DADDRID6].rw_lock);
2112 		ipgpc_trie_list[IPGPC_TRIE_DADDRID6].trie = NULL;
2113 	}
2114 
2115 	/* free remaining tries structures */
2116 	for (i = 0; i < (NUM_TRIES - 2); ++i) {
2117 		if (ipgpc_trie_list[i].trie != NULL) {
2118 			/* free trie root */
2119 			kmem_cache_free(trie_node_cache,
2120 			    ipgpc_trie_list[i].trie);
2121 			/* destroy lock */
2122 			rw_destroy(&ipgpc_trie_list[i].rw_lock);
2123 			ipgpc_trie_list[i].trie = NULL;
2124 		}
2125 	}
2126 
2127 	/* destroy caches */
2128 	if (ht_node_cache != NULL) {
2129 		kmem_cache_destroy(ht_node_cache);
2130 		ht_node_cache = NULL;
2131 	}
2132 	if (trie_node_cache != NULL) {
2133 		kmem_cache_destroy(trie_node_cache);
2134 		trie_node_cache = NULL;
2135 	}
2136 	if (element_node_cache != NULL) {
2137 		kmem_cache_destroy(element_node_cache);
2138 		element_node_cache = NULL;
2139 	}
2140 	if (ht_match_cache != NULL) {
2141 		kmem_cache_destroy(ht_match_cache);
2142 		ht_match_cache = NULL;
2143 	}
2144 }
2145 
2146 /*
2147  * Module info code
2148  */
2149 
2150 /*
2151  * ipgpc_params_info(fn, arg)
2152  *
2153  * allocates, builds and passes an nvlist to fn with arg
2154  */
2155 int
2156 ipgpc_params_info(int (*fn)(nvlist_t *, void *), void *arg)
2157 {
2158 	nvlist_t *nvlp;
2159 	int rc;
2160 
2161 	/* allocate nvlist to be passed back */
2162 	if ((rc = nvlist_alloc(&nvlp, NV_UNIQUE_NAME, KM_NOSLEEP)) != 0) {
2163 		return (rc);
2164 	}
2165 
2166 	/* add config type */
2167 	if ((rc = nvlist_add_byte(nvlp, IPP_CONFIG_TYPE, IPP_SET)) != 0) {
2168 		nvlist_free(nvlp);
2169 		return (rc);
2170 	}
2171 
2172 	/* add gather stats boolean */
2173 	if ((rc = nvlist_add_uint32(nvlp, IPP_ACTION_STATS_ENABLE,
2174 	    (uint32_t)ipgpc_gather_stats)) != 0) {
2175 		nvlist_free(nvlp);
2176 		return (rc);
2177 	}
2178 
2179 	/* call back with nvlist */
2180 	rc = fn(nvlp, arg);
2181 
2182 	nvlist_free(nvlp);
2183 
2184 	return (rc);
2185 }
2186 
2187 /*
2188  * build_class_nvlist(nvlpp, in_class)
2189  *
2190  * build an nvlist based on in_class
2191  * if isdefault, add apporiate configuration type to nvlpp
2192  */
2193 static int
2194 build_class_nvlist(nvlist_t **nvlpp, ipgpc_class_t *in_class,
2195     boolean_t isdefault)
2196 {
2197 	nvlist_t *nvlp = *nvlpp;
2198 	char *next_action;
2199 	int rc;
2200 
2201 	/*
2202 	 * add configuration type
2203 	 * if class is the default class, config type should be
2204 	 * CLASSIFIER_MODIFY_CLASS
2205 	 * otherwise it should be CLASSIFIER_ADD_CLASS
2206 	 */
2207 	/* add config type */
2208 	if ((rc = nvlist_add_byte(nvlp, IPP_CONFIG_TYPE,
2209 	    ((isdefault) ? CLASSIFIER_MODIFY_CLASS : CLASSIFIER_ADD_CLASS)))
2210 	    != 0) {
2211 		return (rc);
2212 	}
2213 
2214 	/* add class name */
2215 	if ((rc = nvlist_add_string(nvlp, CLASSIFIER_CLASS_NAME,
2216 	    in_class->class_name)) != 0) {
2217 		return (rc);
2218 	}
2219 
2220 	/* add originator */
2221 	if ((rc = nvlist_add_uint32(nvlp, IPP_CONFIG_ORIGINATOR,
2222 	    in_class->originator)) != 0) {
2223 		return (rc);
2224 	}
2225 
2226 	/* look up next action name with next action id */
2227 	if ((rc = ipp_action_name(in_class->next_action, &next_action)) != 0) {
2228 		return (rc);
2229 	}
2230 
2231 	/* add next action name */
2232 	if ((rc = nvlist_add_string(nvlp, CLASSIFIER_NEXT_ACTION,
2233 	    next_action)) != 0) {
2234 		kmem_free(next_action, (strlen(next_action) + 1));
2235 		return (rc);
2236 	}
2237 
2238 	kmem_free(next_action, (strlen(next_action) + 1));
2239 
2240 	/* add gather stats boolean */
2241 	if ((rc = nvlist_add_uint32(nvlp, CLASSIFIER_CLASS_STATS_ENABLE,
2242 	    (uint32_t)in_class->gather_stats)) != 0) {
2243 		return (rc);
2244 	}
2245 
2246 	return (0);
2247 }
2248 
2249 
2250 /*
2251  * ipgpc_classes_info(fn, arg)
2252  *
2253  * foreach class, allocate, build and pass an nvlist to fn with arg
2254  */
2255 int
2256 ipgpc_classes_info(int (*fn)(nvlist_t *, void *), void *arg)
2257 {
2258 	int i;
2259 	int rc;
2260 	nvlist_t *nvlp;
2261 
2262 	for (i = 0; i < ipgpc_max_num_classes; ++i) {
2263 		if (ipgpc_cid_list[i].info <= 0) {
2264 			/* cid not allocated for this entry */
2265 			continue;
2266 		}
2267 		/* allocate an nvlist */
2268 		if ((rc = nvlist_alloc(&nvlp, NV_UNIQUE_NAME, KM_NOSLEEP))
2269 		    != 0) {
2270 			return (rc);
2271 		}
2272 		/* build an nvlist for this particular class */
2273 		if ((rc = (build_class_nvlist(&nvlp,
2274 		    &ipgpc_cid_list[i].aclass,
2275 		    ((i == ipgpc_def_class_id) ? B_TRUE : B_FALSE)))) != 0) {
2276 			nvlist_free(nvlp);
2277 			return (rc);
2278 		}
2279 		/* call back with nvlist */
2280 		if ((rc = fn(nvlp, arg)) != 0) {
2281 			nvlist_free(nvlp);
2282 			return (rc);
2283 		}
2284 
2285 		nvlist_free(nvlp); /* free nvlist and continue */
2286 	}
2287 
2288 	return (0);
2289 }
2290 
2291 /*
2292  * build_filter_nvlist(nvlpp, in_filter, class_name)
2293  *
2294  * build an nvlist based on in_filter and class_name.
2295  * Only non-wildcard/dontcare selectors are added to the nvlist.
2296  */
2297 static int
2298 build_filter_nvlist(nvlist_t **nvlpp, ipgpc_filter_t *in_filter,
2299     char *class_name)
2300 {
2301 	nvlist_t *nvlp = *nvlpp;
2302 	int rc;
2303 	in6_addr_t zero_addr = IN6ADDR_ANY_INIT;
2304 
2305 	/* add filter name */
2306 	if ((rc = nvlist_add_string(nvlp, CLASSIFIER_FILTER_NAME,
2307 	    in_filter->filter_name)) != 0) {
2308 		return (rc);
2309 	}
2310 
2311 	/* add class name */
2312 	if ((rc = nvlist_add_string(nvlp, CLASSIFIER_CLASS_NAME, class_name))
2313 	    != 0) {
2314 		return (rc);
2315 	}
2316 
2317 	/* add originator */
2318 	if ((rc = nvlist_add_uint32(nvlp, IPP_CONFIG_ORIGINATOR,
2319 	    in_filter->originator)) != 0) {
2320 		return (rc);
2321 	}
2322 
2323 	/* add configuration type of CLASSIFIER_ADD_FILTER */
2324 	if ((rc = nvlist_add_byte(nvlp, IPP_CONFIG_TYPE,
2325 	    CLASSIFIER_ADD_FILTER)) != 0) {
2326 		return (rc);
2327 	}
2328 
2329 	/* add interface groupname */
2330 	if (in_filter->if_groupname[0] != '\0') {
2331 		if ((rc = nvlist_add_string(nvlp, IPGPC_IF_GROUPNAME,
2332 		    in_filter->if_groupname)) != 0) {
2333 			return (rc);
2334 		}
2335 	}
2336 
2337 	/* add uid */
2338 	if (in_filter->uid != IPGPC_WILDCARD) {
2339 		if ((rc = nvlist_add_uint32(nvlp, IPGPC_UID, in_filter->uid))
2340 		    != 0) {
2341 			return (rc);
2342 		}
2343 	}
2344 
2345 	/* add projid */
2346 	if (in_filter->projid != IPGPC_WILDCARD) {
2347 		if ((rc = nvlist_add_int32(nvlp, IPGPC_PROJID,
2348 		    in_filter->projid)) != 0) {
2349 			return (rc);
2350 		}
2351 	}
2352 
2353 	/* add interface index */
2354 	if (in_filter->if_index != IPGPC_UNSPECIFIED) {
2355 		if ((rc = nvlist_add_uint32(nvlp, IPGPC_IF_INDEX,
2356 		    in_filter->if_index)) != 0) {
2357 			return (rc);
2358 		}
2359 	}
2360 
2361 	/* add direction */
2362 	if (in_filter->direction != IPGPC_UNSPECIFIED) {
2363 		if ((rc = nvlist_add_uint32(nvlp, IPGPC_DIR,
2364 		    in_filter->direction)) != 0) {
2365 			return (rc);
2366 		}
2367 	}
2368 
2369 	/* add protocol */
2370 	if (in_filter->proto != IPGPC_UNSPECIFIED) {
2371 		if ((rc = nvlist_add_byte(nvlp, IPGPC_PROTO, in_filter->proto))
2372 		    != 0) {
2373 			return (rc);
2374 		}
2375 	}
2376 
2377 	/* add dsfield and mask */
2378 	if (in_filter->dsfield_mask != 0) {
2379 		if ((rc = nvlist_add_byte(nvlp, IPGPC_DSFIELD,
2380 		    in_filter->dsfield)) != 0) {
2381 			return (rc);
2382 		}
2383 		if ((rc = nvlist_add_byte(nvlp, IPGPC_DSFIELD_MASK,
2384 		    in_filter->dsfield_mask)) != 0) {
2385 			return (rc);
2386 		}
2387 	}
2388 
2389 	/* add source address, mask and hostname */
2390 	if (!(IN6_ARE_ADDR_EQUAL(&in_filter->saddr_mask, &zero_addr))) {
2391 		if ((rc = nvlist_add_uint32_array(nvlp, IPGPC_SADDR,
2392 		    in_filter->saddr.s6_addr32, 4)) != 0) {
2393 			return (rc);
2394 		}
2395 
2396 		if ((rc = nvlist_add_uint32_array(nvlp, IPGPC_SADDR_MASK,
2397 		    in_filter->saddr_mask.s6_addr32, 4)) != 0) {
2398 			return (rc);
2399 		}
2400 
2401 		if (in_filter->saddr_hostname != NULL) {
2402 			if ((rc = nvlist_add_string(nvlp, IPGPC_SADDR_HOSTNAME,
2403 			    in_filter->saddr_hostname)) != 0) {
2404 				return (rc);
2405 			}
2406 		}
2407 	}
2408 
2409 	/* add destination address, mask and hostname */
2410 	if (!(IN6_ARE_ADDR_EQUAL(&in_filter->daddr_mask, &zero_addr))) {
2411 		if ((rc = nvlist_add_uint32_array(nvlp, IPGPC_DADDR,
2412 		    in_filter->daddr.s6_addr32, 4)) != 0) {
2413 			return (rc);
2414 		}
2415 		if ((rc = nvlist_add_uint32_array(nvlp, IPGPC_DADDR_MASK,
2416 		    in_filter->daddr_mask.s6_addr32, 4)) != 0) {
2417 			return (rc);
2418 		}
2419 		if (in_filter->daddr_hostname != NULL) {
2420 			if ((rc = nvlist_add_string(nvlp, IPGPC_DADDR_HOSTNAME,
2421 			    in_filter->daddr_hostname)) != 0) {
2422 				return (rc);
2423 			}
2424 		}
2425 	}
2426 
2427 	/* add source port and mask */
2428 	if (in_filter->sport_mask != 0) {
2429 		if ((rc = nvlist_add_uint16(nvlp, IPGPC_SPORT,
2430 		    in_filter->sport)) != 0) {
2431 			return (rc);
2432 		}
2433 		if ((rc = nvlist_add_uint16(nvlp, IPGPC_SPORT_MASK,
2434 		    in_filter->sport_mask)) != 0) {
2435 			return (rc);
2436 		}
2437 	}
2438 
2439 	/* add destination port and mask */
2440 	if (in_filter->dport_mask != 0) {
2441 		if ((rc = nvlist_add_uint16(nvlp, IPGPC_DPORT,
2442 		    in_filter->dport)) != 0) {
2443 			return (rc);
2444 		}
2445 		if ((rc = nvlist_add_uint16(nvlp, IPGPC_DPORT_MASK,
2446 		    in_filter->dport_mask)) != 0) {
2447 			return (rc);
2448 		}
2449 	}
2450 
2451 	/* add precedence */
2452 	if (in_filter->precedence != UINT_MAX) {
2453 		if ((rc = nvlist_add_uint32(nvlp, IPGPC_PRECEDENCE,
2454 		    in_filter->precedence)) != 0) {
2455 			return (rc);
2456 		}
2457 	}
2458 
2459 	/* add priority */
2460 	if (in_filter->priority != 0) {
2461 		if ((rc = nvlist_add_uint32(nvlp, IPGPC_PRIORITY,
2462 		    in_filter->priority)) != 0) {
2463 			return (rc);
2464 		}
2465 	}
2466 
2467 	/* add filter type */
2468 	if (in_filter->filter_type != IPGPC_GENERIC_FLTR) {
2469 		if ((rc = nvlist_add_byte(nvlp, IPGPC_FILTER_TYPE,
2470 		    in_filter->filter_type)) != 0) {
2471 			return (rc);
2472 		}
2473 	}
2474 
2475 	/* add filter instance */
2476 	if (in_filter->filter_instance != -1) {
2477 		if ((rc = nvlist_add_int32(nvlp, IPGPC_FILTER_INSTANCE,
2478 		    in_filter->filter_instance)) != 0) {
2479 			return (rc);
2480 		}
2481 	}
2482 
2483 	/* add filter private field */
2484 	if (in_filter->filter_comment != NULL) {
2485 		if ((rc = nvlist_add_string(nvlp, IPGPC_FILTER_PRIVATE,
2486 		    in_filter->filter_comment)) != 0) {
2487 			return (rc);
2488 		}
2489 	}
2490 
2491 	return (0);
2492 }
2493 
2494 /*
2495  * ipgpc_filters_info(fn, arg)
2496  *
2497  * for each filter, allocate, build and pass an nvlist to fn with arg
2498  */
2499 int
2500 ipgpc_filters_info(int (*fn)(nvlist_t *, void *), void *arg)
2501 {
2502 	int i;
2503 	int rc;
2504 	nvlist_t *nvlp;
2505 	int class_id;
2506 
2507 	for (i = 0; i < ipgpc_max_num_filters; ++i) {
2508 		if (ipgpc_fid_list[i].info <= 0) {
2509 			/* fid not allocated for this entry */
2510 			continue;
2511 		}
2512 		/* allocate an nvlist */
2513 		if ((rc = nvlist_alloc(&nvlp, NV_UNIQUE_NAME, KM_NOSLEEP))
2514 		    != 0) {
2515 			return (rc);
2516 		}
2517 		class_id = ipgpc_fid_list[i].class_id;
2518 		/* build an nvlist for this particular filter */
2519 		if ((rc = (build_filter_nvlist(&nvlp,
2520 		    &ipgpc_fid_list[i].filter,
2521 		    ipgpc_cid_list[class_id].aclass.class_name))) != 0) {
2522 			nvlist_free(nvlp);
2523 			return (rc);
2524 		}
2525 		/* call back with nvlist */
2526 		if ((rc = fn(nvlp, arg)) != 0) {
2527 			nvlist_free(nvlp);
2528 			return (rc);
2529 		}
2530 
2531 		nvlist_free(nvlp); /* free nvlist and continue */
2532 	}
2533 	return (0);
2534 }
2535 
2536 /*
2537  * Module invoke code
2538  */
2539 
2540 /*
2541  * ipgpc_findfilters(in_id, key, fid_table)
2542  *
2543  * returns a list of matching filters for searching the given structure
2544  * associated with the input id with the input key
2545  * - returns DONTCARE_ONLY_MATCH if the selector structure described by
2546  *   in_id contains only dontcares
2547  * - returns NO_MATCHES if no filters were found and no dontcares exist
2548  *   for a given selector
2549  * - ENOMEM is returned if memory error occurs
2550  * - NORMAL_MATCH on success
2551  */
2552 int
2553 ipgpc_findfilters(int in_id, int key, ht_match_t *fid_table)
2554 {
2555 	int num_found = 0;
2556 
2557 	if (in_id == IPGPC_BA_DSID) {	/* special search for DSFIELD */
2558 		if (ipgpc_ds_table_id.info.dontcareonly == B_TRUE) {
2559 			/* trie is loaded with only DONTCARE(*) keys */
2560 			return (DONTCARE_ONLY_MATCH);
2561 		}
2562 		num_found = ba_retrieve(&ipgpc_ds_table_id, (uint8_t)key,
2563 		    fid_table);
2564 		/* check to see if no matches were made */
2565 		if ((num_found == 0) &&
2566 		    (ipgpc_ds_table_id.stats.num_dontcare == 0)) {
2567 			return (NO_MATCHES);
2568 		}
2569 	} else if (in_id >= TABLE_ID_OFFSET) {	/* table to search */
2570 		table_id_t *taid = &ipgpc_table_list[in_id - TABLE_ID_OFFSET];
2571 
2572 		if (taid->info.dontcareonly == B_TRUE) {
2573 			/* trie is loaded with only DONTCARE(*) keys */
2574 			return (DONTCARE_ONLY_MATCH);
2575 		}
2576 		num_found = ht_retrieve(taid, key, fid_table);
2577 		/* check to see if no matches were made */
2578 		if ((num_found == 0) && (taid->stats.num_dontcare == 0)) {
2579 			return (NO_MATCHES);
2580 		}
2581 	} else {		/* trie to search */
2582 		trie_id_t *tid = &ipgpc_trie_list[in_id];
2583 
2584 		if (tid->info.dontcareonly == B_TRUE) {
2585 			/* trie is loaded with only DONTCARE(*) keys */
2586 			return (DONTCARE_ONLY_MATCH);
2587 		}
2588 		/* search the trie for matches */
2589 		num_found = t_retrieve(tid, key, fid_table);
2590 		/* check to see if no matches were made */
2591 		if ((num_found == 0) && (tid->stats.num_dontcare == 0)) {
2592 			return (NO_MATCHES);
2593 		}
2594 	}
2595 	if (num_found == -1) {	/* num_found == -1 if memory error */
2596 		return (ENOMEM);
2597 	} else {
2598 		return (NORMAL_MATCH);
2599 	}
2600 }
2601 
2602 /*
2603  * ipgpc_findfilters6(in_id, key, fid_table)
2604  *
2605  * findfilters specific to IPv6 traffic
2606  */
2607 int
2608 ipgpc_findfilters6(int in_id, in6_addr_t key, ht_match_t *fid_table)
2609 {
2610 	trie_id_t *tid = &ipgpc_trie_list[in_id];
2611 	int num_found = 0;
2612 
2613 	if (tid->info.dontcareonly == B_TRUE) {
2614 		/* trie is loaded with only DONTCARE(*) keys */
2615 		return (DONTCARE_ONLY_MATCH);
2616 	}
2617 	/* search the trie for matches */
2618 	num_found = t_retrieve6(tid, key, fid_table);
2619 	/* check to see if no matches were made */
2620 	if ((num_found == 0) && (tid->stats.num_dontcare == 0)) {
2621 		return (NO_MATCHES);
2622 	} else if (num_found == -1) { /* num_found == -1 if memory error */
2623 		return (ENOMEM);
2624 	} else {
2625 		return (NORMAL_MATCH);
2626 	}
2627 }
2628 
2629 /*
2630  * ht_match_insert(a, id, mask)
2631  *
2632  * inserts id into table and applies mask to match_map
2633  * returns ENOMEM if can't allocate ht_match_t node, 0 otherwise
2634  */
2635 static int
2636 ht_match_insert(ht_match_t *a, int id, uint16_t mask)
2637 {
2638 	int x = (id % HASH_SIZE); /* has for index */
2639 	ht_match_t *p = NULL;
2640 
2641 	if ((a[x].key == id) || (a[x].key == 0)) {
2642 		a[x].key = id;
2643 		a[x].match_map |= mask;
2644 	} else if (a[x].next == NULL) {
2645 		a[x].next = kmem_cache_alloc(ht_match_cache, KM_NOSLEEP);
2646 		if (a[x].next == NULL) {
2647 			ipgpc0dbg(("ht_match_insert(): kmem_cache_alloc " \
2648 			    "error"));
2649 			return (ENOMEM);
2650 		}
2651 		a[x].next->next = NULL;
2652 		a[x].next->key = id;
2653 		a[x].next->match_map = mask;
2654 	} else {
2655 
2656 		p = a[x].next;
2657 		while (p != NULL) {
2658 			if (p->key == id) {
2659 				p->match_map |= mask;
2660 				return (0);
2661 			}
2662 			p = p->next;
2663 		}
2664 		p = kmem_cache_alloc(ht_match_cache, KM_NOSLEEP);
2665 		if (p == NULL) {
2666 			ipgpc0dbg(("ht_match_insert(): kmem_cache_alloc " \
2667 			    "error"));
2668 			return (ENOMEM);
2669 		}
2670 		p->key = id;
2671 		p->match_map = mask;
2672 		p->next = a[x].next;
2673 		a[x].next = p;
2674 	}
2675 	return (0);
2676 }
2677 
2678 /*
2679  * ipgpc_mark_found(mask, list, fid_table)
2680  *
2681  * given a list of filter ids and a mask for the selector that is being marked,
2682  * the ids are inserted (or updated) in the fid_table to being marked as
2683  * matched for the given selector
2684  * return -1 if memory error
2685  */
2686 int
2687 ipgpc_mark_found(uint16_t mask, linked_list list, ht_match_t *fid_table)
2688 {
2689 	linked_list tnode = NULL;
2690 	int num_found = 0;
2691 
2692 	for (tnode = list; tnode != NULL; tnode = tnode->next) {
2693 		/* apply the trie mask to the match map for this element */
2694 		if (ipgpc_fid_list[tnode->id].info > 0) {
2695 			if (ht_match_insert(fid_table, tnode->id, mask)
2696 			    == ENOMEM) {
2697 				return (-1);
2698 			}
2699 			++num_found;
2700 		}
2701 	}
2702 	return (num_found);
2703 }
2704 
2705 /* updates global stats for ipgpc */
2706 /* ARGSUSED */
2707 static int
2708 update_global_stats(ipp_stat_t *sp, void *arg, int rw)
2709 {
2710 	globalstats_t *gbl_stats = (globalstats_t *)sp->ipps_data;
2711 	uint32_t num_filters = (uint32_t)ipgpc_num_fltrs;
2712 	uint32_t num_classes = (uint32_t)ipgpc_num_cls;
2713 
2714 	ASSERT(gbl_stats != NULL);
2715 	(void) ipp_stat_named_op(&gbl_stats->nfilters, &num_filters, rw);
2716 	(void) ipp_stat_named_op(&gbl_stats->nclasses, &num_classes, rw);
2717 	(void) ipp_stat_named_op(&gbl_stats->nbytes, &ipgpc_nbytes, rw);
2718 	(void) ipp_stat_named_op(&gbl_stats->npackets, &ipgpc_npackets, rw);
2719 	(void) ipp_stat_named_op(&gbl_stats->epackets, &ipgpc_epackets, rw);
2720 	return (0);
2721 }
2722 
2723 
2724 /* updates class stats for a specific class */
2725 static int
2726 update_class_stats(ipp_stat_t *sp, void *arg, int rw)
2727 {
2728 	ipgpc_class_stats_t *stats = (ipgpc_class_stats_t *)arg;
2729 	classstats_t *cl_stats = (classstats_t *)sp->ipps_data;
2730 
2731 	ASSERT(stats != NULL);
2732 	ASSERT(cl_stats != NULL);
2733 	(void) ipp_stat_named_op(&cl_stats->nbytes, &stats->nbytes, rw);
2734 	(void) ipp_stat_named_op(&cl_stats->npackets, &stats->npackets, rw);
2735 	(void) ipp_stat_named_op(&cl_stats->last_match, &stats->last_match, rw);
2736 	return (0);
2737 }
2738