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/conf.h>
31 #include <sys/atomic.h>
32 #include <sys/systm.h>
33 #include <sys/socket.h>
34 #include <sys/spl.h>
35 #include <netinet/in.h>
36 #include <sys/modctl.h>
37 #include <sys/sunddi.h>
38 #include <ipp/ipp.h>
39 #include <ipp/ipp_config.h>
40 #include <inet/common.h>
41 #include <ipp/flowacct/flowacct_impl.h>
42 #include <sys/ddi.h>
43 
44 #define	D_SM_COMMENT	"IPP Flow Accounting Module"
45 
46 /* DDI file for flowacct ipp module */
47 
48 static int flowacct_create_action(ipp_action_id_t, nvlist_t **, ipp_flags_t);
49 static int flowacct_modify_action(ipp_action_id_t, nvlist_t **, ipp_flags_t);
50 static int flowacct_destroy_action(ipp_action_id_t, ipp_flags_t);
51 static int flowacct_info(ipp_action_id_t, int (*)(nvlist_t *, void *), void *,
52     ipp_flags_t);
53 static int flowacct_invoke_action(ipp_action_id_t, ipp_packet_t *);
54 
55 static int update_flowacct_kstats(ipp_stat_t *, void *, int);
56 
57 ipp_ops_t flowacct_ops = {
58 	IPPO_REV,
59 	flowacct_create_action,		/* ippo_action_create */
60 	flowacct_modify_action,		/* ippo_action_modify */
61 	flowacct_destroy_action,	/* ippo_action_destroy */
62 	flowacct_info,			/* ippo_action_info */
63 	flowacct_invoke_action		/* ippo_action_invoke */
64 };
65 
66 extern struct mod_ops mod_ippops;
67 
68 /*
69  * Module linkage information for the kernel.
70  */
71 static struct modlipp modlipp = {
72 	&mod_ippops,
73 	D_SM_COMMENT " 1.12",
74 	&flowacct_ops
75 };
76 
77 static struct modlinkage modlinkage = {
78 	MODREV_1,
79 	(void *)&modlipp,
80 	NULL
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 /* Update global stats */
102 static int
103 update_flowacct_kstats(ipp_stat_t *sp, void *arg, int rw)
104 {
105 	flowacct_data_t *flowacct_data = (flowacct_data_t *)arg;
106 	flowacct_stat_t *fl_stat  = (flowacct_stat_t *)sp->ipps_data;
107 	ASSERT((fl_stat != NULL) && (flowacct_data != 0));
108 
109 	(void) ipp_stat_named_op(&fl_stat->nbytes, &flowacct_data->nbytes, rw);
110 	(void) ipp_stat_named_op(&fl_stat->tbytes, &flowacct_data->tbytes, rw);
111 	(void) ipp_stat_named_op(&fl_stat->nflows, &flowacct_data->nflows, rw);
112 	(void) ipp_stat_named_op(&fl_stat->usedmem, &flowacct_data->usedmem,
113 	    rw);
114 	(void) ipp_stat_named_op(&fl_stat->npackets, &flowacct_data->npackets,
115 	    rw);
116 	(void) ipp_stat_named_op(&fl_stat->epackets, &flowacct_data->epackets,
117 	    rw);
118 	return (0);
119 }
120 
121 /* Initialize global stats */
122 static int
123 global_statinit(ipp_action_id_t aid, flowacct_data_t *flowacct_data)
124 {
125 	flowacct_stat_t *flacct_stat;
126 	int err = 0;
127 
128 	if ((err = ipp_stat_create(aid, FLOWACCT_STATS_STRING,
129 	    FLOWACCT_STATS_COUNT, update_flowacct_kstats, flowacct_data,
130 	    &flowacct_data->stats)) != 0) {
131 		flowacct0dbg(("global_statinit: error creating flowacct "\
132 		    "stats\n"));
133 		return (err);
134 	}
135 	flacct_stat = (flowacct_stat_t *)(flowacct_data->stats)->ipps_data;
136 	ASSERT(flacct_stat != NULL);
137 
138 	if ((err = ipp_stat_named_init(flowacct_data->stats, "bytes_in_tbl",
139 	    IPP_STAT_UINT64, &flacct_stat->tbytes)) != 0) {
140 		flowacct0dbg(("global_statinit: ipp_stat_named_init returned "\
141 		    "with error %d\n", err));
142 		return (err);
143 	}
144 	if ((err = ipp_stat_named_init(flowacct_data->stats, "nbytes",
145 	    IPP_STAT_UINT64, &flacct_stat->nbytes)) != 0) {
146 		flowacct0dbg(("global_statinit: ipp_stat_named_init returned "\
147 		    "with error %d\n", err));
148 		return (err);
149 	}
150 	if ((err = ipp_stat_named_init(flowacct_data->stats, "npackets",
151 	    IPP_STAT_UINT64, &flacct_stat->npackets)) != 0) {
152 		flowacct0dbg(("global_statinit:ipp_stat_named_init returned "\
153 		    "with error %d\n", err));
154 		return (err);
155 	}
156 	if ((err = ipp_stat_named_init(flowacct_data->stats, "usedmem",
157 	    IPP_STAT_UINT64, &flacct_stat->usedmem)) != 0) {
158 		flowacct0dbg(("global_statinit:ipp_stat_named_init returned "\
159 		    "with error %d\n", err));
160 		return (err);
161 	}
162 	if ((err = ipp_stat_named_init(flowacct_data->stats, "flows_in_tbl",
163 	    IPP_STAT_UINT32, &flacct_stat->nflows)) != 0) {
164 		flowacct0dbg(("global_statinit:ipp_stat_named_init returned "\
165 		    "with error %d\n", err));
166 		return (err);
167 	}
168 	if ((err = ipp_stat_named_init(flowacct_data->stats, "epackets",
169 	    IPP_STAT_UINT64, &flacct_stat->epackets)) != 0) {
170 		flowacct0dbg(("global_statinit:ipp_stat_named_init returned "\
171 		    "with error %d\n", err));
172 		return (err);
173 	}
174 	ipp_stat_install(flowacct_data->stats);
175 
176 	return (err);
177 }
178 
179 static int
180 flowacct_create_action(ipp_action_id_t aid, nvlist_t **nvlpp, ipp_flags_t flags)
181 {
182 	nvlist_t *nvlp;
183 	flowacct_data_t *flowacct_data;
184 	char *next_action;
185 	int rc, flow_count;
186 	list_head_t *head;
187 	uint32_t bstats;
188 	uint32_t timeout = FLOWACCT_DEF_TIMEOUT;
189 	uint32_t timer = FLOWACCT_DEF_TIMER;
190 
191 	nvlp = *nvlpp;
192 	*nvlpp = NULL;		/* nvlist should be NULL on return */
193 
194 	if ((flowacct_data = kmem_zalloc(FLOWACCT_DATA_SZ, KM_NOSLEEP))
195 	    == NULL) {
196 		nvlist_free(nvlp);
197 		return (ENOMEM);
198 	}
199 
200 	/* parse next action name */
201 	if ((rc = nvlist_lookup_string(nvlp, FLOWACCT_NEXT_ACTION_NAME,
202 	    &next_action)) != 0) {
203 		nvlist_free(nvlp);
204 		kmem_free(flowacct_data, FLOWACCT_DATA_SZ);
205 		flowacct0dbg(("flowacct_create_action: invalid config, "\
206 		    "next_action missing\n"));
207 		return (rc);
208 	}
209 	if ((flowacct_data->next_action = ipp_action_lookup(next_action))
210 	    == IPP_ACTION_INVAL) {
211 		nvlist_free(nvlp);
212 		flowacct0dbg(("flowacct_create_action: invalid next_action\n"));
213 		kmem_free(flowacct_data, FLOWACCT_DATA_SZ);
214 		return (EINVAL);
215 	}
216 
217 	if ((rc = ipp_action_name(aid, &flowacct_data->act_name)) != 0) {
218 		nvlist_free(nvlp);
219 		flowacct0dbg(("flowacct_create_action: invalid next aid\n"));
220 		kmem_free(flowacct_data, FLOWACCT_DATA_SZ);
221 		return (EINVAL);
222 	}
223 
224 	/* parse flow timeout - in millisec, if present */
225 	(void) nvlist_lookup_uint32(nvlp, FLOWACCT_TIMEOUT, &timeout);
226 
227 	/* Convert to FLOWACCT_MSEC_TO_NSEC */
228 	flowacct_data->timeout = (uint64_t)timeout * FLOWACCT_MSEC_TO_NSEC;
229 
230 	/* parse flow timer - in millisec, if present  */
231 	(void) nvlist_lookup_uint32(nvlp, FLOWACCT_TIMER, &timer);
232 
233 	/* Convert to FLOWACCT_MSEC_TO_USEC */
234 	flowacct_data->timer = (uint64_t)timer * FLOWACCT_MSEC_TO_USEC;
235 
236 	if ((rc = nvlist_lookup_uint32(nvlp, FLOWACCT_MAX_LIMIT,
237 	    &flowacct_data->max_limit)) != 0) {
238 		nvlist_free(nvlp);
239 		flowacct0dbg(("flowacct_create_action: invalid config, "\
240 		    "max_limit missing\n"));
241 		kmem_free(flowacct_data, FLOWACCT_DATA_SZ);
242 		return (rc);
243 	}
244 
245 	if ((rc = nvlist_lookup_uint32(nvlp, IPP_ACTION_STATS_ENABLE,
246 	    &bstats)) != 0) {
247 		flowacct_data->global_stats = B_FALSE;
248 	} else {
249 		flowacct_data->global_stats = (boolean_t)bstats;
250 		if (flowacct_data->global_stats) {
251 			if ((rc = global_statinit(aid, flowacct_data)) != 0) {
252 				kmem_free(flowacct_data, FLOWACCT_DATA_SZ);
253 				return (rc);
254 			}
255 		}
256 	}
257 
258 	nvlist_free(nvlp);
259 
260 	/* set action chain reference */
261 	if ((rc = ipp_action_ref(aid, flowacct_data->next_action,
262 	    flags)) != 0) {
263 		flowacct0dbg(("flowacct_create_action: ipp_action_ref " \
264 		    "returned with error %d\n", rc));
265 		if (flowacct_data->stats != NULL) {
266 			ipp_stat_destroy(flowacct_data->stats);
267 		}
268 		kmem_free(flowacct_data, FLOWACCT_DATA_SZ);
269 		return (rc);
270 	}
271 
272 	/* Initialize locks */
273 	for (flow_count = 0, head = flowacct_data->flows_tbl;
274 	    flow_count < (FLOW_TBL_COUNT + 1); flow_count++, head++) {
275 		mutex_init(&head->lock, NULL, MUTEX_DEFAULT, 0);
276 	}
277 
278 	ipp_action_set_ptr(aid, (void *)flowacct_data);
279 	return (0);
280 }
281 
282 static int
283 flowacct_modify_action(ipp_action_id_t aid, nvlist_t **nvlpp, ipp_flags_t flags)
284 {
285 	nvlist_t *nvlp;
286 	int rc = 0;
287 	uint8_t config_type;
288 	char *next_action_name, *act_name;
289 	ipp_action_id_t next_action;
290 	uint32_t timeout, timer, bstats, max_limit;
291 	flowacct_data_t *flowacct_data;
292 
293 	nvlp = *nvlpp;
294 	*nvlpp = NULL;		/* nvlist should be NULL when this returns */
295 
296 	if ((rc = nvlist_lookup_byte(nvlp, IPP_CONFIG_TYPE, &config_type))
297 	    != 0) {
298 		nvlist_free(nvlp);
299 		flowacct0dbg(("flowacct_modify_action: invalid configuration "\
300 		    "type\n"));
301 		return (rc);
302 	}
303 
304 	if (config_type != IPP_SET) {
305 		nvlist_free(nvlp);
306 		flowacct0dbg(("flowacct_modify_action: invalid configuration "\
307 		    "type %d\n", config_type));
308 		return (EINVAL);
309 	}
310 
311 	flowacct_data = (flowacct_data_t *)ipp_action_get_ptr(aid);
312 
313 	/* parse next action name, if present */
314 	if ((rc = nvlist_lookup_string(nvlp, FLOWACCT_NEXT_ACTION_NAME,
315 	    &next_action_name)) == 0) {
316 		/* lookup action name to get action id */
317 		if ((next_action = ipp_action_lookup(next_action_name))
318 		    == IPP_ACTION_INVAL) {
319 			nvlist_free(nvlp);
320 			flowacct0dbg(("flowacct_modify_action: next_action "\
321 			    "invalid\n"));
322 			return (EINVAL);
323 		}
324 		/* reference new action */
325 		if ((rc = ipp_action_ref(aid, next_action, flags)) != 0) {
326 			nvlist_free(nvlp);
327 			flowacct0dbg(("flowacct_modify_action: "\
328 			    "ipp_action_ref returned with error %d\n", rc));
329 			return (rc);
330 		}
331 
332 		if ((rc = ipp_action_name(aid, &act_name)) != 0) {
333 			nvlist_free(nvlp);
334 			flowacct0dbg(("flowacct_modify_action: invalid next "\
335 			    "aid\n"));
336 			return (EINVAL);
337 		}
338 
339 		/* unref old action */
340 		rc = ipp_action_unref(aid, flowacct_data->next_action, flags);
341 		ASSERT(rc == 0);
342 		flowacct_data->next_action = next_action;
343 		kmem_free(flowacct_data->act_name,
344 		    (strlen(flowacct_data->act_name) + 1));
345 		flowacct_data->act_name = act_name;
346 	}
347 
348 	/* parse timeout, if present */
349 	if ((rc = nvlist_lookup_uint32(nvlp, FLOWACCT_TIMEOUT, &timeout))
350 	    == 0) {
351 		flowacct_data->timeout = (uint64_t)timeout *
352 		    FLOWACCT_MSEC_TO_NSEC;
353 	}
354 
355 	/* parse timer, if present */
356 	if ((rc = nvlist_lookup_uint32(nvlp, FLOWACCT_TIMER, &timer)) == 0) {
357 		flowacct_data->timer = (uint64_t)timer * FLOWACCT_MSEC_TO_USEC;
358 	}
359 
360 	/* parse max_flow, if present */
361 	if ((rc = nvlist_lookup_uint32(nvlp, FLOWACCT_MAX_LIMIT, &max_limit))
362 	    == 0) {
363 		flowacct_data->max_limit = max_limit;
364 	}
365 
366 	/* parse gather_stats boolean, if present */
367 	if ((rc = nvlist_lookup_uint32(nvlp, IPP_ACTION_STATS_ENABLE, &bstats))
368 	    == 0) {
369 		boolean_t new_val = (boolean_t)bstats;
370 
371 		/* Turning global stats on */
372 		if (new_val && !flowacct_data->global_stats) {
373 			rc = global_statinit(aid, flowacct_data);
374 			if (rc == 0) {
375 				flowacct_data->global_stats = new_val;
376 			} else {
377 				flowacct0dbg(("flowacct_modify_action: error "\
378 				    "enabling stats\n"));
379 			}
380 		} else if (!new_val && flowacct_data->global_stats) {
381 			flowacct_data->global_stats = new_val;
382 			ipp_stat_destroy(flowacct_data->stats);
383 		}
384 	}
385 	return (0);
386 }
387 
388 static int
389 flowacct_destroy_action(ipp_action_id_t aid, ipp_flags_t flags)
390 {
391 	flowacct_data_t *flowacct_data;
392 	int rc, flow_count;
393 	list_head_t *head;
394 
395 	flowacct_data = (flowacct_data_t *)ipp_action_get_ptr(aid);
396 	ASSERT(flowacct_data != NULL);
397 
398 	while (flowacct_data->flow_tid != 0) {
399 		timeout_id_t tid = flowacct_data->flow_tid;
400 		flowacct_data->flow_tid = 0;
401 		(void) untimeout(tid);
402 	}
403 
404 	if (flowacct_data->stats != NULL) {
405 		ipp_stat_destroy(flowacct_data->stats);
406 	}
407 
408 	/* Dump all the flows to the file */
409 	flowacct_timer(FLOWACCT_PURGE_FLOW, flowacct_data);
410 
411 	kmem_free(flowacct_data->act_name, (strlen(flowacct_data->act_name)
412 	    + 1));
413 
414 	/* Destroy the locks */
415 	for (flow_count = 0, head = flowacct_data->flows_tbl;
416 	    flow_count < FLOW_TBL_COUNT; flow_count++, head++) {
417 		mutex_destroy(&head->lock);
418 	}
419 	/* unreference the action */
420 	rc = ipp_action_unref(aid, flowacct_data->next_action, flags);
421 	ASSERT(rc == 0);
422 
423 
424 	kmem_free(flowacct_data, FLOWACCT_DATA_SZ);
425 	return (0);
426 }
427 
428 static int
429 flowacct_invoke_action(ipp_action_id_t aid, ipp_packet_t *packet)
430 {
431 	flowacct_data_t *flowacct_data;
432 	mblk_t *mp = NULL;
433 	int rc;
434 
435 	/* get mblk from ipp_packet structure */
436 	mp = ipp_packet_get_data(packet);
437 	flowacct_data = (flowacct_data_t *)ipp_action_get_ptr(aid);
438 	ASSERT(flowacct_data != NULL);
439 
440 	/* flowacct packet as configured */
441 	if ((rc = flowacct_process(&mp, flowacct_data)) != 0) {
442 		return (rc);
443 	} else {
444 		/* return packet with next action set */
445 		return (ipp_packet_next(packet, flowacct_data->next_action));
446 	}
447 }
448 
449 /* ARGSUSED */
450 static int
451 flowacct_info(ipp_action_id_t aid, int (*fn)(nvlist_t *, void *), void *arg,
452     ipp_flags_t flags)
453 {
454 	nvlist_t *nvlp;
455 	flowacct_data_t *flowacct_data;
456 	char *next_action;
457 	uint32_t param;
458 	int rc;
459 
460 	flowacct_data = (flowacct_data_t *)ipp_action_get_ptr(aid);
461 	ASSERT(flowacct_data != NULL);
462 	ASSERT(fn != NULL);
463 
464 	/* allocate nvlist to be passed back */
465 	if ((rc = nvlist_alloc(&nvlp, NV_UNIQUE_NAME, KM_NOSLEEP)) != 0) {
466 		flowacct0dbg(("flowacct_info: memory allocation failure\n"));
467 		return (rc);
468 	}
469 
470 	/* look up next action with the next action id */
471 	if ((rc = ipp_action_name(flowacct_data->next_action,
472 	    &next_action)) != 0) {
473 		flowacct0dbg(("flowacct_info: next action not available\n"));
474 		nvlist_free(nvlp);
475 		return (rc);
476 	}
477 
478 	/* add next action name */
479 	if ((rc = nvlist_add_string(nvlp, FLOWACCT_NEXT_ACTION_NAME,
480 	    next_action)) != 0) {
481 		flowacct0dbg(("flowacct_info: error adding next action\n"));
482 		nvlist_free(nvlp);
483 		kmem_free(next_action, (strlen(next_action) + 1));
484 		return (rc);
485 	}
486 
487 	/* free action name */
488 	kmem_free(next_action, (strlen(next_action) + 1));
489 
490 	/* add config type */
491 	if ((rc = nvlist_add_byte(nvlp, IPP_CONFIG_TYPE, IPP_SET)) != 0) {
492 		flowacct0dbg(("flowacct_info: error adding config type\n"));
493 		nvlist_free(nvlp);
494 		return (rc);
495 	}
496 
497 	/* add timer */
498 	param = flowacct_data->timer / FLOWACCT_MSEC_TO_USEC;
499 	if ((rc = nvlist_add_uint32(nvlp, FLOWACCT_TIMER, param)) != 0) {
500 		flowacct0dbg(("flowacct_info: error adding timer info.\n"));
501 		nvlist_free(nvlp);
502 		return (rc);
503 	}
504 
505 	/* add max_limit */
506 	if ((rc = nvlist_add_uint32(nvlp, FLOWACCT_MAX_LIMIT,
507 	    flowacct_data->max_limit)) != 0) {
508 		flowacct0dbg(("flowacct_info: error adding max_flow info.\n"));
509 		nvlist_free(nvlp);
510 		return (rc);
511 	}
512 
513 
514 	param = flowacct_data->timeout / FLOWACCT_MSEC_TO_NSEC;
515 	/* add timeout */
516 	if ((rc = nvlist_add_uint32(nvlp, FLOWACCT_TIMEOUT, param)) != 0) {
517 		flowacct0dbg(("flowacct_info: error adding timeout info.\n"));
518 		nvlist_free(nvlp);
519 		return (rc);
520 	}
521 
522 	/* add global stats boolean */
523 	if ((rc = nvlist_add_uint32(nvlp, IPP_ACTION_STATS_ENABLE,
524 	    (uint32_t)flowacct_data->global_stats)) != 0) {
525 		flowacct0dbg(("flowacct_info: error adding global stats "\
526 		    "info.\n"));
527 		nvlist_free(nvlp);
528 		return (rc);
529 	}
530 
531 	/* call back with nvlist */
532 	rc = fn(nvlp, arg);
533 
534 	nvlist_free(nvlp);
535 	return (rc);
536 }
537