summaryrefslogtreecommitdiff
path: root/usr/src/uts/common/inet/iptun/iptun_dev.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/uts/common/inet/iptun/iptun_dev.c')
-rw-r--r--usr/src/uts/common/inet/iptun/iptun_dev.c261
1 files changed, 261 insertions, 0 deletions
diff --git a/usr/src/uts/common/inet/iptun/iptun_dev.c b/usr/src/uts/common/inet/iptun/iptun_dev.c
new file mode 100644
index 0000000000..52218bdc18
--- /dev/null
+++ b/usr/src/uts/common/inet/iptun/iptun_dev.c
@@ -0,0 +1,261 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * IP Tunneling Driver
+ *
+ * As viewed from the top, this module is a GLDv3 driver that consumes the
+ * mac driver interfaces. It implements the logic for various forms of IP
+ * (IPv4 or IPv6) encapsulation within IP (IPv4 or IPv6).
+ */
+
+#include <sys/file.h>
+#include <sys/list.h>
+#include "iptun_impl.h"
+
+#define IPTUN_LINKINFO "IP tunneling driver"
+#define IPTUN_HASHSZ 67
+
+dev_info_t *iptun_dip;
+ldi_ident_t iptun_ldi_ident;
+
+static int iptun_attach(dev_info_t *, ddi_attach_cmd_t);
+static int iptun_detach(dev_info_t *, ddi_detach_cmd_t);
+static int iptun_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
+static int iptun_constructor(void *, void *, int);
+static void iptun_destructor(void *, void *);
+
+DDI_DEFINE_STREAM_OPS(iptun_dev_ops, nulldev, nulldev, iptun_attach,
+ iptun_detach, nodev, iptun_getinfo, D_MP, NULL, ddi_quiesce_not_supported);
+
+static struct modldrv iptun_modldrv = {
+ &mod_driverops,
+ IPTUN_LINKINFO,
+ &iptun_dev_ops
+};
+
+static struct modlinkage iptun_modlinkage = {
+ MODREV_1,
+ &iptun_modldrv,
+ NULL
+};
+
+/*
+ * Initialize the tunnel stack instance.
+ */
+/* ARGSUSED */
+static void *
+iptun_stack_init(netstackid_t stackid, netstack_t *ns)
+{
+ iptun_stack_t *iptuns;
+
+ iptuns = kmem_zalloc(sizeof (*iptuns), KM_SLEEP);
+ iptuns->iptuns_netstack = ns;
+ mutex_init(&iptuns->iptuns_lock, NULL, MUTEX_DEFAULT, NULL);
+ list_create(&iptuns->iptuns_iptunlist, sizeof (iptun_t),
+ offsetof(iptun_t, iptun_link));
+
+ return (iptuns);
+}
+
+/* ARGSUSED */
+static void
+iptun_stack_shutdown(netstackid_t stackid, void *arg)
+{
+ iptun_stack_t *iptuns = arg;
+ iptun_t *iptun;
+ datalink_id_t linkid;
+
+ /* note that iptun_delete() removes iptun from the list */
+ while ((iptun = list_head(&iptuns->iptuns_iptunlist)) != NULL) {
+ linkid = iptun->iptun_linkid;
+ (void) iptun_delete(linkid, iptun->iptun_cred);
+ (void) dls_mgmt_destroy(linkid, B_FALSE);
+ }
+ if (iptuns->iptuns_g_q != NULL)
+ (void) ldi_close(iptuns->iptuns_g_q_lh, FWRITE|FREAD, CRED());
+}
+
+/*
+ * Free the tunnel stack instance.
+ */
+/* ARGSUSED */
+static void
+iptun_stack_fini(netstackid_t stackid, void *arg)
+{
+ iptun_stack_t *iptuns = arg;
+
+ list_destroy(&iptuns->iptuns_iptunlist);
+ mutex_destroy(&iptuns->iptuns_lock);
+ kmem_free(iptuns, sizeof (*iptuns));
+}
+
+static void
+iptun_fini(void)
+{
+ ddi_taskq_destroy(iptun_taskq);
+ mac_fini_ops(&iptun_dev_ops);
+ ldi_ident_release(iptun_ldi_ident);
+ mod_hash_destroy_idhash(iptun_hash);
+ kmem_cache_destroy(iptun_cache);
+}
+
+int
+_init(void)
+{
+ int rc;
+
+ rc = ldi_ident_from_mod(&iptun_modlinkage, &iptun_ldi_ident);
+ if (rc != 0)
+ return (rc);
+
+ iptun_cache = kmem_cache_create("iptun_cache", sizeof (iptun_t), 0,
+ iptun_constructor, iptun_destructor, NULL, NULL, NULL, 0);
+ if (iptun_cache == NULL) {
+ ldi_ident_release(iptun_ldi_ident);
+ return (ENOMEM);
+ }
+
+ iptun_taskq = ddi_taskq_create(NULL, "iptun_taskq", 1,
+ TASKQ_DEFAULTPRI, 0);
+ if (iptun_taskq == NULL) {
+ ldi_ident_release(iptun_ldi_ident);
+ kmem_cache_destroy(iptun_cache);
+ return (ENOMEM);
+ }
+
+ iptun_hash = mod_hash_create_idhash("iptun_hash", IPTUN_HASHSZ,
+ mod_hash_null_valdtor);
+
+ mac_init_ops(&iptun_dev_ops, IPTUN_DRIVER_NAME);
+
+ if ((rc = mod_install(&iptun_modlinkage)) != 0)
+ iptun_fini();
+ return (rc);
+}
+
+int
+_fini(void)
+{
+ int rc;
+
+ if ((rc = mod_remove(&iptun_modlinkage)) == 0)
+ iptun_fini();
+ return (rc);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&iptun_modlinkage, modinfop));
+}
+
+static int
+iptun_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+{
+ switch (cmd) {
+ case DDI_ATTACH:
+ if (ddi_get_instance(dip) != 0 || iptun_ioc_init() != 0)
+ return (DDI_FAILURE);
+ iptun_dip = dip;
+ netstack_register(NS_IPTUN, iptun_stack_init,
+ iptun_stack_shutdown, iptun_stack_fini);
+ return (DDI_SUCCESS);
+
+ default:
+ return (DDI_FAILURE);
+ }
+}
+
+/* ARGSUSED */
+static int
+iptun_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+ switch (cmd) {
+ case DDI_DETACH:
+ /*
+ * We prevent the pseudo device from detaching (and thus the
+ * driver from unloading) when there are tunnels configured by
+ * consulting iptun_count(). We don't need to hold a lock
+ * here because the tunnel count is only changed when a tunnel
+ * is created or deleted, which can't happen while the detach
+ * routine is running (the ioctl path calls
+ * ddi_hold_devi_by_instance() in dld's drv_ioctl(), and the
+ * /dev/net implicit path has the device open).
+ */
+ if (iptun_count() > 0)
+ return (DDI_FAILURE);
+ netstack_unregister(NS_IPTUN);
+ iptun_dip = NULL;
+ iptun_ioc_fini();
+ return (DDI_SUCCESS);
+
+ default:
+ return (DDI_FAILURE);
+ }
+}
+
+/* ARGSUSED */
+static int
+iptun_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
+{
+ switch (infocmd) {
+ case DDI_INFO_DEVT2DEVINFO:
+ *result = iptun_dip;
+ return (DDI_SUCCESS);
+ case DDI_INFO_DEVT2INSTANCE:
+ *result = NULL;
+ return (DDI_SUCCESS);
+ }
+ return (DDI_FAILURE);
+}
+
+/* ARGSUSED */
+static int
+iptun_constructor(void *buf, void *cdrarg, int kmflags)
+{
+ iptun_t *iptun = buf;
+
+ bzero(iptun, sizeof (*iptun));
+ mutex_init(&iptun->iptun_lock, NULL, MUTEX_DEFAULT, NULL);
+ cv_init(&iptun->iptun_upcall_cv, NULL, CV_DRIVER, NULL);
+ cv_init(&iptun->iptun_enter_cv, NULL, CV_DRIVER, NULL);
+
+ return (0);
+}
+
+/* ARGSUSED */
+static void
+iptun_destructor(void *buf, void *cdrarg)
+{
+ iptun_t *iptun = buf;
+
+ /* This iptun_t must not still be in use. */
+ ASSERT(!(iptun->iptun_flags & (IPTUN_BOUND|IPTUN_MAC_REGISTERED|
+ IPTUN_MAC_STARTED|IPTUN_HASH_INSERTED|IPTUN_UPCALL_PENDING)));
+
+ mutex_destroy(&iptun->iptun_lock);
+ cv_destroy(&iptun->iptun_upcall_cv);
+}