Nat464Xlat.java revision 9158825f9c41869689d6b1786d7c7aa8bdd524ce
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.NetworkStateTracker;
29import android.net.NetworkUtils;
30import android.net.RouteInfo;
31import android.os.Handler;
32import android.os.Message;
33import android.os.INetworkManagementService;
34import android.os.RemoteException;
35import android.util.Slog;
36
37import com.android.server.net.BaseNetworkObserver;
38
39/**
40 * @hide
41 *
42 * Class to manage a 464xlat CLAT daemon.
43 */
44public class Nat464Xlat extends BaseNetworkObserver {
45    private Context mContext;
46    private INetworkManagementService mNMService;
47    private IConnectivityManager mConnService;
48    private NetworkStateTracker mTracker;
49    private Handler mHandler;
50
51    // Whether we started clatd and expect it to be running.
52    private boolean mIsStarted;
53    // Whether the clatd interface exists (i.e., clatd is running).
54    private boolean mIsRunning;
55    // The LinkProperties of the clat interface.
56    private LinkProperties mLP;
57
58    // This must match the interface name in clatd.conf.
59    private static final String CLAT_INTERFACE_NAME = "clat4";
60
61    private static final String TAG = "Nat464Xlat";
62
63    public Nat464Xlat(Context context, INetworkManagementService nmService,
64                      IConnectivityManager connService, Handler handler) {
65        mContext = context;
66        mNMService = nmService;
67        mConnService = connService;
68        mHandler = handler;
69
70        mIsStarted = false;
71        mIsRunning = false;
72        mLP = new LinkProperties();
73    }
74
75    /**
76     * Determines whether an interface requires clat.
77     * @param netType the network type (one of the
78     *   android.net.ConnectivityManager.TYPE_* constants)
79     * @param tracker the NetworkStateTracker corresponding to the network type.
80     * @return true if the interface requires clat, false otherwise.
81     */
82    public boolean requiresClat(int netType, NetworkStateTracker tracker) {
83        LinkProperties lp = tracker.getLinkProperties();
84        // Only support clat on mobile for now.
85        Slog.d(TAG, "requiresClat: netType=" + netType + ", hasIPv4Address=" +
86               lp.hasIPv4Address());
87        return netType == TYPE_MOBILE && !lp.hasIPv4Address();
88    }
89
90    public static boolean isRunningClat(LinkProperties lp) {
91      return lp != null && lp.getAllInterfaceNames().contains(CLAT_INTERFACE_NAME);
92    }
93
94    /**
95     * Starts the clat daemon.
96     * @param lp The link properties of the interface to start clatd on.
97     */
98    public void startClat(NetworkStateTracker tracker) {
99        if (mIsStarted) {
100            Slog.e(TAG, "startClat: already started");
101            return;
102        }
103        mTracker = tracker;
104        LinkProperties lp = mTracker.getLinkProperties();
105        String iface = lp.getInterfaceName();
106        Slog.i(TAG, "Starting clatd on " + iface + ", lp=" + lp);
107        try {
108            mNMService.startClatd(iface);
109        } catch(RemoteException e) {
110            Slog.e(TAG, "Error starting clat daemon: " + e);
111        }
112        mIsStarted = true;
113    }
114
115    /**
116     * Stops the clat daemon.
117     */
118    public void stopClat() {
119        if (mIsStarted) {
120            Slog.i(TAG, "Stopping clatd");
121            try {
122                mNMService.stopClatd();
123            } catch(RemoteException e) {
124                Slog.e(TAG, "Error stopping clat daemon: " + e);
125            }
126            mIsStarted = false;
127            mIsRunning = false;
128            mTracker = null;
129            mLP.clear();
130        } else {
131            Slog.e(TAG, "stopClat: already stopped");
132        }
133    }
134
135    public boolean isStarted() {
136        return mIsStarted;
137    }
138
139    public boolean isRunning() {
140        return mIsRunning;
141    }
142
143    @Override
144    public void interfaceAdded(String iface) {
145        if (iface.equals(CLAT_INTERFACE_NAME)) {
146            Slog.i(TAG, "interface " + CLAT_INTERFACE_NAME +
147                   " added, mIsRunning = " + mIsRunning + " -> true");
148            mIsRunning = true;
149
150            // Create the LinkProperties for the clat interface by fetching the
151            // IPv4 address for the interface and adding an IPv4 default route,
152            // then stack the LinkProperties on top of the link it's running on.
153            // Although the clat interface is a point-to-point tunnel, we don't
154            // point the route directly at the interface because some apps don't
155            // understand routes without gateways (see, e.g., http://b/9597256
156            // http://b/9597516). Instead, set the next hop of the route to the
157            // clat IPv4 address itself (for those apps, it doesn't matter what
158            // the IP of the gateway is, only that there is one).
159            try {
160                InterfaceConfiguration config = mNMService.getInterfaceConfig(iface);
161                LinkAddress clatAddress = config.getLinkAddress();
162                mLP.clear();
163                mLP.setInterfaceName(iface);
164                RouteInfo ipv4Default = new RouteInfo(new LinkAddress(Inet4Address.ANY, 0),
165                                                      clatAddress.getAddress(), iface);
166                mLP.addRoute(ipv4Default);
167                mLP.addLinkAddress(clatAddress);
168                mTracker.addStackedLink(mLP);
169                Slog.i(TAG, "Adding stacked link. tracker LP: " +
170                       mTracker.getLinkProperties());
171            } catch(RemoteException e) {
172                Slog.e(TAG, "Error getting link properties: " + e);
173            }
174
175            // Inform ConnectivityService that things have changed.
176            Message msg = mHandler.obtainMessage(
177                NetworkStateTracker.EVENT_CONFIGURATION_CHANGED,
178                mTracker.getNetworkInfo());
179            Slog.i(TAG, "sending message to ConnectivityService: " + msg);
180            msg.sendToTarget();
181        }
182    }
183
184    @Override
185    public void interfaceRemoved(String iface) {
186        if (iface == CLAT_INTERFACE_NAME) {
187            if (mIsRunning) {
188                NetworkUtils.resetConnections(
189                    CLAT_INTERFACE_NAME,
190                    NetworkUtils.RESET_IPV4_ADDRESSES);
191            }
192            Slog.i(TAG, "interface " + CLAT_INTERFACE_NAME +
193                   " removed, mIsRunning = " + mIsRunning + " -> false");
194            mIsRunning = false;
195            mTracker.removeStackedLink(mLP);
196            mLP.clear();
197            Slog.i(TAG, "mLP = " + mLP);
198        }
199    }
200};
201