blob: 83ad5b12d40e4c7ce69f6b3c940e3de7b1c2b78b [file] [log] [blame]
Chenbo Feng36575d02018-02-05 15:19:15 -08001/*
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#include <linux/bpf.h>
Chenbo Feng89c12f12018-03-21 10:29:18 -070018#include <linux/if_ether.h>
19#include <linux/in.h>
20#include <linux/in6.h>
21#include <linux/ip.h>
22#include <linux/ipv6.h>
Chenbo Feng36575d02018-02-05 15:19:15 -080023#include <stdint.h>
Chenbo Feng89c12f12018-03-21 10:29:18 -070024#include "bpf/bpf_shared.h"
Chenbo Feng36575d02018-02-05 15:19:15 -080025
26#define ELF_SEC(NAME) __attribute__((section(NAME), used))
27
28struct uid_tag {
29 uint32_t uid;
30 uint32_t tag;
31};
32
33struct stats_key {
34 uint32_t uid;
35 uint32_t tag;
36 uint32_t counterSet;
37 uint32_t ifaceIndex;
38};
39
40struct stats_value {
41 uint64_t rxPackets;
42 uint64_t rxBytes;
43 uint64_t txPackets;
44 uint64_t txBytes;
45};
46
47/* helper functions called from eBPF programs written in C */
48static void* (*find_map_entry)(uint64_t map, void* key) = (void*)BPF_FUNC_map_lookup_elem;
49static int (*write_to_map_entry)(uint64_t map, void* key, void* value,
50 uint64_t flags) = (void*)BPF_FUNC_map_update_elem;
51static int (*delete_map_entry)(uint64_t map, void* key) = (void*)BPF_FUNC_map_delete_elem;
52static uint64_t (*get_socket_cookie)(struct __sk_buff* skb) = (void*)BPF_FUNC_get_socket_cookie;
53static uint32_t (*get_socket_uid)(struct __sk_buff* skb) = (void*)BPF_FUNC_get_socket_uid;
54static int (*bpf_skb_load_bytes)(struct __sk_buff* skb, int off, void* to,
55 int len) = (void*)BPF_FUNC_skb_load_bytes;
Chenbo Feng36575d02018-02-05 15:19:15 -080056#define BPF_PASS 1
57#define BPF_DROP 0
Chenbo Feng5ed17992018-03-13 21:30:49 -070058#define BPF_EGRESS 0
59#define BPF_INGRESS 1
60
Chenbo Feng89c12f12018-03-21 10:29:18 -070061#define IP_PROTO_OFF offsetof(struct iphdr, protocol)
62#define IPV6_PROTO_OFF offsetof(struct ipv6hdr, nexthdr)
63#define IPPROTO_IHL_OFF 0
64#define TCP_FLAG_OFF 13
65#define RST_OFFSET 2
66
Chenbo Feng5ed17992018-03-13 21:30:49 -070067static __always_inline int xt_bpf_count(struct __sk_buff* skb, int type) {
68 uint32_t key = skb->ifindex;
69 struct stats_value* value;
70
71 value = find_map_entry(IFACE_STATS_MAP, &key);
72 if (!value) {
73 struct stats_value newValue = {};
74 write_to_map_entry(IFACE_STATS_MAP, &key, &newValue, BPF_NOEXIST);
75 value = find_map_entry(IFACE_STATS_MAP, &key);
76 }
77 if (value) {
78 if (type == BPF_EGRESS) {
79 __sync_fetch_and_add(&value->txPackets, 1);
80 __sync_fetch_and_add(&value->txBytes, skb->len);
81 } else if (type == BPF_INGRESS) {
82 __sync_fetch_and_add(&value->rxPackets, 1);
83 __sync_fetch_and_add(&value->rxBytes, skb->len);
84 }
85 }
86 return BPF_PASS;
87}
Chenbo Feng2cc3d6a2018-03-06 02:13:51 -080088
89static __always_inline inline void bpf_update_stats(struct __sk_buff* skb, uint64_t map,
90 int direction, struct stats_key key) {
91 struct stats_value* value;
92 value = find_map_entry(map, &key);
93 if (!value) {
94 struct stats_value newValue = {};
95 write_to_map_entry(map, &key, &newValue, BPF_NOEXIST);
96 value = find_map_entry(map, &key);
97 }
98 if (value) {
99 if (direction == BPF_INGRESS) {
100 __sync_fetch_and_add(&value->rxPackets, 1);
101 __sync_fetch_and_add(&value->rxBytes, skb->len);
102 } else {
103 __sync_fetch_and_add(&value->txPackets, 1);
104 __sync_fetch_and_add(&value->txBytes, skb->len);
105 }
106 }
107}
108
Chenbo Feng89c12f12018-03-21 10:29:18 -0700109static inline int bpf_owner_match(struct __sk_buff* skb, uint32_t uid) {
110 int offset = -1;
111 int ret = 0;
112 if (skb->protocol == ETH_P_IP) {
113 offset = IP_PROTO_OFF;
114 uint8_t proto, ihl;
115 uint16_t flag;
116 ret = bpf_skb_load_bytes(skb, offset, &proto, 1);
117 if (!ret) {
118 if (proto == IPPROTO_ESP) {
119 return 1;
120 } else if (proto == IPPROTO_TCP) {
121 ret = bpf_skb_load_bytes(skb, IPPROTO_IHL_OFF, &ihl, 1);
122 ihl = ihl & 0x0F;
123 ret = bpf_skb_load_bytes(skb, ihl * 4 + TCP_FLAG_OFF, &flag, 1);
124 if (ret == 0 && (flag >> RST_OFFSET & 1)) {
125 return BPF_PASS;
126 }
127 }
128 }
129 } else if (skb->protocol == ETH_P_IPV6) {
130 offset = IPV6_PROTO_OFF;
131 uint8_t proto;
132 ret = bpf_skb_load_bytes(skb, offset, &proto, 1);
133 if (!ret) {
134 if (proto == IPPROTO_ESP) {
135 return BPF_PASS;
136 } else if (proto == IPPROTO_TCP) {
137 uint16_t flag;
138 ret = bpf_skb_load_bytes(skb, sizeof(struct ipv6hdr) + TCP_FLAG_OFF, &flag, 1);
139 if (ret == 0 && (flag >> RST_OFFSET & 1)) {
140 return BPF_PASS;
141 }
142 }
143 }
144 }
145
146 if ((uid <= MAX_SYSTEM_UID) && (uid >= MIN_SYSTEM_UID)) return BPF_PASS;
147
148 // In each of these maps, the entry with key UID_MAP_ENABLED tells us whether that
149 // map is enabled or not.
150 // TODO: replace this with a map of size one that contains a config structure defined in
151 // bpf_shared.h that can be written by userspace and read here.
152 uint32_t mapSettingKey = UID_MAP_ENABLED;
153 uint8_t* ownerMatch;
154 uint8_t* mapEnabled = find_map_entry(DOZABLE_UID_MAP, &mapSettingKey);
155 if (mapEnabled && *mapEnabled) {
156 ownerMatch = find_map_entry(DOZABLE_UID_MAP, &uid);
157 if (ownerMatch) return *ownerMatch;
158 return BPF_DROP;
159 }
160 mapEnabled = find_map_entry(STANDBY_UID_MAP, &mapSettingKey);
161 if (mapEnabled && *mapEnabled) {
162 ownerMatch = find_map_entry(STANDBY_UID_MAP, &uid);
163 if (ownerMatch) return *ownerMatch;
164 }
165 mapEnabled = find_map_entry(POWERSAVE_UID_MAP, &mapSettingKey);
166 if (mapEnabled && *mapEnabled) {
167 ownerMatch = find_map_entry(POWERSAVE_UID_MAP, &uid);
168 if (ownerMatch) return *ownerMatch;
169 return BPF_DROP;
170 }
171 return BPF_PASS;
172}
173
Chenbo Feng2cc3d6a2018-03-06 02:13:51 -0800174static __always_inline inline int bpf_traffic_account(struct __sk_buff* skb, int direction) {
175 uint64_t cookie = get_socket_cookie(skb);
176 struct uid_tag* utag = find_map_entry(COOKIE_TAG_MAP, &cookie);
177 uint32_t uid, tag;
178 if (utag) {
179 uid = utag->uid;
180 tag = utag->tag;
181 } else {
182 uid = get_socket_uid(skb);
183 tag = 0;
184 }
185
186 struct stats_key key = {.uid = uid, .tag = tag, .counterSet = 0, .ifaceIndex = skb->ifindex};
187
188 uint32_t* counterSet;
189 counterSet = find_map_entry(UID_COUNTERSET_MAP, &uid);
190 if (counterSet) key.counterSet = *counterSet;
191
192 int ret;
193 if (tag) {
194 bpf_update_stats(skb, TAG_STATS_MAP, direction, key);
195 }
196
197 key.tag = 0;
198 bpf_update_stats(skb, UID_STATS_MAP, direction, key);
Chenbo Feng89c12f12018-03-21 10:29:18 -0700199 return bpf_owner_match(skb, uid);
Chenbo Feng2cc3d6a2018-03-06 02:13:51 -0800200}