xref: /xnu-10002.1.13/bsd/skywalk/nexus/flowswitch/fsw_qos.c (revision 1031c584a5e37aff177559b9f69dbd3c8c3fd30a)
1 /*
2  * Copyright (c) 2017-2019 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 <skywalk/os_skywalk_private.h>
30 #include <skywalk/nexus/flowswitch/fsw_var.h>
31 
32 #include <netinet/in_tclass.h>
33 
34 static inline uint16_t
fsw_qos_csum_fixup(uint16_t cksum,uint16_t old,uint16_t new)35 fsw_qos_csum_fixup(uint16_t cksum, uint16_t old, uint16_t new)
36 {
37 	uint32_t l;
38 
39 	l = cksum + old - new;
40 	l = (l >> 16) + (l & 0xffff);
41 	l = l & 0xffff;
42 	return (uint16_t)l;
43 }
44 
45 static inline void
fsw_qos_set_ip_tos(struct ip * ip,uint8_t dscp)46 fsw_qos_set_ip_tos(struct ip *ip, uint8_t dscp)
47 {
48 	uint8_t old_tos;
49 	old_tos = ip->ip_tos;
50 	ip->ip_tos &= IPTOS_ECN_MASK;
51 	ip->ip_tos |= (u_char)(dscp << IPTOS_DSCP_SHIFT);
52 	ip->ip_sum = fsw_qos_csum_fixup(ip->ip_sum, htons(old_tos),
53 	    htons(ip->ip_tos));
54 }
55 
56 static inline void
fsw_qos_set_ipv6_tc(struct ip6_hdr * ip6,uint8_t dscp)57 fsw_qos_set_ipv6_tc(struct ip6_hdr *ip6, uint8_t dscp)
58 {
59 	ip6->ip6_flow &= ~htonl(IP6FLOW_DSCP_MASK);
60 	ip6->ip6_flow |= htonl((u_int32_t)dscp << IP6FLOW_DSCP_SHIFT);
61 }
62 
63 static inline void
fsw_qos_set_pkt_dscp(struct __kern_packet * pkt,uint8_t dscp)64 fsw_qos_set_pkt_dscp(struct __kern_packet *pkt, uint8_t dscp)
65 {
66 	struct ip *ip;
67 	struct ip6_hdr *ip6;
68 
69 	if (pkt->pkt_flow->flow_ip_ver == IPVERSION) {
70 		ip = (struct ip *)pkt->pkt_flow->flow_ip_hdr;
71 		fsw_qos_set_ip_tos(ip, dscp);
72 	} else {
73 		ASSERT(pkt->pkt_flow->flow_ip_ver == IPV6_VERSION);
74 		ip6 = (struct ip6_hdr *)pkt->pkt_flow->flow_ip_hdr;
75 		fsw_qos_set_ipv6_tc(ip6, dscp);
76 	}
77 
78 	if (pkt->pkt_pflags & PKT_F_MBUF_DATA) {
79 		if (pkt->pkt_flow->flow_ip_ver == IPVERSION) {
80 			ip = (struct ip *)(void *)
81 			    (pkt->pkt_mbuf->m_data + pkt->pkt_l2_len);
82 			fsw_qos_set_ip_tos(ip, dscp);
83 		} else {
84 			ip6 = (struct ip6_hdr *)(void *)
85 			    (pkt->pkt_mbuf->m_data + pkt->pkt_l2_len);
86 			fsw_qos_set_ipv6_tc(ip6, dscp);
87 		}
88 	}
89 }
90 
91 void
fsw_qos_mark(struct nx_flowswitch * fsw,struct flow_entry * fe,struct __kern_packet * pkt)92 fsw_qos_mark(struct nx_flowswitch *fsw, struct flow_entry *fe,
93     struct __kern_packet *pkt)
94 {
95 	struct ifnet *ifp = fsw->fsw_ifp;
96 	uint8_t dscp = 0;
97 
98 	ASSERT(KPKT_VALID_SVC(fe->fe_svc_class));
99 
100 	/* if unspecified, use flow's default traffic class */
101 	if (__improbable(!KPKT_VALID_SVC(pkt->pkt_svc_class))) {
102 		pkt->pkt_svc_class = fe->fe_svc_class;
103 	}
104 
105 	/* QoS marking not enabled */
106 	if ((ifp->if_eflags & IFEF_QOSMARKING_ENABLED) == 0 ||
107 	    ifp->if_qosmarking_mode == IFRTYPE_QOSMARKING_MODE_NONE) {
108 		SK_DF(SK_VERB_QOS, "%s: QoS Marking off", if_name(ifp));
109 		return;
110 	}
111 
112 	/* QoS enabled, whitelisted app? */
113 	if ((fe->fe_flags & FLOWENTF_QOS_MARKING) == 0) {
114 		/* limit dscp and svc_class to BE/BK and DF */
115 		switch (pkt->pkt_svc_class) {
116 		case PKT_SC_BE:
117 		case PKT_SC_BK:
118 		case PKT_SC_BK_SYS:
119 		case PKT_SC_CTL:
120 			break;
121 		default:
122 			pkt->pkt_svc_class = PKT_SC_BE;
123 			break;
124 		}
125 		SK_DF(SK_VERB_QOS, "%s: restrict flow svc to BE", if_name(ifp));
126 	}
127 
128 	switch (ifp->if_qosmarking_mode) {
129 	case IFRTYPE_QOSMARKING_FASTLANE:
130 		dscp = fastlane_sc_to_dscp(pkt->pkt_svc_class);
131 		break;
132 	case IFRTYPE_QOSMARKING_RFC4594:
133 		dscp = rfc4594_sc_to_dscp(pkt->pkt_svc_class);
134 		break;
135 #if (DEBUG || DEVELOPMENT)
136 	case IFRTYPE_QOSMARKING_CUSTOM:
137 		dscp = custom_sc_to_dscp(pkt->pkt_svc_class);
138 		break;
139 #endif /* (DEBUG || DEVELOPMENT) */
140 	default:
141 		panic("%s: QoS Marking mode invalid!", if_name(ifp));
142 		/* NOTREACHED */
143 		__builtin_unreachable();
144 	}
145 
146 	SK_DF(SK_VERB_QOS, "%s: set dscp to %02d", if_name(ifp), dscp);
147 	fsw_qos_set_pkt_dscp(pkt, dscp);
148 }
149 
150 boolean_t
fsw_qos_default_restricted()151 fsw_qos_default_restricted()
152 {
153 	return !!net_qos_policy_restricted;
154 }
155