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.ConnectivityManager; 20 import android.net.IpPrefix; 21 import android.net.LinkAddress; 22 import android.net.LinkProperties; 23 import android.net.Network; 24 import android.net.NetworkCapabilities; 25 import android.net.NetworkState; 26 import android.net.RouteInfo; 27 import android.util.Log; 28 29 import java.net.Inet6Address; 30 import java.net.InetAddress; 31 import java.util.ArrayList; 32 import java.util.LinkedList; 33 34 35 /** 36 * IPv6 tethering is rather different from IPv4 owing to the absence of NAT. 37 * This coordinator is responsible for evaluating the dedicated prefixes 38 * assigned to the device and deciding how to divvy them up among downstream 39 * interfaces. 40 * 41 * @hide 42 */ 43 public class IPv6TetheringCoordinator { 44 private static final String TAG = IPv6TetheringCoordinator.class.getSimpleName(); 45 private static final boolean DBG = false; 46 private static final boolean VDBG = false; 47 48 private final ArrayList<TetherInterfaceStateMachine> mNotifyList; 49 private final LinkedList<TetherInterfaceStateMachine> mActiveDownstreams; 50 private NetworkState mUpstreamNetworkState; 51 52 public IPv6TetheringCoordinator(ArrayList<TetherInterfaceStateMachine> notifyList) { 53 mNotifyList = notifyList; 54 mActiveDownstreams = new LinkedList<>(); 55 } 56 57 public void addActiveDownstream(TetherInterfaceStateMachine downstream) { 58 if (mActiveDownstreams.indexOf(downstream) == -1) { 59 // Adding a new downstream appends it to the list. Adding a 60 // downstream a second time without first removing it has no effect. 61 mActiveDownstreams.offer(downstream); 62 updateIPv6TetheringInterfaces(); 63 } 64 } 65 66 public void removeActiveDownstream(TetherInterfaceStateMachine downstream) { 67 stopIPv6TetheringOn(downstream); 68 if (mActiveDownstreams.remove(downstream)) { 69 updateIPv6TetheringInterfaces(); 70 } 71 } 72 73 public void updateUpstreamNetworkState(NetworkState ns) { 74 if (VDBG) { 75 Log.d(TAG, "updateUpstreamNetworkState: " + toDebugString(ns)); 76 } 77 if (!canTetherIPv6(ns)) { 78 stopIPv6TetheringOnAllInterfaces(); 79 setUpstreamNetworkState(null); 80 return; 81 } 82 83 if (mUpstreamNetworkState != null && 84 !ns.network.equals(mUpstreamNetworkState.network)) { 85 stopIPv6TetheringOnAllInterfaces(); 86 } 87 88 setUpstreamNetworkState(ns); 89 updateIPv6TetheringInterfaces(); 90 } 91 92 private void stopIPv6TetheringOnAllInterfaces() { 93 for (TetherInterfaceStateMachine sm : mNotifyList) { 94 stopIPv6TetheringOn(sm); 95 } 96 } 97 98 private void setUpstreamNetworkState(NetworkState ns) { 99 if (ns == null) { 100 mUpstreamNetworkState = null; 101 } else { 102 // Make a deep copy of the parts we need. 103 mUpstreamNetworkState = new NetworkState( 104 null, 105 new LinkProperties(ns.linkProperties), 106 new NetworkCapabilities(ns.networkCapabilities), 107 new Network(ns.network), 108 null, 109 null); 110 } 111 112 if (DBG) { 113 Log.d(TAG, "setUpstreamNetworkState: " + toDebugString(mUpstreamNetworkState)); 114 } 115 } 116 117 private void updateIPv6TetheringInterfaces() { 118 for (TetherInterfaceStateMachine sm : mNotifyList) { 119 final LinkProperties lp = getInterfaceIPv6LinkProperties(sm); 120 sm.sendMessage(TetherInterfaceStateMachine.CMD_IPV6_TETHER_UPDATE, 0, 0, lp); 121 break; 122 } 123 } 124 125 private LinkProperties getInterfaceIPv6LinkProperties(TetherInterfaceStateMachine sm) { 126 if (mUpstreamNetworkState == null) return null; 127 128 if (sm.interfaceType() == ConnectivityManager.TETHERING_BLUETOOTH) { 129 // TODO: Figure out IPv6 support on PAN interfaces. 130 return null; 131 } 132 133 // NOTE: Here, in future, we would have policies to decide how to divvy 134 // up the available dedicated prefixes among downstream interfaces. 135 // At this time we have no such mechanism--we only support tethering 136 // IPv6 toward the oldest (first requested) active downstream. 137 138 final TetherInterfaceStateMachine currentActive = mActiveDownstreams.peek(); 139 if (currentActive != null && currentActive == sm) { 140 final LinkProperties lp = getIPv6OnlyLinkProperties( 141 mUpstreamNetworkState.linkProperties); 142 if (lp.hasIPv6DefaultRoute() && lp.hasGlobalIPv6Address()) { 143 return lp; 144 } 145 } 146 147 return null; 148 } 149 150 private static boolean canTetherIPv6(NetworkState ns) { 151 // Broadly speaking: 152 // 153 // [1] does the upstream have an IPv6 default route? 154 // 155 // and 156 // 157 // [2] does the upstream have one or more global IPv6 /64s 158 // dedicated to this device? 159 // 160 // In lieu of Prefix Delegation and other evaluation of whether a 161 // prefix may or may not be dedicated to this device, for now just 162 // check whether the upstream is TRANSPORT_CELLULAR. This works 163 // because "[t]he 3GPP network allocates each default bearer a unique 164 // /64 prefix", per RFC 6459, Section 5.2. 165 166 final boolean canTether = 167 (ns != null) && (ns.network != null) && 168 (ns.linkProperties != null) && (ns.networkCapabilities != null) && 169 // At least one upstream DNS server: 170 ns.linkProperties.isProvisioned() && 171 // Minimal amount of IPv6 provisioning: 172 ns.linkProperties.hasIPv6DefaultRoute() && 173 ns.linkProperties.hasGlobalIPv6Address() && 174 // Temporary approximation of "dedicated prefix": 175 ns.networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR); 176 177 // For now, we do not support separate IPv4 and IPv6 upstreams (e.g. 178 // tethering with 464xlat involved). TODO: Rectify this shortcoming, 179 // likely by calling NetworkManagementService#startInterfaceForwarding() 180 // for all upstream interfaces. 181 RouteInfo v4default = null; 182 RouteInfo v6default = null; 183 if (canTether) { 184 for (RouteInfo r : ns.linkProperties.getAllRoutes()) { 185 if (r.isIPv4Default()) { 186 v4default = r; 187 } else if (r.isIPv6Default()) { 188 v6default = r; 189 } 190 191 if (v4default != null && v6default != null) { 192 break; 193 } 194 } 195 } 196 197 final boolean supportedConfiguration = 198 (v4default != null) && (v6default != null) && 199 (v4default.getInterface() != null) && 200 v4default.getInterface().equals(v6default.getInterface()); 201 202 final boolean outcome = canTether && supportedConfiguration; 203 204 if (VDBG) { 205 if (ns == null) { 206 Log.d(TAG, "No available upstream."); 207 } else { 208 Log.d(TAG, String.format("IPv6 tethering is %s for upstream: %s", 209 (outcome ? "available" : "not available"), toDebugString(ns))); 210 } 211 } 212 213 return outcome; 214 } 215 216 private static LinkProperties getIPv6OnlyLinkProperties(LinkProperties lp) { 217 final LinkProperties v6only = new LinkProperties(); 218 if (lp == null) { 219 return v6only; 220 } 221 222 // NOTE: At this time we don't copy over any information about any 223 // stacked links. No current stacked link configuration has IPv6. 224 225 v6only.setInterfaceName(lp.getInterfaceName()); 226 227 v6only.setMtu(lp.getMtu()); 228 229 for (LinkAddress linkAddr : lp.getLinkAddresses()) { 230 if (linkAddr.isGlobalPreferred() && linkAddr.getPrefixLength() == 64) { 231 v6only.addLinkAddress(linkAddr); 232 } 233 } 234 235 for (RouteInfo routeInfo : lp.getRoutes()) { 236 final IpPrefix destination = routeInfo.getDestination(); 237 if ((destination.getAddress() instanceof Inet6Address) && 238 (destination.getPrefixLength() <= 64)) { 239 v6only.addRoute(routeInfo); 240 } 241 } 242 243 for (InetAddress dnsServer : lp.getDnsServers()) { 244 if (isIPv6GlobalAddress(dnsServer)) { 245 // For now we include ULAs. 246 v6only.addDnsServer(dnsServer); 247 } 248 } 249 250 v6only.setDomains(lp.getDomains()); 251 252 return v6only; 253 } 254 255 // TODO: Delete this and switch to LinkAddress#isGlobalPreferred once we 256 // announce our own IPv6 address as DNS server. 257 private static boolean isIPv6GlobalAddress(InetAddress ip) { 258 return (ip instanceof Inet6Address) && 259 !ip.isAnyLocalAddress() && 260 !ip.isLoopbackAddress() && 261 !ip.isLinkLocalAddress() && 262 !ip.isSiteLocalAddress() && 263 !ip.isMulticastAddress(); 264 } 265 266 private static String toDebugString(NetworkState ns) { 267 if (ns == null) { 268 return "NetworkState{null}"; 269 } 270 return String.format("NetworkState{%s, %s, %s}", 271 ns.network, 272 ns.networkCapabilities, 273 ns.linkProperties); 274 } 275 276 private static void stopIPv6TetheringOn(TetherInterfaceStateMachine sm) { 277 sm.sendMessage(TetherInterfaceStateMachine.CMD_IPV6_TETHER_UPDATE, 0, 0, null); 278 } 279 } 280