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 2009 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 #include <sys/systm.h>
  27 #include <sys/socket.h>
  28 #include <netinet/in.h>
  29 #include <sys/modctl.h>
  30 #include <sys/sunddi.h>
  31 #include <ipp/ipp.h>
  32 #include <ipp/ipp_config.h>
  33 #include <ipp/ipgpc/classifier.h>
  34 #include <inet/ip.h>
  35 #include <net/if.h>
  36 #include <inet/ip_if.h>
  37 #include <inet/ipp_common.h>
  38 
  39 /* DDI file for ipgpc ipp module */
  40 
  41 /* protects against multiple configs  */
  42 static kmutex_t ipgpc_config_lock;
  43 
  44 static int ipgpc_create_action(ipp_action_id_t, nvlist_t **, ipp_flags_t);
  45 static int ipgpc_modify_action(ipp_action_id_t, nvlist_t **, ipp_flags_t);
  46 static int ipgpc_destroy_action(ipp_action_id_t, ipp_flags_t);
  47 static int ipgpc_info(ipp_action_id_t aid, int (*)(nvlist_t *, void *), void *,
  48     ipp_flags_t);
  49 static int ipgpc_invoke_action(ipp_action_id_t, ipp_packet_t *);
  50 
  51 ipp_ops_t ipgpc_ops = {
  52         IPPO_REV,
  53         ipgpc_create_action,    /* ippo_action_create */
  54         ipgpc_modify_action,    /* ippo_action_modify */
  55         ipgpc_destroy_action,   /* ippo_action_destroy */
  56         ipgpc_info,             /* ippo_action_info */
  57         ipgpc_invoke_action     /* ippo_action_invoke */
  58 };
  59 
  60 extern struct mod_ops mod_ippops;
  61 
  62 /*
  63  * Module linkage information for the kernel.
  64  */
  65 static struct modlipp modlipp = {
  66         &mod_ippops,
  67         "IP Generic Packet Classifier (ipgpc) module 1.0",
  68         &ipgpc_ops
  69 };
  70 
  71 static struct modlinkage modlinkage = {
  72         MODREV_1,
  73         { (void *)&modlipp, NULL }
  74 };
  75 
  76 #define __FN__  "_init"
  77 int
  78 _init(
  79         void)
  80 {
  81         int rc;
  82 
  83         if (ipgpc_action_exist) {
  84                 return (EBUSY);
  85         }
  86         /* init mutexes */
  87         mutex_init(&ipgpc_config_lock, NULL, MUTEX_DRIVER, NULL);
  88         mutex_init(&ipgpc_fid_list_lock, NULL, MUTEX_DRIVER, NULL);
  89         mutex_init(&ipgpc_cid_list_lock, NULL, MUTEX_DRIVER, NULL);
  90         mutex_init(&ipgpc_table_list_lock, NULL, MUTEX_DRIVER, NULL);
  91         mutex_init(&ipgpc_ds_table_id.lock, NULL, MUTEX_DRIVER, NULL);
  92 
  93         if ((rc = mod_install(&modlinkage)) != 0) {
  94                 /* clean up after fail */
  95                 mutex_destroy(&ipgpc_config_lock);
  96                 mutex_destroy(&ipgpc_fid_list_lock);
  97                 mutex_destroy(&ipgpc_cid_list_lock);
  98                 mutex_destroy(&ipgpc_table_list_lock);
  99                 mutex_destroy(&ipgpc_ds_table_id.lock);
 100         }
 101 
 102         return (rc);
 103 }
 104 #undef  __FN__
 105 
 106 #define __FN__  "_fini"
 107 int
 108 _fini(
 109         void)
 110 {
 111         int rc;
 112 
 113         if (ipgpc_action_exist) {
 114                 return (EBUSY);
 115         }
 116 
 117         if ((rc = mod_remove(&modlinkage)) != 0) {
 118                 return (rc);
 119         }
 120         /* destroy mutexes */
 121         mutex_destroy(&ipgpc_config_lock);
 122         mutex_destroy(&ipgpc_fid_list_lock);
 123         mutex_destroy(&ipgpc_cid_list_lock);
 124         mutex_destroy(&ipgpc_table_list_lock);
 125         mutex_destroy(&ipgpc_ds_table_id.lock);
 126         return (rc);
 127 }
 128 #undef  __FN__
 129 
 130 #define __FN__  "_info"
 131 int
 132 _info(
 133         struct  modinfo *modinfop)
 134 {
 135         return (mod_info(&modlinkage, modinfop));
 136 }
 137 #undef  __FN__
 138 
 139 /*
 140  * ipgpc_create_action(aid, nvlpp, flags)
 141  *
 142  * creates a single instance of ipgpc, if one does not exist.  If an action
 143  * instance already exists, fail with EBUSY
 144  *
 145  * if nvlpp contains the name IPP_ACTION_STATS_ENABLE, then process it and
 146  * determine if global stats should be collected
 147  *
 148  * the ipgpc_config_lock is taken to block out any other creates or destroys
 149  * the are issued while the create is taking place
 150  */
 151 /* ARGSUSED */
 152 static int
 153 ipgpc_create_action(ipp_action_id_t aid, nvlist_t **nvlpp, ipp_flags_t flags)
 154 {
 155         int rc;
 156         uint32_t stat;
 157         nvlist_t *nvlp;
 158 
 159         nvlp = *nvlpp;
 160         *nvlpp = NULL;          /* nvlist should be NULL when this returns */
 161 
 162         /* only one ipgpc action instance can be loaded at once */
 163         if (ipgpc_action_exist) {
 164                 nvlist_free(nvlp);
 165                 return (EBUSY);
 166         } else {
 167                 mutex_enter(&ipgpc_config_lock);
 168                 if (ipgpc_action_exist) {
 169                         nvlist_free(nvlp);
 170                         mutex_exit(&ipgpc_config_lock);
 171                         return (EBUSY);
 172                 }
 173                 /* check for action param IPP_ACTION_STATS_ENABLE */
 174                 if ((rc = nvlist_lookup_uint32(nvlp, IPP_ACTION_STATS_ENABLE,
 175                     &stat)) != 0) {
 176                         ipgpc_gather_stats = B_FALSE; /* disabled by default */
 177                 } else {
 178                         ipgpc_gather_stats = (boolean_t)stat;
 179                 }
 180                 if ((rc = ipgpc_initialize(aid)) != 0) {
 181                         ipgpc0dbg(("ipgpc_create_action: ipgpc_intialize " \
 182                             "error %d", rc));
 183                         ipgpc_destroy(IPP_DESTROY_REF);
 184                         ipgpc_action_exist = B_FALSE;
 185                         nvlist_free(nvlp);
 186                         mutex_exit(&ipgpc_config_lock);
 187                         return (rc);
 188                 }
 189                 ipgpc_action_exist = B_TRUE;
 190                 nvlist_free(nvlp);
 191                 mutex_exit(&ipgpc_config_lock);
 192                 return (0);
 193         }
 194 }
 195 
 196 /*
 197  * ipgpc_modify_action
 198  *
 199  * modify an instance of ipgpc
 200  *
 201  * nvlpp will contain the configuration type to switch off of.  Use this
 202  * to determine what modification should be made.  If the modification fails,
 203  * return the appropriate error.
 204  */
 205 /* ARGSUSED */
 206 static int
 207 ipgpc_modify_action(ipp_action_id_t aid, nvlist_t **nvlpp, ipp_flags_t flags)
 208 {
 209         nvlist_t *nvlp;
 210         int rc = 0;
 211         uint8_t config_type;
 212         uint32_t stat;
 213         char *name;
 214         int32_t filter_instance;
 215         ipgpc_filter_t *filter;
 216         ipgpc_class_t *aclass;
 217 
 218         nvlp = *nvlpp;
 219         *nvlpp = NULL;          /* nvlist should be NULL when this returns */
 220 
 221         if ((rc = nvlist_lookup_byte(nvlp, IPP_CONFIG_TYPE, &config_type))
 222             != 0) {
 223                 nvlist_free(nvlp);
 224                 ipgpc0dbg(("ipgpc_modify_action: invalid configuration type"));
 225                 return (EINVAL);
 226         }
 227 
 228         switch (config_type) {
 229         case IPP_SET:           /* set an action parameter */
 230                 if ((rc = nvlist_lookup_uint32(nvlp, IPP_ACTION_STATS_ENABLE,
 231                     &stat)) != 0) {
 232                         nvlist_free(nvlp);
 233                         ipgpc0dbg(("ipgpc_modify_action: invalid IPP_SET " \
 234                             "parameter"));
 235                         return (EINVAL);
 236                 } else {
 237                         ipgpc_gather_stats = (boolean_t)stat;
 238                 }
 239                 break;
 240         case CLASSIFIER_ADD_FILTER: /* add a filter */
 241                 filter = kmem_zalloc(sizeof (ipgpc_filter_t), KM_SLEEP);
 242                 if ((rc = ipgpc_parse_filter(filter, nvlp)) != 0) {
 243                         ipgpc0dbg(("ipgpc_modify_action: invalid filter"));
 244                         ipgpc_filter_destructor(filter);
 245                         kmem_free(filter, sizeof (ipgpc_filter_t));
 246                         break;
 247                 }
 248                 /* parse class name */
 249                 if ((rc = nvlist_lookup_string(nvlp, CLASSIFIER_CLASS_NAME,
 250                     &name)) != 0) {
 251                         ipgpc0dbg(("ipgpc_modify_action: class name missing"));
 252                         ipgpc_filter_destructor(filter);
 253                         kmem_free(filter, sizeof (ipgpc_filter_t));
 254                         break;
 255                 }
 256                 rc = ipgpc_addfilter(filter, name, flags);
 257                 if (rc != 0) {
 258                         ipgpc_filter_destructor(filter);
 259                 }
 260                 kmem_free(filter, sizeof (ipgpc_filter_t));
 261                 break;
 262         case CLASSIFIER_ADD_CLASS: /* add a class */
 263                 aclass = kmem_zalloc(sizeof (ipgpc_class_t), KM_SLEEP);
 264                 if ((rc = ipgpc_parse_class(aclass, nvlp)) != 0) {
 265                         ipgpc0dbg(("ipgpc_modify_action: invalid class"));
 266                         kmem_free(aclass, sizeof (ipgpc_class_t));
 267                         break;
 268                 }
 269                 rc = ipgpc_addclass(aclass, flags);
 270                 kmem_free(aclass, sizeof (ipgpc_class_t));
 271                 break;
 272         case CLASSIFIER_REMOVE_FILTER: /* remove a filter */
 273                 /* parse filter name */
 274                 if ((rc = nvlist_lookup_string(nvlp, CLASSIFIER_FILTER_NAME,
 275                     &name)) != 0) {
 276                         ipgpc0dbg(("ipgpc_modify_action: filtername missing"));
 277                         break;
 278                 }
 279                 /* parse optional filter_instance */
 280                 if (nvlist_lookup_int32(nvlp, IPGPC_FILTER_INSTANCE,
 281                     &filter_instance) != 0) {
 282                         filter_instance = -1;
 283                 }
 284                 rc = ipgpc_removefilter(name, filter_instance, flags);
 285                 break;
 286         case CLASSIFIER_REMOVE_CLASS: /* remove a class */
 287                 /* parse class name */
 288                 if ((rc = nvlist_lookup_string(nvlp, CLASSIFIER_CLASS_NAME,
 289                     &name)) != 0) {
 290                         ipgpc0dbg(("ipgpc_modify_action: class name missing"));
 291                         break;
 292                 }
 293                 rc = ipgpc_removeclass(name, flags);
 294                 break;
 295         case CLASSIFIER_MODIFY_FILTER: /* modify a filter */
 296                 rc = ipgpc_modifyfilter(&nvlp, flags);
 297                 break;
 298         case CLASSIFIER_MODIFY_CLASS: /* modify a class */
 299                 rc = ipgpc_modifyclass(&nvlp, flags);
 300                 break;
 301         default:                /* invalid config type */
 302                 nvlist_free(nvlp);
 303                 ipgpc0dbg(("ipgpc_modify_action:invalid configuration type %u",
 304                     config_type));
 305                 return (EINVAL);
 306         }
 307         nvlist_free(nvlp);      /* free the list */
 308         return (rc);            /* nvlist is passed back NULL */
 309 }
 310 
 311 /*
 312  * ipgpc_destroy_action(aid, flags)
 313  *
 314  * action destructor for ipgpc
 315  *
 316  * Destroys an instance of the ipgpc action, if one exists. The
 317  * ipgpc_action_lock is taken to block out any other destroys or creates
 318  * that might be issued while the action is being destroyed
 319  */
 320 /* ARGSUSED */
 321 static int
 322 ipgpc_destroy_action(ipp_action_id_t aid, ipp_flags_t flags)
 323 {
 324         /* only destroy action if it exists */
 325         if (ipgpc_action_exist == B_TRUE) {
 326                 mutex_enter(&ipgpc_config_lock);
 327                 if (ipgpc_action_exist == B_FALSE) {
 328                         mutex_exit(&ipgpc_config_lock);
 329                         return (EBUSY);
 330                 }
 331                 ipgpc_action_exist = B_FALSE;
 332                 ipgpc_destroy(flags);
 333                 mutex_exit(&ipgpc_config_lock);
 334         }
 335         return (0);
 336 }
 337 
 338 /*
 339  * ipgpc_info(aid, fn, arg)
 340  *
 341  * configuration quering function for ipgpc
 342  *
 343  * passes back the configuration of ipgpc through allocated nvlists
 344  * all action paramaters, classes and filters are built into nvlists
 345  * and passed to the function pointer fn with arg
 346  */
 347 /* ARGSUSED */
 348 static int
 349 ipgpc_info(ipp_action_id_t aid, int (*fn)(nvlist_t *, void *), void *arg,
 350     ipp_flags_t flags)
 351 {
 352         int rc;
 353 
 354         /* set parameters */
 355         if ((rc = ipgpc_params_info(fn, arg)) != 0) {
 356                 return (rc);
 357         }
 358 
 359         /* set all classes */
 360         if ((rc = ipgpc_classes_info(fn, arg)) != 0) {
 361                 return (rc);
 362         }
 363 
 364         /* set all filters */
 365         if ((rc = ipgpc_filters_info(fn, arg)) != 0) {
 366                 return (rc);
 367         }
 368         return (0);
 369 }
 370 
 371 /*
 372  * ipgpc_invoke_action(aid, packet)
 373  *
 374  * packet processing function for ipgpc
 375  *
 376  * given packet the selector information is parsed and the classify
 377  * function is called with those selectors.  The classify function will
 378  * return either a class or NULL, which represents a memory error and
 379  * ENOMEM is returned.  If the class returned is not NULL, the class and next
 380  * action, associated with that class, are added to packet
 381  */
 382 /* ARGSUSED */
 383 static int
 384 ipgpc_invoke_action(ipp_action_id_t aid, ipp_packet_t *packet)
 385 {
 386         ipgpc_class_t *out_class;
 387         hrtime_t start, end;
 388         mblk_t *mp = NULL;
 389         ip_priv_t *priv = NULL;
 390         ill_t *ill = NULL;
 391         ipha_t *ipha;
 392         ip_proc_t callout_pos;
 393         int af;
 394         int rc;
 395         ipgpc_packet_t pkt;
 396         uint_t ill_idx;
 397 
 398         /* extract packet data */
 399         mp = ipp_packet_get_data(packet);
 400         ASSERT(mp != NULL);
 401 
 402         priv = (ip_priv_t *)ipp_packet_get_private(packet);
 403         ASSERT(priv != NULL);
 404 
 405         callout_pos = priv->proc;
 406         ill_idx = priv->ill_index;
 407 
 408         /* If we don't get an M_DATA, then return an error */
 409         if (mp->b_datap->db_type != M_DATA) {
 410                 if ((mp->b_cont != NULL) &&
 411                     (mp->b_cont->b_datap->db_type == M_DATA)) {
 412                         mp = mp->b_cont; /* jump over the M_CTL into M_DATA */
 413                 } else {
 414                         ipgpc0dbg(("ipgpc_invoke_action: no data\n"));
 415                         atomic_inc_64(&ipgpc_epackets);
 416                         return (EINVAL);
 417                 }
 418         }
 419 
 420         /*
 421          * Translate the callout_pos into the direction the packet is traveling
 422          */
 423         if (callout_pos != IPP_LOCAL_IN) {
 424                 if (callout_pos & IPP_LOCAL_OUT) {
 425                         callout_pos = IPP_LOCAL_OUT;
 426                 } else if (callout_pos & IPP_FWD_IN) {
 427                         callout_pos = IPP_FWD_IN;
 428                 } else {        /* IPP_FWD_OUT */
 429                         callout_pos = IPP_FWD_OUT;
 430                 }
 431         }
 432 
 433         /* parse the packet from the message block */
 434         ipha = (ipha_t *)mp->b_rptr;
 435         /* Determine IP Header Version */
 436         if (IPH_HDR_VERSION(ipha) == IPV4_VERSION) {
 437                 parse_packet(&pkt, mp);
 438                 af = AF_INET;
 439         } else {
 440                 parse_packet6(&pkt, mp);
 441                 af = AF_INET6;
 442         }
 443 
 444         pkt.direction = callout_pos; /* set packet direction */
 445 
 446         /* The ill_index could be 0 when called from forwarding (read) path */
 447         if (ill_idx > 0)
 448                 ill = ill_lookup_on_ifindex_global_instance(ill_idx, B_FALSE);
 449 
 450         if (ill != NULL) {
 451                 /*
 452                  * Since all IPP actions in an IPMP group are performed
 453                  * relative to the IPMP group interface, if this is an
 454                  * underlying interface in an IPMP group, use the IPMP
 455                  * group interface's index.
 456                  */
 457                 if (IS_UNDER_IPMP(ill))
 458                         pkt.if_index = ipmp_ill_get_ipmp_ifindex(ill);
 459                 else
 460                         pkt.if_index = ill->ill_phyint->phyint_ifindex;
 461                 /* Got the field from the ILL, go ahead and refrele */
 462                 ill_refrele(ill);
 463         } else {
 464                 /* unknown if_index */
 465                 pkt.if_index = IPGPC_UNSPECIFIED;
 466         }
 467 
 468         if (ipgpc_debug > 5) {
 469                 /* print pkt under high debug level */
 470 #ifdef  IPGPC_DEBUG
 471                 print_packet(af, &pkt);
 472 #endif
 473         }
 474         if (ipgpc_debug > 3) {
 475                 start = gethrtime(); /* start timer */
 476         }
 477 
 478         /* classify this packet */
 479         out_class = ipgpc_classify(af, &pkt);
 480 
 481         if (ipgpc_debug > 3) {
 482                 end = gethrtime(); /* stop timer */
 483         }
 484 
 485         /* ipgpc_classify will only return NULL if a memory error occured */
 486         if (out_class == NULL) {
 487                 atomic_inc_64(&ipgpc_epackets);
 488                 return (ENOMEM);
 489         }
 490 
 491         ipgpc1dbg(("ipgpc_invoke_action: class = %s", out_class->class_name));
 492         /* print time to classify(..) */
 493         ipgpc2dbg(("ipgpc_invoke_action: time = %lld nsec\n", (end - start)));
 494 
 495         if ((rc = ipp_packet_add_class(packet, out_class->class_name,
 496             out_class->next_action)) != 0) {
 497                 atomic_inc_64(&ipgpc_epackets);
 498                 ipgpc0dbg(("ipgpc_invoke_action: ipp_packet_add_class " \
 499                     "failed with error %d", rc));
 500                 return (rc);
 501         }
 502         return (ipp_packet_next(packet, IPP_ACTION_CONT));
 503 }