#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
};
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);
}
static void
iptun_stack_shutdown(netstackid_t stackid, void *arg)
{
iptun_stack_t *iptuns = arg;
iptun_t *iptun;
datalink_id_t linkid;
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);
}
}
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);
}
}
static int
iptun_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
{
switch (cmd) {
case DDI_DETACH:
if (iptun_count() > 0)
return (DDI_FAILURE);
netstack_unregister(NS_IPTUN);
iptun_dip = NULL;
iptun_ioc_fini();
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
}
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);
}
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);
}
static void
iptun_destructor(void *buf, void *cdrarg)
{
iptun_t *iptun = buf;
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);
}