xref: /xnu-11215.41.3/bsd/skywalk/nexus/flowswitch/fsw_qos.c (revision 33de042d024d46de5ff4e89f2471de6608e37fa4)
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 *__single ip;
67 	struct ip6_hdr *__single ip6;
68 
69 	if (pkt->pkt_flow->flow_ip_ver == IPVERSION) {
70 		ip = __unsafe_forge_single(struct ip *,
71 		    pkt->pkt_flow->flow_ip_hdr);
72 		fsw_qos_set_ip_tos(ip, dscp);
73 	} else {
74 		ASSERT(pkt->pkt_flow->flow_ip_ver == IPV6_VERSION);
75 		ip6 = __unsafe_forge_single(struct ip6_hdr *,
76 		    pkt->pkt_flow->flow_ip_hdr);
77 		fsw_qos_set_ipv6_tc(ip6, dscp);
78 	}
79 
80 	if (pkt->pkt_pflags & PKT_F_MBUF_DATA) {
81 		if (pkt->pkt_flow->flow_ip_ver == IPVERSION) {
82 			ip = (struct ip *__single)(void *)
83 			    (m_mtod_current(pkt->pkt_mbuf) + pkt->pkt_l2_len);
84 			fsw_qos_set_ip_tos(ip, dscp);
85 		} else {
86 			ip6 = (struct ip6_hdr *__single)(void *)
87 			    (m_mtod_current(pkt->pkt_mbuf) + pkt->pkt_l2_len);
88 			fsw_qos_set_ipv6_tc(ip6, dscp);
89 		}
90 	}
91 }
92 
93 void
fsw_qos_mark(struct nx_flowswitch * fsw,struct flow_entry * fe,struct __kern_packet * pkt)94 fsw_qos_mark(struct nx_flowswitch *fsw, struct flow_entry *fe,
95     struct __kern_packet *pkt)
96 {
97 	struct ifnet *ifp = fsw->fsw_ifp;
98 	uint8_t dscp = 0;
99 
100 	ASSERT(KPKT_VALID_SVC(fe->fe_svc_class));
101 
102 	/* if unspecified, use flow's default traffic class */
103 	if (__improbable(!KPKT_VALID_SVC(pkt->pkt_svc_class))) {
104 		pkt->pkt_svc_class = fe->fe_svc_class;
105 	}
106 
107 	/* QoS marking not enabled */
108 	if ((ifp->if_eflags & IFEF_QOSMARKING_ENABLED) == 0 ||
109 	    ifp->if_qosmarking_mode == IFRTYPE_QOSMARKING_MODE_NONE) {
110 		SK_DF(SK_VERB_QOS, "%s: QoS Marking off", if_name(ifp));
111 		return;
112 	}
113 
114 	/* QoS enabled, whitelisted app? */
115 	if ((fe->fe_flags & FLOWENTF_QOS_MARKING) == 0) {
116 		/* limit dscp and svc_class to BE/BK and DF */
117 		switch (pkt->pkt_svc_class) {
118 		case PKT_SC_BE:
119 		case PKT_SC_BK:
120 		case PKT_SC_BK_SYS:
121 		case PKT_SC_CTL:
122 			break;
123 		default:
124 			pkt->pkt_svc_class = PKT_SC_BE;
125 			break;
126 		}
127 		SK_DF(SK_VERB_QOS, "%s: restrict flow svc to BE", if_name(ifp));
128 	}
129 
130 	switch (ifp->if_qosmarking_mode) {
131 	case IFRTYPE_QOSMARKING_FASTLANE:
132 		dscp = fastlane_sc_to_dscp(pkt->pkt_svc_class);
133 		break;
134 	case IFRTYPE_QOSMARKING_RFC4594:
135 		dscp = rfc4594_sc_to_dscp(pkt->pkt_svc_class);
136 		break;
137 #if (DEBUG || DEVELOPMENT)
138 	case IFRTYPE_QOSMARKING_CUSTOM:
139 		dscp = custom_sc_to_dscp(pkt->pkt_svc_class);
140 		break;
141 #endif /* (DEBUG || DEVELOPMENT) */
142 	default:
143 		panic("%s: QoS Marking mode invalid!", if_name(ifp));
144 		/* NOTREACHED */
145 		__builtin_unreachable();
146 	}
147 
148 	SK_DF(SK_VERB_QOS, "%s: set dscp to %02d", if_name(ifp), dscp);
149 	fsw_qos_set_pkt_dscp(pkt, dscp);
150 }
151 
152 boolean_t
fsw_qos_default_restricted()153 fsw_qos_default_restricted()
154 {
155 	return !!net_qos_policy_restricted;
156 }
157