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 }