X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/43866e378188c25dd1e2208016ab3cbeb086ae6c..eee3565979933af707c711411001ba11fe406a3c:/bsd/net/ndrv.c diff --git a/bsd/net/ndrv.c b/bsd/net/ndrv.c index 4707ef7c5..e171b48f3 100644 --- a/bsd/net/ndrv.c +++ b/bsd/net/ndrv.c @@ -1,17 +1,20 @@ /* - * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. + * Copyright (c) 1997-2014 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * - * @APPLE_LICENSE_HEADER_START@ - * - * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. - * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, @@ -19,10 +22,9 @@ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ -/* Copyright (c) 1997, 1998 Apple Computer, Inc. All Rights Reserved */ /* * @(#)ndrv.c 1.1 (MacOSX) 6/10/43 * Justin Walker, 970604 @@ -34,15 +36,14 @@ /* * PF_NDRV allows raw access to a specified network device, directly * with a socket. Expected use involves a socket option to request - * protocol packets. This lets ndrv_output() call dlil_output(), and + * protocol packets. This lets ndrv_output() call ifnet_output(), and * lets DLIL find the proper recipient for incoming packets. * The purpose here is for user-mode protocol implementation. * Note that "pure raw access" will still be accomplished with BPF. * * In addition to the former use, when combined with socket NKEs, * PF_NDRV permits a fairly flexible mechanism for implementing - * strange protocol support. One of the main ones will be the - * BlueBox/Classic Shared IP Address support. + * strange protocol support. */ #include @@ -56,6 +57,7 @@ #include #include #include +#include #include #include #include @@ -63,12 +65,12 @@ #include #include -#include #include #include #include #include #include +#include #if INET #include @@ -78,81 +80,61 @@ #include -int ndrv_do_detach(struct ndrv_cb *); -int ndrv_do_disconnect(struct ndrv_cb *); -struct ndrv_cb *ndrv_find_tag(unsigned int); -void ndrv_read_event(struct socket* inSo, caddr_t ref, int waitf); -int ndrv_setspec(struct ndrv_cb *np, struct sockopt *sopt); -int ndrv_delspec(struct ndrv_cb *); -int ndrv_to_dlil_demux(struct ndrv_demux_desc* ndrv, struct dlil_demux_desc* dlil); -void ndrv_handle_ifp_detach(u_long family, short unit); +static unsigned int ndrv_multi_max_count = NDRV_DMUX_MAX_DESCR; +SYSCTL_UINT(_net, OID_AUTO, ndrv_multi_max_count, CTLFLAG_RW | CTLFLAG_LOCKED, + &ndrv_multi_max_count, 0, "Number of allowed multicast addresses per NRDV socket"); + +static int ndrv_do_detach(struct ndrv_cb *); +static int ndrv_do_disconnect(struct ndrv_cb *); +static struct ndrv_cb *ndrv_find_inbound(struct ifnet *ifp, u_int32_t protocol_family); +static int ndrv_setspec(struct ndrv_cb *np, struct sockopt *sopt); +static int ndrv_delspec(struct ndrv_cb *); +static int ndrv_to_ifnet_demux(struct ndrv_demux_desc* ndrv, struct ifnet_demux_desc* ifdemux); +static void ndrv_handle_ifp_detach(u_int32_t family, short unit); static int ndrv_do_add_multicast(struct ndrv_cb *np, struct sockopt *sopt); static int ndrv_do_remove_multicast(struct ndrv_cb *np, struct sockopt *sopt); static struct ndrv_multiaddr* ndrv_have_multicast(struct ndrv_cb *np, struct sockaddr* addr); static void ndrv_remove_all_multicast(struct ndrv_cb *np); +static void ndrv_dominit(struct domain *); -unsigned long ndrv_sendspace = NDRVSNDQ; -unsigned long ndrv_recvspace = NDRVRCVQ; -struct ndrv_cb ndrvl; /* Head of controlblock list */ +u_int32_t ndrv_sendspace = NDRVSNDQ; +u_int32_t ndrv_recvspace = NDRVRCVQ; +TAILQ_HEAD(, ndrv_cb) ndrvl = TAILQ_HEAD_INITIALIZER(ndrvl); -struct domain ndrvdomain; -struct protosw ndrvsw; -static struct socket* ndrv_so; +static struct domain *ndrvdomain = NULL; +extern struct domain ndrvdomain_s; +#define NDRV_PROTODEMUX_COUNT 10 /* - * Protocol init function for NDRV protocol - * Init the control block list. + * Verify these values match. + * To keep clients from including dlil.h, we define + * these values independently in ndrv.h. They must + * match or a conversion function must be written. */ -void -ndrv_init() -{ - int retval; - struct kev_request kev_request; - - ndrvl.nd_next = ndrvl.nd_prev = &ndrvl; - - /* Create a PF_SYSTEM socket so we can listen for events */ - retval = socreate(PF_SYSTEM, &ndrv_so, SOCK_RAW, SYSPROTO_EVENT); - if (retval != 0 || ndrv_so == NULL) - retval = KERN_FAILURE; - - /* Install a callback function for the socket */ - ndrv_so->so_rcv.sb_flags |= SB_NOTIFY|SB_UPCALL; - ndrv_so->so_upcall = ndrv_read_event; - ndrv_so->so_upcallarg = NULL; - - /* Configure the socket to receive the events we're interested in */ - kev_request.vendor_code = KEV_VENDOR_APPLE; - kev_request.kev_class = KEV_NETWORK_CLASS; - kev_request.kev_subclass = KEV_DL_SUBCLASS; - retval = ndrv_so->so_proto->pr_usrreqs->pru_control(ndrv_so, SIOCSKEVFILT, (caddr_t)&kev_request, 0, 0); - if (retval != 0) - { - /* - * We will not get attaching or detaching events in this case. - * We should probably prevent any sockets from binding so we won't - * panic later if the interface goes away. - */ - log(LOG_WARNING, "PF_NDRV: ndrv_init - failed to set event filter (%d)", - retval); - } -} +#if NDRV_DEMUXTYPE_ETHERTYPE != DLIL_DESC_ETYPE2 +#error NDRV_DEMUXTYPE_ETHERTYPE must match DLIL_DESC_ETYPE2 +#endif +#if NDRV_DEMUXTYPE_SAP != DLIL_DESC_SAP +#error NDRV_DEMUXTYPE_SAP must match DLIL_DESC_SAP +#endif +#if NDRV_DEMUXTYPE_SNAP != DLIL_DESC_SNAP +#error NDRV_DEMUXTYPE_SNAP must match DLIL_DESC_SNAP +#endif /* * Protocol output - Called to output a raw network packet directly * to the driver. */ -int -ndrv_output(register struct mbuf *m, register struct socket *so) +static int +ndrv_output(struct mbuf *m, struct socket *so) { - register struct ndrv_cb *np = sotondrvcb(so); - register struct ifnet *ifp = np->nd_if; - extern void kprintf(const char *, ...); + struct ndrv_cb *np = sotondrvcb(so); + struct ifnet *ifp = np->nd_if; int result = 0; #if NDRV_DEBUG - kprintf("NDRV output: %x, %x, %x\n", m, so, np); + printf("NDRV output: %x, %x, %x\n", m, so, np); #endif /* @@ -161,33 +143,36 @@ ndrv_output(register struct mbuf *m, register struct socket *so) if ((m->m_flags&M_PKTHDR) == 0) return(EINVAL); + /* Unlock before calling ifnet_output */ + socket_unlock(so, 0); + /* * Call DLIL if we can. DLIL is much safer than calling the * ifp directly. */ - if (np->nd_tag != 0) - result = dlil_output(np->nd_tag, m, (caddr_t)NULL, - (struct sockaddr*)NULL, 1); - else if (np->nd_send_tag != 0) - result = dlil_output(np->nd_send_tag, m, (caddr_t)NULL, - (struct sockaddr*)NULL, 1); - else - result = ENXIO; + result = ifnet_output_raw(ifp, np->nd_proto_family, m); + + socket_lock(so, 0); + return (result); } /* Our input routine called from DLIL */ -int -ndrv_input(struct mbuf *m, - char *frame_header, - struct ifnet *ifp, - u_long dl_tag, - int sync_ok) +static errno_t +ndrv_input( + ifnet_t ifp, + protocol_family_t proto_family, + mbuf_t m, + char *frame_header) { struct socket *so; - struct sockaddr_dl ndrvsrc = {sizeof (struct sockaddr_dl), AF_NDRV}; - register struct ndrv_cb *np; + struct sockaddr_dl ndrvsrc; + struct ndrv_cb *np; + int error = 0; + ndrvsrc.sdl_len = sizeof (struct sockaddr_dl); + ndrvsrc.sdl_family = AF_NDRV; + ndrvsrc.sdl_index = 0; /* move packet from if queue to socket */ /* Should be media-independent */ @@ -197,70 +182,65 @@ ndrv_input(struct mbuf *m, ndrvsrc.sdl_slen = 0; bcopy(frame_header, &ndrvsrc.sdl_data, 6); - np = ndrv_find_tag(dl_tag); + np = ndrv_find_inbound(ifp, proto_family); if (np == NULL) { return(ENOENT); } so = np->nd_socket; /* prepend the frame header */ - m = m_prepend(m, ifp->if_data.ifi_hdrlen, M_NOWAIT); + m = m_prepend(m, ifnet_hdrlen(ifp), M_NOWAIT); if (m == NULL) return EJUSTRETURN; - bcopy(frame_header, m->m_data, ifp->if_data.ifi_hdrlen); + bcopy(frame_header, m->m_data, ifnet_hdrlen(ifp)); + + lck_mtx_assert(ndrvdomain->dom_mtx, LCK_MTX_ASSERT_NOTOWNED); + lck_mtx_lock(ndrvdomain->dom_mtx); if (sbappendaddr(&(so->so_rcv), (struct sockaddr *)&ndrvsrc, - m, (struct mbuf *)0) == 0) - { - /* yes, sbappendaddr returns zero if the sockbuff is full... */ - /* caller will free m */ - return(ENOMEM); - } else + m, (struct mbuf *)0, &error) != 0) { sorwakeup(so); - return(0); -} - -int -ndrv_control(struct socket *so, u_long cmd, caddr_t data, - struct ifnet *ifp, struct proc *p) -{ - return (0); + } + lck_mtx_unlock(ndrvdomain->dom_mtx); + return 0; /* radar 4030377 - always return 0 */ } /* * Allocate an ndrv control block and some buffer space for the socket */ -int -ndrv_attach(struct socket *so, int proto, struct proc *p) +static int +ndrv_attach(struct socket *so, int proto, __unused struct proc *p) { int error; - register struct ndrv_cb *np = sotondrvcb(so); + struct ndrv_cb *np = sotondrvcb(so); if ((so->so_state & SS_PRIV) == 0) return(EPERM); #if NDRV_DEBUG - kprintf("NDRV attach: %x, %x, %x\n", so, proto, np); + printf("NDRV attach: %x, %x, %x\n", so, proto, np); #endif + + if ((error = soreserve(so, ndrv_sendspace, ndrv_recvspace))) + return(error); + MALLOC(np, struct ndrv_cb *, sizeof(*np), M_PCB, M_WAITOK); if (np == NULL) return (ENOMEM); so->so_pcb = (caddr_t)np; bzero(np, sizeof(*np)); #if NDRV_DEBUG - kprintf("NDRV attach: %x, %x, %x\n", so, proto, np); + printf("NDRV attach: %x, %x, %x\n", so, proto, np); #endif - if ((error = soreserve(so, ndrv_sendspace, ndrv_recvspace))) - return(error); TAILQ_INIT(&np->nd_dlist); np->nd_signature = NDRV_SIGNATURE; np->nd_socket = so; - np->nd_proto.sp_family = so->so_proto->pr_domain->dom_family; + np->nd_proto.sp_family = SOCK_DOM(so); np->nd_proto.sp_protocol = proto; np->nd_if = NULL; - np->nd_tag = 0; + np->nd_proto_family = 0; np->nd_family = 0; np->nd_unit = 0; - insque((queue_t)np, (queue_t)&ndrvl); + TAILQ_INSERT_TAIL(&ndrvl, np, nd_next); return(0); } @@ -269,10 +249,10 @@ ndrv_attach(struct socket *so, int proto, struct proc *p) * Flush data or not depending on the options. */ -int +static int ndrv_detach(struct socket *so) { - register struct ndrv_cb *np = sotondrvcb(so); + struct ndrv_cb *np = sotondrvcb(so); if (np == 0) return EINVAL; @@ -289,46 +269,60 @@ ndrv_detach(struct socket *so) * Don't expect this to be used. */ -int ndrv_connect(struct socket *so, struct sockaddr *nam, struct proc *p) +static int +ndrv_connect(struct socket *so, struct sockaddr *nam, __unused struct proc *p) { - register struct ndrv_cb *np = sotondrvcb(so); - int result = 0; + struct ndrv_cb *np = sotondrvcb(so); if (np == 0) return EINVAL; if (np->nd_faddr) return EISCONN; - - /* Allocate memory to store the remote address */ - MALLOC(np->nd_faddr, struct sockaddr_ndrv*, + + /* Allocate memory to store the remote address */ + MALLOC(np->nd_faddr, struct sockaddr_ndrv*, nam->sa_len, M_IFADDR, M_WAITOK); - if (result != 0) - return result; - if (np->nd_faddr == NULL) - return ENOMEM; - + if (np->nd_faddr == NULL) + return ENOMEM; + bcopy((caddr_t) nam, (caddr_t) np->nd_faddr, nam->sa_len); soisconnected(so); return 0; } +static void +ndrv_event(struct ifnet *ifp, __unused protocol_family_t protocol, + const struct kev_msg *event) +{ + if (event->vendor_code == KEV_VENDOR_APPLE && + event->kev_class == KEV_NETWORK_CLASS && + event->kev_subclass == KEV_DL_SUBCLASS && + event->event_code == KEV_DL_IF_DETACHING) { + lck_mtx_assert(ndrvdomain->dom_mtx, LCK_MTX_ASSERT_NOTOWNED); + lck_mtx_lock(ndrvdomain->dom_mtx); + ndrv_handle_ifp_detach(ifnet_family(ifp), ifnet_unit(ifp)); + lck_mtx_unlock(ndrvdomain->dom_mtx); + } +} + +static int name_cmp(struct ifnet *, char *); + /* * This is the "driver open" hook - we 'bind' to the * named driver. * Here's where we latch onto the driver. */ -int -ndrv_bind(struct socket *so, struct sockaddr *nam, struct proc *p) +static int +ndrv_bind(struct socket *so, struct sockaddr *nam, __unused struct proc *p) { - register struct sockaddr_ndrv *sa = (struct sockaddr_ndrv *) nam; - register char *dname; - register struct ndrv_cb *np; - register struct ifnet *ifp; - extern int name_cmp(struct ifnet *, char *); + struct sockaddr_ndrv *sa = (struct sockaddr_ndrv *) nam; + char *dname; + struct ndrv_cb *np; + struct ifnet *ifp; int result; - if TAILQ_EMPTY(&ifnet) + if TAILQ_EMPTY(&ifnet_head) return(EADDRNOTAVAIL); /* Quick sanity check */ np = sotondrvcb(so); if (np == 0) @@ -342,68 +336,59 @@ ndrv_bind(struct socket *so, struct sockaddr *nam, struct proc *p) if (np->nd_laddr == NULL) return(ENOMEM); bcopy((caddr_t) sa, (caddr_t) np->nd_laddr, sizeof(struct sockaddr_ndrv)); - dname = sa->snd_name; + dname = (char *) sa->snd_name; if (*dname == '\0') return(EINVAL); #if NDRV_DEBUG - kprintf("NDRV bind: %x, %x, %s\n", so, np, dname); + printf("NDRV bind: %x, %x, %s\n", so, np, dname); #endif /* Track down the driver and its ifnet structure. * There's no internal call for this so we have to dup the code * in if.c/ifconf() */ - TAILQ_FOREACH(ifp, &ifnet, if_link) { + ifnet_head_lock_shared(); + TAILQ_FOREACH(ifp, &ifnet_head, if_link) { if (name_cmp(ifp, dname) == 0) break; } + ifnet_head_done(); if (ifp == NULL) return(EADDRNOTAVAIL); - - /* - * Loopback demuxing doesn't work with PF_NDRV. - * The first 4 bytes of the packet must be the - * protocol ptr. Can't get that from userland. - */ - if (ifp->if_family == APPLE_IF_FAM_LOOPBACK) - return (ENOTSUP); - - if ((dlil_find_dltag(ifp->if_family, ifp->if_unit, - PF_NDRV, &np->nd_send_tag) != 0) && - (ifp->if_family != APPLE_IF_FAM_PPP)) { - /* NDRV isn't registered on this interface, lets change that */ - struct dlil_proto_reg_str ndrv_proto; - int result = 0; - bzero(&ndrv_proto, sizeof(ndrv_proto)); - TAILQ_INIT(&ndrv_proto.demux_desc_head); - - ndrv_proto.interface_family = ifp->if_family; - ndrv_proto.protocol_family = PF_NDRV; - ndrv_proto.unit_number = ifp->if_unit; - - result = dlil_attach_protocol(&ndrv_proto, &np->nd_send_tag); - - /* - * If the interface does not allow PF_NDRV to attach, we will - * respect it's wishes. Sending will be disabled. No error is - * returned because the client may later attach a real protocol - * that the interface may accept. - */ - if (result != 0) - np->nd_send_tag = 0; - } - + + // PPP doesn't support PF_NDRV. + if (ifnet_family(ifp) != APPLE_IF_FAM_PPP) + { + /* NDRV on this interface */ + struct ifnet_attach_proto_param ndrv_proto; + result = 0; + bzero(&ndrv_proto, sizeof(ndrv_proto)); + ndrv_proto.event = ndrv_event; + + /* We aren't worried about double attaching, that should just return an error */ + socket_unlock(so, 0); + result = ifnet_attach_protocol(ifp, PF_NDRV, &ndrv_proto); + socket_lock(so, 0); + if (result && result != EEXIST) { + return result; + } + np->nd_proto_family = PF_NDRV; + } + else { + np->nd_proto_family = 0; + } + np->nd_if = ifp; - np->nd_family = ifp->if_family; - np->nd_unit = ifp->if_unit; - + np->nd_family = ifnet_family(ifp); + np->nd_unit = ifnet_unit(ifp); + return(0); } -int +static int ndrv_disconnect(struct socket *so) { - register struct ndrv_cb *np = sotondrvcb(so); + struct ndrv_cb *np = sotondrvcb(so); if (np == 0) return EINVAL; @@ -415,41 +400,13 @@ ndrv_disconnect(struct socket *so) return 0; } -/* - * Accessor function - */ -struct ifnet* -ndrv_get_ifp(caddr_t ndrv_pcb) -{ - struct ndrv_cb* np = (struct ndrv_cb*)ndrv_pcb; - -#if DEBUG - { - struct ndrv_cb* temp = ndrvl.nd_next; - /* Verify existence of pcb */ - for (temp = ndrvl.nd_next; temp != &ndrvl; temp = temp->nd_next) - { - if (temp == np) - break; - } - - if (temp != np) - { - log(LOG_WARNING, "PF_NDRV: ndrv_get_ifp called with invalid ndrv_cb!"); - return NULL; - } - } -#endif - - return np->nd_if; -} - /* * Mark the connection as being incapable of further input. */ -int +static int ndrv_shutdown(struct socket *so) { + lck_mtx_assert(ndrvdomain->dom_mtx, LCK_MTX_ASSERT_OWNED); socantsendmore(so); return 0; } @@ -459,10 +416,10 @@ ndrv_shutdown(struct socket *so) * to the appropriate driver. The really tricky part * is the destination address... */ -int -ndrv_send(struct socket *so, int flags, struct mbuf *m, - struct sockaddr *addr, struct mbuf *control, - struct proc *p) +static int +ndrv_send(struct socket *so, __unused int flags, struct mbuf *m, + __unused struct sockaddr *addr, struct mbuf *control, + __unused struct proc *p) { int error; @@ -475,10 +432,10 @@ ndrv_send(struct socket *so, int flags, struct mbuf *m, } -int +static int ndrv_abort(struct socket *so) { - register struct ndrv_cb *np = sotondrvcb(so); + struct ndrv_cb *np = sotondrvcb(so); if (np == 0) return EINVAL; @@ -487,19 +444,10 @@ ndrv_abort(struct socket *so) return 0; } -int -ndrv_sense(struct socket *so, struct stat *sb) -{ - /* - * stat: don't bother with a blocksize. - */ - return (0); -} - -int +static int ndrv_sockaddr(struct socket *so, struct sockaddr **nam) { - register struct ndrv_cb *np = sotondrvcb(so); + struct ndrv_cb *np = sotondrvcb(so); int len; if (np == 0) @@ -509,16 +457,19 @@ ndrv_sockaddr(struct socket *so, struct sockaddr **nam) return EINVAL; len = np->nd_laddr->snd_len; + MALLOC(*nam, struct sockaddr *, len, M_SONAME, M_WAITOK); + if (*nam == NULL) + return ENOMEM; bcopy((caddr_t)np->nd_laddr, *nam, (unsigned)len); return 0; } -int +static int ndrv_peeraddr(struct socket *so, struct sockaddr **nam) { - register struct ndrv_cb *np = sotondrvcb(so); + struct ndrv_cb *np = sotondrvcb(so); int len; if (np == 0) @@ -528,32 +479,28 @@ ndrv_peeraddr(struct socket *so, struct sockaddr **nam) return ENOTCONN; len = np->nd_faddr->snd_len; + MALLOC(*nam, struct sockaddr *, len, M_SONAME, M_WAITOK); + if (*nam == NULL) + return ENOMEM; bcopy((caddr_t)np->nd_faddr, *nam, (unsigned)len); return 0; } -/* Control input */ - -void -ndrv_ctlinput(int dummy1, struct sockaddr *dummy2, void *dummy3) -{ -} - /* Control output */ -int +static int ndrv_ctloutput(struct socket *so, struct sockopt *sopt) { - register struct ndrv_cb *np = sotondrvcb(so); + struct ndrv_cb *np = sotondrvcb(so); int error = 0; - + switch(sopt->sopt_name) { case NDRV_DELDMXSPEC: /* Delete current spec */ /* Verify no parameter was passed */ - if (sopt->sopt_val != NULL || sopt->sopt_valsize != 0) { + if (sopt->sopt_val != 0 || sopt->sopt_valsize != 0) { /* * We don't support deleting a specific demux, it's * all or nothing. @@ -581,135 +528,125 @@ ndrv_ctloutput(struct socket *so, struct sockopt *sopt) return(error); } -/* Drain the queues */ -void -ndrv_drain() -{ -} - -/* Sysctl hook for NDRV */ -int -ndrv_sysctl() -{ - return(0); -} - -int -ndrv_do_detach(register struct ndrv_cb *np) +static int +ndrv_do_detach(struct ndrv_cb *np) { struct ndrv_cb* cur_np = NULL; struct socket *so = np->nd_socket; - struct ndrv_multicast* next; - int error; + int error = 0; + struct ifnet * ifp; #if NDRV_DEBUG - kprintf("NDRV detach: %x, %x\n", so, np); + printf("NDRV detach: %x, %x\n", so, np); #endif ndrv_remove_all_multicast(np); - - if (np->nd_tag != 0) - { - error = dlil_detach_protocol(np->nd_tag); - if (error) - { - log(LOG_WARNING, "NDRV ndrv_do_detach: error %d removing dl_tag %d", - error, np->nd_tag); - return error; - } - } - + + ifp = np->nd_if; /* Remove from the linked list of control blocks */ - remque((queue_t)np); - - if (np->nd_send_tag != 0) - { - /* Check if this is the last socket attached to this interface */ - for (cur_np = ndrvl.nd_next; cur_np != &ndrvl; cur_np = cur_np->nd_next) - { - if (cur_np->nd_family == np->nd_family && - cur_np->nd_unit == np->nd_unit) - { - break; - } - } - - /* If there are no other interfaces, detach PF_NDRV from the interface */ - if (cur_np == &ndrvl) - { - dlil_detach_protocol(np->nd_send_tag); - } - } - + TAILQ_REMOVE(&ndrvl, np, nd_next); + if (ifp != NULL) { + u_int32_t proto_family = np->nd_proto_family; + + if (proto_family != PF_NDRV && proto_family != 0) { + socket_unlock(so, 0); + ifnet_detach_protocol(ifp, proto_family); + socket_lock(so, 0); + } + + /* Check if this is the last socket attached to this interface */ + TAILQ_FOREACH(cur_np, &ndrvl, nd_next) { + if (cur_np->nd_family == np->nd_family && + cur_np->nd_unit == np->nd_unit) { + break; + } + } + + /* If there are no other interfaces, detach PF_NDRV from the interface */ + if (cur_np == NULL) { + socket_unlock(so, 0); + ifnet_detach_protocol(ifp, PF_NDRV); + socket_lock(so, 0); + } + } + if (np->nd_laddr != NULL) { + FREE((caddr_t)np->nd_laddr, M_IFADDR); + np->nd_laddr = NULL; + } FREE((caddr_t)np, M_PCB); so->so_pcb = 0; + so->so_flags |= SOF_PCBCLEARING; sofree(so); return error; } -int -ndrv_do_disconnect(register struct ndrv_cb *np) +static int +ndrv_do_disconnect(struct ndrv_cb *np) { + struct socket * so = np->nd_socket; #if NDRV_DEBUG - kprintf("NDRV disconnect: %x\n", np); + printf("NDRV disconnect: %x\n", np); #endif if (np->nd_faddr) { FREE(np->nd_faddr, M_IFADDR); np->nd_faddr = 0; } - if (np->nd_socket->so_state & SS_NOFDREF) + /* + * A multipath subflow socket would have its SS_NOFDREF set by default, + * so check for SOF_MP_SUBFLOW socket flag before detaching the PCB; + * when the socket is closed for real, SOF_MP_SUBFLOW would be cleared. + */ + if (!(so->so_flags & SOF_MP_SUBFLOW) && (so->so_state & SS_NOFDREF)) ndrv_do_detach(np); - soisdisconnected(np->nd_socket); + soisdisconnected(so); return(0); } +/* Hackery - return a string version of a decimal number */ +static void +sprint_d(u_int n, char *buf, int buflen) +{ char dbuf[IFNAMSIZ]; + char *cp = dbuf+IFNAMSIZ-1; + + *cp = 0; + do { buflen--; + cp--; + *cp = "0123456789"[n % 10]; + n /= 10; + } while (n != 0 && buflen > 0); + strlcpy(buf, cp, IFNAMSIZ-buflen); + return; +} + /* * Try to compare a device name (q) with one of the funky ifnet * device names (ifp). */ -int name_cmp(register struct ifnet *ifp, register char *q) -{ register char *r; - register int len; +static int name_cmp(struct ifnet *ifp, char *q) +{ char *r; + int len; char buf[IFNAMSIZ]; - static char *sprint_d(); r = buf; - len = strlen(ifp->if_name); - strncpy(r, ifp->if_name, IFNAMSIZ); + len = strlen(ifnet_name(ifp)); + strlcpy(r, ifnet_name(ifp), IFNAMSIZ); r += len; - (void)sprint_d(ifp->if_unit, r, IFNAMSIZ-(r-buf)); + sprint_d(ifnet_unit(ifp), r, IFNAMSIZ-(r-buf)); #if NDRV_DEBUG - kprintf("Comparing %s, %s\n", buf, q); + printf("Comparing %s, %s\n", buf, q); #endif return(strncmp(buf, q, IFNAMSIZ)); } -/* Hackery - return a string version of a decimal number */ -static char * -sprint_d(n, buf, buflen) - u_int n; - char *buf; - int buflen; -{ char dbuf[IFNAMSIZ]; - register char *cp = dbuf+IFNAMSIZ-1; - - *cp = 0; - do { buflen--; - cp--; - *cp = "0123456789"[n % 10]; - n /= 10; - } while (n != 0 && buflen > 0); - strncpy(buf, cp, IFNAMSIZ-buflen); - return (cp); -} - +#if 0 +//### Not used /* * When closing, dump any enqueued mbufs. */ void -ndrv_flushq(register struct ifqueue *q) +ndrv_flushq(struct ifqueue *q) { - register struct mbuf *m; + struct mbuf *m; for (;;) { IF_DEQUEUE(q, m); @@ -720,116 +657,150 @@ ndrv_flushq(register struct ifqueue *q) m_freem(m); } } +#endif int ndrv_setspec(struct ndrv_cb *np, struct sockopt *sopt) { - struct dlil_proto_reg_str dlilSpec; - struct ndrv_protocol_desc ndrvSpec; - struct dlil_demux_desc* dlilDemux = NULL; - struct ndrv_demux_desc* ndrvDemux = NULL; - int error = 0; - - /* Sanity checking */ - if (np->nd_tag) - return EBUSY; - if (np->nd_if == NULL) - return EINVAL; - if (sopt->sopt_valsize != sizeof(struct ndrv_protocol_desc)) - return EINVAL; - - /* Copy the ndrvSpec */ - error = sooptcopyin(sopt, &ndrvSpec, sizeof(struct ndrv_protocol_desc), - sizeof(struct ndrv_protocol_desc)); - if (error != 0) - return error; - - /* Verify the parameter */ - if (ndrvSpec.version > NDRV_PROTOCOL_DESC_VERS) - return ENOTSUP; // version is too new! - else if (ndrvSpec.version < 1) - return EINVAL; // version is not valid - - /* Allocate storage for demux array */ - MALLOC(ndrvDemux, struct ndrv_demux_desc*, - ndrvSpec.demux_count * sizeof(struct ndrv_demux_desc), M_TEMP, M_WAITOK); - if (ndrvDemux == NULL) - return ENOMEM; - - /* Allocate enough dlil_demux_descs */ - MALLOC(dlilDemux, struct dlil_demux_desc*, - sizeof(*dlilDemux) * ndrvSpec.demux_count, M_TEMP, M_WAITOK); - if (dlilDemux == NULL) - error = ENOMEM; - - if (error == 0) - { - /* Copy the ndrv demux array from userland */ - error = copyin(ndrvSpec.demux_list, ndrvDemux, - ndrvSpec.demux_count * sizeof(struct ndrv_demux_desc)); - ndrvSpec.demux_list = ndrvDemux; - } - - if (error == 0) - { - /* At this point, we've at least got enough bytes to start looking around */ - u_long demuxOn = 0; - - bzero(&dlilSpec, sizeof(dlilSpec)); - TAILQ_INIT(&dlilSpec.demux_desc_head); - dlilSpec.interface_family = np->nd_family; - dlilSpec.unit_number = np->nd_unit; - dlilSpec.input = ndrv_input; - dlilSpec.protocol_family = ndrvSpec.protocol_family; - - for (demuxOn = 0; demuxOn < ndrvSpec.demux_count; demuxOn++) - { - /* Convert an ndrv_demux_desc to a dlil_demux_desc */ - error = ndrv_to_dlil_demux(&ndrvSpec.demux_list[demuxOn], &dlilDemux[demuxOn]); - if (error) - break; - - /* Add the dlil_demux_desc to the list */ - TAILQ_INSERT_TAIL(&dlilSpec.demux_desc_head, &dlilDemux[demuxOn], next); - } - } - - if (error == 0) - { - /* We've got all our ducks lined up...lets attach! */ - error = dlil_attach_protocol(&dlilSpec, &np->nd_tag); - } - - /* Free any memory we've allocated */ - if (dlilDemux) - FREE(dlilDemux, M_TEMP); - if (ndrvDemux) - FREE(ndrvDemux, M_TEMP); - - return error; + struct ifnet_attach_proto_param proto_param; + struct ndrv_protocol_desc ndrvSpec; + struct ndrv_demux_desc* ndrvDemux = NULL; + int error = 0; + struct socket * so = np->nd_socket; + user_addr_t user_addr; + + /* Sanity checking */ + if (np->nd_proto_family != PF_NDRV) + return EBUSY; + if (np->nd_if == NULL) + return EINVAL; + + /* Copy the ndrvSpec */ + if (proc_is64bit(sopt->sopt_p)) { + struct ndrv_protocol_desc64 ndrvSpec64; + + if (sopt->sopt_valsize != sizeof(ndrvSpec64)) + return EINVAL; + + error = sooptcopyin(sopt, &ndrvSpec64, sizeof(ndrvSpec64), sizeof(ndrvSpec64)); + if (error != 0) + return error; + + ndrvSpec.version = ndrvSpec64.version; + ndrvSpec.protocol_family = ndrvSpec64.protocol_family; + ndrvSpec.demux_count = ndrvSpec64.demux_count; + + user_addr = ndrvSpec64.demux_list; + } + else { + struct ndrv_protocol_desc32 ndrvSpec32; + + if (sopt->sopt_valsize != sizeof(ndrvSpec32)) + return EINVAL; + + error = sooptcopyin(sopt, &ndrvSpec32, sizeof(ndrvSpec32), sizeof(ndrvSpec32)); + if (error != 0) + return error; + + ndrvSpec.version = ndrvSpec32.version; + ndrvSpec.protocol_family = ndrvSpec32.protocol_family; + ndrvSpec.demux_count = ndrvSpec32.demux_count; + + user_addr = CAST_USER_ADDR_T(ndrvSpec32.demux_list); + } + + /* Verify the parameter */ + if (ndrvSpec.version > NDRV_PROTOCOL_DESC_VERS) + return ENOTSUP; // version is too new! + else if (ndrvSpec.version < 1) + return EINVAL; // version is not valid + else if (ndrvSpec.demux_count > NDRV_PROTODEMUX_COUNT || ndrvSpec.demux_count == 0) + return EINVAL; // demux_count is not valid + + bzero(&proto_param, sizeof(proto_param)); + proto_param.demux_count = ndrvSpec.demux_count; + + /* Allocate storage for demux array */ + MALLOC(ndrvDemux, struct ndrv_demux_desc*, proto_param.demux_count * + sizeof(struct ndrv_demux_desc), M_TEMP, M_WAITOK); + if (ndrvDemux == NULL) + return ENOMEM; + + /* Allocate enough ifnet_demux_descs */ + MALLOC(proto_param.demux_array, struct ifnet_demux_desc*, + sizeof(*proto_param.demux_array) * ndrvSpec.demux_count, + M_TEMP, M_WAITOK); + if (proto_param.demux_array == NULL) + error = ENOMEM; + + if (error == 0) + { + /* Copy the ndrv demux array from userland */ + error = copyin(user_addr, ndrvDemux, + ndrvSpec.demux_count * sizeof(struct ndrv_demux_desc)); + ndrvSpec.demux_list = ndrvDemux; + } + + if (error == 0) + { + /* At this point, we've at least got enough bytes to start looking around */ + u_int32_t demuxOn = 0; + + proto_param.demux_count = ndrvSpec.demux_count; + proto_param.input = ndrv_input; + proto_param.event = ndrv_event; + + for (demuxOn = 0; demuxOn < ndrvSpec.demux_count; demuxOn++) + { + /* Convert an ndrv_demux_desc to a ifnet_demux_desc */ + error = ndrv_to_ifnet_demux(&ndrvSpec.demux_list[demuxOn], + &proto_param.demux_array[demuxOn]); + if (error) + break; + } + } + + if (error == 0) + { + /* We've got all our ducks lined up...lets attach! */ + socket_unlock(so, 0); + error = ifnet_attach_protocol(np->nd_if, ndrvSpec.protocol_family, + &proto_param); + socket_lock(so, 0); + if (error == 0) + np->nd_proto_family = ndrvSpec.protocol_family; + } + + /* Free any memory we've allocated */ + if (proto_param.demux_array) + FREE(proto_param.demux_array, M_TEMP); + if (ndrvDemux) + FREE(ndrvDemux, M_TEMP); + + return error; } int -ndrv_to_dlil_demux(struct ndrv_demux_desc* ndrv, struct dlil_demux_desc* dlil) +ndrv_to_ifnet_demux(struct ndrv_demux_desc* ndrv, struct ifnet_demux_desc* ifdemux) { - bzero(dlil, sizeof(*dlil)); - + bzero(ifdemux, sizeof(*ifdemux)); + if (ndrv->type < DLIL_DESC_ETYPE2) { /* using old "type", not supported */ return ENOTSUP; } - + if (ndrv->length > 28) { return EINVAL; } - - dlil->type = ndrv->type; - dlil->native_type = ndrv->data.other; - dlil->variants.native_type_length = ndrv->length; - + + ifdemux->type = ndrv->type; + ifdemux->data = ndrv->data.other; + ifdemux->datalen = ndrv->length; + return 0; } @@ -837,119 +808,72 @@ int ndrv_delspec(struct ndrv_cb *np) { int result = 0; - - if (np->nd_tag == 0) + + if (np->nd_proto_family == PF_NDRV || + np->nd_proto_family == 0) return EINVAL; - + /* Detach the protocol */ - result = dlil_detach_protocol(np->nd_tag); - if (result == 0) - { - np->nd_tag = 0; - } - + result = ifnet_detach_protocol(np->nd_if, np->nd_proto_family); + np->nd_proto_family = PF_NDRV; + return result; } struct ndrv_cb * -ndrv_find_tag(unsigned int tag) +ndrv_find_inbound(struct ifnet *ifp, u_int32_t protocol) { struct ndrv_cb* np; - int i; - - if (tag == 0) - return NULL; - - for (np = ndrvl.nd_next; np != NULL; np = np->nd_next) - { - if (np->nd_tag == tag) - { + + if (protocol == PF_NDRV) return NULL; + + TAILQ_FOREACH(np, &ndrvl, nd_next) { + if (np->nd_proto_family == protocol && + np->nd_if == ifp) { return np; } } - - return NULL; -} -void ndrv_dominit() -{ - static int ndrv_dominited = 0; - - if (ndrv_dominited == 0 && - net_add_proto(&ndrvsw, &ndrvdomain) == 0) - ndrv_dominited = 1; -} - -void -ndrv_read_event(struct socket* so, caddr_t ref, int waitf) -{ - // Read an event - struct mbuf *m = NULL; - struct kern_event_msg *msg; - struct uio auio = {0}; - int result = 0; - int flags = 0; - - // Get the data - auio.uio_resid = 1000000; // large number to get all of the data - flags = MSG_DONTWAIT; - result = soreceive(so, (struct sockaddr**)NULL, &auio, &m, - (struct mbuf**)NULL, &flags); - if (result != 0 || m == NULL) - return; - - // cast the mbuf to a kern_event_msg - // this is dangerous, doesn't handle linked mbufs - msg = mtod(m, struct kern_event_msg*); - - // check for detaches, assume even filtering is working - if (msg->event_code == KEV_DL_IF_DETACHING || - msg->event_code == KEV_DL_IF_DETACHED) - { - struct net_event_data *ev_data; - ev_data = (struct net_event_data*)msg->event_data; - ndrv_handle_ifp_detach(ev_data->if_family, ev_data->if_unit); - } - - m_free(m); + return NULL; } -void -ndrv_handle_ifp_detach(u_long family, short unit) +static void +ndrv_handle_ifp_detach(u_int32_t family, short unit) { struct ndrv_cb* np; - u_long dl_tag; - + struct ifnet *ifp = NULL; + struct socket *so; + /* Find all sockets using this interface. */ - for (np = ndrvl.nd_next; np != &ndrvl; np = np->nd_next) - { + TAILQ_FOREACH(np, &ndrvl, nd_next) { if (np->nd_family == family && np->nd_unit == unit) { /* This cb is using the detaching interface, but not for long. */ /* Let the protocol go */ - if (np->nd_tag != 0) + ifp = np->nd_if; + if (np->nd_proto_family != 0) ndrv_delspec(np); - + /* Delete the multicasts first */ ndrv_remove_all_multicast(np); - + /* Disavow all knowledge of the ifp */ np->nd_if = NULL; np->nd_unit = 0; np->nd_family = 0; - np->nd_send_tag = 0; - + + so = np->nd_socket; /* Make sure sending returns an error */ - /* Is this safe? Will we drop the funnel? */ - socantsendmore(np->nd_socket); - socantrcvmore(np->nd_socket); + lck_mtx_assert(ndrvdomain->dom_mtx, LCK_MTX_ASSERT_OWNED); + socantsendmore(so); + socantrcvmore(so); } } - + /* Unregister our protocol */ - if (dlil_find_dltag(family, unit, PF_NDRV, &dl_tag) == 0) { - dlil_detach_protocol(dl_tag); + if (ifp) { + ifnet_detach_protocol(ifp, PF_NDRV); } } @@ -958,47 +882,51 @@ ndrv_do_add_multicast(struct ndrv_cb *np, struct sockopt *sopt) { struct ndrv_multiaddr* ndrv_multi; int result; - - if (sopt->sopt_val == NULL || sopt->sopt_valsize < 2 || - sopt->sopt_level != SOL_NDRVPROTO) + + if (sopt->sopt_val == 0 || sopt->sopt_valsize < 2 || + sopt->sopt_level != SOL_NDRVPROTO || sopt->sopt_valsize > SOCK_MAXADDRLEN) return EINVAL; if (np->nd_if == NULL) return ENXIO; - + if (!(np->nd_dlist_cnt < ndrv_multi_max_count)) + return EPERM; + // Allocate storage MALLOC(ndrv_multi, struct ndrv_multiaddr*, sizeof(struct ndrv_multiaddr) - sizeof(struct sockaddr) + sopt->sopt_valsize, M_IFADDR, M_WAITOK); if (ndrv_multi == NULL) return ENOMEM; - + // Copy in the address result = copyin(sopt->sopt_val, &ndrv_multi->addr, sopt->sopt_valsize); - + // Validate the sockaddr if (result == 0 && sopt->sopt_valsize != ndrv_multi->addr.sa_len) result = EINVAL; - + if (result == 0 && ndrv_have_multicast(np, &ndrv_multi->addr)) result = EEXIST; - + if (result == 0) { // Try adding the multicast - result = if_addmulti(np->nd_if, &ndrv_multi->addr, NULL); + result = ifnet_add_multicast(np->nd_if, &ndrv_multi->addr, + &ndrv_multi->ifma); } - + if (result == 0) { // Add to our linked list ndrv_multi->next = np->nd_multiaddrs; np->nd_multiaddrs = ndrv_multi; + np->nd_dlist_cnt++; } else { // Free up the memory, something went wrong FREE(ndrv_multi, M_IFADDR); } - + return result; } @@ -1008,46 +936,48 @@ ndrv_do_remove_multicast(struct ndrv_cb *np, struct sockopt *sopt) struct sockaddr* multi_addr; struct ndrv_multiaddr* ndrv_entry = NULL; int result; - - if (sopt->sopt_val == NULL || sopt->sopt_valsize < 2 || + + if (sopt->sopt_val == 0 || sopt->sopt_valsize < 2 || sopt->sopt_level != SOL_NDRVPROTO) return EINVAL; - if (np->nd_if == NULL) + if (np->nd_if == NULL || np->nd_dlist_cnt == 0) return ENXIO; - + // Allocate storage MALLOC(multi_addr, struct sockaddr*, sopt->sopt_valsize, M_TEMP, M_WAITOK); if (multi_addr == NULL) return ENOMEM; - + // Copy in the address result = copyin(sopt->sopt_val, multi_addr, sopt->sopt_valsize); - + // Validate the sockaddr if (result == 0 && sopt->sopt_valsize != multi_addr->sa_len) result = EINVAL; - + if (result == 0) { /* Find the old entry */ ndrv_entry = ndrv_have_multicast(np, multi_addr); - + if (ndrv_entry == NULL) result = ENOENT; } - + if (result == 0) { // Try deleting the multicast - result = if_delmulti(np->nd_if, &ndrv_entry->addr); + result = ifnet_remove_multicast(ndrv_entry->ifma); } - + if (result == 0) { // Remove from our linked list struct ndrv_multiaddr* cur = np->nd_multiaddrs; - + + ifmaddr_release(ndrv_entry->ifma); + if (cur == ndrv_entry) { np->nd_multiaddrs = cur->next; @@ -1063,12 +993,14 @@ ndrv_do_remove_multicast(struct ndrv_cb *np, struct sockopt *sopt) } } } - + + np->nd_dlist_cnt--; + // Free the memory FREE(ndrv_entry, M_IFADDR); } FREE(multi_addr, M_TEMP); - + return result; } @@ -1078,7 +1010,7 @@ ndrv_have_multicast(struct ndrv_cb *np, struct sockaddr* inAddr) struct ndrv_multiaddr* cur; for (cur = np->nd_multiaddrs; cur != NULL; cur = cur->next) { - + if ((inAddr->sa_len == cur->addr.sa_len) && (bcmp(&cur->addr, inAddr, inAddr->sa_len) == 0)) { @@ -1086,7 +1018,7 @@ ndrv_have_multicast(struct ndrv_cb *np, struct sockaddr* inAddr) return cur; } } - + return NULL; } @@ -1094,37 +1026,66 @@ static void ndrv_remove_all_multicast(struct ndrv_cb* np) { struct ndrv_multiaddr* cur; - + if (np->nd_if != NULL) { while (np->nd_multiaddrs != NULL) { cur = np->nd_multiaddrs; np->nd_multiaddrs = cur->next; - - if_delmulti(np->nd_if, &cur->addr); + + ifnet_remove_multicast(cur->ifma); + ifmaddr_release(cur->ifma); FREE(cur, M_IFADDR); } } } -struct pr_usrreqs ndrv_usrreqs = { - ndrv_abort, pru_accept_notsupp, ndrv_attach, ndrv_bind, - ndrv_connect, pru_connect2_notsupp, ndrv_control, ndrv_detach, - ndrv_disconnect, pru_listen_notsupp, ndrv_peeraddr, pru_rcvd_notsupp, - pru_rcvoob_notsupp, ndrv_send, ndrv_sense, ndrv_shutdown, - ndrv_sockaddr, sosend, soreceive, sopoll +static struct pr_usrreqs ndrv_usrreqs = { + .pru_abort = ndrv_abort, + .pru_attach = ndrv_attach, + .pru_bind = ndrv_bind, + .pru_connect = ndrv_connect, + .pru_detach = ndrv_detach, + .pru_disconnect = ndrv_disconnect, + .pru_peeraddr = ndrv_peeraddr, + .pru_send = ndrv_send, + .pru_shutdown = ndrv_shutdown, + .pru_sockaddr = ndrv_sockaddr, + .pru_sosend = sosend, + .pru_soreceive = soreceive, }; -struct protosw ndrvsw = -{ SOCK_RAW, &ndrvdomain, NDRVPROTO_NDRV, PR_ATOMIC|PR_ADDR, - 0, ndrv_output, ndrv_ctlinput, ndrv_ctloutput, - 0, ndrv_init, 0, 0, - ndrv_drain, ndrv_sysctl, &ndrv_usrreqs +static struct protosw ndrvsw[] = { +{ + .pr_type = SOCK_RAW, + .pr_protocol = NDRVPROTO_NDRV, + .pr_flags = PR_ATOMIC|PR_ADDR, + .pr_output = ndrv_output, + .pr_ctloutput = ndrv_ctloutput, + .pr_usrreqs = &ndrv_usrreqs, +} }; -struct domain ndrvdomain = -{ AF_NDRV, "NetDriver", ndrv_dominit, NULL, NULL, - NULL, - NULL, NULL, 0, 0, 0, 0 +static int ndrv_proto_count = (sizeof (ndrvsw) / sizeof (struct protosw)); + +struct domain ndrvdomain_s = { + .dom_family = PF_NDRV, + .dom_name = "NetDriver", + .dom_init = ndrv_dominit, }; + +static void +ndrv_dominit(struct domain *dp) +{ + struct protosw *pr; + int i; + + VERIFY(!(dp->dom_flags & DOM_INITIALIZED)); + VERIFY(ndrvdomain == NULL); + + ndrvdomain = dp; + + for (i = 0, pr = &ndrvsw[0]; i < ndrv_proto_count; i++, pr++) + net_add_proto(pr, dp, 1); +}