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