]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/netinet/raw_ip.c
xnu-3789.70.16.tar.gz
[apple/xnu.git] / bsd / netinet / raw_ip.c
index 89d1a5f3a28db6a9995c877593edc48cfdbb5ae9..1f7ccb2275b5789fa97487811661ac884fbef7ae 100644 (file)
@@ -1,8 +1,8 @@
 /*
- * Copyright (c) 2000-2013 Apple Inc. All rights reserved.
+ * Copyright (c) 2000-2016 Apple Inc. All rights reserved.
  *
  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
- * 
+ *
  * This file contains Original Code and/or Modifications of Original Code
  * as defined in and that are subject to the Apple Public Source License
  * Version 2.0 (the 'License'). You may not use this file except in
  * unlawful or unlicensed copies of an Apple operating system, or to
  * circumvent, violate, or enable the circumvention or violation of, any
  * terms of an Apple operating system software license agreement.
- * 
+ *
  * Please obtain a copy of the License at
  * http://www.opensource.apple.com/apsl/ and read it before using this file.
- * 
+ *
  * The Original Code and all software distributed under the License are
  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
@@ -22,7 +22,7 @@
  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
  * Please see the License for the specific language governing rights and
  * limitations under the License.
- * 
+ *
  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
  */
 /*
 #define _IP_VHL
 #include <netinet/in.h>
 #include <netinet/in_systm.h>
+#include <netinet/in_tclass.h>
 #include <netinet/ip.h>
 #include <netinet/in_pcb.h>
 #include <netinet/in_var.h>
 #include <netinet/ip_var.h>
-#include <netinet/ip_mroute.h>
 
 #if INET6
 #include <netinet6/in6_pcb.h>
@@ -120,10 +120,6 @@ int rip_disconnect(struct socket *);
 int rip_bind(struct socket *, struct sockaddr *, struct proc *);
 int rip_connect(struct socket *, struct sockaddr *, struct proc *);
 int rip_shutdown(struct socket *);
-#if IPSEC
-extern int ipsec_bypass;
-#endif
 
 struct inpcbhead ripcb;
 struct inpcbinfo ripcbinfo;
@@ -202,9 +198,7 @@ static struct       sockaddr_in ripsrc = { sizeof(ripsrc), AF_INET , 0, {0}, {0,0,0,0,
  * mbuf chain.
  */
 void
