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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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