blob: fcf3a724f91942a8489267675000045fe080cbb1 [file] [log] [blame]
Chenbo Feng75b410b2018-10-10 15:01:19 -07001/*
2 * Copyright (C) 2018 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/*
Chenbo Fengc1dd7642018-12-22 11:41:20 -080018 * This h file together with netd.c is used for compiling the eBPF kernel
Chenbo Feng4c9e9ec2018-10-16 20:31:52 -070019 * program.
Chenbo Feng75b410b2018-10-10 15:01:19 -070020 */
21
Joel Fernandes (Google)14ba8882019-02-12 12:49:03 -050022#include <bpf_helpers.h>
Chenbo Feng75b410b2018-10-10 15:01:19 -070023#include <linux/bpf.h>
24#include <linux/if.h>
25#include <linux/if_ether.h>
26#include <linux/in.h>
27#include <linux/in6.h>
28#include <linux/ip.h>
29#include <linux/ipv6.h>
30#include <stdbool.h>
31#include <stdint.h>
Chenbo Feng4c9e9ec2018-10-16 20:31:52 -070032#include "netdbpf/bpf_shared.h"
Chenbo Feng75b410b2018-10-10 15:01:19 -070033
Chenbo Feng75b410b2018-10-10 15:01:19 -070034struct uid_tag {
35 uint32_t uid;
36 uint32_t tag;
37};
38
39struct stats_key {
40 uint32_t uid;
41 uint32_t tag;
42 uint32_t counterSet;
43 uint32_t ifaceIndex;
44};
45
46struct stats_value {
47 uint64_t rxPackets;
48 uint64_t rxBytes;
49 uint64_t txPackets;
50 uint64_t txBytes;
51};
52
Chenbo Fengc1dd7642018-12-22 11:41:20 -080053struct IfaceValue {
54 char name[IFNAMSIZ];
55};
56
Chenbo Feng75b410b2018-10-10 15:01:19 -070057// This is defined for cgroup bpf filter only.
58#define BPF_PASS 1
59#define BPF_DROP 0
60
61// This is used for xt_bpf program only.
62#define BPF_NOMATCH 0
63#define BPF_MATCH 1
64
65#define BPF_EGRESS 0
66#define BPF_INGRESS 1
67
68#define IP_PROTO_OFF offsetof(struct iphdr, protocol)
69#define IPV6_PROTO_OFF offsetof(struct ipv6hdr, nexthdr)
70#define IPPROTO_IHL_OFF 0
71#define TCP_FLAG_OFF 13
72#define RST_OFFSET 2
73
Chenbo Fengc1dd7642018-12-22 11:41:20 -080074struct bpf_map_def SEC("maps") cookie_tag_map = {
75 .type = BPF_MAP_TYPE_HASH,
76 .key_size = sizeof(uint64_t),
77 .value_size = sizeof(struct uid_tag),
78 .max_entries = COOKIE_UID_MAP_SIZE,
79};
80
81struct bpf_map_def SEC("maps") uid_counterset_map = {
82 .type = BPF_MAP_TYPE_HASH,
83 .key_size = sizeof(uint32_t),
84 .value_size = sizeof(uint8_t),
85 .max_entries = UID_COUNTERSET_MAP_SIZE,
86};
87
88struct bpf_map_def SEC("maps") app_uid_stats_map = {
89 .type = BPF_MAP_TYPE_HASH,
90 .key_size = sizeof(uint32_t),
91 .value_size = sizeof(struct stats_value),
Chenbo Feng9cd8f142018-12-04 16:54:56 -080092 .max_entries = APP_STATS_MAP_SIZE,
Chenbo Fengc1dd7642018-12-22 11:41:20 -080093};
94
Chenbo Feng9cd8f142018-12-04 16:54:56 -080095struct bpf_map_def SEC("maps") stats_map_A = {
Chenbo Fengc1dd7642018-12-22 11:41:20 -080096 .type = BPF_MAP_TYPE_HASH,
97 .key_size = sizeof(struct stats_key),
98 .value_size = sizeof(struct stats_value),
Chenbo Feng9cd8f142018-12-04 16:54:56 -080099 .max_entries = STATS_MAP_SIZE,
Chenbo Fengc1dd7642018-12-22 11:41:20 -0800100};
101
Chenbo Feng9cd8f142018-12-04 16:54:56 -0800102struct bpf_map_def SEC("maps") stats_map_B = {
Chenbo Fengc1dd7642018-12-22 11:41:20 -0800103 .type = BPF_MAP_TYPE_HASH,
104 .key_size = sizeof(struct stats_key),
105 .value_size = sizeof(struct stats_value),
Chenbo Feng9cd8f142018-12-04 16:54:56 -0800106 .max_entries = STATS_MAP_SIZE,
Chenbo Fengc1dd7642018-12-22 11:41:20 -0800107};
108
109struct bpf_map_def SEC("maps") iface_stats_map = {
110 .type = BPF_MAP_TYPE_HASH,
111 .key_size = sizeof(uint32_t),
112 .value_size = sizeof(struct stats_value),
113 .max_entries = IFACE_STATS_MAP_SIZE,
114};
115
116struct bpf_map_def SEC("maps") configuration_map = {
117 .type = BPF_MAP_TYPE_HASH,
118 .key_size = sizeof(uint32_t),
119 .value_size = sizeof(uint8_t),
120 .max_entries = CONFIGURATION_MAP_SIZE,
121};
122
123struct bpf_map_def SEC("maps") uid_owner_map = {
124 .type = BPF_MAP_TYPE_HASH,
125 .key_size = sizeof(uint32_t),
126 .value_size = sizeof(uint8_t),
127 .max_entries = UID_OWNER_MAP_SIZE,
128};
129
130struct bpf_map_def SEC("maps") iface_index_name_map = {
131 .type = BPF_MAP_TYPE_HASH,
132 .key_size = sizeof(uint32_t),
133 .value_size = sizeof(struct IfaceValue),
134 .max_entries = IFACE_INDEX_NAME_MAP_SIZE,
135};
136
Chenbo Feng75b410b2018-10-10 15:01:19 -0700137static __always_inline int is_system_uid(uint32_t uid) {
138 return (uid <= MAX_SYSTEM_UID) && (uid >= MIN_SYSTEM_UID);
139}
140
Chenbo Fengc1dd7642018-12-22 11:41:20 -0800141static __always_inline inline void bpf_update_stats(struct __sk_buff* skb, struct bpf_map_def* map,
142 int direction, void* key) {
Chenbo Feng75b410b2018-10-10 15:01:19 -0700143 struct stats_value* value;
Joel Fernandes (Google)14ba8882019-02-12 12:49:03 -0500144 value = bpf_map_lookup_elem(map, key);
Chenbo Feng75b410b2018-10-10 15:01:19 -0700145 if (!value) {
146 struct stats_value newValue = {};
Joel Fernandes (Google)14ba8882019-02-12 12:49:03 -0500147 bpf_map_update_elem(map, key, &newValue, BPF_NOEXIST);
148 value = bpf_map_lookup_elem(map, key);
Chenbo Feng75b410b2018-10-10 15:01:19 -0700149 }
150 if (value) {
151 if (direction == BPF_EGRESS) {
152 __sync_fetch_and_add(&value->txPackets, 1);
153 __sync_fetch_and_add(&value->txBytes, skb->len);
154 } else if (direction == BPF_INGRESS) {
155 __sync_fetch_and_add(&value->rxPackets, 1);
156 __sync_fetch_and_add(&value->rxBytes, skb->len);
157 }
158 }
159}
160
161static inline bool skip_owner_match(struct __sk_buff* skb) {
162 int offset = -1;
163 int ret = 0;
164 if (skb->protocol == ETH_P_IP) {
165 offset = IP_PROTO_OFF;
166 uint8_t proto, ihl;
167 uint16_t flag;
168 ret = bpf_skb_load_bytes(skb, offset, &proto, 1);
169 if (!ret) {
170 if (proto == IPPROTO_ESP) {
171 return true;
172 } else if (proto == IPPROTO_TCP) {
173 ret = bpf_skb_load_bytes(skb, IPPROTO_IHL_OFF, &ihl, 1);
174 ihl = ihl & 0x0F;
175 ret = bpf_skb_load_bytes(skb, ihl * 4 + TCP_FLAG_OFF, &flag, 1);
176 if (ret == 0 && (flag >> RST_OFFSET & 1)) {
177 return true;
178 }
179 }
180 }
181 } else if (skb->protocol == ETH_P_IPV6) {
182 offset = IPV6_PROTO_OFF;
183 uint8_t proto;
184 ret = bpf_skb_load_bytes(skb, offset, &proto, 1);
185 if (!ret) {
186 if (proto == IPPROTO_ESP) {
187 return true;
188 } else if (proto == IPPROTO_TCP) {
189 uint16_t flag;
190 ret = bpf_skb_load_bytes(skb, sizeof(struct ipv6hdr) + TCP_FLAG_OFF, &flag, 1);
191 if (ret == 0 && (flag >> RST_OFFSET & 1)) {
192 return true;
193 }
194 }
195 }
196 }
197 return false;
198}
199
Chenbo Feng9cd8f142018-12-04 16:54:56 -0800200static __always_inline BpfConfig getConfig(uint32_t configKey) {
201 uint32_t mapSettingKey = configKey;
Joel Fernandes (Google)14ba8882019-02-12 12:49:03 -0500202 BpfConfig* config = bpf_map_lookup_elem(&configuration_map, &mapSettingKey);
Chenbo Feng75b410b2018-10-10 15:01:19 -0700203 if (!config) {
204 // Couldn't read configuration entry. Assume everything is disabled.
205 return DEFAULT_CONFIG;
206 }
207 return *config;
208}
209
210static inline int bpf_owner_match(struct __sk_buff* skb, uint32_t uid) {
211 if (skip_owner_match(skb)) return BPF_PASS;
212
213 if ((uid <= MAX_SYSTEM_UID) && (uid >= MIN_SYSTEM_UID)) return BPF_PASS;
214
Chenbo Feng9cd8f142018-12-04 16:54:56 -0800215 BpfConfig enabledRules = getConfig(UID_RULES_CONFIGURATION_KEY);
Chenbo Feng75b410b2018-10-10 15:01:19 -0700216 if (!enabledRules) {
217 return BPF_PASS;
218 }
219
Joel Fernandes (Google)14ba8882019-02-12 12:49:03 -0500220 uint8_t* uidEntry = bpf_map_lookup_elem(&uid_owner_map, &uid);
Chenbo Feng75b410b2018-10-10 15:01:19 -0700221 uint8_t uidRules = uidEntry ? *uidEntry : 0;
222 if ((enabledRules & DOZABLE_MATCH) && !(uidRules & DOZABLE_MATCH)) {
223 return BPF_DROP;
224 }
225 if ((enabledRules & STANDBY_MATCH) && (uidRules & STANDBY_MATCH)) {
226 return BPF_DROP;
227 }
228 if ((enabledRules & POWERSAVE_MATCH) && !(uidRules & POWERSAVE_MATCH)) {
229 return BPF_DROP;
230 }
231 return BPF_PASS;
232}
233
Chenbo Feng9cd8f142018-12-04 16:54:56 -0800234static __always_inline inline void update_stats_with_config(struct __sk_buff* skb, int direction,
235 void* key, uint8_t selectedMap) {
236 if (selectedMap == SELECT_MAP_A) {
237 bpf_update_stats(skb, &stats_map_A, direction, key);
238 } else if (selectedMap == SELECT_MAP_B) {
239 bpf_update_stats(skb, &stats_map_B, direction, key);
240 }
241}
242
Chenbo Feng75b410b2018-10-10 15:01:19 -0700243static __always_inline inline int bpf_traffic_account(struct __sk_buff* skb, int direction) {
Joel Fernandes (Google)14ba8882019-02-12 12:49:03 -0500244 uint32_t sock_uid = bpf_get_socket_uid(skb);
Chenbo Feng75b410b2018-10-10 15:01:19 -0700245 int match = bpf_owner_match(skb, sock_uid);
246 if ((direction == BPF_EGRESS) && (match == BPF_DROP)) {
247 // If an outbound packet is going to be dropped, we do not count that
248 // traffic.
249 return match;
250 }
251
Joel Fernandes (Google)14ba8882019-02-12 12:49:03 -0500252 uint64_t cookie = bpf_get_socket_cookie(skb);
253 struct uid_tag* utag = bpf_map_lookup_elem(&cookie_tag_map, &cookie);
Chenbo Feng75b410b2018-10-10 15:01:19 -0700254 uint32_t uid, tag;
255 if (utag) {
256 uid = utag->uid;
257 tag = utag->tag;
258 } else {
259 uid = sock_uid;
260 tag = 0;
261 }
262
263 struct stats_key key = {.uid = uid, .tag = tag, .counterSet = 0, .ifaceIndex = skb->ifindex};
264
Joel Fernandes (Google)14ba8882019-02-12 12:49:03 -0500265 uint8_t* counterSet = bpf_map_lookup_elem(&uid_counterset_map, &uid);
Chenbo Feng75b410b2018-10-10 15:01:19 -0700266 if (counterSet) key.counterSet = (uint32_t)*counterSet;
267
Chenbo Feng9cd8f142018-12-04 16:54:56 -0800268 uint32_t mapSettingKey = CURRENT_STATS_MAP_CONFIGURATION_KEY;
Joel Fernandes (Google)14ba8882019-02-12 12:49:03 -0500269 uint8_t* selectedMap = bpf_map_lookup_elem(&configuration_map, &mapSettingKey);
Chenbo Feng9cd8f142018-12-04 16:54:56 -0800270 if (!selectedMap) {
271 return match;
272 }
273
Chenbo Feng75b410b2018-10-10 15:01:19 -0700274 if (tag) {
Chenbo Feng9cd8f142018-12-04 16:54:56 -0800275 update_stats_with_config(skb, direction, &key, *selectedMap);
Chenbo Feng75b410b2018-10-10 15:01:19 -0700276 }
277
278 key.tag = 0;
Chenbo Feng9cd8f142018-12-04 16:54:56 -0800279 update_stats_with_config(skb, direction, &key, *selectedMap);
Chenbo Fengc1dd7642018-12-22 11:41:20 -0800280 bpf_update_stats(skb, &app_uid_stats_map, direction, &uid);
Chenbo Feng75b410b2018-10-10 15:01:19 -0700281 return match;
282}