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 2008 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 #include <sys/types.h>
  27 #include <sys/systm.h>
  28 #include <sys/socket.h>
  29 #include <netinet/in.h>
  30 #include <sys/modctl.h>
  31 #include <sys/sunddi.h>
  32 #include <ipp/ipp.h>
  33 #include <ipp/ipp_config.h>
  34 #include <inet/common.h>
  35 #include <ipp/dscpmk/dscpmk_impl.h>
  36 
  37 #define D_SM_COMMENT    "IPP dscpmk marker module"
  38 
  39 /* DDI file for dscpmk ipp module */
  40 
  41 /* default dscp map - dscp unchanged */
  42 uint8_t default_dscp_map[DSCPMK_ARRAY_COUNT] = {
  43         0,      1,      2,      3,
  44         4,      5,      6,      7,
  45         8,      9,      10,     11,
  46         12,     13,     14,     15,
  47         16,     17,     18,     19,
  48         20,     21,     22,     23,
  49         24,     25,     26,     27,
  50         28,     29,     30,     31,
  51         32,     33,     34,     35,
  52         36,     37,     38,     39,
  53         40,     41,     42,     43,
  54         44,     45,     46,     47,
  55         48,     49,     50,     51,
  56         52,     53,     54,     55,
  57         56,     57,     58,     59,
  58         60,     61,     62,     63
  59 };
  60 
  61 static int dscpmk_create_action(ipp_action_id_t, nvlist_t **, ipp_flags_t);
  62 static int dscpmk_modify_action(ipp_action_id_t, nvlist_t **, ipp_flags_t);
  63 static int dscpmk_destroy_action(ipp_action_id_t, ipp_flags_t);
  64 static int dscpmk_info(ipp_action_id_t, int (*)(nvlist_t *, void *), void *,
  65     ipp_flags_t);
  66 static int dscpmk_invoke_action(ipp_action_id_t, ipp_packet_t *);
  67 
  68 /* Creating and updating summary stats */
  69 static int dscpmk_summ_statinit(ipp_action_id_t, dscpmk_data_t *);
  70 static int dscpmk_update_stats(ipp_stat_t *, void *, int);
  71 
  72 /* Creating and updating per-dscp stats */
  73 static int dscpmk_det_statinit(ipp_action_id_t, dscpmk_data_t *, int);
  74 static int dscpmk_update_det_stats(ipp_stat_t *, void *, int);
  75 
  76 /* Entry points for this IPP module */
  77 ipp_ops_t dscpmk_ops = {
  78         IPPO_REV,
  79         dscpmk_create_action,   /* ippo_action_create */
  80         dscpmk_modify_action,   /* ippo_action_modify */
  81         dscpmk_destroy_action,  /* ippo_action_destroy */
  82         dscpmk_info,            /* ippo_action_info */
  83         dscpmk_invoke_action    /* ippo_action_invoke */
  84 };
  85 
  86 extern struct mod_ops mod_ippops;
  87 
  88 /*
  89  * Module linkage information for the kernel.
  90  */
  91 static struct modlipp modlipp = {
  92         &mod_ippops,
  93         D_SM_COMMENT,
  94         &dscpmk_ops
  95 };
  96 
  97 static struct modlinkage modlinkage = {
  98         MODREV_1,
  99         (void *)&modlipp,
 100         NULL
 101 };
 102 
 103 
 104 int
 105 _init(void)
 106 {
 107         return (mod_install(&modlinkage));
 108 }
 109 
 110 int
 111 _fini(void)
 112 {
 113         return (mod_remove(&modlinkage));
 114 }
 115 
 116 int
 117 _info(struct modinfo *modinfop)
 118 {
 119         return (mod_info(&modlinkage, modinfop));
 120 }
 121 
 122 static int
 123 dscpmk_create_action(ipp_action_id_t aid, nvlist_t **nvlpp, ipp_flags_t flags)
 124 {
 125         nvlist_t *nvlp;
 126         dscpmk_data_t *dscpmk_data;
 127         char *next_action;
 128         int err, cnt;
 129         int32_t *tbl;
 130         uint_t nelem = DSCPMK_ARRAY_COUNT;
 131         uint32_t bstats;
 132 
 133         ASSERT((nvlpp != NULL) && (*nvlpp != NULL));
 134 
 135         nvlp = *nvlpp;
 136         *nvlpp = NULL;          /* nvlist should be NULL on return */
 137 
 138         if ((dscpmk_data = kmem_zalloc(DSCPMK_DATA_SZ, KM_NOSLEEP)) == NULL) {
 139                 nvlist_free(nvlp);
 140                 return (ENOMEM);
 141         }
 142 
 143         /* parse next action name */
 144         if ((err = nvlist_lookup_string(nvlp, DSCPMK_NEXT_ACTION_NAME,
 145             &next_action)) != 0) {
 146                 nvlist_free(nvlp);
 147                 dscpmk0dbg(("dscpmk_create_action: invalid config, " \
 148                     "next_action name missing\n"));
 149                 kmem_free(dscpmk_data, DSCPMK_DATA_SZ);
 150                 return (err);
 151         }
 152 
 153         if ((dscpmk_data->next_action = ipp_action_lookup(next_action))
 154             == IPP_ACTION_INVAL) {
 155                 nvlist_free(nvlp);
 156                 dscpmk0dbg(("dscpmk_create_action: next_action "\
 157                     "invalid\n"));
 158                 kmem_free(dscpmk_data, DSCPMK_DATA_SZ);
 159                 return (EINVAL);
 160         }
 161 
 162         /* Fill in the default value */
 163         bcopy(default_dscp_map, dscpmk_data->dscp_map,
 164             sizeof (default_dscp_map));
 165         /*
 166          * parse dscp_map, if present. Note that the module gets
 167          * the entire array with unchanged entries marked with -1.
 168          */
 169         if ((err = nvlist_lookup_int32_array(nvlp, DSCPMK_DSCP_MAP,
 170             &tbl, &nelem)) == 0) {
 171                 for (cnt = 0; cnt < DSCPMK_ARRAY_COUNT; cnt++) {
 172                         if ((tbl[cnt] != DSCPMK_UNCHANGED_DSCP) && (tbl[cnt] !=
 173                             dscpmk_data->dscp_map[cnt])) {
 174                                 dscpmk_data->dscp_map[cnt] = tbl[cnt];
 175                         }
 176                 }
 177         }
 178 
 179 
 180         /* parse summary_stats boolean */
 181         if ((err = nvlist_lookup_uint32(nvlp, IPP_ACTION_STATS_ENABLE, &bstats))
 182             != 0) {
 183                 dscpmk_data->summary_stats = B_FALSE;
 184         } else {
 185                 dscpmk_data->summary_stats = (bstats != 0) ? B_TRUE : B_FALSE;
 186                 /* If stats is needed, initialize the stats structure */
 187                 if (dscpmk_data->summary_stats) {
 188                         if ((err = dscpmk_summ_statinit(aid, dscpmk_data))
 189                             != 0) {
 190                                 nvlist_free(nvlp);
 191                                 kmem_free(dscpmk_data, DSCPMK_DATA_SZ);
 192                                 return (err);
 193                         }
 194                 }
 195         }
 196 
 197         /*
 198          * Initialize per-dscp stats; B_FALSE in present indicates a dscp
 199          * with this value (count) is not present in the map.
 200          */
 201         for (cnt = 0; cnt < DSCPMK_ARRAY_COUNT; cnt++) {
 202                 dscpmk_data->dscp_stats[cnt].present = B_FALSE;
 203                 dscpmk_data->dscp_stats[cnt].npackets = 0;
 204         }
 205 
 206         /* parse detailed_stats boolean */
 207         if ((err = nvlist_lookup_uint32(nvlp, DSCPMK_DETAILED_STATS, &bstats))
 208             != 0) {
 209                 dscpmk_data->detailed_stats = B_FALSE;
 210         } else {
 211                 dscpmk_data->detailed_stats = (bstats != 0) ? B_TRUE : B_FALSE;
 212                 /* If stats is needed, initialize the stats structure */
 213                 if (dscpmk_data->detailed_stats) {
 214                         for (cnt = 0; cnt < DSCPMK_ARRAY_COUNT; cnt++) {
 215                                 int val = dscpmk_data->dscp_map[cnt];
 216                                 if (dscpmk_data->dscp_stats[val].present) {
 217                                         continue;
 218                                 }
 219                                 dscpmk_data->dscp_stats[val].present = B_TRUE;
 220                                 if ((err = dscpmk_det_statinit(aid, dscpmk_data,
 221                                     val)) != 0) {
 222                                         nvlist_free(nvlp);
 223                                         kmem_free(dscpmk_data, DSCPMK_DATA_SZ);
 224                                         return (err);
 225                                 }
 226                         }
 227                 }
 228         }
 229 
 230         /* Free the nvlist */
 231         nvlist_free(nvlp);
 232 
 233         /* set action chain reference */
 234         if ((err = ipp_action_ref(aid, dscpmk_data->next_action, flags)) != 0) {
 235                 dscpmk0dbg(("dscpmk_create_action: ipp_action_ref " \
 236                     "returned with error %d\n", err));
 237                 if (dscpmk_data->summary_stats) {
 238                         ipp_stat_destroy(dscpmk_data->stats);
 239                 }
 240                 if (dscpmk_data->detailed_stats) {
 241                         for (cnt = 0; cnt < DSCPMK_ARRAY_COUNT; cnt++) {
 242                                 if (dscpmk_data->dscp_stats[cnt].present) {
 243                                         ipp_stat_destroy(
 244                                             dscpmk_data->dscp_stats[cnt].stats);
 245                                 }
 246                         }
 247                 }
 248                 kmem_free(dscpmk_data, DSCPMK_DATA_SZ);
 249                 return (err);
 250         }
 251 
 252         ipp_action_set_ptr(aid, (void *)dscpmk_data);
 253         return (0);
 254 }
 255 
 256 static int
 257 dscpmk_modify_action(ipp_action_id_t aid, nvlist_t **nvlpp, ipp_flags_t flags)
 258 {
 259         nvlist_t *nvlp;
 260         int err = 0, cnt;
 261         uint8_t config_type;
 262         char *next_action_name;
 263         uint32_t bstats;
 264         uint_t nelem = DSCPMK_ARRAY_COUNT;
 265         int32_t *tbl;
 266         ipp_action_id_t next_action;
 267         dscpmk_data_t *dscpmk_data;
 268 
 269         ASSERT((nvlpp != NULL) && (*nvlpp != NULL));
 270 
 271         nvlp = *nvlpp;
 272         *nvlpp = NULL;          /* nvlist should be NULL when this returns */
 273 
 274         if ((err = nvlist_lookup_byte(nvlp, IPP_CONFIG_TYPE, &config_type))
 275             != 0) {
 276                 nvlist_free(nvlp);
 277                 dscpmk0dbg(("dscpmk_modify_action: invalid cfg. type\n"));
 278                 return (err);
 279         }
 280 
 281         if (config_type != IPP_SET) {
 282                 nvlist_free(nvlp);
 283                 dscpmk0dbg(("dscpmk_modify_action: invalid cfg. type " \
 284                     "%d\n", config_type));
 285                 return (EINVAL);
 286         }
 287 
 288         dscpmk_data = (dscpmk_data_t *)ipp_action_get_ptr(aid);
 289         ASSERT(dscpmk_data != NULL);
 290 
 291         /* parse next action name, if present */
 292         if ((err = nvlist_lookup_string(nvlp, DSCPMK_NEXT_ACTION_NAME,
 293             &next_action_name)) == 0) {
 294                 /* lookup action name to get action id */
 295                 if ((next_action = ipp_action_lookup(next_action_name))
 296                     == IPP_ACTION_INVAL) {
 297                         nvlist_free(nvlp);
 298                         dscpmk0dbg(("dscpmk_modify_action: next_action "\
 299                             "invalid\n"));
 300                         return (EINVAL);
 301                 }
 302                 /* reference new action */
 303                 if ((err = ipp_action_ref(aid, next_action, flags)) != 0) {
 304                         nvlist_free(nvlp);
 305                         dscpmk0dbg(("dscpmk_modify_action: ipp_action_ref " \
 306                             "returned with error %d\n", err));
 307                         return (err);
 308                 }
 309                 /* unref old action */
 310                 err = ipp_action_unref(aid, dscpmk_data->next_action, flags);
 311                 ASSERT(err == 0);
 312                 dscpmk_data->next_action = next_action;
 313         }
 314 
 315         /*
 316          * parse dscp_map, if present. Note that the module gets
 317          * the entire array with unchanged entries marked with -1.
 318          * If this array is absent during modification, it means revert to
 319          * the default table.
 320          */
 321         if ((err = nvlist_lookup_int32_array(nvlp, DSCPMK_DSCP_MAP,
 322             &tbl, &nelem)) == 0) {
 323                 for (cnt = 0; cnt < DSCPMK_ARRAY_COUNT; cnt++) {
 324                         if ((tbl[cnt] != DSCPMK_UNCHANGED_DSCP) && (tbl[cnt] !=
 325                             dscpmk_data->dscp_map[cnt])) {
 326                                 dscpmk_data->dscp_map[cnt] = tbl[cnt];
 327                         }
 328                 }
 329         } else {
 330                 bcopy(default_dscp_map, dscpmk_data->dscp_map,
 331                     sizeof (default_dscp_map));
 332         }
 333 
 334         /* parse summary_stats boolean, if present */
 335         if ((err = nvlist_lookup_uint32(nvlp, IPP_ACTION_STATS_ENABLE, &bstats))
 336             == 0) {
 337                 boolean_t val = (bstats != 0) ? B_TRUE : B_FALSE;
 338                 /* Turning on stats */
 339                 if (!dscpmk_data->summary_stats && val) {
 340                         if ((err = dscpmk_summ_statinit(aid, dscpmk_data))
 341                             != 0) {
 342                                 nvlist_free(nvlp);
 343                                 return (err);
 344                         }
 345                 /* Turning off stats */
 346                 } else if (!val && dscpmk_data->summary_stats) {
 347                         ipp_stat_destroy(dscpmk_data->stats);
 348 
 349                 }
 350                 dscpmk_data->summary_stats = val;
 351         }
 352 
 353         /* parse detailed_stats boolean */
 354         if ((err = nvlist_lookup_uint32(nvlp, DSCPMK_DETAILED_STATS, &bstats))
 355             == 0) {
 356                 boolean_t val = (bstats != 0) ? B_TRUE : B_FALSE;
 357                 if (dscpmk_data->detailed_stats && !val) {
 358                         for (cnt = 0; cnt < DSCPMK_ARRAY_COUNT; cnt++) {
 359                                 if (dscpmk_data->dscp_stats[cnt].present) {
 360                                         dscpmk_data->dscp_stats[cnt].present =
 361                                             B_FALSE;
 362                                         ipp_stat_destroy(dscpmk_data->
 363                                             dscp_stats[cnt].stats);
 364                                 }
 365                         }
 366                 }
 367                 dscpmk_data->detailed_stats = val;
 368         }
 369 
 370         /* The map might have changed */
 371         if (dscpmk_data->detailed_stats) {
 372                 for (cnt = 0; cnt < DSCPMK_ARRAY_COUNT; cnt++) {
 373                         int val = dscpmk_data->dscp_map[cnt];
 374                         if (!dscpmk_data->dscp_stats[val].present) {
 375                                 dscpmk_data->dscp_stats[val].present = B_TRUE;
 376                                 if ((err = dscpmk_det_statinit(aid, dscpmk_data,
 377                                     val)) != 0) {
 378                                         nvlist_free(nvlp);
 379                                         return (err);
 380                                 }
 381                         }
 382                 }
 383         }
 384 
 385         /* Free the nvlist */
 386         nvlist_free(nvlp);
 387         return (0);
 388 }
 389 
 390 static int
 391 dscpmk_destroy_action(ipp_action_id_t aid, ipp_flags_t flags)
 392 {
 393         dscpmk_data_t *dscpmk_data;
 394         int err, cnt;
 395 
 396         dscpmk_data = (dscpmk_data_t *)ipp_action_get_ptr(aid);
 397         ASSERT(dscpmk_data != NULL);
 398 
 399         /* Destroy stats, if gathered */
 400         if (dscpmk_data->summary_stats) {
 401                 ipp_stat_destroy(dscpmk_data->stats);
 402         }
 403 
 404         if (dscpmk_data->detailed_stats) {
 405                 for (cnt = 0; cnt < DSCPMK_ARRAY_COUNT; cnt++) {
 406                         if (dscpmk_data->dscp_stats[cnt].present) {
 407                                 ipp_stat_destroy(dscpmk_data->dscp_stats[cnt].
 408                                     stats);
 409                         }
 410                 }
 411         }
 412 
 413         /* unreference the action */
 414         err = ipp_action_unref(aid, dscpmk_data->next_action, flags);
 415         ASSERT(err == 0);
 416 
 417         kmem_free(dscpmk_data, DSCPMK_DATA_SZ);
 418         return (0);
 419 }
 420 
 421 static int
 422 dscpmk_invoke_action(ipp_action_id_t aid, ipp_packet_t *packet)
 423 {
 424         dscpmk_data_t *dscpmk_data;
 425         mblk_t *mp = NULL;
 426         ip_priv_t *priv;
 427         int err;
 428 
 429         ASSERT(packet != NULL);
 430 
 431         /* get mblk from ipp_packet structure */
 432         mp = ipp_packet_get_data(packet);
 433         priv = (ip_priv_t *)ipp_packet_get_private(packet);
 434 
 435         dscpmk_data = (dscpmk_data_t *)ipp_action_get_ptr(aid);
 436         ASSERT(dscpmk_data != NULL);
 437 
 438         /* dscpmk packet as configured */
 439         if ((err = dscpmk_process(&mp, dscpmk_data, priv->proc)) != 0) {
 440                 return (err);
 441         } else {
 442                 /* return packet with next action set */
 443                 return (ipp_packet_next(packet, dscpmk_data->next_action));
 444         }
 445 }
 446 
 447 static int
 448 dscpmk_det_statinit(ipp_action_id_t aid, dscpmk_data_t *dscpmk_data, int val)
 449 {
 450         int err = 0;
 451         dscpmk_dscp_stats_t *statp;
 452         char stats_string[15];
 453 
 454         (void) sprintf(stats_string, "dscpmk_dscp0x%x", val);
 455 
 456         /* install stats entry */
 457         if ((err = ipp_stat_create(aid, stats_string, DSCPMK_DSCP_STATS_COUNT,
 458             dscpmk_update_det_stats, dscpmk_data,
 459             &dscpmk_data->dscp_stats[val].stats)) != 0) {
 460                 dscpmk0dbg(("dscpmk_det_statinit: ipp_stat_create returned "\
 461                     "with error %d\n", err));
 462                 return (err);
 463         }
 464 
 465         statp = (dscpmk_dscp_stats_t *)
 466             (dscpmk_data->dscp_stats[val].stats)->ipps_data;
 467         ASSERT(statp != NULL);
 468 
 469         if ((err = ipp_stat_named_init(dscpmk_data->dscp_stats[val].stats,
 470             "dscp", IPP_STAT_UINT32, &statp->dscp)) != 0) {
 471                 dscpmk0dbg(("dscpmk_det_statinit: ipp_stat_named_init "\
 472                     "returned with error %d\n", err));
 473                 return (err);
 474         }
 475 
 476         if ((err = ipp_stat_named_init(dscpmk_data->dscp_stats[val].stats,
 477             "npackets", IPP_STAT_UINT64, &statp->npackets)) != 0) {
 478                 dscpmk0dbg(("dscpmk_det_statinit: ipp_stat_named_init "\
 479                     "returned with error %d\n", err));
 480                 return (err);
 481         }
 482 
 483         ipp_stat_install(dscpmk_data->dscp_stats[val].stats);
 484         return (0);
 485 }
 486 
 487 
 488 static int
 489 dscpmk_summ_statinit(ipp_action_id_t aid, dscpmk_data_t *dscpmk_data)
 490 {
 491         int err = 0;
 492         dscpmk_stat_t *statp;
 493 
 494         /* install stats entry */
 495         if ((err = ipp_stat_create(aid, DSCPMK_STATS_STRING, DSCPMK_STATS_COUNT,
 496             dscpmk_update_stats, dscpmk_data, &dscpmk_data->stats)) != 0) {
 497                 dscpmk0dbg(("dscpmk_create_action: ipp_stat_create returned " \
 498                     "with error %d\n", err));
 499                 return (err);
 500         }
 501 
 502         statp = (dscpmk_stat_t *)(dscpmk_data->stats)->ipps_data;
 503         ASSERT(statp != NULL);
 504 
 505         if ((err = ipp_stat_named_init(dscpmk_data->stats, "npackets",
 506             IPP_STAT_UINT64, &statp->npackets)) != 0) {
 507                 dscpmk0dbg(("dscpmk_summ_statinit: ipp_stat_named_init " \
 508                     "returned with error %d\n", err));
 509                 return (err);
 510         }
 511 
 512         if ((err = ipp_stat_named_init(dscpmk_data->stats, "dscp_changed",
 513             IPP_STAT_UINT64, &statp->dscp_changed)) != 0) {
 514                 dscpmk0dbg(("dscpmk_summ_statinit: ipp_stat_named_init " \
 515                     "returned with error %d\n", err));
 516                 return (err);
 517         }
 518 
 519         if ((err = ipp_stat_named_init(dscpmk_data->stats, "dscp_unchanged",
 520             IPP_STAT_UINT64, &statp->dscp_unchanged)) != 0) {
 521                 dscpmk0dbg(("dscpmk_summ_statinit: ipp_stat_named_init " \
 522                     "returned with error %d\n", err));
 523                 return (err);
 524         }
 525 
 526         if ((err = ipp_stat_named_init(dscpmk_data->stats, "ipackets",
 527             IPP_STAT_UINT64, &statp->ipackets)) != 0) {
 528                 dscpmk0dbg(("dscpmk_summ_statinit: ipp_stat_named_init " \
 529                     "returned with error %d\n", err));
 530                 return (err);
 531         }
 532 
 533         if ((err = ipp_stat_named_init(dscpmk_data->stats, "epackets",
 534             IPP_STAT_UINT64, &statp->epackets)) != 0) {
 535                 dscpmk0dbg(("dscpmk_summ_statinit: ipp_stat_named_init " \
 536                     "returned with error %d\n", err));
 537                 return (err);
 538         }
 539 
 540         ipp_stat_install(dscpmk_data->stats);
 541         return (0);
 542 }
 543 
 544 /* ARGSUSED */
 545 static int
 546 dscpmk_update_det_stats(ipp_stat_t *sp, void *arg, int rw)
 547 {
 548         dscpmk_data_t *dscpmk_data = (dscpmk_data_t *)arg;
 549         dscpmk_dscp_stats_t *statp;
 550         uint32_t count;
 551 
 552         for (count = 0; count < DSCPMK_ARRAY_COUNT; count++) {
 553                 if (!dscpmk_data->dscp_stats[count].present)
 554                         continue;
 555                 statp = (dscpmk_dscp_stats_t *)
 556                     (dscpmk_data->dscp_stats[count].stats)->ipps_data;
 557                 ASSERT(statp != NULL);
 558                 (void) ipp_stat_named_op(&statp->npackets,
 559                     &dscpmk_data->dscp_stats[count].npackets, rw);
 560                 (void) ipp_stat_named_op(&statp->dscp, &count, rw);
 561         }
 562         return (0);
 563 }
 564 
 565 static int
 566 dscpmk_update_stats(ipp_stat_t *sp, void *arg, int rw)
 567 {
 568         dscpmk_data_t *dscpmk_data = (dscpmk_data_t *)arg;
 569         dscpmk_stat_t *snames = (dscpmk_stat_t *)sp->ipps_data;
 570         ASSERT(dscpmk_data != NULL);
 571         ASSERT(snames != NULL);
 572 
 573         (void) ipp_stat_named_op(&snames->npackets, &dscpmk_data->npackets, rw);
 574         (void) ipp_stat_named_op(&snames->dscp_changed, &dscpmk_data->changed,
 575             rw);
 576         (void) ipp_stat_named_op(&snames->dscp_unchanged,
 577             &dscpmk_data->unchanged, rw);
 578         (void) ipp_stat_named_op(&snames->ipackets, &dscpmk_data->ipackets, rw);
 579         (void) ipp_stat_named_op(&snames->epackets, &dscpmk_data->epackets, rw);
 580 
 581         return (0);
 582 }
 583 
 584 /* ARGSUSED */
 585 static int
 586 dscpmk_info(ipp_action_id_t aid, int (*fn)(nvlist_t *, void *), void *arg,
 587     ipp_flags_t flags)
 588 {
 589         nvlist_t *nvlp;
 590         dscpmk_data_t *dscpmk_data;
 591         char *next_action;
 592         int err, cnt;
 593         int32_t dscp_map[DSCPMK_ARRAY_COUNT];
 594 
 595         ASSERT(fn != NULL);
 596 
 597         dscpmk_data = (dscpmk_data_t *)ipp_action_get_ptr(aid);
 598         ASSERT(dscpmk_data != NULL);
 599 
 600         /* allocate nvlist to be passed back */
 601         if ((err = nvlist_alloc(&nvlp, NV_UNIQUE_NAME, KM_NOSLEEP)) != 0) {
 602                 dscpmk0dbg(("dscpmk_info: error allocating memory\n"));
 603                 return (err);
 604         }
 605 
 606         /* look up next action with the next action id */
 607         if ((err = ipp_action_name(dscpmk_data->next_action,
 608             &next_action)) != 0) {
 609                 dscpmk0dbg(("dscpmk_info: next action not available\n"));
 610                 nvlist_free(nvlp);
 611                 return (err);
 612         }
 613 
 614         /* add next action name */
 615         if ((err = nvlist_add_string(nvlp, DSCPMK_NEXT_ACTION_NAME,
 616             next_action)) != 0) {
 617                 dscpmk0dbg(("dscpmk_info: error adding next action\n"));
 618                 nvlist_free(nvlp);
 619                 kmem_free(next_action, (strlen(next_action) + 1));
 620                 return (err);
 621         }
 622 
 623         /* free action name */
 624         kmem_free(next_action, (strlen(next_action) + 1));
 625 
 626         /* add config type */
 627         if ((err = nvlist_add_byte(nvlp, IPP_CONFIG_TYPE, IPP_SET)) != 0) {
 628                 dscpmk0dbg(("dscpmk_info: error adding config type\n"));
 629                 nvlist_free(nvlp);
 630                 return (err);
 631         }
 632 
 633         /* add dscp map */
 634         bcopy(dscpmk_data->dscp_map, dscp_map, sizeof (dscp_map));
 635         for (cnt = 0; cnt < DSCPMK_ARRAY_COUNT; cnt++) {
 636                 dscp_map[cnt] = dscpmk_data->dscp_map[cnt];
 637         }
 638         if ((err = nvlist_add_int32_array(nvlp, DSCPMK_DSCP_MAP,
 639             dscp_map, DSCPMK_ARRAY_COUNT)) != 0) {
 640                 dscpmk0dbg(("dscpmk_info: error adding dscp map\n"));
 641                 nvlist_free(nvlp);
 642                 return (err);
 643         }
 644 
 645         /* add summary stats boolean */
 646         if ((err = nvlist_add_uint32(nvlp, IPP_ACTION_STATS_ENABLE,
 647             (dscpmk_data->summary_stats ? 1 : 0))) != 0) {
 648                 dscpmk0dbg(("dscpmk_info: error adding stats status\n"));
 649                 nvlist_free(nvlp);
 650                 return (err);
 651         }
 652 
 653         /* add detailed stats boolean */
 654         if ((err = nvlist_add_uint32(nvlp, DSCPMK_DETAILED_STATS,
 655             (dscpmk_data->detailed_stats ? 1 : 0))) != 0) {
 656                 dscpmk0dbg(("dscpmk_info: error adding det stats status\n"));
 657                 nvlist_free(nvlp);
 658                 return (err);
 659         }
 660 
 661         /* call back with nvlist */
 662         err = fn(nvlp, arg);
 663 
 664         nvlist_free(nvlp);
 665         return (err);
 666 }