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/atomic.h>
28#include <sys/systm.h>
29#include <sys/socket.h>
30#include <netinet/in.h>
31#include <sys/modctl.h>
32#include <sys/sunddi.h>
33#include <ipp/ipp.h>
34#include <ipp/ipp_config.h>
35#include <inet/common.h>
36#include <ipp/meters/meter_impl.h>
37
38#define	D_SM_COMMENT	"IPP Single-Two Rate Token Meter"
39
40/* DDI file for tokenmt ipp module */
41
42/* Default DSCP to colour mapping for colour-aware meter */
43enum meter_colour default_dscp_to_colour[64] = {
44	TOKENMT_GREEN, TOKENMT_GREEN, TOKENMT_GREEN, TOKENMT_GREEN,
45	TOKENMT_GREEN, TOKENMT_GREEN, TOKENMT_GREEN, TOKENMT_GREEN,
46	TOKENMT_GREEN, TOKENMT_GREEN, TOKENMT_GREEN, TOKENMT_GREEN,
47	TOKENMT_YELLOW, TOKENMT_GREEN, TOKENMT_RED, TOKENMT_GREEN,
48	TOKENMT_GREEN, TOKENMT_GREEN, TOKENMT_GREEN, TOKENMT_GREEN,
49	TOKENMT_YELLOW, TOKENMT_GREEN, TOKENMT_RED, TOKENMT_GREEN,
50	TOKENMT_GREEN, TOKENMT_GREEN, TOKENMT_GREEN, TOKENMT_GREEN,
51	TOKENMT_YELLOW, TOKENMT_GREEN, TOKENMT_RED, TOKENMT_GREEN,
52	TOKENMT_GREEN, TOKENMT_GREEN, TOKENMT_GREEN, TOKENMT_GREEN,
53	TOKENMT_YELLOW, TOKENMT_GREEN, TOKENMT_RED, TOKENMT_GREEN,
54	TOKENMT_GREEN, TOKENMT_GREEN, TOKENMT_GREEN, TOKENMT_GREEN,
55	TOKENMT_GREEN, TOKENMT_GREEN, TOKENMT_GREEN, TOKENMT_GREEN,
56	TOKENMT_GREEN, TOKENMT_GREEN, TOKENMT_GREEN, TOKENMT_GREEN,
57	TOKENMT_GREEN, TOKENMT_GREEN, TOKENMT_GREEN, TOKENMT_GREEN,
58	TOKENMT_GREEN, TOKENMT_GREEN, TOKENMT_GREEN, TOKENMT_GREEN,
59	TOKENMT_GREEN, TOKENMT_GREEN, TOKENMT_GREEN, TOKENMT_GREEN
60};
61
62static int tokenmt_create_action(ipp_action_id_t, nvlist_t **, ipp_flags_t);
63static int tokenmt_modify_action(ipp_action_id_t, nvlist_t **, ipp_flags_t);
64static int tokenmt_destroy_action(ipp_action_id_t, ipp_flags_t);
65static int tokenmt_info(ipp_action_id_t, int (*)(nvlist_t *, void *), void *,
66    ipp_flags_t);
67static int tokenmt_invoke_action(ipp_action_id_t, ipp_packet_t *);
68
69/* Initialize stats */
70static int tokenmt_statinit(ipp_action_id_t, tokenmt_data_t *);
71
72/* Stats callback function */
73static int tokenmt_update_stats(ipp_stat_t *, void *, int);
74
75ipp_ops_t tokenmt_ops = {
76	IPPO_REV,
77	tokenmt_create_action,	/* ippo_action_create */
78	tokenmt_modify_action,	/* ippo_action_modify */
79	tokenmt_destroy_action,	/* ippo_action_destroy */
80	tokenmt_info,		/* ippo_action_info */
81	tokenmt_invoke_action	/* ippo_action_invoke */
82};
83
84extern struct mod_ops mod_ippops;
85
86/*
87 * Module linkage information for the kernel.
88 */
89static struct modlipp modlipp = {
90	&mod_ippops,
91	D_SM_COMMENT,
92	&tokenmt_ops
93};
94
95static struct modlinkage modlinkage = {
96	MODREV_1,
97	(void *)&modlipp,
98	NULL
99};
100
101
102int
103_init(void)
104{
105	return (mod_install(&modlinkage));
106}
107
108int
109_fini(void)
110{
111	return (mod_remove(&modlinkage));
112}
113
114int
115_info(struct modinfo *modinfop)
116{
117	return (mod_info(&modlinkage, modinfop));
118}
119
120/* ARGSUSED */
121static int
122tokenmt_create_action(ipp_action_id_t aid, nvlist_t **nvlpp, ipp_flags_t flags)
123{
124	nvlist_t *nvlp;
125	tokenmt_data_t *tokenmt_data;
126	char *next_action;
127	tokenmt_cfg_t *cfg_parms;
128	uint32_t mode;
129	uint32_t bstats;
130	int rc, rc2;
131	int32_t *colour_tbl;
132	uint_t nelem = 64;
133
134	nvlp = *nvlpp;
135	*nvlpp = NULL;		/* nvlist should be NULL on return */
136
137	if ((cfg_parms = kmem_zalloc(TOKENMT_CFG_SZ, KM_NOSLEEP)) == NULL) {
138		nvlist_free(nvlp);
139		return (ENOMEM);
140	}
141
142	/* parse red next action name */
143	if ((rc = nvlist_lookup_string(nvlp, TOKENMT_RED_ACTION_NAME,
144	    &next_action)) != 0) {
145		nvlist_free(nvlp);
146		tokenmt0dbg(("tokenmt_create_action:invalid config, red "\
147		    "action name missing\n"));
148		kmem_free(cfg_parms, TOKENMT_CFG_SZ);
149		return (rc);
150	}
151	if ((cfg_parms->red_action = ipp_action_lookup(next_action))
152	    == IPP_ACTION_INVAL) {
153		nvlist_free(nvlp);
154		tokenmt0dbg(("tokenmt_create_action: red action invalid\n"));
155		kmem_free(cfg_parms, TOKENMT_CFG_SZ);
156		return (EINVAL);
157	}
158
159	/* parse yellow next action name, if present  this is Two Rate meter */
160	if ((rc = nvlist_lookup_string(nvlp, TOKENMT_YELLOW_ACTION_NAME,
161	    &next_action)) == 0) {
162		if ((cfg_parms->yellow_action = ipp_action_lookup(next_action))
163		    == IPP_ACTION_INVAL) {
164			nvlist_free(nvlp);
165			tokenmt0dbg(("tokenmt_create_action: yellow action "\
166			    "invalid\n"));
167			kmem_free(cfg_parms, TOKENMT_CFG_SZ);
168			return (EINVAL);
169		}
170	} else {
171		cfg_parms->yellow_action = TOKENMT_NO_ACTION;
172	}
173
174	/* parse green next action name */
175	if ((rc = nvlist_lookup_string(nvlp, TOKENMT_GREEN_ACTION_NAME,
176	    &next_action)) != 0) {
177		nvlist_free(nvlp);
178		tokenmt0dbg(("tokenmt_create_action:invalid config, green " \
179		    "action name missing\n"));
180		kmem_free(cfg_parms, TOKENMT_CFG_SZ);
181		return (rc);
182	}
183	if ((cfg_parms->green_action = ipp_action_lookup(next_action))
184	    == IPP_ACTION_INVAL) {
185		nvlist_free(nvlp);
186		tokenmt0dbg(("tokenmt_create_action: green action invalid\n"));
187		kmem_free(cfg_parms, TOKENMT_CFG_SZ);
188		return (EINVAL);
189	}
190
191	/* parse committed rate  - in kilo bits / sec */
192	if ((rc = nvlist_lookup_uint32(nvlp, TOKENMT_COMMITTED_RATE,
193	    &cfg_parms->committed_rate)) != 0) {
194		nvlist_free(nvlp);
195		tokenmt0dbg(("tokenmt_create_action: invalid config, "\
196		    " committed rate missing\n"));
197		kmem_free(cfg_parms, TOKENMT_CFG_SZ);
198		return (rc);
199	}
200	if (cfg_parms->committed_rate == 0) {
201		nvlist_free(nvlp);
202		tokenmt0dbg(("tokenmt_create_action: invalid committed rate, "\
203		    "%u\n", cfg_parms->committed_rate));
204		kmem_free(cfg_parms, TOKENMT_CFG_SZ);
205		return (EINVAL);
206	}
207
208	/* parse committed burst in bits */
209	if ((rc = nvlist_lookup_uint32(nvlp, TOKENMT_COMMITTED_BURST,
210	    &cfg_parms->committed_burst)) != 0) {
211		nvlist_free(nvlp);
212		tokenmt0dbg(("tokenmt_create_action: invalid config, "\
213		    " committed burst missing\n"));
214		kmem_free(cfg_parms, TOKENMT_CFG_SZ);
215		return (rc);
216	}
217
218
219	/*
220	 * If the peak burst size is specified, make sure we have the
221	 * yellow action.
222	 */
223	if ((rc = nvlist_lookup_uint32(nvlp, TOKENMT_PEAK_BURST,
224	    &cfg_parms->peak_burst)) == 0) {
225		if (cfg_parms->yellow_action == TOKENMT_NO_ACTION) {
226			nvlist_free(nvlp);
227			tokenmt0dbg(("tokenmt_create_action: peak burst "\
228			    "specified without yellow action\n"));
229			kmem_free(cfg_parms, TOKENMT_CFG_SZ);
230			return (EINVAL);
231		}
232	} else if (cfg_parms->yellow_action != TOKENMT_NO_ACTION) {
233		nvlist_free(nvlp);
234		tokenmt0dbg(("tokenmt_create_action: peak burst must be "\
235		    "provided with yellow action\n"));
236		kmem_free(cfg_parms, TOKENMT_CFG_SZ);
237		return (EINVAL);
238	}
239
240	/* Check if we have a peak_rate */
241	if ((rc = nvlist_lookup_uint32(nvlp, TOKENMT_PEAK_RATE,
242	    &cfg_parms->peak_rate)) == 0) {
243		if (cfg_parms->yellow_action == TOKENMT_NO_ACTION) {
244			nvlist_free(nvlp);
245			tokenmt0dbg(("tokenmt_create_action: peak rate "\
246			    "specified without yellow action\n"));
247			kmem_free(cfg_parms, TOKENMT_CFG_SZ);
248			return (EINVAL);
249		} else if ((cfg_parms->peak_rate == 0) ||
250		    (cfg_parms->peak_rate < cfg_parms->committed_rate)) {
251			nvlist_free(nvlp);
252			tokenmt0dbg(("tokenmt_create_action: invalid "\
253			    "peak rate, %u\n", cfg_parms->peak_rate));
254			kmem_free(cfg_parms, TOKENMT_CFG_SZ);
255			return (EINVAL);
256		}
257		cfg_parms->tokenmt_type = TRTCL_TOKENMT;
258	} else {
259		cfg_parms->tokenmt_type = SRTCL_TOKENMT;
260	}
261
262	/* Validate the committed and peak burst size */
263	if (cfg_parms->tokenmt_type == SRTCL_TOKENMT) {
264		if ((cfg_parms->committed_burst == 0) &&
265		    (cfg_parms->peak_burst == 0)) {
266			nvlist_free(nvlp);
267			tokenmt0dbg(("tokenmt_create_action: at least one "\
268			    "burst size must be non-zero\n"));
269			kmem_free(cfg_parms, TOKENMT_CFG_SZ);
270			return (EINVAL);
271		}
272	} else {	/* TRTCL_TOKENMT */
273		if ((cfg_parms->committed_burst == 0) ||
274		    (cfg_parms->peak_burst == 0)) {
275			nvlist_free(nvlp);
276			tokenmt0dbg(("tokenmt_create_action: both the "\
277			    "burst sizes must be non-zero\n"));
278			kmem_free(cfg_parms, TOKENMT_CFG_SZ);
279			return (EINVAL);
280		}
281	}
282
283	/* just copy default colour mapping */
284	bcopy(default_dscp_to_colour, cfg_parms->dscp_to_colour,
285	    sizeof (default_dscp_to_colour));
286
287	/* parse mode, if present */
288	if ((rc = nvlist_lookup_uint32(nvlp, TOKENMT_COLOUR_AWARE,
289	    &mode)) != 0) {
290		cfg_parms->colour_aware = B_FALSE;
291	} else {
292		cfg_parms->colour_aware = (mode == 0) ? B_FALSE : B_TRUE;
293	}
294
295	/* Get the dscp to colour mapping array */
296	if (cfg_parms->colour_aware) {
297		if ((rc = nvlist_lookup_int32_array(nvlp,
298		    TOKENMT_COLOUR_MAP, &colour_tbl, &nelem)) == 0) {
299			int count;
300			for (count = 0; count < 64; count++) {
301				if (colour_tbl[count] == -1)
302					continue;
303				cfg_parms->dscp_to_colour[count] =
304				    colour_tbl[count];
305			}
306		}
307	}
308
309	/* parse stats */
310	if ((rc = nvlist_lookup_uint32(nvlp, IPP_ACTION_STATS_ENABLE, &bstats))
311	    != 0) {
312		cfg_parms->stats = B_FALSE;
313	} else {
314		cfg_parms->stats = (bstats == 0) ? B_FALSE : B_TRUE;
315	}
316
317	nvlist_free(nvlp);
318
319	/* Initialize other stuff */
320	tokenmt_data = kmem_zalloc(TOKENMT_DATA_SZ, KM_NOSLEEP);
321	if (tokenmt_data == NULL) {
322		kmem_free(cfg_parms, TOKENMT_CFG_SZ);
323		return (ENOMEM);
324	}
325
326	/* Initialize stats, if required */
327	if (cfg_parms->stats) {
328		if ((rc = tokenmt_statinit(aid, tokenmt_data)) != 0) {
329			kmem_free(cfg_parms, TOKENMT_CFG_SZ);
330			kmem_free(tokenmt_data, TOKENMT_DATA_SZ);
331			return (rc);
332		}
333	}
334
335	/* set action chain reference */
336	if ((rc = ipp_action_ref(aid, cfg_parms->red_action, flags)) != 0) {
337		tokenmt0dbg(("tokenmt_create_action: ipp_action_ref " \
338		    "returned with error %d", rc));
339		goto cleanup;
340	}
341	if ((rc = ipp_action_ref(aid, cfg_parms->green_action, flags)) != 0) {
342		tokenmt0dbg(("tokenmt_create_action: ipp_action_ref " \
343		    "returned with error %d", rc));
344		rc2 = ipp_action_unref(aid, cfg_parms->red_action, flags);
345		ASSERT(rc2 == 0);
346		goto cleanup;
347	}
348
349	if (cfg_parms->yellow_action != TOKENMT_NO_ACTION) {
350		if ((rc = ipp_action_ref(aid, cfg_parms->yellow_action,
351		    flags)) != 0) {
352			tokenmt0dbg(("tokenmt_create_action: ipp_action_ref "\
353			    "returned with error %d", rc));
354			rc2 = ipp_action_unref(aid, cfg_parms->red_action,
355			    flags);
356			ASSERT(rc2 == 0);
357			rc2 = ipp_action_unref(aid, cfg_parms->green_action,
358			    flags);
359			ASSERT(rc2 == 0);
360			goto cleanup;
361		}
362	}
363
364
365	tokenmt_data->cfg_parms = cfg_parms;
366
367	tokenmt_data->committed_tokens = cfg_parms->committed_burst;
368	tokenmt_data->peak_tokens = cfg_parms->peak_burst;
369	tokenmt_data->last_seen = gethrtime();
370
371	mutex_init(&tokenmt_data->tokenmt_lock, NULL, MUTEX_DEFAULT, 0);
372	ipp_action_set_ptr(aid, (void *)tokenmt_data);
373	return (0);
374
375cleanup:
376	if (cfg_parms->stats) {
377		ipp_stat_destroy(tokenmt_data->stats);
378	}
379	kmem_free(cfg_parms, TOKENMT_CFG_SZ);
380	kmem_free(tokenmt_data, TOKENMT_DATA_SZ);
381	return (rc);
382}
383
384static int
385tokenmt_modify_action(ipp_action_id_t aid, nvlist_t **nvlpp, ipp_flags_t flags)
386{
387	nvlist_t *nvlp;
388	int err = 0, err2;
389	uint8_t config_type;
390	char *next_action_name;
391	ipp_action_id_t next_action;
392	uint32_t rate, cbs, pbs;
393	tokenmt_cfg_t *cfg_parms, *old_cfg;
394	tokenmt_data_t *tokenmt_data;
395	uint32_t bstats, mode;
396	int32_t *colour_tbl;
397	uint_t nelem = 64;
398
399	nvlp = *nvlpp;
400	*nvlpp = NULL;		/* nvlist should be NULL when this returns */
401
402	if ((err = nvlist_lookup_byte(nvlp, IPP_CONFIG_TYPE, &config_type))
403	    != 0) {
404		nvlist_free(nvlp);
405		tokenmt0dbg(("tokenmt_modify_action: invalid configuration "\
406		    "type"));
407		return (err);
408	}
409
410	if (config_type != IPP_SET) {
411		nvlist_free(nvlp);
412		tokenmt0dbg(("tokenmt_modify_action: invalid configuration "\
413		    "type %d", config_type));
414		return (EINVAL);
415	}
416
417	tokenmt_data = (tokenmt_data_t *)ipp_action_get_ptr(aid);
418	old_cfg = tokenmt_data->cfg_parms;
419
420	cfg_parms = kmem_zalloc(TOKENMT_CFG_SZ, KM_NOSLEEP);
421	if (cfg_parms == NULL) {
422		nvlist_free(nvlp);
423		tokenmt0dbg(("tokenmt_modify_action: memory allocation "\
424		    "failure\n"));
425		return (ENOMEM);
426	}
427
428	/* Just copy all and change as needed */
429	bcopy(old_cfg, cfg_parms, TOKENMT_CFG_SZ);
430
431	/* parse red action name, if present */
432	if ((err = nvlist_lookup_string(nvlp, TOKENMT_RED_ACTION_NAME,
433	    &next_action_name)) == 0) {
434		/* Get action id */
435		if ((next_action = ipp_action_lookup(next_action_name))
436		    == IPP_ACTION_INVAL) {
437			nvlist_free(nvlp);
438			tokenmt0dbg(("tokenmt_modify_action: next_action "\
439			    "invalid"));
440			kmem_free(cfg_parms, TOKENMT_CFG_SZ);
441			return (EINVAL);
442		}
443		cfg_parms->red_action = next_action;
444	}
445
446	/* parse yellow action name, if present */
447	if ((err = nvlist_lookup_string(nvlp, TOKENMT_YELLOW_ACTION_NAME,
448	    &next_action_name)) == 0) {
449		/* Get action id */
450		if ((next_action = ipp_action_lookup(next_action_name))
451		    == IPP_ACTION_INVAL) {
452			nvlist_free(nvlp);
453			tokenmt0dbg(("tokenmt_modify_action: next_action "\
454			    "invalid"));
455			kmem_free(cfg_parms, TOKENMT_CFG_SZ);
456			return (EINVAL);
457		}
458		cfg_parms->yellow_action = next_action;
459	} else {
460		cfg_parms->yellow_action = TOKENMT_NO_ACTION;
461	}
462
463	/* parse green action name, if present */
464	if ((err = nvlist_lookup_string(nvlp, TOKENMT_GREEN_ACTION_NAME,
465	    &next_action_name)) == 0) {
466		/* Get action id */
467		if ((next_action = ipp_action_lookup(next_action_name))
468		    == IPP_ACTION_INVAL) {
469			nvlist_free(nvlp);
470			tokenmt0dbg(("tokenmt_modify_action: next_action "\
471			    "invalid"));
472			kmem_free(cfg_parms, TOKENMT_CFG_SZ);
473			return (EINVAL);
474		}
475		cfg_parms->green_action = next_action;
476	}
477
478	/* parse committed rate, if present */
479	if ((err = nvlist_lookup_uint32(nvlp, TOKENMT_COMMITTED_RATE, &rate))
480	    == 0) {
481		if (rate == 0) {
482			nvlist_free(nvlp);
483			tokenmt0dbg(("tokenmt_modify_action: invalid "\
484			    "committed rate %u\n", cfg_parms->committed_rate));
485			kmem_free(cfg_parms, TOKENMT_CFG_SZ);
486			return (EINVAL);
487		}
488		cfg_parms->committed_rate = rate;
489	}
490
491	/* parse committed burst, if present */
492	if (nvlist_lookup_uint32(nvlp, TOKENMT_COMMITTED_BURST, &cbs) == 0) {
493		cfg_parms->committed_burst = cbs;
494	}
495
496
497	if (nvlist_lookup_uint32(nvlp, TOKENMT_PEAK_BURST, &pbs) == 0) {
498		cfg_parms->peak_burst = pbs;
499	} else {
500		cfg_parms->peak_burst = 0;
501	}
502
503	/* If the peak rate is not specified, then it means single rate meter */
504	if (nvlist_lookup_uint32(nvlp, TOKENMT_PEAK_RATE, &rate) == 0) {
505		cfg_parms->peak_rate = rate;
506		if ((rate == 0) || (rate < cfg_parms->committed_rate)) {
507			nvlist_free(nvlp);
508			tokenmt0dbg(("tokenmt_modify_action: invalid "\
509			    "committed rate %u\n", cfg_parms->committed_rate));
510			kmem_free(cfg_parms, TOKENMT_CFG_SZ);
511			return (EINVAL);
512		}
513		cfg_parms->tokenmt_type = TRTCL_TOKENMT;
514	} else {
515		cfg_parms->peak_rate = 0;
516		cfg_parms->tokenmt_type = SRTCL_TOKENMT;
517	}
518
519	if (cfg_parms->yellow_action == TOKENMT_NO_ACTION) {
520		if ((cfg_parms->peak_burst != 0) ||
521		    (cfg_parms->tokenmt_type == TRTCL_TOKENMT)) {
522			nvlist_free(nvlp);
523			tokenmt0dbg(("tokenmt_modify_action: yellow action "\
524			    "missing\n"));
525			kmem_free(cfg_parms, TOKENMT_CFG_SZ);
526			return (EINVAL);
527		}
528	} else {
529		if ((cfg_parms->tokenmt_type != TRTCL_TOKENMT) &&
530		    (cfg_parms->peak_burst == 0)) {
531			nvlist_free(nvlp);
532			tokenmt0dbg(("tokenmt_modify_action: peak "\
533			    "burst/rate missing\n"));
534			kmem_free(cfg_parms, TOKENMT_CFG_SZ);
535			return (EINVAL);
536		}
537	}
538
539	/* Validate the committed and peak burst size */
540	if (cfg_parms->tokenmt_type == SRTCL_TOKENMT) {
541		if ((cfg_parms->committed_burst == 0) &&
542		    (cfg_parms->peak_burst == 0)) {
543			nvlist_free(nvlp);
544			tokenmt0dbg(("tokenmt_modify_action: at least one "\
545			    "burst size must be non-zero\n"));
546			kmem_free(cfg_parms, TOKENMT_CFG_SZ);
547			return (EINVAL);
548		}
549	} else {	/* TRTCL_TOKENMT */
550		if ((cfg_parms->committed_burst == 0) ||
551		    (cfg_parms->peak_burst == 0)) {
552			nvlist_free(nvlp);
553			tokenmt0dbg(("tokenmt_modify_action: both the "\
554			    "burst sizes must be non-zero\n"));
555			kmem_free(cfg_parms, TOKENMT_CFG_SZ);
556			return (EINVAL);
557		}
558	}
559
560	/* parse mode */
561	if (nvlist_lookup_uint32(nvlp, TOKENMT_COLOUR_AWARE, &mode) == 0) {
562		cfg_parms->colour_aware = (mode == 0) ? B_FALSE : B_TRUE;
563	} else {
564		cfg_parms->colour_aware = B_FALSE;
565	}
566
567	if (cfg_parms->colour_aware) {
568		if (nvlist_lookup_int32_array(nvlp, TOKENMT_COLOUR_MAP,
569		    &colour_tbl, &nelem) == 0) {
570			int count;
571			for (count = 0; count < 64; count++) {
572				if (colour_tbl[count] == -1)
573					continue;
574				cfg_parms->dscp_to_colour[count] =
575				    colour_tbl[count];
576			}
577		} else {
578			bcopy(default_dscp_to_colour, cfg_parms->dscp_to_colour,
579			    sizeof (default_dscp_to_colour));
580		}
581	}
582
583	/* parse stats, if present */
584	if (nvlist_lookup_uint32(nvlp, IPP_ACTION_STATS_ENABLE, &bstats) == 0) {
585		cfg_parms->stats = (bstats == 0) ? B_FALSE : B_TRUE;
586		if (cfg_parms->stats && !old_cfg->stats) {
587			if ((err = tokenmt_statinit(aid, tokenmt_data)) != 0) {
588				nvlist_free(nvlp);
589				kmem_free(cfg_parms, TOKENMT_CFG_SZ);
590				return (err);
591			}
592		} else if (!cfg_parms->stats && old_cfg->stats) {
593			ipp_stat_destroy(tokenmt_data->stats);
594		}
595	}
596
597	/* Can we ref all the new actions? */
598	if ((err = ipp_action_ref(aid, cfg_parms->red_action, flags)) != 0) {
599		tokenmt0dbg(("tokenmt_modify_data: can't ref. red action\n"));
600		kmem_free(cfg_parms, TOKENMT_CFG_SZ);
601		return (err);
602	}
603	if ((err = ipp_action_ref(aid, cfg_parms->green_action, flags)) != 0) {
604		tokenmt0dbg(("tokenmt_modify_data:can't ref. green action\n"));
605		err2 = ipp_action_unref(aid, cfg_parms->red_action, flags);
606		ASSERT(err2 == 0);
607		kmem_free(cfg_parms, TOKENMT_CFG_SZ);
608		return (err);
609	}
610
611	if (cfg_parms->yellow_action != TOKENMT_NO_ACTION) {
612		if ((err = ipp_action_ref(aid, cfg_parms->yellow_action,
613		    flags)) != 0) {
614			tokenmt0dbg(("tokenmt_modify_data:can't ref. yellow "\
615			    "action\n"));
616			err2 = ipp_action_unref(aid, cfg_parms->red_action,
617			    flags);
618			ASSERT(err2 == 0);
619			err2 = ipp_action_unref(aid, cfg_parms->green_action,
620			    flags);
621			ASSERT(err2 == 0);
622			kmem_free(cfg_parms, TOKENMT_CFG_SZ);
623			return (err);
624		}
625	}
626
627
628	/* Actually modify the configuration */
629	mutex_enter(&tokenmt_data->tokenmt_lock);
630	tokenmt_data->cfg_parms = cfg_parms;
631	mutex_exit(&tokenmt_data->tokenmt_lock);
632
633	/* Un-ref the old actions */
634	err = ipp_action_unref(aid, old_cfg->red_action, flags);
635	ASSERT(err == 0);
636	if (old_cfg->yellow_action != TOKENMT_NO_ACTION) {
637		err = ipp_action_unref(aid, old_cfg->yellow_action, flags);
638		ASSERT(err == 0);
639	}
640	err = ipp_action_unref(aid, old_cfg->green_action, flags);
641	ASSERT(err == 0);
642
643	/* Free the old configuration */
644	kmem_free(old_cfg, TOKENMT_CFG_SZ);
645	return (0);
646}
647
648static int
649tokenmt_destroy_action(ipp_action_id_t aid, ipp_flags_t flags)
650{
651	tokenmt_data_t *tokenmt_data;
652	tokenmt_cfg_t *cfg_parms;
653	int rc;
654
655	tokenmt_data = (tokenmt_data_t *)ipp_action_get_ptr(aid);
656	ASSERT(tokenmt_data != NULL);
657
658	cfg_parms = tokenmt_data->cfg_parms;
659
660	if (cfg_parms->stats) {
661		ipp_stat_destroy(tokenmt_data->stats);
662	}
663
664	/* unreference the action */
665	rc = ipp_action_unref(aid, cfg_parms->red_action, flags);
666	ASSERT(rc == 0);
667	if (cfg_parms->yellow_action != TOKENMT_NO_ACTION) {
668		rc = ipp_action_unref(aid, cfg_parms->yellow_action, flags);
669		ASSERT(rc == 0);
670	}
671	rc = ipp_action_unref(aid, cfg_parms->green_action, flags);
672	ASSERT(rc == 0);
673
674	mutex_destroy(&tokenmt_data->tokenmt_lock);
675	kmem_free(cfg_parms, TOKENMT_CFG_SZ);
676	kmem_free(tokenmt_data, TOKENMT_DATA_SZ);
677	return (0);
678}
679
680static int
681tokenmt_invoke_action(ipp_action_id_t aid, ipp_packet_t *packet)
682{
683	tokenmt_data_t *tokenmt_data;
684	ipp_action_id_t next_action;
685	mblk_t *mp = NULL;
686	int rc;
687
688	/* get mblk from ipp_packet structure */
689	mp = ipp_packet_get_data(packet);
690	tokenmt_data = (tokenmt_data_t *)ipp_action_get_ptr(aid);
691	ASSERT(tokenmt_data != NULL);
692
693	/* meter packet as configured */
694	if ((rc = tokenmt_process(&mp, tokenmt_data, &next_action)) != 0) {
695		return (rc);
696	} else {
697		return (ipp_packet_next(packet, next_action));
698	}
699}
700
701static int
702tokenmt_statinit(ipp_action_id_t aid, tokenmt_data_t *tokenmt_data) {
703
704	int rc = 0;
705	meter_stat_t *statsp;
706
707	/* install stats entry */
708	if ((rc = ipp_stat_create(aid, TOKENMT_STATS_STRING, METER_STATS_COUNT,
709	    tokenmt_update_stats, tokenmt_data, &tokenmt_data->stats)) != 0) {
710		tokenmt0dbg(("tokenmt_statinit: ipp_stat_create failed "\
711		    " with %d\n", rc));
712		return (rc);
713	}
714
715	statsp = (meter_stat_t *)(tokenmt_data->stats)->ipps_data;
716	ASSERT(statsp != NULL);
717
718	if ((rc = ipp_stat_named_init(tokenmt_data->stats, "red_packets",
719	    IPP_STAT_UINT64, &statsp->red_packets)) != 0) {
720		tokenmt0dbg(("tokenmt_statinit:ipp_stat_named_init failed "\
721		    " with %d\n", rc));
722		return (rc);
723	}
724	if ((rc = ipp_stat_named_init(tokenmt_data->stats, "yellow_packets",
725	    IPP_STAT_UINT64, &statsp->yellow_packets)) != 0) {
726		tokenmt0dbg(("tokenmt_statinit:ipp_stat_named_init failed "\
727		    " with %d\n", rc));
728		return (rc);
729	}
730	if ((rc = ipp_stat_named_init(tokenmt_data->stats, "green_packets",
731	    IPP_STAT_UINT64, &statsp->green_packets)) != 0) {
732		tokenmt0dbg(("tokenmt_statinit:ipp_stat_named_init failed "\
733		    " with %d\n", rc));
734		return (rc);
735	}
736	if ((rc = ipp_stat_named_init(tokenmt_data->stats, "red_bits",
737	    IPP_STAT_UINT64, &statsp->red_bits)) != 0) {
738		tokenmt0dbg(("tokenmt_statinit:ipp_stat_named_init failed "\
739		    " with %d\n", rc));
740		return (rc);
741	}
742	if ((rc = ipp_stat_named_init(tokenmt_data->stats, "yellow_bits",
743	    IPP_STAT_UINT64, &statsp->yellow_bits)) != 0) {
744		tokenmt0dbg(("tokenmt_statinit:ipp_stat_named_init failed "\
745		    " with %d\n", rc));
746		return (rc);
747	}
748	if ((rc = ipp_stat_named_init(tokenmt_data->stats, "green_bits",
749	    IPP_STAT_UINT64, &statsp->green_bits)) != 0) {
750		tokenmt0dbg(("tokenmt_statinit:ipp_stat_named_init failed "\
751		    " with %d\n", rc));
752		return (rc);
753	}
754	if ((rc = ipp_stat_named_init(tokenmt_data->stats, "epackets",
755	    IPP_STAT_UINT64, &statsp->epackets)) != 0) {
756		tokenmt0dbg(("tokenmt_statinit:ipp_stat_named_init failed "\
757		    " with %d\n", rc));
758		return (rc);
759	}
760
761	ipp_stat_install(tokenmt_data->stats);
762
763	return (rc);
764}
765
766static int
767tokenmt_update_stats(ipp_stat_t *sp, void *args, int rw)
768{
769	tokenmt_data_t *tokenmt_data = (tokenmt_data_t *)args;
770	meter_stat_t *stats = (meter_stat_t *)sp->ipps_data;
771
772	ASSERT((tokenmt_data != NULL) && (stats != NULL));
773
774	(void) ipp_stat_named_op(&stats->red_packets,
775	    &tokenmt_data->red_packets, rw);
776	(void) ipp_stat_named_op(&stats->yellow_packets,
777	    &tokenmt_data->yellow_packets, rw);
778	(void) ipp_stat_named_op(&stats->green_packets,
779	    &tokenmt_data->green_packets, rw);
780	(void) ipp_stat_named_op(&stats->red_bits,
781	    &tokenmt_data->red_bits, rw);
782	(void) ipp_stat_named_op(&stats->yellow_bits,
783	    &tokenmt_data->yellow_bits, rw);
784	(void) ipp_stat_named_op(&stats->green_bits,
785	    &tokenmt_data->green_bits, rw);
786	(void) ipp_stat_named_op(&stats->epackets, &tokenmt_data->epackets,
787	    rw);
788
789	return (0);
790}
791
792/* ARGSUSED */
793static int
794tokenmt_info(ipp_action_id_t aid, int (*fn)(nvlist_t *, void *), void *arg,
795    ipp_flags_t flags)
796{
797	nvlist_t *nvlp;
798	tokenmt_data_t *tokenmt_data;
799	tokenmt_cfg_t *cfg_parms;
800	char *next_action;
801	int32_t dscp_to_colour[64];
802	int rc;
803
804	tokenmt_data = (tokenmt_data_t *)ipp_action_get_ptr(aid);
805	ASSERT(tokenmt_data != NULL);
806
807	cfg_parms = tokenmt_data->cfg_parms;
808
809	/* allocate nvlist to be passed back */
810	if ((rc = nvlist_alloc(&nvlp, NV_UNIQUE_NAME, KM_NOSLEEP)) != 0) {
811		tokenmt0dbg(("tokenmt_info: memory allocation failure\n"));
812		return (rc);
813	}
814
815	/* look up red next action with the next action id */
816	if ((rc = ipp_action_name(cfg_parms->red_action, &next_action)) != 0) {
817		tokenmt0dbg(("tokenmt_info: red_action not available\n"));
818		nvlist_free(nvlp);
819		return (rc);
820	}
821
822	/* add next action name */
823	if ((rc = nvlist_add_string(nvlp, TOKENMT_RED_ACTION_NAME,
824	    next_action)) != 0) {
825		nvlist_free(nvlp);
826		tokenmt0dbg(("tokenmt_info: error adding red_action\n"));
827		kmem_free(next_action, (strlen(next_action) + 1));
828		return (rc);
829	}
830
831	/* free action name */
832	kmem_free(next_action, (strlen(next_action) + 1));
833
834
835	/* look up yellow next action with the next action id */
836	if (cfg_parms->yellow_action != TOKENMT_NO_ACTION) {
837		if ((rc = ipp_action_name(cfg_parms->yellow_action,
838		    &next_action)) != 0) {
839			tokenmt0dbg(("tokenmt_info: yellow_action not "\
840			    "available\n"));
841			nvlist_free(nvlp);
842			return (rc);
843		}
844		/* add next action name */
845		if ((rc = nvlist_add_string(nvlp, TOKENMT_YELLOW_ACTION_NAME,
846		    next_action)) != 0) {
847			nvlist_free(nvlp);
848			tokenmt0dbg(("tokenmt_info: error adding "\
849			    "yellow_action\n"));
850			kmem_free(next_action, (strlen(next_action) + 1));
851			return (rc);
852		}
853		/* free action name */
854		kmem_free(next_action, (strlen(next_action) + 1));
855	}
856
857	/* look up green next action with the next action id */
858	if ((rc = ipp_action_name(cfg_parms->green_action,
859	    &next_action)) != 0) {
860		tokenmt0dbg(("tokenmt_info: green_action not available\n"));
861		nvlist_free(nvlp);
862		return (rc);
863	}
864
865	/* add next action name */
866	if ((rc = nvlist_add_string(nvlp, TOKENMT_GREEN_ACTION_NAME,
867	    next_action)) != 0) {
868		nvlist_free(nvlp);
869		tokenmt0dbg(("tokenmt_info: error adding green_action\n"));
870		kmem_free(next_action, (strlen(next_action) + 1));
871		return (rc);
872	}
873
874	/* free action name */
875	kmem_free(next_action, (strlen(next_action) + 1));
876
877	/* add config type */
878	if ((rc = nvlist_add_byte(nvlp, IPP_CONFIG_TYPE, IPP_SET)) != 0) {
879		tokenmt0dbg(("tokenmt_info: error adding config_type\n"));
880		nvlist_free(nvlp);
881		return (rc);
882	}
883
884	/* add committed_rate  */
885	if ((rc = nvlist_add_uint32(nvlp, TOKENMT_COMMITTED_RATE,
886	    cfg_parms->committed_rate)) != 0) {
887		tokenmt0dbg(("tokenmt_info: error adding committed_rate\n"));
888		nvlist_free(nvlp);
889		return (rc);
890	}
891
892	if (cfg_parms->tokenmt_type == TRTCL_TOKENMT) {
893		/* add peak  rate */
894		if ((rc = nvlist_add_uint32(nvlp, TOKENMT_PEAK_RATE,
895		    cfg_parms->peak_rate)) != 0) {
896			tokenmt0dbg(("tokenmt_info: error adding peak_rate\n"));
897			nvlist_free(nvlp);
898			return (rc);
899		}
900	}
901
902	/* add committed_burst  */
903	if ((rc = nvlist_add_uint32(nvlp, TOKENMT_COMMITTED_BURST,
904	    cfg_parms->committed_burst)) != 0) {
905		tokenmt0dbg(("tokenmt_info: error adding committed_burst\n"));
906		nvlist_free(nvlp);
907		return (rc);
908	}
909
910	/* add peak_burst  */
911	if (cfg_parms->peak_burst != 0) {
912		if ((rc = nvlist_add_uint32(nvlp, TOKENMT_PEAK_BURST,
913		    cfg_parms->peak_burst)) != 0) {
914			tokenmt0dbg(("tokenmt_info: error adding peak "\
915			    "burst\n"));
916			nvlist_free(nvlp);
917			return (rc);
918		}
919	}
920
921	/* add colour aware  */
922	if ((rc = nvlist_add_uint32(nvlp, TOKENMT_COLOUR_AWARE,
923	    cfg_parms->colour_aware)) != 0) {
924		tokenmt0dbg(("tokenmt_info: error adding mode\n"));
925		nvlist_free(nvlp);
926		return (rc);
927	}
928
929	if (cfg_parms->colour_aware) {
930		bcopy(cfg_parms->dscp_to_colour, dscp_to_colour,
931		    sizeof (cfg_parms->dscp_to_colour));
932		if ((rc = nvlist_add_int32_array(nvlp, TOKENMT_COLOUR_MAP,
933		    dscp_to_colour, 64)) != 0) {
934			tokenmt0dbg(("tokenmt_info: error adding colour "\
935			    "array\n"));
936			nvlist_free(nvlp);
937			return (rc);
938		}
939	}
940
941	if ((rc = nvlist_add_uint32(nvlp, IPP_ACTION_STATS_ENABLE,
942	    (uint32_t)cfg_parms->stats)) != 0) {
943		tokenmt0dbg(("tokenmt_info: error adding stats status\n"));
944		nvlist_free(nvlp);
945		return (rc);
946	}
947
948	/* call back with nvlist */
949	rc = fn(nvlp, arg);
950
951	nvlist_free(nvlp);
952	return (rc);
953}
954