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