root/usr/src/uts/common/inet/inetddi.c
/*
 * 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 (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
 */

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/stream.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/priv_names.h>

/*
 * This file contains generic goo needed to hook the STREAMS modules and
 * drivers that live under uts/common/inet into the DDI.  In order to use it,
 * each module/driver should #define the symbols below (as appropriate) and
 * then #include this source file; see the other uts/common/inet/<star>ddi.c
 * files for examples of this in action.
 *
 * The symbols that all modules and drivers must define are:
 *
 *      INET_NAME        The name of the module/driver.
 *
 * The symbols that all modules must define are:
 *
 *      INET_MODSTRTAB   The name of the `streamtab' structure for this module.
 *      INET_MODDESC     The one-line description for this module.
 *      INET_MODMTFLAGS  The mt-streams(9F) flags for the module.
 *
 * The symbols that all drivers must define are:
 *
 *      INET_DEVSTRTAB   The name of the `streamtab' structure for this driver.
 *      INET_DEVDESC     The one-line description for this driver.
 *      INET_DEVMTFLAGS  The mt-streams(9F) flags for the driver.
 *      INET_DEVMINOR    The minor number of the driver (usually 0).
 *
 * Drivers that need to masquerade as IP should set INET_DEVMTFLAGS to
 * IP_DEVMTFLAGS and set INET_DEVSTRTAB to ipinfo.
 *
 * The symbols that all socket modules must define are:
 *
 *      INET_SOCKDESC   The one-line description for this socket module
 *      INET_SOCK_PROTO_CREATE_FUNC The function used to create PCBs
 *
 * In addition, socket modules that can be converted to TPI must define:
 *
 *      INET_SOCK_PROTO_FB_FUNC The function used to fallback to TPI
 */

#if     !defined(INET_NAME)
#error inetddi.c: INET_NAME is not defined!
#elif   !defined(INET_DEVDESC) && !defined(INET_MODDESC) && \
        !defined(INET_SOCKDESC)
#error inetddi.c: at least one of INET_DEVDESC or INET_MODDESC or \
INET_SOCKDESC must be defined!
#elif   defined(INET_DEVDESC) && !defined(INET_DEVSTRTAB)
#error inetddi.c: INET_DEVDESC is defined but INET_DEVSTRTAB is not!
#elif   defined(INET_DEVDESC) && !defined(INET_DEVMTFLAGS)
#error inetddi.c: INET_DEVDESC is defined but INET_DEVMTFLAGS is not!
#elif   defined(INET_DEVDESC) && !defined(INET_DEVMINOR)
#error inetddi.c: INET_DEVDESC is defined but INET_DEVMINOR is not!
#elif   defined(INET_MODDESC) && !defined(INET_MODSTRTAB)
#error inetddi.c: INET_MODDESC is defined but INET_MODSTRTAB is not!
#elif   defined(INET_MODDESC) && !defined(INET_MODMTFLAGS)
#error inetddi.c: INET_MODDESC is defined but INET_MODMTFLAGS is not!
#elif   defined(INET_SOCKDESC) && !defined(SOCKMOD_VERSION)
#error  inetddi.c: INET_SOCKDESC is defined but SOCKMOD_VERSION is not!
#elif   defined(INET_SOCKDESC) && !defined(INET_SOCK_PROTO_CREATE_FUNC)
#error  inetddi.c: INET_SOCKDESC is defined but INET_SOCK_PROTO_CREATE_FUNC \
is not!
#elif   defined(INET_SOCK_PROTO_FB_FUNC) && !defined(INET_SOCK_FALLBACK_DEV_V4)
#error  inetddi.c: INET_SOCK_PROTO_FB_FUNC is defined but \
INET_SOCK_FALLBACK_DEV_V4 is not!
#elif   defined(INET_SOCK_PROTO_FB_FUNC) && !defined(INET_SOCK_FALLBACK_DEV_V6)
#error  inetddi.c: INET_SOCK_PROTO_FB_FUNC is defined but \
INET_SOCK_FALLBACK_DEV_V6 is not!
#endif

#ifdef  INET_DEVDESC

extern struct streamtab INET_DEVSTRTAB;

/*
 * Drivers that actually want to be IP would set INET_DEVSTRTAB to ipinfo.
 */

static dev_info_t *inet_dev_info;

#define INET_DEFAULT_PRIV_MODE  0666

