android支持多种网络类型(WAN口),例如WIFI、3G等。目前android的实现是,WIFI和3G只能同时存在一个(优先级),例如当WIFI连接后,数据通路就从3G切换到WIFI。对上层app而言,这时候数据通路也就从3G切换到WIFI上。
考虑一个特殊的需求,某app只能通过WIFI接口去传输数据,是否可以实现?较新版本的android已经支持了该功能,通过调用setProcessDefaultNetwork()
可以指定某一进程的网络接口,
/**
* Binds the current process to {@code network}. All Sockets created in the future
* (and not explicitly bound via a bound SocketFactory from
* {@link Network#getSocketFactory() Network.getSocketFactory()}) will be bound to
* {@code network}. All host name resolutions will be limited to {@code network} as well.
* Note that if {@code network} ever disconnects, all Sockets created in this way will cease to
* work and all host name resolutions will fail. This is by design so an application doesn't
* accidentally use Sockets it thinks are still bound to a particular {@link Network}.
* To clear binding pass {@code null} for {@code network}. Using individually bound
* Sockets created by Network.getSocketFactory().createSocket() and
* performing network-specific host name resolutions via
* {@link Network#getAllByName Network.getAllByName} is preferred to calling
* {@code setProcessDefaultNetwork}.
*
* @param network The {@link Network} to bind the current process to, or {@code null} to clear
* the current binding.
* @return {@code true} on success, {@code false} if the {@link Network} is no longer valid.
*/
public static boolean setProcessDefaultNetwork(Network network) {
}
该函数的实现原理大致为,
1. 该进程在创建socket时(app首先调用setProcessDefaultNetwork()),android底层会利用setsockopt
函数设置该socket的SO_MARK为netId(android有自己的管理逻辑,每个Network有对应的ID),以后利用该socket发送的数据都会被打上netId的标记(fwmark 值)。
2. 利用策略路由,将打着netId标记的数据包都路由到WIFI的接口wlan0。
这里先介绍打标签的原理,至于策略路由的创建,后续再分析,下面是策略路由表的一个简单例子。
shell@msm8916_64:/ $ ip rule list
ip rule list
0: from all lookup local
10000: from all fwmark 0xc0000/0xd0000 lookup 99
13000: from all fwmark 0x10063/0x1ffff lookup 97
13000: from all fwmark 0x10064/0x1ffff lookup 1012
14000: from all oif rmnet_data0 lookup 1012
15000: from all fwmark 0x0/0x10000 lookup 99
16000: from all fwmark 0x0/0x10000 lookup 98
17000: from all fwmark 0x0/0x10000 lookup 97
19000: from all fwmark 0x64/0x1ffff lookup 1012
22000: from all fwmark 0x0/0xffff lookup 1012
23000: from all fwmark 0x0/0xffff uidrange 0-0 lookup main
32000: from all unreachable
shell@msm8916_64:/ $
android java库是libcore\
目录下的相关代码,而上层的java api基本都是调用该java库中的实现,然后java库通过JNI的方式调用底层的实现,例如android中的c库bionic\
等。
下面,我们从java层调用Socket函数开始,分析上述调用的过程。
public Socket(String dstName, int dstPort, InetAddress localAddress, int localPort) throws IOException {
//无参构造函数
this();
tryAllAddresses(dstName, dstPort, localAddress, localPort, true);
}
//Socket的实现是PlainSocketImpl
public Socket() {
this.impl = factory != null ? factory.createSocketImpl() : new PlainSocketImpl();
this.proxy = null;
}
private void tryAllAddresses(String dstName, int dstPort, InetAddress
localAddress, int localPort, boolean streaming) throws IOException {
startupSocket(dstAddress, dstPort, localAddress, localPort, streaming);
}
private void startupSocket(InetAddress dstAddress, int dstPort,
InetAddress localAddress, int localPort, boolean streaming)
throws IOException {
synchronized (this) {
//其实是调用impl的create和connect函数,
//impl即是PlainSocketImpl
impl.create(streaming);
impl.connect(dstAddress, dstPort);
}
//-------libcore\luni\src\main\java\java\net\PlainSocketImpl.java
protected void create(boolean streaming) throws IOException {
this.streaming = streaming;
//调用IoBridge的socket函数
this.fd = IoBridge.socket(streaming);
}
//-------libcore\luni\src\main\java\libcore\io\IoBridge.java
public static FileDescriptor socket(boolean stream) throws SocketException {
FileDescriptor fd;
try {
//调用类Libcore静态成员os的socket方法
fd = Libcore.os.socket(AF_INET6, stream ? SOCK_STREAM : SOCK_DGRAM, 0);
return fd;
} catch (ErrnoException errnoException) {
throw errnoException.rethrowAsSocketException();
}
}
package libcore.io;
public final class Libcore {
private Libcore() { }
public static Os os = new BlockGuardOs(new Posix());
}
// new Posix()
public interface Os {
}
其实最终调用的是类Posix中的方法,下面是Posix中的socket()函数,是个native函数,
// libcore\luni\src\main\java\libcore\io\Posix.java
public native FileDescriptor socket(int domain, int type, int protocol) throws ErrnoException;
其实现为,
// libcore\luni\src\main\native\libcore_io_Posix.cpp
static jobject Posix_socket(JNIEnv* env, jobject, jint domain, jint type, jint protocol) {
int fd = throwIfMinusOne(env, "socket", TEMP_FAILURE_RETRY(socket(domain, type, protocol)));
return fd != -1 ? jniCreateFileDescriptor(env, fd) : NULL;
}
//进而调用C库中的函数
// -------------->bionic\libc\bionic\socket.cpp
int socket(int domain, int type, int protocol) {
return __netdClientDispatch.socket(domain, type, protocol);
}
在bonic库中,__netdClientDispatch是什么?
// bionic\libc\private\NetdClientDispatch.h
//NetdClientDispatch 结构体包含了socket、accept4、connect、netIdForResolv
//这几个函数的指针
struct NetdClientDispatch {
int (*accept4)(int, struct sockaddr*, socklen_t*, int);
int (*connect)(int, const struct sockaddr*, socklen_t);
int (*socket)(int, int, int);
unsigned (*netIdForResolv)(unsigned);
};
// __netdClientDispatch为NetdClientDispatch的"实例"
extern __LIBC_HIDDEN__ struct NetdClientDispatch __netdClientDispatch;
//bionic\libc\bionic\NetdClientDispatch.cpp
extern "C" __socketcall int __accept4(int, sockaddr*, socklen_t*, int);
extern "C" __socketcall int __connect(int, const sockaddr*, socklen_t);
extern "C" __socketcall int __socket(int, int, int);
static unsigned fallBackNetIdForResolv(unsigned netId) {
return netId;
}
// 填充__netdClientDispatch成员,都是类似标准C库中的函数实现,利用系统调用
// 和操作系统交互
// This structure is modified only at startup (when libc.so is loaded) and never
// afterwards, so it's okay that it's read later at runtime without a lock.
__LIBC_HIDDEN__ NetdClientDispatch __netdClientDispatch __attribute__((aligned(32))) = {
__accept4,
__connect,
__socket,
fallBackNetIdForResolv,
};
到此,我们分析了__netdClientDispatch是什么。
此外,bonic在preinit时会调用netdClientInit(),分析下发生了什么,
/*----bionic\libc\bionic\libc_init_dynamic.cpp----*/
__attribute__((constructor)) static void __libc_preinit() {
//bonic在preinit时会调用netdClientInit()
netdClientInit();
}
/*----bionic\libc\bionic\NetdClient.cpp----*/
extern "C" __LIBC_HIDDEN__ void netdClientInit() {
if (pthread_once(&netdClientInitOnce, netdClientInitImpl)) {
__libc_format_log(ANDROID_LOG_ERROR, "netdClient", "Failed to initialize netd_client");
}
/*----bionic\libc\bionic\NetdClient.cpp----*/
static void netdClientInitImpl() {
//打开库libnetd_client.so,查找netdClientInitSocket、
//netdClientInitAccept4等这几个函数的实现
void* netdClientHandle = dlopen("libnetd_client.so", RTLD_LAZY);
if (netdClientHandle == NULL) {
// If the library is not available, it's not an error. We'll just use
// default implementations of functions that it would've overridden.
return;
}
netdClientInitFunction(netdClientHandle, "netdClientInitAccept4",
&__netdClientDispatch.accept4);
netdClientInitFunction(netdClientHandle, "netdClientInitConnect",
&__netdClientDispatch.connect);
netdClientInitFunction(netdClientHandle, "netdClientInitNetIdForResolv",
&__netdClientDispatch.netIdForResolv);
netdClientInitFunction(netdClientHandle, "netdClientInitSocket", &__netdClientDispatch.socket);
}
//在handle中,即库中找symbol
//以netdClientInitSocket为例,传入的function为&__netdClientDispatch.socket,
//函数的指针,
template <typename FunctionType>
static void netdClientInitFunction(void* handle, const char* symbol, FunctionType* function) {
typedef void (*InitFunctionType)(FunctionType*);
InitFunctionType initFunction = reinterpret_cast(dlsym(handle, symbol));
if (initFunction != NULL) {
initFunction(function);
}
}
而库libnetd_client.so是在netd中,system\netd\client\
。
typedef int (*SocketFunctionType)(int, int, int);
SocketFunctionType libcSocket = 0;
extern "C" void netdClientInitSocket(SocketFunctionType* function) {
//这里*function在前面已经赋值,就是标准c库中的socket函数
//libcSocket 赋值为__socket
//而将__netdClientDispatch.socket赋值为netdClientSocket
if (function && *function) {
libcSocket = *function;
*function = netdClientSocket;
}
}
所以在bonic在初始化时,主要做的工作就是将__netdClientDispatch.socket函数设置为netdClientSocket(),而libcSocket 赋值为__socket()(socket相关函数的实现都类似)。
public static boolean setProcessDefaultNetwork(Network network) {
int netId = (network == null) ? NETID_UNSET : network.netId;
if (netId == NetworkUtils.getNetworkBoundToProcess()) {
return true;
}
//调用bindProcessToNetwork()
if (NetworkUtils.bindProcessToNetwork(netId)) {
return true;
} else {
return false;
}
}
bindProcessToNetwork()利用JNI实现,
/**
* Binds the current process to the network designated by {@code netId}. All sockets created
* in the future (and not explicitly bound via a bound {@link SocketFactory} (see
* {@link Network#getSocketFactory}) will be bound to this network. Note that if this
* {@code Network} ever disconnects all sockets created in this way will cease to work. This
* is by design so an application doesn't accidentally use sockets it thinks are still bound to
* a particular {@code Network}. Passing NETID_UNSET clears the binding.
*/
public native static boolean bindProcessToNetwork(int netId);
static jboolean android_net_utils_bindProcessToNetwork(JNIEnv *env, jobject thiz, jint netId)
{
return (jboolean) !setNetworkForProcess(netId);
}
//进程的netId,netIdForProcess
std::atomic_uint netIdForProcess(NETID_UNSET);
extern "C" int setNetworkForProcess(unsigned netId) {
return setNetworkForTarget(netId, &netIdForProcess);
}
int setNetworkForTarget(unsigned netId, std::atomic_uint* target) {
if (netId == NETID_UNSET) {
*target = netId;
return 0;
}
// Verify that we are allowed to use |netId|, by creating a socket and trying to have it marked
// with the netId. Call libcSocket() directly; else the socket creation (via netdClientSocket())
// might itself cause another check with the fwmark server, which would be wasteful.
int socketFd;
//libcSocket 赋值为__socket
if (libcSocket) {
socketFd = libcSocket(AF_INET6, SOCK_DGRAM, 0);
} else {
socketFd = socket(AF_INET6, SOCK_DGRAM, 0);
}
if (socketFd < 0) {
return -errno;
}
int error = setNetworkForSocket(netId, socketFd);
if (!error) {
//将netIdForProcess设置为netId
*target = netId;
}
close(socketFd);
return error;
}
//给socketFd这个socket设置网络ID
extern "C" int setNetworkForSocket(unsigned netId, int socketFd) {
if (socketFd < 0) {
return -EBADF;
}
//要发送的是FwmarkCommand结构体,命令为SELECT_NETWORK
FwmarkCommand command = {FwmarkCommand::SELECT_NETWORK, netId, 0};
return FwmarkClient().send(&command, sizeof(command), socketFd);
}
//上层创建的socket为fd,而需要设置的netid在data中
int FwmarkClient::send(void* data, size_t len, int fd) {
mChannel = socket(AF_UNIX, SOCK_STREAM, 0);
if (mChannel == -1) {
return -errno;
}
//这里连接fwserver,向server发东西
//fwserver即/dev/socket/fwmarkd,init.rc中在netd启动时创建的socket
if (TEMP_FAILURE_RETRY(connect(mChannel, reinterpret_cast<const sockaddr*>(&FWMARK_SERVER_PATH),
sizeof(FWMARK_SERVER_PATH))) == -1) {
// If we are unable to connect to the fwmark server, assume there's no error. This protects
// against future changes if the fwmark server goes away.
return 0;
}
iovec iov;
iov.iov_base = data;
iov.iov_len = len;
msghdr message;
memset(&message, 0, sizeof(message));
message.msg_iov = &iov;
message.msg_iovlen = 1;
union {
cmsghdr cmh;
char cmsg[CMSG_SPACE(sizeof(fd))];
} cmsgu;
memset(cmsgu.cmsg, 0, sizeof(cmsgu.cmsg));
message.msg_control = cmsgu.cmsg;
message.msg_controllen = sizeof(cmsgu.cmsg);
//把上层创建的socket,即需要设置netId的socket放到这里
cmsghdr* const cmsgh = CMSG_FIRSTHDR(&message);
cmsgh->cmsg_len = CMSG_LEN(sizeof(fd));
cmsgh->cmsg_level = SOL_SOCKET;
cmsgh->cmsg_type = SCM_RIGHTS;
memcpy(CMSG_DATA(cmsgh), &fd, sizeof(fd));
if (TEMP_FAILURE_RETRY(sendmsg(mChannel, &message, 0)) == -1) {
return -errno;
}
int error = 0;
if (TEMP_FAILURE_RETRY(recv(mChannel, &error, sizeof(error), 0)) == -1) {
return -errno;
}
return error;
}
netd中的fwmarkd收到数据,
//fwmarkd这个socket接收到数据啦
bool FwmarkServer::onDataAvailable(SocketClient* client) {
int socketFd = -1;
int error = processClient(client, &socketFd);
if (socketFd >= 0) {
close(socketFd);
}
return false;
}
int FwmarkServer::processClient(SocketClient* client, int* socketFd) {
FwmarkCommand command;
iovec iov;
iov.iov_base = &command;
iov.iov_len = sizeof(command);
msghdr message;
memset(&message, 0, sizeof(message));
message.msg_iov = &iov;
message.msg_iovlen = 1;
union {
cmsghdr cmh;
char cmsg[CMSG_SPACE(sizeof(*socketFd))];
} cmsgu;
memset(cmsgu.cmsg, 0, sizeof(cmsgu.cmsg));
message.msg_control = cmsgu.cmsg;
message.msg_controllen = sizeof(cmsgu.cmsg);
//收取的数据中包含command,FwmarkCommand command = {FwmarkCommand::SELECT_NETWORK, netId, 0};
//里面有需要设置的netId
int messageLength = TEMP_FAILURE_RETRY(recvmsg(client->getSocket(), &message, 0));
if (messageLength <= 0) {
return -errno;
}
if (messageLength != sizeof(command)) {
return -EBADMSG;
}
cmsghdr* const cmsgh = CMSG_FIRSTHDR(&message);
//将client传送过来的需要设置NetId的socket赋值给socketFd,
if (cmsgh && cmsgh->cmsg_level == SOL_SOCKET && cmsgh->cmsg_type == SCM_RIGHTS &&
cmsgh->cmsg_len == CMSG_LEN(sizeof(*socketFd))) {
memcpy(socketFd, CMSG_DATA(cmsgh), sizeof(*socketFd));
}
if (*socketFd < 0) {
return -EBADF;
}
Fwmark fwmark;
socklen_t fwmarkLen = sizeof(fwmark.intValue);
if (getsockopt(*socketFd, SOL_SOCKET, SO_MARK, &fwmark.intValue, &fwmarkLen) == -1) {
return -errno;
}
Permission permission = mNetworkController->getPermissionForUser(client->getUid());
switch (command.cmdId) {
case FwmarkCommand::SELECT_NETWORK: {
fwmark.netId = command.netId;
if (command.netId == NETID_UNSET) {
fwmark.explicitlySelected = false;
fwmark.protectedFromVpn = false;
permission = PERMISSION_NONE;
} else {
if (int ret = mNetworkController->checkUserNetworkAccess(client->getUid(),
command.netId)) {
return ret;
}
//设置explicitlySelected 为true
fwmark.explicitlySelected = true;
fwmark.protectedFromVpn = mNetworkController->canProtect(client->getUid());
}
break;
}
fwmark.permission = permission;
//fwmark是个UNION,这里将NetId,给上层创立的socket即 socketfd设置SO_MARK为NetId数值
if (setsockopt(*socketFd, SOL_SOCKET, SO_MARK, &fwmark.intValue,
sizeof(fwmark.intValue)) == -1) {
return -errno;
}
return 0;
}
通过上面分析,setProcessDefaultNetwork()函数仅仅是将进程的netIdForProcess设置为netId。在调用setProcessDefaultNetwork()后,再继续创建socket,connect会做什么?
上面分析在java层调用Socket函数时,最终会调用__netdClientDispatch.socket(),而__netdClientDispatch.socket()被赋值为netdClientSocket,在此步骤中会取出setProcessDefaultNetwork()函数设置的netIdForProcess值,然后设置给socket为其fwmark。
int socket(int domain, int type, int protocol) {
return __netdClientDispatch.socket(domain, type, protocol);
}
int netdClientSocket(int domain, int type, int protocol) {
int socketFd = -1;
if (propSocket) {
socketFd = propSocket(domain, type, protocol);
} else if (libcSocket) {
//用标准C库函数创建socket
socketFd = libcSocket(domain, type, protocol);
}
if (-1 == socketFd) {
return -1;
}
//通过setProcessDefaultNetwork()已经将netIdForProcess设置为netId了,
unsigned netId = netIdForProcess;
if (netId != NETID_UNSET && FwmarkClient::shouldSetFwmark(domain)) {
if (int error = setNetworkForSocket(netId, socketFd)) {
return closeFdAndSetErrno(socketFd, error);
}
}
return socketFd;
}
关于setNetworkForSocket()实现,上面已经分析过,主要就是为该socketFd通过setsockopt()
设置socket的SO_MARK为netId,关于connect(),accept()的实现和socket()类似。