• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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 com.android.server.connectivity;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.content.Context;
22 import android.net.connectivity.aidl.ConnectivityNative;
23 import android.os.Binder;
24 import android.os.Process;
25 import android.os.ServiceSpecificException;
26 import android.system.ErrnoException;
27 import android.util.Log;
28 
29 import com.android.internal.annotations.VisibleForTesting;
30 import com.android.net.module.util.BpfBitmap;
31 import com.android.net.module.util.CollectionUtils;
32 import com.android.net.module.util.PermissionUtils;
33 
34 import java.util.ArrayList;
35 
36 /**
37  * @hide
38  */
39 public class ConnectivityNativeService extends ConnectivityNative.Stub {
40     public static final String SERVICE_NAME = "connectivity_native";
41 
42     private static final String TAG = ConnectivityNativeService.class.getSimpleName();
43 
44     private static final String BLOCKED_PORTS_MAP_PATH =
45             "/sys/fs/bpf/net_shared/map_block_blocked_ports_map";
46 
47     private final Context mContext;
48 
49     // BPF map for port blocking. Exactly 65536 entries long, with one entry per port number
50     @Nullable
51     private final BpfBitmap mBpfBlockedPortsMap;
52 
53     /**
54      * Dependencies of ConnectivityNativeService, for injection in tests.
55      */
56     @VisibleForTesting
57     public static class Dependencies {
58         /** Get BPF maps. */
getBlockPortsMap()59         @Nullable public BpfBitmap getBlockPortsMap() {
60             try {
61                 return new BpfBitmap(BLOCKED_PORTS_MAP_PATH);
62             } catch (ErrnoException e) {
63                 throw new UnsupportedOperationException("Failed to create blocked ports map: "
64                         + e);
65             }
66         }
67     }
68 
enforceBlockPortPermission()69     private void enforceBlockPortPermission() {
70         final int uid = Binder.getCallingUid();
71         if (uid == Process.ROOT_UID || uid == Process.PHONE_UID) return;
72         PermissionUtils.enforceNetworkStackPermission(mContext);
73     }
74 
ensureValidPortNumber(int port)75     private void ensureValidPortNumber(int port) {
76         if (port < 0 || port > 65535) {
77             throw new IllegalArgumentException("Invalid port number " + port);
78         }
79     }
80 
ConnectivityNativeService(final Context context)81     public ConnectivityNativeService(final Context context) {
82         this(context, new Dependencies());
83     }
84 
85     @VisibleForTesting
ConnectivityNativeService(final Context context, @NonNull Dependencies deps)86     protected ConnectivityNativeService(final Context context, @NonNull Dependencies deps) {
87         mContext = context;
88         mBpfBlockedPortsMap = deps.getBlockPortsMap();
89     }
90 
91     @Override
blockPortForBind(int port)92     public void blockPortForBind(int port) {
93         enforceBlockPortPermission();
94         ensureValidPortNumber(port);
95         try {
96             mBpfBlockedPortsMap.set(port);
97         } catch (ErrnoException e) {
98             throw new ServiceSpecificException(e.errno, e.getMessage());
99         }
100     }
101 
102     @Override
unblockPortForBind(int port)103     public void unblockPortForBind(int port) {
104         enforceBlockPortPermission();
105         ensureValidPortNumber(port);
106         try {
107             mBpfBlockedPortsMap.unset(port);
108         } catch (ErrnoException e) {
109             throw new ServiceSpecificException(e.errno,
110                     "Could not unset bitmap value for (port: " + port + "): " + e);
111         }
112     }
113 
114     @Override
unblockAllPortsForBind()115     public void unblockAllPortsForBind() {
116         enforceBlockPortPermission();
117         try {
118             mBpfBlockedPortsMap.clear();
119         } catch (ErrnoException e) {
120             throw new ServiceSpecificException(e.errno, "Could not clear map: " + e);
121         }
122     }
123 
124     @Override
getPortsBlockedForBind()125     public int[] getPortsBlockedForBind() {
126         enforceBlockPortPermission();
127 
128         ArrayList<Integer> portMap = new ArrayList<Integer>();
129         for (int i = 0; i <= 65535; i++) {
130             try {
131                 if (mBpfBlockedPortsMap.get(i)) portMap.add(i);
132             } catch (ErrnoException e) {
133                 Log.e(TAG, "Failed to get index " + i, e);
134             }
135         }
136         return CollectionUtils.toIntArray(portMap);
137     }
138 
139     @Override
getInterfaceVersion()140     public int getInterfaceVersion() {
141         return this.VERSION;
142     }
143 
144     @Override
getInterfaceHash()145     public String getInterfaceHash() {
146         return this.HASH;
147     }
148 }
149