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 /*
  27  * IP Tunneling Driver
  28  *
  29  * As viewed from the top, this module is a GLDv3 driver that consumes the
  30  * mac driver interfaces.  It implements the logic for various forms of IP
  31  * (IPv4 or IPv6) encapsulation within IP (IPv4 or IPv6).
  32  */
  33 
  34 #include <sys/file.h>
  35 #include <sys/list.h>
  36 #include "iptun_impl.h"
  37 
  38 #define IPTUN_LINKINFO          "IP tunneling driver"
  39 #define IPTUN_HASHSZ            67
  40 
  41 dev_info_t      *iptun_dip;
  42 ldi_ident_t     iptun_ldi_ident;
  43 
  44 static int      iptun_attach(dev_info_t *, ddi_attach_cmd_t);
  45 static int      iptun_detach(dev_info_t *, ddi_detach_cmd_t);
  46 static int      iptun_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
  47 static int      iptun_constructor(void *, void *, int);
  48 static void     iptun_destructor(void *, void *);
  49 
  50 DDI_DEFINE_STREAM_OPS(iptun_dev_ops, nulldev, nulldev, iptun_attach,
  51     iptun_detach, nodev, iptun_getinfo, D_MP, NULL, ddi_quiesce_not_supported);
  52 
  53 static struct modldrv iptun_modldrv = {
  54         &mod_driverops,
  55         IPTUN_LINKINFO,
  56         &iptun_dev_ops
  57 };
  58 
  59 static struct modlinkage iptun_modlinkage = {
  60         MODREV_1,
  61         &iptun_modldrv,
  62         NULL
  63 };
  64 
  65 /*
  66  * Initialize the tunnel stack instance.
  67  */
  68 /* ARGSUSED */
  69 static void *
  70 iptun_stack_init(netstackid_t stackid, netstack_t *ns)
  71 {
  72         iptun_stack_t   *iptuns;
  73 
  74         iptuns = kmem_zalloc(sizeof (*iptuns), KM_SLEEP);
  75         iptuns->iptuns_netstack = ns;
  76         mutex_init(&iptuns->iptuns_lock, NULL, MUTEX_DEFAULT, NULL);
  77         list_create(&iptuns->iptuns_iptunlist, sizeof (iptun_t),
  78             offsetof(iptun_t, iptun_link));
  79 
  80         return (iptuns);
  81 }
  82 
  83 /* ARGSUSED */
  84 static void
  85 iptun_stack_shutdown(netstackid_t stackid, void *arg)
  86 {
  87         iptun_stack_t   *iptuns = arg;
  88         iptun_t         *iptun;
  89         datalink_id_t   linkid;
  90 
  91         /* note that iptun_delete() removes iptun from the list */
  92         while ((iptun = list_head(&iptuns->iptuns_iptunlist)) != NULL) {
  93                 linkid = iptun->iptun_linkid;
  94                 (void) iptun_delete(linkid, iptun->iptun_connp->conn_cred);
  95                 (void) dls_mgmt_destroy(linkid, B_FALSE);
  96         }
  97 }
  98 
  99 /*
 100  * Free the tunnel stack instance.
 101  */
 102 /* ARGSUSED */
 103 static void
 104 iptun_stack_fini(netstackid_t stackid, void *arg)
 105 {
 106         iptun_stack_t *iptuns = arg;
 107 
 108         list_destroy(&iptuns->iptuns_iptunlist);
 109         mutex_destroy(&iptuns->iptuns_lock);
 110         kmem_free(iptuns, sizeof (*iptuns));
 111 }
 112 
 113 static void
 114 iptun_fini(void)
 115 {
 116         ddi_taskq_destroy(iptun_taskq);
 117         mac_fini_ops(&iptun_dev_ops);
 118         ldi_ident_release(iptun_ldi_ident);
 119         mod_hash_destroy_idhash(iptun_hash);
 120         kmem_cache_destroy(iptun_cache);
 121 }
 122 
 123 int
 124 _init(void)
 125 {
 126         int rc;
 127 
 128         rc = ldi_ident_from_mod(&iptun_modlinkage, &iptun_ldi_ident);
 129         if (rc != 0)
 130                 return (rc);
 131 
 132         iptun_cache = kmem_cache_create("iptun_cache", sizeof (iptun_t), 0,
 133             iptun_constructor, iptun_destructor, NULL, NULL, NULL, 0);
 134         if (iptun_cache == NULL) {
 135                 ldi_ident_release(iptun_ldi_ident);
 136                 return (ENOMEM);
 137         }
 138 
 139         iptun_taskq = ddi_taskq_create(NULL, "iptun_taskq", 1,
 140             TASKQ_DEFAULTPRI, 0);
 141         if (iptun_taskq == NULL) {
 142                 ldi_ident_release(iptun_ldi_ident);
 143                 kmem_cache_destroy(iptun_cache);
 144                 return (ENOMEM);
 145         }
 146 
 147         iptun_hash = mod_hash_create_idhash("iptun_hash", IPTUN_HASHSZ,
 148             mod_hash_null_valdtor);
 149 
 150         mac_init_ops(&iptun_dev_ops, IPTUN_DRIVER_NAME);
 151 
 152         if ((rc = mod_install(&iptun_modlinkage)) != 0)
 153                 iptun_fini();
 154         return (rc);
 155 }
 156 
 157 int
 158 _fini(void)
 159 {
 160         int rc;
 161 
 162         if ((rc = mod_remove(&iptun_modlinkage)) == 0)
 163                 iptun_fini();
 164         return (rc);
 165 }
 166 
 167 int
 168 _info(struct modinfo *modinfop)
 169 {
 170         return (mod_info(&iptun_modlinkage, modinfop));
 171 }
 172 
 173 static int
 174 iptun_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
 175 {
 176         switch (cmd) {
 177         case DDI_ATTACH:
 178                 if (ddi_get_instance(dip) != 0 || iptun_ioc_init() != 0)
 179                         return (DDI_FAILURE);
 180                 iptun_dip = dip;
 181                 netstack_register(NS_IPTUN, iptun_stack_init,
 182                     iptun_stack_shutdown, iptun_stack_fini);
 183                 return (DDI_SUCCESS);
 184 
 185         default:
 186                 return (DDI_FAILURE);
 187         }
 188 }
 189 
 190 /* ARGSUSED */
 191 static int
 192 iptun_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
 193 {
 194         switch (cmd) {
 195         case DDI_DETACH:
 196                 /*
 197                  * We prevent the pseudo device from detaching (and thus the
 198                  * driver from unloading) when there are tunnels configured by
 199                  * consulting iptun_count().  We don't need to hold a lock
 200                  * here because the tunnel count is only changed when a tunnel
 201                  * is created or deleted, which can't happen while the detach
 202                  * routine is running (the ioctl path calls
 203                  * ddi_hold_devi_by_instance() in dld's drv_ioctl(), and the
 204                  * /dev/net implicit path has the device open).
 205                  */
 206                 if (iptun_count() > 0)
 207                         return (DDI_FAILURE);
 208                 netstack_unregister(NS_IPTUN);
 209                 iptun_dip = NULL;
 210                 iptun_ioc_fini();
 211                 return (DDI_SUCCESS);
 212 
 213         default:
 214                 return (DDI_FAILURE);
 215         }
 216 }
 217 
 218 /* ARGSUSED */
 219 static int
 220 iptun_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
 221 {
 222         switch (infocmd) {
 223         case DDI_INFO_DEVT2DEVINFO:
 224                 *result = iptun_dip;
 225                 return (DDI_SUCCESS);
 226         case DDI_INFO_DEVT2INSTANCE:
 227                 *result = NULL;
 228                 return (DDI_SUCCESS);
 229         }
 230         return (DDI_FAILURE);
 231 }
 232 
 233 /* ARGSUSED */
 234 static int
 235 iptun_constructor(void *buf, void *cdrarg, int kmflags)
 236 {
 237         iptun_t *iptun = buf;
 238 
 239         bzero(iptun, sizeof (*iptun));
 240         mutex_init(&iptun->iptun_lock, NULL, MUTEX_DEFAULT, NULL);
 241         cv_init(&iptun->iptun_upcall_cv, NULL, CV_DRIVER, NULL);
 242         cv_init(&iptun->iptun_enter_cv, NULL, CV_DRIVER, NULL);
 243 
 244         return (0);
 245 }
 246 
 247 /* ARGSUSED */
 248 static void
 249 iptun_destructor(void *buf, void *cdrarg)
 250 {
 251         iptun_t *iptun = buf;
 252 
 253         /* This iptun_t must not still be in use. */
 254         ASSERT(!(iptun->iptun_flags & (IPTUN_BOUND|IPTUN_MAC_REGISTERED|
 255             IPTUN_MAC_STARTED|IPTUN_HASH_INSERTED|IPTUN_UPCALL_PENDING)));
 256 
 257         mutex_destroy(&iptun->iptun_lock);
 258         cv_destroy(&iptun->iptun_upcall_cv);
 259 }