vni.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (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 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include "vni_impl.h"
#include <sys/conf.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/dlpi.h>
#include <sys/stat.h>
#include <sys/ethernet.h>
#include <sys/strsun.h>
#include <sys/stropts.h>
static int vniopen(queue_t *, dev_t *, int, int, cred_t *);
static int vniclose(queue_t *, int, cred_t *);
static int vniwput(queue_t *, mblk_t *);
static int vniattach(dev_info_t *, ddi_attach_cmd_t);
static int vnidetach(dev_info_t *, ddi_detach_cmd_t);
static struct module_info minfo = {
VNIIDNUM, /* mi_idnum */
VNINAME, /* mi_idname */
VNIMINPSZ, /* mi_minpsz */
VNIMAXPSZ, /* mi_maxpsz */
VNIHIWAT, /* mi_hiwat */
VNILOWAT /* mi_lowat */
};
static struct qinit vnirinit = {
NULL, /* qi_putp */
NULL, /* qi_srvp */
vniopen, /* qi_qopen */
vniclose, /* qi_qclose */
NULL, /* qi_qadmin */
&minfo, /* qi_minfo */
NULL /* qi_mstat */
};
static struct qinit vniwinit = {
vniwput, /* qi_putp */
NULL, /* qi_srvp */
NULL, /* qi_qopen */
NULL, /* qi_qclose */
NULL, /* qi_qadmin */
&minfo, /* qi_minfo */
NULL /* qi_mstat */
};
static struct streamtab vni_info = {
&vnirinit, /* st_rdinit */
&vniwinit, /* st_wrinit */
NULL, /* st_muxrinit */
NULL /* st_muxwrinit */
};
DDI_DEFINE_STREAM_OPS(vni_ops, nulldev, nulldev, vniattach, \
vnidetach, nodev, nodev, VNIFLAGS, &vni_info);
static struct modldrv modldrv = {
&mod_driverops,
"Virtual network interface %I%",
&vni_ops,
};
static struct modlinkage modlinkage = {
MODREV_1, &modldrv, NULL
};
static dev_info_t *vni_dev_info;
static vni_str_t *vni_strlist_head;
/*
* DL_INFO_ACK template for VNI pseudo interface.
*/
static dl_info_ack_t dlvni_infoack = {
DL_INFO_ACK, /* dl_primitive */
0, /* dl_max_sdu */
0, /* dl_min_sdu */
0, /* dl_addr_length */
SUNW_DL_VNI, /* dl_mac_type */
0, /* dl_reserved */
0, /* dl_current_state */
0, /* dl_sap_length */
DL_CLDLS, /* dl_service_mode */
0, /* dl_qos_length */
0, /* dl_qos_offset */
0, /* dl_range_length */
0, /* dl_range_offset */
DL_STYLE2, /* dl_provider_style */
0, /* dl_addr_offset */
DL_VERSION_2, /* dl_version */
0, /* dl_brdcst_addr_length */
0, /* dl_brdcst_addr_offset */
0 /* dl_growth */
};
int
_init(void)
{
return (mod_install(&modlinkage));
}
int
_fini(void)
{
return (mod_remove(&modlinkage));
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&modlinkage, modinfop));
}
static int
vniattach(dev_info_t *devi, ddi_attach_cmd_t cmd)
{
if (cmd != DDI_ATTACH) {
cmn_err(CE_NOTE, "vniattach failure: cmd != DDI_ATTACH\n");
return (DDI_FAILURE);
}
if (ddi_create_minor_node(devi, VNINAME, S_IFCHR,
ddi_get_instance(devi), DDI_PSEUDO, CLONE_DEV) ==
DDI_FAILURE) {
ddi_remove_minor_node(devi, NULL);
cmn_err(CE_NOTE, "vniattach failure: ddi_create_minor_node\n");
return (DDI_FAILURE);
}
vni_dev_info = devi;
return (DDI_SUCCESS);
}
static int
vnidetach(dev_info_t *devi, ddi_detach_cmd_t cmd)
{
if (cmd != DDI_DETACH)
return (DDI_FAILURE);
ddi_remove_minor_node(devi, NULL);
return (DDI_SUCCESS);
}
/* ARGSUSED */
static int
vniopen(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *credp)
{
vni_str_t *stp, *prevstp;
minor_t minordev = 0;
if (sflag != CLONEOPEN)
return (EINVAL);
prevstp = NULL;
for (stp = vni_strlist_head; stp != NULL; stp = stp->st_next) {
if (minordev < stp->st_minor)
break;
minordev++;
prevstp = stp;
}
stp = kmem_zalloc(sizeof (vni_str_t), KM_SLEEP);
*devp = makedevice(getmajor(*devp), minordev);
stp->st_minor = minordev;
stp->st_state = DL_UNATTACHED;
stp->st_next = NULL;
q->q_ptr = stp;
WR(q)->q_ptr = stp;
if (prevstp != NULL) {
stp->st_next = prevstp->st_next;
prevstp->st_next = stp;
} else {
stp->st_next = vni_strlist_head;
vni_strlist_head = stp;
}
qprocson(q);
return (0);
}
/* ARGSUSED */
static int
vniclose(queue_t *q, int flag, cred_t *credp)
{
vni_str_t *stp, **prevstpp;
qprocsoff(q);
stp = (vni_str_t *)q->q_ptr;
stp->st_state = DL_UNATTACHED;
/* Unlink the per-stream entry from the list and free it */
stp = vni_strlist_head;
prevstpp = &vni_strlist_head;
for (; stp != NULL; stp = stp->st_next) {
if (stp == (vni_str_t *)q->q_ptr)
break;
prevstpp = &stp->st_next;
}
ASSERT(stp != NULL);
*prevstpp = stp->st_next;
kmem_free(stp, sizeof (vni_str_t));
q->q_ptr = WR(q)->q_ptr = NULL;
return (0);
}
static int
vniwput(queue_t *q, mblk_t *mp)
{
union DL_primitives *dlp;
vni_str_t *stp;
dl_info_ack_t *dlip;
t_scalar_t prim;
stp = q->q_ptr;
switch ((mp)->b_datap->db_type) {
case M_PROTO:
case M_PCPROTO:
if (MBLKL(mp) < sizeof (t_scalar_t)) {
dlerrorack(q, mp, DL_PRIM_INVAL, DL_UNSUPPORTED, 0);
return (0);
}
dlp = (union DL_primitives *)mp->b_rptr;
prim = dlp->dl_primitive;
switch (prim) {
case DL_ATTACH_REQ:
if (MBLKL(mp) < DL_ATTACH_REQ_SIZE) {
dlerrorack(q, mp, DL_ATTACH_REQ, DL_BADPRIM, 0);
return (0);
}
if (stp->st_state != DL_UNATTACHED) {
dlerrorack(q, mp, DL_ATTACH_REQ, DL_OUTSTATE,
0);
return (0);
}
stp->st_ppa = dlp->attach_req.dl_ppa;
stp->st_state = DL_UNBOUND;
dlokack(q, mp, DL_ATTACH_REQ);
break;
case DL_BIND_REQ:
if (MBLKL(mp) < DL_BIND_REQ_SIZE) {
dlerrorack(q, mp, DL_BIND_REQ, DL_BADPRIM, 0);
return (0);
}
if (stp->st_state != DL_UNBOUND) {
dlerrorack(q, mp, DL_BIND_REQ, DL_OUTSTATE, 0);
return (0);
}
stp->st_state = DL_IDLE;
dlbindack(q, mp, dlp->bind_req.dl_sap, NULL, 0, 0, 0);
break;
case DL_INFO_REQ:
if (MBLKL(mp) < DL_INFO_REQ_SIZE) {
dlerrorack(q, mp, DL_INFO_REQ, DL_BADPRIM, 0);
return (0);
}
if ((mp = mexchange(q, mp, sizeof (dl_info_ack_t),
M_PCPROTO, DL_INFO_ACK)) == NULL) {
return (0);
}
dlip = (dl_info_ack_t *)mp->b_rptr;
*dlip = dlvni_infoack;
dlip->dl_current_state = stp->st_state;
qreply(q, mp);
break;
case DL_PHYS_ADDR_REQ:
if (MBLKL(mp) < DL_PHYS_ADDR_REQ_SIZE) {
dlerrorack(q, mp, DL_PHYS_ADDR_REQ, DL_BADPRIM,
0);
return (0);
}
dlphysaddrack(q, mp, NULL, 0);
break;
case DL_UNBIND_REQ:
if (MBLKL(mp) < DL_UNBIND_REQ_SIZE) {
dlerrorack(q, mp, DL_UNBIND_REQ, DL_BADPRIM, 0);
return (0);
}
if (stp->st_state != DL_IDLE) {
dlerrorack(q, mp, DL_UNBIND_REQ, DL_OUTSTATE,
0);
return (0);
}
/* Nothing to flush. But DLPI spec says to; so do it */
flushq(q, FLUSHALL);
flushq(RD(q), FLUSHALL);
stp->st_state = DL_UNBOUND;
dlokack(q, mp, DL_UNBIND_REQ);
break;
case DL_DETACH_REQ:
if (MBLKL(mp) < DL_DETACH_REQ_SIZE) {
dlerrorack(q, mp, DL_DETACH_REQ, DL_BADPRIM, 0);
return (0);
}
if (stp->st_state != DL_UNBOUND) {
dlerrorack(q, mp, DL_DETACH_REQ, DL_OUTSTATE,
0);
return (0);
}
stp->st_state = DL_UNATTACHED;
dlokack(q, mp, DL_DETACH_REQ);
break;
default:
dlerrorack(q, mp, prim, DL_UNSUPPORTED, 0);
}
break;
case M_IOCTL:
/*
* No ioctl's currently supported. Need to have the NAK since
* ifconfig calls SIOCGTUNPARAM during the end of plumb
*/
miocnak(q, mp, 0, EINVAL);
break;
case M_FLUSH:
/* Really nothing to flush since no msgs enqueued */
if (*mp->b_rptr & FLUSHR) {
qreply(q, mp);
} else {
freemsg(mp);
}
break;
default:
freemsg(mp);
break;
}
return (0);
}