#include <sys/hook.h>
#include <sys/hook_event.h>
#include "viona_impl.h"
static list_t viona_neti_list;
static kmutex_t viona_neti_lock;
static net_instance_t *viona_neti;
int
viona_hook(viona_link_t *link, viona_vring_t *ring, mblk_t **mpp, boolean_t out)
{
viona_neti_t *nip = link->l_neti;
viona_nethook_t *vnh = &nip->vni_nethook;
hook_pkt_event_t info;
hook_event_t he;
hook_event_token_t het;
int ret;
he = out ? vnh->vnh_event_out : vnh->vnh_event_in;
het = out ? vnh->vnh_token_out : vnh->vnh_token_in;
if (!he.he_interested)
return (0);
info.hpe_protocol = vnh->vnh_neti;
info.hpe_ifp = (phy_if_t)link;
info.hpe_ofp = (phy_if_t)link;
info.hpe_mp = mpp;
info.hpe_flags = 0;
ret = hook_run(vnh->vnh_neti->netd_hooks, het, (hook_data_t)&info);
if (ret == 0)
return (0);
if (out) {
VIONA_PROBE3(tx_hook_drop, viona_vring_t *, ring,
mblk_t *, *mpp, int, ret);
VIONA_RING_STAT_INCR(ring, tx_hookdrop);
} else {
VIONA_PROBE3(rx_hook_drop, viona_vring_t *, ring,
mblk_t *, *mpp, int, ret);
VIONA_RING_STAT_INCR(ring, rx_hookdrop);
}
return (ret);
}
static int
viona_neti_getifname(net_handle_t neti __unused, phy_if_t phy __unused,
char *buf __unused, const size_t len __unused)
{
return (-1);
}
static int
viona_neti_getmtu(net_handle_t neti __unused, phy_if_t phy __unused,
lif_if_t ifdata __unused)
{
return (-1);
}
static int
viona_neti_getptmue(net_handle_t neti __unused)
{
return (-1);
}
static int
viona_neti_getlifaddr(net_handle_t neti __unused, phy_if_t phy __unused,
lif_if_t ifdata __unused, size_t nelem __unused,
net_ifaddr_t type[] __unused, void *storage __unused)
{
return (-1);
}
static int
viona_neti_getlifzone(net_handle_t neti __unused, phy_if_t phy __unused,
lif_if_t ifdata __unused, zoneid_t *zid __unused)
{
return (-1);
}
static int
viona_neti_getlifflags(net_handle_t neti __unused, phy_if_t phy __unused,
lif_if_t ifdata __unused, uint64_t *flags __unused)
{
return (-1);
}
static phy_if_t
viona_neti_phygetnext(net_handle_t neti __unused, phy_if_t phy __unused)
{
return ((phy_if_t)-1);
}
static phy_if_t
viona_neti_phylookup(net_handle_t neti __unused, const char *name __unused)
{
return ((phy_if_t)-1);
}
static lif_if_t
viona_neti_lifgetnext(net_handle_t neti __unused, phy_if_t phy __unused,
lif_if_t ifdata __unused)
{
return (-1);
}
static int
viona_neti_inject(net_handle_t neti __unused, inject_t style __unused,
net_inject_t *packet __unused)
{
return (-1);
}
static phy_if_t
viona_neti_route(net_handle_t neti __unused, struct sockaddr *address __unused,
struct sockaddr *next __unused)
{
return ((phy_if_t)-1);
}
static int
viona_neti_ispchksum(net_handle_t neti __unused, mblk_t *mp __unused)
{
return (-1);
}
static int
viona_neti_isvchksum(net_handle_t neti __unused, mblk_t *mp __unused)
{
return (-1);
}
static net_protocol_t viona_netinfo = {
NETINFO_VERSION,
NHF_VIONA,
viona_neti_getifname,
viona_neti_getmtu,
viona_neti_getptmue,
viona_neti_getlifaddr,
viona_neti_getlifzone,
viona_neti_getlifflags,
viona_neti_phygetnext,
viona_neti_phylookup,
viona_neti_lifgetnext,
viona_neti_inject,
viona_neti_route,
viona_neti_ispchksum,
viona_neti_isvchksum
};
static int
viona_nethook_init(netid_t nid, viona_nethook_t *vnh, char *nh_name,
net_protocol_t *netip)
{
int ret;
if ((vnh->vnh_neti = net_protocol_register(nid, netip)) == NULL) {
cmn_err(CE_NOTE, "%s: net_protocol_register failed "
"(netid=%d name=%s)", __func__, nid, nh_name);
goto fail_init_proto;
}
HOOK_FAMILY_INIT(&vnh->vnh_family, nh_name);
if ((ret = net_family_register(vnh->vnh_neti, &vnh->vnh_family)) != 0) {
cmn_err(CE_NOTE, "%s: net_family_register failed "
"(netid=%d name=%s err=%d)", __func__,
nid, nh_name, ret);
goto fail_init_family;
}
HOOK_EVENT_INIT(&vnh->vnh_event_in, NH_PHYSICAL_IN);
if ((vnh->vnh_token_in = net_event_register(vnh->vnh_neti,
&vnh->vnh_event_in)) == NULL) {
cmn_err(CE_NOTE, "%s: net_event_register %s failed "
"(netid=%d name=%s)", __func__, NH_PHYSICAL_IN, nid,
nh_name);
goto fail_init_event_in;
}
HOOK_EVENT_INIT(&vnh->vnh_event_out, NH_PHYSICAL_OUT);
if ((vnh->vnh_token_out = net_event_register(vnh->vnh_neti,
&vnh->vnh_event_out)) == NULL) {
cmn_err(CE_NOTE, "%s: net_event_register %s failed "
"(netid=%d name=%s)", __func__, NH_PHYSICAL_OUT, nid,
nh_name);
goto fail_init_event_out;
}
return (0);
fail_init_event_out:
VERIFY0(net_event_shutdown(vnh->vnh_neti, &vnh->vnh_event_in));
VERIFY0(net_event_unregister(vnh->vnh_neti, &vnh->vnh_event_in));
vnh->vnh_token_in = NULL;
fail_init_event_in:
VERIFY0(net_family_shutdown(vnh->vnh_neti, &vnh->vnh_family));
VERIFY0(net_family_unregister(vnh->vnh_neti, &vnh->vnh_family));
fail_init_family:
VERIFY0(net_protocol_unregister(vnh->vnh_neti));
vnh->vnh_neti = NULL;
fail_init_proto:
return (1);
}
static void
viona_nethook_shutdown(viona_nethook_t *vnh)
{
VERIFY0(net_event_shutdown(vnh->vnh_neti, &vnh->vnh_event_out));
VERIFY0(net_event_shutdown(vnh->vnh_neti, &vnh->vnh_event_in));
VERIFY0(net_family_shutdown(vnh->vnh_neti, &vnh->vnh_family));
}
static void
viona_nethook_fini(viona_nethook_t *vnh)
{
VERIFY0(net_event_unregister(vnh->vnh_neti, &vnh->vnh_event_out));
VERIFY0(net_event_unregister(vnh->vnh_neti, &vnh->vnh_event_in));
VERIFY0(net_family_unregister(vnh->vnh_neti, &vnh->vnh_family));
VERIFY0(net_protocol_unregister(vnh->vnh_neti));
vnh->vnh_neti = NULL;
}
static void *
viona_neti_create(const netid_t netid)
{
viona_neti_t *nip;
VERIFY(netid != -1);
nip = kmem_zalloc(sizeof (*nip), KM_SLEEP);
nip->vni_netid = netid;
nip->vni_zid = net_getzoneidbynetid(netid);
mutex_init(&nip->vni_lock, NULL, MUTEX_DRIVER, NULL);
list_create(&nip->vni_dev_list, sizeof (viona_soft_state_t),
offsetof(viona_soft_state_t, ss_node));
if (viona_nethook_init(netid, &nip->vni_nethook, Hn_VIONA,
&viona_netinfo) == 0)
nip->vni_nethook.vnh_hooked = B_TRUE;
mutex_enter(&viona_neti_lock);
list_insert_tail(&viona_neti_list, nip);
mutex_exit(&viona_neti_lock);
return (nip);
}
static void
viona_neti_shutdown(netid_t nid, void *arg)
{
viona_neti_t *nip = arg;
ASSERT(nip != NULL);
VERIFY(nid == nip->vni_netid);
mutex_enter(&viona_neti_lock);
list_remove(&viona_neti_list, nip);
mutex_exit(&viona_neti_lock);
if (nip->vni_nethook.vnh_hooked)
viona_nethook_shutdown(&nip->vni_nethook);
}
static void
viona_neti_destroy(netid_t nid, void *arg)
{
viona_neti_t *nip = arg;
ASSERT(nip != NULL);
VERIFY(nid == nip->vni_netid);
mutex_enter(&nip->vni_lock);
while (nip->vni_ref != 0)
cv_wait(&nip->vni_ref_change, &nip->vni_lock);
mutex_exit(&nip->vni_lock);
VERIFY(!list_link_active(&nip->vni_node));
if (nip->vni_nethook.vnh_hooked)
viona_nethook_fini(&nip->vni_nethook);
mutex_destroy(&nip->vni_lock);
list_destroy(&nip->vni_dev_list);
kmem_free(nip, sizeof (*nip));
}
viona_neti_t *
viona_neti_lookup_by_zid(zoneid_t zid)
{
viona_neti_t *nip;
mutex_enter(&viona_neti_lock);
for (nip = list_head(&viona_neti_list); nip != NULL;
nip = list_next(&viona_neti_list, nip)) {
if (nip->vni_zid == zid) {
mutex_enter(&nip->vni_lock);
nip->vni_ref++;
mutex_exit(&nip->vni_lock);
mutex_exit(&viona_neti_lock);
return (nip);
}
}
mutex_exit(&viona_neti_lock);
return (NULL);
}
void
viona_neti_rele(viona_neti_t *nip)
{
mutex_enter(&nip->vni_lock);
VERIFY3S(nip->vni_ref, >, 0);
nip->vni_ref--;
mutex_exit(&nip->vni_lock);
cv_broadcast(&nip->vni_ref_change);
}
void
viona_neti_attach(void)
{
mutex_init(&viona_neti_lock, NULL, MUTEX_DRIVER, NULL);
list_create(&viona_neti_list, sizeof (viona_neti_t),
offsetof(viona_neti_t, vni_node));
viona_neti = net_instance_alloc(NETINFO_VERSION);
VERIFY(viona_neti != NULL);
viona_neti->nin_name = "viona";
viona_neti->nin_create = viona_neti_create;
viona_neti->nin_shutdown = viona_neti_shutdown;
viona_neti->nin_destroy = viona_neti_destroy;
VERIFY3S(net_instance_register(viona_neti), ==, DDI_SUCCESS);
}
void
viona_neti_detach(void)
{
VERIFY3S(net_instance_unregister(viona_neti), ==, DDI_SUCCESS);
net_instance_free(viona_neti);
viona_neti = NULL;
list_destroy(&viona_neti_list);
mutex_destroy(&viona_neti_lock);
}