xref: /illumos-gate/usr/src/uts/common/ipp/ipgpc/filters.c (revision 1a5e258f)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
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>
40 
41 /* Implementation for filter management and configuration support of ipgpc */
42 
43 #define	BITLENGTH(x) (sizeof (x) * NBBY)
44 
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 */
72 
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 */
86 
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 *);
118 
119 
120 /*
121  * Module initialization code
122  */
123 
124 /*
125  * global_statinit()
126  *
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
136  */
137 static int
global_statinit(void)138 global_statinit(void)
139 {
140 	int rc;
141 	globalstats_t *gblsnames = NULL;
142 
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);
148 	}
149 
150 	ASSERT(ipgpc_global_stats != NULL);
151 	gblsnames = (globalstats_t *)ipgpc_global_stats->ipps_data;
152 	ASSERT(gblsnames != NULL);
153 
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);
158 	}
159 	if ((rc = ipp_stat_named_init(ipgpc_global_stats, "nclasses",
160 	    IPP_STAT_UINT32, &gblsnames->nclasses)) != 0) {
161 		return (rc);
162 	}
163 	if ((rc = ipp_stat_named_init(ipgpc_global_stats, "nbytes",
164 	    IPP_STAT_UINT64, &gblsnames->nbytes)) != 0) {
165 		return (rc);
166 	}
167 	if ((rc = ipp_stat_named_init(ipgpc_global_stats, "npackets",
168 	    IPP_STAT_UINT64, &gblsnames->npackets)) != 0) {
169 		return (rc);
170 	}
171 	if ((rc = ipp_stat_named_init(ipgpc_global_stats, "epackets",
172 	    IPP_STAT_UINT64, &gblsnames->epackets)) != 0) {
173 		return (rc);
174 	}
175 	ipp_stat_install(ipgpc_global_stats);
176 	return (0);
177 }
178 
179 static void
insert_ipgpc_trie_list_info(int trie_id,size_t key_len,trie in_trie,uint16_t mask)180 insert_ipgpc_trie_list_info(int trie_id, size_t key_len, trie in_trie,
181     uint16_t mask)
182 {
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;
188 }
189 
190 static int
initialize_tries(void)191 initialize_tries(void)
192 {
193 	/* IPv4 Source Address field structure */
194 	if ((saddr_trie = create_node(KM_NOSLEEP)) == NULL) {
195 		return (ENOMEM);
196 	}
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);
203 	}
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);
210 	}
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);
217 	}
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);
224 	}
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);
231 	}
232 	daddr6_trie->isroot = 1;
233 	insert_ipgpc_trie_list_info(IPGPC_TRIE_DADDRID6, IPV6_ABITS,
234 	    daddr6_trie, DADDR6_MASK);
235 	return (0);
236 }
237 
238 static void
insert_ipgpc_table_list_info(int table_id,hash_table table,int wildcard,uint16_t mask)239 insert_ipgpc_table_list_info(int table_id, hash_table table, int wildcard,
240     uint16_t mask)
241 {
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;
246 }
247 static void
initialize_tables(void)248 initialize_tables(void)
249 {
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);
265 }
266 
267 static void
initialize_ba_tables(void)268 initialize_ba_tables(void)
269 {
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;
273 }
274 
275 static void
element_node_ref(element_node_t * element)276 element_node_ref(element_node_t *element)
277 {
278 	atomic_inc_32(&element->element_refcnt);
279 	ASSERT(element->element_refcnt > 1);
280 }
281 
282 static void
element_node_unref(element_node_t * element)283 element_node_unref(element_node_t *element)
284 {
285 	ASSERT(element->element_refcnt > 0);
286 	if (atomic_dec_32_nv(&element->element_refcnt) == 0) {
287 		kmem_cache_free(element_node_cache, element);
288 	}
289 }
290 
291 /* ARGSUSED1 */
292 static int
element_node_cache_constructor(void * buf,void * cdrarg,int kmflags)293 element_node_cache_constructor(void *buf, void *cdrarg, int kmflags)
294 {
295 	element_node_t *node = buf;
296 
297 	node->element_ref = element_node_ref;
298 	node->element_unref = element_node_unref;
299 	return (0);
300 }
301 
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}
308 
309 /*
310  * ipgpc_initialize(in_aid)
311  *
312  * initializes locks, data structures, configuration variables used and
313  * sets globals.  Will fail on memory or initialization error.
314  */
315 int
ipgpc_initialize(ipp_action_id_t in_aid)316 ipgpc_initialize(ipp_action_id_t in_aid)
317 {
318 	ipgpc_class_t def_class;
319 	int i;
320 	int rc;
321 	int sizes[] = IPGPC_PRIMES();
322 
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;
330 
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;
337 			}
338 		}
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];
346 		}
347 	}
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;
353 			}
354 		}
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];
362 		}
363 	}
364 
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);
371 	}
372 
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);
379 	}
380 
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);
391 
392 	/* initialize tries, catch memory errors */
393 	if ((rc = initialize_tries()) != 0) {
394 		return (rc);
395 	}
396 
397 	initialize_tables();	/* no memory is allocated here */
398 	initialize_ba_tables();	/* no memory is allocated here */
399 
400 	if ((rc = global_statinit()) != 0) { /* init global stats */
401 		ipgpc0dbg(("ipgpc_initialize: global_statinit error " \
402 		    "%d", rc));
403 		return (rc);
404 	}
405 
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 */
412 
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);
418 	}
419 	return (0);
420 }
421 
422 /*
423  * Module modify code
424  */
425 
426 /*
427  * name_hash(name, M)
428  *
429  * hash function for a string (name) of lenght M
430  */
431 unsigned
name_hash(char * name,size_t M)432 name_hash(char *name, size_t M)
433 {
434 	unsigned h;
435 
436 	for (h = 0; *name != '\0'; name++) {
437 		h = ((64 * h) + *name);
438 	}
439 	return ((h % M));
440 }
441 
442 
443 /*
444  * ipgpc_filter_destructor(filter)
445  *
446  * frees any allocated memory pointed to in the filter structure
447  * this function should be run before freeing an ipgpc_filter_t
448  */
449 void
ipgpc_filter_destructor(ipgpc_filter_t * filter)450 ipgpc_filter_destructor(ipgpc_filter_t *filter)
451 {
452 	if (filter->filter_comment != NULL) {
453 		kmem_free(filter->filter_comment,
454 		    (strlen(filter->filter_comment) + 1));
455 	}
456 	if (filter->saddr_hostname != NULL) {
457 		kmem_free(filter->saddr_hostname,
458 		    (strlen(filter->saddr_hostname) + 1));
459 	}
460 	if (filter->daddr_hostname != NULL) {
461 		kmem_free(filter->daddr_hostname,
462 		    (strlen(filter->daddr_hostname) + 1));
463 	}
464 }
465 
466 /*
467  * filter_name2id(*out_id, name, filter_instance, in_num_filters)
468  *
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
474  */
475 static int
filter_name2id(unsigned * out_id,char name[],int32_t filter_instance,int in_num_filters)476 filter_name2id(unsigned *out_id, char name[], int32_t filter_instance,
477     int in_num_filters)
478 {
479 	unsigned h;
480 	int dirty = -1;		/* set dirty to not found */
481 
482 	if (in_num_filters >= ipgpc_max_num_filters) {
483 		return (ENOSPC); /* will exceed maximum number of filters */
484 	}
485 
486 	/*
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
490 	 */
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;
498 			}
499 		}
500 		h = (h + 1) % ipgpc_max_num_filters;
501 	}
502 	/*
503 	 * check to see if searching stopped because a clean spot was found
504 	 * and a dirty space was seen before
505 	 */
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 */
518 		}
519 	}
520 }
521 
522 /*
523  * class_name2id(*out_id, name, in_num_classes)
524  *
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
530  */
531 static int
class_name2id(unsigned * out_id,char name[],int in_num_classes)532 class_name2id(unsigned *out_id, char name[], int in_num_classes)
533 {
534 	unsigned h;
535 	int dirty = -1;		/* set dirty to not found */
536 
537 	if (in_num_classes >= ipgpc_max_num_classes) {
538 		return (ENOSPC); /* will exceed maximum number of classes */
539 	}
540 
541 	/*
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
545 	 */
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;
552 			}
553 		}
554 		h = (h + 1) % ipgpc_max_num_classes;
555 	}
556 	/*
557 	 * check to see if searching stopped because a clean spot was found
558 	 * and a dirty space was seen before
559 	 */
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 */
572 		}
573 	}
574 }
575 
576 /*
577  * ipgpc_parse_filter(filter, nvlp)
578  *
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
582  */
583 int
ipgpc_parse_filter(ipgpc_filter_t * filter,nvlist_t * nvlp)584 ipgpc_parse_filter(ipgpc_filter_t *filter, nvlist_t *nvlp)
585 {
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;
592 
593 	/* parse filter name */
594 	if (nvlist_lookup_string(nvlp, CLASSIFIER_FILTER_NAME, &s) != 0) {
595 		return (EINVAL); /* filter name is missing, error */
596 	}
597 
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);
603 	}
604 
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);
610 	}
611 
612 	bcopy(s, filter->filter_name, (strlen(s) + 1));
613 
614 	/* parse uid */
615 	if (nvlist_lookup_uint32(nvlp, IPGPC_UID, &filter->uid) != 0) {
616 		filter->uid = (uid_t)IPGPC_WILDCARD;
617 	}
618 
619 	/* parse projid */
620 	if (nvlist_lookup_int32(nvlp, IPGPC_PROJID, &filter->projid) != 0) {
621 		filter->projid = IPGPC_WILDCARD;
622 	}
623 
624 	/* parse if_index */
625 	if (nvlist_lookup_uint32(nvlp, IPGPC_IF_INDEX, &filter->if_index)
626 	    != 0) {
627 		filter->if_index = 0;
628 	}
629 
630 	/* parse direction */
631 	if (nvlist_lookup_uint32(nvlp, IPGPC_DIR, &filter->direction) != 0) {
632 		filter->direction = 0;
633 	}
634 
635 	/* parse proto */
636 	if (nvlist_lookup_byte(nvlp, IPGPC_PROTO, &filter->proto) != 0) {
637 		filter->proto = 0;
638 	}
639 
640 	/*
641 	 * parse dsfield mask, if mask is present and dsfield value is not,
642 	 * then this is an invalid filter configuration
643 	 */
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);
653 		}
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);
663 		}
664 		filter->dsfield = 0;
665 	}
666 
667 	/* parse source port */
668 	if (nvlist_lookup_uint16(nvlp, IPGPC_SPORT, &filter->sport) != 0) {
669 		filter->sport = 0;
670 	}
671 
672 	/*
673 	 * parse source port mask, mask and value must be present, or neither
674 	 */
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);
681 		}
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);
689 		}
690 	}
691 
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);
697 	}
698 
699 	/* parse destination port */
700 	if (nvlist_lookup_uint16(nvlp, IPGPC_DPORT, &filter->dport) != 0) {
701 		filter->dport = 0;
702 	}
703 
704 	/*
705 	 * parse destination port mask, mask and value must be present,
706 	 * or neither
707 	 */
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);
714 		}
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);
722 		}
723 	}
724 
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);
730 	}
731 
732 	/* parse precedence */
733 	if (nvlist_lookup_uint32(nvlp, IPGPC_PRECEDENCE, &filter->precedence)
734 	    != 0) {
735 		filter->precedence = UINT_MAX; /* worst precedence */
736 	}
737 
738 	/* parse priority */
739 	if (nvlist_lookup_uint32(nvlp, IPGPC_PRIORITY, &filter->priority)
740 	    != 0) {
741 		filter->priority = 0; /* worst priority */
742 	}
743 
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;
748 	}
749 
750 	/* parse filter instance */
751 	if (nvlist_lookup_int32(nvlp, IPGPC_FILTER_INSTANCE,
752 	    &filter->filter_instance) != 0) {
753 		filter->filter_instance = -1;
754 	}
755 
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);
762 	}
763 
764 	/*
765 	 * parse source address mask, if address is present, mask must be
766 	 * present
767 	 */
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));
779 		}
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));
792 		}
793 		bcopy(mask, filter->saddr_mask.s6_addr32,
794 		    sizeof (filter->saddr_mask.s6_addr32));
795 	}
796 
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);
809 				}
810 			}
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);
816 			}
817 		}
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);
824 		}
825 	}
826 
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);
833 	}
834 
835 	/*
836 	 * parse destination address mask, if address is present, mask must be
837 	 * present
838 	 */
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));
850 		}
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));
863 		}
864 		bcopy(mask, filter->daddr_mask.s6_addr32,
865 		    sizeof (filter->daddr_mask.s6_addr32));
866 	}
867 
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);
880 				}
881 			}
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);
887 			}
888 		}
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);
895 		}
896 	}
897 
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);
904 	}
905 
906 	return (0);
907 }
908 
909 /*
910  * iscontinuousmask(mask, len)
911  *
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.
916  */
917 static boolean_t
iscontinuousmask(uint32_t mask,uint8_t len)918 iscontinuousmask(uint32_t mask, uint8_t len)
919 {
920 	uint8_t pos;
921 	boolean_t zero_found = B_FALSE;
922 
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);
929 			}
930 		}
931 	}
932 	return (B_TRUE);
933 }
934 
935 
936 /*
937  * insertfid(filter_id, filter, class_id)
938  *
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.
943  */
944 static void
insertfid(int filter_id,ipgpc_filter_t * filter,uint_t class_id)945 insertfid(int filter_id, ipgpc_filter_t *filter, uint_t class_id)
946 {
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;
954 }
955 
956 
957 static void
common_addfilter(fid_t * fid,int filter_id)958 common_addfilter(fid_t *fid, int filter_id)
959 {
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;
965 	}
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;
970 	}
971 	/* end trie inserts */
972 
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;
978 	}
979 	mutex_exit(&ipgpc_ds_table_id.lock);
980 
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;
987 	}
988 
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;
993 	}
994 
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;
999 	}
1000 
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;
1005 	}
1006 
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;
1011 	}
1012 	mutex_exit(&ipgpc_table_list_lock);
1013 	/* end table inserts */
1014 }
1015 
1016 static void
v4_addfilter(fid_t * fid,int filter_id)1017 v4_addfilter(fid_t *fid, int filter_id)
1018 {
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;
1024 	}
1025 
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;
1031 	}
1032 }
1033 
1034 static void
v6_addfilter(fid_t * fid,int filter_id)1035 v6_addfilter(fid_t *fid, int filter_id)
1036 {
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;
1041 	}
1042 
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;
1047 	}
1048 }
1049 
1050 /*
1051  * ipgpc_addfilter(filter, class_name, flags)
1052  *
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
1063  */
1064 /* ARGSUSED1 */
1065 int
ipgpc_addfilter(ipgpc_filter_t * filter,char * class_name,ipp_flags_t flags)1066 ipgpc_addfilter(ipgpc_filter_t *filter, char *class_name, ipp_flags_t flags)
1067 {
1068 	unsigned filter_id;
1069 	int err = 0;
1070 	fid_t *fid;
1071 	unsigned class_id;
1072 
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);
1077 	}
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);
1092 	}
1093 	insertfid(filter_id, filter, class_id);
1094 
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);
1119 	}
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);
1137 	}
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);
1142 }
1143 
1144 /*
1145  * reset_dontcare_stats()
1146  *
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
1150  */
1151 static void
reset_dontcare_stats(void)1152 reset_dontcare_stats(void)
1153 {
1154 	int i;
1155 
1156 	for (i = 0; i < NUM_TRIES; ++i) {
1157 		atomic_dec_32(&ipgpc_trie_list[i].stats.num_dontcare);
1158 	}
1159 	for (i = 0; i < NUM_TABLES; ++i) {
1160 		atomic_dec_32(&ipgpc_table_list[i].stats.num_dontcare);
1161 	}
1162 	atomic_dec_32(&ipgpc_ds_table_id.stats.num_dontcare);
1163 }
1164 
1165 /*
1166  * ipgpc_parse_class(out_class, nvlp)
1167  *
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
1171  */
1172 int
ipgpc_parse_class(ipgpc_class_t * out_class,nvlist_t * nvlp)1173 ipgpc_parse_class(ipgpc_class_t *out_class, nvlist_t *nvlp)
1174 {
1175 	char *name;
1176 	size_t name_len;
1177 	uint32_t gather_stats;
1178 
1179 	/* parse class name */
1180 	if (nvlist_lookup_string(nvlp, CLASSIFIER_CLASS_NAME, &name) != 0) {
1181 		return (EINVAL); /* class name missing, error */
1182 	}
1183 
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);
1190 	}
1191 
1192 	bcopy(name, out_class->class_name, (name_len + 1));
1193 
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);
1199 	}
1200 
1201 	/* parse action name */
1202 	if (nvlist_lookup_string(nvlp, CLASSIFIER_NEXT_ACTION, &name) != 0) {
1203 		return (EINVAL); /* action name missing, error */
1204 	}
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);
1209 	}
1210 
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;
1218 	}
1219 	return (0);
1220 }
1221 
1222 
1223 /*
1224  * ipgpc_addclass(in_class, flags)
1225  *
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
1231  */
1232 /* ARGSUSED */
1233 int
ipgpc_addclass(ipgpc_class_t * in_class,ipp_flags_t flags)1234 ipgpc_addclass(ipgpc_class_t *in_class, ipp_flags_t flags) {
1235 	int class_id;
1236 	int err;
1237 
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);
1247 	}
1248 	/* add reference to next action */
1249 	if ((err = ipp_action_ref(ipgpc_aid, in_class->next_action, 0)) != 0) {
1250 		/*
1251 		 * the action id we want to reference must have been
1252 		 * destroyed before we could reference it. remove class
1253 		 * and fail.
1254 		 */
1255 		removecid(class_id);
1256 		return (err);
1257 	}
1258 	return (0);
1259 }
1260 
1261 
1262 
1263 /*
1264  * class_statinit(in_class, in_class_id)
1265  *
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
1272  */
1273 static int
class_statinit(ipgpc_class_t * in_class,int in_class_id)1274 class_statinit(ipgpc_class_t *in_class, int in_class_id)
1275 {
1276 	int rc;
1277 	ipp_stat_t *ipp_cl_stats;
1278 	classstats_t *clsnames = NULL;
1279 
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);
1286 	}
1287 
1288 	ASSERT(ipp_cl_stats != NULL);
1289 	clsnames = (classstats_t *)ipp_cl_stats->ipps_data;
1290 	ASSERT(clsnames != NULL);
1291 
1292 	/* create stats entry */
1293 	bzero(&ipgpc_cid_list[in_class_id].stats,
1294 	    sizeof (ipgpc_class_stats_t));
1295 
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;
1299 
1300 	if ((rc = ipp_stat_named_init(ipp_cl_stats, "nbytes",
1301 	    IPP_STAT_UINT64, &clsnames->nbytes)) != 0) {
1302 		return (rc);
1303 	}
1304 	if ((rc = ipp_stat_named_init(ipp_cl_stats, "npackets",
1305 	    IPP_STAT_UINT64, &clsnames->npackets)) != 0) {
1306 		return (rc);
1307 	}
1308 	if ((rc = ipp_stat_named_init(ipp_cl_stats, "last_match",
1309 	    IPP_STAT_INT64, &clsnames->last_match)) != 0) {
1310 		return (rc);
1311 	}
1312 
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);
1317 }
1318 
1319 /*
1320  * insertcid(in_class, out_class_id)
1321  *
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.
1329  */
1330 static int
insertcid(ipgpc_class_t * in_class,int * out_class_id)1331 insertcid(ipgpc_class_t *in_class, int *out_class_id)
1332 {
1333 	int err, rc;
1334 	unsigned class_id;
1335 
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);
1351 			}
1352 		} else {
1353 			ipgpc_cid_list[class_id].cl_stats = NULL;
1354 		}
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);
1365 	}
1366 	mutex_exit(&ipgpc_cid_list_lock);
1367 	*out_class_id = class_id;
1368 	return (err);
1369 }
1370 
1371 /*
1372  * common_removefilter(in_filter_id, fid)
1373  *
1374  * removes in_filter_id from each of the common selector structures
1375  */
1376 static void
common_removefilter(int in_filter_id,fid_t * fid)1377 common_removefilter(int in_filter_id, fid_t *fid)
1378 {
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 */
1386 
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);
1392 
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 */
1411 }
1412 
1413 /*
1414  * v4_removefilter(in_filter_id, fid)
1415  *
1416  * removes id from IPV4 specific structures
1417  */
1418 static void
v4_removefilter(int in_filter_id,fid_t * fid)1419 v4_removefilter(int in_filter_id, fid_t *fid)
1420 {
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));
1429 }
1430 
1431 /*
1432  * v6_removefilter(in_filter_id, fid)
1433  *
1434  * removes id from IPV6 specific structures
1435  */
1436 static void
v6_removefilter(int in_filter_id,fid_t * fid)1437 v6_removefilter(int in_filter_id, fid_t *fid)
1438 {
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);
1445 }
1446 
1447 /*
1448  * ipgpc_removefilter(filter_name, filter_instance, flags)
1449  *
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
1455  */
1456 /* ARGSUSED */
1457 int
ipgpc_removefilter(char * filter_name,int32_t filter_instance,ipp_flags_t flags)1458 ipgpc_removefilter(char *filter_name, int32_t filter_instance,
1459     ipp_flags_t flags)
1460 {
1461 	unsigned filter_id;
1462 	fid_t *fid;
1463 	int rc;
1464 
1465 	/* check to see if any filters are loaded */
1466 	if (ipgpc_num_fltrs == 0) {
1467 		return (ENOENT);
1468 	}
1469 
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);
1476 	}
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);
1497 	}
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);
1509 }
1510 
1511 /*
1512  * removecid(in_class_id)
1513  *
1514  * removes the cid entry from the cid list and frees allocated structures
1515  */
1516 static void
removecid(int in_class_id)1517 removecid(int in_class_id)
1518 {
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;
1526 	}
1527 	/* decrement total number of classes loaded */
1528 	atomic_dec_ulong(&ipgpc_num_cls);
1529 }
1530 
1531 /*
1532  * remove_from_cid_filter_list(in_class_id, in_filter_id)
1533  *
1534  * removes the input filter_id from the filter_list of the class associated
1535  * with the input class_id
1536  */
1537 static void
remove_from_cid_filter_list(int in_class_id,int in_filter_id)1538 remove_from_cid_filter_list(int in_class_id, int in_filter_id)
1539 {
1540 	cid_t *cid = &ipgpc_cid_list[in_class_id];
1541 
1542 	if (cid->filter_list != NULL) {
1543 		(void) ipgpc_list_remove(&cid->filter_list, in_filter_id);
1544 	}
1545 }
1546 
1547 /*
1548  * ipgpc_removeclass(class_name)
1549  *
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'
1554  */
1555 int
ipgpc_removeclass(char * class_name,ipp_flags_t flags)1556 ipgpc_removeclass(char *class_name, ipp_flags_t flags)
1557 {
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;
1564 
1565 	/* check to see if any classes are loaded */
1566 	if (ipgpc_num_cls == 0) {
1567 		return (ENOENT);
1568 	}
1569 
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);
1576 	}
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);
1582 	}
1583 
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);
1597 		}
1598 	}
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);
1607 }
1608 
1609 /*
1610  * ipgpc_modifyfilter(nvlist, flags)
1611  *
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
1618  */
1619 int
ipgpc_modifyfilter(nvlist_t ** nvlpp,ipp_flags_t flags)1620 ipgpc_modifyfilter(nvlist_t **nvlpp, ipp_flags_t flags)
1621 {
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;
1630 
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);
1638 	}
1639 
1640 	/* parse class name */
1641 	if (nvlist_lookup_string(*nvlpp, CLASSIFIER_CLASS_NAME, &name)
1642 	    != 0) {
1643 		name = NULL;	/* no class specified */
1644 	}
1645 
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);
1661 			}
1662 		}
1663 		/* copy out old filter just in case we need to revert */
1664 		old_filter = ipgpc_fid_list[filter_id].filter;
1665 
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;
1674 		}
1675 
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;
1684 		}
1685 
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;
1694 		}
1695 
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);
1713 			}
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);
1722 		}
1723 	} else {
1724 		ipgpc0dbg(("ipgpc_modifyfilter: filter lookup error %d", rc));
1725 		return (rc); /* filter name does not exist */
1726 	}
1727 	kmem_free(filter, sizeof (ipgpc_filter_t));
1728 	return (0);
1729 }
1730 
1731 /*
1732  * ipgpc_modifyclass(in_class)
1733  *
1734  * if the input class exists, then the action list is modified
1735  * if the input class does not exist, ENOENT is returned
1736  */
1737 /* ARGSUSED */
1738 int
ipgpc_modifyclass(nvlist_t ** nvlpp,ipp_flags_t flags)1739 ipgpc_modifyclass(nvlist_t **nvlpp, ipp_flags_t flags)
1740 {
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;
1749 
1750 	/* parse class name */
1751 	if (nvlist_lookup_string(*nvlpp, CLASSIFIER_CLASS_NAME, &name) != 0) {
1752 		return (EINVAL); /* class name missing, error */
1753 	}
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);
1760 	}
1761 	bcopy(name, in_class.class_name, (name_len + 1));
1762 
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 */
1781 			}
1782 			ref_action = B_TRUE;
1783 		}
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;
1792 		}
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;
1803 				}
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);
1812 				}
1813 			}
1814 		}
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);
1826 			}
1827 			/* fix up references */
1828 			rc = ipp_action_unref(ipgpc_aid, old_next_action,
1829 			    flags);
1830 			ASSERT(rc == 0);
1831 		}
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);
1843 	}
1844 	return (0);
1845 }
1846 
1847 
1848 /*
1849  * ipgpc_list_insert(listpp, id)
1850  *
1851  * inserts an item, id, into the list, if item exists EEXIST is returned
1852  */
1853 int
ipgpc_list_insert(linked_list * listpp,key_t id)1854 ipgpc_list_insert(linked_list *listpp, key_t id)
1855 {
1856 	element_node_t *p;
1857 
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);
1868 			}
1869 		}
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;
1880 		}
1881 	}
1882 	return (0);
1883 }
1884 
1885 /*
1886  * ipgpc_list_remove(listpp, id)
1887  *
1888  * removes an item, id, from the list if it exists and returns TRUE or FALSE
1889  * if not removed
1890  */
1891 boolean_t
ipgpc_list_remove(element_node_t ** listpp,key_t id)1892 ipgpc_list_remove(element_node_t **listpp, key_t id)
1893 {
1894 	element_node_t *p = NULL;
1895 	element_node_t *t = NULL;
1896 
1897 	if (*listpp == NULL) {
1898 		return (B_FALSE);
1899 	}
1900 	if ((*listpp)->id == id) {
1901 		p = *listpp;
1902 		if ((*listpp)->element_refcnt == 1) {
1903 			*listpp = (*listpp)->next;
1904 		}
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;
1914 				}
1915 				(*t->element_unref)(t);
1916 				return (B_TRUE);
1917 			}
1918 		}
1919 	}
1920 	return (B_FALSE);
1921 }
1922 
1923 /*
1924  * Module destroy code
1925  */
1926 
1927 static void
removeclasses(ipp_flags_t flags)1928 removeclasses(ipp_flags_t flags)
1929 {
1930 	int i;
1931 
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);
1936 		}
1937 	}
1938 }
1939 
1940 static void
freetriev6nodes(node_t ** inNode)1941 freetriev6nodes(node_t **inNode)
1942 {
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 */
1949 
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;
1972 			}
1973 			if (anode == NULL) {
1974 				return;
1975 			}
1976 		}
1977 	}
1978 }
1979 
1980 
1981 void
ipgpc_destroy(ipp_flags_t flags)1982 ipgpc_destroy(ipp_flags_t flags)
1983 {
1984 	int i;
1985 	int rc;
1986 	element_node_t *anode = NULL;
1987 	element_node_t *tnode = NULL;
1988 	fid_t *fid = NULL;
1989 
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;
1994 
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);
2012 			}
2013 		}
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;
2018 	}
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;
2024 	}
2025 
2026 	/*
2027 	 * remove all classes, which will remove all filters, stats and
2028 	 * selectors
2029 	 */
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;
2035 	}
2036 	/* all filters and classes should have been removed at this point */
2037 	ASSERT((ipgpc_num_cls == 0) && (ipgpc_num_fltrs == 0));
2038 
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;
2044 	}
2045 
2046 	/*
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
2050 	 */
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;
2059 	}
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;
2068 	}
2069 
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;
2079 		}
2080 	}
2081 
2082 	/* destroy caches */
2083 	if (ht_node_cache != NULL) {
2084 		kmem_cache_destroy(ht_node_cache);
2085 		ht_node_cache = NULL;
2086 	}
2087 	if (trie_node_cache != NULL) {
2088 		kmem_cache_destroy(trie_node_cache);
2089 		trie_node_cache = NULL;
2090 	}
2091 	if (element_node_cache != NULL) {
2092 		kmem_cache_destroy(element_node_cache);
2093 		element_node_cache = NULL;
2094 	}
2095 	if (ht_match_cache != NULL) {
2096 		kmem_cache_destroy(ht_match_cache);
2097 		ht_match_cache = NULL;
2098 	}
2099 }
2100 
2101 /*
2102  * Module info code
2103  */
2104 
2105 /*
2106  * ipgpc_params_info(fn, arg)
2107  *
2108  * allocates, builds and passes an nvlist to fn with arg
2109  */
2110 int
ipgpc_params_info(int (* fn)(nvlist_t *,void *),void * arg)2111 ipgpc_params_info(int (*fn)(nvlist_t *, void *), void *arg)
2112 {
2113 	nvlist_t *nvlp;
2114 	int rc;
2115 
2116 	/* allocate nvlist to be passed back */
2117 	if ((rc = nvlist_alloc(&nvlp, NV_UNIQUE_NAME, KM_NOSLEEP)) != 0) {
2118 		return (rc);
2119 	}
2120 
2121 	/* add config type */
2122 	if ((rc = nvlist_add_byte(nvlp, IPP_CONFIG_TYPE, IPP_SET)) != 0) {
2123 		nvlist_free(nvlp);
2124 		return (rc);
2125 	}
2126 
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);
2132 	}
2133 
2134 	/* call back with nvlist */
2135 	rc = fn(nvlp, arg);
2136 
2137 	nvlist_free(nvlp);
2138 
2139 	return (rc);
2140 }
2141 
2142 /*
2143  * build_class_nvlist(nvlpp, in_class)
2144  *
2145  * build an nvlist based on in_class
2146  * if isdefault, add apporiate configuration type to nvlpp
2147  */
2148 static int
build_class_nvlist(nvlist_t ** nvlpp,ipgpc_class_t * in_class,boolean_t isdefault)2149 build_class_nvlist(nvlist_t **nvlpp, ipgpc_class_t *in_class,
2150     boolean_t isdefault)
2151 {
2152 	nvlist_t *nvlp = *nvlpp;
2153 	char *next_action;
2154 	int rc;
2155 
2156 	/*
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
2161 	 */
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);
2167 	}
2168 
2169 	/* add class name */
2170 	if ((rc = nvlist_add_string(nvlp, CLASSIFIER_CLASS_NAME,
2171 	    in_class->class_name)) != 0) {
2172 		return (rc);
2173 	}
2174 
2175 	/* add originator */
2176 	if ((rc = nvlist_add_uint32(nvlp, IPP_CONFIG_ORIGINATOR,
2177 	    in_class->originator)) != 0) {
2178 		return (rc);
2179 	}
2180 
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);
2184 	}
2185 
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);
2191 	}
2192 
2193 	kmem_free(next_action, (strlen(next_action) + 1));
2194 
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);
2199 	}
2200 
2201 	return (0);
2202 }
2203 
2204 
2205 /*
2206  * ipgpc_classes_info(fn, arg)
2207  *
2208  * foreach class, allocate, build and pass an nvlist to fn with arg
2209  */
2210 int
ipgpc_classes_info(int (* fn)(nvlist_t *,void *),void * arg)2211 ipgpc_classes_info(int (*fn)(nvlist_t *, void *), void *arg)
2212 {
2213 	int i;
2214 	int rc;
2215 	nvlist_t *nvlp;
2216 
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;
2221 		}
2222 		/* allocate an nvlist */
2223 		if ((rc = nvlist_alloc(&nvlp, NV_UNIQUE_NAME, KM_NOSLEEP))
2224 		    != 0) {
2225 			return (rc);
2226 		}
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);
2233 		}
2234 		/* call back with nvlist */
2235 		if ((rc = fn(nvlp, arg)) != 0) {
2236 			nvlist_free(nvlp);
2237 			return (rc);
2238 		}
2239 
2240 		nvlist_free(nvlp); /* free nvlist and continue */
2241 	}
2242 
2243 	return (0);
2244 }
2245 
2246 /*
2247  * build_filter_nvlist(nvlpp, in_filter, class_name)
2248  *
2249  * build an nvlist based on in_filter and class_name.
2250  * Only non-wildcard/dontcare selectors are added to the nvlist.
2251  */
2252 static int
build_filter_nvlist(nvlist_t ** nvlpp,ipgpc_filter_t * in_filter,char * class_name)2253 build_filter_nvlist(nvlist_t **nvlpp, ipgpc_filter_t *in_filter,
2254     char *class_name)
2255 {
2256 	nvlist_t *nvlp = *nvlpp;
2257 	int rc;
2258 	in6_addr_t zero_addr = IN6ADDR_ANY_INIT;
2259 
2260 	/* add filter name */
2261 	if ((rc = nvlist_add_string(nvlp, CLASSIFIER_FILTER_NAME,
2262 	    in_filter->filter_name)) != 0) {
2263 		return (rc);
2264 	}
2265 
2266 	/* add class name */
2267 	if ((rc = nvlist_add_string(nvlp, CLASSIFIER_CLASS_NAME, class_name))
2268 	    != 0) {
2269 		return (rc);
2270 	}
2271 
2272 	/* add originator */
2273 	if ((rc = nvlist_add_uint32(nvlp, IPP_CONFIG_ORIGINATOR,
2274 	    in_filter->originator)) != 0) {
2275 		return (rc);
2276 	}
2277 
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);
2282 	}
2283 
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);
2289 		}
2290 	}
2291 
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);
2297 		}
2298 	}
2299 
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);
2305 		}
2306 	}
2307 
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);
2313 		}
2314 	}
2315 
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);
2321 		}
2322 	}
2323 
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);
2329 		}
2330 		if ((rc = nvlist_add_byte(nvlp, IPGPC_DSFIELD_MASK,
2331 		    in_filter->dsfield_mask)) != 0) {
2332 			return (rc);
2333 		}
2334 	}
2335 
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);
2341 		}
2342 
2343 		if ((rc = nvlist_add_uint32_array(nvlp, IPGPC_SADDR_MASK,
2344 		    in_filter->saddr_mask.s6_addr32, 4)) != 0) {
2345 			return (rc);
2346 		}
2347 
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);
2352 			}
2353 		}
2354 	}
2355 
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);
2361 		}
2362 		if ((rc = nvlist_add_uint32_array(nvlp, IPGPC_DADDR_MASK,
2363 		    in_filter->daddr_mask.s6_addr32, 4)) != 0) {
2364 			return (rc);
2365 		}
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);
2370 			}
2371 		}
2372 	}
2373 
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);
2379 		}
2380 		if ((rc = nvlist_add_uint16(nvlp, IPGPC_SPORT_MASK,
2381 		    in_filter->sport_mask)) != 0) {
2382 			return (rc);
2383 		}
2384 	}
2385 
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);
2391 		}
2392 		if ((rc = nvlist_add_uint16(nvlp, IPGPC_DPORT_MASK,
2393 		    in_filter->dport_mask)) != 0) {
2394 			return (rc);
2395 		}
2396 	}
2397 
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);
2403 		}
2404 	}
2405 
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);
2411 		}
2412 	}
2413 
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);
2419 		}
2420 	}
2421 
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);
2427 		}
2428 	}
2429 
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);
2435 		}
2436 	}
2437 
2438 	return (0);
2439 }
2440 
2441 /*
2442  * ipgpc_filters_info(fn, arg)
2443  *
2444  * for each filter, allocate, build and pass an nvlist to fn with arg
2445  */
2446 int
ipgpc_filters_info(int (* fn)(nvlist_t *,void *),void * arg)2447 ipgpc_filters_info(int (*fn)(nvlist_t *, void *), void *arg)
2448 {
2449 	int i;
2450 	int rc;
2451 	nvlist_t *nvlp;
2452 	int class_id;
2453 
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;
2458 		}
2459 		/* allocate an nvlist */
2460 		if ((rc = nvlist_alloc(&nvlp, NV_UNIQUE_NAME, KM_NOSLEEP))
2461 		    != 0) {
2462 			return (rc);
2463 		}
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);
2471 		}
2472 		/* call back with nvlist */
2473 		if ((rc = fn(nvlp, arg)) != 0) {
2474 			nvlist_free(nvlp);
2475 			return (rc);
2476 		}
2477 
2478 		nvlist_free(nvlp); /* free nvlist and continue */
2479 	}
2480 	return (0);
2481 }
2482 
2483 /*
2484  * Module invoke code
2485  */
2486 
2487 /*
2488  * ipgpc_findfilters(in_id, key, fid_table)
2489  *
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
2498  */
2499 int
ipgpc_findfilters(int in_id,int key,ht_match_t * fid_table)2500 ipgpc_findfilters(int in_id, int key, ht_match_t *fid_table)
2501 {
2502 	int num_found = 0;
2503 
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);
2508 		}
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);
2515 		}
2516 	} else if (in_id >= TABLE_ID_OFFSET) {	/* table to search */
2517 		table_id_t *taid = &ipgpc_table_list[in_id - TABLE_ID_OFFSET];
2518 
2519 		if (taid->info.dontcareonly == B_TRUE) {
2520 			/* trie is loaded with only DONTCARE(*) keys */
2521 			return (DONTCARE_ONLY_MATCH);
2522 		}
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);
2527 		}
2528 	} else {		/* trie to search */
2529 		trie_id_t *tid = &ipgpc_trie_list[in_id];
2530 
2531 		if (tid->info.dontcareonly == B_TRUE) {
2532 			/* trie is loaded with only DONTCARE(*) keys */
2533 			return (DONTCARE_ONLY_MATCH);
2534 		}
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);
2540 		}
2541 	}
2542 	if (num_found == -1) {	/* num_found == -1 if memory error */
2543 		return (ENOMEM);
2544 	} else {
2545 		return (NORMAL_MATCH);
2546 	}
2547 }
2548 
2549 /*
2550  * ipgpc_findfilters6(in_id, key, fid_table)
2551  *
2552  * findfilters specific to IPv6 traffic
2553  */
2554 int
ipgpc_findfilters6(int in_id,in6_addr_t key,ht_match_t * fid_table)2555 ipgpc_findfilters6(int in_id, in6_addr_t key, ht_match_t *fid_table)
2556 {
2557 	trie_id_t *tid = &ipgpc_trie_list[in_id];
2558 	int num_found = 0;
2559 
2560 	if (tid->info.dontcareonly == B_TRUE) {
2561 		/* trie is loaded with only DONTCARE(*) keys */
2562 		return (DONTCARE_ONLY_MATCH);
2563 	}
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);
2573 	}
2574 }
2575 
2576 /*
2577  * ht_match_insert(a, id, mask)
2578  *
2579  * inserts id into table and applies mask to match_map
2580  * returns ENOMEM if can't allocate ht_match_t node, 0 otherwise
2581  */
2582 static int
ht_match_insert(ht_match_t * a,int id,uint16_t mask)2583 ht_match_insert(ht_match_t *a, int id, uint16_t mask)
2584 {
2585 	int x = (id % HASH_SIZE); /* has for index */
2586 	ht_match_t *p = NULL;
2587 
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);
2597 		}
2598 		a[x].next->next = NULL;
2599 		a[x].next->key = id;
2600 		a[x].next->match_map = mask;
2601 	} else {
2602 
2603 		p = a[x].next;
2604 		while (p != NULL) {
2605 			if (p->key == id) {
2606 				p->match_map |= mask;
2607 				return (0);
2608 			}
2609 			p = p->next;
2610 		}
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);
2616 		}
2617 		p->key = id;
2618 		p->match_map = mask;
2619 		p->next = a[x].next;
2620 		a[x].next = p;
2621 	}
2622 	return (0);
2623 }
2624 
2625 /*
2626  * ipgpc_mark_found(mask, list, fid_table)
2627  *
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
2632  */
2633 int
ipgpc_mark_found(uint16_t mask,linked_list list,ht_match_t * fid_table)2634 ipgpc_mark_found(uint16_t mask, linked_list list, ht_match_t *fid_table)
2635 {
2636 	linked_list tnode = NULL;
2637 	int num_found = 0;
2638 
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);
2645 			}
2646 			++num_found;
2647 		}
2648 	}
2649 	return (num_found);
2650 }
2651 
2652 /* updates global stats for ipgpc */
2653 /* ARGSUSED */
2654 static int
update_global_stats(ipp_stat_t * sp,void * arg,int rw)2655 update_global_stats(ipp_stat_t *sp, void *arg, int rw)
2656 {
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;
2660 
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);
2668 }
2669 
2670 
2671 /* updates class stats for a specific class */
2672 static int
update_class_stats(ipp_stat_t * sp,void * arg,int rw)2673 update_class_stats(ipp_stat_t *sp, void *arg, int rw)
2674 {
2675 	ipgpc_class_stats_t *stats = (ipgpc_class_stats_t *)arg;
2676 	classstats_t *cl_stats = (classstats_t *)sp->ipps_data;
2677 
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);
2684 }
2685