xref: /illumos-gate/usr/src/uts/common/ipp/dscpmk/dscpmkddi.c (revision 85f4cb87104c72587029a6e0f1663332c85ba118)
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 }
667