突破点,已知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 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