-rip_input(m, iphlen)
-       struct mbuf *m;
-       int iphlen;
+rip_input(struct mbuf *m, int iphlen)
 {
        struct ip *ip = mtod(m, struct ip *);
        struct inpcb *inp;
@@ -231,29 +225,21 @@ rip_input(m, iphlen)
                if (inp->inp_faddr.s_addr &&
                   inp->inp_faddr.s_addr != ip->ip_src.s_addr)
                        continue;
-
-               if (inp_restricted(inp, ifp))
-                       continue;
-
-               if (ifp != NULL && IFNET_IS_CELLULAR(ifp) &&
-                   (inp->inp_flags & INP_NO_IFT_CELLULAR))
+               if (inp_restricted_recv(inp, ifp))
                        continue;
-
                if (last) {
                        struct mbuf *n = m_copy(m, 0, (int)M_COPYALL);
-               
+
                        skipit = 0;
-#if IPSEC
-                       /* check AH/ESP integrity. */
-                       if (ipsec_bypass == 0 && n) {
-                               if (ipsec4_in_reject_so(n, last->inp_socket)) {
-                                       m_freem(n);
-                                       IPSEC_STAT_INCREMENT(ipsecstat.in_polvio);
-                                       /* do not inject data to pcb */
-                                       skipit = 1;
-                               }
-                       } 
-#endif /*IPSEC*/
+
+#if NECP
+                       if (n && !necp_socket_is_allowed_to_send_recv_v4(last, 0, 0,
+                               &ip->ip_dst, &ip->ip_src, ifp, NULL, NULL)) {
+                               m_freem(n);
+                               /* do not inject data to pcb */
+                               skipit = 1;
+                       }
+#endif /* NECP */
 #if CONFIG_MACF_NET
                        if (n && skipit == 0) {
                                if (mac_inpcb_check_deliver(last, n, AF_INET,
@@ -289,7 +275,7 @@ rip_input(m, iphlen)
                                } else {
                                        if (error) {
                                                /* should notify about lost packet */
-                                               kprintf("rip_input can't append to socket\n");
+                                               ipstat.ips_raw_sappend_fail++;
                                        }
                                }
                                opts = 0;
@@ -299,18 +285,15 @@ rip_input(m, iphlen)
        }
 
        skipit = 0;
-#if IPSEC
-       /* check AH/ESP integrity. */
-       if (ipsec_bypass == 0 && last) {
-               if (ipsec4_in_reject_so(m, last->inp_socket)) {
-                       m_freem(m);
-                       IPSEC_STAT_INCREMENT(ipsecstat.in_polvio);
-                       OSAddAtomic(1, &ipstat.ips_delivered);
-                       /* do not inject data to pcb */
-                       skipit = 1;
-               }
-       } 
-#endif /*IPSEC*/
+#if NECP
+       if (last && !necp_socket_is_allowed_to_send_recv_v4(last, 0, 0,
+               &ip->ip_dst, &ip->ip_src, ifp, NULL, NULL)) {
+               m_freem(m);
+               OSAddAtomic(1, &ipstat.ips_delivered);
+               /* do not inject data to pcb */
+               skipit = 1;
+       }
+#endif /* NECP */
 #if CONFIG_MACF_NET
        if (last && skipit == 0) {
                if (mac_inpcb_check_deliver(last, m, AF_INET, SOCK_RAW) != 0) {
@@ -328,7 +311,7 @@ rip_input(m, iphlen)
                                if (ret != 0) {
                                        m_freem(m);
                                        m_freem(opts);
-                                       goto unlock;            
+                                       goto unlock;
                                }
                        }
                        if (last->inp_flags & INP_STRIPHDR) {
@@ -341,7 +324,7 @@ rip_input(m, iphlen)
                                (struct sockaddr *)&ripsrc, m, opts, NULL) != 0) {
                                sorwakeup(last->inp_socket);
                        } else {
-                               kprintf("rip_input(2) can't append to socket\n");
+                               ipstat.ips_raw_sappend_fail++;
                        }
                } else {
                        m_freem(m);
@@ -351,7 +334,7 @@ rip_input(m, iphlen)
        }
 unlock:
        /*
-        * Keep the list locked because socket filter may force the socket lock 
+        * Keep the list locked because socket filter may force the socket lock
         * to be released when calling sbappendaddr() -- see rdar://7627704
         */
        lck_rw_done(ripcbinfo.ipi_lock);
@@ -372,19 +355,28 @@ rip_output(
        struct inpcb *inp = sotoinpcb(so);
        int flags = (so->so_options & SO_DONTROUTE) | IP_ALLOWBROADCAST;
        struct ip_out_args ipoa =
-           { IFSCOPE_NONE, { 0 }, IPOAF_SELECT_SRCIF, 0 };
+           { IFSCOPE_NONE, { 0 }, IPOAF_SELECT_SRCIF, 0, 0, 0 };
        struct ip_moptions *imo;
        int error = 0;
-       mbuf_svc_class_t msc = MBUF_SC_UNSPEC;
+       int sotc = SO_TC_UNSPEC;
+       int netsvctype = _NET_SERVICE_TYPE_UNSPEC;
 
        if (control != NULL) {
-               msc = mbuf_service_class_from_control(control);
+               sotc = so_tc_from_control(control, &netsvctype);
 
                m_freem(control);
                control = NULL;
        }
+       if (sotc == SO_TC_UNSPEC) {
+               sotc = so->so_traffic_class;
+               netsvctype = so->so_netsvctype;
+       }
 
-       if (inp == NULL || (inp->inp_flags2 & INP2_WANT_FLOW_DIVERT)) {
+       if (inp == NULL
+#if NECP
+               || (necp_socket_should_use_flow_divert(inp))
+#endif /* NECP */
+               ) {
                if (m != NULL)
                        m_freem(m);
                VERIFY(control == NULL);
@@ -397,8 +389,14 @@ rip_output(
                ipoa.ipoa_boundif = inp->inp_boundifp->if_index;
                ipoa.ipoa_flags |= IPOAF_BOUND_IF;
        }
-       if (inp->inp_flags & INP_NO_IFT_CELLULAR)
+       if (INP_NO_CELLULAR(inp))
                ipoa.ipoa_flags |=  IPOAF_NO_CELLULAR;
+       if (INP_NO_EXPENSIVE(inp))
+               ipoa.ipoa_flags |=  IPOAF_NO_EXPENSIVE;
+       if (INP_AWDL_UNRESTRICTED(inp))
+               ipoa.ipoa_flags |=  IPOAF_AWDL_UNRESTRICTED;
+       ipoa.ipoa_sotc = sotc;
+       ipoa.ipoa_netsvctype = netsvctype;
 
        if (inp->inp_flowhash == 0)
                inp->inp_flowhash = inp_calc_flowhash(inp);
@@ -412,7 +410,7 @@ rip_output(
                        m_freem(m);
                        return(EMSGSIZE);
                }
-               M_PREPEND(m, sizeof(struct ip), M_WAIT);
+               M_PREPEND(m, sizeof(struct ip), M_WAIT, 1);
                if (m == NULL)
                        return ENOBUFS;
                ip = mtod(m, struct ip *);
@@ -448,8 +446,76 @@ rip_output(
        if (inp->inp_laddr.s_addr != INADDR_ANY)
                ipoa.ipoa_flags |= IPOAF_BOUND_SRCADDR;
 
+#if NECP
+       {
+               necp_kernel_policy_id policy_id;
+               u_int32_t route_rule_id;
+
+               /*
+                * We need a route to perform NECP route rule checks
+                */
+               if (net_qos_policy_restricted != 0 &&
+                   ROUTE_UNUSABLE(&inp->inp_route)) {
+                       struct sockaddr_in to;
+                       struct sockaddr_in from;
+                       struct in_addr laddr = ip->ip_src;
+
+                       ROUTE_RELEASE(&inp->inp_route);
+
+                       bzero(&from, sizeof(struct sockaddr_in));
+                       from.sin_family = AF_INET;
+                       from.sin_len = sizeof(struct sockaddr_in);
+                       from.sin_addr = laddr;
+
+                       bzero(&to, sizeof(struct sockaddr_in));
+                       to.sin_family = AF_INET;
+                       to.sin_len = sizeof(struct sockaddr_in);
+                       to.sin_addr.s_addr = ip->ip_dst.s_addr;
+
+                       if ((error = in_pcbladdr(inp, (struct sockaddr *)&to,
+                           &laddr, ipoa.ipoa_boundif, NULL, 1)) != 0) {
+                               printf("%s in_pcbladdr(%p) error %d\n",
+                                       __func__, inp, error);
+                               m_freem(m);
+                               return (error);
+                       }
+
+                       inp_update_necp_policy(inp, (struct sockaddr *)&from,
+                           (struct sockaddr *)&to, ipoa.ipoa_boundif);
+                       inp->inp_policyresult.results.qos_marking_gencount = 0;
+               }
+
+               if (!necp_socket_is_allowed_to_send_recv_v4(inp, 0, 0,
+                       &ip->ip_src, &ip->ip_dst, NULL, &policy_id, &route_rule_id)) {
+                       m_freem(m);
+                       return(EHOSTUNREACH);
+               }
+
+               necp_mark_packet_from_socket(m, inp, policy_id, route_rule_id);
+
+               if (net_qos_policy_restricted != 0) {
+                       struct ifnet *rt_ifp = NULL;
+
+                       if (inp->inp_route.ro_rt != NULL)
+                               rt_ifp = inp->inp_route.ro_rt->rt_ifp;
+
+                       printf("%s inp %p last_pid %u inp_boundifp %d inp_last_outifp %d rt_ifp %d route_rule_id %u\n",
+                               __func__, inp,
+                               inp->inp_socket != NULL ? inp->inp_socket->last_pid : -1,
+                               inp->inp_boundifp != NULL ? inp->inp_boundifp->if_index : -1,
+                               inp->inp_last_outifp != NULL ?  inp->inp_last_outifp->if_index : -1,
+                               rt_ifp != NULL ?  rt_ifp->if_index : -1,
+                               route_rule_id);
+                       necp_socket_update_qos_marking(inp, inp->inp_route.ro_rt,
+                           NULL, route_rule_id);
+               }
+       }
+#endif /* NECP */
+       if ((so->so_flags1 & SOF1_QOSMARKING_ALLOWED))
+               ipoa.ipoa_flags |= IPOAF_QOSMARKING_ALLOWED;
+
 #if IPSEC
-       if (ipsec_bypass == 0 && ipsec_setsocket(m, so) != 0) {
+       if (inp->inp_sp != NULL && ipsec_setsocket(m, so) != 0) {
                m_freem(m);
                return ENOBUFS;
        }
@@ -458,7 +524,7 @@ rip_output(
        if (ROUTE_UNUSABLE(&inp->inp_route))
                ROUTE_RELEASE(&inp->inp_route);
 
-       set_packet_service_class(m, so, msc, 0);
+       set_packet_service_class(m, so, sotc, 0);
        m->m_pkthdr.pkt_flowsrc = FLOWSRC_INPCB;
        m->m_pkthdr.pkt_flowid = inp->inp_flowhash;
        m->m_pkthdr.pkt_flags |= (PKTF_FLOW_ID | PKTF_FLOW_LOCALSRC |
@@ -477,6 +543,7 @@ rip_output(
         * to pass the PCB cached route pointer directly to IP and
         * the modules beneath it.
         */
+       // TODO: PASS DOWN ROUTE RULE ID
        error = ip_output(m, inp->inp_options, &inp->inp_route, flags,
            imo, &ipoa);
 
@@ -511,11 +578,11 @@ rip_output(
        }
 
        /*
-        * If output interface was cellular, and this socket is denied
-        * access to it, generate an event.
+        * If output interface was cellular/expensive, and this socket is
+        * denied access to it, generate an event.
         */
        if (error != 0 && (ipoa.ipoa_retflags & IPOARF_IFDENIED) &&
-           (inp->inp_flags & INP_NO_IFT_CELLULAR))
+           (INP_NO_CELLULAR(inp) || INP_NO_EXPENSIVE(inp)))
                soevent(so, (SO_FILT_HINT_LOCKED|SO_FILT_HINT_IFDENIED));
 
        return (error);
@@ -526,15 +593,15 @@ int
 load_ipfw(void)
 {
        kern_return_t   err;
-       
+
        ipfw_init();
-       
+
 #if DUMMYNET
        if (!DUMMYNET_LOADED)
                ip_dn_init();
 #endif /* DUMMYNET */
        err = 0;
-       
+
        return err == 0 && ip_fw_ctl_ptr == NULL ? -1 : err;
 }
 #endif /* IPFIREWALL */
@@ -543,9 +610,7 @@ load_ipfw(void)
  * Raw IP socket option processing.
  */
 int
-rip_ctloutput(so, sopt)
-       struct socket *so;
-       struct sockopt *sopt;
+rip_ctloutput(struct socket *so, struct sockopt *sopt)
 {
        struct  inpcb *inp = sotoinpcb(so);
        int     error, optval;
@@ -595,19 +660,6 @@ rip_ctloutput(so, sopt)
                        break ;
 #endif /* DUMMYNET */
 
-#if MROUTING
-               case MRT_INIT:
-               case MRT_DONE:
-               case MRT_ADD_VIF:
-               case MRT_DEL_VIF:
-               case MRT_ADD_MFC:
-               case MRT_DEL_MFC:
-               case MRT_VERSION:
-               case MRT_ASSERT:
-                       error = ip_mrouter_get(so, sopt);
-                       break;
-#endif /* MROUTING */
-
                default:
                        error = ip_ctloutput(so, sopt);
                        break;
@@ -671,36 +723,6 @@ rip_ctloutput(so, sopt)
                        break ;
 #endif
 
-#if MROUTING
-               case IP_RSVP_ON:
-                       error = ip_rsvp_init(so);
-                       break;
-
-               case IP_RSVP_OFF:
-                       error = ip_rsvp_done();
-                       break;
-
-                       /* XXX - should be combined */
-               case IP_RSVP_VIF_ON:
-                       error = ip_rsvp_vif_init(so, sopt);
-                       break;
-
-               case IP_RSVP_VIF_OFF:
-                       error = ip_rsvp_vif_done(so, sopt);
-                       break;
-
-               case MRT_INIT:
-               case MRT_DONE:
-               case MRT_ADD_VIF:
-               case MRT_DEL_VIF:
-               case MRT_ADD_MFC:
-               case MRT_DEL_MFC:
-               case MRT_VERSION:
-               case MRT_ASSERT:
-                       error = ip_mrouter_set(so, sopt);
-                       break;
-#endif /* MROUTING */
-
                case SO_FLUSH:
                        if ((error = sooptcopyin(sopt, &optval, sizeof (optval),
                            sizeof (optval))) != 0)
@@ -854,13 +876,6 @@ rip_detach(struct socket *so)
        inp = sotoinpcb(so);
        if (inp == 0)
                panic("rip_detach");
-#if MROUTING
-       if (so == ip_mrouter)
-               ip_mrouter_done();
-       ip_rsvp_force_done(so);
-       if (so == ip_rsvpd)
-               ip_rsvp_done();
-#endif /* MROUTING */
        in_pcbdetach(inp);
        return 0;
 }
@@ -889,7 +904,11 @@ rip_bind(struct socket *so, struct sockaddr *nam, struct proc *p)
        struct ifaddr *ifa = NULL;
        struct ifnet *outif = NULL;
 
-       if (inp == NULL || (inp->inp_flags2 & INP2_WANT_FLOW_DIVERT))
+       if (inp == NULL
+#if NECP
+               || (necp_socket_should_use_flow_divert(inp))
+#endif /* NECP */
+               )
                return (inp == NULL ? EINVAL : EPROTOTYPE);
 
        if (nam->sa_len != sizeof (struct sockaddr_in))
@@ -930,7 +949,11 @@ rip_connect(struct socket *so, struct sockaddr *nam, __unused  struct proc *p)
        struct inpcb *inp = sotoinpcb(so);
        struct sockaddr_in *addr = (struct sockaddr_in *)(void *)nam;
 
-       if (inp == NULL || (inp->inp_flags2 & INP2_WANT_FLOW_DIVERT))
+       if (inp == NULL
+#if NECP
+               || (necp_socket_should_use_flow_divert(inp))
+#endif /* NECP */
+               )
                return (inp == NULL ? EINVAL : EPROTOTYPE);
        if (nam->sa_len != sizeof(*addr))
                return EINVAL;
@@ -961,8 +984,15 @@ rip_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam,
        u_int32_t dst;
        int error = 0;
 
-       if (inp == NULL || (inp->inp_flags2 & INP2_WANT_FLOW_DIVERT)) {
-               error = (inp == NULL ? EINVAL : EPROTOTYPE);
+       if (inp == NULL
+#if NECP
+               || (necp_socket_should_use_flow_divert(inp) && (error = EPROTOTYPE))
+#endif /* NECP */
+               ) {
+               if (inp == NULL)
+                       error = EINVAL;
+               else
+                       error = EPROTOTYPE;
                goto bad;
        }
 
@@ -993,7 +1023,7 @@ bad:
 }
 
 /* note: rip_unlock is called from different protos  instead of the generic socket_unlock,
- * it will handle the socket dealloc on last reference 
+ * it will handle the socket dealloc on last reference
  * */
 int
 rip_unlock(struct socket *so, int refcount, void *debug)
@@ -1068,7 +1098,7 @@ rip_pcblist SYSCTL_HANDLER_ARGS
         */
        gencnt = ripcbinfo.ipi_gencnt;
        n = ripcbinfo.ipi_count;
-       
+
        bzero(&xig, sizeof(xig));
        xig.xig_len = sizeof xig;
        xig.xig_count = n;
@@ -1084,7 +1114,7 @@ rip_pcblist SYSCTL_HANDLER_ARGS
      */
     if (n == 0) {
        lck_rw_done(ripcbinfo.ipi_lock);
-        return 0; 
+        return 0;
     }
 
        inp_list = _MALLOC(n * sizeof *inp_list, M_TEMP, M_WAITOK);
@@ -1092,7 +1122,7 @@ rip_pcblist SYSCTL_HANDLER_ARGS
                lck_rw_done(ripcbinfo.ipi_lock);
                return ENOMEM;
        }
-       
+
        for (inp = ripcbinfo.ipi_listhead->lh_first, i = 0; inp && i < n;
             inp = inp->inp_list.le_next) {
                if (inp->inp_gencnt <= gencnt && inp->inp_state != INPCB_STATE_DEAD)
@@ -1135,7 +1165,8 @@ rip_pcblist SYSCTL_HANDLER_ARGS
        return error;
 }
 
-SYSCTL_PROC(_net_inet_raw, OID_AUTO/*XXX*/, pcblist, CTLFLAG_RD | CTLFLAG_LOCKED, 0, 0,
+SYSCTL_PROC(_net_inet_raw, OID_AUTO/*XXX*/, pcblist,
+           CTLTYPE_STRUCT | CTLFLAG_RD | CTLFLAG_LOCKED, 0, 0,
            rip_pcblist, "S,xinpcb", "List of active raw IP sockets");
 
 
@@ -1237,7 +1268,8 @@ rip_pcblist64 SYSCTL_HANDLER_ARGS
         return error;
 }
 
-SYSCTL_PROC(_net_inet_raw, OID_AUTO, pcblist64, CTLFLAG_RD | CTLFLAG_LOCKED, 0, 0,
+SYSCTL_PROC(_net_inet_raw, OID_AUTO, pcblist64,
+            CTLTYPE_STRUCT | CTLFLAG_RD | CTLFLAG_LOCKED, 0, 0,
             rip_pcblist64, "S,xinpcb64", "List of active raw IP sockets");
 
 
@@ -1253,7 +1285,8 @@ rip_pcblist_n SYSCTL_HANDLER_ARGS
        return error;
 }
 
-SYSCTL_PROC(_net_inet_raw, OID_AUTO, pcblist_n, CTLFLAG_RD | CTLFLAG_LOCKED, 0, 0,
+SYSCTL_PROC(_net_inet_raw, OID_AUTO, pcblist_n,
+            CTLTYPE_STRUCT | CTLFLAG_RD | CTLFLAG_LOCKED, 0, 0,
             rip_pcblist_n, "S,xinpcb_n", "List of active raw IP sockets");
 
 struct pr_usrreqs rip_usrreqs = {