blob: 4b8ea8aff1d3ad1b56c29a202e04c2c66059482f [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
22#include <linux/bpf.h>
23#include <linux/if.h>
24#include <linux/if_ether.h>
25#include <linux/in.h>
26#include <linux/in6.h>
27#include <linux/ip.h>
28#include <linux/ipv6.h>
29#include <stdbool.h>
30#include <stdint.h>
Chenbo Feng4c9e9ec2018-10-16 20:31:52 -070031#include "netdbpf/bpf_shared.h"
Chenbo Feng75b410b2018-10-10 15:01:19 -070032
Chenbo Fengc1dd7642018-12-22 11:41:20 -080033#define SEC(NAME) __attribute__((section(NAME), used))
Chenbo Feng75b410b2018-10-10 15:01:19 -070034
35struct uid_tag {
36 uint32_t uid;
37 uint32_t tag;
38};
39
40struct stats_key {
41 uint32_t uid;
42 uint32_t tag;
43 uint32_t counterSet;
44 uint32_t ifaceIndex;
45};
46
47struct stats_value {
48 uint64_t rxPackets;
49 uint64_t rxBytes;
50 uint64_t txPackets;
51 uint64_t txBytes;
52};
53
Chenbo Fengc1dd7642018-12-22 11:41:20 -080054struct IfaceValue {
55 char name[IFNAMSIZ];
56};
57
Chenbo Feng75b410b2018-10-10 15:01:19 -070058/* helper functions called from eBPF programs written in C */
Chenbo Fengc1dd7642018-12-22 11:41:20 -080059static void* (*find_map_entry)(void* map, void* key) = (void*)BPF_FUNC_map_lookup_elem;
60static int (*write_to_map_entry)(void* map, void* key, void* value,
Chenbo Feng75b410b2018-10-10 15:01:19 -070061 uint64_t flags) = (void*)BPF_FUNC_map_update_elem;
Chenbo Fengc1dd7642018-12-22 11:41:20 -080062static int (*delete_map_entry)(void* map, void* key) = (void*)BPF_FUNC_map_delete_elem;
Chenbo Feng75b410b2018-10-10 15:01:19 -070063static uint64_t (*get_socket_cookie)(struct __sk_buff* skb) = (void*)BPF_FUNC_get_socket_cookie;
64static uint32_t (*get_socket_uid)(struct __sk_buff* skb) = (void*)BPF_FUNC_get_socket_uid;
65static int (*bpf_skb_load_bytes)(struct __sk_buff* skb, int off, void* to,
66 int len) = (void*)BPF_FUNC_skb_load_bytes;
Chenbo Fengef6d45e2019-01-15 14:49:17 -080067static uint64_t (*bpf_get_current_uid_gid)(void) = (void*)BPF_FUNC_get_current_uid_gid;
Chenbo Feng75b410b2018-10-10 15:01:19 -070068
69// This is defined for cgroup bpf filter only.
70#define BPF_PASS 1
71#define BPF_DROP 0
72
73// This is used for xt_bpf program only.
74#define BPF_NOMATCH 0
75#define BPF_MATCH 1
76
77#define BPF_EGRESS 0
78#define BPF_INGRESS 1
79
80#define IP_PROTO_OFF offsetof(struct iphdr, protocol)
81#define IPV6_PROTO_OFF offsetof(struct ipv6hdr, nexthdr)
82#define IPPROTO_IHL_OFF 0
83#define TCP_FLAG_OFF 13
84#define RST_OFFSET 2
85
Chenbo Fengc1dd7642018-12-22 11:41:20 -080086/* loader usage */
87struct bpf_map_def {
88 unsigned int type;
89 unsigned int key_size;
90 unsigned int value_size;
91 unsigned int max_entries;
92 unsigned int map_flags;
93 unsigned int pad[2];
94};
95
96struct bpf_map_def SEC("maps") cookie_tag_map = {
97 .type = BPF_MAP_TYPE_HASH,
98 .key_size = sizeof(uint64_t),
99 .value_size = sizeof(struct uid_tag),
100 .max_entries = COOKIE_UID_MAP_SIZE,
101};
102
103struct bpf_map_def SEC("maps") uid_counterset_map = {
104 .type = BPF_MAP_TYPE_HASH,
105 .key_size = sizeof(uint32_t),
106 .value_size = sizeof(uint8_t),
107 .max_entries = UID_COUNTERSET_MAP_SIZE,
108};
109
110struct bpf_map_def SEC("maps") app_uid_stats_map = {
111 .type = BPF_MAP_TYPE_HASH,
112 .key_size = sizeof(uint32_t),
113 .value_size = sizeof(struct stats_value),
Chenbo Feng9cd8f142018-12-04 16:54:56 -0800114 .max_entries = APP_STATS_MAP_SIZE,
Chenbo Fengc1dd7642018-12-22 11:41:20 -0800115};
116
Chenbo Feng9cd8f142018-12-04 16:54:56 -0800117struct bpf_map_def SEC("maps") stats_map_A = {
Chenbo Fengc1dd7642018-12-22 11:41:20 -0800118 .type = BPF_MAP_TYPE_HASH,
119 .key_size = sizeof(struct stats_key),
120 .value_size = sizeof(struct stats_value),
Chenbo Feng9cd8f142018-12-04 16:54:56 -0800121 .max_entries = STATS_MAP_SIZE,
Chenbo Fengc1dd7642018-12-22 11:41:20 -0800122};
123
Chenbo Feng9cd8f142018-12-04 16:54:56 -0800124struct bpf_map_def SEC("maps") stats_map_B = {
Chenbo Fengc1dd7642018-12-22 11:41:20 -0800125 .type = BPF_MAP_TYPE_HASH,
126 .key_size = sizeof(struct stats_key),
127 .value_size = sizeof(struct stats_value),
Chenbo Feng9cd8f142018-12-04 16:54:56 -0800128 .max_entries = STATS_MAP_SIZE,
Chenbo Fengc1dd7642018-12-22 11:41:20 -0800129};
130
131struct bpf_map_def SEC("maps") iface_stats_map = {
132 .type = BPF_MAP_TYPE_HASH,
133 .key_size = sizeof(uint32_t),
134 .value_size = sizeof(struct stats_value),
135 .max_entries = IFACE_STATS_MAP_SIZE,
136};
137
138struct bpf_map_def SEC("maps") configuration_map = {
139 .type = BPF_MAP_TYPE_HASH,
140 .key_size = sizeof(uint32_t),
141 .value_size = sizeof(uint8_t),
142 .max_entries = CONFIGURATION_MAP_SIZE,
143};
144
145struct bpf_map_def SEC("maps") uid_owner_map = {
146 .type = BPF_MAP_TYPE_HASH,
147 .key_size = sizeof(uint32_t),
148 .value_size = sizeof(uint8_t),
149 .max_entries = UID_OWNER_MAP_SIZE,
150};
151
152struct bpf_map_def SEC("maps") iface_index_name_map = {
153 .type = BPF_MAP_TYPE_HASH,
154 .key_size = sizeof(uint32_t),
155 .value_size = sizeof(struct IfaceValue),
156 .max_entries = IFACE_INDEX_NAME_MAP_SIZE,
157};
158
Chenbo Feng75b410b2018-10-10 15:01:19 -0700159static __always_inline int is_system_uid(uint32_t uid) {
160 return (uid <= MAX_SYSTEM_UID) && (uid >= MIN_SYSTEM_UID);
161}
162
Chenbo Fengc1dd7642018-12-22 11:41:20 -0800163static __always_inline inline void bpf_update_stats(struct __sk_buff* skb, struct bpf_map_def* map,
164 int direction, void* key) {
Chenbo Feng75b410b2018-10-10 15:01:19 -0700165 struct stats_value* value;
166 value = find_map_entry(map, key);
167 if (!value) {
168 struct stats_value newValue = {};
169 write_to_map_entry(map, key, &newValue, BPF_NOEXIST);
170 value = find_map_entry(map, key);
171 }
172 if (value) {
173 if (direction == BPF_EGRESS) {
174 __sync_fetch_and_add(&value->txPackets, 1);
175 __sync_fetch_and_add(&value->txBytes, skb->len);
176 } else if (direction == BPF_INGRESS) {
177 __sync_fetch_and_add(&value->rxPackets, 1);
178 __sync_fetch_and_add(&value->rxBytes, skb->len);
179 }
180 }
181}
182
183static inline bool skip_owner_match(struct __sk_buff* skb) {
184 int offset = -1;
185 int ret = 0;
186 if (skb->protocol == ETH_P_IP) {
187 offset = IP_PROTO_OFF;
188 uint8_t proto, ihl;
189 uint16_t flag;
190 ret = bpf_skb_load_bytes(skb, offset, &proto, 1);
191 if (!ret) {
192 if (proto == IPPROTO_ESP) {
193 return true;
194 } else if (proto == IPPROTO_TCP) {
195 ret = bpf_skb_load_bytes(skb, IPPROTO_IHL_OFF, &ihl, 1);
196 ihl = ihl & 0x0F;
197 ret = bpf_skb_load_bytes(skb, ihl * 4 + TCP_FLAG_OFF, &flag, 1);
198 if (ret == 0 && (flag >> RST_OFFSET & 1)) {
199 return true;
200 }
201 }
202 }
203 } else if (skb->protocol == ETH_P_IPV6) {
204 offset = IPV6_PROTO_OFF;
205 uint8_t proto;
206 ret = bpf_skb_load_bytes(skb, offset, &proto, 1);
207 if (!ret) {
208 if (proto == IPPROTO_ESP) {
209 return true;
210 } else if (proto == IPPROTO_TCP) {
211 uint16_t flag;
212 ret = bpf_skb_load_bytes(skb, sizeof(struct ipv6hdr) + TCP_FLAG_OFF, &flag, 1);
213 if (ret == 0 && (flag >> RST_OFFSET & 1)) {
214 return true;
215 }
216 }
217 }
218 }
219 return false;
220}
221
Chenbo Feng9cd8f142018-12-04 16:54:56 -0800222static __always_inline BpfConfig getConfig(uint32_t configKey) {
223 uint32_t mapSettingKey = configKey;
Chenbo Fengc1dd7642018-12-22 11:41:20 -0800224 BpfConfig* config = find_map_entry(&configuration_map, &mapSettingKey);
Chenbo Feng75b410b2018-10-10 15:01:19 -0700225 if (!config) {
226 // Couldn't read configuration entry. Assume everything is disabled.
227 return DEFAULT_CONFIG;
228 }
229 return *config;
230}
231
232static inline int bpf_owner_match(struct __sk_buff* skb, uint32_t uid) {
233 if (skip_owner_match(skb)) return BPF_PASS;
234
235 if ((uid <= MAX_SYSTEM_UID) && (uid >= MIN_SYSTEM_UID)) return BPF_PASS;
236
Chenbo Feng9cd8f142018-12-04 16:54:56 -0800237 BpfConfig enabledRules = getConfig(UID_RULES_CONFIGURATION_KEY);
Chenbo Feng75b410b2018-10-10 15:01:19 -0700238 if (!enabledRules) {
239 return BPF_PASS;
240 }
241
Chenbo Fengc1dd7642018-12-22 11:41:20 -0800242 uint8_t* uidEntry = find_map_entry(&uid_owner_map, &uid);
Chenbo Feng75b410b2018-10-10 15:01:19 -0700243 uint8_t uidRules = uidEntry ? *uidEntry : 0;
244 if ((enabledRules & DOZABLE_MATCH) && !(uidRules & DOZABLE_MATCH)) {
245 return BPF_DROP;
246 }
247 if ((enabledRules & STANDBY_MATCH) && (uidRules & STANDBY_MATCH)) {
248 return BPF_DROP;
249 }
250 if ((enabledRules & POWERSAVE_MATCH) && !(uidRules & POWERSAVE_MATCH)) {
251 return BPF_DROP;
252 }
253 return BPF_PASS;
254}
255
Chenbo Feng9cd8f142018-12-04 16:54:56 -0800256static __always_inline inline void update_stats_with_config(struct __sk_buff* skb, int direction,
257 void* key, uint8_t selectedMap) {
258 if (selectedMap == SELECT_MAP_A) {
259 bpf_update_stats(skb, &stats_map_A, direction, key);
260 } else if (selectedMap == SELECT_MAP_B) {
261 bpf_update_stats(skb, &stats_map_B, direction, key);
262 }
263}
264
Chenbo Feng75b410b2018-10-10 15:01:19 -0700265static __always_inline inline int bpf_traffic_account(struct __sk_buff* skb, int direction) {
266 uint32_t sock_uid = get_socket_uid(skb);
267 int match = bpf_owner_match(skb, sock_uid);
268 if ((direction == BPF_EGRESS) && (match == BPF_DROP)) {
269 // If an outbound packet is going to be dropped, we do not count that
270 // traffic.
271 return match;
272 }
273
274 uint64_t cookie = get_socket_cookie(skb);
Chenbo Fengc1dd7642018-12-22 11:41:20 -0800275 struct uid_tag* utag = find_map_entry(&cookie_tag_map, &cookie);
Chenbo Feng75b410b2018-10-10 15:01:19 -0700276 uint32_t uid, tag;
277 if (utag) {
278 uid = utag->uid;
279 tag = utag->tag;
280 } else {
281 uid = sock_uid;
282 tag = 0;
283 }
284
285 struct stats_key key = {.uid = uid, .tag = tag, .counterSet = 0, .ifaceIndex = skb->ifindex};
286
Chenbo Fengc1dd7642018-12-22 11:41:20 -0800287 uint8_t* counterSet = find_map_entry(&uid_counterset_map, &uid);
Chenbo Feng75b410b2018-10-10 15:01:19 -0700288 if (counterSet) key.counterSet = (uint32_t)*counterSet;
289
Chenbo Feng9cd8f142018-12-04 16:54:56 -0800290 uint32_t mapSettingKey = CURRENT_STATS_MAP_CONFIGURATION_KEY;
291 uint8_t* selectedMap = find_map_entry(&configuration_map, &mapSettingKey);
292 if (!selectedMap) {
293 return match;
294 }
295
Chenbo Feng75b410b2018-10-10 15:01:19 -0700296 if (tag) {
Chenbo Feng9cd8f142018-12-04 16:54:56 -0800297 update_stats_with_config(skb, direction, &key, *selectedMap);
Chenbo Feng75b410b2018-10-10 15:01:19 -0700298 }
299
300 key.tag = 0;
Chenbo Feng9cd8f142018-12-04 16:54:56 -0800301 update_stats_with_config(skb, direction, &key, *selectedMap);
Chenbo Fengc1dd7642018-12-22 11:41:20 -0800302 bpf_update_stats(skb, &app_uid_stats_map, direction, &uid);
Chenbo Feng75b410b2018-10-10 15:01:19 -0700303 return match;
304}