X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/2d21ac55c334faf3a56e5634905ed6987fc787d4..2dced7af2b695f87fe26496a3e73c219b7880cbc:/bsd/net/ndrv.c diff --git a/bsd/net/ndrv.c b/bsd/net/ndrv.c index adc642bc3..34d7504b9 100644 --- a/bsd/net/ndrv.c +++ b/bsd/net/ndrv.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-2007 Apple Inc. All rights reserved. + * Copyright (c) 1997-2014 Apple Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * @@ -43,8 +43,7 @@ * * 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 @@ -58,6 +57,7 @@ #include #include #include +#include #include #include #include @@ -80,26 +80,31 @@ #include +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_long protocol_family); +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_long family, short unit); +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(void) __attribute__((section("__TEXT, initcode"))); +static void ndrv_dominit(struct domain *); -unsigned long ndrv_sendspace = NDRVSNDQ; -unsigned long ndrv_recvspace = NDRVRCVQ; +u_int32_t ndrv_sendspace = NDRVSNDQ; +u_int32_t ndrv_recvspace = NDRVRCVQ; TAILQ_HEAD(, ndrv_cb) ndrvl = TAILQ_HEAD_INITIALIZER(ndrvl); -extern struct domain ndrvdomain; -extern struct protosw ndrvsw; -extern lck_mtx_t *domain_proto_mtx; +static struct domain *ndrvdomain = NULL; +extern struct domain ndrvdomain_s; + +#define NDRV_PROTODEMUX_COUNT 10 /* * Verify these values match. @@ -189,13 +194,13 @@ ndrv_input( return EJUSTRETURN; bcopy(frame_header, m->m_data, ifnet_hdrlen(ifp)); - lck_mtx_assert(so->so_proto->pr_domain->dom_mtx, LCK_MTX_ASSERT_NOTOWNED); - lck_mtx_lock(so->so_proto->pr_domain->dom_mtx); + 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, &error) != 0) { sorwakeup(so); } - lck_mtx_unlock(so->so_proto->pr_domain->dom_mtx); + lck_mtx_unlock(ndrvdomain->dom_mtx); return 0; /* radar 4030377 - always return 0 */ } @@ -229,7 +234,7 @@ ndrv_attach(struct socket *so, int proto, __unused struct proc *p) 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_proto_family = 0; @@ -268,7 +273,6 @@ static int ndrv_connect(struct socket *so, struct sockaddr *nam, __unused struct proc *p) { struct ndrv_cb *np = sotondrvcb(so); - int result = 0; if (np == 0) return EINVAL; @@ -276,13 +280,11 @@ ndrv_connect(struct socket *so, struct sockaddr *nam, __unused struct proc *p) 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); @@ -297,10 +299,10 @@ ndrv_event(struct ifnet *ifp, __unused protocol_family_t protocol, 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); + 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); + lck_mtx_unlock(ndrvdomain->dom_mtx); } } @@ -404,7 +406,7 @@ ndrv_disconnect(struct socket *so) static int ndrv_shutdown(struct socket *so) { - lck_mtx_assert(so->so_proto->pr_domain->dom_mtx, LCK_MTX_ASSERT_OWNED); + lck_mtx_assert(ndrvdomain->dom_mtx, LCK_MTX_ASSERT_OWNED); socantsendmore(so); return 0; } @@ -543,7 +545,7 @@ ndrv_do_detach(struct ndrv_cb *np) /* Remove from the linked list of control blocks */ TAILQ_REMOVE(&ndrvl, np, nd_next); if (ifp != NULL) { - u_long proto_family = np->nd_proto_family; + u_int32_t proto_family = np->nd_proto_family; if (proto_family != PF_NDRV && proto_family != 0) { socket_unlock(so, 0); @@ -589,14 +591,19 @@ ndrv_do_disconnect(struct ndrv_cb *np) FREE(np->nd_faddr, M_IFADDR); np->nd_faddr = 0; } - if (so->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(so); return(0); } /* Hackery - return a string version of a decimal number */ -static char * +static void sprint_d(u_int n, char *buf, int buflen) { char dbuf[IFNAMSIZ]; char *cp = dbuf+IFNAMSIZ-1; @@ -607,8 +614,8 @@ sprint_d(u_int n, char *buf, int buflen) *cp = "0123456789"[n % 10]; n /= 10; } while (n != 0 && buflen > 0); - strncpy(buf, cp, IFNAMSIZ-buflen); - return (cp); + strlcpy(buf, cp, IFNAMSIZ-buflen); + return; } /* @@ -622,9 +629,9 @@ static int name_cmp(struct ifnet *ifp, char *q) r = buf; len = strlen(ifnet_name(ifp)); - strncpy(r, ifnet_name(ifp), IFNAMSIZ); + strlcpy(r, ifnet_name(ifp), IFNAMSIZ); r += len; - (void)sprint_d(ifnet_unit(ifp), r, IFNAMSIZ-(r-buf)); + sprint_d(ifnet_unit(ifp), r, IFNAMSIZ-(r-buf)); #if NDRV_DEBUG kprintf("Comparing %s, %s\n", buf, q); #endif @@ -669,7 +676,7 @@ ndrv_setspec(struct ndrv_cb *np, struct sockopt *sopt) return EINVAL; /* Copy the ndrvSpec */ - if (proc_is64bit(current_proc())) { + if (proc_is64bit(sopt->sopt_p)) { struct ndrv_protocol_desc64 ndrvSpec64; if (sopt->sopt_valsize != sizeof(ndrvSpec64)) @@ -686,14 +693,20 @@ ndrv_setspec(struct ndrv_cb *np, struct sockopt *sopt) user_addr = ndrvSpec64.demux_list; } else { - if (sopt->sopt_valsize != sizeof(ndrvSpec)) + struct ndrv_protocol_desc32 ndrvSpec32; + + if (sopt->sopt_valsize != sizeof(ndrvSpec32)) return EINVAL; - error = sooptcopyin(sopt, &ndrvSpec, sizeof(ndrvSpec), sizeof(ndrvSpec)); + error = sooptcopyin(sopt, &ndrvSpec32, sizeof(ndrvSpec32), sizeof(ndrvSpec32)); if (error != 0) return error; - user_addr = CAST_USER_ADDR_T(ndrvSpec.demux_list); + 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 */ @@ -701,6 +714,8 @@ ndrv_setspec(struct ndrv_cb *np, struct sockopt *sopt) 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; @@ -729,7 +744,7 @@ ndrv_setspec(struct ndrv_cb *np, struct sockopt *sopt) if (error == 0) { /* At this point, we've at least got enough bytes to start looking around */ - u_long demuxOn = 0; + u_int32_t demuxOn = 0; proto_param.demux_count = ndrvSpec.demux_count; proto_param.input = ndrv_input; @@ -806,7 +821,7 @@ ndrv_delspec(struct ndrv_cb *np) } struct ndrv_cb * -ndrv_find_inbound(struct ifnet *ifp, u_long protocol) +ndrv_find_inbound(struct ifnet *ifp, u_int32_t protocol) { struct ndrv_cb* np; @@ -822,17 +837,8 @@ ndrv_find_inbound(struct ifnet *ifp, u_long protocol) return NULL; } -static void ndrv_dominit(void) -{ - static int ndrv_dominited = 0; - - if (ndrv_dominited == 0 && - net_add_proto(&ndrvsw, &ndrvdomain) == 0) - ndrv_dominited = 1; -} - static void -ndrv_handle_ifp_detach(u_long family, short unit) +ndrv_handle_ifp_detach(u_int32_t family, short unit) { struct ndrv_cb* np; struct ifnet *ifp = NULL; @@ -859,8 +865,7 @@ ndrv_handle_ifp_detach(u_long family, short unit) so = np->nd_socket; /* Make sure sending returns an error */ - /* Is this safe? Will we drop the funnel? */ - lck_mtx_assert(so->so_proto->pr_domain->dom_mtx, LCK_MTX_ASSERT_OWNED); + lck_mtx_assert(ndrvdomain->dom_mtx, LCK_MTX_ASSERT_OWNED); socantsendmore(so); socantrcvmore(so); } @@ -879,10 +884,12 @@ ndrv_do_add_multicast(struct ndrv_cb *np, struct sockopt *sopt) int result; if (sopt->sopt_val == 0 || sopt->sopt_valsize < 2 || - sopt->sopt_level != SOL_NDRVPROTO) + 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) - @@ -912,6 +919,7 @@ ndrv_do_add_multicast(struct ndrv_cb *np, struct sockopt *sopt) // Add to our linked list ndrv_multi->next = np->nd_multiaddrs; np->nd_multiaddrs = ndrv_multi; + np->nd_dlist_cnt++; } else { @@ -932,7 +940,7 @@ ndrv_do_remove_multicast(struct ndrv_cb *np, struct sockopt *sopt) 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 @@ -986,6 +994,8 @@ ndrv_do_remove_multicast(struct ndrv_cb *np, struct sockopt *sopt) } } + np->nd_dlist_cnt--; + // Free the memory FREE(ndrv_entry, M_IFADDR); } @@ -1031,39 +1041,51 @@ ndrv_remove_all_multicast(struct ndrv_cb* np) } } -struct pr_usrreqs ndrv_usrreqs = { - ndrv_abort, pru_accept_notsupp, ndrv_attach, ndrv_bind, - ndrv_connect, pru_connect2_notsupp, pru_control_notsupp, ndrv_detach, - ndrv_disconnect, pru_listen_notsupp, ndrv_peeraddr, pru_rcvd_notsupp, - pru_rcvoob_notsupp, ndrv_send, pru_sense_null, ndrv_shutdown, - ndrv_sockaddr, sosend, soreceive, pru_sopoll_notsupp +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, - NULL, ndrv_output, NULL, ndrv_ctloutput, - NULL, - NULL, NULL, NULL, NULL, NULL, - &ndrv_usrreqs, - NULL, NULL, NULL, - { NULL, NULL}, NULL, - { 0 } +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, - NULL, - 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); +}