]>
Commit | Line | Data |
---|---|---|
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 | ||
59 | extern char *proc_name_address(void *p); | |
60 | ||
61 | static int tfp_count = 0; | |
62 | ||
63 | static TAILQ_HEAD(, tclass_for_proc) tfp_head = TAILQ_HEAD_INITIALIZER(tfp_head); | |
64 | ||
65 | struct 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 | ||
72 | extern void tcp_set_background_cc(struct socket *); | |
73 | extern void tcp_set_foreground_cc(struct socket *); | |
74 | ||
75 | int dscp_code_from_mbuf_tclass(int ); | |
76 | ||
77 | static int get_pid_tclass(pid_t , int *); | |
78 | static int get_pname_tclass(const char * , int *); | |
79 | static int set_pid_tclass(pid_t , int ); | |
80 | static int set_pname_tclass(const char * , int ); | |
81 | static int purge_tclass_for_proc(void); | |
82 | static int flush_tclass_for_proc(void); | |
83 | ||
84 | ||
85 | static lck_grp_attr_t *tclass_lck_grp_attr = NULL; /* mutex group attributes */ | |
86 | static lck_grp_t *tclass_lck_grp = NULL; /* mutex group definition */ | |
87 | static lck_attr_t *tclass_lck_attr = NULL; /* mutex attributes */ | |
88 | static lck_mtx_t *tclass_lock = NULL; | |
89 | ||
90 | /* | |
91 | * Must be called with tclass_lock held | |
92 | */ | |
93 | static struct tclass_for_proc * | |
94 | find_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 | */ | |
108 | static struct tclass_for_proc * | |
109 | find_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 | ||
120 | static int | |
121 | get_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 | */ | |
147 | int | |
148 | purge_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 | */ | |
179 | static void | |
180 | free_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 | */ | |
192 | int | |
193 | flush_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 | */ | |
213 | static struct tclass_for_proc * | |
214 | alloc_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 | */ | |
246 | int | |
247 | set_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; | |
316 | done: | |
317 | if (p != NULL) | |
318 | proc_rele(p); | |
319 | ||
320 | return error; | |
321 | } | |
322 | ||
323 | int | |
324 | set_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; | |
350 | done: | |
351 | ||
352 | return error; | |
353 | } | |
354 | ||
355 | int | |
356 | get_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); | |
379 | done: | |
380 | if (p != NULL) | |
381 | proc_rele(p); | |
382 | ||
383 | return error; | |
384 | } | |
385 | ||
386 | int | |
387 | get_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 | |
413 | so_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 | |
454 | sogetopt_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 | |
547 | so_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 | |
565 | so_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 | |
580 | mbuf_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 | |
618 | dscp_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 | |
642 | so_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 | |
656 | set_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 | |
695 | set_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 | ||
744 | no_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 | ||
821 | no_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 | |
840 | socket_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 |