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 | 1eb8c69 | 2016-07-08 17:21:26 +0900 | [diff] [blame] | 19 | import android.net.IpPrefix; |
| 20 | import android.net.LinkAddress; |
| 21 | import android.net.LinkProperties; |
| 22 | import android.net.Network; |
| 23 | import android.net.NetworkCapabilities; |
| 24 | import android.net.NetworkState; |
| 25 | import android.net.RouteInfo; |
paulhu | d9736de | 2019-03-08 16:35:20 +0800 | [diff] [blame] | 26 | import android.net.ip.IpServer; |
Erik Kline | ea9cc48 | 2017-03-10 19:35:34 +0900 | [diff] [blame] | 27 | import android.net.util.NetworkConstants; |
Erik Kline | 7747fd4 | 2017-05-12 16:52:48 +0900 | [diff] [blame] | 28 | import android.net.util.SharedLog; |
Erik Kline | 1eb8c69 | 2016-07-08 17:21:26 +0900 | [diff] [blame] | 29 | import android.util.Log; |
| 30 | |
| 31 | import java.net.Inet6Address; |
| 32 | import java.net.InetAddress; |
| 33 | import java.util.ArrayList; |
Erik Kline | ea9cc48 | 2017-03-10 19:35:34 +0900 | [diff] [blame] | 34 | import java.util.Arrays; |
Erik Kline | 6e29bf0 | 2016-08-15 16:16:18 +0900 | [diff] [blame] | 35 | import java.util.LinkedList; |
Erik Kline | ea9cc48 | 2017-03-10 19:35:34 +0900 | [diff] [blame] | 36 | import java.util.Random; |
Erik Kline | 1eb8c69 | 2016-07-08 17:21:26 +0900 | [diff] [blame] | 37 | |
| 38 | |
| 39 | /** |
| 40 | * IPv6 tethering is rather different from IPv4 owing to the absence of NAT. |
| 41 | * This coordinator is responsible for evaluating the dedicated prefixes |
| 42 | * assigned to the device and deciding how to divvy them up among downstream |
| 43 | * interfaces. |
| 44 | * |
| 45 | * @hide |
| 46 | */ |
| 47 | public class IPv6TetheringCoordinator { |
| 48 | private static final String TAG = IPv6TetheringCoordinator.class.getSimpleName(); |
| 49 | private static final boolean DBG = false; |
| 50 | private static final boolean VDBG = false; |
| 51 | |
Erik Kline | ea9cc48 | 2017-03-10 19:35:34 +0900 | [diff] [blame] | 52 | private static class Downstream { |
Erik Kline | 7a4ccc6 | 2018-08-27 17:26:47 +0900 | [diff] [blame] | 53 | public final IpServer ipServer; |
| 54 | public final int mode; // IpServer.STATE_* |
Erik Kline | ea9cc48 | 2017-03-10 19:35:34 +0900 | [diff] [blame] | 55 | // Used to append to a ULA /48, constructing a ULA /64 for local use. |
| 56 | public final short subnetId; |
| 57 | |
Erik Kline | 7a4ccc6 | 2018-08-27 17:26:47 +0900 | [diff] [blame] | 58 | Downstream(IpServer ipServer, int mode, short subnetId) { |
| 59 | this.ipServer = ipServer; |
Erik Kline | ea9cc48 | 2017-03-10 19:35:34 +0900 | [diff] [blame] | 60 | this.mode = mode; |
| 61 | this.subnetId = subnetId; |
| 62 | } |
| 63 | } |
| 64 | |
Erik Kline | 7a4ccc6 | 2018-08-27 17:26:47 +0900 | [diff] [blame] | 65 | private final ArrayList<IpServer> mNotifyList; |
Erik Kline | 7747fd4 | 2017-05-12 16:52:48 +0900 | [diff] [blame] | 66 | private final SharedLog mLog; |
Erik Kline | ea9cc48 | 2017-03-10 19:35:34 +0900 | [diff] [blame] | 67 | // NOTE: mActiveDownstreams is a list and not a hash data structure because |
| 68 | // we keep active downstreams in arrival order. This is done so /64s can |
| 69 | // be parceled out on a "first come, first served" basis and a /64 used by |
| 70 | // a downstream that is no longer active can be redistributed to any next |
| 71 | // waiting active downstream (again, in arrival order). |
| 72 | private final LinkedList<Downstream> mActiveDownstreams; |
| 73 | private final byte[] mUniqueLocalPrefix; |
| 74 | private short mNextSubnetId; |
Erik Kline | 1eb8c69 | 2016-07-08 17:21:26 +0900 | [diff] [blame] | 75 | private NetworkState mUpstreamNetworkState; |
| 76 | |
Erik Kline | 7a4ccc6 | 2018-08-27 17:26:47 +0900 | [diff] [blame] | 77 | public IPv6TetheringCoordinator(ArrayList<IpServer> notifyList, SharedLog log) { |
Erik Kline | 1eb8c69 | 2016-07-08 17:21:26 +0900 | [diff] [blame] | 78 | mNotifyList = notifyList; |
Erik Kline | 7747fd4 | 2017-05-12 16:52:48 +0900 | [diff] [blame] | 79 | mLog = log.forSubComponent(TAG); |
Erik Kline | 6e29bf0 | 2016-08-15 16:16:18 +0900 | [diff] [blame] | 80 | mActiveDownstreams = new LinkedList<>(); |
Erik Kline | ea9cc48 | 2017-03-10 19:35:34 +0900 | [diff] [blame] | 81 | mUniqueLocalPrefix = generateUniqueLocalPrefix(); |
| 82 | mNextSubnetId = 0; |
Erik Kline | 6e29bf0 | 2016-08-15 16:16:18 +0900 | [diff] [blame] | 83 | } |
| 84 | |
Erik Kline | 7a4ccc6 | 2018-08-27 17:26:47 +0900 | [diff] [blame] | 85 | public void addActiveDownstream(IpServer downstream, int mode) { |
Erik Kline | ea9cc48 | 2017-03-10 19:35:34 +0900 | [diff] [blame] | 86 | if (findDownstream(downstream) == null) { |
Erik Kline | 6e29bf0 | 2016-08-15 16:16:18 +0900 | [diff] [blame] | 87 | // Adding a new downstream appends it to the list. Adding a |
| 88 | // downstream a second time without first removing it has no effect. |
Erik Kline | ea9cc48 | 2017-03-10 19:35:34 +0900 | [diff] [blame] | 89 | // We never change the mode of a downstream except by first removing |
| 90 | // it and then re-adding it (with its new mode specified); |
| 91 | if (mActiveDownstreams.offer(new Downstream(downstream, mode, mNextSubnetId))) { |
| 92 | // Make sure subnet IDs are always positive. They are appended |
| 93 | // to a ULA /48 to make a ULA /64 for local use. |
| 94 | mNextSubnetId = (short) Math.max(0, mNextSubnetId + 1); |
| 95 | } |
Erik Kline | 6e29bf0 | 2016-08-15 16:16:18 +0900 | [diff] [blame] | 96 | updateIPv6TetheringInterfaces(); |
| 97 | } |
| 98 | } |
| 99 | |
Erik Kline | 7a4ccc6 | 2018-08-27 17:26:47 +0900 | [diff] [blame] | 100 | public void removeActiveDownstream(IpServer downstream) { |
Erik Kline | 6e29bf0 | 2016-08-15 16:16:18 +0900 | [diff] [blame] | 101 | stopIPv6TetheringOn(downstream); |
Erik Kline | ea9cc48 | 2017-03-10 19:35:34 +0900 | [diff] [blame] | 102 | if (mActiveDownstreams.remove(findDownstream(downstream))) { |
Erik Kline | 6e29bf0 | 2016-08-15 16:16:18 +0900 | [diff] [blame] | 103 | updateIPv6TetheringInterfaces(); |
| 104 | } |
Erik Kline | ea9cc48 | 2017-03-10 19:35:34 +0900 | [diff] [blame] | 105 | |
| 106 | // When tethering is stopping we can reset the subnet counter. |
| 107 | if (mNotifyList.isEmpty()) { |
| 108 | if (!mActiveDownstreams.isEmpty()) { |
| 109 | Log.wtf(TAG, "Tethering notify list empty, IPv6 downstreams non-empty."); |
| 110 | } |
| 111 | mNextSubnetId = 0; |
| 112 | } |
Erik Kline | 1eb8c69 | 2016-07-08 17:21:26 +0900 | [diff] [blame] | 113 | } |
| 114 | |
| 115 | public void updateUpstreamNetworkState(NetworkState ns) { |
| 116 | if (VDBG) { |
| 117 | Log.d(TAG, "updateUpstreamNetworkState: " + toDebugString(ns)); |
| 118 | } |
Remi NGUYEN VAN | 25a7e4f | 2018-03-09 14:07:18 +0900 | [diff] [blame] | 119 | if (TetheringInterfaceUtils.getIPv6Interface(ns) == null) { |
Erik Kline | 1eb8c69 | 2016-07-08 17:21:26 +0900 | [diff] [blame] | 120 | stopIPv6TetheringOnAllInterfaces(); |
| 121 | setUpstreamNetworkState(null); |
| 122 | return; |
| 123 | } |
| 124 | |
| 125 | if (mUpstreamNetworkState != null && |
| 126 | !ns.network.equals(mUpstreamNetworkState.network)) { |
| 127 | stopIPv6TetheringOnAllInterfaces(); |
| 128 | } |
Erik Kline | fa37b2f | 2016-08-02 18:27:03 +0900 | [diff] [blame] | 129 | |
Erik Kline | 1eb8c69 | 2016-07-08 17:21:26 +0900 | [diff] [blame] | 130 | setUpstreamNetworkState(ns); |
Erik Kline | fa37b2f | 2016-08-02 18:27:03 +0900 | [diff] [blame] | 131 | updateIPv6TetheringInterfaces(); |
Erik Kline | 1eb8c69 | 2016-07-08 17:21:26 +0900 | [diff] [blame] | 132 | } |
| 133 | |
| 134 | private void stopIPv6TetheringOnAllInterfaces() { |
Erik Kline | 7a4ccc6 | 2018-08-27 17:26:47 +0900 | [diff] [blame] | 135 | for (IpServer ipServer : mNotifyList) { |
| 136 | stopIPv6TetheringOn(ipServer); |
Erik Kline | 1eb8c69 | 2016-07-08 17:21:26 +0900 | [diff] [blame] | 137 | } |
| 138 | } |
| 139 | |
| 140 | private void setUpstreamNetworkState(NetworkState ns) { |
Erik Kline | fa37b2f | 2016-08-02 18:27:03 +0900 | [diff] [blame] | 141 | if (ns == null) { |
Erik Kline | 1eb8c69 | 2016-07-08 17:21:26 +0900 | [diff] [blame] | 142 | mUpstreamNetworkState = null; |
| 143 | } else { |
Erik Kline | fa37b2f | 2016-08-02 18:27:03 +0900 | [diff] [blame] | 144 | // Make a deep copy of the parts we need. |
Erik Kline | 1eb8c69 | 2016-07-08 17:21:26 +0900 | [diff] [blame] | 145 | mUpstreamNetworkState = new NetworkState( |
| 146 | null, |
| 147 | new LinkProperties(ns.linkProperties), |
| 148 | new NetworkCapabilities(ns.networkCapabilities), |
| 149 | new Network(ns.network), |
| 150 | null, |
| 151 | null); |
| 152 | } |
| 153 | |
Erik Kline | 7747fd4 | 2017-05-12 16:52:48 +0900 | [diff] [blame] | 154 | mLog.log("setUpstreamNetworkState: " + toDebugString(mUpstreamNetworkState)); |
Erik Kline | 1eb8c69 | 2016-07-08 17:21:26 +0900 | [diff] [blame] | 155 | } |
| 156 | |
Erik Kline | fa37b2f | 2016-08-02 18:27:03 +0900 | [diff] [blame] | 157 | private void updateIPv6TetheringInterfaces() { |
Erik Kline | 7a4ccc6 | 2018-08-27 17:26:47 +0900 | [diff] [blame] | 158 | for (IpServer ipServer : mNotifyList) { |
| 159 | final LinkProperties lp = getInterfaceIPv6LinkProperties(ipServer); |
| 160 | ipServer.sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0, lp); |
Erik Kline | 1eb8c69 | 2016-07-08 17:21:26 +0900 | [diff] [blame] | 161 | break; |
| 162 | } |
| 163 | } |
| 164 | |
Erik Kline | 7a4ccc6 | 2018-08-27 17:26:47 +0900 | [diff] [blame] | 165 | private LinkProperties getInterfaceIPv6LinkProperties(IpServer ipServer) { |
Erik Kline | 7a4ccc6 | 2018-08-27 17:26:47 +0900 | [diff] [blame] | 166 | final Downstream ds = findDownstream(ipServer); |
Erik Kline | ea9cc48 | 2017-03-10 19:35:34 +0900 | [diff] [blame] | 167 | if (ds == null) return null; |
| 168 | |
Erik Kline | 7a4ccc6 | 2018-08-27 17:26:47 +0900 | [diff] [blame] | 169 | if (ds.mode == IpServer.STATE_LOCAL_ONLY) { |
Erik Kline | ea9cc48 | 2017-03-10 19:35:34 +0900 | [diff] [blame] | 170 | // Build a Unique Locally-assigned Prefix configuration. |
| 171 | return getUniqueLocalConfig(mUniqueLocalPrefix, ds.subnetId); |
| 172 | } |
| 173 | |
Erik Kline | 7a4ccc6 | 2018-08-27 17:26:47 +0900 | [diff] [blame] | 174 | // This downstream is in IpServer.STATE_TETHERED mode. |
Erik Kline | ea9cc48 | 2017-03-10 19:35:34 +0900 | [diff] [blame] | 175 | if (mUpstreamNetworkState == null || mUpstreamNetworkState.linkProperties == null) { |
| 176 | return null; |
| 177 | } |
| 178 | |
Erik Kline | 1eb8c69 | 2016-07-08 17:21:26 +0900 | [diff] [blame] | 179 | // NOTE: Here, in future, we would have policies to decide how to divvy |
| 180 | // up the available dedicated prefixes among downstream interfaces. |
| 181 | // At this time we have no such mechanism--we only support tethering |
Erik Kline | 6e29bf0 | 2016-08-15 16:16:18 +0900 | [diff] [blame] | 182 | // IPv6 toward the oldest (first requested) active downstream. |
Erik Kline | 1eb8c69 | 2016-07-08 17:21:26 +0900 | [diff] [blame] | 183 | |
Erik Kline | ea9cc48 | 2017-03-10 19:35:34 +0900 | [diff] [blame] | 184 | final Downstream currentActive = mActiveDownstreams.peek(); |
Erik Kline | 7a4ccc6 | 2018-08-27 17:26:47 +0900 | [diff] [blame] | 185 | if (currentActive != null && currentActive.ipServer == ipServer) { |
Erik Kline | 6e29bf0 | 2016-08-15 16:16:18 +0900 | [diff] [blame] | 186 | final LinkProperties lp = getIPv6OnlyLinkProperties( |
| 187 | mUpstreamNetworkState.linkProperties); |
paulhu | d9736de | 2019-03-08 16:35:20 +0800 | [diff] [blame] | 188 | if (lp.hasIpv6DefaultRoute() && lp.hasGlobalIpv6Address()) { |
Erik Kline | 6e29bf0 | 2016-08-15 16:16:18 +0900 | [diff] [blame] | 189 | return lp; |
| 190 | } |
Erik Kline | 1eb8c69 | 2016-07-08 17:21:26 +0900 | [diff] [blame] | 191 | } |
| 192 | |
| 193 | return null; |
| 194 | } |
| 195 | |
Erik Kline | 7a4ccc6 | 2018-08-27 17:26:47 +0900 | [diff] [blame] | 196 | Downstream findDownstream(IpServer ipServer) { |
Erik Kline | ea9cc48 | 2017-03-10 19:35:34 +0900 | [diff] [blame] | 197 | for (Downstream ds : mActiveDownstreams) { |
Erik Kline | 7a4ccc6 | 2018-08-27 17:26:47 +0900 | [diff] [blame] | 198 | if (ds.ipServer == ipServer) return ds; |
Erik Kline | ea9cc48 | 2017-03-10 19:35:34 +0900 | [diff] [blame] | 199 | } |
| 200 | return null; |
| 201 | } |
| 202 | |
Erik Kline | 1eb8c69 | 2016-07-08 17:21:26 +0900 | [diff] [blame] | 203 | private static LinkProperties getIPv6OnlyLinkProperties(LinkProperties lp) { |
| 204 | final LinkProperties v6only = new LinkProperties(); |
| 205 | if (lp == null) { |
| 206 | return v6only; |
| 207 | } |
| 208 | |
| 209 | // NOTE: At this time we don't copy over any information about any |
| 210 | // stacked links. No current stacked link configuration has IPv6. |
| 211 | |
| 212 | v6only.setInterfaceName(lp.getInterfaceName()); |
| 213 | |
| 214 | v6only.setMtu(lp.getMtu()); |
| 215 | |
| 216 | for (LinkAddress linkAddr : lp.getLinkAddresses()) { |
| 217 | if (linkAddr.isGlobalPreferred() && linkAddr.getPrefixLength() == 64) { |
| 218 | v6only.addLinkAddress(linkAddr); |
| 219 | } |
| 220 | } |
| 221 | |
| 222 | for (RouteInfo routeInfo : lp.getRoutes()) { |
| 223 | final IpPrefix destination = routeInfo.getDestination(); |
| 224 | if ((destination.getAddress() instanceof Inet6Address) && |
| 225 | (destination.getPrefixLength() <= 64)) { |
| 226 | v6only.addRoute(routeInfo); |
| 227 | } |
| 228 | } |
| 229 | |
| 230 | for (InetAddress dnsServer : lp.getDnsServers()) { |
| 231 | if (isIPv6GlobalAddress(dnsServer)) { |
| 232 | // For now we include ULAs. |
| 233 | v6only.addDnsServer(dnsServer); |
| 234 | } |
| 235 | } |
| 236 | |
| 237 | v6only.setDomains(lp.getDomains()); |
| 238 | |
| 239 | return v6only; |
| 240 | } |
| 241 | |
| 242 | // TODO: Delete this and switch to LinkAddress#isGlobalPreferred once we |
| 243 | // announce our own IPv6 address as DNS server. |
| 244 | private static boolean isIPv6GlobalAddress(InetAddress ip) { |
| 245 | return (ip instanceof Inet6Address) && |
| 246 | !ip.isAnyLocalAddress() && |
| 247 | !ip.isLoopbackAddress() && |
| 248 | !ip.isLinkLocalAddress() && |
| 249 | !ip.isSiteLocalAddress() && |
| 250 | !ip.isMulticastAddress(); |
| 251 | } |
| 252 | |
Erik Kline | ea9cc48 | 2017-03-10 19:35:34 +0900 | [diff] [blame] | 253 | private static LinkProperties getUniqueLocalConfig(byte[] ulp, short subnetId) { |
| 254 | final LinkProperties lp = new LinkProperties(); |
| 255 | |
| 256 | final IpPrefix local48 = makeUniqueLocalPrefix(ulp, (short) 0, 48); |
| 257 | lp.addRoute(new RouteInfo(local48, null, null)); |
| 258 | |
| 259 | final IpPrefix local64 = makeUniqueLocalPrefix(ulp, subnetId, 64); |
| 260 | // Because this is a locally-generated ULA, we don't have an upstream |
| 261 | // address. But because the downstream IP address management code gets |
| 262 | // its prefix from the upstream's IP address, we create a fake one here. |
| 263 | lp.addLinkAddress(new LinkAddress(local64.getAddress(), 64)); |
| 264 | |
| 265 | lp.setMtu(NetworkConstants.ETHER_MTU); |
| 266 | return lp; |
| 267 | } |
| 268 | |
| 269 | private static IpPrefix makeUniqueLocalPrefix(byte[] in6addr, short subnetId, int prefixlen) { |
| 270 | final byte[] bytes = Arrays.copyOf(in6addr, in6addr.length); |
| 271 | bytes[7] = (byte) (subnetId >> 8); |
| 272 | bytes[8] = (byte) subnetId; |
| 273 | return new IpPrefix(bytes, prefixlen); |
| 274 | } |
| 275 | |
| 276 | // Generates a Unique Locally-assigned Prefix: |
| 277 | // |
| 278 | // https://tools.ietf.org/html/rfc4193#section-3.1 |
| 279 | // |
| 280 | // The result is a /48 that can be used for local-only communications. |
| 281 | private static byte[] generateUniqueLocalPrefix() { |
| 282 | final byte[] ulp = new byte[6]; // 6 = 48bits / 8bits/byte |
| 283 | (new Random()).nextBytes(ulp); |
| 284 | |
| 285 | final byte[] in6addr = Arrays.copyOf(ulp, NetworkConstants.IPV6_ADDR_LEN); |
| 286 | in6addr[0] = (byte) 0xfd; // fc00::/7 and L=1 |
| 287 | |
| 288 | return in6addr; |
| 289 | } |
| 290 | |
Erik Kline | 1eb8c69 | 2016-07-08 17:21:26 +0900 | [diff] [blame] | 291 | private static String toDebugString(NetworkState ns) { |
| 292 | if (ns == null) { |
| 293 | return "NetworkState{null}"; |
| 294 | } |
| 295 | return String.format("NetworkState{%s, %s, %s}", |
| 296 | ns.network, |
| 297 | ns.networkCapabilities, |
| 298 | ns.linkProperties); |
| 299 | } |
Erik Kline | 6e29bf0 | 2016-08-15 16:16:18 +0900 | [diff] [blame] | 300 | |
Erik Kline | 7a4ccc6 | 2018-08-27 17:26:47 +0900 | [diff] [blame] | 301 | private static void stopIPv6TetheringOn(IpServer ipServer) { |
| 302 | ipServer.sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0, null); |
Erik Kline | 6e29bf0 | 2016-08-15 16:16:18 +0900 | [diff] [blame] | 303 | } |
Erik Kline | 1eb8c69 | 2016-07-08 17:21:26 +0900 | [diff] [blame] | 304 | } |