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