blob: d5df7ef6ba0106932fa4744e67b4e6e436d17c1b [file] [log] [blame]
Tyler Wear72388212021-09-09 14:49:02 -07001/*
2 * Copyright (C) 2021 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <linux/types.h>
18#include <linux/bpf.h>
Tyler Wear3ad80892022-02-03 15:14:44 -080019#include <linux/if_packet.h>
Tyler Wear72388212021-09-09 14:49:02 -070020#include <linux/ip.h>
21#include <linux/ipv6.h>
22#include <linux/if_ether.h>
23#include <linux/pkt_cls.h>
24#include <linux/tcp.h>
25#include <stdint.h>
26#include <netinet/in.h>
27#include <netinet/udp.h>
28#include <string.h>
29
30#include "bpf_helpers.h"
Tyler Wear3ad80892022-02-03 15:14:44 -080031#include "dscp_policy.h"
Tyler Wear72388212021-09-09 14:49:02 -070032
Tyler Wear72388212021-09-09 14:49:02 -070033DEFINE_BPF_MAP_GRW(switch_comp_map, ARRAY, int, uint64_t, 1, AID_SYSTEM)
34
Tyler Wear3ad80892022-02-03 15:14:44 -080035DEFINE_BPF_MAP_GRW(ipv4_socket_to_policies_map_A, HASH, uint64_t, RuleEntry, MAX_POLICIES,
Tyler Wear72388212021-09-09 14:49:02 -070036 AID_SYSTEM)
Tyler Wear3ad80892022-02-03 15:14:44 -080037DEFINE_BPF_MAP_GRW(ipv4_socket_to_policies_map_B, HASH, uint64_t, RuleEntry, MAX_POLICIES,
38 AID_SYSTEM)
39DEFINE_BPF_MAP_GRW(ipv6_socket_to_policies_map_A, HASH, uint64_t, RuleEntry, MAX_POLICIES,
40 AID_SYSTEM)
41DEFINE_BPF_MAP_GRW(ipv6_socket_to_policies_map_B, HASH, uint64_t, RuleEntry, MAX_POLICIES,
Tyler Wear72388212021-09-09 14:49:02 -070042 AID_SYSTEM)
43
Tyler Wear3ad80892022-02-03 15:14:44 -080044DEFINE_BPF_MAP_GRW(ipv4_dscp_policies_map, ARRAY, uint32_t, DscpPolicy, MAX_POLICIES,
45 AID_SYSTEM)
46DEFINE_BPF_MAP_GRW(ipv6_dscp_policies_map, ARRAY, uint32_t, DscpPolicy, MAX_POLICIES,
47 AID_SYSTEM)
48
49static inline __always_inline void match_policy(struct __sk_buff* skb, bool ipv4, bool is_eth) {
50 void* data = (void*)(long)skb->data;
51 const void* data_end = (void*)(long)skb->data_end;
52
53 const int l2_header_size = is_eth ? sizeof(struct ethhdr) : 0;
54 struct ethhdr* eth = is_eth ? data : NULL;
55
56 if (data + l2_header_size > data_end) return;
57
58 int zero = 0;
59 int hdr_size = 0;
60 uint64_t* selectedMap = bpf_switch_comp_map_lookup_elem(&zero);
Tyler Wear72388212021-09-09 14:49:02 -070061
62 // use this with HASH map so map lookup only happens once policies have been added?
63 if (!selectedMap) {
Tyler Wear3ad80892022-02-03 15:14:44 -080064 return;
Tyler Wear72388212021-09-09 14:49:02 -070065 }
66
67 // used for map lookup
68 uint64_t cookie = bpf_get_socket_cookie(skb);
Tyler Wear3ad80892022-02-03 15:14:44 -080069 if (!cookie)
70 return;
Tyler Wear72388212021-09-09 14:49:02 -070071
Tyler Wear3ad80892022-02-03 15:14:44 -080072 uint16_t sport = 0;
73 uint16_t dport = 0;
74 uint8_t protocol = 0; // TODO: Use are reserved value? Or int (-1) and cast to uint below?
75 struct in6_addr srcIp = {};
76 struct in6_addr dstIp = {};
77 uint8_t tos = 0; // Only used for IPv4
78 uint8_t priority = 0; // Only used for IPv6
79 uint8_t flow_lbl = 0; // Only used for IPv6
80 if (ipv4) {
81 const struct iphdr* const iph = is_eth ? (void*)(eth + 1) : data;
Tyler Wear72388212021-09-09 14:49:02 -070082 // Must have ipv4 header
Tyler Wear3ad80892022-02-03 15:14:44 -080083 if (data + l2_header_size + sizeof(*iph) > data_end) return;
Tyler Wear72388212021-09-09 14:49:02 -070084
85 // IP version must be 4
Tyler Wear3ad80892022-02-03 15:14:44 -080086 if (iph->version != 4) return;
Tyler Wear72388212021-09-09 14:49:02 -070087
88 // We cannot handle IP options, just standard 20 byte == 5 dword minimal IPv4 header
Tyler Wear3ad80892022-02-03 15:14:44 -080089 if (iph->ihl != 5) return;
Tyler Wear72388212021-09-09 14:49:02 -070090
Tyler Wear3ad80892022-02-03 15:14:44 -080091 // V4 mapped address in in6_addr sets 10/11 position to 0xff.
92 srcIp.s6_addr32[2] = htonl(0x0000ffff);
93 dstIp.s6_addr32[2] = htonl(0x0000ffff);
Tyler Wear72388212021-09-09 14:49:02 -070094
Tyler Wear3ad80892022-02-03 15:14:44 -080095 // Copy IPv4 address into in6_addr for easy comparison below.
96 srcIp.s6_addr32[3] = iph->saddr;
97 dstIp.s6_addr32[3] = iph->daddr;
98 protocol = iph->protocol;
99 tos = iph->tos;
100 hdr_size = sizeof(struct iphdr);
101 } else {
102 struct ipv6hdr* ip6h = is_eth ? (void*)(eth + 1) : data;
103 // Must have ipv6 header
104 if (data + l2_header_size + sizeof(*ip6h) > data_end) return;
Tyler Wear72388212021-09-09 14:49:02 -0700105
Tyler Wear3ad80892022-02-03 15:14:44 -0800106 if (ip6h->version != 6) return;
Tyler Wear72388212021-09-09 14:49:02 -0700107
Tyler Wear3ad80892022-02-03 15:14:44 -0800108 srcIp = ip6h->saddr;
109 dstIp = ip6h->daddr;
110 protocol = ip6h->nexthdr;
111 priority = ip6h->priority;
112 flow_lbl = ip6h->flow_lbl[0];
113 hdr_size = sizeof(struct ipv6hdr);
114 }
Tyler Wear72388212021-09-09 14:49:02 -0700115
Tyler Wear3ad80892022-02-03 15:14:44 -0800116 switch (protocol) {
117 case IPPROTO_UDP:
118 case IPPROTO_UDPLITE:
119 {
120 struct udphdr *udp;
121 udp = data + hdr_size;
122 if ((void*)(udp + 1) > data_end) return;
123 sport = udp->source;
124 dport = udp->dest;
125 }
126 break;
127 case IPPROTO_TCP:
128 {
129 struct tcphdr *tcp;
130 tcp = data + hdr_size;
131 if ((void*)(tcp + 1) > data_end) return;
132 sport = tcp->source;
133 dport = tcp->dest;
134 }
135 break;
136 default:
137 return;
138 }
139
140 RuleEntry* existingRule;
141 if (ipv4) {
142 if (*selectedMap == MAP_A) {
143 existingRule = bpf_ipv4_socket_to_policies_map_A_lookup_elem(&cookie);
144 } else {
145 existingRule = bpf_ipv4_socket_to_policies_map_B_lookup_elem(&cookie);
146 }
147 } else {
148 if (*selectedMap == MAP_A) {
149 existingRule = bpf_ipv6_socket_to_policies_map_A_lookup_elem(&cookie);
150 } else {
151 existingRule = bpf_ipv6_socket_to_policies_map_B_lookup_elem(&cookie);
152 }
153 }
154
155 if (existingRule && v6_equal(srcIp, existingRule->srcIp) &&
156 v6_equal(dstIp, existingRule->dstIp) &&
157 skb->ifindex == existingRule->ifindex &&
158 ntohs(sport) == htons(existingRule->srcPort) &&
159 ntohs(dport) == htons(existingRule->dstPort) &&
160 protocol == existingRule->proto) {
161 if (ipv4) {
162 int ecn = tos & 3;
163 uint8_t newDscpVal = (existingRule->dscpVal << 2) + ecn;
164 int oldDscpVal = tos >> 2;
Tyler Wear72388212021-09-09 14:49:02 -0700165 bpf_l3_csum_replace(skb, 1, oldDscpVal, newDscpVal, sizeof(uint8_t));
166 bpf_skb_store_bytes(skb, 1, &newDscpVal, sizeof(uint8_t), 0);
Tyler Wear3ad80892022-02-03 15:14:44 -0800167 } else {
168 uint8_t new_priority = (existingRule->dscpVal >> 2) + 0x60;
169 uint8_t new_flow_label = ((existingRule->dscpVal & 0xf) << 6) + (priority >> 6);
170 bpf_skb_store_bytes(skb, 0, &new_priority, sizeof(uint8_t), 0);
171 bpf_skb_store_bytes(skb, 1, &new_flow_label, sizeof(uint8_t), 0);
172 }
173 return;
174 }
175
176 // Linear scan ipv4_dscp_policies_map since no stored params match skb.
177 int bestScore = -1;
178 uint32_t bestMatch = 0;
179
180 for (register uint64_t i = 0; i < MAX_POLICIES; i++) {
181 int score = 0;
182 uint8_t tempMask = 0;
183 // Using a uint64 in for loop prevents infinite loop during BPF load,
184 // but the key is uint32, so convert back.
185 uint32_t key = i;
186
187 DscpPolicy* policy;
188 if (ipv4) {
189 policy = bpf_ipv4_dscp_policies_map_lookup_elem(&key);
190 } else {
191 policy = bpf_ipv6_dscp_policies_map_lookup_elem(&key);
Tyler Wear72388212021-09-09 14:49:02 -0700192 }
193
Tyler Wear3ad80892022-02-03 15:14:44 -0800194 // If the policy lookup failed, presentFields is 0, or iface index does not match
195 // index on skb buff, then we can continue to next policy.
196 if (!policy || policy->presentFields == 0 || policy->ifindex != skb->ifindex)
197 continue;
Tyler Wear72388212021-09-09 14:49:02 -0700198
Tyler Wear3ad80892022-02-03 15:14:44 -0800199 if ((policy->presentFields & SRC_IP_MASK_FLAG) == SRC_IP_MASK_FLAG &&
200 v6_equal(srcIp, policy->srcIp)) {
201 score++;
202 tempMask |= SRC_IP_MASK_FLAG;
203 }
204 if ((policy->presentFields & DST_IP_MASK_FLAG) == DST_IP_MASK_FLAG &&
205 v6_equal(dstIp, policy->dstIp)) {
206 score++;
207 tempMask |= DST_IP_MASK_FLAG;
208 }
209 if ((policy->presentFields & SRC_PORT_MASK_FLAG) == SRC_PORT_MASK_FLAG &&
210 ntohs(sport) == htons(policy->srcPort)) {
211 score++;
212 tempMask |= SRC_PORT_MASK_FLAG;
213 }
214 if ((policy->presentFields & DST_PORT_MASK_FLAG) == DST_PORT_MASK_FLAG &&
215 ntohs(dport) >= htons(policy->dstPortStart) &&
216 ntohs(dport) <= htons(policy->dstPortEnd)) {
217 score++;
218 tempMask |= DST_PORT_MASK_FLAG;
219 }
220 if ((policy->presentFields & PROTO_MASK_FLAG) == PROTO_MASK_FLAG &&
221 protocol == policy->proto) {
222 score++;
223 tempMask |= PROTO_MASK_FLAG;
224 }
Tyler Wear72388212021-09-09 14:49:02 -0700225
Tyler Wear3ad80892022-02-03 15:14:44 -0800226 if (score > bestScore && tempMask == policy->presentFields) {
227 bestMatch = i;
228 bestScore = score;
229 }
230 }
Tyler Wear72388212021-09-09 14:49:02 -0700231
Tyler Wear3ad80892022-02-03 15:14:44 -0800232 uint8_t new_tos= 0; // Can 0 be used as default forwarding value?
233 uint8_t new_priority = 0;
234 uint8_t new_flow_lbl = 0;
235 if (bestScore > 0) {
236 DscpPolicy* policy;
237 if (ipv4) {
238 policy = bpf_ipv4_dscp_policies_map_lookup_elem(&bestMatch);
239 } else {
240 policy = bpf_ipv6_dscp_policies_map_lookup_elem(&bestMatch);
241 }
242
243 if (policy) {
244 // TODO: if DSCP value is already set ignore?
245 if (ipv4) {
246 int ecn = tos & 3;
247 new_tos = (policy->dscpVal << 2) + ecn;
248 } else {
249 new_priority = (policy->dscpVal >> 2) + 0x60;
250 new_flow_lbl = ((policy->dscpVal & 0xf) << 6) + (flow_lbl >> 6);
251
252 // Set IPv6 curDscp value to stored value and recalulate priority
253 // and flow label during next use.
254 new_tos = policy->dscpVal;
Tyler Wear72388212021-09-09 14:49:02 -0700255 }
256 }
Tyler Wear3ad80892022-02-03 15:14:44 -0800257 } else return;
Tyler Wear72388212021-09-09 14:49:02 -0700258
Tyler Wear3ad80892022-02-03 15:14:44 -0800259 RuleEntry value = {
260 .srcIp = srcIp,
261 .dstIp = dstIp,
262 .ifindex = skb->ifindex,
263 .srcPort = sport,
264 .dstPort = dport,
265 .proto = protocol,
266 .dscpVal = new_tos,
267 };
Tyler Wear72388212021-09-09 14:49:02 -0700268
Tyler Wear3ad80892022-02-03 15:14:44 -0800269 //Update map with new policy.
270 if (ipv4) {
Tyler Wear72388212021-09-09 14:49:02 -0700271 if (*selectedMap == MAP_A) {
272 bpf_ipv4_socket_to_policies_map_A_update_elem(&cookie, &value, BPF_ANY);
273 } else {
274 bpf_ipv4_socket_to_policies_map_B_update_elem(&cookie, &value, BPF_ANY);
275 }
Tyler Wear3ad80892022-02-03 15:14:44 -0800276 } else {
Tyler Wear72388212021-09-09 14:49:02 -0700277 if (*selectedMap == MAP_A) {
Tyler Wear3ad80892022-02-03 15:14:44 -0800278 bpf_ipv6_socket_to_policies_map_A_update_elem(&cookie, &value, BPF_ANY);
Tyler Wear72388212021-09-09 14:49:02 -0700279 } else {
Tyler Wear3ad80892022-02-03 15:14:44 -0800280 bpf_ipv6_socket_to_policies_map_B_update_elem(&cookie, &value, BPF_ANY);
Tyler Wear72388212021-09-09 14:49:02 -0700281 }
Tyler Wear3ad80892022-02-03 15:14:44 -0800282 }
Tyler Wear72388212021-09-09 14:49:02 -0700283
Tyler Wear3ad80892022-02-03 15:14:44 -0800284 // Need to store bytes after updating map or program will not load.
285 if (ipv4 && new_tos != (tos & 252)) {
286 int oldDscpVal = tos >> 2;
287 bpf_l3_csum_replace(skb, 1, oldDscpVal, new_tos, sizeof(uint8_t));
288 bpf_skb_store_bytes(skb, 1, &new_tos, sizeof(uint8_t), 0);
289 } else if (!ipv4 && (new_priority != priority || new_flow_lbl != flow_lbl)) {
290 bpf_skb_store_bytes(skb, 0, &new_priority, sizeof(uint8_t), 0);
291 bpf_skb_store_bytes(skb, 1, &new_flow_lbl, sizeof(uint8_t), 0);
292 }
293 return;
294}
Tyler Wear72388212021-09-09 14:49:02 -0700295
Tyler Wear3ad80892022-02-03 15:14:44 -0800296DEFINE_BPF_PROG_KVER("schedcls/set_dscp_ether", AID_ROOT, AID_SYSTEM,
297 schedcls_set_dscp_ether, KVER(5, 4, 0))
298(struct __sk_buff* skb) {
299
300 if (skb->pkt_type != PACKET_HOST) return TC_ACT_PIPE;
301
302 if (skb->protocol == htons(ETH_P_IP)) {
303 match_policy(skb, true, true);
304 } else if (skb->protocol == htons(ETH_P_IPV6)) {
305 match_policy(skb, false, true);
306 }
307
308 // Always return TC_ACT_PIPE
309 return TC_ACT_PIPE;
310}
311
312DEFINE_BPF_PROG_KVER("schedcls/set_dscp_raw_ip", AID_ROOT, AID_SYSTEM,
313 schedcls_set_dscp_raw_ip, KVER(5, 4, 0))
314(struct __sk_buff* skb) {
315 if (skb->protocol == htons(ETH_P_IP)) {
316 match_policy(skb, true, false);
317 } else if (skb->protocol == htons(ETH_P_IPV6)) {
318 match_policy(skb, false, false);
Tyler Wear72388212021-09-09 14:49:02 -0700319 }
320
321 // Always return TC_ACT_PIPE
322 return TC_ACT_PIPE;
323}
324
325LICENSE("Apache 2.0");
326CRITICAL("Connectivity");