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