1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.connectivity.tethering; 18 19 import android.net.INetd; 20 import android.net.IpPrefix; 21 import android.net.LinkAddress; 22 import android.net.LinkProperties; 23 import android.net.NetworkCapabilities; 24 import android.net.NetworkState; 25 import android.net.RouteInfo; 26 import android.net.ip.RouterAdvertisementDaemon; 27 import android.net.ip.RouterAdvertisementDaemon.RaParams; 28 import android.os.INetworkManagementService; 29 import android.os.ServiceSpecificException; 30 import android.os.RemoteException; 31 import android.util.Log; 32 import android.util.Slog; 33 34 import java.net.Inet6Address; 35 import java.net.InetAddress; 36 import java.net.NetworkInterface; 37 import java.net.SocketException; 38 import java.net.UnknownHostException; 39 import java.util.ArrayList; 40 import java.util.HashSet; 41 import java.util.Objects; 42 43 44 /** 45 * @hide 46 */ 47 class IPv6TetheringInterfaceServices { 48 private static final String TAG = IPv6TetheringInterfaceServices.class.getSimpleName(); 49 private static final IpPrefix LINK_LOCAL_PREFIX = new IpPrefix("fe80::/64"); 50 private static final int RFC7421_IP_PREFIX_LENGTH = 64; 51 52 private final String mIfName; 53 private final INetworkManagementService mNMService; 54 55 private NetworkInterface mNetworkInterface; 56 private byte[] mHwAddr; 57 private LinkProperties mLastIPv6LinkProperties; 58 private RouterAdvertisementDaemon mRaDaemon; 59 private RaParams mLastRaParams; 60 61 IPv6TetheringInterfaceServices(String ifname, INetworkManagementService nms) { 62 mIfName = ifname; 63 mNMService = nms; 64 } 65 66 public boolean start() { 67 try { 68 mNetworkInterface = NetworkInterface.getByName(mIfName); 69 } catch (SocketException e) { 70 Log.e(TAG, "Failed to find NetworkInterface for " + mIfName, e); 71 stop(); 72 return false; 73 } 74 75 try { 76 mHwAddr = mNetworkInterface.getHardwareAddress(); 77 } catch (SocketException e) { 78 Log.e(TAG, "Failed to find hardware address for " + mIfName, e); 79 stop(); 80 return false; 81 } 82 83 final int ifindex = mNetworkInterface.getIndex(); 84 mRaDaemon = new RouterAdvertisementDaemon(mIfName, ifindex, mHwAddr); 85 if (!mRaDaemon.start()) { 86 stop(); 87 return false; 88 } 89 90 return true; 91 } 92 93 public void stop() { 94 mNetworkInterface = null; 95 mHwAddr = null; 96 setRaParams(null); 97 98 if (mRaDaemon != null) { 99 mRaDaemon.stop(); 100 mRaDaemon = null; 101 } 102 } 103 104 // IPv6TetheringCoordinator sends updates with carefully curated IPv6-only 105 // LinkProperties. These have extraneous data filtered out and only the 106 // necessary prefixes included (per its prefix distribution policy). 107 // 108 // TODO: Evaluate using a data structure than is more directly suited to 109 // communicating only the relevant information. 110 public void updateUpstreamIPv6LinkProperties(LinkProperties v6only) { 111 if (mRaDaemon == null) return; 112 113 // Avoid unnecessary work on spurious updates. 114 if (Objects.equals(mLastIPv6LinkProperties, v6only)) { 115 return; 116 } 117 118 RaParams params = null; 119 120 if (v6only != null) { 121 params = new RaParams(); 122 params.mtu = v6only.getMtu(); 123 params.hasDefaultRoute = v6only.hasIPv6DefaultRoute(); 124 125 for (LinkAddress linkAddr : v6only.getLinkAddresses()) { 126 if (linkAddr.getPrefixLength() != RFC7421_IP_PREFIX_LENGTH) continue; 127 128 final IpPrefix prefix = new IpPrefix( 129 linkAddr.getAddress(), linkAddr.getPrefixLength()); 130 params.prefixes.add(prefix); 131 132 final Inet6Address dnsServer = getLocalDnsIpFor(prefix); 133 if (dnsServer != null) { 134 params.dnses.add(dnsServer); 135 } 136 } 137 } 138 // If v6only is null, we pass in null to setRaParams(), which handles 139 // deprecation of any existing RA data. 140 141 setRaParams(params); 142 mLastIPv6LinkProperties = v6only; 143 } 144 145 146 private void configureLocalRoutes( 147 HashSet<IpPrefix> deprecatedPrefixes, HashSet<IpPrefix> newPrefixes) { 148 // [1] Remove the routes that are deprecated. 149 if (!deprecatedPrefixes.isEmpty()) { 150 final ArrayList<RouteInfo> toBeRemoved = getLocalRoutesFor(deprecatedPrefixes); 151 try { 152 final int removalFailures = mNMService.removeRoutesFromLocalNetwork(toBeRemoved); 153 if (removalFailures > 0) { 154 Log.e(TAG, String.format("Failed to remove %d IPv6 routes from local table.", 155 removalFailures)); 156 } 157 } catch (RemoteException e) { 158 Log.e(TAG, "Failed to remove IPv6 routes from local table: ", e); 159 } 160 } 161 162 // [2] Add only the routes that have not previously been added. 163 if (newPrefixes != null && !newPrefixes.isEmpty()) { 164 HashSet<IpPrefix> addedPrefixes = (HashSet) newPrefixes.clone(); 165 if (mLastRaParams != null) { 166 addedPrefixes.removeAll(mLastRaParams.prefixes); 167 } 168 169 if (mLastRaParams == null || mLastRaParams.prefixes.isEmpty()) { 170 // We need to be able to send unicast RAs, and clients might 171 // like to ping the default router's link-local address. Note 172 // that we never remove the link-local route from the network 173 // until Tethering disables tethering on the interface. We 174 // only need to add the link-local prefix once, but in the 175 // event we add it more than once netd silently ignores EEXIST. 176 addedPrefixes.add(LINK_LOCAL_PREFIX); 177 } 178 179 if (!addedPrefixes.isEmpty()) { 180 final ArrayList<RouteInfo> toBeAdded = getLocalRoutesFor(addedPrefixes); 181 try { 182 // It's safe to call addInterfaceToLocalNetwork() even if 183 // the interface is already in the local_network. Note also 184 // that adding routes that already exist does not cause an 185 // error (EEXIST is silently ignored). 186 mNMService.addInterfaceToLocalNetwork(mIfName, toBeAdded); 187 } catch (RemoteException e) { 188 Log.e(TAG, "Failed to add IPv6 routes to local table: ", e); 189 } 190 } 191 } 192 } 193 194 private void configureLocalDns( 195 HashSet<Inet6Address> deprecatedDnses, HashSet<Inet6Address> newDnses) { 196 INetd netd = getNetdServiceOrNull(); 197 if (netd == null) { 198 if (newDnses != null) newDnses.clear(); 199 Log.e(TAG, "No netd service instance available; not setting local IPv6 addresses"); 200 return; 201 } 202 203 // [1] Remove deprecated local DNS IP addresses. 204 if (!deprecatedDnses.isEmpty()) { 205 for (Inet6Address dns : deprecatedDnses) { 206 final String dnsString = dns.getHostAddress(); 207 try { 208 netd.interfaceDelAddress(mIfName, dnsString, RFC7421_IP_PREFIX_LENGTH); 209 } catch (ServiceSpecificException | RemoteException e) { 210 Log.e(TAG, "Failed to remove local dns IP: " + dnsString, e); 211 } 212 } 213 } 214 215 // [2] Add only the local DNS IP addresses that have not previously been added. 216 if (newDnses != null && !newDnses.isEmpty()) { 217 final HashSet<Inet6Address> addedDnses = (HashSet) newDnses.clone(); 218 if (mLastRaParams != null) { 219 addedDnses.removeAll(mLastRaParams.dnses); 220 } 221 222 for (Inet6Address dns : addedDnses) { 223 final String dnsString = dns.getHostAddress(); 224 try { 225 netd.interfaceAddAddress(mIfName, dnsString, RFC7421_IP_PREFIX_LENGTH); 226 } catch (ServiceSpecificException | RemoteException e) { 227 Log.e(TAG, "Failed to add local dns IP: " + dnsString, e); 228 newDnses.remove(dns); 229 } 230 } 231 } 232 233 try { 234 netd.tetherApplyDnsInterfaces(); 235 } catch (ServiceSpecificException | RemoteException e) { 236 Log.e(TAG, "Failed to update local DNS caching server"); 237 if (newDnses != null) newDnses.clear(); 238 } 239 } 240 241 private void setRaParams(RaParams newParams) { 242 if (mRaDaemon != null) { 243 final RaParams deprecatedParams = 244 RaParams.getDeprecatedRaParams(mLastRaParams, newParams); 245 246 configureLocalRoutes(deprecatedParams.prefixes, 247 (newParams != null) ? newParams.prefixes : null); 248 249 configureLocalDns(deprecatedParams.dnses, 250 (newParams != null) ? newParams.dnses : null); 251 252 mRaDaemon.buildNewRa(deprecatedParams, newParams); 253 } 254 255 mLastRaParams = newParams; 256 } 257 258 // Accumulate routes representing "prefixes to be assigned to the local 259 // interface", for subsequent modification of local_network routing. 260 private ArrayList<RouteInfo> getLocalRoutesFor(HashSet<IpPrefix> prefixes) { 261 final ArrayList<RouteInfo> localRoutes = new ArrayList<RouteInfo>(); 262 for (IpPrefix ipp : prefixes) { 263 localRoutes.add(new RouteInfo(ipp, null, mIfName)); 264 } 265 return localRoutes; 266 } 267 268 private INetd getNetdServiceOrNull() { 269 if (mNMService != null) { 270 try { 271 return mNMService.getNetdService(); 272 } catch (RemoteException ignored) { 273 // This blocks until netd can be reached, but it can return 274 // null during a netd crash. 275 } 276 } 277 return null; 278 } 279 280 // Given a prefix like 2001:db8::/64 return 2001:db8::1. 281 private static Inet6Address getLocalDnsIpFor(IpPrefix localPrefix) { 282 final byte[] dnsBytes = localPrefix.getRawAddress(); 283 dnsBytes[dnsBytes.length - 1] = 0x1; 284 try { 285 return Inet6Address.getByAddress(null, dnsBytes, 0); 286 } catch (UnknownHostException e) { 287 Slog.wtf(TAG, "Failed to construct Inet6Address from: " + localPrefix); 288 return null; 289 } 290 } 291 } 292