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,
  76         NULL
  77 };
  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 static int
  99 dlcosmk_create_action(ipp_action_id_t aid, nvlist_t **nvlpp,
 100     ipp_flags_t flags)
 101 {
 102         nvlist_t *nvlp;
 103         dlcosmk_data_t *dlcosmk_data;
 104         char *next_action;
 105         int err;
 106         uint32_t bstats, param;
 107 
 108         ASSERT((nvlpp != NULL) && (*nvlpp != NULL));
 109 
 110         nvlp = *nvlpp;
 111         *nvlpp = NULL;          /* nvlist should be NULL on return */
 112 
 113         if ((dlcosmk_data = kmem_zalloc(DLCOSMK_DATA_SZ, KM_NOSLEEP)) == NULL) {
 114                 nvlist_free(nvlp);
 115                 return (ENOMEM);
 116         }
 117 
 118         /* parse next action name */
 119         if ((err = nvlist_lookup_string(nvlp, DLCOSMK_NEXT_ACTION_NAME,
 120             &next_action)) != 0) {
 121                 nvlist_free(nvlp);
 122                 dlcosmk0dbg(("dlcosmk_create_action: invalid config, "\
 123                     "next_action name missing\n"));
 124                 kmem_free(dlcosmk_data, DLCOSMK_DATA_SZ);
 125                 return (err);
 126         }
 127         if ((dlcosmk_data->next_action =
 128             ipp_action_lookup(next_action)) == IPP_ACTION_INVAL) {
 129                 nvlist_free(nvlp);
 130                 dlcosmk0dbg(("dlcosmk_create_action: next_action invalid\n"));
 131                 kmem_free(dlcosmk_data, DLCOSMK_DATA_SZ);
 132                 return (EINVAL);
 133         }
 134 
 135         /* parse cos - from the config file */
 136         if ((err = nvlist_lookup_byte(nvlp, DLCOSMK_COS,
 137             &dlcosmk_data->usr_pri)) != 0) {
 138                 nvlist_free(nvlp);
 139                 dlcosmk0dbg(("dlcosmk_create_action: invalid config, "\
 140                     "cos missing\n"));
 141                 kmem_free(dlcosmk_data, DLCOSMK_DATA_SZ);
 142                 return (err);
 143         }
 144 
 145         /* parse b_band - mapped from cos */
 146         if ((err = nvlist_lookup_uint32(nvlp, DLCOSMK_BAND, &param)) != 0) {
 147                 nvlist_free(nvlp);
 148                 dlcosmk0dbg(("dlcosmk_create_action: invalid config, "\
 149                     "b_band missing\n"));
 150                 kmem_free(dlcosmk_data, DLCOSMK_DATA_SZ);
 151                 return (err);
 152         }
 153         dlcosmk_data->b_band = param;
 154 
 155         /* parse dl_priority.dl_max  - mapped from cos */
 156         if ((err = nvlist_lookup_uint32(nvlp, DLCOSMK_PRI, &param)) != 0) {
 157                 nvlist_free(nvlp);
 158                 dlcosmk0dbg(("dlcosmk_create_action: invalid config, "\
 159                     "dl_priority missing\n"));
 160                 kmem_free(dlcosmk_data, DLCOSMK_DATA_SZ);
 161                 return (err);
 162         }
 163         dlcosmk_data->dl_max = param;
 164 
 165         /* parse gather_stats boolean */
 166         if ((err = nvlist_lookup_uint32(nvlp, IPP_ACTION_STATS_ENABLE, &bstats))
 167             != 0) {
 168                 dlcosmk_data->gather_stats = B_FALSE;
 169         } else {
 170                 /* If stats is needed, initialize the stats structure */
 171                 dlcosmk_data->gather_stats = (bstats != 0) ? B_TRUE : B_FALSE;
 172                 if (dlcosmk_data->gather_stats) {
 173                         if ((err = dlcosmk_statinit(aid, dlcosmk_data)) != 0) {
 174                                 nvlist_free(nvlp);
 175                                 kmem_free(dlcosmk_data, DLCOSMK_DATA_SZ);
 176                                 return (err);
 177                         }
 178                 }
 179         }
 180 
 181         /* Free the nvlist */
 182         nvlist_free(nvlp);
 183 
 184         /* set action chain reference */
 185         if ((err = ipp_action_ref(aid, dlcosmk_data->next_action,
 186             flags)) != 0) {
 187                 dlcosmk0dbg(("dlcosmk_create_action: ipp_action_ref " \
 188                     "returned with error %d\n", err));
 189                 ipp_stat_destroy(dlcosmk_data->stats);
 190                 kmem_free(dlcosmk_data, DLCOSMK_DATA_SZ);
 191                 return (err);
 192         }
 193 
 194         ipp_action_set_ptr(aid, (void *)dlcosmk_data);
 195         return (0);
 196 }
 197 
 198 static int
 199 dlcosmk_modify_action(ipp_action_id_t aid, nvlist_t **nvlpp, ipp_flags_t flags)
 200 {
 201         nvlist_t *nvlp;
 202         int err = 0;
 203         uint32_t band, dlpri;
 204         uint8_t config_type;
 205         uint8_t cos;
 206         char *next_action_name;
 207         ipp_action_id_t next_action;
 208         dlcosmk_data_t *dlcosmk_data;
 209         uint32_t bstats;
 210 
 211         ASSERT((nvlpp != NULL) && (*nvlpp != NULL));
 212 
 213         nvlp = *nvlpp;
 214         *nvlpp = NULL;          /* nvlist should be NULL when this returns */
 215 
 216         if ((err = nvlist_lookup_byte(nvlp, IPP_CONFIG_TYPE, &config_type))
 217             != 0) {
 218                 nvlist_free(nvlp);
 219                 dlcosmk0dbg(("dlcosmk_modify_action: invalid configuration "\
 220                     "type\n"));
 221                 return (err);
 222         }
 223 
 224         if (config_type != IPP_SET) {
 225                 nvlist_free(nvlp);
 226                 dlcosmk0dbg(("dlcosmk_modify_action: invalid configuration "\
 227                     "type %d\n", config_type));
 228                 return (EINVAL);
 229         }
 230 
 231         dlcosmk_data = (dlcosmk_data_t *)ipp_action_get_ptr(aid);
 232         ASSERT(dlcosmk_data != NULL);
 233 
 234         /* parse next action name, if present */
 235         if ((err = nvlist_lookup_string(nvlp, DLCOSMK_NEXT_ACTION_NAME,
 236             &next_action_name)) == 0) {
 237                 /* lookup action name to get action id */
 238                 if ((next_action = ipp_action_lookup(next_action_name))
 239                     == IPP_ACTION_INVAL) {
 240                         nvlist_free(nvlp);
 241                         dlcosmk0dbg(("dlcosmk_modify_action: next_action "\
 242                             "invalid\n"));
 243                         return (EINVAL);
 244                 }
 245                 /* reference new action */
 246                 if ((err = ipp_action_ref(aid, next_action, flags)) != 0) {
 247                         nvlist_free(nvlp);
 248                         dlcosmk0dbg(("dlcosmk_modify_action: ipp_action_ref "\
 249                             "returned with error %d\n", err));
 250                         return (err);
 251                 }
 252                 /* unref old action */
 253                 err = ipp_action_unref(aid, dlcosmk_data->next_action, flags);
 254                 ASSERT(err == 0);
 255                 dlcosmk_data->next_action = next_action;
 256         }
 257 
 258         /* parse cos, if present */
 259         if ((err = nvlist_lookup_byte(nvlp, DLCOSMK_COS, &cos)) == 0) {
 260 
 261                 /* parse b_band, mapped from cos */
 262                 if ((err = nvlist_lookup_uint32(nvlp, DLCOSMK_BAND,
 263                     &band)) != 0) {
 264                         nvlist_free(nvlp);
 265                         dlcosmk0dbg(("dlcosmk_modify_action: b_band not "\
 266                             "provided\n"));
 267                         return (err);
 268                 }
 269 
 270                 /* parse dl_priority, mapped from cos */
 271                 if ((err = nvlist_lookup_uint32(nvlp, DLCOSMK_PRI,
 272                     &dlpri)) != 0) {
 273                         nvlist_free(nvlp);
 274                         dlcosmk0dbg(("dlcosmk_modify_action: dl_priority not "\
 275                             "provided\n"));
 276                         return (err);
 277                 }
 278 
 279                 /* Have all the three values, change them */
 280                 dlcosmk_data->usr_pri = cos;
 281                 dlcosmk_data->b_band = band;
 282                 dlcosmk_data->dl_max = dlpri;
 283         }
 284 
 285 
 286         /* parse gather_stats boolean, if present */
 287         if ((err = nvlist_lookup_uint32(nvlp, IPP_ACTION_STATS_ENABLE, &bstats))
 288             == 0) {
 289                 boolean_t val = (bstats != 0) ? B_TRUE : B_FALSE;
 290                 /* Turning on stats */
 291                 if (!dlcosmk_data->gather_stats && val) {
 292                         if ((err = dlcosmk_statinit(aid, dlcosmk_data)) != 0) {
 293                                 nvlist_free(nvlp);
 294                                 return (err);
 295                         }
 296                 /* Turning off stats */
 297                 } else if (!val && dlcosmk_data->gather_stats) {
 298                         ipp_stat_destroy(dlcosmk_data->stats);
 299 
 300                 }
 301                 dlcosmk_data->gather_stats = val;
 302         }
 303 
 304         /* Free thenvlist */
 305         nvlist_free(nvlp);
 306         return (0);
 307 }
 308 
 309 static int
 310 dlcosmk_destroy_action(ipp_action_id_t aid, ipp_flags_t flags)
 311 {
 312         dlcosmk_data_t *dlcosmk_data;
 313         int err;
 314 
 315         dlcosmk_data = (dlcosmk_data_t *)ipp_action_get_ptr(aid);
 316         ASSERT(dlcosmk_data != NULL);
 317 
 318         /* Destroy stats, if gathered */
 319         if (dlcosmk_data->gather_stats) {
 320                 ipp_stat_destroy(dlcosmk_data->stats);
 321         }
 322 
 323         /* unreference the action */
 324         err = ipp_action_unref(aid, dlcosmk_data->next_action, flags);
 325         ASSERT(err == 0);
 326 
 327         kmem_free(dlcosmk_data, DLCOSMK_DATA_SZ);
 328         return (0);
 329 }
 330 
 331 static int
 332 dlcosmk_invoke_action(ipp_action_id_t aid, ipp_packet_t *packet)
 333 {
 334         dlcosmk_data_t *dlcosmk_data;
 335         mblk_t *mp = NULL;
 336         int err;
 337         ip_priv_t *priv;
 338 
 339         ASSERT(packet != NULL);
 340 
 341         /* get mblk from ipp_packet structure */
 342         mp = ipp_packet_get_data(packet);
 343         priv = (ip_priv_t *)ipp_packet_get_private(packet);
 344 
 345         dlcosmk_data = (dlcosmk_data_t *)ipp_action_get_ptr(aid);
 346         ASSERT(dlcosmk_data != NULL);
 347 
 348         /* dlcosmk packet as configured */
 349         if ((err = dlcosmk_process(&mp, dlcosmk_data, priv->ill_index,
 350             priv->proc)) != 0) {
 351                 return (err);
 352         } else {
 353                 /* return packet with next action set */
 354                 return (ipp_packet_next(packet, dlcosmk_data->next_action));
 355         }
 356 }
 357 
 358 static int
 359 dlcosmk_statinit(ipp_action_id_t aid, dlcosmk_data_t *dlcosmk_data)
 360 {
 361         int err;
 362         dlcosmk_stat_t *statp;
 363 
 364         /* install stats entry */
 365         if ((err = ipp_stat_create(aid, DLCOSMK_STATS_STRING,
 366             DLCOSMK_STATS_COUNT, dlcosmk_update_stats, dlcosmk_data,
 367             &dlcosmk_data->stats)) != 0) {
 368                 dlcosmk0dbg(("dlcosmk_create_action: ipp_stat_create " \
 369                     "returned with error %d\n", err));
 370                 return (err);
 371         }
 372 
 373         statp = (dlcosmk_stat_t *)(dlcosmk_data->stats)->ipps_data;
 374         ASSERT(statp != NULL);
 375 
 376         if ((err = ipp_stat_named_init(dlcosmk_data->stats, "npackets",
 377             IPP_STAT_UINT64, &statp->npackets)) != 0) {
 378                 dlcosmk0dbg(("dlcosmk_create_action: ipp_stat_named_init " \
 379                     "returned with error %d\n", err));
 380                 return (err);
 381         }
 382 
 383         if ((err = ipp_stat_named_init(dlcosmk_data->stats, "ipackets",
 384             IPP_STAT_UINT64, &statp->ipackets)) != 0) {
 385                 dlcosmk0dbg(("dlcosmk_create_action: ipp_stat_named_init " \
 386                     "returned with error %d\n", err));
 387                 return (err);
 388         }
 389 
 390         if ((err = ipp_stat_named_init(dlcosmk_data->stats, "epackets",
 391             IPP_STAT_UINT64, &statp->epackets)) != 0) {
 392                 dlcosmk0dbg(("dlcosmk_create_action: ipp_stat_named_init " \
 393                     "returned with error %d\n", err));
 394                 return (err);
 395         }
 396 
 397         if ((err = ipp_stat_named_init(dlcosmk_data->stats, "usr_pri",
 398             IPP_STAT_INT32, &statp->usr_pri)) != 0) {
 399                 dlcosmk0dbg(("dlcosmk_create_action: ipp_stat_named_init " \
 400                     "returned with error %d", err));
 401                 return (err);
 402         }
 403 
 404         if ((err = ipp_stat_named_init(dlcosmk_data->stats, "b_band",
 405             IPP_STAT_INT32, &statp->b_band)) != 0) {
 406                 dlcosmk0dbg(("dlcosmk_create_action: ipp_stat_named_init " \
 407                     "returned with error %d\n", err));
 408                 return (err);
 409         }
 410 
 411         if ((err = ipp_stat_named_init(dlcosmk_data->stats, "dl_max",
 412             IPP_STAT_INT32, &statp->dl_max)) != 0) {
 413                 dlcosmk0dbg(("dlcosmk_create_action: ipp_stat_named_init " \
 414                     "returned with error %d\n", err));
 415                 return (err);
 416         }
 417 
 418         ipp_stat_install(dlcosmk_data->stats);
 419         return (0);
 420 }
 421 
 422 static int
 423 dlcosmk_update_stats(ipp_stat_t *sp, void *arg, int rw)
 424 {
 425         dlcosmk_data_t *dlcosmk_data = (dlcosmk_data_t *)arg;
 426         dlcosmk_stat_t *snames = (dlcosmk_stat_t *)sp->ipps_data;
 427         uint32_t upri, bband;
 428 
 429         ASSERT(dlcosmk_data != NULL);
 430         ASSERT(snames != NULL);
 431 
 432         upri = dlcosmk_data->usr_pri;
 433         bband = dlcosmk_data->b_band;
 434 
 435         (void) ipp_stat_named_op(&snames->npackets, &dlcosmk_data->npackets,
 436             rw);
 437         (void) ipp_stat_named_op(&snames->ipackets, &dlcosmk_data->ipackets,
 438             rw);
 439         (void) ipp_stat_named_op(&snames->epackets, &dlcosmk_data->epackets,
 440             rw);
 441         (void) ipp_stat_named_op(&snames->usr_pri, &upri, rw);
 442         (void) ipp_stat_named_op(&snames->b_band, &bband, rw);
 443         (void) ipp_stat_named_op(&snames->dl_max, &dlcosmk_data->dl_max, rw);
 444 
 445         return (0);
 446 }
 447 
 448 /* ARGSUSED */
 449 static int
 450 dlcosmk_info(ipp_action_id_t aid, int (*fn)(nvlist_t *, void *), void *arg,
 451     ipp_flags_t flags)
 452 {
 453         nvlist_t *nvlp;
 454         dlcosmk_data_t *dlcosmk_data;
 455         char *next_action;
 456         int err;
 457 
 458         ASSERT(fn != NULL);
 459 
 460         dlcosmk_data = (dlcosmk_data_t *)ipp_action_get_ptr(aid);
 461         ASSERT(dlcosmk_data != NULL);
 462 
 463         /* allocate nvlist to be passed back */
 464         if ((err = nvlist_alloc(&nvlp, NV_UNIQUE_NAME, KM_NOSLEEP)) != 0) {
 465                 dlcosmk0dbg(("dlcosmk_info: error allocating memory\n"));
 466                 return (err);
 467         }
 468 
 469         /* look up next action with the next action id */
 470         if ((err = ipp_action_name(dlcosmk_data->next_action,
 471             &next_action)) != 0) {
 472                 dlcosmk0dbg(("dlcosmk_info: next action not available\n"));
 473                 nvlist_free(nvlp);
 474                 return (err);
 475         }
 476 
 477         /* add next action name */
 478         if ((err = nvlist_add_string(nvlp, DLCOSMK_NEXT_ACTION_NAME,
 479             next_action)) != 0) {
 480                 dlcosmk0dbg(("dlcosmk_info: error adding next action\n"));
 481                 nvlist_free(nvlp);
 482                 kmem_free(next_action, (strlen(next_action) + 1));
 483                 return (err);
 484         }
 485 
 486         /* free action name */
 487         kmem_free(next_action, (strlen(next_action) + 1));
 488 
 489         /* add config type */
 490         if ((err = nvlist_add_byte(nvlp, IPP_CONFIG_TYPE, IPP_SET)) != 0) {
 491                 dlcosmk0dbg(("dlcosmk_info: error adding config. type\n"));
 492                 nvlist_free(nvlp);
 493                 return (err);
 494         }
 495 
 496         /* just give the cos, since that is what is provided in the config */
 497         if ((err = nvlist_add_byte(nvlp, DLCOSMK_COS, dlcosmk_data->usr_pri))
 498             != 0) {
 499                 dlcosmk0dbg(("dlcosmk_info: error adding cos\n"));
 500                 nvlist_free(nvlp);
 501                 return (err);
 502         }
 503 
 504         /* add gather stats boolean */
 505         if ((err = nvlist_add_uint32(nvlp, IPP_ACTION_STATS_ENABLE,
 506             (dlcosmk_data->gather_stats ? 1 : 0))) != 0) {
 507                 dlcosmk0dbg(("dlcosmk_info: error adding stats status\n"));
 508                 nvlist_free(nvlp);
 509                 return (err);
 510         }
 511 
 512         /* call back with nvlist */
 513         err = fn(nvlp, arg);
 514 
 515         nvlist_free(nvlp);
 516         return (err);
 517 }