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
_init(void)84 _init(void)
85 {
86 return (mod_install(&modlinkage));
87 }
88
89 int
_fini(void)90 _fini(void)
91 {
92 return (mod_remove(&modlinkage));
93 }
94
95 int
_info(struct modinfo * modinfop)96 _info(struct modinfo *modinfop)
97 {
98 return (mod_info(&modlinkage, modinfop));
99 }
100
101 /* Update global stats */
102 static int
update_flowacct_kstats(ipp_stat_t * sp,void * arg,int rw)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
global_statinit(ipp_action_id_t aid,flowacct_data_t * flowacct_data)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
flowacct_create_action(ipp_action_id_t aid,nvlist_t ** nvlpp,ipp_flags_t flags)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
flowacct_modify_action(ipp_action_id_t aid,nvlist_t ** nvlpp,ipp_flags_t flags)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
flowacct_destroy_action(ipp_action_id_t aid,ipp_flags_t flags)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
flowacct_invoke_action(ipp_action_id_t aid,ipp_packet_t * packet)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
flowacct_info(ipp_action_id_t aid,int (* fn)(nvlist_t *,void *),void * arg,ipp_flags_t flags)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