• 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 #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