Nat464Xlat.java revision 954394653dad05838235f48244a4320893e0f0cf
1/*
2 * Copyright (C) 2012 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;
18
19import static android.net.ConnectivityManager.TYPE_MOBILE;
20
21import java.net.Inet4Address;
22
23import android.content.Context;
24import android.net.IConnectivityManager;
25import android.net.InterfaceConfiguration;
26import android.net.LinkAddress;
27import android.net.LinkProperties;
28import android.net.NetworkAgent;
29import android.net.NetworkUtils;
30import android.net.RouteInfo;
31import android.os.Handler;
32import android.os.Message;
33import android.os.Messenger;
34import android.os.INetworkManagementService;
35import android.os.RemoteException;
36import android.util.Slog;
37
38import com.android.server.net.BaseNetworkObserver;
39
40/**
41 * @hide
42 *
43 * Class to manage a 464xlat CLAT daemon.
44 */
45public class Nat464Xlat extends BaseNetworkObserver {
46    private static final String TAG = "Nat464Xlat";
47
48    // This must match the interface prefix in clatd.c.
49    private static final String CLAT_PREFIX = "v4-";
50
51    private final INetworkManagementService mNMService;
52
53    // ConnectivityService Handler for LinkProperties updates.
54    private final Handler mHandler;
55
56    // The network we're running on.
57    private final NetworkAgentInfo mNetwork;
58
59    // Internal state variables.
60    //
61    // The possible states are:
62    //  - Idle: start() not called. Everything is null.
63    //  - Starting: start() called. Interfaces are non-null. isStarted() returns true.
64    //    mIsRunning is false.
65    //  - Running: start() called, and interfaceAdded() told us that mIface is up. Clat IP address
66    //    is non-null. mIsRunning is true.
67    //
68    // Once mIface is non-null and isStarted() is true, methods called by ConnectivityService on
69    // its handler thread must not modify any internal state variables; they are only updated by the
70    // interface observers, called on the notification threads.
71    private String mBaseIface;
72    private String mIface;
73    private boolean mIsRunning;
74
75    public Nat464Xlat(
76            Context context, INetworkManagementService nmService,
77            Handler handler, NetworkAgentInfo nai) {
78        mNMService = nmService;
79        mHandler = handler;
80        mNetwork = nai;
81    }
82
83    /**
84     * Determines whether a network requires clat.
85     * @param network the NetworkAgentInfo corresponding to the network.
86     * @return true if the network requires clat, false otherwise.
87     */
88    public static boolean requiresClat(NetworkAgentInfo nai) {
89        final int netType = nai.networkInfo.getType();
90        final boolean connected = nai.networkInfo.isConnected();
91        final boolean hasIPv4Address =
92                (nai.linkProperties != null) ? nai.linkProperties.hasIPv4Address() : false;
93        // Only support clat on mobile for now.
94        return netType == TYPE_MOBILE && connected && !hasIPv4Address;
95    }
96
97    /**
98     * Determines whether clatd is started. Always true, except a) if start has not yet been called,
99     * or b) if our interface was removed.
100     */
101    public boolean isStarted() {
102        return mIface != null;
103    }
104
105    /**
106     * Clears internal state. Must not be called by ConnectivityService.
107     */
108    private void clear() {
109        mIface = null;
110        mBaseIface = null;
111        mIsRunning = false;
112    }
113
114    /**
115     * Starts the clat daemon. Called by ConnectivityService on the handler thread.
116     */
117    public void start() {
118        if (isStarted()) {
119            Slog.e(TAG, "startClat: already started");
120            return;
121        }
122
123        if (mNetwork.linkProperties == null) {
124            Slog.e(TAG, "startClat: Can't start clat with null LinkProperties");
125            return;
126        }
127
128        try {
129            mNMService.registerObserver(this);
130        } catch(RemoteException e) {
131            Slog.e(TAG, "startClat: Can't register interface observer for clat on " + mNetwork);
132            return;
133        }
134
135        mBaseIface = mNetwork.linkProperties.getInterfaceName();
136        if (mBaseIface == null) {
137            Slog.e(TAG, "startClat: Can't start clat on null interface");
138            return;
139        }
140        mIface = CLAT_PREFIX + mBaseIface;
141        // From now on, isStarted() will return true.
142
143        Slog.i(TAG, "Starting clatd on " + mBaseIface);
144        try {
145            mNMService.startClatd(mBaseIface);
146        } catch(RemoteException|IllegalStateException e) {
147            Slog.e(TAG, "Error starting clatd: " + e);
148        }
149    }
150
151    /**
152     * Stops the clat daemon. Called by ConnectivityService on the handler thread.
153     */
154    public void stop() {
155        if (isStarted()) {
156            Slog.i(TAG, "Stopping clatd");
157            try {
158                mNMService.stopClatd(mBaseIface);
159            } catch(RemoteException|IllegalStateException e) {
160                Slog.e(TAG, "Error stopping clatd: " + e);
161            }
162            // When clatd stops and its interface is deleted, interfaceRemoved() will notify
163            // ConnectivityService and call clear().
164        } else {
165            Slog.e(TAG, "clatd: already stopped");
166        }
167    }
168
169    private void updateConnectivityService(LinkProperties lp) {
170        Message msg = mHandler.obtainMessage(NetworkAgent.EVENT_NETWORK_PROPERTIES_CHANGED, lp);
171        msg.replyTo = mNetwork.messenger;
172        Slog.i(TAG, "sending message to ConnectivityService: " + msg);
173        msg.sendToTarget();
174    }
175
176    /**
177     * Copies the stacked clat link in oldLp, if any, to the LinkProperties in mNetwork.
178     * This is necessary because the LinkProperties in mNetwork come from the transport layer, which
179     * has no idea that 464xlat is running on top of it.
180     */
181    public void fixupLinkProperties(LinkProperties oldLp) {
182        if (mNetwork.clatd != null &&
183                mIsRunning &&
184                mNetwork.linkProperties != null &&
185                !mNetwork.linkProperties.getAllInterfaceNames().contains(mIface)) {
186            Slog.d(TAG, "clatd running, updating NAI for " + mIface);
187            for (LinkProperties stacked: oldLp.getStackedLinks()) {
188                if (mIface.equals(stacked.getInterfaceName())) {
189                    mNetwork.linkProperties.addStackedLink(stacked);
190                    break;
191                }
192            }
193        }
194    }
195
196    private LinkProperties makeLinkProperties(LinkAddress clatAddress) {
197        LinkProperties stacked = new LinkProperties();
198        stacked.setInterfaceName(mIface);
199
200        // Although the clat interface is a point-to-point tunnel, we don't
201        // point the route directly at the interface because some apps don't
202        // understand routes without gateways (see, e.g., http://b/9597256
203        // http://b/9597516). Instead, set the next hop of the route to the
204        // clat IPv4 address itself (for those apps, it doesn't matter what
205        // the IP of the gateway is, only that there is one).
206        RouteInfo ipv4Default = new RouteInfo(
207                new LinkAddress(Inet4Address.ANY, 0),
208                clatAddress.getAddress(), mIface);
209        stacked.addRoute(ipv4Default);
210        stacked.addLinkAddress(clatAddress);
211        return stacked;
212    }
213
214    @Override
215    public void interfaceAdded(String iface) {
216        // Called by the InterfaceObserver on its own thread, so can race with stop().
217        if (isStarted() && mIface.equals(iface)) {
218            Slog.i(TAG, "interface " + iface + " added, mIsRunning " + mIsRunning + "->true");
219
220            LinkAddress clatAddress;
221            try {
222                InterfaceConfiguration config = mNMService.getInterfaceConfig(iface);
223                clatAddress = config.getLinkAddress();
224            } catch(RemoteException e) {
225                Slog.e(TAG, "Error getting link properties: " + e);
226                return;
227            }
228
229            if (!mIsRunning) {
230                mIsRunning = true;
231                LinkProperties lp = new LinkProperties(mNetwork.linkProperties);
232                lp.addStackedLink(makeLinkProperties(clatAddress));
233                Slog.i(TAG, "Adding stacked link " + mIface + " on top of " + mBaseIface);
234                updateConnectivityService(lp);
235            }
236        }
237    }
238
239    @Override
240    public void interfaceRemoved(String iface) {
241        if (isStarted() && mIface.equals(iface)) {
242            Slog.i(TAG, "interface " + iface + " removed, mIsRunning " + mIsRunning + "->false");
243
244            if (mIsRunning) {
245                // The interface going away likely means clatd has crashed. Ask netd to stop it,
246                // because otherwise when we try to start it again on the same base interface netd
247                // will complain that it's already started.
248                //
249                // Note that this method can be called by the interface observer at the same time
250                // that ConnectivityService calls stop(). In this case, the second call to
251                // stopClatd() will just throw IllegalStateException, which we'll ignore.
252                try {
253                    mNMService.unregisterObserver(this);
254                    mNMService.stopClatd(mBaseIface);
255                } catch (RemoteException|IllegalStateException e) {
256                    // Well, we tried.
257                }
258                LinkProperties lp = new LinkProperties(mNetwork.linkProperties);
259                lp.removeStackedLink(mIface);
260                clear();
261                updateConnectivityService(lp);
262            }
263        }
264    }
265}
266