static struct dev_priv {
        char *driver;
        int privonly;
        const char *read_priv;
        const char *write_priv;
} netdev_privs[] = {
        {"icmp", PRIVONLY_DEV,  PRIV_NET_ICMPACCESS,    PRIV_NET_ICMPACCESS},
        {"icmp6", PRIVONLY_DEV, PRIV_NET_ICMPACCESS,    PRIV_NET_ICMPACCESS},
        {"ip", PRIVONLY_DEV,    PRIV_NET_RAWACCESS,     PRIV_NET_RAWACCESS},
        {"ip6", PRIVONLY_DEV,   PRIV_NET_RAWACCESS,     PRIV_NET_RAWACCESS},
        {"keysock", PRIVONLY_DEV, PRIV_SYS_IP_CONFIG,   PRIV_SYS_IP_CONFIG},
        {"ipsecah", PRIVONLY_DEV, PRIV_SYS_IP_CONFIG,   PRIV_SYS_IP_CONFIG},
        {"ipsecesp", PRIVONLY_DEV, PRIV_SYS_IP_CONFIG,  PRIV_SYS_IP_CONFIG},
        {"spdsock", PRIVONLY_DEV, PRIV_SYS_IP_CONFIG,   PRIV_SYS_IP_CONFIG},
        {NULL,  0,              NULL,                   NULL}
};

static int
inet_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
{
        size_t i, ndevs;

        if (cmd != DDI_ATTACH)
                return (DDI_FAILURE);

        inet_dev_info = devi;

        ndevs = sizeof (netdev_privs) / sizeof (struct dev_priv);
        for (i = 0; i < ndevs; i++) {
                char *drv = netdev_privs[i].driver;
                if (drv == NULL || strcmp(drv, ddi_driver_name(devi)) == 0)
                        break;
        }

        /* smatch has no idea what VERIFY does. */
        if (i == ndevs) {
                VERIFY(i < ndevs);
                return (DDI_FAILURE);
        }

        return (ddi_create_priv_minor_node(devi, INET_NAME, S_IFCHR,
            INET_DEVMINOR, DDI_PSEUDO, netdev_privs[i].privonly,
            netdev_privs[i].read_priv, netdev_privs[i].write_priv,
            INET_DEFAULT_PRIV_MODE));
}

static int
inet_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
{
        if (cmd != DDI_DETACH)
                return (DDI_FAILURE);

        ASSERT(devi == inet_dev_info);

        ddi_remove_minor_node(devi, NULL);
        return (DDI_SUCCESS);
}


/* ARGSUSED */
static int
inet_info(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
{
        int error = DDI_FAILURE;

        switch (cmd) {
        case DDI_INFO_DEVT2DEVINFO:
                if (inet_dev_info != NULL) {
                        *result = (void *)inet_dev_info;
                        error = DDI_SUCCESS;
                }
                break;

        case DDI_INFO_DEVT2INSTANCE:
                *result = NULL;
                error = DDI_SUCCESS;
                break;

        default:
                break;
        }

        return (error);
}

DDI_DEFINE_STREAM_OPS(inet_devops, nulldev, nulldev, inet_attach, inet_detach,
    nulldev, inet_info, INET_DEVMTFLAGS, &INET_DEVSTRTAB,
    ddi_quiesce_not_supported);

static struct modldrv modldrv = {
        &mod_driverops,
        INET_DEVDESC,
        &inet_devops
};

#endif /* INET_DEVDESC */

#ifdef  INET_MODDESC
extern struct streamtab INET_MODSTRTAB;

static struct fmodsw fsw = {
        INET_NAME,
        &INET_MODSTRTAB,
        INET_MODMTFLAGS
};

static struct modlstrmod modlstrmod = {
        &mod_strmodops,
        INET_MODDESC,
        &fsw
};

#endif /* INET_MODDESC */

#ifdef  INET_SOCKDESC

#ifdef  INET_SOCK_PROTO_FB_FUNC
static __smod_priv_t smodpriv = {
        NULL,
        NULL,
        INET_SOCK_PROTO_FB_FUNC,
        INET_SOCK_FALLBACK_DEV_V4,
        INET_SOCK_FALLBACK_DEV_V6
};
#endif  /* INET_SOCK_PROTO_FB_FUNC */

static struct smod_reg_s smodreg = {
        SOCKMOD_VERSION,
        INET_NAME,
        SOCK_UC_VERSION,
        SOCK_DC_VERSION,
        INET_SOCK_PROTO_CREATE_FUNC,
#ifdef  INET_SOCK_PROTO_FB_FUNC
        &smodpriv
#else
        NULL
#endif  /* INET_SOCK_PROTO_FB_FUNC */
};

static struct modlsockmod modlsockmod = {
        &mod_sockmodops,
        INET_SOCKDESC,
        &smodreg
};
#endif  /* INET_SOCKDESC */

static struct modlinkage modlinkage = {
        MODREV_1,
#ifdef  INET_DEVDESC
        &modldrv,
#endif
#ifdef  INET_MODDESC
        &modlstrmod,
#endif
#ifdef  INET_SOCKDESC
        &modlsockmod,
#endif
        NULL
};