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