/* * 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 #include #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_connp->conn_cred); (void) dls_mgmt_destroy(linkid, B_FALSE); } } /* * 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); }