]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/net/rtsock.c
xnu-4570.51.1.tar.gz
[apple/xnu.git] / bsd / net / rtsock.c
index 2c2ae2dcdb1754ff0ba10eae08d7c65f574bbf54..dff054212f241bf07012750e647ccae0252afb12 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000-2013 Apple Inc. All rights reserved.
+ * Copyright (c) 2000-2017 Apple Inc. All rights reserved.
  *
  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
  *
@@ -75,6 +75,7 @@
 #include <sys/syslog.h>
 #include <sys/mcache.h>
 #include <kern/locks.h>
+#include <sys/codesign.h>
 
 #include <net/if.h>
 #include <net/route.h>
@@ -124,7 +125,7 @@ static int rts_shutdown(struct socket *);
 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);
@@ -145,6 +146,11 @@ SYSCTL_NODE(_net, PF_ROUTE, routetable, CTLFLAG_RD | CTLFLAG_LOCKED,
 
 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))
@@ -307,7 +313,7 @@ route_output(struct mbuf *m, struct socket *so)
        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))
@@ -420,6 +426,22 @@ route_output(struct mbuf *m, struct socket *so)
                        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.
@@ -439,7 +461,6 @@ route_output(struct mbuf *m, struct socket *so)
        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)
@@ -481,7 +502,7 @@ route_output(struct mbuf *m, struct socket *so)
                        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);
@@ -526,8 +547,18 @@ route_output(struct mbuf *m, struct socket *so)
                 */
                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);
@@ -556,36 +587,39 @@ report:
                        }
                        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]))) {
@@ -598,7 +632,7 @@ report:
                         * 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)
@@ -613,9 +647,32 @@ report:
                            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);
@@ -625,7 +682,6 @@ report:
                }
                RT_UNLOCK(rt);
                break;
-
        default:
                senderr(EOPNOTSUPP);
        }
@@ -705,41 +761,54 @@ rt_setexpire(struct rtentry *rt, uint64_t expiry)
        }
 }
 
-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
@@ -779,7 +848,7 @@ rt_setif(struct rtentry *rt, struct sockaddr *Ifpaddr, struct sockaddr *Ifaaddr,
        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);
 
@@ -983,7 +1052,7 @@ rt_msg1(int type, struct rt_addrinfo *rtinfo)
        struct rt_msghdr *rtm;
        struct mbuf *m;
        int i;
-       int len, dlen;
+       int len, dlen, off;
 
        switch (type) {
 
@@ -1004,8 +1073,6 @@ rt_msg1(int type, struct rt_addrinfo *rtinfo)
        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);
@@ -1020,6 +1087,7 @@ rt_msg1(int type, struct rt_addrinfo *rtinfo)
        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];
@@ -1048,9 +1116,10 @@ rt_msg1(int type, struct rt_addrinfo *rtinfo)
                }
 
                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);
@@ -1067,7 +1136,7 @@ rt_msg2(int type, struct rt_addrinfo *rtinfo, caddr_t cp, struct walkarg *w,
        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;
@@ -1132,7 +1201,7 @@ again:
                        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);
@@ -1143,12 +1212,15 @@ again:
                }
 
                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;
@@ -1252,7 +1324,7 @@ rt_newaddrmsg(int cmd, struct ifaddr *ifa, int error, struct rtentry *rt)
        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)
@@ -1447,8 +1519,14 @@ sysctl_dumpentry(struct radix_node *rn, void *vw)
        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))
@@ -1460,7 +1538,7 @@ sysctl_dumpentry(struct radix_node *rn, void *vw)
        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;
@@ -1476,7 +1554,7 @@ sysctl_dumpentry(struct radix_node *rn, void *vw)
                        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;
@@ -1574,7 +1652,7 @@ sysctl_iflist(int af, struct walkarg *w)
        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;
@@ -1623,6 +1701,14 @@ sysctl_iflist(int af, struct walkarg *w)
                                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)));
@@ -1716,7 +1802,7 @@ sysctl_iflist2(int af, struct walkarg *w)
        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;
@@ -1772,6 +1858,14 @@ sysctl_iflist2(int af, struct walkarg *w)
                                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)));