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