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 82 _init(void) 83 { 84 return (mod_install(&modlinkage)); 85 } 86 87 int 88 _fini(void) 89 { 90 return (mod_remove(&modlinkage)); 91 } 92 93 int 94 _info(struct modinfo *modinfop) 95 { 96 return (mod_info(&modlinkage, modinfop)); 97 } 98 99 /* Update global stats */ 100 static int 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 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 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 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 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 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 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