#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/proc.h>
+#include <sys/kauth.h>
#include <net/if.h>
#include <net/route.h>
#endif
#include <netkey/key.h>
extern int ipsec_bypass;
+extern lck_mtx_t *sadb_mutex;
+extern lck_mtx_t *nd6_mutex;
#endif /* IPSEC */
#include <netinet6/ip6_fw.h>
#include <net/net_osdep.h>
+#include <netinet/kpi_ipfilter_var.h>
+
#ifndef __APPLE__
static MALLOC_DEFINE(M_IPMOPTS, "ip6_moptions", "internet multicast options");
#endif
-static u_long lo_dl_tag = 0;
extern u_long route_generation;
-
struct ip6_exthdrs {
struct mbuf *ip6e_ip6;
struct mbuf *ip6e_hbh;
struct mbuf *ip6e_dest2;
};
-static int ip6_pcbopts __P((struct ip6_pktopts **, struct mbuf *,
- struct socket *, struct sockopt *sopt));
-static int ip6_setmoptions __P((int, struct inpcb *, struct mbuf *));
-static int ip6_getmoptions __P((int, struct ip6_moptions *, struct mbuf **));
-static int ip6_copyexthdr __P((struct mbuf **, caddr_t, int));
-static int ip6_insertfraghdr __P((struct mbuf *, struct mbuf *, int,
- struct ip6_frag **));
-static int ip6_insert_jumboopt __P((struct ip6_exthdrs *, u_int32_t));
-static int ip6_splithdr __P((struct mbuf *, struct ip6_exthdrs *));
+static int ip6_pcbopts(struct ip6_pktopts **, struct mbuf *,
+ struct socket *, struct sockopt *sopt);
+static int ip6_setmoptions(int, struct inpcb *, struct mbuf *);
+static int ip6_getmoptions(int, struct ip6_moptions *, struct mbuf **);
+static int ip6_copyexthdr(struct mbuf **, caddr_t, int);
+static int ip6_insertfraghdr(struct mbuf *, struct mbuf *, int,
+ struct ip6_frag **);
+static int ip6_insert_jumboopt(struct ip6_exthdrs *, u_int32_t);
+static int ip6_splithdr(struct mbuf *, struct ip6_exthdrs *);
extern int ip_createmoptions(struct ip_moptions **imop);
extern int ip_addmembership(struct ip_moptions *imo, struct ip_mreq *mreq);
extern int ip_dropmembership(struct ip_moptions *imo, struct ip_mreq *mreq);
+extern lck_mtx_t *ip6_mutex;
/*
* IP6 output. The packet in mbuf chain m contains a skeletal IP6
* which is rt_rmx.rmx_mtu.
*/
int
-ip6_output(m0, opt, ro, flags, im6o, ifpp)
- struct mbuf *m0;
- struct ip6_pktopts *opt;
- struct route_in6 *ro;
- int flags;
- struct ip6_moptions *im6o;
- struct ifnet **ifpp; /* XXX: just for statistics */
+ip6_output(
+ struct mbuf *m0,
+ struct ip6_pktopts *opt,
+ struct route_in6 *ro,
+ int flags,
+ struct ip6_moptions *im6o,
+ struct ifnet **ifpp, /* XXX: just for statistics */
+ int locked)
{
struct ip6_hdr *ip6, *mhip6;
struct ifnet *ifp, *origifp;
struct route_in6 *ro_pmtu = NULL;
int hdrsplit = 0;
int needipsec = 0;
+ ipfilter_t inject_filter_ref;
+
#if IPSEC
int needipsectun = 0;
struct socket *so = NULL;
struct secpolicy *sp = NULL;
+ if (!locked)
+ lck_mtx_lock(ip6_mutex);
/* for AH processing. stupid to have "socket" variable in IP layer... */
if (ipsec_bypass == 0)
{
so = ipsec_getsocket(m);
(void)ipsec_setsocket(m, NULL);
}
- ip6 = mtod(m, struct ip6_hdr *);
#endif /* IPSEC */
+ ip6 = mtod(m, struct ip6_hdr *);
+ inject_filter_ref = ipf_get_inject_filter(m);
+
#define MAKE_EXTHDR(hp, mp) \
do { \
if (hp) { \
#if IPSEC
if (ipsec_bypass != 0)
goto skip_ipsec;
-
+
+ lck_mtx_lock(sadb_mutex);
/* get a security policy for this packet */
if (so == NULL)
sp = ipsec6_getpolicybyaddr(m, IPSEC_DIR_OUTBOUND, 0, &error);
if (sp == NULL) {
ipsec6stat.out_inval++;
+ lck_mtx_unlock(sadb_mutex);
goto freehdrs;
}
* This packet is just discarded.
*/
ipsec6stat.out_polvio++;
+ lck_mtx_unlock(sadb_mutex);
goto freehdrs;
case IPSEC_POLICY_BYPASS:
if (sp->req == NULL) {
/* acquire a policy */
error = key_spdacquire(sp);
+ lck_mtx_unlock(sadb_mutex);
goto freehdrs;
}
needipsec = 1;
default:
printf("ip6_output: Invalid policy found. %d\n", sp->policy);
}
+ lck_mtx_unlock(sadb_mutex);
skip_ipsec:
#endif /* IPSEC */
MAKE_CHAIN(exthdrs.ip6e_rthdr, mprev,
nexthdrp, IPPROTO_ROUTING);
+ if (!TAILQ_EMPTY(&ipv6_filters)) {
+ struct ipfilter *filter;
+ int seen = (inject_filter_ref == 0);
+ int fixscope = 0;
+ struct ipf_pktopts *ippo = 0, ipf_pktopts;
+
+ if (im6o != NULL && IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
+ ippo = &ipf_pktopts;
+ ippo->ippo_flags = IPPOF_MCAST_OPTS;
+ ippo->ippo_mcast_ifnet = im6o->im6o_multicast_ifp;
+ ippo->ippo_mcast_ttl = im6o->im6o_multicast_hlim;
+ ippo->ippo_mcast_loop = im6o->im6o_multicast_loop;
+ }
+
+ /* Hack: embed the scope_id in the destination */
+ if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst) &&
+ (ip6->ip6_dst.s6_addr16[1] == 0) && (ro != NULL)) {
+ fixscope = 1;
+ ip6->ip6_dst.s6_addr16[1] = htons(ro->ro_dst.sin6_scope_id);
+ }
+ {
+ lck_mtx_unlock(ip6_mutex);
+ ipf_ref();
+ TAILQ_FOREACH(filter, &ipv6_filters, ipf_link) {
+ /*
+ * No need to proccess packet twice if we've
+ * already seen it
+ */
+ if (seen == 0) {
+ if ((struct ipfilter *)inject_filter_ref == filter)
+ seen = 1;
+ } else if (filter->ipf_filter.ipf_output) {
+ errno_t result;
+
+ result = filter->ipf_filter.ipf_output(filter->ipf_filter.cookie, (mbuf_t*)&m, ippo);
+ if (result == EJUSTRETURN) {
+ ipf_unref();
+ locked = 1; /* Don't want to take lock to unlock it right away */
+ goto done;
+ }
+ if (result != 0) {
+ ipf_unref();
+ locked = 1; /* Don't want to take lock to unlock it right away */
+ goto bad;
+ }
+ }
+ }
+ ipf_unref();
+ lck_mtx_lock(ip6_mutex);
+ }
+ /* Hack: cleanup embedded scope_id if we put it there */
+ if (fixscope)
+ ip6->ip6_dst.s6_addr16[1] = 0;
+ }
+
#if IPSEC
if (!needipsec)
goto skip_ipsec2;
bzero(&state, sizeof(state));
state.m = m;
+ lck_mtx_unlock(ip6_mutex);
+ lck_mtx_lock(sadb_mutex);
error = ipsec6_output_trans(&state, nexthdrp, mprev, sp, flags,
&needipsectun);
+ lck_mtx_unlock(sadb_mutex);
+ lck_mtx_lock(ip6_mutex);
m = state.m;
if (error) {
/* mbuf is already reclaimed in ipsec6_output_trans. */
state.m = m;
state.ro = (struct route *)ro;
state.dst = (struct sockaddr *)dst;
-
+ lck_mtx_unlock(ip6_mutex);
+ lck_mtx_lock(sadb_mutex);
error = ipsec6_output_tunnel(&state, sp, flags);
-
+ lck_mtx_unlock(sadb_mutex);
+ lck_mtx_lock(ip6_mutex);
m = state.m;
ro = (struct route_in6 *)state.ro;
dst = (struct sockaddr_in6 *)state.dst;
error = 0;
break;
}
+ lck_mtx_unlock(sadb_mutex);
goto bad;
}
* if an interface is specified from an upper layer,
* ifp must point it.
*/
+ lck_mtx_lock(rt_mtx);
if (ro->ro_rt == 0) {
/*
* non-bsdi always clone routes, if parent is
* PRF_CLONING.
*/
- rtalloc((struct route *)ro);
+ rtalloc_ign_locked((struct route *)ro, 0UL);
}
if (ro->ro_rt == 0) {
ip6stat.ip6s_noroute++;
error = EHOSTUNREACH;
+ lck_mtx_unlock(rt_mtx);
/* XXX in6_ifstat_inc(ifp, ifs6_out_discard); */
goto bad;
}
ro->ro_rt->rt_use++;
if (ro->ro_rt->rt_flags & RTF_GATEWAY)
dst = (struct sockaddr_in6 *)ro->ro_rt->rt_gateway;
+ lck_mtx_unlock(rt_mtx);
m->m_flags &= ~(M_BCAST | M_MCAST); /* just in case */
in6_ifstat_inc(ifp, ifs6_out_request);
* ``net'' ff00::/8).
*/
if (ifp == NULL) {
+ lck_mtx_lock(rt_mtx);
if (ro->ro_rt == 0) {
- ro->ro_rt = rtalloc1((struct sockaddr *)
+ ro->ro_rt = rtalloc1_locked((struct sockaddr *)
&ro->ro_dst, 0, 0UL);
}
if (ro->ro_rt == 0) {
ip6stat.ip6s_noroute++;
+ lck_mtx_unlock(rt_mtx);
error = EHOSTUNREACH;
/* XXX in6_ifstat_inc(ifp, ifs6_out_discard) */
goto bad;
ia = ifatoia6(ro->ro_rt->rt_ifa);
ifp = ro->ro_rt->rt_ifp;
ro->ro_rt->rt_use++;
+ lck_mtx_unlock(rt_mtx);
}
if ((flags & IPV6_FORWARDING) == 0)
error = ENETUNREACH;
goto bad;
}
+ ifnet_lock_shared(ifp);
IN6_LOOKUP_MULTI(ip6->ip6_dst, ifp, in6m);
+ ifnet_lock_done(ifp);
if (in6m != NULL &&
(im6o == NULL || im6o->im6o_multicast_loop)) {
/*
u_short port = 0;
m->m_pkthdr.rcvif = NULL; /* XXX */
/* If ipfw says divert, we have to just drop packet */
- if ((*ip6_fw_chk_ptr)(&ip6, ifp, &port, &m)) {
+ if (ip6_fw_chk_ptr(&ip6, ifp, &port, &m)) {
m_freem(m);
goto done;
}
ipsec_delaux(m);
#endif
- error = nd6_output(ifp, origifp, m, dst, ro->ro_rt);
+ error = nd6_output(ifp, origifp, m, dst, ro->ro_rt, 1);
goto done;
} else if (mtu < IPV6_MMTU) {
/*
}
m_cat(m, m_frgpart);
m->m_pkthdr.len = len + hlen + sizeof(*ip6f);
- m->m_pkthdr.rcvif = (struct ifnet *)0;
+ m->m_pkthdr.rcvif = 0;
+ m->m_pkthdr.socket_id = m0->m_pkthdr.socket_id;
ip6f->ip6f_reserved = 0;
ip6f->ip6f_ident = id;
ip6f->ip6f_nxt = nextproto;
/* clean ipsec history once it goes out of the node */
ipsec_delaux(m);
#endif
- error = nd6_output(ifp, origifp, m, dst, ro->ro_rt);
+ error = nd6_output(ifp, origifp, m, dst, ro->ro_rt, 1);
+
} else
m_freem(m);
}
ip6stat.ip6s_fragmented++;
done:
+ if (!locked)
+ lck_mtx_unlock(ip6_mutex);
if (ro == &ip6route && ro->ro_rt) { /* brace necessary for rtfree */
rtfree(ro->ro_rt);
} else if (ro_pmtu == &ip6route && ro_pmtu->ro_rt) {
}
#if IPSEC
- if (sp != NULL)
+ if (sp != NULL) {
+ lck_mtx_lock(sadb_mutex);
key_freesp(sp);
+ lck_mtx_unlock(sadb_mutex);
+ }
#endif /* IPSEC */
return(error);
int optlen;
struct proc *p;
+ level = error = optval = 0;
if (sopt == NULL)
panic("ip6_ctloutput: arg soopt is NULL");
else {
optlen = sopt->sopt_valsize;
p = sopt->sopt_p;
}
- error = optval = 0;
- privileged = (p == 0 || suser(p->p_ucred, &p->p_acflag)) ? 0 : 1;
+ privileged = (p == 0 || proc_suser(p)) ? 0 : 1;
if (level == IPPROTO_IPV6) {
switch (op) {
{
struct mbuf *m;
+ if (sopt->sopt_valsize > MCLBYTES) {
+ error = EMSGSIZE;
+ break;
+ }
error = soopt_getm(sopt, &m); /* XXX */
if (error != NULL)
break;
size_t len = 0;
struct mbuf *m;
+ if (sopt->sopt_valsize > MCLBYTES) {
+ error = EMSGSIZE;
+ break;
+ }
if ((error = soopt_getm(sopt, &m)) != 0) /* XXX */
break;
if ((error = soopt_mcopyin(sopt, m)) != 0) /* XXX */
req = mtod(m, caddr_t);
len = m->m_len;
}
+ lck_mtx_lock(sadb_mutex);
error = ipsec6_set_policy(in6p, optname, req,
len, privileged);
+ lck_mtx_unlock(sadb_mutex);
m_freem(m);
}
break;
case IPV6_HOPOPTS:
case IPV6_RTHDR:
case IPV6_DSTOPTS:
- if (optname == IPV6_HOPOPTS ||
- optname == IPV6_DSTOPTS ||
+ if ((optname == IPV6_HOPOPTS ||
+ optname == IPV6_DSTOPTS) &&
!privileged)
return(EPERM);
switch (optname) {
struct mbuf *m = NULL;
struct mbuf **mp = &m;
+ if (sopt->sopt_valsize > MCLBYTES) {
+ error = EMSGSIZE;
+ break;
+ }
error = soopt_getm(sopt, &m); /* XXX */
if (error != NULL)
break;
req = mtod(m, caddr_t);
len = m->m_len;
}
+ lck_mtx_lock(sadb_mutex);
error = ipsec6_get_policy(in6p, req, len, mp);
+ lck_mtx_unlock(sadb_mutex);
if (error == 0)
error = soopt_mcopyout(sopt, m); /*XXX*/
if (error == 0 && m)
}
/* set options specified by user. */
- if (p && !suser(p->p_ucred, &p->p_acflag))
+ if (p && !proc_suser(p))
priv = 1;
if ((error = ip6_setpktoptions(m, opt, priv, 1)) != 0) {
ip6_clearpktopts(opt, 1, -1); /* XXX: discard all options */
* Set the IP6 multicast options in response to user setsockopt().
*/
static int
-ip6_setmoptions(optname, in6p, m)
- int optname;
- struct inpcb* in6p;
- struct mbuf *m;
+ip6_setmoptions(
+ int optname,
+ struct inpcb* in6p,
+ struct mbuf *m)
{
int error = 0;
u_int loop, ifindex;
* all multicast addresses. Only super user is allowed
* to do this.
*/
- if (suser(p->p_ucred, &p->p_acflag))
+ if (suser(kauth_cred_get(), 0))
{
error = EACCES;
break;
ifp = ifindex2ifnet[mreq->ipv6mr_interface];
+ lck_mtx_lock(rt_mtx);
TAILQ_FOREACH(ifa, &in_ifaddrhead, ia_link) {
if (ifa->ia_ifp == ifp) {
v4req.imr_interface = IA_SIN(ifa)->sin_addr;
break;
}
}
+ lck_mtx_unlock(rt_mtx);
if (v4req.imr_multiaddr.s_addr == 0) {
/* Interface has no IPv4 address. */
/*
* See if the membership already exists.
*/
+ lck_mtx_lock(nd6_mutex);
for (imm = im6o->im6o_memberships.lh_first;
imm != NULL; imm = imm->i6mm_chain.le_next)
if (imm->i6mm_maddr->in6m_ifp == ifp &&
break;
if (imm != NULL) {
error = EADDRINUSE;
+ lck_mtx_unlock(nd6_mutex);
break;
}
/*
imm = _MALLOC(sizeof(*imm), M_IPMADDR, M_WAITOK);
if (imm == NULL) {
error = ENOBUFS;
+ lck_mtx_unlock(nd6_mutex);
break;
}
if ((imm->i6mm_maddr =
- in6_addmulti(&mreq->ipv6mr_multiaddr, ifp, &error)) == NULL) {
+ in6_addmulti(&mreq->ipv6mr_multiaddr, ifp, &error, 1)) == NULL) {
FREE(imm, M_IPMADDR);
+ lck_mtx_unlock(nd6_mutex);
break;
}
LIST_INSERT_HEAD(&im6o->im6o_memberships, imm, i6mm_chain);
+ lck_mtx_unlock(nd6_mutex);
break;
case IPV6_LEAVE_GROUP:
ifp = ifindex2ifnet[mreq->ipv6mr_interface];
if (IN6_IS_ADDR_UNSPECIFIED(&mreq->ipv6mr_multiaddr)) {
- if (suser(p->p_ucred, &p->p_acflag)) {
+ if (suser(kauth_cred_get(), 0)) {
error = EACCES;
break;
}
if (ifp != NULL) {
struct in_ifaddr *ifa;
+ lck_mtx_lock(rt_mtx);
TAILQ_FOREACH(ifa, &in_ifaddrhead, ia_link) {
if (ifa->ia_ifp == ifp) {
v4req.imr_interface = IA_SIN(ifa)->sin_addr;
break;
}
}
+ lck_mtx_unlock(rt_mtx);
}
error = ip_dropmembership(imo, &v4req);
/*
* Find the membership in the membership list.
*/
+ lck_mtx_lock(nd6_mutex);
for (imm = im6o->im6o_memberships.lh_first;
imm != NULL; imm = imm->i6mm_chain.le_next) {
if ((ifp == NULL ||
if (imm == NULL) {
/* Unable to resolve interface */
error = EADDRNOTAVAIL;
+ lck_mtx_unlock(nd6_mutex);
break;
}
/*
* membership points.
*/
LIST_REMOVE(imm, i6mm_chain);
- in6_delmulti(imm->i6mm_maddr);
+ in6_delmulti(imm->i6mm_maddr, 1);
+ lck_mtx_unlock(nd6_mutex);
FREE(imm, M_IPMADDR);
break;
/*
* If all options have default values, no need to keep the mbuf.
*/
+ lck_mtx_lock(nd6_mutex);
if (im6o->im6o_multicast_ifp == NULL &&
im6o->im6o_multicast_hlim == ip6_defmcasthlim &&
im6o->im6o_multicast_loop == IPV6_DEFAULT_MULTICAST_LOOP &&
ip_freemoptions(imo);
in6p->inp_moptions = 0;
}
+ lck_mtx_unlock(nd6_mutex);
return(error);
}
if (im6o == NULL)
return;
-
+
+ lck_mtx_lock(nd6_mutex);
while ((imm = im6o->im6o_memberships.lh_first) != NULL) {
LIST_REMOVE(imm, i6mm_chain);
if (imm->i6mm_maddr)
- in6_delmulti(imm->i6mm_maddr);
+ in6_delmulti(imm->i6mm_maddr, 1);
FREE(imm, M_IPMADDR);
}
+ lck_mtx_unlock(nd6_mutex);
FREE(im6o, M_IPMOPTS);
}
ia6 = (struct in6_ifaddr *)ifa_ifwithaddr(sin6tosa(&sin6));
if (ia6 == NULL ||
(ia6->ia6_flags & (IN6_IFF_ANYCAST |
- IN6_IFF_NOTREADY)) != 0)
+ IN6_IFF_NOTREADY)) != 0) {
+ if (ia6) ifafree(&ia6->ia_ifa);
return(EADDRNOTAVAIL);
+ }
+ ifafree(&ia6->ia_ifa);
+ ia6 = NULL;
}
break;
* pointer that might NOT be &loif -- easier than replicating that code here.
*/
void
-ip6_mloopback(ifp, m, dst)
- struct ifnet *ifp;
- struct mbuf *m;
- struct sockaddr_in6 *dst;
+ip6_mloopback(
+ struct ifnet *ifp,
+ struct mbuf *m,
+ struct sockaddr_in6 *dst)
{
struct mbuf *copym;
struct ip6_hdr *ip6;
/* Makes sure the HW checksum flags are cleaned before sending the packet */
- copym->m_pkthdr.rcvif = (struct ifnet *)0;
+ copym->m_pkthdr.rcvif = 0;
copym->m_pkthdr.csum_data = 0;
copym->m_pkthdr.csum_flags = 0;
- if (lo_dl_tag == 0)
- dlil_find_dltag(APPLE_IF_FAM_LOOPBACK, 0, PF_INET, &lo_dl_tag);
-
- if (lo_dl_tag) {
+ if (lo_ifp) {
copym->m_pkthdr.rcvif = ifp;
- dlil_output(lo_dl_tag, copym, 0, (struct sockaddr *)dst, 0);
+ lck_mtx_unlock(ip6_mutex);
+ dlil_output(lo_ifp, PF_INET6, copym, 0, (struct sockaddr *)dst, 0);
+ lck_mtx_lock(ip6_mutex);
} else
m_free(copym);
#else