/*
- * Copyright (c) 2000-2013 Apple Inc. All rights reserved.
+ * Copyright (c) 2000-2017 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
#include <sys/syslog.h>
#include <sys/mcache.h>
#include <kern/locks.h>
+#include <sys/codesign.h>
#include <net/if.h>
#include <net/route.h>
static int rts_sockaddr(struct socket *, struct sockaddr **);
static int route_output(struct mbuf *, struct socket *);
-static void rt_setmetrics(u_int32_t, struct rt_metrics *, struct rtentry *);
+static int rt_setmetrics(u_int32_t, struct rt_metrics *, struct rtentry *);
static void rt_getmetrics(struct rtentry *, struct rt_metrics *);
static void rt_setif(struct rtentry *, struct sockaddr *, struct sockaddr *,
struct sockaddr *, unsigned int);
SYSCTL_NODE(_net, OID_AUTO, route, CTLFLAG_RW|CTLFLAG_LOCKED, 0, "routing");
+/* Align x to 1024 (only power of 2) assuming x is positive */
+#define ALIGN_BYTES(x) do { \
+ x = P2ALIGN(x, 1024); \
+} while(0)
+
#define ROUNDUP32(a) \
((a) > 0 ? (1 + (((a) - 1) | (sizeof (uint32_t) - 1))) : \
sizeof (uint32_t))
int sendonlytoself = 0;
unsigned int ifscope = IFSCOPE_NONE;
struct rawcb *rp = NULL;
-
+ boolean_t is_router = FALSE;
#define senderr(e) { error = (e); goto flush; }
if (m == NULL || ((m->m_len < sizeof (intptr_t)) &&
(m = m_pullup(m, sizeof (intptr_t))) == NULL))
senderr(EINVAL);
ifscope = rtm->rtm_index;
}
+ /*
+ * Block changes on INTCOPROC interfaces.
+ */
+ if (ifscope) {
+ unsigned int intcoproc_scope = 0;
+ ifnet_head_lock_shared();
+ TAILQ_FOREACH(ifp, &ifnet_head, if_link) {
+ if (IFNET_IS_INTCOPROC(ifp)) {
+ intcoproc_scope = ifp->if_index;
+ break;
+ }
+ }
+ ifnet_head_done();
+ if (intcoproc_scope == ifscope && current_proc()->p_pid != 0)
+ senderr(EINVAL);
+ }
/*
* RTF_PROXY can only be set internally from within the kernel.
if (info.rti_info[RTAX_GATEWAY] != NULL &&
info.rti_info[RTAX_GATEWAY]->sa_family == AF_INET)
sin_set_ifscope(info.rti_info[RTAX_GATEWAY], IFSCOPE_NONE);
-
switch (rtm->rtm_type) {
case RTM_ADD:
if (info.rti_info[RTAX_GATEWAY] == NULL)
rt_setif(saved_nrt,
info.rti_info[RTAX_IFP], info.rti_info[RTAX_IFA],
info.rti_info[RTAX_GATEWAY], ifscope);
- rt_setmetrics(rtm->rtm_inits, &rtm->rtm_rmx, saved_nrt);
+ (void)rt_setmetrics(rtm->rtm_inits, &rtm->rtm_rmx, saved_nrt);
saved_nrt->rt_rmx.rmx_locks &= ~(rtm->rtm_inits);
saved_nrt->rt_rmx.rmx_locks |=
(rtm->rtm_inits & rtm->rtm_rmx.rmx_locks);
*/
switch (rtm->rtm_type) {
case RTM_GET: {
+ kauth_cred_t cred;
+ kauth_cred_t* credp;
struct ifaddr *ifa2;
report:
+ cred = kauth_cred_proc_ref(current_proc());
+
+ if (rt->rt_ifp == lo_ifp ||
+ route_op_entitlement_check(so, NULL, ROUTE_OP_READ, TRUE) != 0)
+ credp = &cred;
+ else
+ credp = NULL;
+
ifa2 = NULL;
RT_LOCK_ASSERT_HELD(rt);
info.rti_info[RTAX_DST] = rt_key(rt);
}
if (ifa2 != NULL)
IFA_LOCK(ifa2);
- len = rt_msg2(rtm->rtm_type, &info, NULL, NULL, NULL);
+ len = rt_msg2(rtm->rtm_type, &info, NULL, NULL, credp);
if (ifa2 != NULL)
IFA_UNLOCK(ifa2);
- if (len > rtm->rtm_msglen) {
- struct rt_msghdr *new_rtm;
- R_Malloc(new_rtm, struct rt_msghdr *, len);
- if (new_rtm == NULL) {
- RT_UNLOCK(rt);
- if (ifa2 != NULL)
- IFA_REMREF(ifa2);
- senderr(ENOBUFS);
- }
- Bcopy(rtm, new_rtm, rtm->rtm_msglen);
- R_Free(rtm); rtm = new_rtm;
+ struct rt_msghdr *out_rtm;
+ R_Malloc(out_rtm, struct rt_msghdr *, len);
+ if (out_rtm == NULL) {
+ RT_UNLOCK(rt);
+ if (ifa2 != NULL)
+ IFA_REMREF(ifa2);
+ senderr(ENOBUFS);
}
+ Bcopy(rtm, out_rtm, sizeof(struct rt_msghdr));
if (ifa2 != NULL)
IFA_LOCK(ifa2);
- (void) rt_msg2(rtm->rtm_type, &info, (caddr_t)rtm,
- NULL, NULL);
+ (void) rt_msg2(out_rtm->rtm_type, &info, (caddr_t)out_rtm,
+ NULL, &cred);
if (ifa2 != NULL)
IFA_UNLOCK(ifa2);
+ R_Free(rtm);
+ rtm = out_rtm;
rtm->rtm_flags = rt->rt_flags;
rt_getmetrics(rt, &rtm->rtm_rmx);
rtm->rtm_addrs = info.rti_addrs;
if (ifa2 != NULL)
IFA_REMREF(ifa2);
+
+ kauth_cred_unref(&cred);
break;
}
case RTM_CHANGE:
+ is_router = (rt->rt_flags & RTF_ROUTER) ? TRUE : FALSE;
+
if (info.rti_info[RTAX_GATEWAY] != NULL &&
(error = rt_setgate(rt, rt_key(rt),
info.rti_info[RTAX_GATEWAY]))) {
* the required gateway, then just use the old one.
* This can happen if the user tries to change the
* flags on the default route without changing the
- * default gateway. Changing flags still doesn't work.
+ * default gateway. Changing flags still doesn't work.
*/
if ((rt->rt_flags & RTF_GATEWAY) &&
info.rti_info[RTAX_GATEWAY] == NULL)
info.rti_info[RTAX_IFP], info.rti_info[RTAX_IFA],
info.rti_info[RTAX_GATEWAY], ifscope);
- rt_setmetrics(rtm->rtm_inits, &rtm->rtm_rmx, rt);
+ if ((error = rt_setmetrics(rtm->rtm_inits,
+ &rtm->rtm_rmx, rt))) {
+ int tmp = error;
+ RT_UNLOCK(rt);
+ senderr(tmp);
+ }
if (info.rti_info[RTAX_GENMASK])
rt->rt_genmask = info.rti_info[RTAX_GENMASK];
+
+ /*
+ * Enqueue work item to invoke callback for this route entry
+ * This may not be needed always, but for now issue it anytime
+ * RTM_CHANGE gets called.
+ */
+ route_event_enqueue_nwk_wq_entry(rt, NULL, ROUTE_ENTRY_REFRESH, NULL, TRUE);
+ /*
+ * If the route is for a router, walk the tree to send refresh
+ * event to protocol cloned entries
+ */
+ if (is_router) {
+ struct route_event rt_ev;
+ route_event_init(&rt_ev, rt, NULL, ROUTE_ENTRY_REFRESH);
+ RT_UNLOCK(rt);
+ (void) rnh->rnh_walktree(rnh, route_event_walktree, (void *)&rt_ev);
+ RT_LOCK(rt);
+ }
/* FALLTHRU */
case RTM_LOCK:
rt->rt_rmx.rmx_locks &= ~(rtm->rtm_inits);
}
RT_UNLOCK(rt);
break;
-
default:
senderr(EOPNOTSUPP);
}
}
}
-static void
+static int
rt_setmetrics(u_int32_t which, struct rt_metrics *in, struct rtentry *out)
{
- struct timeval caltime;
-
- getmicrotime(&caltime);
-
+ if (!(which & RTV_REFRESH_HOST)) {
+ struct timeval caltime;
+ getmicrotime(&caltime);
#define metric(f, e) if (which & (f)) out->rt_rmx.e = in->e;
- metric(RTV_RPIPE, rmx_recvpipe);
- metric(RTV_SPIPE, rmx_sendpipe);
- metric(RTV_SSTHRESH, rmx_ssthresh);
- metric(RTV_RTT, rmx_rtt);
- metric(RTV_RTTVAR, rmx_rttvar);
- metric(RTV_HOPCOUNT, rmx_hopcount);
- metric(RTV_MTU, rmx_mtu);
- metric(RTV_EXPIRE, rmx_expire);
+ metric(RTV_RPIPE, rmx_recvpipe);
+ metric(RTV_SPIPE, rmx_sendpipe);
+ metric(RTV_SSTHRESH, rmx_ssthresh);
+ metric(RTV_RTT, rmx_rtt);
+ metric(RTV_RTTVAR, rmx_rttvar);
+ metric(RTV_HOPCOUNT, rmx_hopcount);
+ metric(RTV_MTU, rmx_mtu);
+ metric(RTV_EXPIRE, rmx_expire);
#undef metric
+ if (out->rt_rmx.rmx_expire > 0) {
+ /* account for system time change */
+ getmicrotime(&caltime);
+ out->base_calendartime +=
+ NET_CALCULATE_CLOCKSKEW(caltime,
+ out->base_calendartime,
+ net_uptime(), out->base_uptime);
+ rt_setexpire(out,
+ out->rt_rmx.rmx_expire -
+ out->base_calendartime +
+ out->base_uptime);
+ } else {
+ rt_setexpire(out, 0);
+ }
- if (out->rt_rmx.rmx_expire > 0) {
- /* account for system time change */
- getmicrotime(&caltime);
- out->base_calendartime +=
- NET_CALCULATE_CLOCKSKEW(caltime,
- out->base_calendartime,
- net_uptime(), out->base_uptime);
- rt_setexpire(out,
- out->rt_rmx.rmx_expire -
- out->base_calendartime +
- out->base_uptime);
+ VERIFY(out->rt_expire == 0 || out->rt_rmx.rmx_expire != 0);
+ VERIFY(out->rt_expire != 0 || out->rt_rmx.rmx_expire == 0);
} else {
- rt_setexpire(out, 0);
- }
+ /* Only RTV_REFRESH_HOST must be set */
+ if ((which & ~RTV_REFRESH_HOST) ||
+ (out->rt_flags & RTF_STATIC) ||
+ !(out->rt_flags & RTF_LLINFO)) {
+ return (EINVAL);
+ }
- VERIFY(out->rt_expire == 0 || out->rt_rmx.rmx_expire != 0);
- VERIFY(out->rt_expire != 0 || out->rt_rmx.rmx_expire == 0);
+ if (out->rt_llinfo_refresh == NULL) {
+ return (ENOTSUP);
+ }
+
+ out->rt_llinfo_refresh(out);
+ }
+ return (0);
}
static void
struct ifnet *ifp = NULL;
void (*ifa_rtrequest)(int, struct rtentry *, struct sockaddr *);
- lck_mtx_assert(rnh_lock, LCK_MTX_ASSERT_OWNED);
+ LCK_MTX_ASSERT(rnh_lock, LCK_MTX_ASSERT_OWNED);
RT_LOCK_ASSERT_HELD(rt);
struct rt_msghdr *rtm;
struct mbuf *m;
int i;
- int len, dlen;
+ int len, dlen, off;
switch (type) {
default:
len = sizeof (struct rt_msghdr);
}
- if (len > MCLBYTES)
- panic("rt_msg1");
m = m_gethdr(M_DONTWAIT, MT_DATA);
if (m && len > MHLEN) {
MCLGET(m, M_DONTWAIT);
m->m_pkthdr.rcvif = NULL;
rtm = mtod(m, struct rt_msghdr *);
bzero((caddr_t)rtm, len);
+ off = len;
for (i = 0; i < RTAX_MAX; i++) {
struct sockaddr *sa, *hint;
uint8_t ssbuf[SOCK_MAXADDRLEN + 1];
}
rtinfo->rti_addrs |= (1 << i);
- dlen = ROUNDUP32(sa->sa_len);
- m_copyback(m, len, dlen, (caddr_t)sa);
- len += dlen;
+ dlen = sa->sa_len;
+ m_copyback(m, off, dlen, (caddr_t)sa);
+ len = off + dlen;
+ off += ROUNDUP32(dlen);
}
if (m->m_pkthdr.len != len) {
m_freem(m);
kauth_cred_t* credp)
{
int i;
- int len, dlen, second_time = 0;
+ int len, dlen, rlen, second_time = 0;
caddr_t cp0;
rtinfo->rti_addrs = 0;
sa = rtm_scrub(type, i, hint, sa, &ssbuf,
sizeof (ssbuf), NULL);
break;
-
+ case RTAX_GATEWAY:
case RTAX_IFP:
sa = rtm_scrub(type, i, NULL, sa, &ssbuf,
sizeof (ssbuf), credp);
}
rtinfo->rti_addrs |= (1 << i);
- dlen = ROUNDUP32(sa->sa_len);
+ dlen = sa->sa_len;
+ rlen = ROUNDUP32(dlen);
if (cp) {
- bcopy((caddr_t)sa, cp, (unsigned)dlen);
- cp += dlen;
+ bcopy((caddr_t)sa, cp, (size_t)dlen);
+ if (dlen != rlen)
+ bzero(cp + dlen, rlen - dlen);
+ cp += rlen;
}
- len += dlen;
+ len += rlen;
}
if (cp == NULL && w != NULL && !second_time) {
struct walkarg *rw = w;
struct ifnet *ifp = ifa->ifa_ifp;
struct sockproto route_proto = { PF_ROUTE, 0 };
- lck_mtx_assert(rnh_lock, LCK_MTX_ASSERT_OWNED);
+ LCK_MTX_ASSERT(rnh_lock, LCK_MTX_ASSERT_OWNED);
RT_LOCK_ASSERT_HELD(rt);
if (route_cb.any_count == 0)
int error = 0, size;
struct rt_addrinfo info;
kauth_cred_t cred;
+ kauth_cred_t *credp;
cred = kauth_cred_proc_ref(current_proc());
+ if (rt->rt_ifp == lo_ifp ||
+ route_op_entitlement_check(NULL, cred, ROUTE_OP_READ, TRUE) != 0)
+ credp = &cred;
+ else
+ credp = NULL;
RT_LOCK(rt);
if (w->w_op == NET_RT_FLAGS && !(rt->rt_flags & w->w_arg))
info.rti_info[RTAX_GENMASK] = rt->rt_genmask;
if (w->w_op != NET_RT_DUMP2) {
- size = rt_msg2(RTM_GET, &info, NULL, w, &cred);
+ size = rt_msg2(RTM_GET, &info, NULL, w, credp);
if (w->w_req != NULL && w->w_tmem != NULL) {
struct rt_msghdr *rtm =
(struct rt_msghdr *)(void *)w->w_tmem;
error = SYSCTL_OUT(w->w_req, (caddr_t)rtm, size);
}
} else {
- size = rt_msg2(RTM_GET2, &info, NULL, w, &cred);
+ size = rt_msg2(RTM_GET2, &info, NULL, w, credp);
if (w->w_req != NULL && w->w_tmem != NULL) {
struct rt_msghdr2 *rtm =
(struct rt_msghdr2 *)(void *)w->w_tmem;
struct ifnet *ifp;
struct ifaddr *ifa;
struct rt_addrinfo info;
- int len, error = 0;
+ int len = 0, error = 0;
int pass = 0;
int total_len = 0, current_len = 0;
char *total_buffer = NULL, *cp = NULL;
if_data_internal_to_if_data(ifp, &ifp->if_data,
&ifm->ifm_data);
ifm->ifm_addrs = info.rti_addrs;
+ /*
+ * <rdar://problem/32940901>
+ * Round bytes only for non-platform
+ */
+ if (!csproc_get_platform_binary(w->w_req->p)) {
+ ALIGN_BYTES(ifm->ifm_data.ifi_ibytes);
+ ALIGN_BYTES(ifm->ifm_data.ifi_obytes);
+ }
cp += len;
VERIFY(IS_P2ALIGNED(cp, sizeof (u_int32_t)));
struct ifnet *ifp;
struct ifaddr *ifa;
struct rt_addrinfo info;
- int len, error = 0;
+ int len = 0, error = 0;
int pass = 0;
int total_len = 0, current_len = 0;
char *total_buffer = NULL, *cp = NULL;
ifm->ifm_timer = ifp->if_timer;
if_data_internal_to_if_data64(ifp,
&ifp->if_data, &ifm->ifm_data);
+ /*
+ * <rdar://problem/32940901>
+ * Round bytes only for non-platform
+ */
+ if (!csproc_get_platform_binary(w->w_req->p)) {
+ ALIGN_BYTES(ifm->ifm_data.ifi_ibytes);
+ ALIGN_BYTES(ifm->ifm_data.ifi_obytes);
+ }
cp += len;
VERIFY(IS_P2ALIGNED(cp, sizeof (u_int32_t)));