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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2002 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <sys/types.h>
30 #include <sys/atomic.h>
31 #include <sys/systm.h>
32 #include <sys/socket.h>
33 #include <netinet/in.h>
34 #include <sys/modctl.h>
35 #include <sys/sunddi.h>
36 #include <ipp/ipp.h>
37 #include <ipp/ipp_config.h>
38 #include <inet/common.h>
39 #include <ipp/dlcosmk/dlcosmk_impl.h>
40 
41 #define	D_SM_COMMENT	"IPP dlcosmk marker module"
42 
43 /* DDI file for dlcosmk ipp module */
44 
45 static int dlcosmk_create_action(ipp_action_id_t, nvlist_t **, ipp_flags_t);
46 static int dlcosmk_modify_action(ipp_action_id_t, nvlist_t **, ipp_flags_t);
47 static int dlcosmk_destroy_action(ipp_action_id_t, ipp_flags_t);
48 static int dlcosmk_info(ipp_action_id_t, int (*)(nvlist_t *, void *), void *,
49     ipp_flags_t);
50 static int dlcosmk_invoke_action(ipp_action_id_t, ipp_packet_t *);
51 
52 static int dlcosmk_statinit(ipp_action_id_t, dlcosmk_data_t *);
53 static int dlcosmk_update_stats(ipp_stat_t *, void *, int);
54 
55 /* Entry points for this IPP module */
56 ipp_ops_t dlcosmk_ops = {
57 	IPPO_REV,
58 	dlcosmk_create_action,	/* ippo_action_create */
59 	dlcosmk_modify_action,	/* ippo_action_modify */
60 	dlcosmk_destroy_action,	/* ippo_action_destroy */
61 	dlcosmk_info,		/* ippo_action_info */
62 	dlcosmk_invoke_action	/* ippo_action_invoke */
63 };
64 
65 extern struct mod_ops mod_ippops;
66 
67 /*
68  * Module linkage information for the kernel.
69  */
70 static struct modlipp modlipp = {
71 	&mod_ippops,
72 	D_SM_COMMENT " %I%",
73 	&dlcosmk_ops
74 };
75 
76 static struct modlinkage modlinkage = {
77 	MODREV_1,
78 	(void *)&modlipp,
79 	NULL
80 };
81 
82 
83 int
84 _init(void)
85 {
86 	return (mod_install(&modlinkage));
87 }
88 
89 int
90 _fini(void)
91 {
92 	return (mod_remove(&modlinkage));
93 }
94 
95 int
96 _info(struct modinfo *modinfop)
97 {
98 	return (mod_info(&modlinkage, modinfop));
99 }
100 
101 static int
102 dlcosmk_create_action(ipp_action_id_t aid, nvlist_t **nvlpp,
103     ipp_flags_t flags)
104 {
105 	nvlist_t *nvlp;
106 	dlcosmk_data_t *dlcosmk_data;
107 	char *next_action;
108 	int err;
109 	uint32_t bstats, param;
110 
111 	ASSERT((nvlpp != NULL) && (*nvlpp != NULL));
112 
113 	nvlp = *nvlpp;
114 	*nvlpp = NULL;		/* nvlist should be NULL on return */
115 
116 	if ((dlcosmk_data = kmem_zalloc(DLCOSMK_DATA_SZ, KM_NOSLEEP)) == NULL) {
117 		nvlist_free(nvlp);
118 		return (ENOMEM);
119 	}
120 
121 	/* parse next action name */
122 	if ((err = nvlist_lookup_string(nvlp, DLCOSMK_NEXT_ACTION_NAME,
123 	    &next_action)) != 0) {
124 		nvlist_free(nvlp);
125 		dlcosmk0dbg(("dlcosmk_create_action: invalid config, "\
126 		    "next_action name missing\n"));
127 		kmem_free(dlcosmk_data, DLCOSMK_DATA_SZ);
128 		return (err);
129 	}
130 	if ((dlcosmk_data->next_action =
131 	    ipp_action_lookup(next_action)) == IPP_ACTION_INVAL) {
132 		nvlist_free(nvlp);
133 		dlcosmk0dbg(("dlcosmk_create_action: next_action invalid\n"));
134 		kmem_free(dlcosmk_data, DLCOSMK_DATA_SZ);
135 		return (EINVAL);
136 	}
137 
138 	/* parse cos - from the config file */
139 	if ((err = nvlist_lookup_byte(nvlp, DLCOSMK_COS,
140 	    &dlcosmk_data->usr_pri)) != 0) {
141 		nvlist_free(nvlp);
142 		dlcosmk0dbg(("dlcosmk_create_action: invalid config, "\
143 		    "cos missing\n"));
144 		kmem_free(dlcosmk_data, DLCOSMK_DATA_SZ);
145 		return (err);
146 	}
147 
148 	/* parse b_band - mapped from cos */
149 	if ((err = nvlist_lookup_uint32(nvlp, DLCOSMK_BAND, &param)) != 0) {
150 		nvlist_free(nvlp);
151 		dlcosmk0dbg(("dlcosmk_create_action: invalid config, "\
152 		    "b_band missing\n"));
153 		kmem_free(dlcosmk_data, DLCOSMK_DATA_SZ);
154 		return (err);
155 	}
156 	dlcosmk_data->b_band = param;
157 
158 	/* parse dl_priority.dl_max  - mapped from cos */
159 	if ((err = nvlist_lookup_uint32(nvlp, DLCOSMK_PRI, &param)) != 0) {
160 		nvlist_free(nvlp);
161 		dlcosmk0dbg(("dlcosmk_create_action: invalid config, "\
162 		    "dl_priority missing\n"));
163 		kmem_free(dlcosmk_data, DLCOSMK_DATA_SZ);
164 		return (err);
165 	}
166 	dlcosmk_data->dl_max = param;
167 
168 	/* parse gather_stats boolean */
169 	if ((err = nvlist_lookup_uint32(nvlp, IPP_ACTION_STATS_ENABLE, &bstats))
170 	    != 0) {
171 		dlcosmk_data->gather_stats = B_FALSE;
172 	} else {
173 		/* If stats is needed, initialize the stats structure */
174 		dlcosmk_data->gather_stats = (bstats != 0) ? B_TRUE : B_FALSE;
175 		if (dlcosmk_data->gather_stats) {
176 			if ((err = dlcosmk_statinit(aid, dlcosmk_data)) != 0) {
177 				nvlist_free(nvlp);
178 				kmem_free(dlcosmk_data, DLCOSMK_DATA_SZ);
179 				return (err);
180 			}
181 		}
182 	}
183 
184 	/* Free the nvlist */
185 	nvlist_free(nvlp);
186 
187 	/* set action chain reference */
188 	if ((err = ipp_action_ref(aid, dlcosmk_data->next_action,
189 	    flags)) != 0) {
190 		dlcosmk0dbg(("dlcosmk_create_action: ipp_action_ref " \
191 		    "returned with error %d\n", err));
192 		ipp_stat_destroy(dlcosmk_data->stats);
193 		kmem_free(dlcosmk_data, DLCOSMK_DATA_SZ);
194 		return (err);
195 	}
196 
197 	ipp_action_set_ptr(aid, (void *)dlcosmk_data);
198 	return (0);
199 }
200 
201 static int
202 dlcosmk_modify_action(ipp_action_id_t aid, nvlist_t **nvlpp, ipp_flags_t flags)
203 {
204 	nvlist_t *nvlp;
205 	int err = 0;
206 	uint32_t band, dlpri;
207 	uint8_t config_type;
208 	uint8_t cos;
209 	char *next_action_name;
210 	ipp_action_id_t next_action;
211 	dlcosmk_data_t *dlcosmk_data;
212 	uint32_t bstats;
213 
214 	ASSERT((nvlpp != NULL) && (*nvlpp != NULL));
215 
216 	nvlp = *nvlpp;
217 	*nvlpp = NULL;		/* nvlist should be NULL when this returns */
218 
219 	if ((err = nvlist_lookup_byte(nvlp, IPP_CONFIG_TYPE, &config_type))
220 	    != 0) {
221 		nvlist_free(nvlp);
222 		dlcosmk0dbg(("dlcosmk_modify_action: invalid configuration "\
223 		    "type\n"));
224 		return (err);
225 	}
226 
227 	if (config_type != IPP_SET) {
228 		nvlist_free(nvlp);
229 		dlcosmk0dbg(("dlcosmk_modify_action: invalid configuration "\
230 		    "type %d\n", config_type));
231 		return (EINVAL);
232 	}
233 
234 	dlcosmk_data = (dlcosmk_data_t *)ipp_action_get_ptr(aid);
235 	ASSERT(dlcosmk_data != NULL);
236 
237 	/* parse next action name, if present */
238 	if ((err = nvlist_lookup_string(nvlp, DLCOSMK_NEXT_ACTION_NAME,
239 	    &next_action_name)) == 0) {
240 		/* lookup action name to get action id */
241 		if ((next_action = ipp_action_lookup(next_action_name))
242 		    == IPP_ACTION_INVAL) {
243 			nvlist_free(nvlp);
244 			dlcosmk0dbg(("dlcosmk_modify_action: next_action "\
245 			    "invalid\n"));
246 			return (EINVAL);
247 		}
248 		/* reference new action */
249 		if ((err = ipp_action_ref(aid, next_action, flags)) != 0) {
250 			nvlist_free(nvlp);
251 			dlcosmk0dbg(("dlcosmk_modify_action: ipp_action_ref "\
252 			    "returned with error %d\n", err));
253 			return (err);
254 		}
255 		/* unref old action */
256 		err = ipp_action_unref(aid, dlcosmk_data->next_action, flags);
257 		ASSERT(err == 0);
258 		dlcosmk_data->next_action = next_action;
259 	}
260 
261 	/* parse cos, if present */
262 	if ((err = nvlist_lookup_byte(nvlp, DLCOSMK_COS, &cos)) == 0) {
263 
264 		/* parse b_band, mapped from cos */
265 		if ((err = nvlist_lookup_uint32(nvlp, DLCOSMK_BAND,
266 		    &band)) != 0) {
267 			nvlist_free(nvlp);
268 			dlcosmk0dbg(("dlcosmk_modify_action: b_band not "\
269 			    "provided\n"));
270 			return (err);
271 		}
272 
273 		/* parse dl_priority, mapped from cos */
274 		if ((err = nvlist_lookup_uint32(nvlp, DLCOSMK_PRI,
275 		    &dlpri)) != 0) {
276 			nvlist_free(nvlp);
277 			dlcosmk0dbg(("dlcosmk_modify_action: dl_priority not "\
278 			    "provided\n"));
279 			return (err);
280 		}
281 
282 		/* Have all the three values, change them */
283 		dlcosmk_data->usr_pri = cos;
284 		dlcosmk_data->b_band = band;
285 		dlcosmk_data->dl_max = dlpri;
286 	}
287 
288 
289 	/* parse gather_stats boolean, if present */
290 	if ((err = nvlist_lookup_uint32(nvlp, IPP_ACTION_STATS_ENABLE, &bstats))
291 	    == 0) {
292 		boolean_t val = (bstats != 0) ? B_TRUE : B_FALSE;
293 		/* Turning on stats */
294 		if (!dlcosmk_data->gather_stats && val) {
295 			if ((err = dlcosmk_statinit(aid, dlcosmk_data)) != 0) {
296 				nvlist_free(nvlp);
297 				return (err);
298 			}
299 		/* Turning off stats */
300 		} else if (!val && dlcosmk_data->gather_stats) {
301 			ipp_stat_destroy(dlcosmk_data->stats);
302 
303 		}
304 		dlcosmk_data->gather_stats = val;
305 	}
306 
307 	/* Free thenvlist */
308 	nvlist_free(nvlp);
309 	return (0);
310 }
311 
312 static int
313 dlcosmk_destroy_action(ipp_action_id_t aid, ipp_flags_t flags)
314 {
315 	dlcosmk_data_t *dlcosmk_data;
316 	int err;
317 
318 	dlcosmk_data = (dlcosmk_data_t *)ipp_action_get_ptr(aid);
319 	ASSERT(dlcosmk_data != NULL);
320 
321 	/* Destroy stats, if gathered */
322 	if (dlcosmk_data->gather_stats) {
323 		ipp_stat_destroy(dlcosmk_data->stats);
324 	}
325 
326 	/* unreference the action */
327 	err = ipp_action_unref(aid, dlcosmk_data->next_action, flags);
328 	ASSERT(err == 0);
329 
330 	kmem_free(dlcosmk_data, DLCOSMK_DATA_SZ);
331 	return (0);
332 }
333 
334 static int
335 dlcosmk_invoke_action(ipp_action_id_t aid, ipp_packet_t *packet)
336 {
337 	dlcosmk_data_t *dlcosmk_data;
338 	mblk_t *mp = NULL;
339 	int err;
340 	ip_priv_t *priv;
341 
342 	ASSERT(packet != NULL);
343 
344 	/* get mblk from ipp_packet structure */
345 	mp = ipp_packet_get_data(packet);
346 	priv = (ip_priv_t *)ipp_packet_get_private(packet);
347 
348 	dlcosmk_data = (dlcosmk_data_t *)ipp_action_get_ptr(aid);
349 	ASSERT(dlcosmk_data != NULL);
350 
351 	/* dlcosmk packet as configured */
352 	if ((err = dlcosmk_process(&mp, dlcosmk_data, priv->ill_index,
353 	    priv->proc)) != 0) {
354 		return (err);
355 	} else {
356 		/* return packet with next action set */
357 		return (ipp_packet_next(packet, dlcosmk_data->next_action));
358 	}
359 }
360 
361 static int
362 dlcosmk_statinit(ipp_action_id_t aid, dlcosmk_data_t *dlcosmk_data)
363 {
364 	int err;
365 	dlcosmk_stat_t *statp;
366 
367 	/* install stats entry */
368 	if ((err = ipp_stat_create(aid, DLCOSMK_STATS_STRING,
369 	    DLCOSMK_STATS_COUNT, dlcosmk_update_stats, dlcosmk_data,
370 	    &dlcosmk_data->stats)) != 0) {
371 		dlcosmk0dbg(("dlcosmk_create_action: ipp_stat_create " \
372 		    "returned with error %d\n", err));
373 		return (err);
374 	}
375 
376 	statp = (dlcosmk_stat_t *)(dlcosmk_data->stats)->ipps_data;
377 	ASSERT(statp != NULL);
378 
379 	if ((err = ipp_stat_named_init(dlcosmk_data->stats, "npackets",
380 	    IPP_STAT_UINT64, &statp->npackets)) != 0) {
381 		dlcosmk0dbg(("dlcosmk_create_action: ipp_stat_named_init " \
382 		    "returned with error %d\n", err));
383 		return (err);
384 	}
385 
386 	if ((err = ipp_stat_named_init(dlcosmk_data->stats, "ipackets",
387 	    IPP_STAT_UINT64, &statp->ipackets)) != 0) {
388 		dlcosmk0dbg(("dlcosmk_create_action: ipp_stat_named_init " \
389 		    "returned with error %d\n", err));
390 		return (err);
391 	}
392 
393 	if ((err = ipp_stat_named_init(dlcosmk_data->stats, "epackets",
394 	    IPP_STAT_UINT64, &statp->epackets)) != 0) {
395 		dlcosmk0dbg(("dlcosmk_create_action: ipp_stat_named_init " \
396 		    "returned with error %d\n", err));
397 		return (err);
398 	}
399 
400 	if ((err = ipp_stat_named_init(dlcosmk_data->stats, "usr_pri",
401 	    IPP_STAT_INT32, &statp->usr_pri)) != 0) {
402 		dlcosmk0dbg(("dlcosmk_create_action: ipp_stat_named_init " \
403 		    "returned with error %d", err));
404 		return (err);
405 	}
406 
407 	if ((err = ipp_stat_named_init(dlcosmk_data->stats, "b_band",
408 	    IPP_STAT_INT32, &statp->b_band)) != 0) {
409 		dlcosmk0dbg(("dlcosmk_create_action: ipp_stat_named_init " \
410 		    "returned with error %d\n", err));
411 		return (err);
412 	}
413 
414 	if ((err = ipp_stat_named_init(dlcosmk_data->stats, "dl_max",
415 	    IPP_STAT_INT32, &statp->dl_max)) != 0) {
416 		dlcosmk0dbg(("dlcosmk_create_action: ipp_stat_named_init " \
417 		    "returned with error %d\n", err));
418 		return (err);
419 	}
420 
421 	ipp_stat_install(dlcosmk_data->stats);
422 	return (0);
423 }
424 
425 static int
426 dlcosmk_update_stats(ipp_stat_t *sp, void *arg, int rw)
427 {
428 	dlcosmk_data_t *dlcosmk_data = (dlcosmk_data_t *)arg;
429 	dlcosmk_stat_t *snames = (dlcosmk_stat_t *)sp->ipps_data;
430 	uint32_t upri, bband;
431 
432 	ASSERT(dlcosmk_data != NULL);
433 	ASSERT(snames != NULL);
434 
435 	upri = dlcosmk_data->usr_pri;
436 	bband = dlcosmk_data->b_band;
437 
438 	(void) ipp_stat_named_op(&snames->npackets, &dlcosmk_data->npackets,
439 	    rw);
440 	(void) ipp_stat_named_op(&snames->ipackets, &dlcosmk_data->ipackets,
441 	    rw);
442 	(void) ipp_stat_named_op(&snames->epackets, &dlcosmk_data->epackets,
443 	    rw);
444 	(void) ipp_stat_named_op(&snames->usr_pri, &upri, rw);
445 	(void) ipp_stat_named_op(&snames->b_band, &bband, rw);
446 	(void) ipp_stat_named_op(&snames->dl_max, &dlcosmk_data->dl_max, rw);
447 
448 	return (0);
449 }
450 
451 /* ARGSUSED */
452 static int
453 dlcosmk_info(ipp_action_id_t aid, int (*fn)(nvlist_t *, void *), void *arg,
454     ipp_flags_t flags)
455 {
456 	nvlist_t *nvlp;
457 	dlcosmk_data_t *dlcosmk_data;
458 	char *next_action;
459 	int err;
460 
461 	ASSERT(fn != NULL);
462 
463 	dlcosmk_data = (dlcosmk_data_t *)ipp_action_get_ptr(aid);
464 	ASSERT(dlcosmk_data != NULL);
465 
466 	/* allocate nvlist to be passed back */
467 	if ((err = nvlist_alloc(&nvlp, NV_UNIQUE_NAME, KM_NOSLEEP)) != 0) {
468 		dlcosmk0dbg(("dlcosmk_info: error allocating memory\n"));
469 		return (err);
470 	}
471 
472 	/* look up next action with the next action id */
473 	if ((err = ipp_action_name(dlcosmk_data->next_action,
474 	    &next_action)) != 0) {
475 		dlcosmk0dbg(("dlcosmk_info: next action not available\n"));
476 		nvlist_free(nvlp);
477 		return (err);
478 	}
479 
480 	/* add next action name */
481 	if ((err = nvlist_add_string(nvlp, DLCOSMK_NEXT_ACTION_NAME,
482 	    next_action)) != 0) {
483 		dlcosmk0dbg(("dlcosmk_info: error adding next action\n"));
484 		nvlist_free(nvlp);
485 		kmem_free(next_action, (strlen(next_action) + 1));
486 		return (err);
487 	}
488 
489 	/* free action name */
490 	kmem_free(next_action, (strlen(next_action) + 1));
491 
492 	/* add config type */
493 	if ((err = nvlist_add_byte(nvlp, IPP_CONFIG_TYPE, IPP_SET)) != 0) {
494 		dlcosmk0dbg(("dlcosmk_info: error adding config. type\n"));
495 		nvlist_free(nvlp);
496 		return (err);
497 	}
498 
499 	/* just give the cos, since that is what is provided in the config */
500 	if ((err = nvlist_add_byte(nvlp, DLCOSMK_COS, dlcosmk_data->usr_pri))
501 	    != 0) {
502 		dlcosmk0dbg(("dlcosmk_info: error adding cos\n"));
503 		nvlist_free(nvlp);
504 		return (err);
505 	}
506 
507 	/* add gather stats boolean */
508 	if ((err = nvlist_add_uint32(nvlp, IPP_ACTION_STATS_ENABLE,
509 	    (dlcosmk_data->gather_stats ? 1 : 0))) != 0) {
510 		dlcosmk0dbg(("dlcosmk_info: error adding stats status\n"));
511 		nvlist_free(nvlp);
512 		return (err);
513 	}
514 
515 	/* call back with nvlist */
516 	err = fn(nvlp, arg);
517 
518 	nvlist_free(nvlp);
519 	return (err);
520 }
521