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 (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 #include <sys/types.h>
  27 #include <sys/atomic.h>
  28 #include <sys/systm.h>
  29 #include <sys/socket.h>
  30 #include <netinet/in.h>
  31 #include <sys/modctl.h>
  32 #include <sys/sunddi.h>
  33 #include <ipp/ipp.h>
  34 #include <ipp/ipp_config.h>
  35 #include <inet/common.h>
  36 #include <ipp/dlcosmk/dlcosmk_impl.h>
  37 
  38 #define D_SM_COMMENT    "IPP dlcosmk marker module"
  39 
  40 /* DDI file for dlcosmk ipp module */
  41 
  42 static int dlcosmk_create_action(ipp_action_id_t, nvlist_t **, ipp_flags_t);
  43 static int dlcosmk_modify_action(ipp_action_id_t, nvlist_t **, ipp_flags_t);
  44 static int dlcosmk_destroy_action(ipp_action_id_t, ipp_flags_t);
  45 static int dlcosmk_info(ipp_action_id_t, int (*)(nvlist_t *, void *), void *,
  46     ipp_flags_t);
  47 static int dlcosmk_invoke_action(ipp_action_id_t, ipp_packet_t *);
  48 
  49 static int dlcosmk_statinit(ipp_action_id_t, dlcosmk_data_t *);
  50 static int dlcosmk_update_stats(ipp_stat_t *, void *, int);
  51 
  52 /* Entry points for this IPP module */
  53 ipp_ops_t dlcosmk_ops = {
  54         IPPO_REV,
  55         dlcosmk_create_action,  /* ippo_action_create */
  56         dlcosmk_modify_action,  /* ippo_action_modify */
  57         dlcosmk_destroy_action, /* ippo_action_destroy */
  58         dlcosmk_info,           /* ippo_action_info */
  59         dlcosmk_invoke_action   /* ippo_action_invoke */
  60 };
  61 
  62 extern struct mod_ops mod_ippops;
  63 
  64 /*
  65  * Module linkage information for the kernel.
  66  */
  67 static struct modlipp modlipp = {
  68         &mod_ippops,
  69         D_SM_COMMENT,
  70         &dlcosmk_ops
  71 };
  72 
  73 static struct modlinkage modlinkage = {
  74         MODREV_1,
  75         { (void *)&modlipp, NULL }
  76 };
  77 
  78 
  79 int
  80 _init(void)
  81 {
  82         return (mod_install(&modlinkage));
  83 }
  84 
  85 int
  86 _fini(void)
  87 {
  88         return (mod_remove(&modlinkage));
  89 }
  90 
  91 int
  92 _info(struct modinfo *modinfop)
  93 {
  94         return (mod_info(&modlinkage, modinfop));
  95 }
  96 
  97 static int
  98 dlcosmk_create_action(ipp_action_id_t aid, nvlist_t **nvlpp,
  99     ipp_flags_t flags)
 100 {
 101         nvlist_t *nvlp;
 102         dlcosmk_data_t *dlcosmk_data;
 103         char *next_action;
 104         int err;
 105         uint32_t bstats, param;
 106 
 107         ASSERT((nvlpp != NULL) && (*nvlpp != NULL));
 108 
 109         nvlp = *nvlpp;
 110         *nvlpp = NULL;          /* nvlist should be NULL on return */
 111 
 112         if ((dlcosmk_data = kmem_zalloc(DLCOSMK_DATA_SZ, KM_NOSLEEP)) == NULL) {
 113                 nvlist_free(nvlp);
 114                 return (ENOMEM);
 115         }
 116 
 117         /* parse next action name */
 118         if ((err = nvlist_lookup_string(nvlp, DLCOSMK_NEXT_ACTION_NAME,
 119             &next_action)) != 0) {
 120                 nvlist_free(nvlp);
 121                 dlcosmk0dbg(("dlcosmk_create_action: invalid config, "\
 122                     "next_action name missing\n"));
 123                 kmem_free(dlcosmk_data, DLCOSMK_DATA_SZ);
 124                 return (err);
 125         }
 126         if ((dlcosmk_data->next_action =
 127             ipp_action_lookup(next_action)) == IPP_ACTION_INVAL) {
 128                 nvlist_free(nvlp);
 129                 dlcosmk0dbg(("dlcosmk_create_action: next_action invalid\n"));
 130                 kmem_free(dlcosmk_data, DLCOSMK_DATA_SZ);
 131                 return (EINVAL);
 132         }
 133 
 134         /* parse cos - from the config file */
 135         if ((err = nvlist_lookup_byte(nvlp, DLCOSMK_COS,
 136             &dlcosmk_data->usr_pri)) != 0) {
 137                 nvlist_free(nvlp);
 138                 dlcosmk0dbg(("dlcosmk_create_action: invalid config, "\
 139                     "cos missing\n"));
 140                 kmem_free(dlcosmk_data, DLCOSMK_DATA_SZ);
 141                 return (err);
 142         }
 143 
 144         /* parse b_band - mapped from cos */
 145         if ((err = nvlist_lookup_uint32(nvlp, DLCOSMK_BAND, &param)) != 0) {
 146                 nvlist_free(nvlp);
 147                 dlcosmk0dbg(("dlcosmk_create_action: invalid config, "\
 148                     "b_band missing\n"));
 149                 kmem_free(dlcosmk_data, DLCOSMK_DATA_SZ);
 150                 return (err);
 151         }
 152         dlcosmk_data->b_band = param;
 153 
 154         /* parse dl_priority.dl_max  - mapped from cos */
 155         if ((err = nvlist_lookup_uint32(nvlp, DLCOSMK_PRI, &param)) != 0) {
 156                 nvlist_free(nvlp);
 157                 dlcosmk0dbg(("dlcosmk_create_action: invalid config, "\
 158                     "dl_priority missing\n"));
 159                 kmem_free(dlcosmk_data, DLCOSMK_DATA_SZ);
 160                 return (err);
 161         }
 162         dlcosmk_data->dl_max = param;
 163 
 164         /* parse gather_stats boolean */
 165         if ((err = nvlist_lookup_uint32(nvlp, IPP_ACTION_STATS_ENABLE, &bstats))
 166             != 0) {
 167                 dlcosmk_data->gather_stats = B_FALSE;
 168         } else {
 169                 /* If stats is needed, initialize the stats structure */
 170                 dlcosmk_data->gather_stats = (bstats != 0) ? B_TRUE : B_FALSE;
 171                 if (dlcosmk_data->gather_stats) {
 172                         if ((err = dlcosmk_statinit(aid, dlcosmk_data)) != 0) {
 173                                 nvlist_free(nvlp);
 174                                 kmem_free(dlcosmk_data, DLCOSMK_DATA_SZ);
 175                                 return (err);
 176                         }
 177                 }
 178         }
 179 
 180         /* Free the nvlist */
 181         nvlist_free(nvlp);
 182 
 183         /* set action chain reference */
 184         if ((err = ipp_action_ref(aid, dlcosmk_data->next_action,
 185             flags)) != 0) {
 186                 dlcosmk0dbg(("dlcosmk_create_action: ipp_action_ref " \
 187                     "returned with error %d\n", err));
 188                 ipp_stat_destroy(dlcosmk_data->stats);
 189                 kmem_free(dlcosmk_data, DLCOSMK_DATA_SZ);
 190                 return (err);
 191         }
 192 
 193         ipp_action_set_ptr(aid, (void *)dlcosmk_data);
 194         return (0);
 195 }
 196 
 197 static int
 198 dlcosmk_modify_action(ipp_action_id_t aid, nvlist_t **nvlpp, ipp_flags_t flags)
 199 {
 200         nvlist_t *nvlp;
 201         int err = 0;
 202         uint32_t band, dlpri;
 203         uint8_t config_type;
 204         uint8_t cos;
 205         char *next_action_name;
 206         ipp_action_id_t next_action;
 207         dlcosmk_data_t *dlcosmk_data;
 208         uint32_t bstats;
 209 
 210         ASSERT((nvlpp != NULL) && (*nvlpp != NULL));
 211 
 212         nvlp = *nvlpp;
 213         *nvlpp = NULL;          /* nvlist should be NULL when this returns */
 214 
 215         if ((err = nvlist_lookup_byte(nvlp, IPP_CONFIG_TYPE, &config_type))
 216             != 0) {
 217                 nvlist_free(nvlp);
 218                 dlcosmk0dbg(("dlcosmk_modify_action: invalid configuration "\
 219                     "type\n"));
 220                 return (err);
 221         }
 222 
 223         if (config_type != IPP_SET) {
 224                 nvlist_free(nvlp);
 225                 dlcosmk0dbg(("dlcosmk_modify_action: invalid configuration "\
 226                     "type %d\n", config_type));
 227                 return (EINVAL);
 228         }
 229 
 230         dlcosmk_data = (dlcosmk_data_t *)ipp_action_get_ptr(aid);
 231         ASSERT(dlcosmk_data != NULL);
 232 
 233         /* parse next action name, if present */
 234         if ((err = nvlist_lookup_string(nvlp, DLCOSMK_NEXT_ACTION_NAME,
 235             &next_action_name)) == 0) {
 236                 /* lookup action name to get action id */
 237                 if ((next_action = ipp_action_lookup(next_action_name))
 238                     == IPP_ACTION_INVAL) {
 239                         nvlist_free(nvlp);
 240                         dlcosmk0dbg(("dlcosmk_modify_action: next_action "\
 241                             "invalid\n"));
 242                         return (EINVAL);
 243                 }
 244                 /* reference new action */
 245                 if ((err = ipp_action_ref(aid, next_action, flags)) != 0) {
 246                         nvlist_free(nvlp);
 247                         dlcosmk0dbg(("dlcosmk_modify_action: ipp_action_ref "\
 248                             "returned with error %d\n", err));
 249                         return (err);
 250                 }
 251                 /* unref old action */
 252                 err = ipp_action_unref(aid, dlcosmk_data->next_action, flags);
 253                 ASSERT(err == 0);
 254                 dlcosmk_data->next_action = next_action;
 255         }
 256 
 257         /* parse cos, if present */
 258         if ((err = nvlist_lookup_byte(nvlp, DLCOSMK_COS, &cos)) == 0) {
 259 
 260                 /* parse b_band, mapped from cos */
 261                 if ((err = nvlist_lookup_uint32(nvlp, DLCOSMK_BAND,
 262                     &band)) != 0) {
 263                         nvlist_free(nvlp);
 264                         dlcosmk0dbg(("dlcosmk_modify_action: b_band not "\
 265                             "provided\n"));
 266                         return (err);
 267                 }
 268 
 269                 /* parse dl_priority, mapped from cos */
 270                 if ((err = nvlist_lookup_uint32(nvlp, DLCOSMK_PRI,
 271                     &dlpri)) != 0) {
 272                         nvlist_free(nvlp);
 273                         dlcosmk0dbg(("dlcosmk_modify_action: dl_priority not "\
 274                             "provided\n"));
 275                         return (err);
 276                 }
 277 
 278                 /* Have all the three values, change them */
 279                 dlcosmk_data->usr_pri = cos;
 280                 dlcosmk_data->b_band = band;
 281                 dlcosmk_data->dl_max = dlpri;
 282         }
 283 
 284 
 285         /* parse gather_stats boolean, if present */
 286         if ((err = nvlist_lookup_uint32(nvlp, IPP_ACTION_STATS_ENABLE, &bstats))
 287             == 0) {
 288                 boolean_t val = (bstats != 0) ? B_TRUE : B_FALSE;
 289                 /* Turning on stats */
 290                 if (!dlcosmk_data->gather_stats && val) {
 291                         if ((err = dlcosmk_statinit(aid, dlcosmk_data)) != 0) {
 292                                 nvlist_free(nvlp);
 293                                 return (err);
 294                         }
 295                 /* Turning off stats */
 296                 } else if (!val && dlcosmk_data->gather_stats) {
 297                         ipp_stat_destroy(dlcosmk_data->stats);
 298 
 299                 }
 300                 dlcosmk_data->gather_stats = val;
 301         }
 302 
 303         /* Free thenvlist */
 304         nvlist_free(nvlp);
 305         return (0);
 306 }
 307 
 308 static int
 309 dlcosmk_destroy_action(ipp_action_id_t aid, ipp_flags_t flags)
 310 {
 311         dlcosmk_data_t *dlcosmk_data;
 312         int err;
 313 
 314         dlcosmk_data = (dlcosmk_data_t *)ipp_action_get_ptr(aid);
 315         ASSERT(dlcosmk_data != NULL);
 316 
 317         /* Destroy stats, if gathered */
 318         if (dlcosmk_data->gather_stats) {
 319                 ipp_stat_destroy(dlcosmk_data->stats);
 320         }
 321 
 322         /* unreference the action */
 323         err = ipp_action_unref(aid, dlcosmk_data->next_action, flags);
 324         ASSERT(err == 0);
 325 
 326         kmem_free(dlcosmk_data, DLCOSMK_DATA_SZ);
 327         return (0);
 328 }
 329 
 330 static int
 331 dlcosmk_invoke_action(ipp_action_id_t aid, ipp_packet_t *packet)
 332 {
 333         dlcosmk_data_t *dlcosmk_data;
 334         mblk_t *mp = NULL;
 335         int err;
 336         ip_priv_t *priv;
 337 
 338         ASSERT(packet != NULL);
 339 
 340         /* get mblk from ipp_packet structure */
 341         mp = ipp_packet_get_data(packet);
 342         priv = (ip_priv_t *)ipp_packet_get_private(packet);
 343 
 344         dlcosmk_data = (dlcosmk_data_t *)ipp_action_get_ptr(aid);
 345         ASSERT(dlcosmk_data != NULL);
 346 
 347         /* dlcosmk packet as configured */
 348         if ((err = dlcosmk_process(&mp, dlcosmk_data, priv->ill_index,
 349             priv->proc)) != 0) {
 350                 return (err);
 351         } else {
 352                 /* return packet with next action set */
 353                 return (ipp_packet_next(packet, dlcosmk_data->next_action));
 354         }
 355 }
 356 
 357 static int
 358 dlcosmk_statinit(ipp_action_id_t aid, dlcosmk_data_t *dlcosmk_data)
 359 {
 360         int err;
 361         dlcosmk_stat_t *statp;
 362 
 363         /* install stats entry */
 364         if ((err = ipp_stat_create(aid, DLCOSMK_STATS_STRING,
 365             DLCOSMK_STATS_COUNT, dlcosmk_update_stats, dlcosmk_data,
 366             &dlcosmk_data->stats)) != 0) {
 367                 dlcosmk0dbg(("dlcosmk_create_action: ipp_stat_create " \
 368                     "returned with error %d\n", err));
 369                 return (err);
 370         }
 371 
 372         statp = (dlcosmk_stat_t *)(dlcosmk_data->stats)->ipps_data;
 373         ASSERT(statp != NULL);
 374 
 375         if ((err = ipp_stat_named_init(dlcosmk_data->stats, "npackets",
 376             IPP_STAT_UINT64, &statp->npackets)) != 0) {
 377                 dlcosmk0dbg(("dlcosmk_create_action: ipp_stat_named_init " \
 378                     "returned with error %d\n", err));
 379                 return (err);
 380         }
 381 
 382         if ((err = ipp_stat_named_init(dlcosmk_data->stats, "ipackets",
 383             IPP_STAT_UINT64, &statp->ipackets)) != 0) {
 384                 dlcosmk0dbg(("dlcosmk_create_action: ipp_stat_named_init " \
 385                     "returned with error %d\n", err));
 386                 return (err);
 387         }
 388 
 389         if ((err = ipp_stat_named_init(dlcosmk_data->stats, "epackets",
 390             IPP_STAT_UINT64, &statp->epackets)) != 0) {
 391                 dlcosmk0dbg(("dlcosmk_create_action: ipp_stat_named_init " \
 392                     "returned with error %d\n", err));
 393                 return (err);
 394         }
 395 
 396         if ((err = ipp_stat_named_init(dlcosmk_data->stats, "usr_pri",
 397             IPP_STAT_INT32, &statp->usr_pri)) != 0) {
 398                 dlcosmk0dbg(("dlcosmk_create_action: ipp_stat_named_init " \
 399                     "returned with error %d", err));
 400                 return (err);
 401         }
 402 
 403         if ((err = ipp_stat_named_init(dlcosmk_data->stats, "b_band",
 404             IPP_STAT_INT32, &statp->b_band)) != 0) {
 405                 dlcosmk0dbg(("dlcosmk_create_action: ipp_stat_named_init " \
 406                     "returned with error %d\n", err));
 407                 return (err);
 408         }
 409 
 410         if ((err = ipp_stat_named_init(dlcosmk_data->stats, "dl_max",
 411             IPP_STAT_INT32, &statp->dl_max)) != 0) {
 412                 dlcosmk0dbg(("dlcosmk_create_action: ipp_stat_named_init " \
 413                     "returned with error %d\n", err));
 414                 return (err);
 415         }
 416 
 417         ipp_stat_install(dlcosmk_data->stats);
 418         return (0);
 419 }
 420 
 421 static int
 422 dlcosmk_update_stats(ipp_stat_t *sp, void *arg, int rw)
 423 {
 424         dlcosmk_data_t *dlcosmk_data = (dlcosmk_data_t *)arg;
 425         dlcosmk_stat_t *snames = (dlcosmk_stat_t *)sp->ipps_data;
 426         uint32_t upri, bband;
 427 
 428         ASSERT(dlcosmk_data != NULL);
 429         ASSERT(snames != NULL);
 430 
 431         upri = dlcosmk_data->usr_pri;
 432         bband = dlcosmk_data->b_band;
 433 
 434         (void) ipp_stat_named_op(&snames->npackets, &dlcosmk_data->npackets,
 435             rw);
 436         (void) ipp_stat_named_op(&snames->ipackets, &dlcosmk_data->ipackets,
 437             rw);
 438         (void) ipp_stat_named_op(&snames->epackets, &dlcosmk_data->epackets,
 439             rw);
 440         (void) ipp_stat_named_op(&snames->usr_pri, &upri, rw);
 441         (void) ipp_stat_named_op(&snames->b_band, &bband, rw);
 442         (void) ipp_stat_named_op(&snames->dl_max, &dlcosmk_data->dl_max, rw);
 443 
 444         return (0);
 445 }
 446 
 447 /* ARGSUSED */
 448 static int
 449 dlcosmk_info(ipp_action_id_t aid, int (*fn)(nvlist_t *, void *), void *arg,
 450     ipp_flags_t flags)
 451 {
 452         nvlist_t *nvlp;
 453         dlcosmk_data_t *dlcosmk_data;
 454         char *next_action;
 455         int err;
 456 
 457         ASSERT(fn != NULL);
 458 
 459         dlcosmk_data = (dlcosmk_data_t *)ipp_action_get_ptr(aid);
 460         ASSERT(dlcosmk_data != NULL);
 461 
 462         /* allocate nvlist to be passed back */
 463         if ((err = nvlist_alloc(&nvlp, NV_UNIQUE_NAME, KM_NOSLEEP)) != 0) {
 464                 dlcosmk0dbg(("dlcosmk_info: error allocating memory\n"));
 465                 return (err);
 466         }
 467 
 468         /* look up next action with the next action id */
 469         if ((err = ipp_action_name(dlcosmk_data->next_action,
 470             &next_action)) != 0) {
 471                 dlcosmk0dbg(("dlcosmk_info: next action not available\n"));
 472                 nvlist_free(nvlp);
 473                 return (err);
 474         }
 475 
 476         /* add next action name */
 477         if ((err = nvlist_add_string(nvlp, DLCOSMK_NEXT_ACTION_NAME,
 478             next_action)) != 0) {
 479                 dlcosmk0dbg(("dlcosmk_info: error adding next action\n"));
 480                 nvlist_free(nvlp);
 481                 kmem_free(next_action, (strlen(next_action) + 1));
 482                 return (err);
 483         }
 484 
 485         /* free action name */
 486         kmem_free(next_action, (strlen(next_action) + 1));
 487 
 488         /* add config type */
 489         if ((err = nvlist_add_byte(nvlp, IPP_CONFIG_TYPE, IPP_SET)) != 0) {
 490                 dlcosmk0dbg(("dlcosmk_info: error adding config. type\n"));
 491                 nvlist_free(nvlp);
 492                 return (err);
 493         }
 494 
 495         /* just give the cos, since that is what is provided in the config */
 496         if ((err = nvlist_add_byte(nvlp, DLCOSMK_COS, dlcosmk_data->usr_pri))
 497             != 0) {
 498                 dlcosmk0dbg(("dlcosmk_info: error adding cos\n"));
 499                 nvlist_free(nvlp);
 500                 return (err);
 501         }
 502 
 503         /* add gather stats boolean */
 504         if ((err = nvlist_add_uint32(nvlp, IPP_ACTION_STATS_ENABLE,
 505             (dlcosmk_data->gather_stats ? 1 : 0))) != 0) {
 506                 dlcosmk0dbg(("dlcosmk_info: error adding stats status\n"));
 507                 nvlist_free(nvlp);
 508                 return (err);
 509         }
 510 
 511         /* call back with nvlist */
 512         err = fn(nvlp, arg);
 513 
 514         nvlist_free(nvlp);
 515         return (err);
 516 }