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 }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 }上面的实现是将一组命令通过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 }这里会最终通过调用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 may2508 // 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 }
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 marked117 // 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 }这里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 }这个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 }
fwmark.permission = permission; 206 if (setsockopt(*socketFd, SOL_SOCKET, SO_MARK, &fwmark.intValue,207 sizeof(fwmark.intValue)) == -1) {208 return -errno;209 }上面的处理中,首先会将netid赋值给fwmark,最后通过setsocketopt将fwmark设置到socket中,这样就为建立的socket设置了mark。下面就是看如何使用mark。
在每个网络建立起来后,系统都会为其建立一条路由规则。下图是Android系统中所有的路由规则
以下面这条路由规则为例:
0x10064其中0x64(十进制为100)就是该网络的netid,如果应用选择在这个网络上建立socket,那么通过上面那些步骤建立的socket,就会被带有0x64的mark,这样当改socket输出数据时候,就会被上面的路由规则捕获,从而使用eth0这个路由表。
下面是eth0路由表的内容
版权声明:本文为博主原创文章,未经博主允许不得转载。