参考?(八十七) WiFi & DHCP
1.DhcpClient
获取到ip地址后进度到ConfiguringInterfaceState 状态
class ConfiguringInterfaceState extends LoggingState {
@Override
public void enter() {
super.enter();
// We must call notifySuccess to apply the rest of the DHCP configuration (e.g., DNS
// servers) before adding the IP address to the interface. Otherwise, as soon as
// IpClient sees the IP address appear, it will enter provisioned state without any
// configuration information from DHCP. http://b/146850745.
notifySuccess();
mController.sendMessage(CMD_CONFIGURE_LINKADDRESS, mDhcpLease.ipAddress);
}
@Override
public boolean processMessage(Message message) {
super.processMessage(message);
switch (message.what) {
case EVENT_LINKADDRESS_CONFIGURED:
transitionTo(mDhcpBoundState);
return HANDLED;
default:
return NOT_HANDLED;
}
}
}
private void notifySuccess() {
if (isDhcpLeaseCacheEnabled()) {
maybeSaveLeaseToIpMemoryStore();
}
mController.sendMessage(
CMD_POST_DHCP_ACTION, DHCP_SUCCESS, 0, new DhcpResults(mDhcpLease));
}
?告知IpClient当前dhcp成功,将DhcpResult发给IpClient.
2.IpClient
ipClient开始解析DhcpResult里的内容进行封装
// This message is only received when:
//
// a) initial address acquisition succeeds,
// b) renew succeeds or is NAK'd,
// c) rebind succeeds or is NAK'd, or
// c) the lease expires,
//
// but never when initial address acquisition fails. The latter
// condition is now governed by the provisioning timeout.
case DhcpClient.CMD_POST_DHCP_ACTION:
stopDhcpAction();
switch (msg.arg1) {
case DhcpClient.DHCP_SUCCESS:
handleIPv4Success((DhcpResults) msg.obj);
break;
private void handleIPv4Success(DhcpResults dhcpResults) {
mDhcpResults = new DhcpResults(dhcpResults);
final LinkProperties newLp = assembleLinkProperties();
final int delta = setLinkProperties(newLp);
if (mDhcpResults.vendorInfo == null && detectUpstreamHotspotFromVendorIe()) {
mDhcpResults.vendorInfo = DhcpPacket.VENDOR_INFO_ANDROID_METERED;
}
if (DBG) {
Log.d(mTag, "onNewDhcpResults(" + Objects.toString(mDhcpResults) + ")");
Log.d(mTag, "handleIPv4Success newLp{" + newLp + "}");
}
mCallback.onNewDhcpResults(mDhcpResults);
maybeSaveNetworkToIpMemoryStore();
dispatchCallback(delta, newLp);
}
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());
newLp.setNat64Prefix(netlinkLinkProperties.getNat64Prefix());
// [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;
}
private void dispatchCallback(int delta, LinkProperties newLp) {
switch (delta) {
case PROV_CHANGE_GAINED_PROVISIONING:
if (DBG) {
Log.d(mTag, "onProvisioningSuccess()");
}
recordMetric(IpManagerEvent.PROVISIONING_OK);
mCallback.onProvisioningSuccess(newLp);
break;
3.ClientModeImpl
dhcp完成后,wifi状态机收到provision success的回调, 调用CS的api更新lp
@Override
public void onProvisioningSuccess(LinkProperties newLp) {
mWifiMetrics.logStaEvent(StaEvent.TYPE_CMD_IP_CONFIGURATION_SUCCESSFUL);
sendMessage(CMD_UPDATE_LINKPROPERTIES, newLp);
sendMessage(CMD_IP_CONFIGURATION_SUCCESSFUL);
}
private void updateLinkProperties(LinkProperties newLp) {
if (mVerboseLoggingEnabled) {
log("Link configuration changed for netId: " + mLastNetworkId
+ " old: " + mLinkProperties + " new: " + newLp);
}
// We own this instance of LinkProperties because IpClient passes us a copy.
mLinkProperties = newLp;
if (mNetworkAgent != null) {
mNetworkAgent.sendLinkProperties(mLinkProperties);
}
if (mNetworkAgentState == DetailedState.CONNECTED) {
// If anything has changed and we're already connected, send out a notification.
// TODO: Update all callers to use NetworkCallbacks and delete this.
sendLinkConfigurationChangedBroadcast();
}
if (mVerboseLoggingEnabled) {
StringBuilder sb = new StringBuilder();
sb.append("updateLinkProperties nid: " + mLastNetworkId);
sb.append(" state: " + mNetworkAgentState);
if (mLinkProperties != null) {
sb.append(" ");
sb.append(getLinkPropertiesSummary(mLinkProperties));
}
logd(sb.toString());
}
}
4.ConnectivityService
CS获取ClientModeImpl传来的Lp更新dns
case NetworkAgent.EVENT_NETWORK_PROPERTIES_CHANGED: {
LinkProperties newLp = (LinkProperties) msg.obj;
processLinkPropertiesFromAgent(nai, newLp);
handleUpdateLinkProperties(nai, newLp);
break;
}
public void handleUpdateLinkProperties(NetworkAgentInfo nai, LinkProperties newLp) {
ensureRunningOnConnectivityServiceThread();
if (getNetworkAgentInfoForNetId(nai.network.netId) != nai) {
// Ignore updates for disconnected networks
return;
}
if (VDBG || DDBG) {
log("Update of LinkProperties for " + nai.toShortString()
+ "; created=" + nai.created
+ "; everConnected=" + nai.everConnected);
}
// TODO: eliminate this defensive copy after confirming that updateLinkProperties does not
// modify its oldLp parameter.
updateLinkProperties(nai, newLp, new LinkProperties(nai.linkProperties));
}
private void updateLinkProperties(NetworkAgentInfo networkAgent, LinkProperties newLp,
@NonNull LinkProperties oldLp) {
int netId = networkAgent.network.netId;
// The NetworkAgent does not know whether clatd is running on its network or not, or whether
// a NAT64 prefix was discovered by the DNS resolver. Before we do anything else, make sure
// the LinkProperties for the network are accurate.
networkAgent.clatd.fixupLinkProperties(oldLp, newLp);
updateInterfaces(newLp, oldLp, netId, networkAgent.networkCapabilities,
networkAgent.networkInfo.getType());
// update filtering rules, need to happen after the interface update so netd knows about the
// new interface (the interface name -> index map becomes initialized)
updateVpnFiltering(newLp, oldLp, networkAgent);
updateMtu(newLp, oldLp);
// TODO - figure out what to do for clat
// for (LinkProperties lp : newLp.getStackedLinks()) {
// updateMtu(lp, null);
// }
if (isDefaultNetwork(networkAgent)) {
updateTcpBufferSizes(newLp.getTcpBufferSizes());
}
updateRoutes(newLp, oldLp, netId);
updateDnses(newLp, oldLp, netId);
private void updateDnses(LinkProperties newLp, LinkProperties oldLp, int netId) {
if (oldLp != null && newLp.isIdenticalDnses(oldLp)) {
return; // no updating necessary
}
final NetworkAgentInfo defaultNai = getDefaultNetwork();
final boolean isDefaultNetwork = (defaultNai != null && defaultNai.network.netId == netId);
if (DBG) {
final Collection<InetAddress> dnses = newLp.getDnsServers();
log("Setting DNS servers for network " + netId + " to " + dnses);
}
try {
mDnsManager.noteDnsServersForNetwork(netId, newLp);
// TODO: netd should listen on [::1]:53 and proxy queries to the current
// default network, and we should just set net.dns1 to ::1, not least
// because applications attempting to use net.dns resolvers will bypass
// the privacy protections of things like DNS-over-TLS.
if (isDefaultNetwork) mDnsManager.setDefaultDnsSystemProperties(newLp.getDnsServers());
mDnsManager.flushVmDnsCache();
} catch (Exception e) {
loge("Exception in setDnsConfigurationForNetwork: " + e);
}
}
5.DnsManager
DnsManager这边负责dns的后续设置
public void noteDnsServersForNetwork(int netId, @NonNull LinkProperties lp) {
mLinkPropertiesMap.put(netId, lp);
sendDnsConfigurationForNetwork(netId);
}
/**
* Send dns configuration parameters to resolver for a given network.
*/
public void sendDnsConfigurationForNetwork(int netId) {
final LinkProperties lp = mLinkPropertiesMap.get(netId);
final int[] transportTypes = mTransportsMap.get(netId);
if (lp == null || transportTypes == null) return;
updateParametersSettings();
final ResolverParamsParcel paramsParcel = new ResolverParamsParcel();
// We only use the PrivateDnsConfig data pushed to this class instance
// from ConnectivityService because it works in coordination with
// NetworkMonitor to decide which networks need validation and runs the
// blocking calls to resolve Private DNS strict mode hostnames.
//
// At this time we do not attempt to enable Private DNS on non-Internet
// networks like IMS.
final PrivateDnsConfig privateDnsCfg = mPrivateDnsMap.getOrDefault(netId,
PRIVATE_DNS_OFF);
final boolean useTls = privateDnsCfg.useTls;
final boolean strictMode = privateDnsCfg.inStrictMode();
paramsParcel.netId = netId;
paramsParcel.sampleValiditySeconds = mSampleValidity;
paramsParcel.successThreshold = mSuccessThreshold;
paramsParcel.minSamples = mMinSamples;
paramsParcel.maxSamples = mMaxSamples;
paramsParcel.servers =
NetworkUtils.makeStrings(lp.getDnsServers());
paramsParcel.domains = getDomainStrings(lp.getDomains());
paramsParcel.tlsName = strictMode ? privateDnsCfg.hostname : "";
paramsParcel.tlsServers =
strictMode ? NetworkUtils.makeStrings(
Arrays.stream(privateDnsCfg.ips)
.filter((ip) -> lp.isReachable(ip))
.collect(Collectors.toList()))
: useTls ? paramsParcel.servers // Opportunistic
: new String[0]; // Off
paramsParcel.resolverOptions = new ResolverOptionsParcel();
paramsParcel.transportTypes = transportTypes;
// Prepare to track the validation status of the DNS servers in the
// resolver config when private DNS is in opportunistic or strict mode.
if (useTls) {
if (!mPrivateDnsValidationMap.containsKey(netId)) {
mPrivateDnsValidationMap.put(netId, new PrivateDnsValidationStatuses());
}
mPrivateDnsValidationMap.get(netId).updateTrackedDnses(paramsParcel.tlsServers,
paramsParcel.tlsName);
} else {
mPrivateDnsValidationMap.remove(netId);
}
Slog.d(TAG, String.format("sendDnsConfigurationForNetwork(%d, %s, %s, %d, %d, %d, %d, "
+ "%d, %d, %s, %s)", paramsParcel.netId, Arrays.toString(paramsParcel.servers),
Arrays.toString(paramsParcel.domains), paramsParcel.sampleValiditySeconds,
paramsParcel.successThreshold, paramsParcel.minSamples,
paramsParcel.maxSamples, paramsParcel.baseTimeoutMsec,
paramsParcel.retryCount, paramsParcel.tlsName,
Arrays.toString(paramsParcel.tlsServers)));
try {
mDnsResolver.setResolverConfiguration(paramsParcel);
} catch (RemoteException | ServiceSpecificException e) {
Slog.e(TAG, "Error setting DNS configuration: " + e);
return;
}
}
后面看是通过aidl调用到netd那边去了
./netd/server/binder/android/net/INetd.aidl
./netd/server/ResolverController.cpp:494:int ResolverController::setResolverConfiguration(int32_t netId, ?
6.总结
dns设置流程是
NetworkStack->ClientModeImpl->CS->DnsManager->Netd
|