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 }