diff --git a/Tethering/Android.bp b/Tethering/Android.bp index 285f4ab6cd..4efe93439b 100644 --- a/Tethering/Android.bp +++ b/Tethering/Android.bp @@ -28,6 +28,7 @@ java_defaults { "netd_aidl_interface-unstable-java", "netlink-client", "networkstack-aidl-interfaces-unstable-java", + "android.hardware.tetheroffload.config-V1.0-java", "android.hardware.tetheroffload.control-V1.0-java", "net-utils-framework-common", ], @@ -48,25 +49,26 @@ android_library { // Due to b/143733063, APK can't access a jni lib that is in APEX (but not in the APK). cc_library { name: "libtetherutilsjni", + sdk_version: "current", srcs: [ "jni/android_net_util_TetheringUtils.cpp", ], shared_libs: [ - "libcgrouprc", - "libnativehelper_compat_libc++", - "libvndksupport", - ], - static_libs: [ - "android.hardware.tetheroffload.config@1.0", "liblog", - "libbase", - "libcutils", - "libhidlbase", - "libjsoncpp", - "libprocessgroup", - "libutils", + "libnativehelper_compat_libc++", ], + // We cannot use plain "libc++" here to link libc++ dynamically because it results in: + // java.lang.UnsatisfiedLinkError: dlopen failed: library "libc++_shared.so" not found + // even if "libc++" is added into jni_libs below. Adding "libc++_shared" into jni_libs doesn't + // build because soong complains of: + // module Tethering missing dependencies: libc++_shared + // + // So, link libc++ statically. This means that we also need to ensure that all the C++ libraries + // we depend on do not dynamically link libc++. This is currently the case, because liblog is + // C-only and libnativehelper_compat_libc also uses stl: "c++_static". + stl: "c++_static", + cflags: [ "-Wall", "-Werror", @@ -85,9 +87,8 @@ java_defaults { // Build system doesn't track transitive dependeicies for jni_libs, list all the dependencies // explicitly. jni_libs: [ - "libcgrouprc", + "liblog", "libnativehelper_compat_libc++", - "libvndksupport", "libtetherutilsjni", ], resource_dirs: [ diff --git a/Tethering/jni/android_net_util_TetheringUtils.cpp b/Tethering/jni/android_net_util_TetheringUtils.cpp index 1cf8f98843..5493440644 100644 --- a/Tethering/jni/android_net_util_TetheringUtils.cpp +++ b/Tethering/jni/android_net_util_TetheringUtils.cpp @@ -16,123 +16,18 @@ #include #include -#include #include #include #include -#include -#include #include #include #include -#include -#include #define LOG_TAG "TetheringUtils" -#include +#include namespace android { -using hardware::hidl_handle; -using hardware::hidl_string; -using hardware::tetheroffload::config::V1_0::IOffloadConfig; - -namespace { - -inline const sockaddr * asSockaddr(const sockaddr_nl *nladdr) { - return reinterpret_cast(nladdr); -} - -int conntrackSocket(unsigned groups) { - base::unique_fd s(socket(AF_NETLINK, SOCK_DGRAM, NETLINK_NETFILTER)); - if (s.get() < 0) return -errno; - - const struct sockaddr_nl bind_addr = { - .nl_family = AF_NETLINK, - .nl_pad = 0, - .nl_pid = 0, - .nl_groups = groups, - }; - if (bind(s.get(), asSockaddr(&bind_addr), sizeof(bind_addr)) != 0) { - return -errno; - } - - const struct sockaddr_nl kernel_addr = { - .nl_family = AF_NETLINK, - .nl_pad = 0, - .nl_pid = 0, - .nl_groups = groups, - }; - if (connect(s.get(), asSockaddr(&kernel_addr), sizeof(kernel_addr)) != 0) { - return -errno; - } - - return s.release(); -} - -// Return a hidl_handle that owns the file descriptor owned by fd, and will -// auto-close it (otherwise there would be double-close problems). -// -// Rely upon the compiler to eliminate the constexprs used for clarity. -hidl_handle handleFromFileDescriptor(base::unique_fd fd) { - hidl_handle h; - - static constexpr int kNumFds = 1; - static constexpr int kNumInts = 0; - native_handle_t *nh = native_handle_create(kNumFds, kNumInts); - nh->data[0] = fd.release(); - - static constexpr bool kTakeOwnership = true; - h.setTo(nh, kTakeOwnership); - - return h; -} - -} // namespace - -static jboolean android_net_util_configOffload( - JNIEnv* /* env */) { - sp configInterface = IOffloadConfig::getService(); - if (configInterface.get() == nullptr) { - ALOGD("Could not find IOffloadConfig service."); - return false; - } - - // Per the IConfigOffload definition: - // - // fd1 A file descriptor bound to the following netlink groups - // (NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY). - // - // fd2 A file descriptor bound to the following netlink groups - // (NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY). - base::unique_fd - fd1(conntrackSocket(NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY)), - fd2(conntrackSocket(NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY)); - if (fd1.get() < 0 || fd2.get() < 0) { - ALOGE("Unable to create conntrack handles: %d/%s", errno, strerror(errno)); - return false; - } - - hidl_handle h1(handleFromFileDescriptor(std::move(fd1))), - h2(handleFromFileDescriptor(std::move(fd2))); - - bool rval(false); - hidl_string msg; - const auto status = configInterface->setHandles(h1, h2, - [&rval, &msg](bool success, const hidl_string& errMsg) { - rval = success; - msg = errMsg; - }); - if (!status.isOk() || !rval) { - ALOGE("IOffloadConfig::setHandles() error: '%s' / '%s'", - status.description().c_str(), msg.c_str()); - // If status is somehow not ok, make sure rval captures this too. - rval = false; - } - - return rval; -} - static void android_net_util_setupRaSocket(JNIEnv *env, jobject clazz, jobject javaFd, jint ifIndex) { @@ -229,7 +124,6 @@ static void android_net_util_setupRaSocket(JNIEnv *env, jobject clazz, jobject j */ static const JNINativeMethod gMethods[] = { /* name, signature, funcPtr */ - { "configOffload", "()Z", (void*) android_net_util_configOffload }, { "setupRaSocket", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_util_setupRaSocket }, }; @@ -242,7 +136,7 @@ int register_android_net_util_TetheringUtils(JNIEnv* env) { extern "C" jint JNI_OnLoad(JavaVM* vm, void*) { JNIEnv *env; if (vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6) != JNI_OK) { - ALOGE("ERROR: GetEnv failed"); + __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "ERROR: GetEnv failed"); return JNI_ERR; } diff --git a/Tethering/src/android/net/util/TetheringUtils.java b/Tethering/src/android/net/util/TetheringUtils.java index fa543bdce7..5a6d5c1cbf 100644 --- a/Tethering/src/android/net/util/TetheringUtils.java +++ b/Tethering/src/android/net/util/TetheringUtils.java @@ -24,14 +24,6 @@ import java.net.SocketException; * {@hide} */ public class TetheringUtils { - - /** - * Offload management process need to know conntrack rules to support NAT, but it may not have - * permission to create netlink netfilter sockets. Create two netlink netfilter sockets and - * share them with offload management process. - */ - public static native boolean configOffload(); - /** * Configures a socket for receiving ICMPv6 router solicitations and sending advertisements. * @param fd the socket's {@link FileDescriptor}. diff --git a/Tethering/src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java b/Tethering/src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java index 90b9d3f148..b545717208 100644 --- a/Tethering/src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java +++ b/Tethering/src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java @@ -18,19 +18,28 @@ package com.android.server.connectivity.tethering; import static android.net.util.TetheringUtils.uint16; +import android.hardware.tetheroffload.config.V1_0.IOffloadConfig; import android.hardware.tetheroffload.control.V1_0.IOffloadControl; import android.hardware.tetheroffload.control.V1_0.ITetheringOffloadCallback; import android.hardware.tetheroffload.control.V1_0.NatTimeoutUpdate; import android.hardware.tetheroffload.control.V1_0.NetworkProtocol; import android.hardware.tetheroffload.control.V1_0.OffloadCallbackEvent; +import android.net.netlink.NetlinkSocket; import android.net.util.SharedLog; -import android.net.util.TetheringUtils; +import android.net.util.SocketUtils; import android.os.Handler; +import android.os.NativeHandle; import android.os.RemoteException; +import android.system.ErrnoException; +import android.system.Os; import android.system.OsConstants; import com.android.internal.annotations.VisibleForTesting; +import java.io.FileDescriptor; +import java.io.IOException; +import java.net.SocketAddress; +import java.net.SocketException; import java.util.ArrayList; @@ -49,6 +58,10 @@ public class OffloadHardwareInterface { private static final String NO_INTERFACE_NAME = ""; private static final String NO_IPV4_ADDRESS = ""; private static final String NO_IPV4_GATEWAY = ""; + // Reference kernel/uapi/linux/netfilter/nfnetlink_compat.h + private static final int NF_NETLINK_CONNTRACK_NEW = 1; + private static final int NF_NETLINK_CONNTRACK_UPDATE = 2; + private static final int NF_NETLINK_CONNTRACK_DESTROY = 4; private final Handler mHandler; private final SharedLog mLog; @@ -121,9 +134,103 @@ public class OffloadHardwareInterface { return DEFAULT_TETHER_OFFLOAD_DISABLED; } - /** Configure offload management process. */ + /** + * Offload management process need to know conntrack rules to support NAT, but it may not have + * permission to create netlink netfilter sockets. Create two netlink netfilter sockets and + * share them with offload management process. + */ public boolean initOffloadConfig() { - return TetheringUtils.configOffload(); + IOffloadConfig offloadConfig; + try { + offloadConfig = IOffloadConfig.getService(); + } catch (RemoteException e) { + mLog.e("getIOffloadConfig error " + e); + return false; + } + if (offloadConfig == null) { + mLog.e("Could not find IOffloadConfig service"); + return false; + } + // Per the IConfigOffload definition: + // + // h1 provides a file descriptor bound to the following netlink groups + // (NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY). + // + // h2 provides a file descriptor bound to the following netlink groups + // (NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY). + final NativeHandle h1 = createConntrackSocket( + NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY); + if (h1 == null) return false; + + final NativeHandle h2 = createConntrackSocket( + NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY); + if (h2 == null) { + closeFdInNativeHandle(h1); + return false; + } + + final CbResults results = new CbResults(); + try { + offloadConfig.setHandles(h1, h2, + (boolean success, String errMsg) -> { + results.mSuccess = success; + results.mErrMsg = errMsg; + }); + } catch (RemoteException e) { + record("initOffloadConfig, setHandles fail", e); + return false; + } + // Explicitly close FDs. + closeFdInNativeHandle(h1); + closeFdInNativeHandle(h2); + + record("initOffloadConfig, setHandles results:", results); + return results.mSuccess; + } + + private void closeFdInNativeHandle(final NativeHandle h) { + try { + h.close(); + } catch (IOException | IllegalStateException e) { + // IllegalStateException means fd is already closed, do nothing here. + // Also nothing we can do if IOException. + } + } + + private NativeHandle createConntrackSocket(final int groups) { + FileDescriptor fd; + try { + fd = NetlinkSocket.forProto(OsConstants.NETLINK_NETFILTER); + } catch (ErrnoException e) { + mLog.e("Unable to create conntrack socket " + e); + return null; + } + + final SocketAddress sockAddr = SocketUtils.makeNetlinkSocketAddress(0, groups); + try { + Os.bind(fd, sockAddr); + } catch (ErrnoException | SocketException e) { + mLog.e("Unable to bind conntrack socket for groups " + groups + " error: " + e); + try { + SocketUtils.closeSocket(fd); + } catch (IOException ie) { + // Nothing we can do here + } + return null; + } + try { + Os.connect(fd, sockAddr); + } catch (ErrnoException | SocketException e) { + mLog.e("connect to kernel fail for groups " + groups + " error: " + e); + try { + SocketUtils.closeSocket(fd); + } catch (IOException ie) { + // Nothing we can do here + } + return null; + } + + return new NativeHandle(fd, true); } /** Initialize the tethering offload HAL. */