1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
|
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.connectivity;
import static com.android.net.module.util.BpfUtils.BPF_CGROUP_INET4_BIND;
import static com.android.net.module.util.BpfUtils.BPF_CGROUP_INET6_BIND;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.net.connectivity.aidl.ConnectivityNative;
import android.os.Binder;
import android.os.Process;
import android.os.ServiceSpecificException;
import android.system.ErrnoException;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.net.module.util.BpfBitmap;
import com.android.net.module.util.BpfUtils;
import com.android.net.module.util.CollectionUtils;
import com.android.net.module.util.PermissionUtils;
import java.io.IOException;
import java.util.ArrayList;
/**
* @hide
*/
public class ConnectivityNativeService extends ConnectivityNative.Stub {
public static final String SERVICE_NAME = "connectivity_native";
private static final String TAG = ConnectivityNativeService.class.getSimpleName();
private static final String CGROUP_PATH = "/sys/fs/cgroup";
private static final String V4_PROG_PATH =
"/sys/fs/bpf/net_shared/prog_block_bind4_block_port";
private static final String V6_PROG_PATH =
"/sys/fs/bpf/net_shared/prog_block_bind6_block_port";
private static final String BLOCKED_PORTS_MAP_PATH =
"/sys/fs/bpf/net_shared/map_block_blocked_ports_map";
private final Context mContext;
// BPF map for port blocking. Exactly 65536 entries long, with one entry per port number
@Nullable
private final BpfBitmap mBpfBlockedPortsMap;
/**
* Dependencies of ConnectivityNativeService, for injection in tests.
*/
@VisibleForTesting
public static class Dependencies {
/** Get BPF maps. */
@Nullable public BpfBitmap getBlockPortsMap() {
try {
return new BpfBitmap(BLOCKED_PORTS_MAP_PATH);
} catch (ErrnoException e) {
throw new UnsupportedOperationException("Failed to create blocked ports map: "
+ e);
}
}
}
private void enforceBlockPortPermission() {
final int uid = Binder.getCallingUid();
if (uid == Process.ROOT_UID || uid == Process.PHONE_UID) return;
PermissionUtils.enforceNetworkStackPermission(mContext);
}
private void ensureValidPortNumber(int port) {
if (port < 0 || port > 65535) {
throw new IllegalArgumentException("Invalid port number " + port);
}
}
public ConnectivityNativeService(final Context context) {
this(context, new Dependencies());
}
@VisibleForTesting
protected ConnectivityNativeService(final Context context, @NonNull Dependencies deps) {
mContext = context;
mBpfBlockedPortsMap = deps.getBlockPortsMap();
attachProgram();
}
@Override
public void blockPortForBind(int port) {
enforceBlockPortPermission();
ensureValidPortNumber(port);
try {
mBpfBlockedPortsMap.set(port);
} catch (ErrnoException e) {
throw new ServiceSpecificException(e.errno, e.getMessage());
}
}
@Override
public void unblockPortForBind(int port) {
enforceBlockPortPermission();
ensureValidPortNumber(port);
try {
mBpfBlockedPortsMap.unset(port);
} catch (ErrnoException e) {
throw new ServiceSpecificException(e.errno,
"Could not unset bitmap value for (port: " + port + "): " + e);
}
}
@Override
public void unblockAllPortsForBind() {
enforceBlockPortPermission();
try {
mBpfBlockedPortsMap.clear();
} catch (ErrnoException e) {
throw new ServiceSpecificException(e.errno, "Could not clear map: " + e);
}
}
@Override
public int[] getPortsBlockedForBind() {
enforceBlockPortPermission();
ArrayList<Integer> portMap = new ArrayList<Integer>();
for (int i = 0; i <= 65535; i++) {
try {
if (mBpfBlockedPortsMap.get(i)) portMap.add(i);
} catch (ErrnoException e) {
Log.e(TAG, "Failed to get index " + i, e);
}
}
return CollectionUtils.toIntArray(portMap);
}
@Override
public int getInterfaceVersion() {
return this.VERSION;
}
@Override
public String getInterfaceHash() {
return this.HASH;
}
/**
* Attach BPF program
*/
private void attachProgram() {
try {
BpfUtils.attachProgram(BPF_CGROUP_INET4_BIND, V4_PROG_PATH, CGROUP_PATH, 0);
} catch (IOException e) {
throw new UnsupportedOperationException("Unable to attach to BPF_CGROUP_INET4_BIND: "
+ e);
}
try {
BpfUtils.attachProgram(BPF_CGROUP_INET6_BIND, V6_PROG_PATH, CGROUP_PATH, 0);
} catch (IOException e) {
throw new UnsupportedOperationException("Unable to attach to BPF_CGROUP_INET6_BIND: "
+ e);
}
Log.d(TAG, "Attached BPF_CGROUP_INET4_BIND and BPF_CGROUP_INET6_BIND programs");
}
}
|