/* * 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 #include #include #include #include #include #include #include #include #include #include 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)); } int net_getlifzone(net_handle_t info, phy_if_t phy_ifdata, lif_if_t ifdata, zoneid_t *zoneid) { ASSERT(info != NULL); if (info->netd_condemned != 0 || info->netd_stack == NULL) return (-1); return (info->netd_info.neti_getlifzone(info, phy_ifdata, ifdata, zoneid)); } int net_getlifflags(net_handle_t info, phy_if_t phy_ifdata, lif_if_t ifdata, uint64_t *flags) { ASSERT(info != NULL); if (info->netd_condemned != 0 || info->netd_stack == NULL) return (-1); return (info->netd_info.neti_getlifflags(info, phy_ifdata, ifdata, flags)); } 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. * Passing a pointer to info->netd_hooks into hook_family_add is required * so that this can be set before the notify functions are called. If this * does not happen, the notify function may do something that seems fine, * like add a notify function to the family but cause a panic because * netd_hooks is NULL when we get to hook_family_notify_register. */ int net_family_register(net_handle_t info, hook_family_t *hf) { 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); if (hook_family_add(hf, ns->netstack_hook, (void **)&info->netd_hooks) == NULL) return (EEXIST); 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); } int net_family_shutdown(net_handle_t info, hook_family_t *hf) { 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); return (hook_family_shutdown(info->netd_hooks)); } /* * 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)); } int net_event_shutdown(net_handle_t info, hook_event_t *he) { ASSERT(info != NULL); ASSERT(he != NULL); if (info->netd_hooks == NULL) return (ENXIO); return (hook_event_shutdown(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); }