/*
- * Copyright (c) 2000-2008 Apple Inc. All rights reserved.
+ * Copyright (c) 2000-2011 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
#endif
#include <sys/kernel.h>
#include <sys/sysctl.h>
+#include <sys/mcache.h>
+#include <sys/kauth.h>
+#include <sys/priv.h>
#include <libkern/OSAtomic.h>
#include <machine/limits.h>
SYSCTL_NODE(_net_inet_ip, IPPROTO_IP, portrange, CTLFLAG_RW|CTLFLAG_LOCKED, 0, "IP Ports");
-SYSCTL_PROC(_net_inet_ip_portrange, OID_AUTO, lowfirst, CTLTYPE_INT|CTLFLAG_RW,
+SYSCTL_PROC(_net_inet_ip_portrange, OID_AUTO, lowfirst, CTLTYPE_INT|CTLFLAG_RW | CTLFLAG_LOCKED,
&ipport_lowfirstauto, 0, &sysctl_net_ipport_check, "I", "");
-SYSCTL_PROC(_net_inet_ip_portrange, OID_AUTO, lowlast, CTLTYPE_INT|CTLFLAG_RW,
+SYSCTL_PROC(_net_inet_ip_portrange, OID_AUTO, lowlast, CTLTYPE_INT|CTLFLAG_RW | CTLFLAG_LOCKED,
&ipport_lowlastauto, 0, &sysctl_net_ipport_check, "I", "");
-SYSCTL_PROC(_net_inet_ip_portrange, OID_AUTO, first, CTLTYPE_INT|CTLFLAG_RW,
+SYSCTL_PROC(_net_inet_ip_portrange, OID_AUTO, first, CTLTYPE_INT|CTLFLAG_RW | CTLFLAG_LOCKED,
&ipport_firstauto, 0, &sysctl_net_ipport_check, "I", "");
-SYSCTL_PROC(_net_inet_ip_portrange, OID_AUTO, last, CTLTYPE_INT|CTLFLAG_RW,
+SYSCTL_PROC(_net_inet_ip_portrange, OID_AUTO, last, CTLTYPE_INT|CTLFLAG_RW | CTLFLAG_LOCKED,
&ipport_lastauto, 0, &sysctl_net_ipport_check, "I", "");
-SYSCTL_PROC(_net_inet_ip_portrange, OID_AUTO, hifirst, CTLTYPE_INT|CTLFLAG_RW,
+SYSCTL_PROC(_net_inet_ip_portrange, OID_AUTO, hifirst, CTLTYPE_INT|CTLFLAG_RW | CTLFLAG_LOCKED,
&ipport_hifirstauto, 0, &sysctl_net_ipport_check, "I", "");
-SYSCTL_PROC(_net_inet_ip_portrange, OID_AUTO, hilast, CTLTYPE_INT|CTLFLAG_RW,
+SYSCTL_PROC(_net_inet_ip_portrange, OID_AUTO, hilast, CTLTYPE_INT|CTLFLAG_RW | CTLFLAG_LOCKED,
&ipport_hilastauto, 0, &sysctl_net_ipport_check, "I", "");
extern int udp_use_randomport;
}
mac_inpcb_label_associate(so, inp);
#endif
+ // make sure inp_stat is always 64bit aligned
+ inp->inp_stat = (struct inp_stat*)P2ROUNDUP(inp->inp_stat_store, sizeof(u_int64_t));
+ if (((uintptr_t)inp->inp_stat - (uintptr_t)inp->inp_stat_store)
+ + sizeof(*inp->inp_stat) > sizeof(inp->inp_stat_store)) {
+ panic("insufficient space to align inp_stat");
+ }
+
so->so_pcb = (caddr_t)inp;
if (so->so_proto->pr_flags & PR_PCBLOCK) {
- inp->inpcb_mtx = lck_mtx_alloc_init(pcbinfo->mtx_grp, pcbinfo->mtx_attr);
- if (inp->inpcb_mtx == NULL) {
- printf("in_pcballoc: can't alloc mutex! so=%p\n", so);
- return(ENOMEM);
- }
+ lck_mtx_init(&inp->inpcb_mtx, pcbinfo->mtx_grp, pcbinfo->mtx_attr);
}
#if IPSEC
if (inp && inp->inp_wantcnt == WNT_STOPUSING) {
struct socket *so = inp->inp_socket;
- lck_mtx_lock(inp->inpcb_mtx);
+ lck_mtx_lock(&inp->inpcb_mtx);
if (so->so_usecount == 0) {
if (inp->inp_state != INPCB_STATE_DEAD)
inp = NULL;
}
else {
- lck_mtx_unlock(inp->inpcb_mtx);
+ lck_mtx_unlock(&inp->inpcb_mtx);
}
}
struct kev_msg ev_msg;
struct kev_in_portinuse in_portinuse;
+ bzero(&in_portinuse, sizeof(struct kev_in_portinuse));
+ bzero(&ev_msg, sizeof(struct kev_msg));
in_portinuse.port = ntohs(port); /* port in host order */
in_portinuse.req_pid = proc_selfpid();
ev_msg.vendor_code = KEV_VENDOR_APPLE;
* EACCES Permission denied
* EADDRINUSE Address in use
* EAGAIN Resource unavailable, try again
- * proc_suser:EPERM Operation not permitted
+ * priv_check_cred:EPERM Operation not permitted
*/
int
in_pcbbind(struct inpcb *inp, struct sockaddr *nam, struct proc *p)
u_short lport = 0, rand_port = 0;
int wild = 0, reuseport = (so->so_options & SO_REUSEPORT);
int error, randomport, conflict = 0;
+ kauth_cred_t cred;
if (TAILQ_EMPTY(&in_ifaddrhead)) /* XXX broken! */
return (EADDRNOTAVAIL);
socket_unlock(so, 0); /* keep reference on socket */
lck_rw_lock_exclusive(pcbinfo->mtx);
if (nam) {
+ unsigned int outif = 0;
+
sin = (struct sockaddr_in *)nam;
if (nam->sa_len != sizeof (*sin)) {
lck_rw_done(pcbinfo->mtx);
return (EADDRNOTAVAIL);
}
else {
- ifafree(ifa);
+ IFA_LOCK(ifa);
+ outif = ifa->ifa_ifp->if_index;
+ IFA_UNLOCK(ifa);
+ IFA_REMREF(ifa);
}
}
if (lport) {
/* GROSS */
#if !CONFIG_EMBEDDED
- if (ntohs(lport) < IPPORT_RESERVED && proc_suser(p)) {
- lck_rw_done(pcbinfo->mtx);
- socket_lock(so, 0);
- return (EACCES);
+ if (ntohs(lport) < IPPORT_RESERVED) {
+ cred = kauth_cred_proc_ref(p);
+ error = priv_check_cred(cred, PRIV_NETINET_RESERVEDPORT, 0);
+ kauth_cred_unref(&cred);
+ if (error != 0) {
+ lck_rw_done(pcbinfo->mtx);
+ socket_lock(so, 0);
+ return (EACCES);
+ }
}
#endif
if (so->so_uid &&
}
}
inp->inp_laddr = sin->sin_addr;
+ inp->inp_last_outif = outif;
}
if (lport == 0) {
u_short first, last;
last = ipport_hilastauto;
lastport = &pcbinfo->lasthi;
} else if (inp->inp_flags & INP_LOWPORT) {
- if ((error = proc_suser(p)) != 0) {
+ cred = kauth_cred_proc_ref(p);
+ error = priv_check_cred(cred, PRIV_NETINET_RESERVEDPORT, 0);
+ kauth_cred_unref(&cred);
+ if (error != 0) {
lck_rw_done(pcbinfo->mtx);
socket_lock(so, 0);
return error;
lck_rw_done(pcbinfo->mtx);
socket_lock(so, 0);
inp->inp_laddr.s_addr = INADDR_ANY;
+ inp->inp_last_outif = 0;
return (EADDRNOTAVAIL);
}
--*lastport;
lck_rw_done(pcbinfo->mtx);
socket_lock(so, 0);
inp->inp_laddr.s_addr = INADDR_ANY;
+ inp->inp_last_outif = 0;
return (EADDRNOTAVAIL);
}
++*lastport;
if (in_pcbinshash(inp, 1) != 0) {
inp->inp_laddr.s_addr = INADDR_ANY;
inp->inp_lport = 0;
+ inp->inp_last_outif = 0;
lck_rw_done(pcbinfo->mtx);
return (EAGAIN);
}
*/
int
in_pcbladdr(struct inpcb *inp, struct sockaddr *nam,
- struct sockaddr_in **plocal_sin)
+ struct sockaddr_in *plocal_sin, unsigned int *out_ifscope)
{
struct in_ifaddr *ia;
struct sockaddr_in *sin = (struct sockaddr_in *)nam;
lck_rw_lock_shared(in_ifaddr_rwlock);
if (!TAILQ_EMPTY(&in_ifaddrhead)) {
+ ia = TAILQ_FIRST(&in_ifaddrhead);
/*
* If the destination address is INADDR_ANY,
* use the primary local address.
#define satosin(sa) ((struct sockaddr_in *)(sa))
#define sintosa(sin) ((struct sockaddr *)(sin))
#define ifatoia(ifa) ((struct in_ifaddr *)(ifa))
+ IFA_LOCK_SPIN(&ia->ia_ifa);
if (sin->sin_addr.s_addr == INADDR_ANY)
- sin->sin_addr = IA_SIN(TAILQ_FIRST(&in_ifaddrhead))->sin_addr;
+ sin->sin_addr = IA_SIN(ia)->sin_addr;
else if (sin->sin_addr.s_addr == (u_int32_t)INADDR_BROADCAST &&
- (TAILQ_FIRST(&in_ifaddrhead)->ia_ifp->if_flags & IFF_BROADCAST))
- sin->sin_addr = satosin(&TAILQ_FIRST(&in_ifaddrhead)->ia_broadaddr)->sin_addr;
+ (ia->ia_ifp->if_flags & IFF_BROADCAST))
+ sin->sin_addr = satosin(&ia->ia_broadaddr)->sin_addr;
+ IFA_UNLOCK(&ia->ia_ifa);
+ ia = NULL;
}
lck_rw_done(in_ifaddr_rwlock);
if (inp->inp_laddr.s_addr == INADDR_ANY) {
struct route *ro;
- unsigned int ifscope;
-
+ unsigned int ifscope = IFSCOPE_NONE;
+ unsigned int nocell;
+ /*
+ * If the socket is bound to a specifc interface, the
+ * optional scoped takes precedence over that if it
+ * is set by the caller.
+ */
ia = (struct in_ifaddr *)0;
- ifscope = (inp->inp_flags & INP_BOUND_IF) ?
- inp->inp_boundif : IFSCOPE_NONE;
+
+ if (out_ifscope != NULL && *out_ifscope != IFSCOPE_NONE)
+ ifscope = *out_ifscope;
+ else if (inp->inp_flags & INP_BOUND_IF)
+ ifscope = inp->inp_boundif;
+
+ nocell = (inp->inp_flags & INP_NO_IFT_CELLULAR) ? 1 : 0;
/*
* If route is known or can be allocated now,
* our src addr is taken from the i/f, else punt.
ro->ro_dst.sa_len = sizeof(struct sockaddr_in);
((struct sockaddr_in *) &ro->ro_dst)->sin_addr =
sin->sin_addr;
- rtalloc_scoped_ign(ro, 0, ifscope);
+ rtalloc_scoped(ro, ifscope);
if (ro->ro_rt != NULL)
RT_LOCK_SPIN(ro->ro_rt);
}
+ /*
+ * If the route points to a cellular interface and the
+ * caller forbids our using interfaces of such type,
+ * pretend that there is no route.
+ */
+ if (nocell && ro->ro_rt != NULL) {
+ RT_LOCK_ASSERT_HELD(ro->ro_rt);
+ if (ro->ro_rt->rt_ifp->if_type == IFT_CELLULAR) {
+ RT_UNLOCK(ro->ro_rt);
+ rtfree(ro->ro_rt);
+ ro->ro_rt = NULL;
+ }
+ }
/*
* If we found a route, use the address
* corresponding to the outgoing interface
* to our address on another net goes to loopback).
*/
if (ro->ro_rt != NULL) {
- RT_LOCK_ASSERT_HELD(ro->ro_rt);
+ /* Become a regular mutex */
+ RT_CONVERT_LOCK(ro->ro_rt);
if (!(ro->ro_rt->rt_ifp->if_flags & IFF_LOOPBACK)) {
ia = ifatoia(ro->ro_rt->rt_ifa);
- if (ia)
- ifaref(&ia->ia_ifa);
+ if (ia) {
+ IFA_ADDREF(&ia->ia_ifa);
+ }
}
RT_UNLOCK(ro->ro_rt);
}
lck_rw_lock_shared(in_ifaddr_rwlock);
ia = TAILQ_FIRST(&in_ifaddrhead);
if (ia)
- ifaref(&ia->ia_ifa);
+ IFA_ADDREF(&ia->ia_ifa);
lck_rw_done(in_ifaddr_rwlock);
}
+ /*
+ * If the source address belongs to a cellular interface
+ * and the socket forbids our using interfaces of such
+ * type, pretend that there is no source address.
+ */
+ if (nocell && ia != NULL &&
+ ia->ia_ifa.ifa_ifp->if_type == IFT_CELLULAR) {
+ IFA_REMREF(&ia->ia_ifa);
+ ia = NULL;
+ }
if (ia == 0)
return (EADDRNOTAVAIL);
}
struct ifnet *ifp;
imo = inp->inp_moptions;
+ IMO_LOCK(imo);
if (imo->imo_multicast_ifp != NULL && (ia == NULL ||
ia->ia_ifp != imo->imo_multicast_ifp)) {
ifp = imo->imo_multicast_ifp;
if (ia)
- ifafree(&ia->ia_ifa);
+ IFA_REMREF(&ia->ia_ifa);
lck_rw_lock_shared(in_ifaddr_rwlock);
TAILQ_FOREACH(ia, &in_ifaddrhead, ia_link) {
if (ia->ia_ifp == ifp)
break;
}
if (ia)
- ifaref(&ia->ia_ifa);
+ IFA_ADDREF(&ia->ia_ifa);
lck_rw_done(in_ifaddr_rwlock);
- if (ia == 0)
+ if (ia == 0) {
+ IMO_UNLOCK(imo);
return (EADDRNOTAVAIL);
+ }
}
+ IMO_UNLOCK(imo);
}
/*
* Don't do pcblookup call here; return interface in plocal_sin
* and exit to caller, that will do the lookup.
*/
- *plocal_sin = &ia->ia_addr;
- ifafree(&ia->ia_ifa);
+ IFA_LOCK_SPIN(&ia->ia_ifa);
+ *plocal_sin = ia->ia_addr;
+ if (out_ifscope != NULL)
+ *out_ifscope = ia->ia_ifp->if_index;
+ IFA_UNLOCK(&ia->ia_ifa);
+ IFA_REMREF(&ia->ia_ifa);
}
return(0);
}
* then pick one.
*/
int
-in_pcbconnect(struct inpcb *inp, struct sockaddr *nam, struct proc *p)
+in_pcbconnect(struct inpcb *inp, struct sockaddr *nam, struct proc *p, unsigned int *ifscope)
{
- struct sockaddr_in *ifaddr;
+ struct sockaddr_in ifaddr;
struct sockaddr_in *sin = (struct sockaddr_in *)nam;
struct inpcb *pcb;
int error;
/*
* Call inner routine, to assign local interface address.
*/
- if ((error = in_pcbladdr(inp, nam, &ifaddr)) != 0)
+ if ((error = in_pcbladdr(inp, nam, &ifaddr, ifscope)) != 0)
return(error);
socket_unlock(inp->inp_socket, 0);
pcb = in_pcblookup_hash(inp->inp_pcbinfo, sin->sin_addr, sin->sin_port,
- inp->inp_laddr.s_addr ? inp->inp_laddr : ifaddr->sin_addr,
+ inp->inp_laddr.s_addr ? inp->inp_laddr : ifaddr.sin_addr,
inp->inp_lport, 0, NULL);
socket_lock(inp->inp_socket, 0);
+
+ /* Check if the socket is still in a valid state. When we unlock this
+ * embryonic socket, it can get aborted if another thread is closing
+ * the listener (radar 7947600).
+ */
+ if ((inp->inp_socket->so_flags & SOF_ABORTED) != 0) {
+ return ECONNREFUSED;
+ }
+
if (pcb != NULL) {
in_pcb_checkstate(pcb, WNT_RELEASE, pcb == inp ? 1 : 0);
return (EADDRINUSE);
lck_rw_lock_exclusive(inp->inp_pcbinfo->mtx);
socket_lock(inp->inp_socket, 0);
}
- inp->inp_laddr = ifaddr->sin_addr;
+ inp->inp_laddr = ifaddr.sin_addr;
+ inp->inp_last_outif = ifscope ? *ifscope : IFSCOPE_NONE;
inp->inp_flags |= INP_INADDR_ANY;
}
else {
#endif
if ((so->so_flags & SOF_PCBCLEARING) == 0) {
struct rtentry *rt;
+ struct ip_moptions *imo;
inp->inp_vflag = 0;
if (inp->inp_options)
inp->inp_route.ro_rt = NULL;
rtfree(rt);
}
- ip_freemoptions(inp->inp_moptions);
+ imo = inp->inp_moptions;
inp->inp_moptions = NULL;
+ if (imo != NULL)
+ IMO_REMREF(imo);
sofreelastref(so, 0);
inp->inp_state = INPCB_STATE_DEAD;
so->so_flags |= SOF_PCBCLEARING; /* makes sure we're not called twice from so_close */
printf("in_pcbdispose: not dead yet? so=%p\n", so);
}
#endif
-
if (so && so->so_usecount != 0)
- panic("in_pcbdispose: use count=%x so=%p\n", so->so_usecount, so);
+ panic("%s: so %p so_usecount %d so_lockhistory %s\n",
+ __func__, so, so->so_usecount,
+ (so != NULL) ? solockhistory_nr(so) : "--");
lck_rw_assert(ipi->mtx, LCK_RW_ASSERT_EXCLUSIVE);
}
if (so->so_head != NULL)
panic("in_pcbdispose, so=%p head still exist\n", so);
- lck_mtx_unlock(inp->inpcb_mtx);
- lck_mtx_free(inp->inpcb_mtx, ipi->mtx_grp);
+ lck_mtx_unlock(&inp->inpcb_mtx);
+ lck_mtx_destroy(&inp->inpcb_mtx, ipi->mtx_grp);
}
so->so_flags |= SOF_PCBCLEARING; /* makes sure we're not called twice from so_close */
so->so_saved_pcb = (caddr_t) inp;
if ((ia = ifa_foraddr(inp->inp_laddr.s_addr)) != NULL) {
inp->inp_route.ro_rt = NULL;
rtfree(rt);
- ifafree(&ia->ia_ifa);
+ IFA_REMREF(&ia->ia_ifa);
}
/*
* A new route can be allocated
if ((ia = ifa_foraddr(inp->inp_laddr.s_addr)) == NULL) {
return; /* we can't remove the route now. not sure if still ok to use src */
}
- ifafree(&ia->ia_ifa);
+ IFA_REMREF(&ia->ia_ifa);
rtfree(rt);
inp->inp_route.ro_rt = NULL;
/*
}
}
+/*
+ * Check if PCB exists in hash list.
+ */
+int
+in_pcblookup_hash_exists(
+ struct inpcbinfo *pcbinfo,
+ struct in_addr faddr,
+ u_int fport_arg,
+ struct in_addr laddr,
+ u_int lport_arg,
+ int wildcard,
+ uid_t *uid,
+ gid_t *gid,
+ __unused struct ifnet *ifp)
+{
+ struct inpcbhead *head;
+ struct inpcb *inp;
+ u_short fport = fport_arg, lport = lport_arg;
+ int found;
+
+ *uid = UID_MAX;
+ *gid = GID_MAX;
+
+ /*
+ * We may have found the pcb in the last lookup - check this first.
+ */
+
+ lck_rw_lock_shared(pcbinfo->mtx);
+
+ /*
+ * First look for an exact match.
+ */
+ head = &pcbinfo->hashbase[INP_PCBHASH(faddr.s_addr, lport, fport,
+ pcbinfo->hashmask)];
+ LIST_FOREACH(inp, head, inp_hash) {
+#if INET6
+ if ((inp->inp_vflag & INP_IPV4) == 0)
+ continue;
+#endif
+ if (inp->inp_faddr.s_addr == faddr.s_addr &&
+ inp->inp_laddr.s_addr == laddr.s_addr &&
+ inp->inp_fport == fport &&
+ inp->inp_lport == lport) {
+ if ((found = (inp->inp_socket != NULL))) {
+ /*
+ * Found.
+ */
+ *uid = inp->inp_socket->so_uid;
+ *gid = inp->inp_socket->so_gid;
+ }
+ lck_rw_done(pcbinfo->mtx);
+ return (found);
+ }
+ }
+ if (wildcard) {
+ struct inpcb *local_wild = NULL;
+#if INET6
+ struct inpcb *local_wild_mapped = NULL;
+#endif
+
+ head = &pcbinfo->hashbase[INP_PCBHASH(INADDR_ANY, lport, 0,
+ pcbinfo->hashmask)];
+ LIST_FOREACH(inp, head, inp_hash) {
+#if INET6
+ if ((inp->inp_vflag & INP_IPV4) == 0)
+ continue;
+#endif
+ if (inp->inp_faddr.s_addr == INADDR_ANY &&
+ inp->inp_lport == lport) {
+#if defined(NFAITH) && NFAITH > 0
+ if (ifp && ifp->if_type == IFT_FAITH &&
+ (inp->inp_flags & INP_FAITH) == 0)
+ continue;
+#endif
+ if (inp->inp_laddr.s_addr == laddr.s_addr) {
+ if ((found = (inp->inp_socket != NULL))) {
+ *uid = inp->inp_socket->so_uid;
+ *gid = inp->inp_socket->so_gid;
+ }
+ lck_rw_done(pcbinfo->mtx);
+ return (found);
+ }
+ else if (inp->inp_laddr.s_addr == INADDR_ANY) {
+#if INET6
+ if (inp->inp_socket &&
+ INP_CHECK_SOCKAF(inp->inp_socket,
+ AF_INET6))
+ local_wild_mapped = inp;
+ else
+#endif /* INET6 */
+ local_wild = inp;
+ }
+ }
+ }
+ if (local_wild == NULL) {
+#if INET6
+ if (local_wild_mapped != NULL) {
+ if ((found = (local_wild_mapped->inp_socket != NULL))) {
+ *uid = local_wild_mapped->inp_socket->so_uid;
+ *gid = local_wild_mapped->inp_socket->so_gid;
+ }
+ lck_rw_done(pcbinfo->mtx);
+ return (found);
+ }
+#endif /* INET6 */
+ lck_rw_done(pcbinfo->mtx);
+ return (0);
+ }
+ if (local_wild != NULL) {
+ if ((found = (local_wild->inp_socket != NULL))) {
+ *uid = local_wild->inp_socket->so_uid;
+ *gid = local_wild->inp_socket->so_gid;
+ }
+ lck_rw_done(pcbinfo->mtx);
+ return (found);
+ }
+ }
+
+ /*
+ * Not found.
+ */
+ lck_rw_done(pcbinfo->mtx);
+ return (0);
+}
+
/*
* Lookup PCB in hash list.
*/
if (!locked) {
if (!lck_rw_try_lock_exclusive(pcbinfo->mtx)) {
- /*lock inversion issue, mostly with udp multicast packets */
+ /*lock inversion issue, mostly with udp multicast packets */
socket_unlock(inp->inp_socket, 0);
lck_rw_lock_exclusive(pcbinfo->mtx);
socket_lock(inp->inp_socket, 0);
+ if (inp->inp_state == INPCB_STATE_DEAD) {
+ /* The socket got dropped when it was unlocked */
+ lck_rw_done(pcbinfo->mtx);
+ return(ECONNABORTED);
+ }
}
}
if (locked == 0)
socket_lock(pcb->inp_socket, 1);
pcb->inp_state = INPCB_STATE_DEAD;
+
stopusing:
if (pcb->inp_socket->so_usecount < 0)
panic("in_pcb_checkstate STOP pcb=%p so=%p usecount is negative\n", pcb, pcb->inp_socket);
struct inpcb *inp,
struct xinpcb64 *xinp)
{
- xinp->inp_fport = inp->inp_fport;
- xinp->inp_lport = inp->inp_lport;
- xinp->inp_gencnt = inp->inp_gencnt;
- xinp->inp_flags = inp->inp_flags;
- xinp->inp_flow = inp->inp_flow;
- xinp->inp_vflag = inp->inp_vflag;
- xinp->inp_ip_ttl = inp->inp_ip_ttl;
- xinp->inp_ip_p = inp->inp_ip_p;
- xinp->inp_dependfaddr.inp6_foreign = inp->inp_dependfaddr.inp6_foreign;
- xinp->inp_dependladdr.inp6_local = inp->inp_dependladdr.inp6_local;
- xinp->inp_depend4.inp4_ip_tos = inp->inp_depend4.inp4_ip_tos;
- xinp->inp_depend6.inp6_hlim = inp->inp_depend6.inp6_hlim;
- xinp->inp_depend6.inp6_cksum = inp->inp_depend6.inp6_cksum;
+ xinp->inp_fport = inp->inp_fport;
+ xinp->inp_lport = inp->inp_lport;
+ xinp->inp_gencnt = inp->inp_gencnt;
+ xinp->inp_flags = inp->inp_flags;
+ xinp->inp_flow = inp->inp_flow;
+ xinp->inp_vflag = inp->inp_vflag;
+ xinp->inp_ip_ttl = inp->inp_ip_ttl;
+ xinp->inp_ip_p = inp->inp_ip_p;
+ xinp->inp_dependfaddr.inp6_foreign = inp->inp_dependfaddr.inp6_foreign;
+ xinp->inp_dependladdr.inp6_local = inp->inp_dependladdr.inp6_local;
+ xinp->inp_depend4.inp4_ip_tos = inp->inp_depend4.inp4_ip_tos;
+ xinp->inp_depend6.inp6_hlim = inp->inp_depend6.inp6_hlim;
+ xinp->inp_depend6.inp6_cksum = inp->inp_depend6.inp6_cksum;
xinp->inp_depend6.inp6_ifindex = inp->inp_depend6.inp6_ifindex;
- xinp->inp_depend6.inp6_hops = inp->inp_depend6.inp6_hops;
+ xinp->inp_depend6.inp6_hops = inp->inp_depend6.inp6_hops;
}
#endif /* !CONFIG_EMBEDDED */
+
/*
* The following routines implement this scheme:
*
{
struct route *src = &inp->inp_route;
- lck_mtx_assert(inp->inpcb_mtx, LCK_MTX_ASSERT_OWNED);
+ lck_mtx_assert(&inp->inpcb_mtx, LCK_MTX_ASSERT_OWNED);
/*
* If the route in the PCB is not for IPv4, blow it away;
rtfree(src->ro_rt);
src->ro_rt = NULL;
}
-
- /* Copy everything (rt, dst, flags) from PCB */
- bcopy(src, dst, sizeof (*dst));
-
- /* Hold one reference for the local copy of struct route */
- if (dst->ro_rt != NULL)
- RT_ADDREF(dst->ro_rt);
+
+ route_copyout(dst, src, sizeof(*dst));
}
void
{
struct route *dst = &inp->inp_route;
- lck_mtx_assert(inp->inpcb_mtx, LCK_MTX_ASSERT_OWNED);
+ lck_mtx_assert(&inp->inpcb_mtx, LCK_MTX_ASSERT_OWNED);
/* Minor sanity check */
if (src->ro_rt != NULL && rt_key(src->ro_rt)->sa_family != AF_INET)
panic("%s: wrong or corrupted route: %p", __func__, src);
- /* No cached route in the PCB? */
- if (dst->ro_rt == NULL) {
- /*
- * Copy everything (rt, dst, flags) from ip_output();
- * the reference to the route was held at the time
- * it was allocated and is kept intact.
- */
- bcopy(src, dst, sizeof (*dst));
- } else if (src->ro_rt != NULL) {
- /*
- * If the same, update just the ro_flags and ditch the one
- * in the local copy. Else ditch the one that is currently
- * cached, and cache what we got back from ip_output().
- */
- if (dst->ro_rt == src->ro_rt) {
- dst->ro_flags = src->ro_flags;
- rtfree(src->ro_rt);
- src->ro_rt = NULL;
- } else {
- rtfree(dst->ro_rt);
- bcopy(src, dst, sizeof (*dst));
- }
+ route_copyin(src, dst, sizeof(*src));
+}
+
+/*
+ * Handler for setting IP_FORCE_OUT_IFP/IP_BOUND_IF/IPV6_BOUND_IF socket option.
+ */
+void
+inp_bindif(struct inpcb *inp, unsigned int ifscope)
+{
+ /*
+ * A zero interface scope value indicates an "unbind".
+ * Otherwise, take in whatever value the app desires;
+ * the app may already know the scope (or force itself
+ * to such a scope) ahead of time before the interface
+ * gets attached. It doesn't matter either way; any
+ * route lookup from this point on will require an
+ * exact match for the embedded interface scope.
+ */
+ inp->inp_boundif = ifscope;
+ if (inp->inp_boundif == IFSCOPE_NONE)
+ inp->inp_flags &= ~INP_BOUND_IF;
+ else
+ inp->inp_flags |= INP_BOUND_IF;
+
+ /* Blow away any cached route in the PCB */
+ if (inp->inp_route.ro_rt != NULL) {
+ rtfree(inp->inp_route.ro_rt);
+ inp->inp_route.ro_rt = NULL;
+ }
+}
+
+/*
+ * Handler for setting IP_NO_IFT_CELLULAR/IPV6_NO_IFT_CELLULAR socket option.
+ */
+int
+inp_nocellular(struct inpcb *inp, unsigned int val)
+{
+ if (val) {
+ inp->inp_flags |= INP_NO_IFT_CELLULAR;
+ } else if (inp->inp_flags & INP_NO_IFT_CELLULAR) {
+ /* once set, it cannot be unset */
+ return (EINVAL);
}
+
+ /* Blow away any cached route in the PCB */
+ if (inp->inp_route.ro_rt != NULL) {
+ rtfree(inp->inp_route.ro_rt);
+ inp->inp_route.ro_rt = NULL;
+ }
+
+ return (0);
}