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