ipnet.c revision 133be3055da09253fc544763dcb744957f1f6319
/*
* 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
* 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.
*/
/*
* The ipnet device defined here provides access to packets at the IP layer. To
* provide access to packets at this layer it registers a callback function in
* the ip module and when there are open instances of the device ip will pass
* packets into the device. Packets from ip are passed on the input, output and
* loopback paths. Internally the module returns to ip as soon as possible by
* deferring processing using a taskq.
*
* filesystem and use of the neti interfaces. This module registers for NIC
* events using the neti framework so that when IP interfaces are bought up,
* taken down etc. the ipnet module is notified and its view of the interfaces
* configured on the system adjusted. On attach, the module gets an initial
* view of the system again using the neti framework but as it has already
* registered for IP interface events, it is still up-to-date with any changes.
*/
#include <sys/id_space.h>
#include <sys/hook_event.h>
#include <sys/sysmacros.h>
#include <inet/ip_multi.h>
static struct module_info ipnet_minfo = {
1, /* mi_idnum */
"ipnet", /* mi_idname */
0, /* mi_minpsz */
INFPSZ, /* mi_maxpsz */
2048, /* mi_hiwat */
0 /* mi_lowat */
};
/*
* List to hold static view of ipnetif_t's on the system. This is needed to
* avoid holding the lock protecting the avl tree of ipnetif's over the
* callback into the dev filesystem.
*/
typedef struct ipnetif_cbdata {
/*
* Convenience enumerated type for ipnet_accept(). It describes the
* properties of a given ipnet_addrp_t relative to a single ipnet_t
* client stream. The values represent whether the address is ...
*/
typedef enum {
IPNETADDR_MYADDR, /* an address on my ipnetif_t. */
IPNETADDR_MBCAST, /* a multicast or broadcast address. */
IPNETADDR_UNKNOWN /* none of the above. */
/* Argument used for the ipnet_nicevent_taskq callback. */
typedef struct ipnet_nicevent_s {
char ipne_ifname[LIFNAMSIZ];
static dev_info_t *ipnet_dip;
static major_t ipnet_major;
static id_space_t *ipnet_minor_space;
static void ipnet_input(mblk_t *);
static int ipnet_rsrv(queue_t *);
static int ipnet_close(queue_t *);
static void ipnet_nicevent_task(void *);
static int ipnet_if_compare_name(const void *, const void *);
static int ipnet_if_compare_index(const void *, const void *);
static void ipnetif_refhold(ipnetif_t *);
static void ipnetif_refrele(ipnetif_t *);
static void ipnet_walkers_inc(ipnet_stack_t *);
static void ipnet_walkers_dec(ipnet_stack_t *);
static void ipnet_register_netihook(ipnet_stack_t *);
static void ipnet_stack_fini(netstackid_t, void *);
static struct qinit ipnet_rinit = {
NULL, /* qi_putp */
ipnet_rsrv, /* qi_srvp */
ipnet_open, /* qi_qopen */
ipnet_close, /* qi_qclose */
NULL, /* qi_qadmin */
&ipnet_minfo, /* qi_minfo */
};
static struct qinit ipnet_winit = {
ipnet_wput, /* qi_putp */
NULL, /* qi_srvp */
NULL, /* qi_qopen */
NULL, /* qi_qclose */
NULL, /* qi_qadmin */
&ipnet_minfo, /* qi_minfo */
};
static struct streamtab ipnet_info = {
};
"STREAMS ipnet driver",
};
static struct modlinkage modlinkage = {
};
/*
* Walk the list of physical interfaces on the machine, for each
* interface create a new ipnetif_t and add any addresses to it. We
* need to do the walk twice, once for IPv4 and once for IPv6.
*
* The interfaces are destroyed as part of ipnet_stack_fini() for each
* stack. Note that we cannot do this initialization in
* ipnet_stack_init(), since ipnet_stack_init() cannot fail.
*/
static int
ipnet_if_init(void)
{
netstack_t *ns;
int ret = 0;
if (ret != 0)
break;
}
return (ret);
}
/*
* Standard module entry points.
*/
int
_init(void)
{
int ret;
return (ENODEV);
/*
* We call ddi_taskq_create() with nthread == 1 to ensure in-order
* delivery of packets to clients.
*/
1, TASKQ_DEFAULTPRI, 0);
goto done;
}
if ((ret = ipnet_if_init()) == 0)
done:
if (ret != 0) {
if (ipnet_taskq != NULL)
if (ipnet_nicevent_taskq != NULL)
}
return (ret);
}
int
_fini(void)
{
int err;
return (err);
return (0);
}
int
{
}
static void
{
int ret;
ips);
/*
* It is possible for an exclusive stack to be in the process of
* shutting down here, and the netid and protocol lookups could fail
* in that case.
*/
return;
ips->ips_nicevents)) != 0) {
}
}
ips->ips_nicevents)) != 0) {
}
}
}
/*
* This function is called on attach to build an initial view of the
* interfaces on the system. It will be called once for IPv4 and once
* for IPv6, although there is only one ipnet interface for both IPv4
* and IPv6 there are separate address lists.
*/
static int
{
int ret = 0;
/*
* If ipnet_register_netihook() was unable to initialize this
* stack's net_handle_t, then we cannot populate any interface
* information. This usually happens when we attempted to
* grab a net_handle_t as a stack was shutting down. We don't
* want to fail the entire _init() operation because of a
* stack shutdown (other stacks will continue to work just
* fine), so we silently return success here.
*/
return (0);
/*
* Make sure we're not processing NIC events during the
* population of our interfaces and address lists.
*/
continue;
goto done;
}
}
/*
* Skip addresses that aren't up. We'll add
* them when we receive an NE_LIF_UP event.
*/
continue;
/* Don't add it if we already have it. */
continue;
}
if (!new_if)
}
done:
return (ret);
}
static int
{
if (cmd != DDI_ATTACH)
return (DDI_FAILURE);
DDI_PSEUDO, 0) == DDI_FAILURE)
return (DDI_FAILURE);
return (DDI_SUCCESS);
}
static int
{
if (cmd != DDI_DETACH)
return (DDI_FAILURE);
return (DDI_SUCCESS);
}
/* ARGSUSED */
static int
{
int error = DDI_FAILURE;
switch (infocmd) {
case DDI_INFO_DEVT2INSTANCE:
*result = (void *)0;
error = DDI_SUCCESS;
break;
case DDI_INFO_DEVT2DEVINFO:
error = DDI_SUCCESS;
}
break;
}
return (error);
}
/* ARGSUSED */
static int
{
int err = 0;
/*
* If the system is labeled, only the global zone is allowed to open
* IP observability nodes.
*/
return (EACCES);
/* We don't support open as a module */
return (ENOTSUP);
/* This driver is self-cloning, we don't support re-open. */
return (EBUSY);
return (ENOMEM);
/*
* We need to hold ips_event_lock here as any NE_LIF_DOWN events need
* to be processed after ipnet_if is set and the ipnet_t has been
* inserted in the ips_str_list.
*/
} else {
goto done;
}
}
while (ips->ips_walkers_cnt != 0)
/*
* Only register our callback if we're the first open client; we call
* unregister in close() for the last open client.
*/
done:
if (err != 0) {
}
return (err);
}
static int
{
while (ips->ips_walkers_cnt != 0)
return (0);
}
static int
{
case M_FLUSH:
}
else
break;
case M_PROTO:
case M_PCPROTO:
ipnet_wputnondata(q, mp);
break;
case M_IOCTL:
ipnet_ioctl(q, mp);
break;
case M_IOCDATA:
ipnet_iocdata(q, mp);
break;
default:
break;
}
return (0);
}
static int
ipnet_rsrv(queue_t *q)
{
if (canputnext(q)) {
} else {
break;
}
}
return (0);
}
static void
{
case DLIOCRAW:
break;
case DLIOCIPNETINFO:
break;
}
/* Fallthrough, we don't support I_STR with DLIOCIPNETINFO. */
default:
break;
}
}
static void
{
case DLIOCIPNETINFO:
else
goto iocnak;
break;
default:
break;
}
}
static void
{
switch (prim) {
case DL_INFO_REQ:
ipnet_inforeq(q, mp);
break;
case DL_UNBIND_REQ:
ipnet_unbindreq(q, mp);
break;
case DL_BIND_REQ:
ipnet_bindreq(q, mp);
break;
case DL_PROMISCON_REQ:
ipnet_dlpromisconreq(q, mp);
break;
case DL_PROMISCOFF_REQ:
ipnet_dlpromiscoffreq(q, mp);
break;
case DL_UNITDATA_REQ:
case DL_DETACH_REQ:
case DL_PHYS_ADDR_REQ:
case DL_SET_PHYS_ADDR_REQ:
case DL_ENABMULTI_REQ:
case DL_DISABMULTI_REQ:
case DL_ATTACH_REQ:
break;
default:
break;
}
}
static void
{
return;
}
return;
*dlip = ipnet_infoack;
}
static void
{
return;
}
} else {
}
}
static void
{
return;
}
} else {
}
}
static void
{
int err;
return;
}
return;
}
return;
}
}
switch (level) {
case DL_PROMISC_PHYS:
break;
case DL_PROMISC_SAP:
break;
case DL_PROMISC_MULTI:
break;
default:
return;
}
}
static void
{
return;
}
return;
}
switch (level) {
case DL_PROMISC_PHYS:
break;
case DL_PROMISC_SAP:
break;
case DL_PROMISC_MULTI:
break;
default:
return;
}
return;
}
}
}
static int
{
int err = 0;
if (ipnetif->if_multicnt == 0) {
(IPNETIF_IPV4ALLMULTI | IPNETIF_IPV6ALLMULTI)) == 0);
if (err != 0)
goto done;
}
if (err != 0 &&
goto done;
}
}
}
ipnetif->if_multicnt++;
done:
return (err);
}
static void
{
int err;
if (--ipnetif->if_multicnt == 0) {
}
}
}
}
static mblk_t *
{
return (NULL);
}
return (dlhdr);
}
static ipnet_addrtype_t
{
/* First check if the address is multicast or limited broadcast. */
switch (addr->iap_family) {
case AF_INET:
return (IPNETADDR_MBCAST);
break;
case AF_INET6:
return (IPNETADDR_MBCAST);
break;
}
/*
* Walk the address list to see if the address belongs to our
* interface or is one of our subnet broadcast addresses.
*/
/*
* If we're not in the global zone, then only look at
* addresses in our zone.
*/
continue;
switch (addr->iap_family) {
case AF_INET:
break;
case AF_INET6:
&ifaddr->ifa_ip6addr))
break;
}
}
return (addrtype);
}
/*
* Verify if the packet contained in ihd should be passed up to the
* ipnet client stream.
*/
static boolean_t
{
/*
* Do not allow an ipnet stream to see packets that are not from or to
* its zone. The exception is when zones are using the shared stack
* model. In this case, streams in the global zone have visibility
* into other shared-stack zones, and broadcast and multicast traffic
* is visible by all zones in the stack.
*/
dsttype != IPNETADDR_MBCAST) {
return (B_FALSE);
}
/*
* If DL_PROMISC_SAP isn't enabled, then the bound SAP must match the
* packet's IP version.
*/
return (B_FALSE);
/* If the destination address is ours, then accept the packet. */
if (dsttype == IPNETADDR_MYADDR)
return (B_TRUE);
/*
* If DL_PROMISC_PHYS is enabled, then we can see all packets that are
* sent or received on the interface we're observing, or packets that
* have our source address (this allows us to see packets we send).
*/
return (B_TRUE);
}
/*
* We accept multicast and broadcast packets transmitted or received
* on the interface we're observing.
*/
return (B_TRUE);
return (B_FALSE);
}
/*
* Verify if the packet contained in ihd should be passed up to the ipnet
* client stream that's in IPNET_LOMODE.
*/
/* ARGSUSED */
static boolean_t
{
return (B_FALSE);
/*
*/
return (B_FALSE);
}
}
static void
ipnet_dispatch(void *arg)
{
} else {
}
continue;
} else {
continue;
}
}
continue;
}
}
} else {
}
}
}
static void
{
DDI_SUCCESS) {
}
}
/*
* Create a new ipnetif_t and new minor node for it. If creation is
* successful the new ipnetif_t is inserted into an avl_tree
* containing ipnetif's for this stack instance.
*/
static ipnetif_t *
{
avl_index_t where = 0;
/*
* Because ipnet_create_if() can be called from a NIC event
* callback, it should not block.
*/
return (NULL);
return (NULL);
}
return (ipnetif);
}
static void
{
/* Send a SIGHUP to all open streams associated with this ipnetif. */
}
/* Release the reference we implicitly held in ipnet_create_if(). */
}
static void
{
}
}
static void
{
}
/*
* Create an ipnetif_addr_t with the given logical interface id (lif)
* and add it to the supplied ipnetif. The lif is the netinfo
* representation of logical interface id, and we use this id to match
* incoming netinfo events against our lists of addresses.
*/
static void
{
struct sockaddr_in bcast;
struct sockaddr_storage addr;
return;
return;
case AF_INET:
/*
* Try and get the broadcast address. Note that it's okay for
* an interface to not have a broadcast address, so we don't
* fail the entire operation if net_getlifaddr() fails here.
*/
type = NA_BROADCAST;
break;
case AF_INET6:
break;
}
}
static void
{
}
static void
{
}
}
if (ipnetif->if_multicnt != 0) {
}
}
if (refrele_needed)
}
static void
{
return;
/*
* Note that we have one ipnetif for both IPv4 and IPv6, but we receive
* separate NE_UNPLUMB events for IPv4 and IPv6. We remove the ipnetif
* if both IPv4 and IPv6 interfaces have been unplumbed.
*/
}
static void
{
return;
/*
* We must have missed a NE_LIF_DOWN event. Delete this
* ifaddr and re-create it.
*/
}
}
static void
{
return;
/*
* Make sure that open streams on this ipnetif are still allowed to
* have it open.
*/
}
/*
* This callback from the NIC event framework dispatches a taskq as the event
* handlers may block.
*/
/* ARGSUSED */
static int
{
return (0);
if (hn->hne_datalen != 0) {
sizeof (ipne->ipne_ifname));
}
ipne, DDI_NOSLEEP);
return (0);
}
static void
ipnet_nicevent_task(void *arg)
{
netstack_t *ns;
goto done;
switch (ipne->ipne_event) {
case NE_PLUMB:
isv6);
break;
case NE_UNPLUMB:
break;
case NE_LIF_UP:
break;
case NE_LIF_DOWN:
isv6);
break;
default:
break;
}
done:
}
{
netstack_t *ns;
return (dev);
return (dev);
}
return (dev);
}
static ipnetif_t *
{
return (ipnetif);
}
static ipnetif_t *
{
break;
}
}
return (ipnetif);
}
static ipnetif_addr_t *
{
break;
}
return (ifaddr);
}
/* ARGSUSED */
static void *
{
return (ips);
}
/* ARGSUSED */
static void
{
ips->ips_nicevents) == 0);
}
ips->ips_nicevents) == 0);
}
}
}
/* Do any of the addresses in addrlist belong the supplied zoneid? */
static boolean_t
{
return (B_TRUE);
}
return (B_FALSE);
}
/* Should the supplied ipnetif be visible from the supplied zoneid? */
static boolean_t
{
int ret;
/*
* The global zone has visibility into all interfaces in the global
* stack, and exclusive stack zones have visibility into all
* interfaces in their stack.
*/
if (zoneid == GLOBAL_ZONEID ||
return (B_TRUE);
/*
* Shared-stack zones only have visibility for interfaces that have
* addresses in their zone.
*/
return (ret);
}
/*
* Verify that any ipnet_t that has a reference to the supplied ipnetif should
* still be allowed to have it open. A given ipnet_t may no longer be allowed
* to have an ipnetif open if there are no longer any addresses that belong to
* the ipnetif in the ipnet_t's non-global shared-stack zoneid. If that's the
* case, send the ipnet_t an M_HANGUP.
*/
static void
{
continue;
}
}
void
{
netstack_t *ns;
/*
* On labeled systems, non-global zones shouldn't see anything
*/
return;
return;
continue;
}
}
}
static int
{
}
static int
{
int res;
}
static void
{
}
static void
{
else
}
static void
{
ips->ips_walkers_cnt++;
}
static void
{
if (--ips->ips_walkers_cnt == 0)
}