当前位置: 代码迷 >> Android >> Android5.0网络之多网络共处与应用
  详细解决方案

Android5.0网络之多网络共处与应用

热度:453   发布时间:2016-04-28 00:03:49.0
Android5.0网络之多网络共存与应用

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路由表的内容










版权声明:本文为博主原创文章,未经博主允许不得转载。

  相关解决方案