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 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(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 ..."
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 "
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 }
这个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 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 }
这里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路由表的内容