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 }