关闭

[置顶] Android5.0网络之多网络共存与应用

标签: android网络framework网络编程socket
3456人阅读 评论(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 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路由表的内容










1
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:22818次
    • 积分:445
    • 等级:
    • 排名:千里之外
    • 原创:23篇
    • 转载:2篇
    • 译文:0篇
    • 评论:2条
    最新评论