美文网首页
安卓如何是ip地址生效的

安卓如何是ip地址生效的

作者: 叶迎宪 | 来源:发表于2022-11-04 21:39 被阅读0次

    突破点,已知ip地址存储在 ipconfig.txt。通过搜索代码,知道 EthernetConfigStore.java 负责读取和存储ip配置。而拥有 EthernetConfigStore实例的,是 EthernetTracker.java。阅读EthernetTracker代码,发现一处

        private void addInterface(String iface) {
            ...
            IpConfiguration ipConfiguration = getOrCreateIpConfiguration(iface);
            Log.d(TAG, "Tracking interface in client mode: " + iface);
            mFactory.addInterface(iface, hwAddress, ipConfiguration, nc);
    
            // Note: if the interface already has link (e.g., if we crashed and got
            // restarted while it was running), we need to fake a link up notification so we
            // start configuring it.
            if (NetdUtils.hasFlag(config, INetd.IF_FLAG_RUNNING)) {
                // no need to send an interface state change as this is not a true "state change". The
                // callers (maybeTrackInterface() and setTetheringInterfaceMode()) already broadcast the
                // state change.
                mFactory.updateInterfaceLinkState(iface, true);
            }
        }
    

    ip地址配置,只有mFactory类使用了,这是一个 EthernetNetworkFactory 类。

        protected void addInterface(@NonNull final String ifaceName, @NonNull final String hwAddress,
                @NonNull final IpConfiguration ipConfig,
                @NonNull final NetworkCapabilities capabilities) {
            if (mTrackingInterfaces.containsKey(ifaceName)) {
                Log.e(TAG, "Interface with name " + ifaceName + " already exists.");
                return;
            }
    
            final NetworkCapabilities nc = new NetworkCapabilities.Builder(capabilities)
                    .setNetworkSpecifier(new EthernetNetworkSpecifier(ifaceName))
                    .build();
    
            if (DBG) {
                Log.d(TAG, "addInterface, iface: " + ifaceName + ", capabilities: " + nc);
            }
    
            final NetworkInterfaceState iface = new NetworkInterfaceState(
                    ifaceName, hwAddress, mHandler, mContext, ipConfig, nc, mProvider, mDeps);
            mTrackingInterfaces.put(ifaceName, iface);
        }
    

    ipConfig传递给了NetworkInterfaceState类。在这个类的构造函数里面,只是简单的把 IpConfiguration 保存到类成员 mIpConfig 了,还看不出怎么是ip地址配置到网卡上的。继续搜代码,看哪里使用了 mIpConfig

            private void start() {
                if (mIpClient != null) {
                    if (DBG) Log.d(TAG, "IpClient already started");
                    return;
                }
                if (DBG) {
                    Log.d(TAG, String.format("Starting Ethernet IpClient(%s)", name));
                }
    
                mIpClientCallback = new EthernetIpClientCallback();
                mDeps.makeIpClient(mContext, name, mIpClientCallback);
                mIpClientCallback.awaitIpClientStart();
    
                if (sTcpBufferSizes == null) {
                    sTcpBufferSizes = mDeps.getTcpBufferSizesFromResource(mContext);
                }
                provisionIpClient(mIpClient, mIpConfig, sTcpBufferSizes);
            }
    
            private static void provisionIpClient(@NonNull final IpClientManager ipClient,
                    @NonNull final IpConfiguration config, @NonNull final String tcpBufferSizes) {
                if (config.getProxySettings() == ProxySettings.STATIC ||
                        config.getProxySettings() == ProxySettings.PAC) {
                    ipClient.setHttpProxy(config.getHttpProxy());
                }
    
                if (!TextUtils.isEmpty(tcpBufferSizes)) {
                    ipClient.setTcpBufferSizes(tcpBufferSizes);
                }
    
                ipClient.startProvisioning(createProvisioningConfiguration(config));
            }
    

    继续java的俄罗斯套娃之旅,跳到 IpClientManager.java

        /**
         * Start provisioning with the provided parameters.
         */
        public boolean startProvisioning(ProvisioningConfiguration prov) {
            final long token = Binder.clearCallingIdentity();
            try {
                mIpClient.startProvisioning(prov.toStableParcelable());
                return true;
            } catch (RemoteException e) {
                log("Error starting IpClient provisioning", e);
                return false;
            } finally {
                Binder.restoreCallingIdentity(token);
            }
        } 
    

    android/net/ip/IpClient.java

        public void startProvisioning(ProvisioningConfiguration req) {
            if (!req.isValid()) {
                doImmediateProvisioningFailure(IpManagerEvent.ERROR_INVALID_PROVISIONING);
                return;
            }
    
            mCurrentBssid = getInitialBssid(req.mLayer2Info, req.mScanResultInfo,
                    ShimUtils.isAtLeastS());
            if (req.mLayer2Info != null) {
                mL2Key = req.mLayer2Info.mL2Key;
                mCluster = req.mLayer2Info.mCluster;
            }
            sendMessage(CMD_START, new android.net.shared.ProvisioningConfiguration(req));
        }
    
        class StoppedState extends State { 
            @Override
            public boolean processMessage(Message msg) {
                switch (msg.what) {
                    case CMD_START:
                        mClearAddressesOnStop = shouldClearAddressesOnStop();
                        mConfiguration = (android.net.shared.ProvisioningConfiguration) msg.obj;
                        transitionTo(mClearAddressesOnStartState);
                        break;
            }
        } 
    

    继续跟踪 mConfiguration 哪里用了

        private LinkProperties assembleLinkProperties() {
            // [1] Create a new LinkProperties object to populate.
            LinkProperties newLp = new LinkProperties();
            newLp.setInterfaceName(mInterfaceName);
    
            // [2] Pull in data from netlink:
            //         - IPv4 addresses
            //         - IPv6 addresses
            //         - IPv6 routes
            //         - IPv6 DNS servers
            //
            // N.B.: this is fundamentally race-prone and should be fixed by
            // changing IpClientLinkObserver from a hybrid edge/level model to an
            // edge-only model, or by giving IpClient its own netlink socket(s)
            // so as to track all required information directly.
            LinkProperties netlinkLinkProperties = mLinkObserver.getLinkProperties();
            newLp.setLinkAddresses(netlinkLinkProperties.getLinkAddresses());
            for (RouteInfo route : netlinkLinkProperties.getRoutes()) {
                newLp.addRoute(route);
            }
            addAllReachableDnsServers(newLp, netlinkLinkProperties.getDnsServers());
            mShim.setNat64Prefix(newLp, mShim.getNat64Prefix(netlinkLinkProperties));
    
            // [3] Add in data from DHCPv4, if available.
            //
            // mDhcpResults is never shared with any other owner so we don't have
            // to worry about concurrent modification.
            if (mDhcpResults != null) {
                final List<RouteInfo> routes =
                        mDhcpResults.toStaticIpConfiguration().getRoutes(mInterfaceName);
                for (RouteInfo route : routes) {
                    newLp.addRoute(route);
                }
                addAllReachableDnsServers(newLp, mDhcpResults.dnsServers);
                newLp.setDomains(mDhcpResults.domains);
    
                if (mDhcpResults.mtu != 0) {
                    newLp.setMtu(mDhcpResults.mtu);
                }
    
                if (mDhcpResults.serverAddress != null) {
                    mShim.setDhcpServerAddress(newLp, mDhcpResults.serverAddress);
                }
    
                final String capportUrl = mDhcpResults.captivePortalApiUrl;
                // Uri.parse does no syntax check; do a simple check to eliminate garbage.
                // If the URL is still incorrect data fetching will fail later, which is fine.
                if (isParseableUrl(capportUrl)) {
                    NetworkInformationShimImpl.newInstance()
                            .setCaptivePortalApiUrl(newLp, Uri.parse(capportUrl));
                }
                // TODO: also look at the IPv6 RA (netlink) for captive portal URL
            }
    
            // [4] Add in TCP buffer sizes and HTTP Proxy config, if available.
            if (!TextUtils.isEmpty(mTcpBufferSizes)) {
                newLp.setTcpBufferSizes(mTcpBufferSizes);
            }
            if (mHttpProxy != null) {
                newLp.setHttpProxy(mHttpProxy);
            }
    
            // [5] Add data from InitialConfiguration
            if (mConfiguration != null && mConfiguration.mInitialConfig != null) {
                InitialConfiguration config = mConfiguration.mInitialConfig;
                // Add InitialConfiguration routes and dns server addresses once all addresses
                // specified in the InitialConfiguration have been observed with Netlink.
                if (config.isProvisionedBy(newLp.getLinkAddresses(), null)) {
                    for (IpPrefix prefix : config.directlyConnectedRoutes) {
                        newLp.addRoute(new RouteInfo(prefix, null, mInterfaceName, RTN_UNICAST));
                    }
                }
                addAllReachableDnsServers(newLp, config.dnsServers);
            }
            final LinkProperties oldLp = mLinkProperties;
            if (DBG) {
                Log.d(mTag, String.format("Netlink-seen LPs: %s, new LPs: %s; old LPs: %s",
                        netlinkLinkProperties, newLp, oldLp));
            }
    
            // TODO: also learn via netlink routes specified by an InitialConfiguration and specified
            // from a static IP v4 config instead of manually patching them in in steps [3] and [5].
            return newLp;
        }
    

    感觉快要接近真相了,继续查看 LinkProperties 类的实现。结果发现这个类只是一个保存网络属性状态的类,并没有实质性的配置ip等的实现。看来 assembleLinkProperties 这个是找错地方了。继续看IpClient的代码,哪里用了mConfiguration

        private boolean startIPv4() {
            // If we have a StaticIpConfiguration attempt to apply it and
            // handle the result accordingly.
            if (mConfiguration.mStaticIpConfig != null) {
                if (mInterfaceCtrl.setIPv4Address(mConfiguration.mStaticIpConfig.getIpAddress())) {
                    handleIPv4Success(new DhcpResults(mConfiguration.mStaticIpConfig));
                } else {
                    return false;
                }
            } else {
                if (mDhcpClient != null) {
                    Log.wtf(mTag, "DhcpClient should never be non-null in startIPv4()");
                }
                startDhcpClient();
            }
    
            return true;
        }
    

    frameworks/libs/net/common/device/com/android/net/module/util/ip/InterfaceController.java

        public boolean setIPv4Address(final LinkAddress address) {
            return setInterfaceConfiguration(address, null);
        }
    
        public boolean setInterfaceConfiguration(final LinkAddress ipv4Addr,
                final Boolean setIfaceUp) {
            if (!(ipv4Addr.getAddress() instanceof Inet4Address)) {
                throw new IllegalArgumentException("Invalid or mismatched Inet4Address");
            }
            // Note: currently netd only support INetd#IF_STATE_UP and #IF_STATE_DOWN.
            // Other flags would be ignored.
    
            final InterfaceConfigurationParcel ifConfig = new InterfaceConfigurationParcel();
            ifConfig.ifName = mIfName;
            ifConfig.ipv4Addr = ipv4Addr.getAddress().getHostAddress();
            ifConfig.prefixLength = ipv4Addr.getPrefixLength();
            // Netd ignores hwaddr in interfaceSetCfg.
            ifConfig.hwAddr = "";
            if (setIfaceUp == null) {
                // Empty array means no change.
                ifConfig.flags = new String[0];
            } else {
                // Netd ignores any flag that's not IF_STATE_UP or IF_STATE_DOWN in interfaceSetCfg.
                ifConfig.flags = setIfaceUp.booleanValue()
                        ? new String[] {IF_STATE_UP} : new String[] {IF_STATE_DOWN};
            }
            try {
                mNetd.interfaceSetCfg(ifConfig);
            } catch (RemoteException | ServiceSpecificException e) {
                logError("Setting IPv4 address to %s/%d failed: %s",
                        ifConfig.ipv4Addr, ifConfig.prefixLength, e);
                return false;
            }
            return true;
        }
    

    看到这里,已经可以猜到要进入netd服务去了。

          @Override public void interfaceSetCfg(android.net.InterfaceConfigurationParcel cfg) throws android.os.RemoteException
          {
            android.os.Parcel _data = android.os.Parcel.obtain();
            android.os.Parcel _reply = android.os.Parcel.obtain();
            try {
              _data.writeInterfaceToken(DESCRIPTOR);
              _data.writeTypedObject(cfg, 0);
              boolean _status = mRemote.transact(Stub.TRANSACTION_interfaceSetCfg, _data, _reply, 0);
              if (!_status) {
                throw new android.os.RemoteException("Method interfaceSetCfg is unimplemented.");
              }
              _reply.readException();
            }
            finally {
              _reply.recycle();
              _data.recycle();
            }
          }
    

    通过IBinder远程调用netd去了。netd/server/NetdNativeService.cpp

    binder::Status NetdNativeService::interfaceSetCfg(const InterfaceConfigurationParcel& cfg) {
        NETD_LOCKING_RPC(InterfaceController::mutex, PERM_NETWORK_STACK, PERM_MAINLINE_NETWORK_STACK);
        auto entry = gLog.newEntry()
                             .prettyFunction(__PRETTY_FUNCTION__)
                             .arg(interfaceConfigurationParcelToString(cfg));
    
        const auto& res = InterfaceController::setCfg(cfg);
        RETURN_BINDER_STATUS_IF_NOT_OK(entry, res);
    
        gLog.log(entry.withAutomaticDuration());
        return binder::Status::ok();
    } 
    

    到Native层就不跟下去了。有兴趣的可以继续参考
    https://stackoverflow.com/questions/67533929/where-does-android-actually-invoke-ip-link-or-ifconfig-to-bring-an-interface

    netd最终是通过AF_NETLINK把网络配置传给内核的。
    https://android.stackexchange.com/questions/225643/how-to-make-ethernet-work-on-android-over-otg

    相关文章

      网友评论

          本文标题:安卓如何是ip地址生效的

          本文链接:https://www.haomeiwen.com/subject/qypftdtx.html