blob: 49e874ad9e3a8814f0dc62ac0b91722eadb9f9a8 [file] [log] [blame]
Junyu Lai626045a2023-08-28 18:49:44 +08001/*
2 * Copyright (C) 2023 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
17package android.net;
18
19import static android.net.BpfNetMapsConstants.CONFIGURATION_MAP_PATH;
20import static android.net.BpfNetMapsConstants.UID_OWNER_MAP_PATH;
21import static android.net.BpfNetMapsConstants.UID_RULES_CONFIGURATION_KEY;
22import static android.net.BpfNetMapsUtils.getMatchByFirewallChain;
23import static android.net.BpfNetMapsUtils.isFirewallAllowList;
24import static android.net.BpfNetMapsUtils.throwIfPreT;
25import static android.net.ConnectivityManager.FIREWALL_RULE_ALLOW;
26import static android.net.ConnectivityManager.FIREWALL_RULE_DENY;
27
28import android.annotation.NonNull;
29import android.annotation.RequiresApi;
30import android.os.Build;
31import android.os.ServiceSpecificException;
32import android.system.ErrnoException;
33import android.system.Os;
34
35import com.android.internal.annotations.VisibleForTesting;
36import com.android.modules.utils.build.SdkLevel;
37import com.android.net.module.util.BpfMap;
38import com.android.net.module.util.IBpfMap;
39import com.android.net.module.util.Struct;
40import com.android.net.module.util.Struct.S32;
41import com.android.net.module.util.Struct.U32;
42
43/**
44 * A helper class to *read* java BpfMaps.
45 * @hide
46 */
47@RequiresApi(Build.VERSION_CODES.TIRAMISU) // BPF maps were only mainlined in T
48public class BpfNetMapsReader {
49 // Locally store the handle of bpf maps. The FileDescriptors are statically cached inside the
50 // BpfMap implementation.
51
52 // Bpf map to store various networking configurations, the format of the value is different
53 // for different keys. See BpfNetMapsConstants#*_CONFIGURATION_KEY for keys.
54 private final IBpfMap<S32, U32> mConfigurationMap;
55 // Bpf map to store per uid traffic control configurations.
56 // See {@link UidOwnerValue} for more detail.
57 private final IBpfMap<S32, UidOwnerValue> mUidOwnerMap;
58 private final Dependencies mDeps;
59
60 public BpfNetMapsReader() {
61 this(new Dependencies());
62 }
63
64 @VisibleForTesting
65 public BpfNetMapsReader(@NonNull Dependencies deps) {
66 if (!SdkLevel.isAtLeastT()) {
67 throw new UnsupportedOperationException(
68 BpfNetMapsReader.class.getSimpleName() + " is not supported below Android T");
69 }
70 mDeps = deps;
71 mConfigurationMap = mDeps.getConfigurationMap();
72 mUidOwnerMap = mDeps.getUidOwnerMap();
73 }
74
75 /**
76 * Dependencies of BpfNetMapReader, for injection in tests.
77 */
78 @VisibleForTesting
79 public static class Dependencies {
80 /** Get the configuration map. */
81 public IBpfMap<S32, U32> getConfigurationMap() {
82 try {
83 return new BpfMap<>(CONFIGURATION_MAP_PATH, BpfMap.BPF_F_RDONLY,
84 S32.class, U32.class);
85 } catch (ErrnoException e) {
86 throw new IllegalStateException("Cannot open configuration map", e);
87 }
88 }
89
90 /** Get the uid owner map. */
91 public IBpfMap<S32, UidOwnerValue> getUidOwnerMap() {
92 try {
93 return new BpfMap<>(UID_OWNER_MAP_PATH, BpfMap.BPF_F_RDONLY,
94 S32.class, UidOwnerValue.class);
95 } catch (ErrnoException e) {
96 throw new IllegalStateException("Cannot open uid owner map", e);
97 }
98 }
99 }
100
101 /**
102 * Get the specified firewall chain's status.
103 *
104 * @param chain target chain
105 * @return {@code true} if chain is enabled, {@code false} if chain is not enabled.
106 * @throws UnsupportedOperationException if called on pre-T devices.
107 * @throws ServiceSpecificException in case of failure, with an error code indicating the
108 * cause of the failure.
109 */
110 public boolean isChainEnabled(final int chain) {
111 return isChainEnabled(mConfigurationMap, chain);
112 }
113
114 /**
115 * Get firewall rule of specified firewall chain on specified uid.
116 *
117 * @param chain target chain
118 * @param uid target uid
119 * @return either {@link ConnectivityManager#FIREWALL_RULE_ALLOW} or
120 * {@link ConnectivityManager#FIREWALL_RULE_DENY}.
121 * @throws UnsupportedOperationException if called on pre-T devices.
122 * @throws ServiceSpecificException in case of failure, with an error code indicating the
123 * cause of the failure.
124 */
125 public int getUidRule(final int chain, final int uid) {
126 return getUidRule(mUidOwnerMap, chain, uid);
127 }
128
129 /**
130 * Get the specified firewall chain's status.
131 *
132 * @param configurationMap target configurationMap
133 * @param chain target chain
134 * @return {@code true} if chain is enabled, {@code false} if chain is not enabled.
135 * @throws UnsupportedOperationException if called on pre-T devices.
136 * @throws ServiceSpecificException in case of failure, with an error code indicating the
137 * cause of the failure.
138 */
139 public static boolean isChainEnabled(
140 final IBpfMap<Struct.S32, Struct.U32> configurationMap, final int chain) {
141 throwIfPreT("isChainEnabled is not available on pre-T devices");
142
143 final long match = getMatchByFirewallChain(chain);
144 try {
145 final Struct.U32 config = configurationMap.getValue(UID_RULES_CONFIGURATION_KEY);
146 return (config.val & match) != 0;
147 } catch (ErrnoException e) {
148 throw new ServiceSpecificException(e.errno,
149 "Unable to get firewall chain status: " + Os.strerror(e.errno));
150 }
151 }
152
153 /**
154 * Get firewall rule of specified firewall chain on specified uid.
155 *
156 * @param uidOwnerMap target uidOwnerMap.
157 * @param chain target chain.
158 * @param uid target uid.
159 * @return either FIREWALL_RULE_ALLOW or FIREWALL_RULE_DENY
160 * @throws UnsupportedOperationException if called on pre-T devices.
161 * @throws ServiceSpecificException in case of failure, with an error code indicating the
162 * cause of the failure.
163 */
164 public static int getUidRule(final IBpfMap<Struct.S32, UidOwnerValue> uidOwnerMap,
165 final int chain, final int uid) {
166 throwIfPreT("getUidRule is not available on pre-T devices");
167
168 final long match = getMatchByFirewallChain(chain);
169 final boolean isAllowList = isFirewallAllowList(chain);
170 try {
171 final UidOwnerValue uidMatch = uidOwnerMap.getValue(new Struct.S32(uid));
172 final boolean isMatchEnabled = uidMatch != null && (uidMatch.rule & match) != 0;
173 return isMatchEnabled == isAllowList ? FIREWALL_RULE_ALLOW : FIREWALL_RULE_DENY;
174 } catch (ErrnoException e) {
175 throw new ServiceSpecificException(e.errno,
176 "Unable to get uid rule status: " + Os.strerror(e.errno));
177 }
178 }
179}