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