Separate NAT from forwarding.

Bug: 19500693
Change-Id: I39878644e21d51def1c31d1857e815f473ef0938
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index 5d5d2b3..154a5a5 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -178,6 +178,18 @@
     String[] getDnsForwarders();
 
     /**
+     * Enables unidirectional packet forwarding from {@code fromIface} to
+     * {@code toIface}.
+     */
+    void startInterfaceForwarding(String fromIface, String toIface);
+
+    /**
+     * Disables unidirectional packet forwarding from {@code fromIface} to
+     * {@code toIface}.
+     */
+    void stopInterfaceForwarding(String fromIface, String toIface);
+
+    /**
      *  Enables Network Address Translation between two interfaces.
      *  The address and netmask of the external interface is used for
      *  the NAT'ed network.
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 6719aa6..8c56c8c 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -1259,6 +1259,27 @@
         return filtered;
     }
 
+    private void modifyInterfaceForward(boolean add, String fromIface, String toIface) {
+        final Command cmd = new Command("ipfwd", add ? "add" : "remove", fromIface, toIface);
+        try {
+            mConnector.execute(cmd);
+        } catch (NativeDaemonConnectorException e) {
+            throw e.rethrowAsParcelableException();
+        }
+    }
+
+    @Override
+    public void startInterfaceForwarding(String fromIface, String toIface) {
+        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+        modifyInterfaceForward(true, fromIface, toIface);
+    }
+
+    @Override
+    public void stopInterfaceForwarding(String fromIface, String toIface) {
+        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+        modifyInterfaceForward(false, fromIface, toIface);
+    }
+
     private void modifyNat(String action, String internalInterface, String externalInterface)
             throws SocketException {
         final Command cmd = new Command("nat", action, internalInterface, externalInterface);
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index 9566f93..5ff7022 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -981,6 +981,12 @@
                         if (VDBG) Log.e(TAG, "Exception in forceUpdate: " + e.toString());
                     }
                     try {
+                        mNMService.stopInterfaceForwarding(mIfaceName, mMyUpstreamIfaceName);
+                    } catch (Exception e) {
+                        if (VDBG) Log.e(
+                                TAG, "Exception in removeInterfaceForward: " + e.toString());
+                    }
+                    try {
                         mNMService.disableNat(mIfaceName, mMyUpstreamIfaceName);
                     } catch (Exception e) {
                         if (VDBG) Log.e(TAG, "Exception in disableNat: " + e.toString());
@@ -1033,9 +1039,14 @@
                         if (newUpstreamIfaceName != null) {
                             try {
                                 mNMService.enableNat(mIfaceName, newUpstreamIfaceName);
+                                mNMService.startInterfaceForwarding(mIfaceName,
+                                        newUpstreamIfaceName);
                             } catch (Exception e) {
                                 Log.e(TAG, "Exception enabling Nat: " + e.toString());
                                 try {
+                                    mNMService.disableNat(mIfaceName, newUpstreamIfaceName);
+                                } catch (Exception ee) {}
+                                try {
                                     mNMService.untetherInterface(mIfaceName);
                                 } catch (Exception ee) {}