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 }