diff options
Diffstat (limited to 'usr/src/uts/common/inet/iptun/iptun_dev.c')
-rw-r--r-- | usr/src/uts/common/inet/iptun/iptun_dev.c | 261 |
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); +} |