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_add_32(&element->element_refcnt, 1);
 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_add_32_nv(&element->element_refcnt, -1) == 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_add_long(&ipgpc_num_fltrs, 1);
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_add_32(&ipgpc_trie_list[i].stats.num_dontcare, -1);
1158         }
1159         for (i = 0; i < NUM_TABLES; ++i) {
1160                 atomic_add_32(&ipgpc_table_list[i].stats.num_dontcare, -1);
1161         }
1162         atomic_add_32(&ipgpc_ds_table_id.stats.num_dontcare, -1);
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_add_long(&ipgpc_num_cls, 1);
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_add_long(&ipgpc_num_fltrs, -1);
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_add_long(&ipgpc_num_cls, -1);
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 }