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