--- /dev/null
+/*
+ * Copyright (c) 2009-2011 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
+ * compliance with the License. The rights granted to you under the License
+ * may not be used to create, or enable the creation or redistribution of,
+ * 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,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * 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@
+ */
+
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/types.h>
+#include <sys/filedesc.h>
+#include <sys/file_internal.h>
+#include <sys/proc.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/errno.h>
+#include <sys/protosw.h>
+#include <sys/domain.h>
+#include <sys/mbuf.h>
+#include <sys/queue.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet/in_pcb.h>
+#include <netinet/ip.h>
+#include <netinet/ip_var.h>
+#include <netinet/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet/udp.h>
+#include <netinet/udp_var.h>
+#include <netinet/tcp.h>
+#include <netinet/tcp_var.h>
+#include <netinet/tcp_cc.h>
+
+extern char *proc_name_address(void *p);
+
+static int tfp_count = 0;
+
+static TAILQ_HEAD(, tclass_for_proc) tfp_head = TAILQ_HEAD_INITIALIZER(tfp_head);
+
+struct tclass_for_proc {
+ TAILQ_ENTRY(tclass_for_proc) tfp_link;
+ int tfp_class;
+ pid_t tfp_pid;
+ char tfp_pname[MAXCOMLEN + 1];
+};
+
+extern void tcp_set_background_cc(struct socket *);
+extern void tcp_set_foreground_cc(struct socket *);
+
+int dscp_code_from_mbuf_tclass(int );
+
+static int get_pid_tclass(pid_t , int *);
+static int get_pname_tclass(const char * , int *);
+static int set_pid_tclass(pid_t , int );
+static int set_pname_tclass(const char * , int );
+static int purge_tclass_for_proc(void);
+static int flush_tclass_for_proc(void);
+
+
+static lck_grp_attr_t *tclass_lck_grp_attr = NULL; /* mutex group attributes */
+static lck_grp_t *tclass_lck_grp = NULL; /* mutex group definition */
+static lck_attr_t *tclass_lck_attr = NULL; /* mutex attributes */
+static lck_mtx_t *tclass_lock = NULL;
+
+/*
+ * Must be called with tclass_lock held
+ */
+static struct tclass_for_proc *
+find_tfp_by_pid(pid_t pid)
+{
+ struct tclass_for_proc *tfp;
+
+ TAILQ_FOREACH(tfp, &tfp_head, tfp_link) {
+ if (tfp->tfp_pid == pid)
+ break;
+ }
+ return tfp;
+}
+
+/*
+ * Must be called with tclass_lock held
+ */
+static struct tclass_for_proc *
+find_tfp_by_pname(const char *pname)
+{
+ struct tclass_for_proc *tfp;
+
+ TAILQ_FOREACH(tfp, &tfp_head, tfp_link) {
+ if (strncmp(pname, tfp->tfp_pname, sizeof(tfp->tfp_pname)) == 0)
+ break;
+ }
+ return tfp;
+}
+
+static int
+get_tclass_for_curr_proc(void)
+{
+ struct tclass_for_proc *tfp;
+ int sotc = SO_TC_BE;
+ proc_t p = current_proc(); /* Not ref counted */
+ pid_t pid = proc_pid(p);
+ char *pname = proc_name_address(p);
+
+ lck_mtx_lock(tclass_lock);
+
+ TAILQ_FOREACH(tfp, &tfp_head, tfp_link) {
+ if ((tfp->tfp_pid == pid) ||
+ (tfp->tfp_pid == -1 && strncmp(pname, tfp->tfp_pname, sizeof(tfp->tfp_pname)) == 0)) {
+ sotc = tfp->tfp_class;
+ break;
+ }
+ }
+
+ lck_mtx_unlock(tclass_lock);
+
+ return sotc;
+}
+
+/*
+ * Purge entries with PIDs of exited processes
+ */
+int
+purge_tclass_for_proc(void)
+{
+ int error = 0;
+ struct tclass_for_proc *tfp, *tvar;
+
+ lck_mtx_lock(tclass_lock);
+
+ TAILQ_FOREACH_SAFE(tfp, &tfp_head, tfp_link, tvar) {
+ proc_t p;
+
+ if (tfp->tfp_pid == -1)
+ continue;
+ if ((p = proc_find(tfp->tfp_pid)) == NULL) {
+ tfp_count--;
+ TAILQ_REMOVE(&tfp_head, tfp, tfp_link);
+
+ _FREE(tfp, M_TEMP);
+ } else {
+ proc_rele(p);
+ }
+ }
+
+ lck_mtx_unlock(tclass_lock);
+
+ return error;
+}
+
+/*
+ * Remove one entry
+ * Must be called with tclass_lock held
+ */
+static void
+free_tclass_for_proc(struct tclass_for_proc *tfp)
+{
+ if (tfp == NULL)
+ return;
+ tfp_count--;
+ TAILQ_REMOVE(&tfp_head, tfp, tfp_link);
+ _FREE(tfp, M_TEMP);
+}
+
+/*
+ * Remove all entries
+ */
+int
+flush_tclass_for_proc(void)
+{
+ int error = 0;
+ struct tclass_for_proc *tfp, *tvar;
+
+ lck_mtx_lock(tclass_lock);
+
+ TAILQ_FOREACH_SAFE(tfp, &tfp_head, tfp_link, tvar) {
+ free_tclass_for_proc(tfp);
+ }
+
+ lck_mtx_unlock(tclass_lock);
+
+ return error;
+
+}
+
+/*
+ * Must be called with tclass_lock held
+ */
+static struct tclass_for_proc *
+alloc_tclass_for_proc(pid_t pid, const char *pname, int tclass)
+{
+ struct tclass_for_proc *tfp;
+
+ if (pid == -1 && pname == NULL)
+ return NULL;
+
+ tfp = _MALLOC(sizeof(struct tclass_for_proc), M_TEMP, M_NOWAIT | M_ZERO);
+ if (tfp == NULL)
+ return NULL;
+
+ tfp->tfp_pid = pid;
+ tfp->tfp_class = tclass;
+ /*
+ * Add per pid entries before per proc name so we can find
+ * a specific instance of a process before the general name base entry.
+ */
+ if (pid != -1) {
+ TAILQ_INSERT_HEAD(&tfp_head, tfp, tfp_link);
+ } else {
+ strlcpy(tfp->tfp_pname, pname, sizeof(tfp->tfp_pname));
+ TAILQ_INSERT_TAIL(&tfp_head, tfp, tfp_link);
+ }
+
+ tfp_count++;
+
+ return tfp;
+}
+
+/*
+ * -1 for tclass means to remove the entry
+ */
+int
+set_pid_tclass(pid_t pid, int tclass)
+{
+ int error = EINVAL;
+ proc_t p = NULL;
+ struct filedesc *fdp;
+ struct fileproc *fp;
+ struct tclass_for_proc *tfp;
+ int i;
+
+ p = proc_find(pid);
+ if (p == NULL) {
+ printf("set_pid_tclass proc_find(%d) \n", pid);
+ goto done;
+ }
+
+ /* Need a tfp */
+ lck_mtx_lock(tclass_lock);
+
+ tfp = find_tfp_by_pid(pid);
+ if (tclass == -1) {
+ if (tfp != NULL) {
+ free_tclass_for_proc(tfp);
+ error = 0;
+ }
+ lck_mtx_unlock(tclass_lock);
+ goto done;
+ } else {
+ if (tfp == NULL) {
+ tfp = alloc_tclass_for_proc(pid, NULL, tclass);
+ if (tfp == NULL) {
+ lck_mtx_unlock(tclass_lock);
+ error = ENOBUFS;
+ goto done;
+ }
+ } else {
+ tfp->tfp_class = tclass;
+ }
+ }
+ lck_mtx_unlock(tclass_lock);
+
+ if (tfp != NULL) {
+ proc_fdlock(p);
+
+ fdp = p->p_fd;
+ for (i = 0; i < fdp->fd_nfiles; i++) {
+ struct socket *so;
+
+ fp = fdp->fd_ofiles[i];
+ if (fp == NULL || (fdp->fd_ofileflags[i] & UF_RESERVED) != 0 ||
+ fp->f_fglob->fg_type != DTYPE_SOCKET)
+ continue;
+
+ so = (struct socket *)fp->f_fglob->fg_data;
+ if (so->so_proto->pr_domain->dom_family != AF_INET &&
+ so->so_proto->pr_domain->dom_family != AF_INET6)
+ continue;
+ socket_lock(so, 1);
+ error = so_set_traffic_class(so, tclass != -1 ? tclass : SO_TC_BE);
+ socket_unlock(so, 1);
+ if (error != 0) {
+ printf("set_pid_tclass so_set_traffic_class(%p, %d) failed %d\n", so, tclass, error);
+ error = 0;
+ }
+ }
+
+ proc_fdunlock(p);
+ }
+
+ error = 0;
+done:
+ if (p != NULL)
+ proc_rele(p);
+
+ return error;
+}
+
+int
+set_pname_tclass(const char *pname, int tclass)
+{
+ int error = EINVAL;
+ struct tclass_for_proc *tfp;
+
+ lck_mtx_lock(tclass_lock);
+
+ tfp = find_tfp_by_pname(pname);
+ if (tclass == -1) {
+ if (tfp != NULL)
+ free_tclass_for_proc(tfp);
+ } else {
+ if (tfp == NULL) {
+ tfp = alloc_tclass_for_proc(-1, pname, tclass);
+ if (tfp == NULL) {
+ lck_mtx_unlock(tclass_lock);
+ error = ENOBUFS;
+ goto done;
+ }
+ } else {
+ tfp->tfp_class = tclass;
+ }
+ }
+ lck_mtx_unlock(tclass_lock);
+
+ error = 0;
+done:
+
+ return error;
+}
+
+int
+get_pid_tclass(pid_t pid, int *tclass)
+{
+ int error = EINVAL;
+ proc_t p = NULL;
+ struct tclass_for_proc *tfp;
+
+ *tclass = -1; /* Means not set */
+
+ p = proc_find(pid);
+ if (p == NULL) {
+ printf("get_pid_tclass proc_find(%d) \n", pid);
+ goto done;
+ }
+
+ /* Need a tfp */
+ lck_mtx_lock(tclass_lock);
+
+ tfp = find_tfp_by_pid(pid);
+ if (tfp != NULL) {
+ *tclass = tfp->tfp_class ;
+ error = 0;
+ }
+ lck_mtx_unlock(tclass_lock);
+done:
+ if (p != NULL)
+ proc_rele(p);
+
+ return error;
+}
+
+int
+get_pname_tclass(const char *pname, int *tclass)
+{
+ int error = EINVAL;
+ struct tclass_for_proc *tfp;
+
+ *tclass = -1; /* Means not set */
+
+ /* Need a tfp */
+ lck_mtx_lock(tclass_lock);
+
+ tfp = find_tfp_by_pname(pname);
+ if (tfp != NULL) {
+ *tclass = tfp->tfp_class ;
+ error = 0;
+ }
+ lck_mtx_unlock(tclass_lock);
+
+ return error;
+}
+
+
+
+/*
+ * Setting options requires privileges
+ */
+__private_extern__ int
+so_set_tcdbg(struct socket *so, struct so_tcdbg *so_tcdbg)
+{
+ int error = 0;
+
+ if ((so->so_state & SS_PRIV) == 0)
+ return EPERM;
+
+ socket_unlock(so, 0);
+
+ switch (so_tcdbg->so_tcdbg_cmd) {
+ case SO_TCDBG_PID:
+ error = set_pid_tclass(so_tcdbg->so_tcdbg_pid, so_tcdbg->so_tcdbg_tclass);
+ break;
+
+ case SO_TCDBG_PNAME:
+ error = set_pname_tclass(so_tcdbg->so_tcdbg_pname, so_tcdbg->so_tcdbg_tclass);
+ break;
+
+ case SO_TCDBG_PURGE:
+ error = purge_tclass_for_proc();
+ break;
+
+ case SO_TCDBG_FLUSH:
+ error = flush_tclass_for_proc();
+ break;
+
+ default:
+ error = EINVAL;
+ break;
+
+ }
+
+ socket_lock(so, 0);
+
+ return error;
+}
+
+/*
+ * Not required to be privileged to get
+ */
+__private_extern__ int
+sogetopt_tcdbg(struct socket *so, struct sockopt *sopt)
+{
+ int error = 0;
+ struct so_tcdbg so_tcdbg;
+ void *buf = NULL;
+ size_t len = sopt->sopt_valsize;
+
+ error = sooptcopyin(sopt, &so_tcdbg, sizeof(struct so_tcdbg), sizeof(struct so_tcdbg));
+ if (error != 0)
+ return error;
+
+ sopt->sopt_valsize = len;
+
+ socket_unlock(so, 0);
+
+ switch (so_tcdbg.so_tcdbg_cmd) {
+ case SO_TCDBG_PID:
+ error = get_pid_tclass(so_tcdbg.so_tcdbg_pid, &so_tcdbg.so_tcdbg_tclass);
+ break;
+
+ case SO_TCDBG_PNAME:
+ error = get_pname_tclass(so_tcdbg.so_tcdbg_pname, &so_tcdbg.so_tcdbg_tclass);
+ break;
+
+ case SO_TCDBG_COUNT:
+ lck_mtx_lock(tclass_lock);
+ so_tcdbg.so_tcdbg_count = tfp_count;
+ lck_mtx_unlock(tclass_lock);
+ break;
+
+ case SO_TCDBG_LIST: {
+ struct tclass_for_proc *tfp;
+ int n, alloc_count;
+ struct so_tcdbg *ptr;
+
+ lck_mtx_lock(tclass_lock);
+ if ((alloc_count = tfp_count) == 0) {
+ lck_mtx_unlock(tclass_lock);
+ error = EINVAL;
+ break;
+ }
+ len = alloc_count * sizeof(struct so_tcdbg);
+ lck_mtx_unlock(tclass_lock);
+
+ buf = _MALLOC(len, M_TEMP, M_WAITOK | M_ZERO);
+ if (buf == NULL) {
+ error = ENOBUFS;
+ break;
+ }
+
+ lck_mtx_lock(tclass_lock);
+ n = 0;
+ ptr = (struct so_tcdbg *)buf;
+ TAILQ_FOREACH(tfp, &tfp_head, tfp_link) {
+ if (++n > alloc_count)
+ break;
+ if (tfp->tfp_pid != -1) {
+ ptr->so_tcdbg_cmd = SO_TCDBG_PID;
+ ptr->so_tcdbg_pid = tfp->tfp_pid;
+ } else {
+ ptr->so_tcdbg_cmd = SO_TCDBG_PNAME;
+ ptr->so_tcdbg_pid = -1;
+ strlcpy(ptr->so_tcdbg_pname, tfp->tfp_pname, sizeof(ptr->so_tcdbg_pname));
+ }
+ ptr->so_tcdbg_tclass = tfp->tfp_class;
+ ptr++;
+ }
+
+ lck_mtx_unlock(tclass_lock);
+ }
+ break;
+
+ default:
+ error = EINVAL;
+ break;
+
+ }
+
+ socket_lock(so, 0);
+
+ if (error == 0) {
+ if (buf == NULL) {
+ error = sooptcopyout(sopt, &so_tcdbg, sizeof(struct so_tcdbg));
+ } else {
+ error = sooptcopyout(sopt, buf, len);
+ _FREE(buf, M_TEMP);
+ }
+ }
+ return error;
+}
+
+
+__private_extern__ int
+so_set_traffic_class(struct socket *so, int optval)
+{
+ int error = 0;
+
+ if (optval < SO_TC_BE || optval > SO_TC_VO) {
+ error = EINVAL;
+ } else {
+ so->so_traffic_class = optval;
+
+ if ((INP_SOCKAF(so) == AF_INET || INP_SOCKAF(so) == AF_INET6) &&
+ INP_SOCKTYPE(so) == SOCK_STREAM) {
+ set_tcp_stream_priority(so);
+ }
+ }
+ return error;
+}
+
+__private_extern__ void
+so_set_default_traffic_class(struct socket *so)
+{
+ int sotc = SO_TC_BE;
+
+ if (tfp_count > 0 && (INP_SOCKAF(so) == AF_INET || INP_SOCKAF(so) == AF_INET6)) {
+ sotc = get_tclass_for_curr_proc();
+ }
+
+ so->so_traffic_class = sotc;
+
+ return;
+}
+
+
+__private_extern__ int
+mbuf_traffic_class_from_control(struct mbuf *control)
+{
+ struct cmsghdr *cm;
+
+ for (cm = M_FIRST_CMSGHDR(control);
+ cm != NULL;
+ cm = M_NXT_CMSGHDR(control, cm)) {
+ int tc;
+
+ if (cm->cmsg_len < sizeof(struct cmsghdr))
+ break;
+
+ if (cm->cmsg_level != SOL_SOCKET ||
+ cm->cmsg_type != SO_TRAFFIC_CLASS)
+ continue;
+ if (cm->cmsg_len != CMSG_LEN(sizeof(int)))
+ continue;
+
+ tc = *(int *)CMSG_DATA(cm);
+
+ switch (tc) {
+ case SO_TC_BE:
+ return MBUF_TC_BE;
+ case SO_TC_BK:
+ return MBUF_TC_BK;
+ case SO_TC_VI:
+ return MBUF_TC_VI;
+ case SO_TC_VO:
+ return MBUF_TC_VO;
+ default:
+ break;
+ }
+ }
+
+ return MBUF_TC_UNSPEC;
+}
+
+__private_extern__ int
+dscp_code_from_mbuf_tclass(int mtc)
+{
+ int dscp_code;
+
+ switch (mtc) {
+ default:
+ case MBUF_TC_BE:
+ dscp_code = 0;
+ break;
+ case MBUF_TC_BK:
+ dscp_code = 0x08;
+ break;
+ case MBUF_TC_VI:
+ dscp_code = 0x20;
+ break;
+ case MBUF_TC_VO:
+ dscp_code = 0x30;
+ break;
+ }
+
+ return dscp_code;
+}
+
+__private_extern__ void
+so_recv_data_stat(struct socket *so, struct mbuf *m, size_t off)
+{
+ uint32_t sotc = m->m_pkthdr.prio;
+
+ if (sotc >= SO_TC_STATS_MAX)
+ sotc = SO_TC_BE;
+
+ so->so_tc_stats[sotc].rxpackets += 1;
+ so->so_tc_stats[sotc].rxbytes += ((m->m_flags & M_PKTHDR) ? m->m_pkthdr.len : 0) + off;
+
+ return;
+}
+
+__private_extern__ void
+set_tcp_stream_priority(struct socket *so)
+{
+ struct tcpcb *tp = intotcpcb(sotoinpcb(so));
+
+ /* If the socket was marked as a background socket or if the
+ * traffic class is set to background with traffic class socket
+ * option then make both send and recv side of the stream to be
+ * background. The variable sotcdb which can be set with sysctl
+ * is used to disable these settings for testing.
+ */
+ if (soisbackground(so) || so->so_traffic_class == SO_TC_BK) {
+ if ((sotcdb & SOTCDB_NO_SENDTCPBG) != 0) {
+ if (tp->tcp_cc_index == TCP_CC_ALGO_BACKGROUND_INDEX)
+ tcp_set_foreground_cc(so);
+ } else {
+ if (tp->tcp_cc_index != TCP_CC_ALGO_BACKGROUND_INDEX)
+ tcp_set_background_cc(so);
+ }
+
+ /* Set receive side background flags */
+ if ((sotcdb & SOTCDB_NO_RECVTCPBG) != 0) {
+ so->so_traffic_mgt_flags &= ~(TRAFFIC_MGT_TCP_RECVBG);
+ } else {
+ so->so_traffic_mgt_flags |= TRAFFIC_MGT_TCP_RECVBG;
+ }
+ } else {
+ so->so_traffic_mgt_flags &= ~(TRAFFIC_MGT_TCP_RECVBG);
+ if (tp->tcp_cc_index == TCP_CC_ALGO_BACKGROUND_INDEX)
+ tcp_set_foreground_cc(so);
+ }
+ return;
+}
+
+/*
+ * Set traffic class to an IPv4 or IPv6 packet
+ * - mark the mbuf
+ * - set the DSCP code following the WMM mapping
+ */
+__private_extern__ void
+set_packet_tclass(struct mbuf *m, struct socket *so, int in_mtc, int isipv6)
+{
+ int mtc = MBUF_TC_BE; /* Best effort by default */
+ struct inpcb *inp = sotoinpcb(so); /* in6pcb and inpcb are the same */
+ struct ip *ip = mtod(m, struct ip *);
+#if INET6
+ struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
+#endif /* INET6 */
+
+ if (!(m->m_flags & M_PKTHDR))
+ return;
+
+ /*
+ * Here is the precedence:
+ * 1) TRAFFIC_MGT_SO_BACKGROUND trumps all
+ * 2) Traffic class passed via ancillary data to sendmsdg(2)
+ * 3) Traffic class socket option last
+ */
+ if (soisbackground(so)) {
+ mtc = MBUF_TC_BK;
+ } else if (in_mtc != MBUF_TC_UNSPEC) {
+ if (in_mtc >= MBUF_TC_BE && in_mtc <= MBUF_TC_VO)
+ mtc = in_mtc;
+ } else {
+ switch (so->so_traffic_class) {
+ case SO_TC_BE:
+ mtc = MBUF_TC_BE;
+ break;
+ case SO_TC_BK:
+ mtc = MBUF_TC_BK;
+ break;
+ case SO_TC_VI:
+ mtc = MBUF_TC_VI;
+ break;
+ case SO_TC_VO:
+ mtc = MBUF_TC_VO;
+ break;
+ default:
+ break;
+ }
+ }
+
+ /*
+ * Set the traffic class in the mbuf packet header prio field
+ */
+ if ((sotcdb & SOTCDB_NO_MTC))
+ goto no_mbtc;
+ m->m_pkthdr.prio = mtc;
+
+no_mbtc:
+ /*
+ * Quick exit when best effort
+ */
+ if (mtc == MBUF_TC_BE)
+ goto no_dscp;
+ /*
+ * Now let set the DSCP code in IPv4 or IPv6 header
+ * By default do this only for local traffic if a code is not already set
+ */
+ if ((sotcdb & SOTCDB_NO_DSCP))
+ goto no_dscp;
+
+ /*
+ * Test if a IP TOS or IPV6 TCLASS has already been set on the socket or the raw packet
+ */
+ if ((sotcdb & SOTCDB_NO_DSCPTST) == 0) {
+#if INET6
+ if (isipv6)
+ {
+ if ((so->so_type == SOCK_RAW && (ip6->ip6_flow & htonl(0xff << 20)) != 0) ||
+ (inp->in6p_outputopts && inp->in6p_outputopts->ip6po_tclass != -1))
+ goto no_dscp;
+ }
+ else
+#endif /* INET6 */
+ {
+ if ((so->so_type == SOCK_RAW && (inp->inp_flags & INP_HDRINCL)) ||
+ inp->inp_ip_tos != 0)
+ goto no_dscp;
+ }
+ }
+
+ /*
+ * Test if destination is local
+ */
+ if ((sotcdb & SOTCDB_NO_LCLTST) == 0) {
+ int islocal = 0;
+ struct route *ro = &inp->inp_route;
+
+ if (so->so_type == SOCK_STREAM) {
+ struct tcpcb *tp = intotcpcb(inp);
+
+ if ((tp->t_flags & TF_LOCAL))
+ islocal = 1;
+ }
+ else
+#if INET6
+ if (isipv6)
+ {
+ if ((ro != NULL && ro->ro_rt != NULL &&
+ (ro->ro_rt->rt_gateway->sa_family == AF_LINK ||
+ (ro->ro_rt->rt_ifp->if_flags & IFF_LOOPBACK))) ||
+ in6addr_local(&ip6->ip6_dst))
+ islocal = 1;
+ }
+ else
+#endif /* INET6 */
+ {
+ if ((ro != NULL && ro->ro_rt != NULL &&
+ (ro->ro_rt->rt_gateway->sa_family == AF_LINK ||
+ (ro->ro_rt->rt_ifp->if_flags & IFF_LOOPBACK))) ||
+ inaddr_local(ip->ip_dst))
+ islocal = 1;
+ }
+ if (islocal == 0)
+ goto no_dscp;
+ }
+
+#if INET6
+ if (isipv6)
+ ip6->ip6_flow |=
+ htonl(dscp_code_from_mbuf_tclass(m->m_pkthdr.prio) << 20);
+ else
+#endif /* INET6 */
+ ip->ip_tos |= dscp_code_from_mbuf_tclass(m->m_pkthdr.prio) << 2;
+
+no_dscp:
+ /*
+ * For TCP with background traffic class switch CC algo based on sysctl
+ */
+ if (so->so_type == SOCK_STREAM) {
+ set_tcp_stream_priority(so);
+ }
+
+ /*
+ * Assume socket and mbuf traffic class values are the same
+ * Also assume the socket lock is held
+ */
+ so->so_tc_stats[mtc].txpackets += 1;
+ so->so_tc_stats[mtc].txbytes += m->m_pkthdr.len;
+
+ return;
+}
+
+__private_extern__ void
+socket_tclass_init(void)
+{
+ tclass_lck_grp_attr = lck_grp_attr_alloc_init();
+ tclass_lck_grp = lck_grp_alloc_init("tclass", tclass_lck_grp_attr);
+ tclass_lck_attr = lck_attr_alloc_init();
+ if ((tclass_lock = lck_mtx_alloc_init(tclass_lck_grp, tclass_lck_attr)) == NULL) {
+ panic("failed to allocate memory for tclass\n");
+ }
+}
+
+