5045 use atomic_{inc,dec}_* instead of atomic_add_*
[illumos-gate.git] / usr / src / uts / common / ipp / ipgpc / filters.c
blobd3a21f14cd655261e491dd0d657201d0f99e73b3
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 #include <sys/atomic.h>
27 #include <sys/types.h>
28 #include <sys/systm.h>
29 #include <netinet/in.h>
30 #include <netinet/ip6.h>
31 #include <inet/common.h>
32 #include <inet/ip.h>
33 #include <inet/ip6.h>
34 #include <ipp/ipp_config.h>
35 #include <ipp/ipgpc/filters.h>
36 #include <ipp/ipgpc/trie.h>
37 #include <ipp/ipgpc/table.h>
38 #include <ipp/ipgpc/ba_table.h>
39 #include <ipp/ipgpc/classifier.h>
41 /* Implementation for filter management and configuration support of ipgpc */
43 #define BITLENGTH(x) (sizeof (x) * NBBY)
45 /* Globals */
46 kmutex_t ipgpc_table_list_lock; /* table list lock */
47 kmutex_t ipgpc_fid_list_lock; /* filter id list lock */
48 kmutex_t ipgpc_cid_list_lock; /* class id list lock */
49 trie_id_t ipgpc_trie_list[NUM_TRIES]; /* list of all trie structures ids */
50 table_id_t ipgpc_table_list[NUM_TABLES]; /* list of all table ids */
51 ba_table_id_t ipgpc_ds_table_id; /* DiffServ field table id */
52 fid_t *ipgpc_fid_list = NULL; /* filter id list */
53 cid_t *ipgpc_cid_list = NULL; /* class id list */
54 kmem_cache_t *ht_node_cache = NULL; /* hashtable cache */
55 kmem_cache_t *ht_match_cache = NULL; /* ht_match cache */
56 kmem_cache_t *trie_node_cache = NULL; /* trie node cache */
57 kmem_cache_t *element_node_cache = NULL; /* element node cache */
58 boolean_t ipgpc_gather_stats; /* should stats be performed for ipgpc */
59 uint64_t ipgpc_npackets; /* number of packets stat */
60 uint64_t ipgpc_nbytes; /* number of bytes stat */
61 uint64_t ipgpc_epackets; /* number of packets in error */
62 int ipgpc_def_class_id = -1; /* class id of default class */
63 size_t ipgpc_num_fltrs; /* number of loaded filter */
64 size_t ipgpc_num_cls; /* number of loaded classes */
65 /* max number of allowable filters */
66 size_t ipgpc_max_num_filters = IPGPC_DEFAULT_MAX_FILTERS;
67 /* max number of allowable classes */
68 size_t ipgpc_max_num_classes = IPGPC_DEFAULT_MAX_CLASSES;
69 size_t ipgpc_max_filters = 0; /* set in /etc/system */
70 size_t ipgpc_max_classes = 0; /* set in /etc/system */
71 ipp_stat_t *ipgpc_global_stats = NULL; /* global stats structure */
73 /* Statics */
74 static trie saddr_trie; /* IPv4 source address trie */
75 static trie daddr_trie; /* IPv4 destination address trie */
76 static trie sport_trie; /* source port trie */
77 static trie dport_trie; /* destination port trie */
78 static trie saddr6_trie; /* IPv6 source address trie */
79 static trie daddr6_trie; /* IPv6 destination address trie */
80 static ht_node_t proto_table[TABLE_SIZE]; /* protocol table */
81 static ht_node_t uid_table[TABLE_SIZE]; /* IPGPC_UID table */
82 static ht_node_t projid_table[TABLE_SIZE]; /* IPGPC_PROJID table */
83 static ht_node_t if_table[TABLE_SIZE]; /* Interface ID table */
84 static ht_node_t dir_table[TABLE_SIZE]; /* packet direction table */
85 static ipp_action_id_t ipgpc_aid; /* the action id for ipgpc */
87 static int global_statinit(void);
88 static void insert_ipgpc_trie_list_info(int, size_t, trie, uint16_t);
89 static int initialize_tries(void);
90 static void insert_ipgpc_table_list_info(int, hash_table, int, uint16_t);
91 static void initialize_tables(void);
92 static void initialize_ba_tables(void);
93 static void element_node_ref(element_node_t *);
94 static void element_node_unref(element_node_t *);
95 static int element_node_cache_constructor(void *, void *, int);
96 static int filter_name2id(unsigned *, char[], int32_t, int);
97 static int class_name2id(unsigned *, char[], int);
98 static boolean_t iscontinuousmask(uint32_t, uint8_t);
99 static void insertfid(int, ipgpc_filter_t *, uint_t);
100 static void common_addfilter(fid_t *, int);
101 static void v4_addfilter(fid_t *, int);
102 static void v6_addfilter(fid_t *, int);
103 static void reset_dontcare_stats(void);
104 static int class_statinit(ipgpc_class_t *, int);
105 static int insertcid(ipgpc_class_t *, int *);
106 static void common_removefilter(int, fid_t *);
107 static void v4_removefilter(int, fid_t *);
108 static void v6_removefilter(int, fid_t *);
109 static void removecid(int);
110 static void remove_from_cid_filter_list(int, int);
111 static void removeclasses(ipp_flags_t);
112 static void freetriev6nodes(node_t **);
113 static int ht_match_insert(ht_match_t *, int, uint16_t);
114 static int update_class_stats(ipp_stat_t *, void *, int);
115 static int update_global_stats(ipp_stat_t *, void *, int);
116 static int build_class_nvlist(nvlist_t **, ipgpc_class_t *, boolean_t);
117 static int build_filter_nvlist(nvlist_t **, ipgpc_filter_t *, char *);
121 * Module initialization code
125 * global_statinit()
127 * initializes global stats for ipgpc action module.
128 * global include:
129 * - number of filters loaded
130 * - number of classes loaded
131 * - number of packets that have passed through ipgpc since action create
132 * - number of bytes that have passed through ipgpc since action create
133 * if ipp_stat_create fails, an error code is returned
134 * if ipp_stat_named_init fails, an error code is returned
135 * 0 is returned on success
137 static int
138 global_statinit(void)
140 int rc;
141 globalstats_t *gblsnames = NULL;
143 /* create stat structure */
144 if ((rc = ipp_stat_create(ipgpc_aid, "ipgpc_global_stats", 5,
145 update_global_stats, NULL, &ipgpc_global_stats)) != 0) {
146 ipgpc0dbg(("global_statinit: error creating ipp_stat entry"));
147 return (rc);
150 ASSERT(ipgpc_global_stats != NULL);
151 gblsnames = (globalstats_t *)ipgpc_global_stats->ipps_data;
152 ASSERT(gblsnames != NULL);
154 /* add stat name entries */
155 if ((rc = ipp_stat_named_init(ipgpc_global_stats, "nfilters",
156 IPP_STAT_UINT32, &gblsnames->nfilters)) != 0) {
157 return (rc);
159 if ((rc = ipp_stat_named_init(ipgpc_global_stats, "nclasses",
160 IPP_STAT_UINT32, &gblsnames->nclasses)) != 0) {
161 return (rc);
163 if ((rc = ipp_stat_named_init(ipgpc_global_stats, "nbytes",
164 IPP_STAT_UINT64, &gblsnames->nbytes)) != 0) {
165 return (rc);
167 if ((rc = ipp_stat_named_init(ipgpc_global_stats, "npackets",
168 IPP_STAT_UINT64, &gblsnames->npackets)) != 0) {
169 return (rc);
171 if ((rc = ipp_stat_named_init(ipgpc_global_stats, "epackets",
172 IPP_STAT_UINT64, &gblsnames->epackets)) != 0) {
173 return (rc);
175 ipp_stat_install(ipgpc_global_stats);
176 return (0);
179 static void
180 insert_ipgpc_trie_list_info(int trie_id, size_t key_len, trie in_trie,
181 uint16_t mask)
183 ipgpc_trie_list[trie_id].trie = in_trie;
184 rw_init(&ipgpc_trie_list[trie_id].rw_lock, NULL, RW_DEFAULT, NULL);
185 ipgpc_trie_list[trie_id].key_len = key_len;
186 ipgpc_trie_list[trie_id].info.mask = mask;
187 ipgpc_trie_list[trie_id].info.dontcareonly = B_TRUE;
190 static int
191 initialize_tries(void)
193 /* IPv4 Source Address field structure */
194 if ((saddr_trie = create_node(KM_NOSLEEP)) == NULL) {
195 return (ENOMEM);
197 saddr_trie->isroot = 1;
198 insert_ipgpc_trie_list_info(IPGPC_TRIE_SADDRID, IP_ABITS, saddr_trie,
199 SADDR_MASK);
200 /* IPv4 Destination Address field structure */
201 if ((daddr_trie = create_node(KM_NOSLEEP)) == NULL) {
202 return (ENOMEM);
204 daddr_trie->isroot = 1;
205 insert_ipgpc_trie_list_info(IPGPC_TRIE_DADDRID, IP_ABITS, daddr_trie,
206 DADDR_MASK);
207 /* TCP Source Port field structure */
208 if ((sport_trie = create_node(KM_NOSLEEP)) == NULL) {
209 return (ENOMEM);
211 sport_trie->isroot = 1;
212 insert_ipgpc_trie_list_info(IPGPC_TRIE_SPORTID, BITLENGTH(uint16_t),
213 sport_trie, SPORT_MASK);
214 /* TCP Destination Port field structure */
215 if ((dport_trie = create_node(KM_NOSLEEP)) == NULL) {
216 return (ENOMEM);
218 dport_trie->isroot = 1;
219 insert_ipgpc_trie_list_info(IPGPC_TRIE_DPORTID, BITLENGTH(uint16_t),
220 dport_trie, DPORT_MASK);
221 /* IPv6 Source Address field structure */
222 if ((saddr6_trie = create_node(KM_NOSLEEP)) == NULL) {
223 return (ENOMEM);
225 saddr6_trie->isroot = 1;
226 insert_ipgpc_trie_list_info(IPGPC_TRIE_SADDRID6, IPV6_ABITS,
227 saddr6_trie, SADDR6_MASK);
228 /* IPv6 Destination Address field structure */
229 if ((daddr6_trie = create_node(KM_NOSLEEP)) == NULL) {
230 return (ENOMEM);
232 daddr6_trie->isroot = 1;
233 insert_ipgpc_trie_list_info(IPGPC_TRIE_DADDRID6, IPV6_ABITS,
234 daddr6_trie, DADDR6_MASK);
235 return (0);
238 static void
239 insert_ipgpc_table_list_info(int table_id, hash_table table, int wildcard,
240 uint16_t mask)
242 ipgpc_table_list[table_id].table = table;
243 ipgpc_table_list[table_id].wildcard = wildcard;
244 ipgpc_table_list[table_id].info.mask = mask;
245 ipgpc_table_list[table_id].info.dontcareonly = B_TRUE;
247 static void
248 initialize_tables(void)
250 /* Protocol selector structure */
251 insert_ipgpc_table_list_info(PROTOID_IDX, proto_table,
252 IPGPC_UNSPECIFIED, PROTO_MASK);
253 /* UID selector structure */
254 insert_ipgpc_table_list_info(UID_IDX, uid_table, IPGPC_WILDCARD,
255 UID_MASK);
256 /* PROJID selector structure */
257 insert_ipgpc_table_list_info(PROJID_IDX, projid_table, IPGPC_WILDCARD,
258 PROJID_MASK);
259 /* IF_INDEX selector structure */
260 insert_ipgpc_table_list_info(IF_IDX, if_table, IPGPC_UNSPECIFIED,
261 IF_MASK);
262 /* DIR selector structure */
263 insert_ipgpc_table_list_info(DIR_IDX, dir_table, IPGPC_UNSPECIFIED,
264 DIR_MASK);
267 static void
268 initialize_ba_tables(void)
270 /* DS (ToS/Traffic Class) field structure */
271 ipgpc_ds_table_id.info.mask = DS_MASK;
272 ipgpc_ds_table_id.info.dontcareonly = B_TRUE;
275 static void
276 element_node_ref(element_node_t *element)
278 atomic_inc_32(&element->element_refcnt);
279 ASSERT(element->element_refcnt > 1);
282 static void
283 element_node_unref(element_node_t *element)
285 ASSERT(element->element_refcnt > 0);
286 if (atomic_dec_32_nv(&element->element_refcnt) == 0) {
287 kmem_cache_free(element_node_cache, element);
291 /* ARGSUSED1 */
292 static int
293 element_node_cache_constructor(void *buf, void *cdrarg, int kmflags)
295 element_node_t *node = buf;
297 node->element_ref = element_node_ref;
298 node->element_unref = element_node_unref;
299 return (0);
302 /* prime values to be used for hashing of filter and class tables */
303 #define IPGPC_PRIMES() {0, 0, 0, 5, 11, 23, 47, 89, 191, 383, 503, 761, \
304 1009, 1531, 2003, 2503, 3067, 3511, 4001, 5003, 6143, \
305 10007, 12281, 15013, 20011, 24571, 49139, 98299, \
306 100003, 196597, 393209, 786431, 1000003, 1251409, \
307 1572853, 3145721, 0}
310 * ipgpc_initialize(in_aid)
312 * initializes locks, data structures, configuration variables used and
313 * sets globals. Will fail on memory or initialization error.
316 ipgpc_initialize(ipp_action_id_t in_aid)
318 ipgpc_class_t def_class;
319 int i;
320 int rc;
321 int sizes[] = IPGPC_PRIMES();
323 /* initialize globals */
324 ipgpc_aid = in_aid; /* store away action id for ipgpc */
325 ipgpc_num_fltrs = 0;
326 ipgpc_num_cls = 0;
327 ipgpc_npackets = 0;
328 ipgpc_nbytes = 0;
329 ipgpc_epackets = 0;
331 /* check for user tunable maximums (set in /etc/system) */
332 if (ipgpc_max_filters > 0) {
333 /* start with a reasonably small value to find closest prime */
334 for (i = 3; i < sizeof (sizes) / sizeof (*sizes) - 1; ++i) {
335 if (sizes[i] >= ipgpc_max_filters) {
336 break;
339 if (sizes[i] == 0) {
340 ipgpc0dbg(("ipgpc_initialize: ipgpc_max_filters " \
341 "out of range"));
342 /* use the largest allowable value */
343 ipgpc_max_num_filters = sizes[(i - 1)];
344 } else {
345 ipgpc_max_num_filters = sizes[i];
348 if (ipgpc_max_classes > 0) {
349 /* start with a reasonably small value to find closest prime */
350 for (i = 3; i < sizeof (sizes) / sizeof (*sizes) - 1; ++i) {
351 if (sizes[i] >= ipgpc_max_classes) {
352 break;
355 if (sizes[i] == 0) {
356 ipgpc0dbg(("ipgpc_initialize: ipgpc_max_classes " \
357 "out of range"));
358 /* use the largest allowable value */
359 ipgpc_max_num_classes = sizes[(i - 1)];
360 } else {
361 ipgpc_max_num_classes = sizes[i];
365 /* create filter id list */
366 ipgpc_fid_list =
367 kmem_zalloc(sizeof (fid_t) * ipgpc_max_num_filters, KM_NOSLEEP);
368 if (ipgpc_fid_list == NULL) {
369 ipgpc0dbg(("ipgpc_initialize: failed to create fid list"));
370 return (ENOMEM);
373 /* create class id list */
374 ipgpc_cid_list = kmem_zalloc(sizeof (cid_t) * ipgpc_max_num_classes,
375 KM_NOSLEEP);
376 if (ipgpc_cid_list == NULL) {
377 ipgpc0dbg(("ipgpc_initialize: failed to create cid list"));
378 return (ENOMEM);
381 /* create object caches */
382 element_node_cache = kmem_cache_create("element_node_cache",
383 sizeof (element_node_t), 0, element_node_cache_constructor,
384 NULL, NULL, NULL, NULL, 0);
385 trie_node_cache = kmem_cache_create("trie_node_cache",
386 sizeof (node_t), 0, NULL, NULL, NULL, NULL, NULL, 0);
387 ht_node_cache = kmem_cache_create("ht_node_cache",
388 sizeof (ht_node_t), 0, NULL, NULL, NULL, NULL, NULL, 0);
389 ht_match_cache = kmem_cache_create("ht_match_cache",
390 sizeof (ht_match_t), 0, NULL, NULL, NULL, NULL, NULL, 0);
392 /* initialize tries, catch memory errors */
393 if ((rc = initialize_tries()) != 0) {
394 return (rc);
397 initialize_tables(); /* no memory is allocated here */
398 initialize_ba_tables(); /* no memory is allocated here */
400 if ((rc = global_statinit()) != 0) { /* init global stats */
401 ipgpc0dbg(("ipgpc_initialize: global_statinit error " \
402 "%d", rc));
403 return (rc);
406 /* create default class */
407 bzero(&def_class, sizeof (ipgpc_class_t));
408 def_class.next_action = IPP_ACTION_CONT;
409 def_class.gather_stats = B_FALSE; /* don't gather stats by default */
410 (void) strcpy(def_class.class_name, "default");
411 def_class.originator = IPP_CONFIG_PERMANENT; /* label as permanent */
413 /* add default class and record default class id */
414 if ((rc = insertcid(&def_class, &ipgpc_def_class_id)) != ENOENT) {
415 ipgpc0dbg(("ipgpc_initialize: insert of default class failed" \
416 " with error %d", rc));
417 return (rc);
419 return (0);
423 * Module modify code
427 * name_hash(name, M)
429 * hash function for a string (name) of lenght M
431 unsigned
432 name_hash(char *name, size_t M)
434 unsigned h;
436 for (h = 0; *name != '\0'; name++) {
437 h = ((64 * h) + *name);
439 return ((h % M));
444 * ipgpc_filter_destructor(filter)
446 * frees any allocated memory pointed to in the filter structure
447 * this function should be run before freeing an ipgpc_filter_t
449 void
450 ipgpc_filter_destructor(ipgpc_filter_t *filter)
452 if (filter->filter_comment != NULL) {
453 kmem_free(filter->filter_comment,
454 (strlen(filter->filter_comment) + 1));
456 if (filter->saddr_hostname != NULL) {
457 kmem_free(filter->saddr_hostname,
458 (strlen(filter->saddr_hostname) + 1));
460 if (filter->daddr_hostname != NULL) {
461 kmem_free(filter->daddr_hostname,
462 (strlen(filter->daddr_hostname) + 1));
467 * filter_name2id(*out_id, name, filter_instance, in_num_filters)
469 * looks up name and instance in filter id table
470 * checks in_num_filters against max filter boundary
471 * if found, returns EEXIST and places the id in out_id
472 * if not found, returns ENOENT and places the new id in out_id
473 * if no additional filter ids are available, ENOMEM is returned
475 static int
476 filter_name2id(unsigned *out_id, char name[], int32_t filter_instance,
477 int in_num_filters)
479 unsigned h;
480 int dirty = -1; /* set dirty to not found */
482 if (in_num_filters >= ipgpc_max_num_filters) {
483 return (ENOSPC); /* will exceed maximum number of filters */
487 * search until fid w/ matching name is found or clean space is found
488 * if clean space is found, return first dirty space found or if
489 * none werer found, return clean space
491 h = name_hash(name, ipgpc_max_num_filters);
492 while ((ipgpc_fid_list[h].info != 0) &&
493 ((ipgpc_fid_list[h].filter.filter_instance != filter_instance) ||
494 (strcmp(name, ipgpc_fid_list[h].filter.filter_name) != 0))) {
495 if (dirty == -1) { /* this is the first dirty space */
496 if (ipgpc_fid_list[h].info == -1) { /* dirty */
497 dirty = h;
500 h = (h + 1) % ipgpc_max_num_filters;
503 * check to see if searching stopped because a clean spot was found
504 * and a dirty space was seen before
506 if ((dirty != -1) && (ipgpc_fid_list[h].info == 0)) {
507 *out_id = dirty;
508 return (ENOENT); /* name does not exist in table */
509 } else if (ipgpc_fid_list[h].info == 0) {
510 *out_id = h;
511 return (ENOENT); /* name does not exist in table */
512 } else {
513 *out_id = h;
514 if (ipgpc_fid_list[h].info == -1) {
515 return (ENOENT);
516 } else {
517 return (EEXIST); /* name exists in table */
523 * class_name2id(*out_id, name, in_num_classes)
525 * looks up name in class id table
526 * checks in_num_classes against max class boundry
527 * if found, returns EEXIST and places the id in out_id
528 * if not found, returns ENOENT and places the new id in out_id
529 * if no additional class ids are available, ENOSPC is returned
531 static int
532 class_name2id(unsigned *out_id, char name[], int in_num_classes)
534 unsigned h;
535 int dirty = -1; /* set dirty to not found */
537 if (in_num_classes >= ipgpc_max_num_classes) {
538 return (ENOSPC); /* will exceed maximum number of classes */
542 * search until cid w/ matching name is found or clean space is found
543 * if clean space is found, return first dirty space found or if
544 * none were found, return clean space
546 h = name_hash(name, ipgpc_max_num_classes);
547 while ((ipgpc_cid_list[h].info != 0) &&
548 (strcmp(name, ipgpc_cid_list[h].aclass.class_name) != 0)) {
549 if (dirty == -1) { /* this is the first dirty space */
550 if (ipgpc_cid_list[h].info == -1) { /* dirty */
551 dirty = h;
554 h = (h + 1) % ipgpc_max_num_classes;
557 * check to see if searching stopped because a clean spot was found
558 * and a dirty space was seen before
560 if ((dirty != -1) && (ipgpc_cid_list[h].info == 0)) {
561 *out_id = dirty;
562 return (ENOENT); /* name does not exist in table */
563 } else if (ipgpc_cid_list[h].info == 0) {
564 *out_id = h;
565 return (ENOENT); /* name does not exist in table */
566 } else {
567 *out_id = h;
568 if (ipgpc_cid_list[h].info == -1) { /* name did exist */
569 return (ENOENT); /* name does not exist in table */
570 } else {
571 return (EEXIST); /* name exists in table */
577 * ipgpc_parse_filter(filter, nvlp)
579 * given a name value pair list, a filter structure is parsed. A valid
580 * filter must have a filter_name and originator id. Any value that is not
581 * present, will be given the default wildcard value for that selector
584 ipgpc_parse_filter(ipgpc_filter_t *filter, nvlist_t *nvlp)
586 uint_t nelem = 4; /* an IPv6 address is an uint32_t array[4] */
587 uint32_t *mask;
588 uint32_t *addr;
589 char *s;
590 int i;
591 in6_addr_t zeroaddr = IN6ADDR_ANY_INIT;
593 /* parse filter name */
594 if (nvlist_lookup_string(nvlp, CLASSIFIER_FILTER_NAME, &s) != 0) {
595 return (EINVAL); /* filter name is missing, error */
598 /* parse originator */
599 if (nvlist_lookup_uint32(nvlp, IPP_CONFIG_ORIGINATOR,
600 &filter->originator) != 0) {
601 ipgpc0dbg(("ipgpc_parse_filter: originator missing"));
602 return (EINVAL);
605 /* check for max name length */
606 if ((strlen(s) + 1) > MAXNAMELEN) {
607 ipgpc0dbg(("ipgpc_parse_filter: filter name length > " \
608 "MAXNAMELEN"));
609 return (EINVAL);
612 bcopy(s, filter->filter_name, (strlen(s) + 1));
614 /* parse uid */
615 if (nvlist_lookup_uint32(nvlp, IPGPC_UID, &filter->uid) != 0) {
616 filter->uid = (uid_t)IPGPC_WILDCARD;
619 /* parse projid */
620 if (nvlist_lookup_int32(nvlp, IPGPC_PROJID, &filter->projid) != 0) {
621 filter->projid = IPGPC_WILDCARD;
624 /* parse if_index */
625 if (nvlist_lookup_uint32(nvlp, IPGPC_IF_INDEX, &filter->if_index)
626 != 0) {
627 filter->if_index = 0;
630 /* parse direction */
631 if (nvlist_lookup_uint32(nvlp, IPGPC_DIR, &filter->direction) != 0) {
632 filter->direction = 0;
635 /* parse proto */
636 if (nvlist_lookup_byte(nvlp, IPGPC_PROTO, &filter->proto) != 0) {
637 filter->proto = 0;
641 * parse dsfield mask, if mask is present and dsfield value is not,
642 * then this is an invalid filter configuration
644 if (nvlist_lookup_byte(nvlp, IPGPC_DSFIELD_MASK, &filter->dsfield_mask)
645 == 0) {
646 /* parse dsfield */
647 if (nvlist_lookup_byte(nvlp, IPGPC_DSFIELD, &filter->dsfield)
648 != 0) {
649 ipgpc0dbg(("ipgpc_parse_filter: dsfield missing" \
650 " when dsfield_mask 0x%x is present",
651 filter->dsfield_mask));
652 return (EINVAL);
654 } else {
655 filter->dsfield_mask = 0;
656 /* check to see if user added dsfield, but not dsfield_mask */
657 if (nvlist_lookup_byte(nvlp, IPGPC_DSFIELD, &filter->dsfield)
658 == 0) {
659 ipgpc0dbg(("ipgpc_parse_filter: dsfield_mask missing" \
660 " when dsfield 0x%x is present",
661 filter->dsfield));
662 return (EINVAL);
664 filter->dsfield = 0;
667 /* parse source port */
668 if (nvlist_lookup_uint16(nvlp, IPGPC_SPORT, &filter->sport) != 0) {
669 filter->sport = 0;
673 * parse source port mask, mask and value must be present, or neither
675 if (nvlist_lookup_uint16(nvlp, IPGPC_SPORT_MASK, &filter->sport_mask)
676 != 0) {
677 if (filter->sport != 0) {
678 ipgpc0dbg(("ipgpc_parse_filter: sport_mask missing " \
679 "to mask sport %u", filter->sport));
680 return (EINVAL);
682 filter->sport_mask = 0;
683 } else { /* sport mask is present */
684 if (filter->sport == 0) {
685 ipgpc0dbg(("ipgpc_parse_filter: sport missing " \
686 "when sport_mask %u is present",
687 filter->sport_mask));
688 return (EINVAL);
692 /* check for non-continuous mask */
693 if (!iscontinuousmask(filter->sport_mask, BITLENGTH(uint16_t))) {
694 ipgpc0dbg(("ipgpc_parse_filter: sport_mask is " \
695 "non-continuous"));
696 return (EINVAL);
699 /* parse destination port */
700 if (nvlist_lookup_uint16(nvlp, IPGPC_DPORT, &filter->dport) != 0) {
701 filter->dport = 0;
705 * parse destination port mask, mask and value must be present,
706 * or neither
708 if (nvlist_lookup_uint16(nvlp, IPGPC_DPORT_MASK, &filter->dport_mask)
709 != 0) {
710 if (filter->dport != 0) {
711 ipgpc0dbg(("ipgpc_parse_filter: dport_mask missing " \
712 "to mask dport %u", filter->dport));
713 return (EINVAL);
715 filter->dport_mask = 0;
716 } else { /* dport mask is present */
717 if (filter->dport == 0) {
718 ipgpc0dbg(("ipgpc_parse_filter: dport missing " \
719 "when dport_mask %u is present",
720 filter->dport_mask));
721 return (EINVAL);
725 /* check for non-continuous mask */
726 if (!iscontinuousmask(filter->dport_mask, BITLENGTH(uint16_t))) {
727 ipgpc0dbg(("ipgpc_parse_filter: dport_mask is " \
728 "non-continuous"));
729 return (EINVAL);
732 /* parse precedence */
733 if (nvlist_lookup_uint32(nvlp, IPGPC_PRECEDENCE, &filter->precedence)
734 != 0) {
735 filter->precedence = UINT_MAX; /* worst precedence */
738 /* parse priority */
739 if (nvlist_lookup_uint32(nvlp, IPGPC_PRIORITY, &filter->priority)
740 != 0) {
741 filter->priority = 0; /* worst priority */
744 /* parse filter type */
745 if (nvlist_lookup_byte(nvlp, IPGPC_FILTER_TYPE, &filter->filter_type)
746 != 0) {
747 filter->filter_type = IPGPC_GENERIC_FLTR;
750 /* parse filter instance */
751 if (nvlist_lookup_int32(nvlp, IPGPC_FILTER_INSTANCE,
752 &filter->filter_instance) != 0) {
753 filter->filter_instance = -1;
756 /* parse filter private field */
757 if (nvlist_lookup_string(nvlp, IPGPC_FILTER_PRIVATE, &s) != 0) {
758 filter->filter_comment = NULL;
759 } else {
760 filter->filter_comment = kmem_alloc((strlen(s) + 1), KM_SLEEP);
761 (void) strcpy(filter->filter_comment, s);
765 * parse source address mask, if address is present, mask must be
766 * present
768 if (nvlist_lookup_uint32_array(nvlp, IPGPC_SADDR_MASK, &mask, &nelem)
769 != 0) {
770 /* check if source address is present */
771 if (nvlist_lookup_uint32_array(nvlp, IPGPC_SADDR, &addr,
772 &nelem) == 0) {
773 ipgpc0dbg(("ipgpc_parse_filter: source address mask " \
774 "missing"));
775 return (EINVAL);
776 } else { /* both saddr and saddr_mask absent */
777 bcopy(zeroaddr.s6_addr32, filter->saddr.s6_addr32,
778 sizeof (filter->saddr.s6_addr32));
780 bcopy(zeroaddr.s6_addr32, filter->saddr_mask.s6_addr32,
781 sizeof (filter->saddr_mask.s6_addr32));
782 } else { /* saddr_mask present */
783 /* parse source address */
784 if (nvlist_lookup_uint32_array(nvlp, IPGPC_SADDR, &addr,
785 &nelem) != 0) {
786 ipgpc0dbg(("ipgpc_parse_filter: source address " \
787 "missing"));
788 return (EINVAL);
789 } else { /* saddr present */
790 bcopy(addr, filter->saddr.s6_addr32,
791 sizeof (filter->saddr.s6_addr32));
793 bcopy(mask, filter->saddr_mask.s6_addr32,
794 sizeof (filter->saddr_mask.s6_addr32));
797 /* check for non-continuous mask */
798 if ((filter->filter_type == IPGPC_V6_FLTR) ||
799 (filter->filter_type == IPGPC_GENERIC_FLTR)) {
800 boolean_t zero_found = B_FALSE;
801 for (i = 0; i < 4; ++i) {
802 if (filter->saddr_mask.s6_addr32[i] == 0) {
803 zero_found = B_TRUE;
804 } else {
805 if (zero_found) {
806 ipgpc0dbg(("ipgpc_parse_filter: "
807 "saddr_mask is non-continuous"));
808 return (EINVAL);
811 if (!iscontinuousmask(filter->saddr_mask.s6_addr32[i],
812 IP_ABITS)) {
813 ipgpc0dbg(("ipgpc_parse_filter: saddr_mask " \
814 "is non-continuous"));
815 return (EINVAL);
818 } else { /* IPGPC_V4_FLTR */
819 if (!iscontinuousmask((V4_PART_OF_V6(filter->saddr_mask)),
820 IP_ABITS)) {
821 ipgpc0dbg(("ipgpc_parse_filter: saddr_mask is " \
822 "non-continuous"));
823 return (EINVAL);
827 /* parse source address hostname */
828 if (nvlist_lookup_string(nvlp, IPGPC_SADDR_HOSTNAME, &s) != 0) {
829 filter->saddr_hostname = NULL;
830 } else {
831 filter->saddr_hostname = kmem_alloc((strlen(s) + 1), KM_SLEEP);
832 (void) strcpy(filter->saddr_hostname, s);
836 * parse destination address mask, if address is present, mask must be
837 * present
839 if (nvlist_lookup_uint32_array(nvlp, IPGPC_DADDR_MASK, &mask, &nelem)
840 != 0) {
841 /* check if destination address is present */
842 if (nvlist_lookup_uint32_array(nvlp, IPGPC_DADDR, &addr,
843 &nelem) == 0) {
844 ipgpc0dbg(("ipgpc_parse_filter: destination address " \
845 "mask missing"));
846 return (EINVAL);
847 } else { /* both daddr and daddr_mask absent */
848 bcopy(zeroaddr.s6_addr32, filter->daddr.s6_addr32,
849 sizeof (filter->daddr.s6_addr32));
851 bcopy(zeroaddr.s6_addr32, filter->daddr_mask.s6_addr32,
852 sizeof (filter->daddr_mask.s6_addr32));
853 } else { /* daddr_mask present */
854 /* parse destination address */
855 if (nvlist_lookup_uint32_array(nvlp, IPGPC_DADDR, &addr,
856 &nelem) != 0) {
857 ipgpc0dbg(("ipgpc_parse_filter: destination address " \
858 "missing"));
859 return (EINVAL);
860 } else { /* daddr present */
861 bcopy(addr, filter->daddr.s6_addr32,
862 sizeof (filter->daddr.s6_addr32));
864 bcopy(mask, filter->daddr_mask.s6_addr32,
865 sizeof (filter->daddr_mask.s6_addr32));
868 /* check for non-continuous mask */
869 if ((filter->filter_type == IPGPC_V6_FLTR) ||
870 (filter->filter_type == IPGPC_GENERIC_FLTR)) {
871 boolean_t zero_found = B_FALSE;
872 for (i = 0; i < 4; ++i) {
873 if (filter->daddr_mask.s6_addr32[i] == 0) {
874 zero_found = B_TRUE;
875 } else {
876 if (zero_found) {
877 ipgpc0dbg(("ipgpc_parse_filter: "
878 "daddr_mask is non-continuous"));
879 return (EINVAL);
882 if (!iscontinuousmask(filter->daddr_mask.s6_addr32[i],
883 IP_ABITS)) {
884 ipgpc0dbg(("ipgpc_parse_filter: daddr_mask " \
885 "is non-continuous"));
886 return (EINVAL);
889 } else { /* IPGPC_V4_FLTR */
890 if (!iscontinuousmask((V4_PART_OF_V6(filter->daddr_mask)),
891 IP_ABITS)) {
892 ipgpc0dbg(("ipgpc_parse_filter: daddr_mask is " \
893 "non-continuous"));
894 return (EINVAL);
898 /* parse destination address hostname */
899 if (nvlist_lookup_string(nvlp, IPGPC_DADDR_HOSTNAME, &s) != 0) {
900 filter->daddr_hostname = NULL;
901 } else {
902 filter->daddr_hostname = kmem_alloc((strlen(s) + 1), KM_SLEEP);
903 (void) strcpy(filter->daddr_hostname, s);
906 return (0);
910 * iscontinuousmask(mask, len)
912 * Searches a given mask of length len from MSB to LSB looking for a zero
913 * bit followed by one bit. A continuous mask must be a string of zero or
914 * more ones followed by a string of zero or more zeros, which would return
915 * B_TRUE. Otherwise, it is not continuous and this function returns B_FALSE.
917 static boolean_t
918 iscontinuousmask(uint32_t mask, uint8_t len)
920 uint8_t pos;
921 boolean_t zero_found = B_FALSE;
923 for (pos = len; pos > 0; --pos) {
924 if (EXTRACTBIT(mask, (pos - 1), len) == 0) {
925 zero_found = B_TRUE;
926 } else {
927 if (zero_found) {
928 return (B_FALSE);
932 return (B_TRUE);
937 * insertfid(filter_id, filter, class_id)
939 * creates a filter id (fid) structure for filter with filter_id.
940 * filter is associated with the input class id
941 * it is assumed that a fid will not be inserted for a filter that already
942 * exists by the same name.
944 static void
945 insertfid(int filter_id, ipgpc_filter_t *filter, uint_t class_id)
947 ipgpc_fid_list[filter_id].info = 1;
948 ipgpc3dbg(("insert_fid: adding filter %s to class %s",
949 filter->filter_name,
950 ipgpc_cid_list[class_id].aclass.class_name));
951 ipgpc_fid_list[filter_id].class_id = class_id;
952 ipgpc_fid_list[filter_id].filter = *filter;
953 ipgpc_fid_list[filter_id].insert_map = 0;
957 static void
958 common_addfilter(fid_t *fid, int filter_id)
960 /* start trie inserts */
961 /* add source port selector */
962 if (t_insert(&ipgpc_trie_list[IPGPC_TRIE_SPORTID], filter_id,
963 fid->filter.sport, fid->filter.sport_mask) == NORMAL_VALUE) {
964 fid->insert_map |= SPORT_MASK;
966 /* add destination port selector */
967 if (t_insert(&ipgpc_trie_list[IPGPC_TRIE_DPORTID], filter_id,
968 fid->filter.dport, fid->filter.dport_mask) == NORMAL_VALUE) {
969 fid->insert_map |= DPORT_MASK;
971 /* end trie inserts */
973 /* add diffserv field selector */
974 mutex_enter(&ipgpc_ds_table_id.lock);
975 if (ba_insert(&ipgpc_ds_table_id, filter_id, fid->filter.dsfield,
976 fid->filter.dsfield_mask) == NORMAL_VALUE) {
977 fid->insert_map |= DS_MASK;
979 mutex_exit(&ipgpc_ds_table_id.lock);
981 /* start table inserts */
982 mutex_enter(&ipgpc_table_list_lock);
983 /* add protocol selector */
984 if (ht_insert(&ipgpc_table_list[PROTOID_IDX], filter_id,
985 fid->filter.proto) == NORMAL_VALUE) {
986 fid->insert_map |= PROTO_MASK;
989 /* add UID selector */
990 if (ht_insert(&ipgpc_table_list[UID_IDX], filter_id, fid->filter.uid)
991 == NORMAL_VALUE) {
992 fid->insert_map |= UID_MASK;
995 /* add PROJID selector */
996 if (ht_insert(&ipgpc_table_list[PROJID_IDX], filter_id,
997 fid->filter.projid) == NORMAL_VALUE) {
998 fid->insert_map |= PROJID_MASK;
1001 /* add interface index selector */
1002 if (ht_insert(&ipgpc_table_list[IF_IDX], filter_id,
1003 fid->filter.if_index) == NORMAL_VALUE) {
1004 fid->insert_map |= IF_MASK;
1007 /* add direction selector */
1008 if (ht_insert(&ipgpc_table_list[DIR_IDX], filter_id,
1009 fid->filter.direction) == NORMAL_VALUE) {
1010 fid->insert_map |= DIR_MASK;
1012 mutex_exit(&ipgpc_table_list_lock);
1013 /* end table inserts */
1016 static void
1017 v4_addfilter(fid_t *fid, int filter_id)
1019 /* add IPv4 source address selector */
1020 if (t_insert(&ipgpc_trie_list[IPGPC_TRIE_SADDRID], filter_id,
1021 V4_PART_OF_V6(fid->filter.saddr),
1022 V4_PART_OF_V6(fid->filter.saddr_mask)) == NORMAL_VALUE) {
1023 fid->insert_map |= SADDR_MASK;
1026 /* add IPv4 destination address selector */
1027 if (t_insert(&ipgpc_trie_list[IPGPC_TRIE_DADDRID], filter_id,
1028 V4_PART_OF_V6(fid->filter.daddr),
1029 V4_PART_OF_V6(fid->filter.daddr_mask)) == NORMAL_VALUE) {
1030 fid->insert_map |= DADDR_MASK;
1034 static void
1035 v6_addfilter(fid_t *fid, int filter_id)
1037 /* add IPv6 source address selector */
1038 if (t_insert6(&ipgpc_trie_list[IPGPC_TRIE_SADDRID6], filter_id,
1039 fid->filter.saddr, fid->filter.saddr_mask) == NORMAL_VALUE) {
1040 fid->insert_map |= SADDR6_MASK;
1043 /* add IPv6 destination address selector */
1044 if (t_insert6(&ipgpc_trie_list[IPGPC_TRIE_DADDRID6], filter_id,
1045 fid->filter.daddr, fid->filter.daddr_mask) == NORMAL_VALUE) {
1046 fid->insert_map |= DADDR6_MASK;
1051 * ipgpc_addfilter(filter, class_name, flags)
1053 * add the specified filter and associate it with the specified class
1054 * name
1055 * - add filter id to filter list
1056 * - add filter keys to selector structures
1057 * - ENOENT is returned if class does not exist
1058 * - EEXIST is returned if add failed because filter name exists
1059 * - ENOMEM is returned if no memory is available to add a new filter
1060 * - EINVAL if filter.filter_type is invalid
1061 * - 0 is returned on success
1062 * flags is unused currently
1064 /* ARGSUSED1 */
1066 ipgpc_addfilter(ipgpc_filter_t *filter, char *class_name, ipp_flags_t flags)
1068 unsigned filter_id;
1069 int err = 0;
1070 fid_t *fid;
1071 unsigned class_id;
1073 err = class_name2id(&class_id, class_name, ipgpc_num_cls);
1074 if (err != EEXIST) {
1075 ipgpc0dbg(("ipgpc_addfilter: class lookup error %d", err));
1076 return (err);
1078 mutex_enter(&ipgpc_fid_list_lock);
1079 /* make sure filter does not already exist */
1080 if ((err = filter_name2id(&filter_id, filter->filter_name,
1081 filter->filter_instance, ipgpc_num_fltrs + 1)) == EEXIST) {
1082 ipgpc0dbg(("ipgpc_addfilter: filter name %s already exists",
1083 filter->filter_name));
1084 mutex_exit(&ipgpc_fid_list_lock);
1085 return (err);
1086 } else if (err == ENOSPC) {
1087 ipgpc0dbg(("ipgpc_addfilter: can not add filter %s, " \
1088 "ipgpc_max_num_filteres has been reached",
1089 filter->filter_name));
1090 mutex_exit(&ipgpc_fid_list_lock);
1091 return (err);
1093 insertfid(filter_id, filter, class_id);
1095 fid = &ipgpc_fid_list[filter_id];
1096 /* add filter id to selector structures */
1097 switch (fid->filter.filter_type) {
1098 case IPGPC_GENERIC_FLTR:
1099 /* add filter id to all selectors */
1100 common_addfilter(fid, filter_id);
1101 v4_addfilter(fid, filter_id);
1102 v6_addfilter(fid, filter_id);
1103 break;
1104 case IPGPC_V4_FLTR:
1105 /* add filter to common and V4 selectors */
1106 common_addfilter(fid, filter_id);
1107 v4_addfilter(fid, filter_id);
1108 break;
1109 case IPGPC_V6_FLTR:
1110 /* add filter to common and V6 selectors */
1111 common_addfilter(fid, filter_id);
1112 v6_addfilter(fid, filter_id);
1113 break;
1114 default:
1115 ipgpc0dbg(("ipgpc_addfilter(): invalid filter type %d",
1116 fid->filter.filter_type));
1117 mutex_exit(&ipgpc_fid_list_lock);
1118 return (EINVAL);
1120 /* check to see if this is a catch all filter, which we reject */
1121 if (fid->insert_map == 0) {
1122 ipgpc0dbg(("ipgpc_addfilter(): filter %s rejected because " \
1123 "catch all filters are not supported\n",
1124 filter->filter_name));
1125 /* cleanup what we allocated */
1126 /* remove filter from filter list */
1127 ipgpc_fid_list[filter_id].info = -1;
1128 ipgpc_fid_list[filter_id].filter.filter_name[0] = '\0';
1129 reset_dontcare_stats(); /* need to fixup stats */
1130 mutex_exit(&ipgpc_fid_list_lock);
1131 return (EINVAL);
1132 } else { /* associate filter with class */
1133 mutex_enter(&ipgpc_cid_list_lock);
1134 (void) ipgpc_list_insert(&ipgpc_cid_list[class_id].filter_list,
1135 filter_id);
1136 mutex_exit(&ipgpc_cid_list_lock);
1138 mutex_exit(&ipgpc_fid_list_lock);
1139 atomic_inc_ulong(&ipgpc_num_fltrs);
1140 ipgpc3dbg(("ipgpc_addfilter: adding filter %s", filter->filter_name));
1141 return (0);
1145 * reset_dontcare_stats()
1147 * when an insertion fails because zero selectors are specified in a filter
1148 * the number of dontcare's recorded for each selector structure needs to be
1149 * decremented
1151 static void
1152 reset_dontcare_stats(void)
1154 int i;
1156 for (i = 0; i < NUM_TRIES; ++i) {
1157 atomic_dec_32(&ipgpc_trie_list[i].stats.num_dontcare);
1159 for (i = 0; i < NUM_TABLES; ++i) {
1160 atomic_dec_32(&ipgpc_table_list[i].stats.num_dontcare);
1162 atomic_dec_32(&ipgpc_ds_table_id.stats.num_dontcare);
1166 * ipgpc_parse_class(out_class, nvlp)
1168 * Given a name value pair list, a class structure will be parsed.
1169 * To be a valid class, the class name, originator id and next action name
1170 * must be present. gather_stats is optional, if absent default value is used
1173 ipgpc_parse_class(ipgpc_class_t *out_class, nvlist_t *nvlp)
1175 char *name;
1176 size_t name_len;
1177 uint32_t gather_stats;
1179 /* parse class name */
1180 if (nvlist_lookup_string(nvlp, CLASSIFIER_CLASS_NAME, &name) != 0) {
1181 return (EINVAL); /* class name missing, error */
1184 name_len = strlen(name);
1185 /* check for max name length */
1186 if ((name_len + 1) > MAXNAMELEN) {
1187 ipgpc0dbg(("ipgpc_parse_class: class name length > " \
1188 "MAXNAMELEN"));
1189 return (EINVAL);
1192 bcopy(name, out_class->class_name, (name_len + 1));
1194 /* parse originator */
1195 if (nvlist_lookup_uint32(nvlp, IPP_CONFIG_ORIGINATOR,
1196 &out_class->originator) != 0) {
1197 ipgpc0dbg(("ipgpc_parse_class: originator missing"));
1198 return (EINVAL);
1201 /* parse action name */
1202 if (nvlist_lookup_string(nvlp, CLASSIFIER_NEXT_ACTION, &name) != 0) {
1203 return (EINVAL); /* action name missing, error */
1205 if ((out_class->next_action = ipp_action_lookup(name))
1206 == IPP_ACTION_INVAL) {
1207 ipgpc0dbg(("ipgpc_parse_class: invalid action name %s", name));
1208 return (EINVAL);
1211 /* parse gather stats boolean */
1212 if (nvlist_lookup_uint32(nvlp, CLASSIFIER_CLASS_STATS_ENABLE,
1213 &gather_stats) != 0) {
1214 /* stats turned off by default */
1215 out_class->gather_stats = B_FALSE;
1216 } else {
1217 out_class->gather_stats = (boolean_t)gather_stats;
1219 return (0);
1224 * ipgpc_addclass(in_class, flags)
1226 * adds the given class to the class id list.
1227 * - EEXIST is returned if class of same name already exists
1228 * - ENOSPC if there is no more available memory to add class
1229 * - 0 for success
1230 * flags is currently unused
1232 /* ARGSUSED */
1234 ipgpc_addclass(ipgpc_class_t *in_class, ipp_flags_t flags) {
1235 int class_id;
1236 int err;
1238 if ((err = insertcid(in_class, &class_id)) == EEXIST) {
1239 ipgpc0dbg(("ipgpc_addclass: class name %s already exists",
1240 in_class->class_name));
1241 return (err);
1242 } else if (err == ENOSPC) {
1243 ipgpc0dbg(("ipgpc_addclass: can not add class %s, " \
1244 "ipgpc_max_num_classes has been reached",
1245 in_class->class_name));
1246 return (err);
1248 /* add reference to next action */
1249 if ((err = ipp_action_ref(ipgpc_aid, in_class->next_action, 0)) != 0) {
1251 * the action id we want to reference must have been
1252 * destroyed before we could reference it. remove class
1253 * and fail.
1255 removecid(class_id);
1256 return (err);
1258 return (0);
1264 * class_statinit(in_class, in_class_id)
1266 * for the given class, create stats entries to record
1267 * - next action id
1268 * - number of bytes that matched this class
1269 * - number of packets that matched this class
1270 * - time in hrtime of last match for this class
1271 * any failures are returned, zero on success
1273 static int
1274 class_statinit(ipgpc_class_t *in_class, int in_class_id)
1276 int rc;
1277 ipp_stat_t *ipp_cl_stats;
1278 classstats_t *clsnames = NULL;
1280 /* create stat structure */
1281 if ((rc = ipp_stat_create(ipgpc_aid, in_class->class_name, 3,
1282 update_class_stats, &ipgpc_cid_list[in_class_id].stats,
1283 &ipp_cl_stats)) != 0) {
1284 ipgpc0dbg(("class_statinit: error creating ipp_stat entry"));
1285 return (rc);
1288 ASSERT(ipp_cl_stats != NULL);
1289 clsnames = (classstats_t *)ipp_cl_stats->ipps_data;
1290 ASSERT(clsnames != NULL);
1292 /* create stats entry */
1293 bzero(&ipgpc_cid_list[in_class_id].stats,
1294 sizeof (ipgpc_class_stats_t));
1296 /* set next action id */
1297 ipgpc_cid_list[in_class_id].stats.next_action =
1298 ipgpc_cid_list[in_class_id].aclass.next_action;
1300 if ((rc = ipp_stat_named_init(ipp_cl_stats, "nbytes",
1301 IPP_STAT_UINT64, &clsnames->nbytes)) != 0) {
1302 return (rc);
1304 if ((rc = ipp_stat_named_init(ipp_cl_stats, "npackets",
1305 IPP_STAT_UINT64, &clsnames->npackets)) != 0) {
1306 return (rc);
1308 if ((rc = ipp_stat_named_init(ipp_cl_stats, "last_match",
1309 IPP_STAT_INT64, &clsnames->last_match)) != 0) {
1310 return (rc);
1313 /* make reference to kstat structure, for removal */
1314 ipgpc_cid_list[in_class_id].cl_stats = ipp_cl_stats;
1315 ipp_stat_install(ipp_cl_stats);
1316 return (0);
1320 * insertcid(in_class, out_class_id)
1322 * creates a class id (cid) structure for in_class, if in_class name
1323 * does not exist already. id is associated with in_class. the internal
1324 * id of the cid associated with in_class is returned in out_class_id
1325 * - ENOENT is returned if in_class->class_name does not already exist
1326 * - EEXIST is returned if in_class->class_name does already exist
1327 * - ENOSPC is returned if by adding this class, the ipgpc_max_num_classes
1328 * will be exceeded.
1330 static int
1331 insertcid(ipgpc_class_t *in_class, int *out_class_id)
1333 int err, rc;
1334 unsigned class_id;
1336 mutex_enter(&ipgpc_cid_list_lock);
1337 /* see if entry already exists for class */
1338 if ((err = class_name2id(&class_id, in_class->class_name,
1339 ipgpc_num_cls + 1)) == ENOENT) {
1340 /* create new filter list for new class */
1341 ipgpc_cid_list[class_id].info = 1;
1342 ipgpc_cid_list[class_id].aclass = *in_class;
1343 if (in_class->gather_stats == B_TRUE) {
1344 /* init kstat entry */
1345 if ((rc = class_statinit(in_class, class_id)) != 0) {
1346 ipgpc_cid_list[class_id].info = -1;
1347 ipgpc0dbg(("insertcid: "
1348 "class_statinit failed with error %d", rc));
1349 mutex_exit(&ipgpc_cid_list_lock);
1350 return (rc);
1352 } else {
1353 ipgpc_cid_list[class_id].cl_stats = NULL;
1355 ipgpc3dbg(("insertcid: adding class %s",
1356 in_class->class_name));
1357 bcopy(in_class->class_name,
1358 ipgpc_cid_list[class_id].aclass.class_name, MAXNAMELEN);
1359 ipgpc_cid_list[class_id].filter_list = NULL;
1360 atomic_inc_ulong(&ipgpc_num_cls);
1361 } else {
1362 ipgpc0dbg(("insertcid: class name lookup error %d", err));
1363 mutex_exit(&ipgpc_cid_list_lock);
1364 return (err);
1366 mutex_exit(&ipgpc_cid_list_lock);
1367 *out_class_id = class_id;
1368 return (err);
1372 * common_removefilter(in_filter_id, fid)
1374 * removes in_filter_id from each of the common selector structures
1376 static void
1377 common_removefilter(int in_filter_id, fid_t *fid)
1379 /* start trie removes */
1380 t_remove(&ipgpc_trie_list[IPGPC_TRIE_SPORTID], in_filter_id,
1381 fid->filter.sport, fid->filter.sport_mask);
1382 /* remove id from destination port trie */
1383 t_remove(&ipgpc_trie_list[IPGPC_TRIE_DPORTID], in_filter_id,
1384 fid->filter.dport, fid->filter.dport_mask);
1385 /* end trie revmoves */
1387 /* remove id from DiffServ field ba table */
1388 mutex_enter(&ipgpc_ds_table_id.lock);
1389 ba_remove(&ipgpc_ds_table_id, in_filter_id, fid->filter.dsfield,
1390 fid->filter.dsfield_mask);
1391 mutex_exit(&ipgpc_ds_table_id.lock);
1393 /* start table removes */
1394 mutex_enter(&ipgpc_table_list_lock);
1395 /* remove id from protocol table */
1396 ht_remove(&ipgpc_table_list[PROTOID_IDX], in_filter_id,
1397 fid->filter.proto);
1398 /* remove id from UID table */
1399 ht_remove(&ipgpc_table_list[UID_IDX], in_filter_id, fid->filter.uid);
1400 /* remove id from PROJID table */
1401 ht_remove(&ipgpc_table_list[PROJID_IDX], in_filter_id,
1402 fid->filter.projid);
1403 /* remove id from interface id table */
1404 ht_remove(&ipgpc_table_list[IF_IDX], in_filter_id,
1405 fid->filter.if_index);
1406 /* remove id from direction table */
1407 ht_remove(&ipgpc_table_list[DIR_IDX], in_filter_id,
1408 fid->filter.direction);
1409 mutex_exit(&ipgpc_table_list_lock);
1410 /* end table removes */
1414 * v4_removefilter(in_filter_id, fid)
1416 * removes id from IPV4 specific structures
1418 static void
1419 v4_removefilter(int in_filter_id, fid_t *fid)
1421 /* remove id from source address trie */
1422 t_remove(&ipgpc_trie_list[IPGPC_TRIE_SADDRID], in_filter_id,
1423 V4_PART_OF_V6(fid->filter.saddr),
1424 V4_PART_OF_V6(fid->filter.saddr_mask));
1425 /* remove id from destination address trie */
1426 t_remove(&ipgpc_trie_list[IPGPC_TRIE_DADDRID], in_filter_id,
1427 V4_PART_OF_V6(fid->filter.daddr),
1428 V4_PART_OF_V6(fid->filter.daddr_mask));
1432 * v6_removefilter(in_filter_id, fid)
1434 * removes id from IPV6 specific structures
1436 static void
1437 v6_removefilter(int in_filter_id, fid_t *fid)
1439 /* remove id from source address trie */
1440 t_remove6(&ipgpc_trie_list[IPGPC_TRIE_SADDRID6], in_filter_id,
1441 fid->filter.saddr, fid->filter.saddr_mask);
1442 /* remove id from destination address trie */
1443 t_remove6(&ipgpc_trie_list[IPGPC_TRIE_DADDRID6], in_filter_id,
1444 fid->filter.daddr, fid->filter.daddr_mask);
1448 * ipgpc_removefilter(filter_name, filter_instance, flags)
1450 * remove the filter associated with the specified name and instance
1451 * - remove filter keys from all search tries
1452 * - remove from filter id list
1453 * - ENOENT is returned if filter name does not exist
1454 * - returns 0 on success
1456 /* ARGSUSED */
1458 ipgpc_removefilter(char *filter_name, int32_t filter_instance,
1459 ipp_flags_t flags)
1461 unsigned filter_id;
1462 fid_t *fid;
1463 int rc;
1465 /* check to see if any filters are loaded */
1466 if (ipgpc_num_fltrs == 0) {
1467 return (ENOENT);
1470 mutex_enter(&ipgpc_fid_list_lock);
1471 /* lookup filter name, only existing filters can be removed */
1472 if ((rc = filter_name2id(&filter_id, filter_name, filter_instance,
1473 ipgpc_num_fltrs)) != EEXIST) {
1474 mutex_exit(&ipgpc_fid_list_lock);
1475 return (rc);
1477 fid = &ipgpc_fid_list[filter_id];
1478 switch (fid->filter.filter_type) {
1479 case IPGPC_GENERIC_FLTR:
1480 common_removefilter(filter_id, fid);
1481 v4_removefilter(filter_id, fid);
1482 v6_removefilter(filter_id, fid);
1483 break;
1484 case IPGPC_V4_FLTR:
1485 common_removefilter(filter_id, fid);
1486 v4_removefilter(filter_id, fid);
1487 break;
1488 case IPGPC_V6_FLTR:
1489 common_removefilter(filter_id, fid);
1490 v6_removefilter(filter_id, fid);
1491 break;
1492 default:
1493 ipgpc0dbg(("ipgpc_removefilter(): invalid filter type %d",
1494 fid->filter.filter_type));
1495 mutex_exit(&ipgpc_fid_list_lock);
1496 return (EINVAL);
1498 /* remove filter from filter list */
1499 ipgpc_fid_list[filter_id].info = -1;
1500 ipgpc_fid_list[filter_id].insert_map = 0;
1501 ipgpc_fid_list[filter_id].filter.filter_name[0] = '\0';
1502 ipgpc_filter_destructor(&ipgpc_fid_list[filter_id].filter);
1503 mutex_exit(&ipgpc_fid_list_lock);
1504 /* remove filter id from class' list of filters */
1505 remove_from_cid_filter_list(ipgpc_fid_list[filter_id].class_id,
1506 filter_id);
1507 atomic_dec_ulong(&ipgpc_num_fltrs);
1508 return (0);
1512 * removecid(in_class_id)
1514 * removes the cid entry from the cid list and frees allocated structures
1516 static void
1517 removecid(int in_class_id)
1519 ipgpc_cid_list[in_class_id].info = -1;
1520 ipgpc_cid_list[in_class_id].aclass.class_name[0] = '\0';
1521 ipgpc_cid_list[in_class_id].aclass.next_action = -1;
1522 /* delete kstat entry */
1523 if (ipgpc_cid_list[in_class_id].cl_stats != NULL) {
1524 ipp_stat_destroy(ipgpc_cid_list[in_class_id].cl_stats);
1525 ipgpc_cid_list[in_class_id].cl_stats = NULL;
1527 /* decrement total number of classes loaded */
1528 atomic_dec_ulong(&ipgpc_num_cls);
1532 * remove_from_cid_filter_list(in_class_id, in_filter_id)
1534 * removes the input filter_id from the filter_list of the class associated
1535 * with the input class_id
1537 static void
1538 remove_from_cid_filter_list(int in_class_id, int in_filter_id)
1540 cid_t *cid = &ipgpc_cid_list[in_class_id];
1542 if (cid->filter_list != NULL) {
1543 (void) ipgpc_list_remove(&cid->filter_list, in_filter_id);
1548 * ipgpc_removeclass(class_name)
1550 * removes a class and all the filters that point to it (ouch!)
1551 * - returns 0 on success
1552 * - ENOENT if class name does not exist
1553 * - ENOTSUP if class name equals 'default'
1556 ipgpc_removeclass(char *class_name, ipp_flags_t flags)
1558 unsigned class_id;
1559 element_node_t *anode = NULL;
1560 element_node_t *tnode = NULL;
1561 fid_t *fid = NULL;
1562 ipp_action_id_t old_next_action;
1563 int rc;
1565 /* check to see if any classes are loaded */
1566 if (ipgpc_num_cls == 0) {
1567 return (ENOENT);
1570 mutex_enter(&ipgpc_cid_list_lock); /* set lock */
1571 /* lookup class name, only classes that exist can be removed */
1572 if ((rc = class_name2id(&class_id, class_name, (ipgpc_num_cls - 1)))
1573 != EEXIST) {
1574 mutex_exit(&ipgpc_cid_list_lock); /* release lock */
1575 return (rc);
1577 if (class_id == ipgpc_def_class_id) {
1578 ipgpc0dbg(("ipgpc_removeclass(): default class may not be " \
1579 "removed"));
1580 mutex_exit(&ipgpc_cid_list_lock); /* release lock */
1581 return (ENOTSUP);
1584 old_next_action = ipgpc_cid_list[class_id].aclass.next_action;
1585 anode = ipgpc_cid_list[class_id].filter_list;
1586 while (anode != NULL) {
1587 fid = &ipgpc_fid_list[anode->id];
1588 if (ipgpc_fid_list[anode->id].info > 0) {
1589 anode = anode->next;
1590 (void) ipgpc_removefilter(fid->filter.filter_name,
1591 fid->filter.filter_instance, flags);
1592 } else {
1593 tnode = anode;
1594 anode = anode->next;
1595 /* free this node */
1596 kmem_cache_free(element_node_cache, tnode);
1599 /* remove cid from ipgpc_cid_list and decrement ipgpc_num_cls */
1600 ipgpc3dbg(("ipgpc_removeclass: class %s has been removed",
1601 class_name));
1602 removecid(class_id);
1603 mutex_exit(&ipgpc_cid_list_lock); /* release lock */
1604 rc = ipp_action_unref(ipgpc_aid, old_next_action, flags);
1605 ASSERT(rc == 0);
1606 return (0);
1610 * ipgpc_modifyfilter(nvlist, flags)
1612 * modifies the input filter
1613 * - if in_class != NULL, filter is associated with that class
1614 * - EINVAL is returned if filter name does not exist in nvlist
1615 * - if filter->filter_name does not exist ENOENT is returned
1616 * - if a class name to associate with is not present in nvlist, then the
1617 * previous class association is used
1620 ipgpc_modifyfilter(nvlist_t **nvlpp, ipp_flags_t flags)
1622 unsigned filter_id;
1623 int ret = 0;
1624 int rc;
1625 ipgpc_filter_t *filter;
1626 ipgpc_filter_t old_filter;
1627 char *name;
1628 char *s;
1629 uint_t class_id;
1631 filter = kmem_zalloc(sizeof (ipgpc_filter_t), KM_SLEEP);
1632 if ((ret = ipgpc_parse_filter(filter, *nvlpp)) != 0) {
1633 ipgpc0dbg(("ipgpc_modifyfilter: error %d parsing filter",
1634 ret));
1635 ipgpc_filter_destructor(filter);
1636 kmem_free(filter, sizeof (ipgpc_filter_t));
1637 return (ret);
1640 /* parse class name */
1641 if (nvlist_lookup_string(*nvlpp, CLASSIFIER_CLASS_NAME, &name)
1642 != 0) {
1643 name = NULL; /* no class specified */
1646 /* modify filter entry */
1647 if ((rc = filter_name2id(&filter_id, filter->filter_name,
1648 filter->filter_instance, ipgpc_num_fltrs)) == EEXIST) {
1649 if (name == NULL) {
1650 /* set class_name to previous class_name association */
1651 class_id = ipgpc_fid_list[filter_id].class_id;
1652 name = ipgpc_cid_list[class_id].aclass.class_name;
1653 } else {
1654 if ((ret = class_name2id(&class_id, name,
1655 ipgpc_num_cls)) != EEXIST) {
1656 ipgpc0dbg(("ipgpc_modifyfilter: class does " \
1657 "not exist"));
1658 ipgpc_filter_destructor(filter);
1659 kmem_free(filter, sizeof (ipgpc_filter_t));
1660 return (ret);
1663 /* copy out old filter just in case we need to revert */
1664 old_filter = ipgpc_fid_list[filter_id].filter;
1666 /* make copy of filter_comment */
1667 if (ipgpc_fid_list[filter_id].filter.filter_comment != NULL) {
1668 s = ipgpc_fid_list[filter_id].filter.filter_comment;
1669 old_filter.filter_comment =
1670 kmem_alloc((strlen(s) + 1), KM_SLEEP);
1671 (void) strcpy(old_filter.filter_comment, s);
1672 } else {
1673 old_filter.filter_comment = NULL;
1676 /* make copy of saddr_hostname */
1677 if (ipgpc_fid_list[filter_id].filter.saddr_hostname != NULL) {
1678 s = ipgpc_fid_list[filter_id].filter.saddr_hostname;
1679 old_filter.saddr_hostname =
1680 kmem_alloc((strlen(s) + 1), KM_SLEEP);
1681 (void) strcpy(old_filter.saddr_hostname, s);
1682 } else {
1683 old_filter.saddr_hostname = NULL;
1686 /* make copy of daddr_hostname */
1687 if (ipgpc_fid_list[filter_id].filter.daddr_hostname != NULL) {
1688 s = ipgpc_fid_list[filter_id].filter.daddr_hostname;
1689 old_filter.daddr_hostname =
1690 kmem_alloc((strlen(s) + 1), KM_SLEEP);
1691 (void) strcpy(old_filter.daddr_hostname, s);
1692 } else {
1693 old_filter.daddr_hostname = NULL;
1696 /* remove old filter entry */
1697 ret = ipgpc_removefilter(filter->filter_name,
1698 filter->filter_instance, flags);
1699 if (ret == 0) { /* no error, add filter */
1700 ret = ipgpc_addfilter(filter, name, flags);
1701 if (ret != 0) {
1702 /* error occurred, free filter fields */
1703 ipgpc0dbg(("ipgpc_modifyfilter: invalid " \
1704 "filter given, unable to modify " \
1705 "existing filter %s",
1706 filter->filter_name));
1707 ipgpc_filter_destructor(filter);
1708 kmem_free(filter, sizeof (ipgpc_filter_t));
1709 /* revert back to old filter */
1710 (void) ipgpc_addfilter(&old_filter, name,
1711 flags);
1712 return (ret);
1714 ipgpc_filter_destructor(&old_filter);
1715 } else {
1716 ipgpc0dbg(("ipgpc_modifyfilter: error %d occurred " \
1717 "when modifying filter", ret));
1718 ipgpc_filter_destructor(&old_filter);
1719 ipgpc_filter_destructor(filter);
1720 kmem_free(filter, sizeof (ipgpc_filter_t));
1721 return (ret);
1723 } else {
1724 ipgpc0dbg(("ipgpc_modifyfilter: filter lookup error %d", rc));
1725 return (rc); /* filter name does not exist */
1727 kmem_free(filter, sizeof (ipgpc_filter_t));
1728 return (0);
1732 * ipgpc_modifyclass(in_class)
1734 * if the input class exists, then the action list is modified
1735 * if the input class does not exist, ENOENT is returned
1737 /* ARGSUSED */
1739 ipgpc_modifyclass(nvlist_t **nvlpp, ipp_flags_t flags)
1741 unsigned class_id;
1742 ipgpc_class_t in_class;
1743 char *name;
1744 int rc;
1745 uint32_t gather_stats;
1746 boolean_t ref_action = B_FALSE;
1747 ipp_action_id_t old_next_action;
1748 size_t name_len;
1750 /* parse class name */
1751 if (nvlist_lookup_string(*nvlpp, CLASSIFIER_CLASS_NAME, &name) != 0) {
1752 return (EINVAL); /* class name missing, error */
1754 name_len = strlen(name);
1755 /* check for max name length */
1756 if ((name_len + 1) > MAXNAMELEN) {
1757 ipgpc0dbg(("ipgpc_modifyclass: class name length > " \
1758 "MAXNAMELEN"));
1759 return (EINVAL);
1761 bcopy(name, in_class.class_name, (name_len + 1));
1763 mutex_enter(&ipgpc_cid_list_lock);
1764 /* look up class name, only existing classes can be modified */
1765 if ((rc = class_name2id(&class_id, in_class.class_name,
1766 ipgpc_num_cls)) == EEXIST) {
1767 /* preserve previous config if values are absent */
1768 /* parse action name */
1769 old_next_action = ipgpc_cid_list[class_id].aclass.next_action;
1770 if (nvlist_lookup_string(*nvlpp, CLASSIFIER_NEXT_ACTION, &name)
1771 != 0) {
1772 /* use previous config */
1773 in_class.next_action = old_next_action;
1774 } else { /* next action name present */
1775 if ((in_class.next_action = ipp_action_lookup(name))
1776 == IPP_ACTION_INVAL) {
1777 ipgpc0dbg(("ipgpc_modifyclass: invalid " \
1778 "action name %s", name));
1779 mutex_exit(&ipgpc_cid_list_lock);
1780 return (EINVAL); /* this is an error */
1782 ref_action = B_TRUE;
1784 /* parse gather stats byte */
1785 if (nvlist_lookup_uint32(*nvlpp, CLASSIFIER_CLASS_STATS_ENABLE,
1786 &gather_stats) != 0) {
1787 /* use previous config */
1788 in_class.gather_stats =
1789 ipgpc_cid_list[class_id].aclass.gather_stats;
1790 } else {
1791 in_class.gather_stats = (boolean_t)gather_stats;
1793 /* check to see if gather_stats booleans differ */
1794 if ((ipgpc_cid_list[class_id].aclass.gather_stats !=
1795 in_class.gather_stats)) {
1796 if (ipgpc_cid_list[class_id].aclass.gather_stats) {
1797 /* delete kstat entry */
1798 if (ipgpc_cid_list[class_id].cl_stats != NULL) {
1799 ipp_stat_destroy(
1800 ipgpc_cid_list[class_id].cl_stats);
1801 ipgpc_cid_list[class_id].cl_stats =
1802 NULL;
1804 } else { /* gather_stats == B_FALSE */
1805 if ((rc = class_statinit(&in_class, class_id))
1806 != 0) {
1807 ipgpc0dbg(("ipgpc_modifyclass: " \
1808 "class_statinit failed with " \
1809 "error %d", rc));
1810 mutex_exit(&ipgpc_cid_list_lock);
1811 return (rc);
1815 mutex_exit(&ipgpc_cid_list_lock);
1816 /* check if next_action was modified */
1817 if (ref_action == B_TRUE) {
1818 if ((rc = ipp_action_ref(ipgpc_aid,
1819 in_class.next_action, 0)) != 0) {
1820 ipgpc0dbg(("ipgpc_modifyclass: error " \
1821 "occurred while adding a reference to " \
1822 "the new next_action %d",
1823 in_class.next_action));
1824 mutex_exit(&ipgpc_cid_list_lock);
1825 return (rc);
1827 /* fix up references */
1828 rc = ipp_action_unref(ipgpc_aid, old_next_action,
1829 flags);
1830 ASSERT(rc == 0);
1832 /* preserve originator id */
1833 in_class.originator =
1834 ipgpc_cid_list[class_id].aclass.originator;
1835 ipgpc_cid_list[class_id].aclass = in_class;
1836 ipgpc_cid_list[class_id].stats.next_action =
1837 in_class.next_action;
1838 } else {
1839 ipgpc0dbg(("ipgpc_modifyclass: class name lookup error %d",
1840 rc));
1841 mutex_exit(&ipgpc_cid_list_lock);
1842 return (rc);
1844 return (0);
1849 * ipgpc_list_insert(listpp, id)
1851 * inserts an item, id, into the list, if item exists EEXIST is returned
1854 ipgpc_list_insert(linked_list *listpp, key_t id)
1856 element_node_t *p;
1858 if (*listpp == NULL) {
1859 *listpp = kmem_cache_alloc(element_node_cache, KM_SLEEP);
1860 (*listpp)->element_refcnt = 1;
1861 (*listpp)->next = NULL;
1862 (*listpp)->id = id;
1863 } else {
1864 for (p = *listpp; p->next != NULL; p = p->next) {
1865 if (p->id == id) {
1866 (*p->element_ref)(p);
1867 return (EEXIST);
1870 if (p->id == id) {
1871 (*p->element_ref)(p);
1872 return (EEXIST);
1873 } else {
1874 p->next =
1875 kmem_cache_alloc(element_node_cache, KM_SLEEP);
1876 p->next->element_refcnt = 1;
1877 p->next->next = NULL;
1878 p = p->next;
1879 p->id = id;
1882 return (0);
1886 * ipgpc_list_remove(listpp, id)
1888 * removes an item, id, from the list if it exists and returns TRUE or FALSE
1889 * if not removed
1891 boolean_t
1892 ipgpc_list_remove(element_node_t **listpp, key_t id)
1894 element_node_t *p = NULL;
1895 element_node_t *t = NULL;
1897 if (*listpp == NULL) {
1898 return (B_FALSE);
1900 if ((*listpp)->id == id) {
1901 p = *listpp;
1902 if ((*listpp)->element_refcnt == 1) {
1903 *listpp = (*listpp)->next;
1905 (*p->element_unref)(p);
1906 return (B_TRUE);
1907 } else if ((*listpp)->next != NULL) {
1908 /* linear search for matching id */
1909 for (p = *listpp; p->next != NULL; p = p->next) {
1910 if (p->next->id == id) {
1911 t = p->next;
1912 if (p->next->element_refcnt == 1) {
1913 p->next = p->next->next;
1915 (*t->element_unref)(t);
1916 return (B_TRUE);
1920 return (B_FALSE);
1924 * Module destroy code
1927 static void
1928 removeclasses(ipp_flags_t flags)
1930 int i;
1932 for (i = 0; i < ipgpc_max_num_classes; ++i) {
1933 if (ipgpc_cid_list[i].info > 0) {
1934 (void) ipgpc_removeclass(
1935 ipgpc_cid_list[i].aclass.class_name, flags);
1940 static void
1941 freetriev6nodes(node_t **inNode)
1943 node_t *anode = *inNode;
1944 node_t *tnode;
1945 node_t *s[130]; /* stack of previous nodes */
1946 int prev_link[130]; /* stack of what the previous link was */
1947 int sp = 0;
1948 node_t *root = *inNode; /* pointer to root node */
1950 s[sp] = NULL;
1951 prev_link[sp] = -1;
1952 /* loop until only the root node remains */
1953 while (!((root->zero == NULL) && (root->one == NULL))) {
1954 if (anode->zero != NULL) { /* check zero node */
1955 tnode = anode;
1956 anode = anode->zero;
1957 s[++sp] = tnode; /* put node on stack */
1958 prev_link[sp] = 0;
1959 } else if (anode->one != NULL) { /* check one node */
1960 tnode = anode;
1961 anode = anode->one;
1962 s[++sp] = tnode; /* put node on stack */
1963 prev_link[sp] = 1;
1964 } else { /* leaf node reached */
1965 /* free leaf node and pop the stack */
1966 kmem_cache_free(trie_node_cache, anode);
1967 anode = s[sp];
1968 if (prev_link[sp--] == 0) {
1969 anode->zero = NULL;
1970 } else {
1971 anode->one = NULL;
1973 if (anode == NULL) {
1974 return;
1981 void
1982 ipgpc_destroy(ipp_flags_t flags)
1984 int i;
1985 int rc;
1986 element_node_t *anode = NULL;
1987 element_node_t *tnode = NULL;
1988 fid_t *fid = NULL;
1990 /* check to see if default class id was set */
1991 if (ipgpc_def_class_id != -1) {
1992 ipp_action_id_t next_action =
1993 ipgpc_cid_list[ipgpc_def_class_id].aclass.next_action;
1995 /* unreference default_class->next_action */
1996 rc = ipp_action_unref(ipgpc_aid, next_action, flags);
1997 ASSERT(rc == 0);
1998 /* removing filter associated with the default class */
1999 anode = ipgpc_cid_list[ipgpc_def_class_id].filter_list;
2000 while (anode != NULL) {
2001 fid = &ipgpc_fid_list[anode->id];
2002 if (ipgpc_fid_list[anode->id].info > 0) {
2003 anode = anode->next;
2004 (void) ipgpc_removefilter(
2005 fid->filter.filter_name,
2006 fid->filter.filter_instance, flags);
2007 } else {
2008 tnode = anode;
2009 anode = anode->next;
2010 /* free this node */
2011 kmem_cache_free(element_node_cache, tnode);
2014 ASSERT(ipgpc_cid_list[ipgpc_def_class_id].filter_list == NULL);
2015 removecid(ipgpc_def_class_id);
2016 ASSERT(ipgpc_cid_list[ipgpc_def_class_id].info == -1);
2017 ipgpc_def_class_id = -1;
2019 /* remove stats entries */
2020 if (ipgpc_global_stats != NULL) {
2021 /* destroy global stats */
2022 ipp_stat_destroy(ipgpc_global_stats);
2023 ipgpc_global_stats = NULL;
2027 * remove all classes, which will remove all filters, stats and
2028 * selectors
2030 if (ipgpc_cid_list != NULL) {
2031 removeclasses(flags);
2032 kmem_free(ipgpc_cid_list,
2033 sizeof (cid_t) * ipgpc_max_num_classes);
2034 ipgpc_cid_list = NULL;
2036 /* all filters and classes should have been removed at this point */
2037 ASSERT((ipgpc_num_cls == 0) && (ipgpc_num_fltrs == 0));
2039 /* free filter id list structure */
2040 if (ipgpc_fid_list != NULL) {
2041 kmem_free(ipgpc_fid_list,
2042 sizeof (fid_t) * ipgpc_max_num_filters);
2043 ipgpc_fid_list = NULL;
2047 * IPv6 address tries don't implement path compression or node
2048 * deletions, like v4/port tries. All allocated nodes must be freed
2049 * before trie root node is destroyed
2051 if (ipgpc_trie_list[IPGPC_TRIE_SADDRID6].trie != NULL) {
2052 freetriev6nodes(&ipgpc_trie_list[IPGPC_TRIE_SADDRID6].trie);
2053 /* free trie root */
2054 kmem_cache_free(trie_node_cache,
2055 ipgpc_trie_list[IPGPC_TRIE_SADDRID6].trie);
2056 /* destroy lock */
2057 rw_destroy(&ipgpc_trie_list[IPGPC_TRIE_SADDRID6].rw_lock);
2058 ipgpc_trie_list[IPGPC_TRIE_SADDRID6].trie = NULL;
2060 if (ipgpc_trie_list[IPGPC_TRIE_DADDRID6].trie != NULL) {
2061 freetriev6nodes(&ipgpc_trie_list[IPGPC_TRIE_DADDRID6].trie);
2062 /* free trie root */
2063 kmem_cache_free(trie_node_cache,
2064 ipgpc_trie_list[IPGPC_TRIE_DADDRID6].trie);
2065 /* destroy lock */
2066 rw_destroy(&ipgpc_trie_list[IPGPC_TRIE_DADDRID6].rw_lock);
2067 ipgpc_trie_list[IPGPC_TRIE_DADDRID6].trie = NULL;
2070 /* free remaining tries structures */
2071 for (i = 0; i < (NUM_TRIES - 2); ++i) {
2072 if (ipgpc_trie_list[i].trie != NULL) {
2073 /* free trie root */
2074 kmem_cache_free(trie_node_cache,
2075 ipgpc_trie_list[i].trie);
2076 /* destroy lock */
2077 rw_destroy(&ipgpc_trie_list[i].rw_lock);
2078 ipgpc_trie_list[i].trie = NULL;
2082 /* destroy caches */
2083 if (ht_node_cache != NULL) {
2084 kmem_cache_destroy(ht_node_cache);
2085 ht_node_cache = NULL;
2087 if (trie_node_cache != NULL) {
2088 kmem_cache_destroy(trie_node_cache);
2089 trie_node_cache = NULL;
2091 if (element_node_cache != NULL) {
2092 kmem_cache_destroy(element_node_cache);
2093 element_node_cache = NULL;
2095 if (ht_match_cache != NULL) {
2096 kmem_cache_destroy(ht_match_cache);
2097 ht_match_cache = NULL;
2102 * Module info code
2106 * ipgpc_params_info(fn, arg)
2108 * allocates, builds and passes an nvlist to fn with arg
2111 ipgpc_params_info(int (*fn)(nvlist_t *, void *), void *arg)
2113 nvlist_t *nvlp;
2114 int rc;
2116 /* allocate nvlist to be passed back */
2117 if ((rc = nvlist_alloc(&nvlp, NV_UNIQUE_NAME, KM_NOSLEEP)) != 0) {
2118 return (rc);
2121 /* add config type */
2122 if ((rc = nvlist_add_byte(nvlp, IPP_CONFIG_TYPE, IPP_SET)) != 0) {
2123 nvlist_free(nvlp);
2124 return (rc);
2127 /* add gather stats boolean */
2128 if ((rc = nvlist_add_uint32(nvlp, IPP_ACTION_STATS_ENABLE,
2129 (uint32_t)ipgpc_gather_stats)) != 0) {
2130 nvlist_free(nvlp);
2131 return (rc);
2134 /* call back with nvlist */
2135 rc = fn(nvlp, arg);
2137 nvlist_free(nvlp);
2139 return (rc);
2143 * build_class_nvlist(nvlpp, in_class)
2145 * build an nvlist based on in_class
2146 * if isdefault, add apporiate configuration type to nvlpp
2148 static int
2149 build_class_nvlist(nvlist_t **nvlpp, ipgpc_class_t *in_class,
2150 boolean_t isdefault)
2152 nvlist_t *nvlp = *nvlpp;
2153 char *next_action;
2154 int rc;
2157 * add configuration type
2158 * if class is the default class, config type should be
2159 * CLASSIFIER_MODIFY_CLASS
2160 * otherwise it should be CLASSIFIER_ADD_CLASS
2162 /* add config type */
2163 if ((rc = nvlist_add_byte(nvlp, IPP_CONFIG_TYPE,
2164 ((isdefault) ? CLASSIFIER_MODIFY_CLASS : CLASSIFIER_ADD_CLASS)))
2165 != 0) {
2166 return (rc);
2169 /* add class name */
2170 if ((rc = nvlist_add_string(nvlp, CLASSIFIER_CLASS_NAME,
2171 in_class->class_name)) != 0) {
2172 return (rc);
2175 /* add originator */
2176 if ((rc = nvlist_add_uint32(nvlp, IPP_CONFIG_ORIGINATOR,
2177 in_class->originator)) != 0) {
2178 return (rc);
2181 /* look up next action name with next action id */
2182 if ((rc = ipp_action_name(in_class->next_action, &next_action)) != 0) {
2183 return (rc);
2186 /* add next action name */
2187 if ((rc = nvlist_add_string(nvlp, CLASSIFIER_NEXT_ACTION,
2188 next_action)) != 0) {
2189 kmem_free(next_action, (strlen(next_action) + 1));
2190 return (rc);
2193 kmem_free(next_action, (strlen(next_action) + 1));
2195 /* add gather stats boolean */
2196 if ((rc = nvlist_add_uint32(nvlp, CLASSIFIER_CLASS_STATS_ENABLE,
2197 (uint32_t)in_class->gather_stats)) != 0) {
2198 return (rc);
2201 return (0);
2206 * ipgpc_classes_info(fn, arg)
2208 * foreach class, allocate, build and pass an nvlist to fn with arg
2211 ipgpc_classes_info(int (*fn)(nvlist_t *, void *), void *arg)
2213 int i;
2214 int rc;
2215 nvlist_t *nvlp;
2217 for (i = 0; i < ipgpc_max_num_classes; ++i) {
2218 if (ipgpc_cid_list[i].info <= 0) {
2219 /* cid not allocated for this entry */
2220 continue;
2222 /* allocate an nvlist */
2223 if ((rc = nvlist_alloc(&nvlp, NV_UNIQUE_NAME, KM_NOSLEEP))
2224 != 0) {
2225 return (rc);
2227 /* build an nvlist for this particular class */
2228 if ((rc = (build_class_nvlist(&nvlp,
2229 &ipgpc_cid_list[i].aclass,
2230 ((i == ipgpc_def_class_id) ? B_TRUE : B_FALSE)))) != 0) {
2231 nvlist_free(nvlp);
2232 return (rc);
2234 /* call back with nvlist */
2235 if ((rc = fn(nvlp, arg)) != 0) {
2236 nvlist_free(nvlp);
2237 return (rc);
2240 nvlist_free(nvlp); /* free nvlist and continue */
2243 return (0);
2247 * build_filter_nvlist(nvlpp, in_filter, class_name)
2249 * build an nvlist based on in_filter and class_name.
2250 * Only non-wildcard/dontcare selectors are added to the nvlist.
2252 static int
2253 build_filter_nvlist(nvlist_t **nvlpp, ipgpc_filter_t *in_filter,
2254 char *class_name)
2256 nvlist_t *nvlp = *nvlpp;
2257 int rc;
2258 in6_addr_t zero_addr = IN6ADDR_ANY_INIT;
2260 /* add filter name */
2261 if ((rc = nvlist_add_string(nvlp, CLASSIFIER_FILTER_NAME,
2262 in_filter->filter_name)) != 0) {
2263 return (rc);
2266 /* add class name */
2267 if ((rc = nvlist_add_string(nvlp, CLASSIFIER_CLASS_NAME, class_name))
2268 != 0) {
2269 return (rc);
2272 /* add originator */
2273 if ((rc = nvlist_add_uint32(nvlp, IPP_CONFIG_ORIGINATOR,
2274 in_filter->originator)) != 0) {
2275 return (rc);
2278 /* add configuration type of CLASSIFIER_ADD_FILTER */
2279 if ((rc = nvlist_add_byte(nvlp, IPP_CONFIG_TYPE,
2280 CLASSIFIER_ADD_FILTER)) != 0) {
2281 return (rc);
2284 /* add uid */
2285 if (in_filter->uid != IPGPC_WILDCARD) {
2286 if ((rc = nvlist_add_uint32(nvlp, IPGPC_UID, in_filter->uid))
2287 != 0) {
2288 return (rc);
2292 /* add projid */
2293 if (in_filter->projid != IPGPC_WILDCARD) {
2294 if ((rc = nvlist_add_int32(nvlp, IPGPC_PROJID,
2295 in_filter->projid)) != 0) {
2296 return (rc);
2300 /* add interface index */
2301 if (in_filter->if_index != IPGPC_UNSPECIFIED) {
2302 if ((rc = nvlist_add_uint32(nvlp, IPGPC_IF_INDEX,
2303 in_filter->if_index)) != 0) {
2304 return (rc);
2308 /* add direction */
2309 if (in_filter->direction != IPGPC_UNSPECIFIED) {
2310 if ((rc = nvlist_add_uint32(nvlp, IPGPC_DIR,
2311 in_filter->direction)) != 0) {
2312 return (rc);
2316 /* add protocol */
2317 if (in_filter->proto != IPGPC_UNSPECIFIED) {
2318 if ((rc = nvlist_add_byte(nvlp, IPGPC_PROTO, in_filter->proto))
2319 != 0) {
2320 return (rc);
2324 /* add dsfield and mask */
2325 if (in_filter->dsfield_mask != 0) {
2326 if ((rc = nvlist_add_byte(nvlp, IPGPC_DSFIELD,
2327 in_filter->dsfield)) != 0) {
2328 return (rc);
2330 if ((rc = nvlist_add_byte(nvlp, IPGPC_DSFIELD_MASK,
2331 in_filter->dsfield_mask)) != 0) {
2332 return (rc);
2336 /* add source address, mask and hostname */
2337 if (!(IN6_ARE_ADDR_EQUAL(&in_filter->saddr_mask, &zero_addr))) {
2338 if ((rc = nvlist_add_uint32_array(nvlp, IPGPC_SADDR,
2339 in_filter->saddr.s6_addr32, 4)) != 0) {
2340 return (rc);
2343 if ((rc = nvlist_add_uint32_array(nvlp, IPGPC_SADDR_MASK,
2344 in_filter->saddr_mask.s6_addr32, 4)) != 0) {
2345 return (rc);
2348 if (in_filter->saddr_hostname != NULL) {
2349 if ((rc = nvlist_add_string(nvlp, IPGPC_SADDR_HOSTNAME,
2350 in_filter->saddr_hostname)) != 0) {
2351 return (rc);
2356 /* add destination address, mask and hostname */
2357 if (!(IN6_ARE_ADDR_EQUAL(&in_filter->daddr_mask, &zero_addr))) {
2358 if ((rc = nvlist_add_uint32_array(nvlp, IPGPC_DADDR,
2359 in_filter->daddr.s6_addr32, 4)) != 0) {
2360 return (rc);
2362 if ((rc = nvlist_add_uint32_array(nvlp, IPGPC_DADDR_MASK,
2363 in_filter->daddr_mask.s6_addr32, 4)) != 0) {
2364 return (rc);
2366 if (in_filter->daddr_hostname != NULL) {
2367 if ((rc = nvlist_add_string(nvlp, IPGPC_DADDR_HOSTNAME,
2368 in_filter->daddr_hostname)) != 0) {
2369 return (rc);
2374 /* add source port and mask */
2375 if (in_filter->sport_mask != 0) {
2376 if ((rc = nvlist_add_uint16(nvlp, IPGPC_SPORT,
2377 in_filter->sport)) != 0) {
2378 return (rc);
2380 if ((rc = nvlist_add_uint16(nvlp, IPGPC_SPORT_MASK,
2381 in_filter->sport_mask)) != 0) {
2382 return (rc);
2386 /* add destination port and mask */
2387 if (in_filter->dport_mask != 0) {
2388 if ((rc = nvlist_add_uint16(nvlp, IPGPC_DPORT,
2389 in_filter->dport)) != 0) {
2390 return (rc);
2392 if ((rc = nvlist_add_uint16(nvlp, IPGPC_DPORT_MASK,
2393 in_filter->dport_mask)) != 0) {
2394 return (rc);
2398 /* add precedence */
2399 if (in_filter->precedence != UINT_MAX) {
2400 if ((rc = nvlist_add_uint32(nvlp, IPGPC_PRECEDENCE,
2401 in_filter->precedence)) != 0) {
2402 return (rc);
2406 /* add priority */
2407 if (in_filter->priority != 0) {
2408 if ((rc = nvlist_add_uint32(nvlp, IPGPC_PRIORITY,
2409 in_filter->priority)) != 0) {
2410 return (rc);
2414 /* add filter type */
2415 if (in_filter->filter_type != IPGPC_GENERIC_FLTR) {
2416 if ((rc = nvlist_add_byte(nvlp, IPGPC_FILTER_TYPE,
2417 in_filter->filter_type)) != 0) {
2418 return (rc);
2422 /* add filter instance */
2423 if (in_filter->filter_instance != -1) {
2424 if ((rc = nvlist_add_int32(nvlp, IPGPC_FILTER_INSTANCE,
2425 in_filter->filter_instance)) != 0) {
2426 return (rc);
2430 /* add filter private field */
2431 if (in_filter->filter_comment != NULL) {
2432 if ((rc = nvlist_add_string(nvlp, IPGPC_FILTER_PRIVATE,
2433 in_filter->filter_comment)) != 0) {
2434 return (rc);
2438 return (0);
2442 * ipgpc_filters_info(fn, arg)
2444 * for each filter, allocate, build and pass an nvlist to fn with arg
2447 ipgpc_filters_info(int (*fn)(nvlist_t *, void *), void *arg)
2449 int i;
2450 int rc;
2451 nvlist_t *nvlp;
2452 int class_id;
2454 for (i = 0; i < ipgpc_max_num_filters; ++i) {
2455 if (ipgpc_fid_list[i].info <= 0) {
2456 /* fid not allocated for this entry */
2457 continue;
2459 /* allocate an nvlist */
2460 if ((rc = nvlist_alloc(&nvlp, NV_UNIQUE_NAME, KM_NOSLEEP))
2461 != 0) {
2462 return (rc);
2464 class_id = ipgpc_fid_list[i].class_id;
2465 /* build an nvlist for this particular filter */
2466 if ((rc = (build_filter_nvlist(&nvlp,
2467 &ipgpc_fid_list[i].filter,
2468 ipgpc_cid_list[class_id].aclass.class_name))) != 0) {
2469 nvlist_free(nvlp);
2470 return (rc);
2472 /* call back with nvlist */
2473 if ((rc = fn(nvlp, arg)) != 0) {
2474 nvlist_free(nvlp);
2475 return (rc);
2478 nvlist_free(nvlp); /* free nvlist and continue */
2480 return (0);
2484 * Module invoke code
2488 * ipgpc_findfilters(in_id, key, fid_table)
2490 * returns a list of matching filters for searching the given structure
2491 * associated with the input id with the input key
2492 * - returns DONTCARE_ONLY_MATCH if the selector structure described by
2493 * in_id contains only dontcares
2494 * - returns NO_MATCHES if no filters were found and no dontcares exist
2495 * for a given selector
2496 * - ENOMEM is returned if memory error occurs
2497 * - NORMAL_MATCH on success
2500 ipgpc_findfilters(int in_id, int key, ht_match_t *fid_table)
2502 int num_found = 0;
2504 if (in_id == IPGPC_BA_DSID) { /* special search for DSFIELD */
2505 if (ipgpc_ds_table_id.info.dontcareonly == B_TRUE) {
2506 /* trie is loaded with only DONTCARE(*) keys */
2507 return (DONTCARE_ONLY_MATCH);
2509 num_found = ba_retrieve(&ipgpc_ds_table_id, (uint8_t)key,
2510 fid_table);
2511 /* check to see if no matches were made */
2512 if ((num_found == 0) &&
2513 (ipgpc_ds_table_id.stats.num_dontcare == 0)) {
2514 return (NO_MATCHES);
2516 } else if (in_id >= TABLE_ID_OFFSET) { /* table to search */
2517 table_id_t *taid = &ipgpc_table_list[in_id - TABLE_ID_OFFSET];
2519 if (taid->info.dontcareonly == B_TRUE) {
2520 /* trie is loaded with only DONTCARE(*) keys */
2521 return (DONTCARE_ONLY_MATCH);
2523 num_found = ht_retrieve(taid, key, fid_table);
2524 /* check to see if no matches were made */
2525 if ((num_found == 0) && (taid->stats.num_dontcare == 0)) {
2526 return (NO_MATCHES);
2528 } else { /* trie to search */
2529 trie_id_t *tid = &ipgpc_trie_list[in_id];
2531 if (tid->info.dontcareonly == B_TRUE) {
2532 /* trie is loaded with only DONTCARE(*) keys */
2533 return (DONTCARE_ONLY_MATCH);
2535 /* search the trie for matches */
2536 num_found = t_retrieve(tid, key, fid_table);
2537 /* check to see if no matches were made */
2538 if ((num_found == 0) && (tid->stats.num_dontcare == 0)) {
2539 return (NO_MATCHES);
2542 if (num_found == -1) { /* num_found == -1 if memory error */
2543 return (ENOMEM);
2544 } else {
2545 return (NORMAL_MATCH);
2550 * ipgpc_findfilters6(in_id, key, fid_table)
2552 * findfilters specific to IPv6 traffic
2555 ipgpc_findfilters6(int in_id, in6_addr_t key, ht_match_t *fid_table)
2557 trie_id_t *tid = &ipgpc_trie_list[in_id];
2558 int num_found = 0;
2560 if (tid->info.dontcareonly == B_TRUE) {
2561 /* trie is loaded with only DONTCARE(*) keys */
2562 return (DONTCARE_ONLY_MATCH);
2564 /* search the trie for matches */
2565 num_found = t_retrieve6(tid, key, fid_table);
2566 /* check to see if no matches were made */
2567 if ((num_found == 0) && (tid->stats.num_dontcare == 0)) {
2568 return (NO_MATCHES);
2569 } else if (num_found == -1) { /* num_found == -1 if memory error */
2570 return (ENOMEM);
2571 } else {
2572 return (NORMAL_MATCH);
2577 * ht_match_insert(a, id, mask)
2579 * inserts id into table and applies mask to match_map
2580 * returns ENOMEM if can't allocate ht_match_t node, 0 otherwise
2582 static int
2583 ht_match_insert(ht_match_t *a, int id, uint16_t mask)
2585 int x = (id % HASH_SIZE); /* has for index */
2586 ht_match_t *p = NULL;
2588 if ((a[x].key == id) || (a[x].key == 0)) {
2589 a[x].key = id;
2590 a[x].match_map |= mask;
2591 } else if (a[x].next == NULL) {
2592 a[x].next = kmem_cache_alloc(ht_match_cache, KM_NOSLEEP);
2593 if (a[x].next == NULL) {
2594 ipgpc0dbg(("ht_match_insert(): kmem_cache_alloc " \
2595 "error"));
2596 return (ENOMEM);
2598 a[x].next->next = NULL;
2599 a[x].next->key = id;
2600 a[x].next->match_map = mask;
2601 } else {
2603 p = a[x].next;
2604 while (p != NULL) {
2605 if (p->key == id) {
2606 p->match_map |= mask;
2607 return (0);
2609 p = p->next;
2611 p = kmem_cache_alloc(ht_match_cache, KM_NOSLEEP);
2612 if (p == NULL) {
2613 ipgpc0dbg(("ht_match_insert(): kmem_cache_alloc " \
2614 "error"));
2615 return (ENOMEM);
2617 p->key = id;
2618 p->match_map = mask;
2619 p->next = a[x].next;
2620 a[x].next = p;
2622 return (0);
2626 * ipgpc_mark_found(mask, list, fid_table)
2628 * given a list of filter ids and a mask for the selector that is being marked,
2629 * the ids are inserted (or updated) in the fid_table to being marked as
2630 * matched for the given selector
2631 * return -1 if memory error
2634 ipgpc_mark_found(uint16_t mask, linked_list list, ht_match_t *fid_table)
2636 linked_list tnode = NULL;
2637 int num_found = 0;
2639 for (tnode = list; tnode != NULL; tnode = tnode->next) {
2640 /* apply the trie mask to the match map for this element */
2641 if (ipgpc_fid_list[tnode->id].info > 0) {
2642 if (ht_match_insert(fid_table, tnode->id, mask)
2643 == ENOMEM) {
2644 return (-1);
2646 ++num_found;
2649 return (num_found);
2652 /* updates global stats for ipgpc */
2653 /* ARGSUSED */
2654 static int
2655 update_global_stats(ipp_stat_t *sp, void *arg, int rw)
2657 globalstats_t *gbl_stats = (globalstats_t *)sp->ipps_data;
2658 uint32_t num_filters = (uint32_t)ipgpc_num_fltrs;
2659 uint32_t num_classes = (uint32_t)ipgpc_num_cls;
2661 ASSERT(gbl_stats != NULL);
2662 (void) ipp_stat_named_op(&gbl_stats->nfilters, &num_filters, rw);
2663 (void) ipp_stat_named_op(&gbl_stats->nclasses, &num_classes, rw);
2664 (void) ipp_stat_named_op(&gbl_stats->nbytes, &ipgpc_nbytes, rw);
2665 (void) ipp_stat_named_op(&gbl_stats->npackets, &ipgpc_npackets, rw);
2666 (void) ipp_stat_named_op(&gbl_stats->epackets, &ipgpc_epackets, rw);
2667 return (0);
2671 /* updates class stats for a specific class */
2672 static int
2673 update_class_stats(ipp_stat_t *sp, void *arg, int rw)
2675 ipgpc_class_stats_t *stats = (ipgpc_class_stats_t *)arg;
2676 classstats_t *cl_stats = (classstats_t *)sp->ipps_data;
2678 ASSERT(stats != NULL);
2679 ASSERT(cl_stats != NULL);
2680 (void) ipp_stat_named_op(&cl_stats->nbytes, &stats->nbytes, rw);
2681 (void) ipp_stat_named_op(&cl_stats->npackets, &stats->npackets, rw);
2682 (void) ipp_stat_named_op(&cl_stats->last_match, &stats->last_match, rw);
2683 return (0);