1 /* 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 17 package android.net; 18 19 import static android.net.BpfNetMapsConstants.CONFIGURATION_MAP_PATH; 20 import static android.net.BpfNetMapsConstants.DATA_SAVER_ENABLED_MAP_PATH; 21 import static android.net.BpfNetMapsConstants.UID_OWNER_MAP_PATH; 22 23 import android.annotation.NonNull; 24 import android.annotation.RequiresApi; 25 import android.os.Build; 26 import android.os.ServiceSpecificException; 27 import android.system.ErrnoException; 28 29 import com.android.internal.annotations.VisibleForTesting; 30 import com.android.modules.utils.build.SdkLevel; 31 import com.android.net.module.util.BpfMap; 32 import com.android.net.module.util.IBpfMap; 33 import com.android.net.module.util.Struct.S32; 34 import com.android.net.module.util.Struct.U32; 35 import com.android.net.module.util.Struct.U8; 36 37 /** 38 * A helper class to *read* java BpfMaps for network stack. 39 * BpfMap operations that are not used from network stack should be in 40 * {@link com.android.server.BpfNetMaps} 41 * @hide 42 */ 43 // NetworkStack can not use this before U due to b/326143935 44 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 45 public class NetworkStackBpfNetMaps { 46 private static final String TAG = NetworkStackBpfNetMaps.class.getSimpleName(); 47 48 // Locally store the handle of bpf maps. The FileDescriptors are statically cached inside the 49 // BpfMap implementation. 50 51 // Bpf map to store various networking configurations, the format of the value is different 52 // for different keys. See BpfNetMapsConstants#*_CONFIGURATION_KEY for keys. 53 private final IBpfMap<S32, U32> mConfigurationMap; 54 // Bpf map to store per uid traffic control configurations. 55 // See {@link UidOwnerValue} for more detail. 56 private final IBpfMap<S32, UidOwnerValue> mUidOwnerMap; 57 private final IBpfMap<S32, U8> mDataSaverEnabledMap; 58 private final Dependencies mDeps; 59 60 private static class SingletonHolder { 61 static final NetworkStackBpfNetMaps sInstance = new NetworkStackBpfNetMaps(); 62 } 63 64 @NonNull getInstance()65 public static NetworkStackBpfNetMaps getInstance() { 66 return SingletonHolder.sInstance; 67 } 68 NetworkStackBpfNetMaps()69 private NetworkStackBpfNetMaps() { 70 this(new Dependencies()); 71 } 72 73 // While the production code uses the singleton to optimize for performance and deal with 74 // concurrent access, the test needs to use a non-static approach for dependency injection and 75 // mocking virtual bpf maps. 76 @VisibleForTesting NetworkStackBpfNetMaps(@onNull Dependencies deps)77 public NetworkStackBpfNetMaps(@NonNull Dependencies deps) { 78 if (!SdkLevel.isAtLeastT()) { 79 throw new UnsupportedOperationException( 80 NetworkStackBpfNetMaps.class.getSimpleName() 81 + " is not supported below Android T"); 82 } 83 mDeps = deps; 84 mConfigurationMap = mDeps.getConfigurationMap(); 85 mUidOwnerMap = mDeps.getUidOwnerMap(); 86 mDataSaverEnabledMap = mDeps.getDataSaverEnabledMap(); 87 } 88 89 /** 90 * Dependencies of BpfNetMapReader, for injection in tests. 91 */ 92 @VisibleForTesting 93 public static class Dependencies { 94 /** Get the configuration map. */ getConfigurationMap()95 public IBpfMap<S32, U32> getConfigurationMap() { 96 try { 97 return new BpfMap<>(CONFIGURATION_MAP_PATH, BpfMap.BPF_F_RDONLY, 98 S32.class, U32.class); 99 } catch (ErrnoException e) { 100 throw new IllegalStateException("Cannot open configuration map", e); 101 } 102 } 103 104 /** Get the uid owner map. */ getUidOwnerMap()105 public IBpfMap<S32, UidOwnerValue> getUidOwnerMap() { 106 try { 107 return new BpfMap<>(UID_OWNER_MAP_PATH, BpfMap.BPF_F_RDONLY, 108 S32.class, UidOwnerValue.class); 109 } catch (ErrnoException e) { 110 throw new IllegalStateException("Cannot open uid owner map", e); 111 } 112 } 113 114 /** Get the data saver enabled map. */ getDataSaverEnabledMap()115 public IBpfMap<S32, U8> getDataSaverEnabledMap() { 116 try { 117 return new BpfMap<>(DATA_SAVER_ENABLED_MAP_PATH, BpfMap.BPF_F_RDONLY, S32.class, 118 U8.class); 119 } catch (ErrnoException e) { 120 throw new IllegalStateException("Cannot open data saver enabled map", e); 121 } 122 } 123 } 124 125 /** 126 * Get the specified firewall chain's status. 127 * 128 * @param chain target chain 129 * @return {@code true} if chain is enabled, {@code false} if chain is not enabled. 130 * @throws UnsupportedOperationException if called on pre-T devices. 131 * @throws ServiceSpecificException in case of failure, with an error code indicating the 132 * cause of the failure. 133 */ isChainEnabled(final int chain)134 public boolean isChainEnabled(final int chain) { 135 return BpfNetMapsUtils.isChainEnabled(mConfigurationMap, chain); 136 } 137 138 /** 139 * Get firewall rule of specified firewall chain on specified uid. 140 * 141 * @param chain target chain 142 * @param uid target uid 143 * @return either {@link ConnectivityManager#FIREWALL_RULE_ALLOW} or 144 * {@link ConnectivityManager#FIREWALL_RULE_DENY}. 145 * @throws UnsupportedOperationException if called on pre-T devices. 146 * @throws ServiceSpecificException in case of failure, with an error code indicating the 147 * cause of the failure. 148 */ getUidRule(final int chain, final int uid)149 public int getUidRule(final int chain, final int uid) { 150 return BpfNetMapsUtils.getUidRule(mUidOwnerMap, chain, uid); 151 } 152 153 /** 154 * Return whether the network is blocked by firewall chains for the given uid. 155 * 156 * Note that {@link #getDataSaverEnabled()} has a latency before V. 157 * 158 * @param uid The target uid. 159 * @param isNetworkMetered Whether the target network is metered. 160 * 161 * @return True if the network is blocked. Otherwise, false. 162 * @throws ServiceSpecificException if the read fails. 163 * 164 * @hide 165 */ isUidNetworkingBlocked(final int uid, boolean isNetworkMetered)166 public boolean isUidNetworkingBlocked(final int uid, boolean isNetworkMetered) { 167 return BpfNetMapsUtils.isUidNetworkingBlocked(uid, isNetworkMetered, 168 mConfigurationMap, mUidOwnerMap, mDataSaverEnabledMap); 169 } 170 171 /** 172 * Get Data Saver enabled or disabled 173 * 174 * Note that before V, the data saver status in bpf is written by ConnectivityService 175 * when receiving {@link ConnectivityManager#ACTION_RESTRICT_BACKGROUND_CHANGED}. Thus, 176 * the status is not synchronized. 177 * On V+, the data saver status is set by platform code when enabling/disabling 178 * data saver, which is synchronized. 179 * 180 * @return whether Data Saver is enabled or disabled. 181 * @throws ServiceSpecificException in case of failure, with an error code indicating the 182 * cause of the failure. 183 */ getDataSaverEnabled()184 public boolean getDataSaverEnabled() { 185 return BpfNetMapsUtils.getDataSaverEnabled(mDataSaverEnabledMap); 186 } 187 } 188