Add native call to retrieve driver name via ethtool API

This adds a JNI function to retrieve an interface's driver name via
ethtool API, which will be used to identify USB NCM interfaces.

Unfortunately, SIOCETHTOOL (ETHTOOL_GDRVINFO in particular) does not
allow passing in the interface index, meaning that this operation is
racy, i.e. the interface might have been replaced when
getDriverNameForInterface() is called.

SICOETHTOOL is slowly being replaced by a netlink alternative that
allows querying by interface index but, unfortunately, does not support
ETHTOOL_GDRVINFO yet.

Test: verified manually; automated test will be added shortly.
Flag: EXEMPT mainline
Bug: 1000001
Change-Id: I8bec47e36a607e7d8fe2fac2eebcd8dff0360a02
This commit is contained in:
Patrick Rohr 2025-05-28 16:13:24 -07:00
commit 0eaec731ec
2 changed files with 37 additions and 0 deletions

View file

@ -49,6 +49,15 @@ public class ServiceConnectivityJni {
*/
public static native void setTimerFdTime(int fd, long timeMs) throws ErrnoException;
/**
* Get the driver name for a given interface using the ethtool API.
*
* @param ifname name of the interface to query.
* @return the name of the driver.
* @throws ErrnoException if ioctl() returns -1.
*/
public static native String getDriverNameForInterface(String ifname) throws ErrnoException;
/** Create tun/tap interface */
public static native int createTunTap(boolean isTun, boolean hasCarrier,
boolean setIffMulticast, @NonNull String iface);

View file

@ -18,6 +18,7 @@
#include <errno.h>
#include <fcntl.h>
#include <jni.h>
#include <linux/ethtool.h>
#include <linux/if.h>
#include <linux/if_tun.h>
#include <linux/ipv6_route.h>
@ -80,6 +81,31 @@ static void setTimerFdTime(JNIEnv *env, jclass clazz, jint tfd,
}
}
static jstring getDriverNameForInterface(JNIEnv *env, jclass clazz, jstring jifname) {
base::unique_fd fd(socket(AF_INET6, SOCK_DGRAM, 0));
if (!fd.ok()) {
jniThrowErrnoException(env, "getDriverNameForInterface", errno);
return nullptr;
}
ScopedUtfChars ifname(env, jifname);
if (!ifname.c_str()) {
jniThrowNullPointerException(env, "getDriverNameForInterface: ifname is null");
return nullptr;
}
ethtool_drvinfo msg = { .cmd = ETHTOOL_GDRVINFO };
ifreq ifr = { .ifr_data = reinterpret_cast<char *>(&msg) };
strlcpy(ifr.ifr_name, ifname.c_str(), IFNAMSIZ);
if (ioctl(fd, SIOCETHTOOL, &ifr)) {
jniThrowErrnoException(env, "getDriverNameForInterface", errno);
return nullptr;
}
return env->NewStringUTF(msg.driver);
}
static void throwException(JNIEnv *env, int error, const char *action,
const char *iface) {
const std::string &msg = "Error: " + std::string(action) + " " +
@ -195,6 +221,8 @@ static const JNINativeMethod gMethods[] = {
/* name, signature, funcPtr */
{"createTimerFd", "()I", (void *)createTimerFd},
{"setTimerFdTime", "(IJ)V", (void *)setTimerFdTime},
{"getDriverNameForInterface", "(Ljava/lang/String;)Ljava/lang/String;",
(void *)getDriverNameForInterface},
{"setTunTapCarrierEnabled", "(Ljava/lang/String;IZ)V",
(void *)setTunTapCarrierEnabled},
{"createTunTap", "(ZZZLjava/lang/String;)I", (void *)createTunTap},