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]
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>
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)
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 */
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
127 * initializes global stats for ipgpc action module.
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
138 global_statinit(void)
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"));
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) {
159 if ((rc
= ipp_stat_named_init(ipgpc_global_stats
, "nclasses",
160 IPP_STAT_UINT32
, &gblsnames
->nclasses
)) != 0) {
163 if ((rc
= ipp_stat_named_init(ipgpc_global_stats
, "nbytes",
164 IPP_STAT_UINT64
, &gblsnames
->nbytes
)) != 0) {
167 if ((rc
= ipp_stat_named_init(ipgpc_global_stats
, "npackets",
168 IPP_STAT_UINT64
, &gblsnames
->npackets
)) != 0) {
171 if ((rc
= ipp_stat_named_init(ipgpc_global_stats
, "epackets",
172 IPP_STAT_UINT64
, &gblsnames
->epackets
)) != 0) {
175 ipp_stat_install(ipgpc_global_stats
);
180 insert_ipgpc_trie_list_info(int trie_id
, size_t key_len
, trie in_trie
,
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
;
191 initialize_tries(void)
193 /* IPv4 Source Address field structure */
194 if ((saddr_trie
= create_node(KM_NOSLEEP
)) == NULL
) {
197 saddr_trie
->isroot
= 1;
198 insert_ipgpc_trie_list_info(IPGPC_TRIE_SADDRID
, IP_ABITS
, saddr_trie
,
200 /* IPv4 Destination Address field structure */
201 if ((daddr_trie
= create_node(KM_NOSLEEP
)) == NULL
) {
204 daddr_trie
->isroot
= 1;
205 insert_ipgpc_trie_list_info(IPGPC_TRIE_DADDRID
, IP_ABITS
, daddr_trie
,
207 /* TCP Source Port field structure */
208 if ((sport_trie
= create_node(KM_NOSLEEP
)) == NULL
) {
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
) {
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
) {
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
) {
232 daddr6_trie
->isroot
= 1;
233 insert_ipgpc_trie_list_info(IPGPC_TRIE_DADDRID6
, IPV6_ABITS
,
234 daddr6_trie
, DADDR6_MASK
);
239 insert_ipgpc_table_list_info(int table_id
, hash_table table
, int wildcard
,
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
;
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
,
256 /* PROJID selector structure */
257 insert_ipgpc_table_list_info(PROJID_IDX
, projid_table
, IPGPC_WILDCARD
,
259 /* IF_INDEX selector structure */
260 insert_ipgpc_table_list_info(IF_IDX
, if_table
, IPGPC_UNSPECIFIED
,
262 /* DIR selector structure */
263 insert_ipgpc_table_list_info(DIR_IDX
, dir_table
, IPGPC_UNSPECIFIED
,
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
;
276 element_node_ref(element_node_t
*element
)
278 atomic_inc_32(&element
->element_refcnt
);
279 ASSERT(element
->element_refcnt
> 1);
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
);
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
;
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, \
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
;
321 int sizes
[] = IPGPC_PRIMES();
323 /* initialize globals */
324 ipgpc_aid
= in_aid
; /* store away action id for ipgpc */
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
) {
340 ipgpc0dbg(("ipgpc_initialize: ipgpc_max_filters " \
342 /* use the largest allowable value */
343 ipgpc_max_num_filters
= sizes
[(i
- 1)];
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
) {
356 ipgpc0dbg(("ipgpc_initialize: ipgpc_max_classes " \
358 /* use the largest allowable value */
359 ipgpc_max_num_classes
= sizes
[(i
- 1)];
361 ipgpc_max_num_classes
= sizes
[i
];
365 /* create filter id 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"));
373 /* create class id list */
374 ipgpc_cid_list
= kmem_zalloc(sizeof (cid_t
) * ipgpc_max_num_classes
,
376 if (ipgpc_cid_list
== NULL
) {
377 ipgpc0dbg(("ipgpc_initialize: failed to create cid list"));
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) {
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 " \
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
));
429 * hash function for a string (name) of lenght M
432 name_hash(char *name
, size_t M
)
436 for (h
= 0; *name
!= '\0'; name
++) {
437 h
= ((64 * h
) + *name
);
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
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
476 filter_name2id(unsigned *out_id
, char name
[], int32_t filter_instance
,
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 */
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)) {
508 return (ENOENT
); /* name does not exist in table */
509 } else if (ipgpc_fid_list
[h
].info
== 0) {
511 return (ENOENT
); /* name does not exist in table */
514 if (ipgpc_fid_list
[h
].info
== -1) {
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
532 class_name2id(unsigned *out_id
, char name
[], int in_num_classes
)
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 */
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)) {
562 return (ENOENT
); /* name does not exist in table */
563 } else if (ipgpc_cid_list
[h
].info
== 0) {
565 return (ENOENT
); /* name does not exist in table */
568 if (ipgpc_cid_list
[h
].info
== -1) { /* name did exist */
569 return (ENOENT
); /* name does not exist in table */
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] */
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"));
605 /* check for max name length */
606 if ((strlen(s
) + 1) > MAXNAMELEN
) {
607 ipgpc0dbg(("ipgpc_parse_filter: filter name length > " \
612 bcopy(s
, filter
->filter_name
, (strlen(s
) + 1));
615 if (nvlist_lookup_uint32(nvlp
, IPGPC_UID
, &filter
->uid
) != 0) {
616 filter
->uid
= (uid_t
)IPGPC_WILDCARD
;
620 if (nvlist_lookup_int32(nvlp
, IPGPC_PROJID
, &filter
->projid
) != 0) {
621 filter
->projid
= IPGPC_WILDCARD
;
625 if (nvlist_lookup_uint32(nvlp
, IPGPC_IF_INDEX
, &filter
->if_index
)
627 filter
->if_index
= 0;
630 /* parse direction */
631 if (nvlist_lookup_uint32(nvlp
, IPGPC_DIR
, &filter
->direction
) != 0) {
632 filter
->direction
= 0;
636 if (nvlist_lookup_byte(nvlp
, IPGPC_PROTO
, &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
)
647 if (nvlist_lookup_byte(nvlp
, IPGPC_DSFIELD
, &filter
->dsfield
)
649 ipgpc0dbg(("ipgpc_parse_filter: dsfield missing" \
650 " when dsfield_mask 0x%x is present",
651 filter
->dsfield_mask
));
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
)
659 ipgpc0dbg(("ipgpc_parse_filter: dsfield_mask missing" \
660 " when dsfield 0x%x is present",
667 /* parse source port */
668 if (nvlist_lookup_uint16(nvlp
, IPGPC_SPORT
, &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
)
677 if (filter
->sport
!= 0) {
678 ipgpc0dbg(("ipgpc_parse_filter: sport_mask missing " \
679 "to mask sport %u", filter
->sport
));
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
));
692 /* check for non-continuous mask */
693 if (!iscontinuousmask(filter
->sport_mask
, BITLENGTH(uint16_t))) {
694 ipgpc0dbg(("ipgpc_parse_filter: sport_mask is " \
699 /* parse destination port */
700 if (nvlist_lookup_uint16(nvlp
, IPGPC_DPORT
, &filter
->dport
) != 0) {
705 * parse destination port mask, mask and value must be present,
708 if (nvlist_lookup_uint16(nvlp
, IPGPC_DPORT_MASK
, &filter
->dport_mask
)
710 if (filter
->dport
!= 0) {
711 ipgpc0dbg(("ipgpc_parse_filter: dport_mask missing " \
712 "to mask dport %u", filter
->dport
));
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
));
725 /* check for non-continuous mask */
726 if (!iscontinuousmask(filter
->dport_mask
, BITLENGTH(uint16_t))) {
727 ipgpc0dbg(("ipgpc_parse_filter: dport_mask is " \
732 /* parse precedence */
733 if (nvlist_lookup_uint32(nvlp
, IPGPC_PRECEDENCE
, &filter
->precedence
)
735 filter
->precedence
= UINT_MAX
; /* worst precedence */
739 if (nvlist_lookup_uint32(nvlp
, IPGPC_PRIORITY
, &filter
->priority
)
741 filter
->priority
= 0; /* worst priority */
744 /* parse filter type */
745 if (nvlist_lookup_byte(nvlp
, IPGPC_FILTER_TYPE
, &filter
->filter_type
)
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
;
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
768 if (nvlist_lookup_uint32_array(nvlp
, IPGPC_SADDR_MASK
, &mask
, &nelem
)
770 /* check if source address is present */
771 if (nvlist_lookup_uint32_array(nvlp
, IPGPC_SADDR
, &addr
,
773 ipgpc0dbg(("ipgpc_parse_filter: source address mask " \
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
,
786 ipgpc0dbg(("ipgpc_parse_filter: source address " \
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) {
806 ipgpc0dbg(("ipgpc_parse_filter: "
807 "saddr_mask is non-continuous"));
811 if (!iscontinuousmask(filter
->saddr_mask
.s6_addr32
[i
],
813 ipgpc0dbg(("ipgpc_parse_filter: saddr_mask " \
814 "is non-continuous"));
818 } else { /* IPGPC_V4_FLTR */
819 if (!iscontinuousmask((V4_PART_OF_V6(filter
->saddr_mask
)),
821 ipgpc0dbg(("ipgpc_parse_filter: saddr_mask is " \
827 /* parse source address hostname */
828 if (nvlist_lookup_string(nvlp
, IPGPC_SADDR_HOSTNAME
, &s
) != 0) {
829 filter
->saddr_hostname
= NULL
;
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
839 if (nvlist_lookup_uint32_array(nvlp
, IPGPC_DADDR_MASK
, &mask
, &nelem
)
841 /* check if destination address is present */
842 if (nvlist_lookup_uint32_array(nvlp
, IPGPC_DADDR
, &addr
,
844 ipgpc0dbg(("ipgpc_parse_filter: destination address " \
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
,
857 ipgpc0dbg(("ipgpc_parse_filter: destination address " \
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) {
877 ipgpc0dbg(("ipgpc_parse_filter: "
878 "daddr_mask is non-continuous"));
882 if (!iscontinuousmask(filter
->daddr_mask
.s6_addr32
[i
],
884 ipgpc0dbg(("ipgpc_parse_filter: daddr_mask " \
885 "is non-continuous"));
889 } else { /* IPGPC_V4_FLTR */
890 if (!iscontinuousmask((V4_PART_OF_V6(filter
->daddr_mask
)),
892 ipgpc0dbg(("ipgpc_parse_filter: daddr_mask is " \
898 /* parse destination address hostname */
899 if (nvlist_lookup_string(nvlp
, IPGPC_DADDR_HOSTNAME
, &s
) != 0) {
900 filter
->daddr_hostname
= NULL
;
902 filter
->daddr_hostname
= kmem_alloc((strlen(s
) + 1), KM_SLEEP
);
903 (void) strcpy(filter
->daddr_hostname
, s
);
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.
918 iscontinuousmask(uint32_t mask
, uint8_t len
)
921 boolean_t zero_found
= B_FALSE
;
923 for (pos
= len
; pos
> 0; --pos
) {
924 if (EXTRACTBIT(mask
, (pos
- 1), len
) == 0) {
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.
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",
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;
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
)
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 */
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
;
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
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
1066 ipgpc_addfilter(ipgpc_filter_t
*filter
, char *class_name
, ipp_flags_t flags
)
1073 err
= class_name2id(&class_id
, class_name
, ipgpc_num_cls
);
1074 if (err
!= EEXIST
) {
1075 ipgpc0dbg(("ipgpc_addfilter: class lookup error %d", 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
);
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
);
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
);
1105 /* add filter to common and V4 selectors */
1106 common_addfilter(fid
, filter_id
);
1107 v4_addfilter(fid
, filter_id
);
1110 /* add filter to common and V6 selectors */
1111 common_addfilter(fid
, filter_id
);
1112 v6_addfilter(fid
, filter_id
);
1115 ipgpc0dbg(("ipgpc_addfilter(): invalid filter type %d",
1116 fid
->filter
.filter_type
));
1117 mutex_exit(&ipgpc_fid_list_lock
);
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
);
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
,
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
));
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
1152 reset_dontcare_stats(void)
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
)
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 > " \
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"));
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
));
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
;
1217 out_class
->gather_stats
= (boolean_t
)gather_stats
;
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
1230 * flags is currently unused
1234 ipgpc_addclass(ipgpc_class_t
*in_class
, ipp_flags_t flags
) {
1238 if ((err
= insertcid(in_class
, &class_id
)) == EEXIST
) {
1239 ipgpc0dbg(("ipgpc_addclass: class name %s already exists",
1240 in_class
->class_name
));
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
));
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
1255 removecid(class_id
);
1264 * class_statinit(in_class, in_class_id)
1266 * for the given class, create stats entries to record
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
1274 class_statinit(ipgpc_class_t
*in_class
, int in_class_id
)
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"));
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) {
1304 if ((rc
= ipp_stat_named_init(ipp_cl_stats
, "npackets",
1305 IPP_STAT_UINT64
, &clsnames
->npackets
)) != 0) {
1308 if ((rc
= ipp_stat_named_init(ipp_cl_stats
, "last_match",
1309 IPP_STAT_INT64
, &clsnames
->last_match
)) != 0) {
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
);
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
1331 insertcid(ipgpc_class_t
*in_class
, int *out_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
);
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
);
1362 ipgpc0dbg(("insertcid: class name lookup error %d", err
));
1363 mutex_exit(&ipgpc_cid_list_lock
);
1366 mutex_exit(&ipgpc_cid_list_lock
);
1367 *out_class_id
= class_id
;
1372 * common_removefilter(in_filter_id, fid)
1374 * removes in_filter_id from each of the common selector structures
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
,
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
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
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
1458 ipgpc_removefilter(char *filter_name
, int32_t filter_instance
,
1465 /* check to see if any filters are loaded */
1466 if (ipgpc_num_fltrs
== 0) {
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
);
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
);
1485 common_removefilter(filter_id
, fid
);
1486 v4_removefilter(filter_id
, fid
);
1489 common_removefilter(filter_id
, fid
);
1490 v6_removefilter(filter_id
, fid
);
1493 ipgpc0dbg(("ipgpc_removefilter(): invalid filter type %d",
1494 fid
->filter
.filter_type
));
1495 mutex_exit(&ipgpc_fid_list_lock
);
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
,
1507 atomic_dec_ulong(&ipgpc_num_fltrs
);
1512 * removecid(in_class_id)
1514 * removes the cid entry from the cid list and frees allocated structures
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
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
)
1559 element_node_t
*anode
= NULL
;
1560 element_node_t
*tnode
= NULL
;
1562 ipp_action_id_t old_next_action
;
1565 /* check to see if any classes are loaded */
1566 if (ipgpc_num_cls
== 0) {
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)))
1574 mutex_exit(&ipgpc_cid_list_lock
); /* release lock */
1577 if (class_id
== ipgpc_def_class_id
) {
1578 ipgpc0dbg(("ipgpc_removeclass(): default class may not be " \
1580 mutex_exit(&ipgpc_cid_list_lock
); /* release lock */
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
);
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",
1602 removecid(class_id
);
1603 mutex_exit(&ipgpc_cid_list_lock
); /* release lock */
1604 rc
= ipp_action_unref(ipgpc_aid
, old_next_action
, flags
);
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
)
1625 ipgpc_filter_t
*filter
;
1626 ipgpc_filter_t old_filter
;
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",
1635 ipgpc_filter_destructor(filter
);
1636 kmem_free(filter
, sizeof (ipgpc_filter_t
));
1640 /* parse class name */
1641 if (nvlist_lookup_string(*nvlpp
, CLASSIFIER_CLASS_NAME
, &name
)
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
) {
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
;
1654 if ((ret
= class_name2id(&class_id
, name
,
1655 ipgpc_num_cls
)) != EEXIST
) {
1656 ipgpc0dbg(("ipgpc_modifyfilter: class does " \
1658 ipgpc_filter_destructor(filter
);
1659 kmem_free(filter
, sizeof (ipgpc_filter_t
));
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
);
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
);
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
);
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
);
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
,
1714 ipgpc_filter_destructor(&old_filter
);
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
));
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
));
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
1739 ipgpc_modifyclass(nvlist_t
**nvlpp
, ipp_flags_t flags
)
1742 ipgpc_class_t in_class
;
1745 uint32_t gather_stats
;
1746 boolean_t ref_action
= B_FALSE
;
1747 ipp_action_id_t old_next_action
;
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 > " \
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
)
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
;
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
) {
1800 ipgpc_cid_list
[class_id
].cl_stats
);
1801 ipgpc_cid_list
[class_id
].cl_stats
=
1804 } else { /* gather_stats == B_FALSE */
1805 if ((rc
= class_statinit(&in_class
, class_id
))
1807 ipgpc0dbg(("ipgpc_modifyclass: " \
1808 "class_statinit failed with " \
1810 mutex_exit(&ipgpc_cid_list_lock
);
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
);
1827 /* fix up references */
1828 rc
= ipp_action_unref(ipgpc_aid
, old_next_action
,
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
;
1839 ipgpc0dbg(("ipgpc_modifyclass: class name lookup error %d",
1841 mutex_exit(&ipgpc_cid_list_lock
);
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
)
1858 if (*listpp
== NULL
) {
1859 *listpp
= kmem_cache_alloc(element_node_cache
, KM_SLEEP
);
1860 (*listpp
)->element_refcnt
= 1;
1861 (*listpp
)->next
= NULL
;
1864 for (p
= *listpp
; p
->next
!= NULL
; p
= p
->next
) {
1866 (*p
->element_ref
)(p
);
1871 (*p
->element_ref
)(p
);
1875 kmem_cache_alloc(element_node_cache
, KM_SLEEP
);
1876 p
->next
->element_refcnt
= 1;
1877 p
->next
->next
= NULL
;
1886 * ipgpc_list_remove(listpp, id)
1888 * removes an item, id, from the list if it exists and returns TRUE or FALSE
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
) {
1900 if ((*listpp
)->id
== id
) {
1902 if ((*listpp
)->element_refcnt
== 1) {
1903 *listpp
= (*listpp
)->next
;
1905 (*p
->element_unref
)(p
);
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
) {
1912 if (p
->next
->element_refcnt
== 1) {
1913 p
->next
= p
->next
->next
;
1915 (*t
->element_unref
)(t
);
1924 * Module destroy code
1928 removeclasses(ipp_flags_t flags
)
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
);
1941 freetriev6nodes(node_t
**inNode
)
1943 node_t
*anode
= *inNode
;
1945 node_t
*s
[130]; /* stack of previous nodes */
1946 int prev_link
[130]; /* stack of what the previous link was */
1948 node_t
*root
= *inNode
; /* pointer to root node */
1952 /* loop until only the root node remains */
1953 while (!((root
->zero
== NULL
) && (root
->one
== NULL
))) {
1954 if (anode
->zero
!= NULL
) { /* check zero node */
1956 anode
= anode
->zero
;
1957 s
[++sp
] = tnode
; /* put node on stack */
1959 } else if (anode
->one
!= NULL
) { /* check one node */
1962 s
[++sp
] = tnode
; /* put node on stack */
1964 } else { /* leaf node reached */
1965 /* free leaf node and pop the stack */
1966 kmem_cache_free(trie_node_cache
, anode
);
1968 if (prev_link
[sp
--] == 0) {
1973 if (anode
== NULL
) {
1982 ipgpc_destroy(ipp_flags_t flags
)
1986 element_node_t
*anode
= NULL
;
1987 element_node_t
*tnode
= 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
);
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
);
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
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
);
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
);
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
);
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
;
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
)
2116 /* allocate nvlist to be passed back */
2117 if ((rc
= nvlist_alloc(&nvlp
, NV_UNIQUE_NAME
, KM_NOSLEEP
)) != 0) {
2121 /* add config type */
2122 if ((rc
= nvlist_add_byte(nvlp
, IPP_CONFIG_TYPE
, IPP_SET
)) != 0) {
2127 /* add gather stats boolean */
2128 if ((rc
= nvlist_add_uint32(nvlp
, IPP_ACTION_STATS_ENABLE
,
2129 (uint32_t)ipgpc_gather_stats
)) != 0) {
2134 /* call back with nvlist */
2143 * build_class_nvlist(nvlpp, in_class)
2145 * build an nvlist based on in_class
2146 * if isdefault, add apporiate configuration type to nvlpp
2149 build_class_nvlist(nvlist_t
**nvlpp
, ipgpc_class_t
*in_class
,
2150 boolean_t isdefault
)
2152 nvlist_t
*nvlp
= *nvlpp
;
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
)))
2169 /* add class name */
2170 if ((rc
= nvlist_add_string(nvlp
, CLASSIFIER_CLASS_NAME
,
2171 in_class
->class_name
)) != 0) {
2175 /* add originator */
2176 if ((rc
= nvlist_add_uint32(nvlp
, IPP_CONFIG_ORIGINATOR
,
2177 in_class
->originator
)) != 0) {
2181 /* look up next action name with next action id */
2182 if ((rc
= ipp_action_name(in_class
->next_action
, &next_action
)) != 0) {
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));
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) {
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
)
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 */
2222 /* allocate an nvlist */
2223 if ((rc
= nvlist_alloc(&nvlp
, NV_UNIQUE_NAME
, KM_NOSLEEP
))
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) {
2234 /* call back with nvlist */
2235 if ((rc
= fn(nvlp
, arg
)) != 0) {
2240 nvlist_free(nvlp
); /* free nvlist and continue */
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.
2253 build_filter_nvlist(nvlist_t
**nvlpp
, ipgpc_filter_t
*in_filter
,
2256 nvlist_t
*nvlp
= *nvlpp
;
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) {
2266 /* add class name */
2267 if ((rc
= nvlist_add_string(nvlp
, CLASSIFIER_CLASS_NAME
, class_name
))
2272 /* add originator */
2273 if ((rc
= nvlist_add_uint32(nvlp
, IPP_CONFIG_ORIGINATOR
,
2274 in_filter
->originator
)) != 0) {
2278 /* add configuration type of CLASSIFIER_ADD_FILTER */
2279 if ((rc
= nvlist_add_byte(nvlp
, IPP_CONFIG_TYPE
,
2280 CLASSIFIER_ADD_FILTER
)) != 0) {
2285 if (in_filter
->uid
!= IPGPC_WILDCARD
) {
2286 if ((rc
= nvlist_add_uint32(nvlp
, IPGPC_UID
, in_filter
->uid
))
2293 if (in_filter
->projid
!= IPGPC_WILDCARD
) {
2294 if ((rc
= nvlist_add_int32(nvlp
, IPGPC_PROJID
,
2295 in_filter
->projid
)) != 0) {
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) {
2309 if (in_filter
->direction
!= IPGPC_UNSPECIFIED
) {
2310 if ((rc
= nvlist_add_uint32(nvlp
, IPGPC_DIR
,
2311 in_filter
->direction
)) != 0) {
2317 if (in_filter
->proto
!= IPGPC_UNSPECIFIED
) {
2318 if ((rc
= nvlist_add_byte(nvlp
, IPGPC_PROTO
, in_filter
->proto
))
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) {
2330 if ((rc
= nvlist_add_byte(nvlp
, IPGPC_DSFIELD_MASK
,
2331 in_filter
->dsfield_mask
)) != 0) {
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) {
2343 if ((rc
= nvlist_add_uint32_array(nvlp
, IPGPC_SADDR_MASK
,
2344 in_filter
->saddr_mask
.s6_addr32
, 4)) != 0) {
2348 if (in_filter
->saddr_hostname
!= NULL
) {
2349 if ((rc
= nvlist_add_string(nvlp
, IPGPC_SADDR_HOSTNAME
,
2350 in_filter
->saddr_hostname
)) != 0) {
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) {
2362 if ((rc
= nvlist_add_uint32_array(nvlp
, IPGPC_DADDR_MASK
,
2363 in_filter
->daddr_mask
.s6_addr32
, 4)) != 0) {
2366 if (in_filter
->daddr_hostname
!= NULL
) {
2367 if ((rc
= nvlist_add_string(nvlp
, IPGPC_DADDR_HOSTNAME
,
2368 in_filter
->daddr_hostname
)) != 0) {
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) {
2380 if ((rc
= nvlist_add_uint16(nvlp
, IPGPC_SPORT_MASK
,
2381 in_filter
->sport_mask
)) != 0) {
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) {
2392 if ((rc
= nvlist_add_uint16(nvlp
, IPGPC_DPORT_MASK
,
2393 in_filter
->dport_mask
)) != 0) {
2398 /* add precedence */
2399 if (in_filter
->precedence
!= UINT_MAX
) {
2400 if ((rc
= nvlist_add_uint32(nvlp
, IPGPC_PRECEDENCE
,
2401 in_filter
->precedence
)) != 0) {
2407 if (in_filter
->priority
!= 0) {
2408 if ((rc
= nvlist_add_uint32(nvlp
, IPGPC_PRIORITY
,
2409 in_filter
->priority
)) != 0) {
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) {
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) {
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) {
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
)
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 */
2459 /* allocate an nvlist */
2460 if ((rc
= nvlist_alloc(&nvlp
, NV_UNIQUE_NAME
, KM_NOSLEEP
))
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) {
2472 /* call back with nvlist */
2473 if ((rc
= fn(nvlp
, arg
)) != 0) {
2478 nvlist_free(nvlp
); /* free nvlist and continue */
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
)
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
,
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 */
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
];
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 */
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
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)) {
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 " \
2598 a
[x
].next
->next
= NULL
;
2599 a
[x
].next
->key
= id
;
2600 a
[x
].next
->match_map
= mask
;
2606 p
->match_map
|= mask
;
2611 p
= kmem_cache_alloc(ht_match_cache
, KM_NOSLEEP
);
2613 ipgpc0dbg(("ht_match_insert(): kmem_cache_alloc " \
2618 p
->match_map
= mask
;
2619 p
->next
= a
[x
].next
;
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
;
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
)
2652 /* updates global stats for ipgpc */
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
);
2671 /* updates class stats for a specific class */
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
);