Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7cacdf74 authored by Mark Chien's avatar Mark Chien Committed by Gerrit Code Review
Browse files

Merge "Move offload config hidl usage from native to java"

parents d61f0dca 0d5a36be
Loading
Loading
Loading
Loading
+15 −14
Original line number Diff line number Diff line
@@ -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: [
+2 −108
Original line number Diff line number Diff line
@@ -16,123 +16,18 @@

#include <errno.h>
#include <error.h>
#include <hidl/HidlSupport.h>
#include <jni.h>
#include <nativehelper/JNIHelp.h>
#include <nativehelper/ScopedUtfChars.h>
#include <linux/netfilter/nfnetlink.h>
#include <linux/netlink.h>
#include <net/if.h>
#include <netinet/icmp6.h>
#include <sys/socket.h>
#include <android-base/unique_fd.h>
#include <android/hardware/tetheroffload/config/1.0/IOffloadConfig.h>

#define LOG_TAG "TetheringUtils"
#include <utils/Log.h>
#include <android/log.h>

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<const sockaddr *>(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<IOffloadConfig> 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<void**>(&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;
    }

+0 −8
Original line number Diff line number Diff line
@@ -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}.
+110 −3
Original line number Diff line number Diff line
@@ -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. */