]> git.saurik.com Git - apple/xnu.git/blame - bsd/netinet/in_tclass.c
xnu-1699.22.81.tar.gz
[apple/xnu.git] / bsd / netinet / in_tclass.c
CommitLineData
6d2010ae
A
1/*
2 * Copyright (c) 2009-2011 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29#include <sys/systm.h>
30#include <sys/kernel.h>
31#include <sys/types.h>
32#include <sys/filedesc.h>
33#include <sys/file_internal.h>
34#include <sys/proc.h>
35#include <sys/socket.h>
36#include <sys/socketvar.h>
37#include <sys/errno.h>
38#include <sys/protosw.h>
39#include <sys/domain.h>
40#include <sys/mbuf.h>
41#include <sys/queue.h>
42
43#include <net/if.h>
44#include <net/route.h>
45
46#include <netinet/in.h>
47#include <netinet/in_var.h>
48#include <netinet/in_pcb.h>
49#include <netinet/ip.h>
50#include <netinet/ip_var.h>
51#include <netinet/ip6.h>
52#include <netinet6/ip6_var.h>
53#include <netinet/udp.h>
54#include <netinet/udp_var.h>
55#include <netinet/tcp.h>
56#include <netinet/tcp_var.h>
57#include <netinet/tcp_cc.h>
58
59extern char *proc_name_address(void *p);
60
61static int tfp_count = 0;
62
63static TAILQ_HEAD(, tclass_for_proc) tfp_head = TAILQ_HEAD_INITIALIZER(tfp_head);
64
65struct tclass_for_proc {
66 TAILQ_ENTRY(tclass_for_proc) tfp_link;
67 int tfp_class;
68 pid_t tfp_pid;
69 char tfp_pname[MAXCOMLEN + 1];
70};
71
72extern void tcp_set_background_cc(struct socket *);
73extern void tcp_set_foreground_cc(struct socket *);
74
75int dscp_code_from_mbuf_tclass(int );
76
77static int get_pid_tclass(pid_t , int *);
78static int get_pname_tclass(const char * , int *);
79static int set_pid_tclass(pid_t , int );
80static int set_pname_tclass(const char * , int );
81static int purge_tclass_for_proc(void);
82static int flush_tclass_for_proc(void);
83
84
85static lck_grp_attr_t *tclass_lck_grp_attr = NULL; /* mutex group attributes */
86static lck_grp_t *tclass_lck_grp = NULL; /* mutex group definition */
87static lck_attr_t *tclass_lck_attr = NULL; /* mutex attributes */
88static lck_mtx_t *tclass_lock = NULL;
89
90/*
91 * Must be called with tclass_lock held
92 */
93static struct tclass_for_proc *
94find_tfp_by_pid(pid_t pid)
95{
96 struct tclass_for_proc *tfp;
97
98 TAILQ_FOREACH(tfp, &tfp_head, tfp_link) {
99 if (tfp->tfp_pid == pid)
100 break;
101 }
102 return tfp;
103}
104
105/*
106 * Must be called with tclass_lock held
107 */
108static struct tclass_for_proc *
109find_tfp_by_pname(const char *pname)
110{
111 struct tclass_for_proc *tfp;
112
113 TAILQ_FOREACH(tfp, &tfp_head, tfp_link) {
114 if (strncmp(pname, tfp->tfp_pname, sizeof(tfp->tfp_pname)) == 0)
115 break;
116 }
117 return tfp;
118}
119
120static int
121get_tclass_for_curr_proc(void)
122{
123 struct tclass_for_proc *tfp;
124 int sotc = SO_TC_BE;
125 proc_t p = current_proc(); /* Not ref counted */
126 pid_t pid = proc_pid(p);
127 char *pname = proc_name_address(p);
128
129 lck_mtx_lock(tclass_lock);
130
131 TAILQ_FOREACH(tfp, &tfp_head, tfp_link) {
132 if ((tfp->tfp_pid == pid) ||
133 (tfp->tfp_pid == -1 && strncmp(pname, tfp->tfp_pname, sizeof(tfp->tfp_pname)) == 0)) {
134 sotc = tfp->tfp_class;
135 break;
136 }
137 }
138
139 lck_mtx_unlock(tclass_lock);
140
141 return sotc;
142}
143
144/*
145 * Purge entries with PIDs of exited processes
146 */
147int
148purge_tclass_for_proc(void)
149{
150 int error = 0;
151 struct tclass_for_proc *tfp, *tvar;
152
153 lck_mtx_lock(tclass_lock);
154
155 TAILQ_FOREACH_SAFE(tfp, &tfp_head, tfp_link, tvar) {
156 proc_t p;
157
158 if (tfp->tfp_pid == -1)
159 continue;
160 if ((p = proc_find(tfp->tfp_pid)) == NULL) {
161 tfp_count--;
162 TAILQ_REMOVE(&tfp_head, tfp, tfp_link);
163
164 _FREE(tfp, M_TEMP);
165 } else {
166 proc_rele(p);
167 }
168 }
169
170 lck_mtx_unlock(tclass_lock);
171
172 return error;
173}
174
175/*
176 * Remove one entry
177 * Must be called with tclass_lock held
178 */
179static void
180free_tclass_for_proc(struct tclass_for_proc *tfp)
181{
182 if (tfp == NULL)
183 return;
184 tfp_count--;
185 TAILQ_REMOVE(&tfp_head, tfp, tfp_link);
186 _FREE(tfp, M_TEMP);
187}
188
189/*
190 * Remove all entries
191 */
192int
193flush_tclass_for_proc(void)
194{
195 int error = 0;
196 struct tclass_for_proc *tfp, *tvar;
197
198 lck_mtx_lock(tclass_lock);
199
200 TAILQ_FOREACH_SAFE(tfp, &tfp_head, tfp_link, tvar) {
201 free_tclass_for_proc(tfp);
202 }
203
204 lck_mtx_unlock(tclass_lock);
205
206 return error;
207
208}
209
210/*
211 * Must be called with tclass_lock held
212 */
213static struct tclass_for_proc *
214alloc_tclass_for_proc(pid_t pid, const char *pname, int tclass)
215{
216 struct tclass_for_proc *tfp;
217
218 if (pid == -1 && pname == NULL)
219 return NULL;
220
221 tfp = _MALLOC(sizeof(struct tclass_for_proc), M_TEMP, M_NOWAIT | M_ZERO);
222 if (tfp == NULL)
223 return NULL;
224
225 tfp->tfp_pid = pid;
226 tfp->tfp_class = tclass;
227 /*
228 * Add per pid entries before per proc name so we can find
229 * a specific instance of a process before the general name base entry.
230 */
231 if (pid != -1) {
232 TAILQ_INSERT_HEAD(&tfp_head, tfp, tfp_link);
233 } else {
234 strlcpy(tfp->tfp_pname, pname, sizeof(tfp->tfp_pname));
235 TAILQ_INSERT_TAIL(&tfp_head, tfp, tfp_link);
236 }
237
238 tfp_count++;
239
240 return tfp;
241}
242
243/*
244 * -1 for tclass means to remove the entry
245 */
246int
247set_pid_tclass(pid_t pid, int tclass)
248{
249 int error = EINVAL;
250 proc_t p = NULL;
251 struct filedesc *fdp;
252 struct fileproc *fp;
253 struct tclass_for_proc *tfp;
254 int i;
255
256 p = proc_find(pid);
257 if (p == NULL) {
258 printf("set_pid_tclass proc_find(%d) \n", pid);
259 goto done;
260 }
261
262 /* Need a tfp */
263 lck_mtx_lock(tclass_lock);
264
265 tfp = find_tfp_by_pid(pid);
266 if (tclass == -1) {
267 if (tfp != NULL) {
268 free_tclass_for_proc(tfp);
269 error = 0;
270 }
271 lck_mtx_unlock(tclass_lock);
272 goto done;
273 } else {
274 if (tfp == NULL) {
275 tfp = alloc_tclass_for_proc(pid, NULL, tclass);
276 if (tfp == NULL) {
277 lck_mtx_unlock(tclass_lock);
278 error = ENOBUFS;
279 goto done;
280 }
281 } else {
282 tfp->tfp_class = tclass;
283 }
284 }
285 lck_mtx_unlock(tclass_lock);
286
287 if (tfp != NULL) {
288 proc_fdlock(p);
289
290 fdp = p->p_fd;
291 for (i = 0; i < fdp->fd_nfiles; i++) {
292 struct socket *so;
293
294 fp = fdp->fd_ofiles[i];
295 if (fp == NULL || (fdp->fd_ofileflags[i] & UF_RESERVED) != 0 ||
296 fp->f_fglob->fg_type != DTYPE_SOCKET)
297 continue;
298
299 so = (struct socket *)fp->f_fglob->fg_data;
300 if (so->so_proto->pr_domain->dom_family != AF_INET &&
301 so->so_proto->pr_domain->dom_family != AF_INET6)
302 continue;
303 socket_lock(so, 1);
304 error = so_set_traffic_class(so, tclass != -1 ? tclass : SO_TC_BE);
305 socket_unlock(so, 1);
306 if (error != 0) {
307 printf("set_pid_tclass so_set_traffic_class(%p, %d) failed %d\n", so, tclass, error);
308 error = 0;
309 }
310 }
311
312 proc_fdunlock(p);
313 }
314
315 error = 0;
316done:
317 if (p != NULL)
318 proc_rele(p);
319
320 return error;
321}
322
323int
324set_pname_tclass(const char *pname, int tclass)
325{
326 int error = EINVAL;
327 struct tclass_for_proc *tfp;
328
329 lck_mtx_lock(tclass_lock);
330
331 tfp = find_tfp_by_pname(pname);
332 if (tclass == -1) {
333 if (tfp != NULL)
334 free_tclass_for_proc(tfp);
335 } else {
336 if (tfp == NULL) {
337 tfp = alloc_tclass_for_proc(-1, pname, tclass);
338 if (tfp == NULL) {
339 lck_mtx_unlock(tclass_lock);
340 error = ENOBUFS;
341 goto done;
342 }
343 } else {
344 tfp->tfp_class = tclass;
345 }
346 }
347 lck_mtx_unlock(tclass_lock);
348
349 error = 0;
350done:
351
352 return error;
353}
354
355int
356get_pid_tclass(pid_t pid, int *tclass)
357{
358 int error = EINVAL;
359 proc_t p = NULL;
360 struct tclass_for_proc *tfp;
361
362 *tclass = -1; /* Means not set */
363
364 p = proc_find(pid);
365 if (p == NULL) {
366 printf("get_pid_tclass proc_find(%d) \n", pid);
367 goto done;
368 }
369
370 /* Need a tfp */
371 lck_mtx_lock(tclass_lock);
372
373 tfp = find_tfp_by_pid(pid);
374 if (tfp != NULL) {
375 *tclass = tfp->tfp_class ;
376 error = 0;
377 }
378 lck_mtx_unlock(tclass_lock);
379done:
380 if (p != NULL)
381 proc_rele(p);
382
383 return error;
384}
385
386int
387get_pname_tclass(const char *pname, int *tclass)
388{
389 int error = EINVAL;
390 struct tclass_for_proc *tfp;
391
392 *tclass = -1; /* Means not set */
393
394 /* Need a tfp */
395 lck_mtx_lock(tclass_lock);
396
397 tfp = find_tfp_by_pname(pname);
398 if (tfp != NULL) {
399 *tclass = tfp->tfp_class ;
400 error = 0;
401 }
402 lck_mtx_unlock(tclass_lock);
403
404 return error;
405}
406
407
408
409/*
410 * Setting options requires privileges
411 */
412__private_extern__ int
413so_set_tcdbg(struct socket *so, struct so_tcdbg *so_tcdbg)
414{
415 int error = 0;
416
417 if ((so->so_state & SS_PRIV) == 0)
418 return EPERM;
419
420 socket_unlock(so, 0);
421
422 switch (so_tcdbg->so_tcdbg_cmd) {
423 case SO_TCDBG_PID:
424 error = set_pid_tclass(so_tcdbg->so_tcdbg_pid, so_tcdbg->so_tcdbg_tclass);
425 break;
426
427 case SO_TCDBG_PNAME:
428 error = set_pname_tclass(so_tcdbg->so_tcdbg_pname, so_tcdbg->so_tcdbg_tclass);
429 break;
430
431 case SO_TCDBG_PURGE:
432 error = purge_tclass_for_proc();
433 break;
434
435 case SO_TCDBG_FLUSH:
436 error = flush_tclass_for_proc();
437 break;
438
439 default:
440 error = EINVAL;
441 break;
442
443 }
444
445 socket_lock(so, 0);
446
447 return error;
448}
449
450/*
451 * Not required to be privileged to get
452 */
453__private_extern__ int
454sogetopt_tcdbg(struct socket *so, struct sockopt *sopt)
455{
456 int error = 0;
457 struct so_tcdbg so_tcdbg;
458 void *buf = NULL;
459 size_t len = sopt->sopt_valsize;
460
461 error = sooptcopyin(sopt, &so_tcdbg, sizeof(struct so_tcdbg), sizeof(struct so_tcdbg));
462 if (error != 0)
463 return error;
464
465 sopt->sopt_valsize = len;
466
467 socket_unlock(so, 0);
468
469 switch (so_tcdbg.so_tcdbg_cmd) {
470 case SO_TCDBG_PID:
471 error = get_pid_tclass(so_tcdbg.so_tcdbg_pid, &so_tcdbg.so_tcdbg_tclass);
472 break;
473
474 case SO_TCDBG_PNAME:
475 error = get_pname_tclass(so_tcdbg.so_tcdbg_pname, &so_tcdbg.so_tcdbg_tclass);
476 break;
477
478 case SO_TCDBG_COUNT:
479 lck_mtx_lock(tclass_lock);
480 so_tcdbg.so_tcdbg_count = tfp_count;
481 lck_mtx_unlock(tclass_lock);
482 break;
483
484 case SO_TCDBG_LIST: {
485 struct tclass_for_proc *tfp;
486 int n, alloc_count;
487 struct so_tcdbg *ptr;
488
489 lck_mtx_lock(tclass_lock);
490 if ((alloc_count = tfp_count) == 0) {
491 lck_mtx_unlock(tclass_lock);
492 error = EINVAL;
493 break;
494 }
495 len = alloc_count * sizeof(struct so_tcdbg);
496 lck_mtx_unlock(tclass_lock);
497
498 buf = _MALLOC(len, M_TEMP, M_WAITOK | M_ZERO);
499 if (buf == NULL) {
500 error = ENOBUFS;
501 break;
502 }
503
504 lck_mtx_lock(tclass_lock);
505 n = 0;
506 ptr = (struct so_tcdbg *)buf;
507 TAILQ_FOREACH(tfp, &tfp_head, tfp_link) {
508 if (++n > alloc_count)
509 break;
510 if (tfp->tfp_pid != -1) {
511 ptr->so_tcdbg_cmd = SO_TCDBG_PID;
512 ptr->so_tcdbg_pid = tfp->tfp_pid;
513 } else {
514 ptr->so_tcdbg_cmd = SO_TCDBG_PNAME;
515 ptr->so_tcdbg_pid = -1;
516 strlcpy(ptr->so_tcdbg_pname, tfp->tfp_pname, sizeof(ptr->so_tcdbg_pname));
517 }
518 ptr->so_tcdbg_tclass = tfp->tfp_class;
519 ptr++;
520 }
521
522 lck_mtx_unlock(tclass_lock);
523 }
524 break;
525
526 default:
527 error = EINVAL;
528 break;
529
530 }
531
532 socket_lock(so, 0);
533
534 if (error == 0) {
535 if (buf == NULL) {
536 error = sooptcopyout(sopt, &so_tcdbg, sizeof(struct so_tcdbg));
537 } else {
538 error = sooptcopyout(sopt, buf, len);
539 _FREE(buf, M_TEMP);
540 }
541 }
542 return error;
543}
544
545
546__private_extern__ int
547so_set_traffic_class(struct socket *so, int optval)
548{
549 int error = 0;
550
551 if (optval < SO_TC_BE || optval > SO_TC_VO) {
552 error = EINVAL;
553 } else {
554 so->so_traffic_class = optval;
555
556 if ((INP_SOCKAF(so) == AF_INET || INP_SOCKAF(so) == AF_INET6) &&
557 INP_SOCKTYPE(so) == SOCK_STREAM) {
558 set_tcp_stream_priority(so);
559 }
560 }
561 return error;
562}
563
564__private_extern__ void
565so_set_default_traffic_class(struct socket *so)
566{
567 int sotc = SO_TC_BE;
568
569 if (tfp_count > 0 && (INP_SOCKAF(so) == AF_INET || INP_SOCKAF(so) == AF_INET6)) {
570 sotc = get_tclass_for_curr_proc();
571 }
572
573 so->so_traffic_class = sotc;
574
575 return;
576}
577
578
579__private_extern__ int
580mbuf_traffic_class_from_control(struct mbuf *control)
581{
582 struct cmsghdr *cm;
583
584 for (cm = M_FIRST_CMSGHDR(control);
585 cm != NULL;
586 cm = M_NXT_CMSGHDR(control, cm)) {
587 int tc;
588
589 if (cm->cmsg_len < sizeof(struct cmsghdr))
590 break;
591
592 if (cm->cmsg_level != SOL_SOCKET ||
593 cm->cmsg_type != SO_TRAFFIC_CLASS)
594 continue;
595 if (cm->cmsg_len != CMSG_LEN(sizeof(int)))
596 continue;
597
598 tc = *(int *)CMSG_DATA(cm);
599
600 switch (tc) {
601 case SO_TC_BE:
602 return MBUF_TC_BE;
603 case SO_TC_BK:
604 return MBUF_TC_BK;
605 case SO_TC_VI:
606 return MBUF_TC_VI;
607 case SO_TC_VO:
608 return MBUF_TC_VO;
609 default:
610 break;
611 }
612 }
613
614 return MBUF_TC_UNSPEC;
615}
616
617__private_extern__ int
618dscp_code_from_mbuf_tclass(int mtc)
619{
620 int dscp_code;
621
622 switch (mtc) {
623 default:
624 case MBUF_TC_BE:
625 dscp_code = 0;
626 break;
627 case MBUF_TC_BK:
628 dscp_code = 0x08;
629 break;
630 case MBUF_TC_VI:
631 dscp_code = 0x20;
632 break;
633 case MBUF_TC_VO:
634 dscp_code = 0x30;
635 break;
636 }
637
638 return dscp_code;
639}
640
641__private_extern__ void
642so_recv_data_stat(struct socket *so, struct mbuf *m, size_t off)
643{
644 uint32_t sotc = m->m_pkthdr.prio;
645
646 if (sotc >= SO_TC_STATS_MAX)
647 sotc = SO_TC_BE;
648
649 so->so_tc_stats[sotc].rxpackets += 1;
650 so->so_tc_stats[sotc].rxbytes += ((m->m_flags & M_PKTHDR) ? m->m_pkthdr.len : 0) + off;
651
652 return;
653}
654
655__private_extern__ void
656set_tcp_stream_priority(struct socket *so)
657{
658 struct tcpcb *tp = intotcpcb(sotoinpcb(so));
659
660 /* If the socket was marked as a background socket or if the
661 * traffic class is set to background with traffic class socket
662 * option then make both send and recv side of the stream to be
663 * background. The variable sotcdb which can be set with sysctl
664 * is used to disable these settings for testing.
665 */
666 if (soisbackground(so) || so->so_traffic_class == SO_TC_BK) {
667 if ((sotcdb & SOTCDB_NO_SENDTCPBG) != 0) {
668 if (tp->tcp_cc_index == TCP_CC_ALGO_BACKGROUND_INDEX)
669 tcp_set_foreground_cc(so);
670 } else {
671 if (tp->tcp_cc_index != TCP_CC_ALGO_BACKGROUND_INDEX)
672 tcp_set_background_cc(so);
673 }
674
675 /* Set receive side background flags */
676 if ((sotcdb & SOTCDB_NO_RECVTCPBG) != 0) {
677 so->so_traffic_mgt_flags &= ~(TRAFFIC_MGT_TCP_RECVBG);
678 } else {
679 so->so_traffic_mgt_flags |= TRAFFIC_MGT_TCP_RECVBG;
680 }
681 } else {
682 so->so_traffic_mgt_flags &= ~(TRAFFIC_MGT_TCP_RECVBG);
683 if (tp->tcp_cc_index == TCP_CC_ALGO_BACKGROUND_INDEX)
684 tcp_set_foreground_cc(so);
685 }
686 return;
687}
688
689/*
690 * Set traffic class to an IPv4 or IPv6 packet
691 * - mark the mbuf
692 * - set the DSCP code following the WMM mapping
693 */
694__private_extern__ void
695set_packet_tclass(struct mbuf *m, struct socket *so, int in_mtc, int isipv6)
696{
697 int mtc = MBUF_TC_BE; /* Best effort by default */
698 struct inpcb *inp = sotoinpcb(so); /* in6pcb and inpcb are the same */
699 struct ip *ip = mtod(m, struct ip *);
700#if INET6
701 struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
702#endif /* INET6 */
703
704 if (!(m->m_flags & M_PKTHDR))
705 return;
706
707 /*
708 * Here is the precedence:
709 * 1) TRAFFIC_MGT_SO_BACKGROUND trumps all
710 * 2) Traffic class passed via ancillary data to sendmsdg(2)
711 * 3) Traffic class socket option last
712 */
713 if (soisbackground(so)) {
714 mtc = MBUF_TC_BK;
715 } else if (in_mtc != MBUF_TC_UNSPEC) {
716 if (in_mtc >= MBUF_TC_BE && in_mtc <= MBUF_TC_VO)
717 mtc = in_mtc;
718 } else {
719 switch (so->so_traffic_class) {
720 case SO_TC_BE:
721 mtc = MBUF_TC_BE;
722 break;
723 case SO_TC_BK:
724 mtc = MBUF_TC_BK;
725 break;
726 case SO_TC_VI:
727 mtc = MBUF_TC_VI;
728 break;
729 case SO_TC_VO:
730 mtc = MBUF_TC_VO;
731 break;
732 default:
733 break;
734 }
735 }
736
737 /*
738 * Set the traffic class in the mbuf packet header prio field
739 */
740 if ((sotcdb & SOTCDB_NO_MTC))
741 goto no_mbtc;
742 m->m_pkthdr.prio = mtc;
743
744no_mbtc:
745 /*
746 * Quick exit when best effort
747 */
748 if (mtc == MBUF_TC_BE)
749 goto no_dscp;
750 /*
751 * Now let set the DSCP code in IPv4 or IPv6 header
752 * By default do this only for local traffic if a code is not already set
753 */
754 if ((sotcdb & SOTCDB_NO_DSCP))
755 goto no_dscp;
756
757 /*
758 * Test if a IP TOS or IPV6 TCLASS has already been set on the socket or the raw packet
759 */
760 if ((sotcdb & SOTCDB_NO_DSCPTST) == 0) {
761#if INET6
762 if (isipv6)
763 {
764 if ((so->so_type == SOCK_RAW && (ip6->ip6_flow & htonl(0xff << 20)) != 0) ||
765 (inp->in6p_outputopts && inp->in6p_outputopts->ip6po_tclass != -1))
766 goto no_dscp;
767 }
768 else
769#endif /* INET6 */
770 {
771 if ((so->so_type == SOCK_RAW && (inp->inp_flags & INP_HDRINCL)) ||
772 inp->inp_ip_tos != 0)
773 goto no_dscp;
774 }
775 }
776
777 /*
778 * Test if destination is local
779 */
780 if ((sotcdb & SOTCDB_NO_LCLTST) == 0) {
781 int islocal = 0;
782 struct route *ro = &inp->inp_route;
783
784 if (so->so_type == SOCK_STREAM) {
785 struct tcpcb *tp = intotcpcb(inp);
786
787 if ((tp->t_flags & TF_LOCAL))
788 islocal = 1;
789 }
790 else
791#if INET6
792 if (isipv6)
793 {
794 if ((ro != NULL && ro->ro_rt != NULL &&
795 (ro->ro_rt->rt_gateway->sa_family == AF_LINK ||
796 (ro->ro_rt->rt_ifp->if_flags & IFF_LOOPBACK))) ||
797 in6addr_local(&ip6->ip6_dst))
798 islocal = 1;
799 }
800 else
801#endif /* INET6 */
802 {
803 if ((ro != NULL && ro->ro_rt != NULL &&
804 (ro->ro_rt->rt_gateway->sa_family == AF_LINK ||
805 (ro->ro_rt->rt_ifp->if_flags & IFF_LOOPBACK))) ||
806 inaddr_local(ip->ip_dst))
807 islocal = 1;
808 }
809 if (islocal == 0)
810 goto no_dscp;
811 }
812
813#if INET6
814 if (isipv6)
815 ip6->ip6_flow |=
816 htonl(dscp_code_from_mbuf_tclass(m->m_pkthdr.prio) << 20);
817 else
818#endif /* INET6 */
819 ip->ip_tos |= dscp_code_from_mbuf_tclass(m->m_pkthdr.prio) << 2;
820
821no_dscp:
822 /*
823 * For TCP with background traffic class switch CC algo based on sysctl
824 */
825 if (so->so_type == SOCK_STREAM) {
826 set_tcp_stream_priority(so);
827 }
828
829 /*
830 * Assume socket and mbuf traffic class values are the same
831 * Also assume the socket lock is held
832 */
833 so->so_tc_stats[mtc].txpackets += 1;
834 so->so_tc_stats[mtc].txbytes += m->m_pkthdr.len;
835
836 return;
837}
838
839__private_extern__ void
840socket_tclass_init(void)
841{
842 tclass_lck_grp_attr = lck_grp_attr_alloc_init();
843 tclass_lck_grp = lck_grp_alloc_init("tclass", tclass_lck_grp_attr);
844 tclass_lck_attr = lck_attr_alloc_init();
845 if ((tclass_lock = lck_mtx_alloc_init(tclass_lck_grp, tclass_lck_attr)) == NULL) {
846 panic("failed to allocate memory for tclass\n");
847 }
848}
849
850