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 #include <arpa/inet.h>
18 #include <jni.h>
19 #include <linux/if_arp.h>
20 #include <linux/if_ether.h>
21 #include <linux/netlink.h>
22 #include <linux/pkt_cls.h>
23 #include <linux/pkt_sched.h>
24 #include <linux/rtnetlink.h>
25 #include <nativehelper/JNIHelp.h>
26 #include <net/if.h>
27 #include <stdio.h>
28 #include <sys/socket.h>
29
30 // TODO: use unique_fd.
31 #define BPF_FD_JUST_USE_INT
32 #include "BpfSyscallWrappers.h"
33 #include "bpf_tethering.h"
34 #include "nativehelper/scoped_utf_chars.h"
35
36 // The maximum length of TCA_BPF_NAME. Sync from net/sched/cls_bpf.c.
37 #define CLS_BPF_NAME_LEN 256
38
39 // Classifier name. See cls_bpf_ops in net/sched/cls_bpf.c.
40 #define CLS_BPF_KIND_NAME "bpf"
41
42 namespace android {
43 // Sync from system/netd/server/NetlinkCommands.h
44 const uint16_t NETLINK_REQUEST_FLAGS = NLM_F_REQUEST | NLM_F_ACK;
45 const sockaddr_nl KERNEL_NLADDR = {AF_NETLINK, 0, 0, 0};
46
47 // TODO: move to frameworks/libs/net/common/native for sharing with
48 // system/netd/server/OffloadUtils.{c, h}.
sendAndProcessNetlinkResponse(JNIEnv * env,const void * req,int len)49 static void sendAndProcessNetlinkResponse(JNIEnv* env, const void* req, int len) {
50 int fd = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE); // TODO: use unique_fd
51 if (fd == -1) {
52 jniThrowExceptionFmt(env, "java/io/IOException",
53 "socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE): %s",
54 strerror(errno));
55 return;
56 }
57
58 static constexpr int on = 1;
59 if (setsockopt(fd, SOL_NETLINK, NETLINK_CAP_ACK, &on, sizeof(on))) {
60 jniThrowExceptionFmt(env, "java/io/IOException",
61 "setsockopt(fd, SOL_NETLINK, NETLINK_CAP_ACK, %d)", on);
62 close(fd);
63 return;
64 }
65
66 // this is needed to get valid strace netlink parsing, it allocates the pid
67 if (bind(fd, (const struct sockaddr*)&KERNEL_NLADDR, sizeof(KERNEL_NLADDR))) {
68 jniThrowExceptionFmt(env, "java/io/IOException", "bind(fd, {AF_NETLINK, 0, 0}): %s",
69 strerror(errno));
70 close(fd);
71 return;
72 }
73
74 // we do not want to receive messages from anyone besides the kernel
75 if (connect(fd, (const struct sockaddr*)&KERNEL_NLADDR, sizeof(KERNEL_NLADDR))) {
76 jniThrowExceptionFmt(env, "java/io/IOException", "connect(fd, {AF_NETLINK, 0, 0}): %s",
77 strerror(errno));
78 close(fd);
79 return;
80 }
81
82 int rv = send(fd, req, len, 0);
83
84 if (rv == -1) {
85 jniThrowExceptionFmt(env, "java/io/IOException", "send(fd, req, len, 0): %s",
86 strerror(errno));
87 close(fd);
88 return;
89 }
90
91 if (rv != len) {
92 jniThrowExceptionFmt(env, "java/io/IOException", "send(fd, req, len, 0): %s",
93 strerror(EMSGSIZE));
94 close(fd);
95 return;
96 }
97
98 struct {
99 nlmsghdr h;
100 nlmsgerr e;
101 char buf[256];
102 } resp = {};
103
104 rv = recv(fd, &resp, sizeof(resp), MSG_TRUNC);
105
106 if (rv == -1) {
107 jniThrowExceptionFmt(env, "java/io/IOException", "recv() failed: %s", strerror(errno));
108 close(fd);
109 return;
110 }
111
112 if (rv < (int)NLMSG_SPACE(sizeof(struct nlmsgerr))) {
113 jniThrowExceptionFmt(env, "java/io/IOException", "recv() returned short packet: %d", rv);
114 close(fd);
115 return;
116 }
117
118 if (resp.h.nlmsg_len != (unsigned)rv) {
119 jniThrowExceptionFmt(env, "java/io/IOException",
120 "recv() returned invalid header length: %d != %d", resp.h.nlmsg_len,
121 rv);
122 close(fd);
123 return;
124 }
125
126 if (resp.h.nlmsg_type != NLMSG_ERROR) {
127 jniThrowExceptionFmt(env, "java/io/IOException",
128 "recv() did not return NLMSG_ERROR message: %d", resp.h.nlmsg_type);
129 close(fd);
130 return;
131 }
132
133 if (resp.e.error) { // returns 0 on success
134 jniThrowExceptionFmt(env, "java/io/IOException", "NLMSG_ERROR message return error: %s",
135 strerror(-resp.e.error));
136 }
137 close(fd);
138 return;
139 }
140
hardwareAddressType(const char * interface)141 static int hardwareAddressType(const char* interface) {
142 int fd = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);
143 if (fd < 0) return -errno;
144
145 struct ifreq ifr = {};
146 // We use strncpy() instead of strlcpy() since kernel has to be able
147 // to handle non-zero terminated junk passed in by userspace anyway,
148 // and this way too long interface names (more than IFNAMSIZ-1 = 15
149 // characters plus terminating NULL) will not get truncated to 15
150 // characters and zero-terminated and thus potentially erroneously
151 // match a truncated interface if one were to exist.
152 strncpy(ifr.ifr_name, interface, sizeof(ifr.ifr_name));
153
154 int rv;
155 if (ioctl(fd, SIOCGIFHWADDR, &ifr, sizeof(ifr))) {
156 rv = -errno;
157 } else {
158 rv = ifr.ifr_hwaddr.sa_family;
159 }
160
161 close(fd);
162 return rv;
163 }
164
com_android_networkstack_tethering_BpfUtils_isEthernet(JNIEnv * env,jobject clazz,jstring iface)165 static jboolean com_android_networkstack_tethering_BpfUtils_isEthernet(JNIEnv* env, jobject clazz,
166 jstring iface) {
167 ScopedUtfChars interface(env, iface);
168
169 int rv = hardwareAddressType(interface.c_str());
170 if (rv < 0) {
171 jniThrowExceptionFmt(env, "java/io/IOException",
172 "Get hardware address type of interface %s failed: %s",
173 interface.c_str(), strerror(-rv));
174 return false;
175 }
176
177 switch (rv) {
178 case ARPHRD_ETHER:
179 return true;
180 case ARPHRD_NONE:
181 case ARPHRD_RAWIP: // in Linux 4.14+ rmnet support was upstreamed and this is 519
182 case 530: // this is ARPHRD_RAWIP on some Android 4.9 kernels with rmnet
183 return false;
184 default:
185 jniThrowExceptionFmt(env, "java/io/IOException",
186 "Unknown hardware address type %d on interface %s", rv,
187 interface.c_str());
188 return false;
189 }
190 }
191
192 // tc filter add dev .. in/egress prio 1 protocol ipv6/ip bpf object-pinned /sys/fs/bpf/...
193 // direct-action
com_android_networkstack_tethering_BpfUtils_tcFilterAddDevBpf(JNIEnv * env,jobject clazz,jint ifIndex,jboolean ingress,jshort prio,jshort proto,jstring bpfProgPath)194 static void com_android_networkstack_tethering_BpfUtils_tcFilterAddDevBpf(
195 JNIEnv* env, jobject clazz, jint ifIndex, jboolean ingress, jshort prio, jshort proto,
196 jstring bpfProgPath) {
197 ScopedUtfChars pathname(env, bpfProgPath);
198
199 const int bpfFd = bpf::retrieveProgram(pathname.c_str());
200 if (bpfFd == -1) {
201 jniThrowExceptionFmt(env, "java/io/IOException", "retrieveProgram failed %s",
202 strerror(errno));
203 return;
204 }
205
206 struct {
207 nlmsghdr n;
208 tcmsg t;
209 struct {
210 nlattr attr;
211 // The maximum classifier name length is defined in
212 // tcf_proto_ops in include/net/sch_generic.h.
213 char str[NLMSG_ALIGN(sizeof(CLS_BPF_KIND_NAME))];
214 } kind;
215 struct {
216 nlattr attr;
217 struct {
218 nlattr attr;
219 __u32 u32;
220 } fd;
221 struct {
222 nlattr attr;
223 char str[NLMSG_ALIGN(CLS_BPF_NAME_LEN)];
224 } name;
225 struct {
226 nlattr attr;
227 __u32 u32;
228 } flags;
229 } options;
230 } req = {
231 .n =
232 {
233 .nlmsg_len = sizeof(req),
234 .nlmsg_type = RTM_NEWTFILTER,
235 .nlmsg_flags = NETLINK_REQUEST_FLAGS | NLM_F_EXCL | NLM_F_CREATE,
236 },
237 .t =
238 {
239 .tcm_family = AF_UNSPEC,
240 .tcm_ifindex = ifIndex,
241 .tcm_handle = TC_H_UNSPEC,
242 .tcm_parent = TC_H_MAKE(TC_H_CLSACT,
243 ingress ? TC_H_MIN_INGRESS : TC_H_MIN_EGRESS),
244 .tcm_info = static_cast<__u32>((static_cast<uint16_t>(prio) << 16) |
245 htons(static_cast<uint16_t>(proto))),
246 },
247 .kind =
248 {
249 .attr =
250 {
251 .nla_len = sizeof(req.kind),
252 .nla_type = TCA_KIND,
253 },
254 .str = CLS_BPF_KIND_NAME,
255 },
256 .options =
257 {
258 .attr =
259 {
260 .nla_len = sizeof(req.options),
261 .nla_type = NLA_F_NESTED | TCA_OPTIONS,
262 },
263 .fd =
264 {
265 .attr =
266 {
267 .nla_len = sizeof(req.options.fd),
268 .nla_type = TCA_BPF_FD,
269 },
270 .u32 = static_cast<__u32>(bpfFd),
271 },
272 .name =
273 {
274 .attr =
275 {
276 .nla_len = sizeof(req.options.name),
277 .nla_type = TCA_BPF_NAME,
278 },
279 // Visible via 'tc filter show', but
280 // is overwritten by strncpy below
281 .str = "placeholder",
282 },
283 .flags =
284 {
285 .attr =
286 {
287 .nla_len = sizeof(req.options.flags),
288 .nla_type = TCA_BPF_FLAGS,
289 },
290 .u32 = TCA_BPF_FLAG_ACT_DIRECT,
291 },
292 },
293 };
294
295 snprintf(req.options.name.str, sizeof(req.options.name.str), "%s:[*fsobj]",
296 basename(pathname.c_str()));
297
298 // The exception may be thrown from sendAndProcessNetlinkResponse. Close the file descriptor of
299 // BPF program before returning the function in any case.
300 sendAndProcessNetlinkResponse(env, &req, sizeof(req));
301 close(bpfFd);
302 }
303
304 // tc filter del dev .. in/egress prio .. protocol ..
com_android_networkstack_tethering_BpfUtils_tcFilterDelDev(JNIEnv * env,jobject clazz,jint ifIndex,jboolean ingress,jshort prio,jshort proto)305 static void com_android_networkstack_tethering_BpfUtils_tcFilterDelDev(JNIEnv* env, jobject clazz,
306 jint ifIndex,
307 jboolean ingress,
308 jshort prio, jshort proto) {
309 const struct {
310 nlmsghdr n;
311 tcmsg t;
312 } req = {
313 .n =
314 {
315 .nlmsg_len = sizeof(req),
316 .nlmsg_type = RTM_DELTFILTER,
317 .nlmsg_flags = NETLINK_REQUEST_FLAGS,
318 },
319 .t =
320 {
321 .tcm_family = AF_UNSPEC,
322 .tcm_ifindex = ifIndex,
323 .tcm_handle = TC_H_UNSPEC,
324 .tcm_parent = TC_H_MAKE(TC_H_CLSACT,
325 ingress ? TC_H_MIN_INGRESS : TC_H_MIN_EGRESS),
326 .tcm_info = static_cast<__u32>((static_cast<uint16_t>(prio) << 16) |
327 htons(static_cast<uint16_t>(proto))),
328 },
329 };
330
331 sendAndProcessNetlinkResponse(env, &req, sizeof(req));
332 }
333
334 /*
335 * JNI registration.
336 */
337 static const JNINativeMethod gMethods[] = {
338 /* name, signature, funcPtr */
339 {"isEthernet", "(Ljava/lang/String;)Z",
340 (void*)com_android_networkstack_tethering_BpfUtils_isEthernet},
341 {"tcFilterAddDevBpf", "(IZSSLjava/lang/String;)V",
342 (void*)com_android_networkstack_tethering_BpfUtils_tcFilterAddDevBpf},
343 {"tcFilterDelDev", "(IZSS)V",
344 (void*)com_android_networkstack_tethering_BpfUtils_tcFilterDelDev},
345 };
346
register_com_android_networkstack_tethering_BpfUtils(JNIEnv * env)347 int register_com_android_networkstack_tethering_BpfUtils(JNIEnv* env) {
348 return jniRegisterNativeMethods(env, "com/android/networkstack/tethering/BpfUtils", gMethods,
349 NELEM(gMethods));
350 }
351
352 }; // namespace android
353