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