Android5.0之后,网络framework出现很大的变化,原生支持了以太网,并且支持多个网络同时连接同时存在。
Android5.0上面,并不是简单的网络共存,而是每个网络有一套自己的dns,网关,路由表。比如eth0,wlan0分别有自己独立的一套。应用层在建立socket连接的时候,可以自由选择使用那套网络。
下面看看Android是如何实现上述功能的
1. 独立保存的网络参数
Android5.0中引入和netid的概念,用它来标示不同的网络。ConnectivityService是Android的网络总管,负责系统上所有网络管理。当有dns,网关,路由表更新,就会由它设置到系统中并保存下来。
- private void updateDnses(LinkProperties newLp, LinkProperties oldLp, int netId, boolean flush) {
- 3682 if (oldLp == null || (newLp.isIdenticalDnses(oldLp) == false)) {
- 3683 Collection<InetAddress> dnses = newLp.getDnsServers();
- 3684 if (dnses.size() == 0 && mDefaultDns != null) {
- 3685 dnses = new ArrayList();
- 3686 dnses.add(mDefaultDns);
- 3687 if (DBG) {
- 3688 loge("no dns provided for netId " + netId + ", so using defaults");
- 3689 }
- 3690 }
- 3691 if (DBG) log("Setting Dns servers for network " + netId + " to " + dnses);
- 3692 try {
- 3693 mNetd.setDnsServersForNetwork(netId, NetworkUtils.makeStrings(dnses),
- 3694 newLp.getDomains());
- 3695 } catch (Exception e) {
- 3696 loge("Exception in setDnsServersForNetwork: " + e);
- 3697 }
- 3698 NetworkAgentInfo defaultNai = mNetworkForRequestId.get(mDefaultRequest.requestId);
- 3699 if (defaultNai != null && defaultNai.network.netId == netId) {
- 3700 setDefaultDnsSystemProperties(dnses);
- 3701 }
- 3702 flushVmDnsCache();
- 3703 } else if (flush) {
- 3704 try {
- 3705 mNetd.flushNetworkDnsCache(netId);
- 3706 } catch (Exception e) {
- 3707 loge("Exception in flushNetworkDnsCache: " + e);
- 3708 }
- 3709 flushVmDnsCache();
- 3710 }
- 3711 }
private void updateDnses(LinkProperties newLp, LinkProperties oldLp, int netId, boolean flush) {
3682 if (oldLp == null || (newLp.isIdenticalDnses(oldLp) == false)) {
3683 Collection<InetAddress> dnses = newLp.getDnsServers();
3684 if (dnses.size() == 0 && mDefaultDns != null) {
3685 dnses = new ArrayList();
3686 dnses.add(mDefaultDns);
3687 if (DBG) {
3688 loge("no dns provided for netId " + netId + ", so using defaults");
3689 }
3690 }
3691 if (DBG) log("Setting Dns servers for network " + netId + " to " + dnses);
3692 try {
3693 mNetd.setDnsServersForNetwork(netId, NetworkUtils.makeStrings(dnses),
3694 newLp.getDomains());
3695 } catch (Exception e) {
3696 loge("Exception in setDnsServersForNetwork: " + e);
3697 }
3698 NetworkAgentInfo defaultNai = mNetworkForRequestId.get(mDefaultRequest.requestId);
3699 if (defaultNai != null && defaultNai.network.netId == netId) {
3700 setDefaultDnsSystemProperties(dnses);
3701 }
3702 flushVmDnsCache();
3703 } else if (flush) {
3704 try {
3705 mNetd.flushNetworkDnsCache(netId);
3706 } catch (Exception e) {
3707 loge("Exception in flushNetworkDnsCache: " + e);
3708 }
3709 flushVmDnsCache();
3710 }
3711 }
updateDnses会调用setDnsServersForNetwork来设置dns
NetworkManagementService.Java
- public void setDnsServersForNetwork(int netId, String[] servers, String domains) {
- 1700 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
- 1701
- 1702 final Command cmd = new Command("resolver", "setnetdns", netId,
- 1703 (domains == null ? "" : domains));
- 1704
- 1705 for (String s : servers) {
- 1706 InetAddress a = NetworkUtils.numericToInetAddress(s);
- 1707 if (a.isAnyLocalAddress() == false) {
- 1708 cmd.appendArg(a.getHostAddress());
- 1709 }
- 1710 }
- 1711
- 1712 try {
- 1713 mConnector.execute(cmd);
- 1714 } catch (NativeDaemonConnectorException e) {
- 1715 throw e.rethrowAsParcelableException();
- 1716 }
- 1717 }
public void setDnsServersForNetwork(int netId, String[] servers, String domains) {
1700 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1701
1702 final Command cmd = new Command("resolver", "setnetdns", netId,
1703 (domains == null ? "" : domains));
1704
1705 for (String s : servers) {
1706 InetAddress a = NetworkUtils.numericToInetAddress(s);
1707 if (a.isAnyLocalAddress() == false) {
1708 cmd.appendArg(a.getHostAddress());
1709 }
1710 }
1711
1712 try {
1713 mConnector.execute(cmd);
1714 } catch (NativeDaemonConnectorException e) {
1715 throw e.rethrowAsParcelableException();
1716 }
1717 }
上面的实现是将一组命令通过socket发送出去,接收端其实是netd中的CommandListener
CommandListener.cpp
- CommandListener::ResolverCmd::ResolverCmd() :
- 773 NetdCommand("resolver") {
- 774 }
- int CommandListener::ResolverCmd::runCommand(SocketClient *cli, int argc, char **margv) {
- 777 int rc = 0;
- 778 const char **argv = const_cast<const char **>(margv);
- 779
- 780 if (argc < 2) {
- 781 cli->sendMsg(ResponseCode::CommandSyntaxError, "Resolver missing arguments", false);
- 782 return 0;
- 783 }
- 784
- 785 if (!strcmp(argv[1], "setnetdns")) {
- 786 // "resolver setnetdns <netId> <domains> <dns1> <dns2> ..."
- 787 if (argc >= 5) {
- 788 rc = sResolverCtrl->setDnsServers(strtoul(argv[2], NULL, 0), argv[3], &argv[4], argc - 4);
- 789 } else {
- 790 cli->sendMsg(ResponseCode::CommandSyntaxError,
- 791 "Wrong number of arguments to resolver setnetdns", false);
- 792 return 0;
- 793 }
- 794 } else if (!strcmp(argv[1], "flushnet")) { // "resolver flushnet <netId>"
- 795 if (argc == 3) {
- 796 rc = sResolverCtrl->flushDnsCache(strtoul(argv[2], NULL, 0));
- 797 } else {
- 798 cli->sendMsg(ResponseCode::CommandSyntaxError,
- 799 "Wrong number of arguments to resolver flushnet", false);
- 800 return 0;
- 801 }
- 802 } else {
- 803 cli->sendMsg(ResponseCode::CommandSyntaxError,"Resolver unknown command", false);
- 804 return 0;
- 805 }
- 806
- 807 if (!rc) {
- 808 cli->sendMsg(ResponseCode::CommandOkay, "Resolver command succeeded", false);
- 809 } else {
- 810 cli->sendMsg(ResponseCode::OperationFailed, "Resolver command failed", true);
- 811 }
- 812
- 813 return 0;
- 814 }
CommandListener::ResolverCmd::ResolverCmd() :
773 NetdCommand("resolver") {
774 }
int CommandListener::ResolverCmd::runCommand(SocketClient *cli, int argc, char **margv) {
777 int rc = 0;
778 const char **argv = const_cast<const char **>(margv);
779
780 if (argc < 2) {
781 cli->sendMsg(ResponseCode::CommandSyntaxError, "Resolver missing arguments", false);
782 return 0;
783 }
784
785 if (!strcmp(argv[1], "setnetdns")) {
786 // "resolver setnetdns <netId> <domains> <dns1> <dns2> ..."
787 if (argc >= 5) {
788 rc = sResolverCtrl->setDnsServers(strtoul(argv[2], NULL, 0), argv[3], &argv[4], argc - 4);
789 } else {
790 cli->sendMsg(ResponseCode::CommandSyntaxError,
791 "Wrong number of arguments to resolver setnetdns", false);
792 return 0;
793 }
794 } else if (!strcmp(argv[1], "flushnet")) { // "resolver flushnet <netId>"
795 if (argc == 3) {
796 rc = sResolverCtrl->flushDnsCache(strtoul(argv[2], NULL, 0));
797 } else {
798 cli->sendMsg(ResponseCode::CommandSyntaxError,
799 "Wrong number of arguments to resolver flushnet", false);
800 return 0;
801 }
802 } else {
803 cli->sendMsg(ResponseCode::CommandSyntaxError,"Resolver unknown command", false);
804 return 0;
805 }
806
807 if (!rc) {
808 cli->sendMsg(ResponseCode::CommandOkay, "Resolver command succeeded", false);
809 } else {
810 cli->sendMsg(ResponseCode::OperationFailed, "Resolver command failed", true);
811 }
812
813 return 0;
814 }
这里会最终通过调用netd的ResolverConntroller的setDnsServers来完成,setDnsServers里面调用bionic中函数__resolv_set_nameservers_for_net完成dns与netid的绑定和dns的存储。网关和路由的过程与dns类似,这里就不列举。只是要知道,系统中为每个网络保持了一份独立的网络参数(dns,路由,网关,代理等),并用netid与之映射。
2. 自由选择要使用的网络
下面这个api可以完成选择指定网络的功能
ConnectivityManager.java
- public static boolean setProcessDefaultNetwork(Network network) {
- 2499 int netId = (network == null) ? NETID_UNSET : network.netId;
- 2500 if (netId == NetworkUtils.getNetworkBoundToProcess()) {
- 2501 return true;
- 2502 }
- 2503 if (NetworkUtils.bindProcessToNetwork(netId)) {
- 2504 Log.d(TAG, "setProcessDefaultNetwork netId: " + netId);
- 2505 // Must flush DNS cache as new network may have different DNS resolutions.
- 2506 InetAddress.clearDnsCache();
- 2507 // Must flush socket pool as idle sockets will be bound to previous network and may
- 2508 // cause subsequent fetches to be performed on old network.
- 2509 NetworkEventDispatcher.getInstance().onNetworkConfigurationChanged();
- 2510 return true;
- 2511 } else {
- 2512 return false;
- 2513 }
- 2514 }
public static boolean setProcessDefaultNetwork(Network network) {
2499 int netId = (network == null) ? NETID_UNSET : network.netId;
2500 if (netId == NetworkUtils.getNetworkBoundToProcess()) {
2501 return true;
2502 }
2503 if (NetworkUtils.bindProcessToNetwork(netId)) {
2504 Log.d(TAG, "setProcessDefaultNetwork netId: " + netId);
2505 // Must flush DNS cache as new network may have different DNS resolutions.
2506 InetAddress.clearDnsCache();
2507 // Must flush socket pool as idle sockets will be bound to previous network and may
2508 // cause subsequent fetches to be performed on old network.
2509 NetworkEventDispatcher.getInstance().onNetworkConfigurationChanged();
2510 return true;
2511 } else {
2512 return false;
2513 }
2514 }
这个api最终会调用到netd
NetdClient.cpp
- extern "C" int setNetworkForProcess(unsigned netId) {
- 192 return setNetworkForTarget(netId, &netIdForProcess);
- 193 }
extern "C" int setNetworkForProcess(unsigned netId) {
192 return setNetworkForTarget(netId, &netIdForProcess);
193 }
- int setNetworkForTarget(unsigned netId, std::atomic_uint* target) {
- 112 if (netId == NETID_UNSET) {
- 113 *target = netId;
- 114 return 0;
- 115 }
- 116 // Verify that we are allowed to use |netId|, by creating a socket and trying to have it marked
- 117 // with the netId. Call libcSocket() directly; else the socket creation (via netdClientSocket())
- 118 // might itself cause another check with the fwmark server, which would be wasteful.
- 119 int socketFd;
- 120 if (libcSocket) {
- 121 socketFd = libcSocket(AF_INET6, SOCK_DGRAM, 0);
- 122 } else {
- 123 socketFd = socket(AF_INET6, SOCK_DGRAM, 0);
- 124 }
- 125 if (socketFd < 0) {
- 126 return -errno;
- 127 }
- 128 int error = setNetworkForSocket(netId, socketFd);
- 129 if (!error) {
- 130 *target = netId;
- 131 }
- 132 close(socketFd);
- 133 return error;
- 134 }
int setNetworkForTarget(unsigned netId, std::atomic_uint* target) {
112 if (netId == NETID_UNSET) {
113 *target = netId;
114 return 0;
115 }
116 // Verify that we are allowed to use |netId|, by creating a socket and trying to have it marked
117 // with the netId. Call libcSocket() directly; else the socket creation (via netdClientSocket())
118 // might itself cause another check with the fwmark server, which would be wasteful.
119 int socketFd;
120 if (libcSocket) {
121 socketFd = libcSocket(AF_INET6, SOCK_DGRAM, 0);
122 } else {
123 socketFd = socket(AF_INET6, SOCK_DGRAM, 0);
124 }
125 if (socketFd < 0) {
126 return -errno;
127 }
128 int error = setNetworkForSocket(netId, socketFd);
129 if (!error) {
130 *target = netId;
131 }
132 close(socketFd);
133 return error;
134 }
上面这段主要作用是对netIdForProcess赋值
当应用建立socket的时候会调用到下面这个api
NetdClient.cpp
- int netdClientSocket(int domain, int type, int protocol) {
- 86 int socketFd = libcSocket(domain, type, protocol);
- 87 if (socketFd == -1) {
- 88 return -1;
- 89 }
- 90 unsigned netId = netIdForProcess;
- 92 if (netId != NETID_UNSET && FwmarkClient::shouldSetFwmark(domain)) {
- 93 if (int error = setNetworkForSocket(netId, socketFd)) {
- 94 return closeFdAndSetErrno(socketFd, error);
- 95 }
- 96 }
- 97 return socketFd;
- 98 }
int netdClientSocket(int domain, int type, int protocol) {
86 int socketFd = libcSocket(domain, type, protocol);
87 if (socketFd == -1) {
88 return -1;
89 }
90 unsigned netId = netIdForProcess;
92 if (netId != NETID_UNSET && FwmarkClient::shouldSetFwmark(domain)) {
93 if (int error = setNetworkForSocket(netId, socketFd)) {
94 return closeFdAndSetErrno(socketFd, error);
95 }
96 }
97 return socketFd;
98 }
这里netIdForProcess就是前面设置下来的,因为它的值不再是NETID_UNSET,导致条件满足,调用到setNetworkForSocket
- extern "C" int setNetworkForSocket(unsigned netId, int socketFd) {
- 184 if (socketFd < 0) {
- 185 return -EBADF;
- 186 }
- 187 FwmarkCommand command = {FwmarkCommand::SELECT_NETWORK, netId, 0};
- 188 return FwmarkClient().send(&command, sizeof(command), socketFd);
- 189 }
extern "C" int setNetworkForSocket(unsigned netId, int socketFd) {
184 if (socketFd < 0) {
185 return -EBADF;
186 }
187 FwmarkCommand command = {FwmarkCommand::SELECT_NETWORK, netId, 0};
188 return FwmarkClient().send(&command, sizeof(command), socketFd);
189 }
这个function最终将netId通过本地socket发送出去,结果由FwmakrServer接收并处理
- case FwmarkCommand::SELECT_NETWORK: {
- 155 fwmark.netId = command.netId;
- 156 if (command.netId == NETID_UNSET) {
- 157 fwmark.explicitlySelected = false;
- 158 fwmark.protectedFromVpn = false;
- 159 permission = PERMISSION_NONE;
- 160 } else {
- 161 if (int ret = mNetworkController->checkUserNetworkAccess(client->getUid(),
- 162 command.netId)) {
- 163 return ret;
- 164 }
- 165 fwmark.explicitlySelected = true;
- 166 fwmark.protectedFromVpn = mNetworkController->canProtect(client->getUid());
- 167 }
- 168 break;
- 169 }
case FwmarkCommand::SELECT_NETWORK: {
155 fwmark.netId = command.netId;
156 if (command.netId == NETID_UNSET) {
157 fwmark.explicitlySelected = false;
158 fwmark.protectedFromVpn = false;
159 permission = PERMISSION_NONE;
160 } else {
161 if (int ret = mNetworkController->checkUserNetworkAccess(client->getUid(),
162 command.netId)) {
163 return ret;
164 }
165 fwmark.explicitlySelected = true;
166 fwmark.protectedFromVpn = mNetworkController->canProtect(client->getUid());
167 }
168 break;
169 }
fwmark.permission = permission;
206 if (setsockopt(*socketFd, SOL_SOCKET, SO_MARK, &fwmark.intValue,
207 sizeof(fwmark.intValue)) == -1) {
208 return -errno;
209 }
在每个网络建立起来后,系统都会为其建立一条路由规则。下图是Android系统中所有的路由规则
以下面这条路由规则为例:
0x10064其中0x64(十进制为100)就是该网络的netid,如果应用选择在这个网络上建立socket,那么通过上面那些步骤建立的socket,就会被带有0x64的mark,这样当改socket输出数据时候,就会被上面的路由规则捕获,从而使用eth0这个路由表。
下面是eth0路由表的内容