summaryrefslogtreecommitdiff
path: root/usr/src/uts/common/io/neti_impl.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/uts/common/io/neti_impl.c')
-rw-r--r--usr/src/uts/common/io/neti_impl.c621
1 files changed, 621 insertions, 0 deletions
diff --git a/usr/src/uts/common/io/neti_impl.c b/usr/src/uts/common/io/neti_impl.c
new file mode 100644
index 0000000000..346e49903d
--- /dev/null
+++ b/usr/src/uts/common/io/neti_impl.c
@@ -0,0 +1,621 @@
+/*
+ * 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 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <sys/param.h>
+#include <sys/atomic.h>
+#include <sys/kmem.h>
+#include <sys/rwlock.h>
+#include <sys/errno.h>
+#include <sys/queue.h>
+#include <inet/common.h>
+#include <inet/led.h>
+#include <inet/ip.h>
+#include <sys/neti.h>
+#include <sys/zone.h>
+
+static net_handle_t net_find(const char *protocol, neti_stack_t *ns);
+
+static net_handle_t
+net_find(const char *protocol, neti_stack_t *nts)
+{
+ struct net_data *n;
+
+ ASSERT(protocol != NULL);
+ ASSERT(nts != NULL);
+
+ LIST_FOREACH(n, &nts->nts_netd_head, netd_list) {
+ ASSERT(n->netd_info.netp_name != NULL);
+ /*
+ * If they're trying to find a protocol that is being
+ * shutdown, just ignore it..
+ */
+ if (n->netd_condemned != 0)
+ continue;
+ if (strcmp(n->netd_info.netp_name, protocol) == 0) {
+ break;
+ }
+ }
+
+ return (n);
+}
+
+net_handle_t
+net_protocol_register(netid_t id, const net_protocol_t *info)
+{
+ struct net_data *n, *new;
+ neti_stack_t *nts;
+
+ ASSERT(info != NULL);
+
+ nts = net_getnetistackbyid(id);
+ if (nts == NULL)
+ return (NULL);
+
+ new = kmem_alloc(sizeof (*new), KM_SLEEP);
+ new->netd_refcnt = 1;
+ new->netd_hooks = NULL;
+ new->netd_info = *info;
+ new->netd_stack = nts;
+ new->netd_condemned = 0;
+
+ mutex_enter(&nts->nts_lock);
+ n = net_find(info->netp_name, nts);
+ if (n != NULL) {
+ mutex_exit(&nts->nts_lock);
+ kmem_free(new, sizeof (*new));
+ return (NULL);
+ }
+
+ if (LIST_EMPTY(&nts->nts_netd_head)) {
+ LIST_INSERT_HEAD(&nts->nts_netd_head, new, netd_list);
+ } else {
+ LIST_INSERT_AFTER(LIST_FIRST(&nts->nts_netd_head),
+ new, netd_list);
+ }
+ mutex_exit(&nts->nts_lock);
+
+ return (new);
+}
+
+int
+net_protocol_unregister(net_handle_t info)
+{
+ neti_stack_t *nts;
+
+ ASSERT(info != NULL);
+
+ nts = info->netd_stack;
+ ASSERT(nts != NULL);
+
+ mutex_enter(&nts->nts_lock);
+ LIST_REMOVE(info, netd_list);
+ info->netd_stack = NULL;
+ mutex_exit(&nts->nts_lock);
+
+ (void) net_protocol_release(info);
+
+ return (0);
+}
+
+net_handle_t
+net_protocol_lookup(netid_t netid, const char *protocol)
+{
+ neti_stack_t *nts;
+ net_handle_t nd;
+
+ ASSERT(protocol != NULL);
+
+ nts = net_getnetistackbyid(netid);
+ if (nts == NULL)
+ return (NULL);
+
+ mutex_enter(&nts->nts_lock);
+ nd = net_find(protocol, nts);
+ if (nd != NULL)
+ atomic_add_32((uint_t *)&nd->netd_refcnt, 1);
+ mutex_exit(&nts->nts_lock);
+ return (nd);
+}
+
+/*
+ * Note: the man page specifies "returns -1 if the value passed in is unknown
+ * to this framework". We are not doing a lookup in this function, just a
+ * simply add to the netd_refcnt of the net_handle_t passed in, so -1 is never a
+ * return value.
+ */
+int
+net_protocol_release(net_handle_t info)
+{
+
+ ASSERT(info->netd_refcnt > 0);
+ /*
+ * Is this safe? No hold on nts_lock? Consider that if the caller
+ * of net_protocol_release() is going to free this structure then
+ * it is now the only owner (refcnt==1) and it will have been
+ * removed from the nts_netd_head list on the neti_stack_t from a
+ * call to net_protocol_unregister already, so it is thus an orphan.
+ */
+ if (atomic_add_32_nv((uint_t *)&info->netd_refcnt, -1) == 0) {
+ ASSERT(info->netd_hooks == NULL);
+ ASSERT(info->netd_stack == NULL);
+ kmem_free(info, sizeof (struct net_data));
+ }
+
+ return (0);
+}
+
+net_handle_t
+net_protocol_walk(netid_t netid, net_handle_t info)
+{
+ struct net_data *n = NULL;
+ boolean_t found = B_FALSE;
+ neti_stack_t *nts;
+
+ nts = net_getnetistackbyid(netid);
+ ASSERT(nts != NULL);
+
+ if (info == NULL)
+ found = B_TRUE;
+
+ mutex_enter(&nts->nts_lock);
+ LIST_FOREACH(n, &nts->nts_netd_head, netd_list) {
+ if (found) {
+ /*
+ * We are only interested in finding protocols that
+ * are not in some sort of shutdown state. There is
+ * no need to check for netd_stack==NULL because
+ * that implies it is no longer on this list.
+ */
+ if (n->netd_condemned == 0)
+ continue;
+ break;
+ }
+
+ if (n == info)
+ found = B_TRUE;
+ }
+
+ if (info != NULL)
+ (void) net_protocol_release(info);
+
+ if (n != NULL)
+ atomic_add_32((uint_t *)&n->netd_refcnt, 1);
+
+ mutex_exit(&nts->nts_lock);
+
+ return (n);
+}
+
+/*
+ * Public accessor functions
+ */
+int
+net_getifname(net_handle_t info, phy_if_t nic, char *buffer,
+ const size_t buflen)
+{
+
+ ASSERT(info != NULL);
+
+ if (info->netd_condemned != 0 || info->netd_stack == NULL)
+ return (-1);
+
+ return (info->netd_info.netp_getifname(info, nic, buffer, buflen));
+}
+
+int
+net_getmtu(net_handle_t info, phy_if_t nic, lif_if_t ifdata)
+{
+
+ ASSERT(info != NULL);
+
+ if (info->netd_condemned != 0 || info->netd_stack == NULL)
+ return (-1);
+
+ return (info->netd_info.netp_getmtu(info, nic, ifdata));
+}
+
+int
+net_getpmtuenabled(net_handle_t info)
+{
+
+ ASSERT(info != NULL);
+
+ if (info->netd_condemned != 0 || info->netd_stack == NULL)
+ return (-1);
+
+ return (info->netd_info.netp_getpmtuenabled(info));
+}
+
+int
+net_getlifaddr(net_handle_t info, phy_if_t nic, lif_if_t ifdata,
+ int nelem, net_ifaddr_t type[], void *storage)
+{
+
+ ASSERT(info != NULL);
+
+ if (info->netd_condemned != 0 || info->netd_stack == NULL)
+ return (-1);
+
+ return (info->netd_info.netp_getlifaddr(info, nic, ifdata,
+ nelem, type, storage));
+}
+
+phy_if_t
+net_phygetnext(net_handle_t info, phy_if_t nic)
+{
+
+ ASSERT(info != NULL);
+
+ if (info->netd_condemned != 0 || info->netd_stack == NULL)
+ return ((phy_if_t)-1);
+
+ return (info->netd_info.netp_phygetnext(info, nic));
+}
+
+phy_if_t
+net_phylookup(net_handle_t info, const char *name)
+{
+
+ ASSERT(info != NULL);
+
+ if (info->netd_condemned != 0 || info->netd_stack == NULL)
+ return ((phy_if_t)-1);
+
+ return (info->netd_info.netp_phylookup(info, name));
+}
+
+lif_if_t
+net_lifgetnext(net_handle_t info, phy_if_t ifidx, lif_if_t ifdata)
+{
+
+ ASSERT(info != NULL);
+
+ if (info->netd_condemned != 0 || info->netd_stack == NULL)
+ return ((lif_if_t)-1);
+
+ return (info->netd_info.netp_lifgetnext(info, ifidx, ifdata));
+}
+
+int
+net_inject(net_handle_t info, inject_t style, net_inject_t *packet)
+{
+
+ ASSERT(info != NULL);
+
+ if (info->netd_condemned != 0 || info->netd_stack == NULL)
+ return (-1);
+
+ return (info->netd_info.netp_inject(info, style, packet));
+}
+
+phy_if_t
+net_routeto(net_handle_t info, struct sockaddr *address, struct sockaddr *next)
+{
+
+ ASSERT(info != NULL);
+
+ if (info->netd_condemned != 0 || info->netd_stack == NULL)
+ return ((phy_if_t)-1);
+
+ return (info->netd_info.netp_routeto(info, address, next));
+}
+
+int
+net_ispartialchecksum(net_handle_t info, mblk_t *mp)
+{
+
+ ASSERT(info != NULL);
+ ASSERT(mp != NULL);
+
+ if (info->netd_condemned != 0 || info->netd_stack == NULL)
+ return (-1);
+
+ return (info->netd_info.netp_ispartialchecksum(info, mp));
+}
+
+int
+net_isvalidchecksum(net_handle_t info, mblk_t *mp)
+{
+
+ ASSERT(info != NULL);
+ ASSERT(mp != NULL);
+
+ if (info->netd_condemned != 0 || info->netd_stack == NULL)
+ return (-1);
+
+ return (info->netd_info.netp_isvalidchecksum(info, mp));
+}
+
+/*
+ * Hooks related functions
+ */
+
+/*
+ * Function: net_family_register
+ * Returns: int - 0 = Succ, Else = Fail
+ * Parameters: info(I) - protocol
+ * hf(I) - family pointer
+ *
+ * Call hook_family_add to register family
+ *
+ * There is no need to bump netd_refcnt in the two functions
+ * net_family_register and net_family_unregister because the caller of these
+ * two functions is assumed to "own" a reference on 'info' via an earlier
+ * call to net_protocol_register(). Thus the owner is expected to do a
+ * call to net_protocol_unregister() after having done a
+ * net_family_unregister() to make sure things are properly cleaned up.
+ */
+int
+net_family_register(net_handle_t info, hook_family_t *hf)
+{
+ hook_family_int_t *hfi;
+ netstack_t *ns;
+
+ ASSERT(info != NULL);
+ ASSERT(hf != NULL);
+
+ if (info->netd_condemned != 0 || info->netd_stack == NULL)
+ return (ESHUTDOWN);
+
+ if (info->netd_hooks != NULL)
+ return (EEXIST);
+
+ ns = info->netd_stack->nts_netstack;
+ ASSERT(ns != NULL);
+ hfi = hook_family_add(hf, ns->netstack_hook);
+ if (hfi == NULL)
+ return (EEXIST);
+
+ info->netd_hooks = hfi;
+ return (0);
+}
+
+/*
+ * Function: net_family_unregister
+ * Returns: int - transparent value, explained by caller
+ * Parameters: info(I) - protocol
+ * hf(I) - family pointer
+ *
+ * Call hook_family_remove to unregister family
+ */
+int
+net_family_unregister(net_handle_t info, hook_family_t *hf)
+{
+ int ret;
+
+ ASSERT(info != NULL);
+ ASSERT(hf != NULL);
+
+ if (info->netd_hooks == NULL)
+ return (ENXIO);
+
+ if (strcmp(info->netd_hooks->hfi_family.hf_name,
+ hf->hf_name) != 0)
+ return (EINVAL);
+
+ ret = hook_family_remove(info->netd_hooks);
+ if (ret == 0)
+ info->netd_hooks = NULL;
+
+ return (ret);
+}
+
+/*
+ * Function: net_event_register
+ * Returns: internal event pointer - NULL = Fail
+ * Parameters: info(I) - protocol
+ * he(I) - event pointer
+ *
+ * Call hook_event_add to register event on specific family
+ * Internal event pointer is returned so caller can get
+ * handle to run hooks
+ */
+hook_event_token_t
+net_event_register(net_handle_t info, hook_event_t *he)
+{
+ hook_event_int_t *hei;
+
+ ASSERT(info != NULL);
+ ASSERT(he != NULL);
+
+ if (info->netd_hooks == NULL || info->netd_condemned != 0 ||
+ info->netd_stack == NULL)
+ return (NULL);
+
+ hei = hook_event_add(info->netd_hooks, he);
+ return ((hook_event_token_t)hei);
+}
+
+/*
+ * Function: net_event_unregister
+ * Returns: int - transparent value, explained by caller
+ * Parameters: info(I) - protocol
+ * he(I) - event pointer
+ *
+ * Call hook_event_remove to unregister event on specific family
+ */
+int
+net_event_unregister(net_handle_t info, hook_event_t *he)
+{
+
+ ASSERT(info != NULL);
+ ASSERT(he != NULL);
+
+ if (info->netd_hooks == NULL)
+ return (ENXIO);
+
+ return (hook_event_remove(info->netd_hooks, he));
+}
+
+/*
+ * Function: net_hook_register
+ * Returns: int - transparent value, explained by caller
+ * Parameters: info(I) - protocol
+ * event(I) - event name
+ * h(I) - hook pointer
+ *
+ * Call hook_register to add hook on specific family/event
+ */
+int
+net_hook_register(net_handle_t info, char *event, hook_t *h)
+{
+
+ ASSERT(info != NULL);
+ ASSERT(event != NULL);
+ ASSERT(h != NULL);
+
+ if (info->netd_condemned != 0 || info->netd_stack == NULL)
+ return (ESHUTDOWN);
+
+ if (info->netd_hooks == NULL)
+ return (ENXIO);
+
+ return (hook_register(info->netd_hooks, event, h));
+}
+
+/*
+ * Function: net_hook_unregister
+ * Returns: int - transparent value, explained by caller
+ * Parameters: info(I) - protocol
+ * event(I) - event name
+ * h(I) - hook pointer
+ *
+ * Call hook_unregister to remove hook on specific family/event
+ */
+int
+net_hook_unregister(net_handle_t info, char *event, hook_t *h)
+{
+
+ ASSERT(info != NULL);
+ ASSERT(event != NULL);
+ ASSERT(h != NULL);
+
+ if (info->netd_hooks == NULL)
+ return (ENXIO);
+
+ return (hook_unregister(info->netd_hooks, event, h));
+}
+
+netid_t
+net_getnetid(net_handle_t netd)
+{
+
+ if (netd->netd_stack == NULL)
+ return (-1);
+ return (netd->netd_stack->nts_id);
+}
+
+net_inject_t *
+net_inject_alloc(const int version)
+{
+ net_inject_t *ni;
+
+ ni = kmem_zalloc(sizeof (*ni), KM_NOSLEEP);
+ if (ni == NULL)
+ return (NULL);
+
+ ni->ni_version = version;
+ return (ni);
+}
+
+void
+net_inject_free(net_inject_t *ni)
+{
+ kmem_free(ni, sizeof (*ni));
+}
+
+kstat_t *
+net_kstat_create(netid_t netid, char *module, int instance, char *name,
+ char *class, uchar_t type, ulong_t ndata, uchar_t ks_flag)
+{
+ netstackid_t stackid = net_getnetstackidbynetid(netid);
+
+ if (stackid == -1)
+ return (NULL);
+
+ return (kstat_create_netstack(module, instance, name, class, type,
+ ndata, ks_flag, stackid));
+}
+
+void
+net_kstat_delete(netid_t netid, kstat_t *ks)
+{
+ netstackid_t stackid = net_getnetstackidbynetid(netid);
+
+ if (stackid != -1)
+ kstat_delete_netstack(ks, stackid);
+}
+
+int
+net_event_notify_register(net_handle_t family, char *event,
+ hook_notify_fn_t callback, void *arg)
+{
+ int error;
+
+ if (family->netd_condemned != 0 || family->netd_stack == NULL)
+ return (ESHUTDOWN);
+
+ error = hook_event_notify_register(family->netd_hooks, event,
+ callback, arg);
+
+ return (error);
+}
+
+int
+net_event_notify_unregister(net_handle_t family, char *event,
+ hook_notify_fn_t callback)
+{
+ int error;
+
+ error = hook_event_notify_unregister(family->netd_hooks, event,
+ callback);
+
+ return (error);
+}
+
+int
+net_protocol_notify_register(net_handle_t family, hook_notify_fn_t callback,
+ void *arg)
+{
+ int error;
+
+ if (family->netd_condemned != 0 || family->netd_stack == NULL)
+ return (ESHUTDOWN);
+
+ error = hook_family_notify_register(family->netd_hooks, callback,
+ arg);
+
+ return (error);
+}
+
+int
+net_protocol_notify_unregister(net_handle_t family, hook_notify_fn_t callback)
+{
+ int error;
+
+ error = hook_family_notify_unregister(family->netd_hooks, callback);
+
+ return (error);
+}