/*
- * Copyright (c) 1997-2007 Apple Inc. All rights reserved.
+ * Copyright (c) 1997-2014 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
*
* 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 <mach/mach_types.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/ioctl.h>
+#include <sys/sysctl.h>
#include <sys/errno.h>
#include <sys/syslog.h>
#include <sys/proc.h>
#include <machine/spl.h>
+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.
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 */
}
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;
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;
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);
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);
}
}
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;
}
/* 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);
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;
*cp = "0123456789"[n % 10];
n /= 10;
} while (n != 0 && buflen > 0);
- strncpy(buf, cp, IFNAMSIZ-buflen);
- return (cp);
+ strlcpy(buf, cp, IFNAMSIZ-buflen);
+ return;
}
/*
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
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))
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 */
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;
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;
}
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;
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;
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);
}
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) -
// Add to our linked list
ndrv_multi->next = np->nd_multiaddrs;
np->nd_multiaddrs = ndrv_multi;
+ np->nd_dlist_cnt++;
}
else
{
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
}
}
+ np->nd_dlist_cnt--;
+
// Free the memory
FREE(ndrv_entry, M_IFADDR);
}
}
}
-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);
+}