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