/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdlib.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define LOG_TAG "NatController"
#include <cutils/log.h>
#include "NatController.h"
extern "C" int logwrap(int argc, const char **argv, int background);
static char IPTABLES_PATH[] = "/system/bin/iptables";
NatController::NatController() {
natCount = 0;
}
NatController::~NatController() {
}
int NatController::runIptablesCmd(const char *cmd) {
char buffer[255];
strncpy(buffer, cmd, sizeof(buffer)-1);
const char *args[16];
char *next = buffer;
char *tmp;
args[0] = IPTABLES_PATH;
args[1] = "--verbose";
int i = 2;
while ((tmp = strsep(&next, " "))) {
args[i++] = tmp;
if (i == 16) {
LOGE("iptables argument overflow");
errno = E2BIG;
return -1;
}
}
args[i] = NULL;
return logwrap(i, args, 0);
}
int NatController::setDefaults() {
if (runIptablesCmd("-P INPUT ACCEPT"))
return -1;
if (runIptablesCmd("-F INPUT"))
return -1;
if (runIptablesCmd("-P OUTPUT ACCEPT"))
return -1;
if (runIptablesCmd("-F OUTPUT"))
return -1;
if (runIptablesCmd("-P FORWARD DROP"))
return -1;
if (runIptablesCmd("-F FORWARD"))
return -1;
if (runIptablesCmd("-t nat -F"))
return -1;
return 0;
}
bool NatController::interfaceExists(const char *iface) {
// XXX: STOPSHIP - Implement this
return true;
}
int NatController::doNatCommands(const char *intIface, const char *extIface, bool add) {
char cmd[255];
// handle decrement to 0 case (do reset to defaults) and erroneous dec below 0
if (add == false) {
if (natCount <= 1) {
int ret = setDefaults();
if (ret == 0) {
natCount=0;
}
return ret;
}
}
if (!interfaceExists(intIface) || !interfaceExists (extIface)) {
LOGE("Invalid interface specified");
errno = ENODEV;
return -1;
}
snprintf(cmd, sizeof(cmd),
"-%s FORWARD -i %s -o %s -m state --state ESTABLISHED,RELATED -j ACCEPT",
(add ? "A" : "D"),
extIface, intIface);
if (runIptablesCmd(cmd)) {
return -1;
}
snprintf(cmd, sizeof(cmd), "-%s FORWARD -i %s -o %s -j ACCEPT", (add ? "A" : "D"),
intIface, extIface);
if (runIptablesCmd(cmd)) {
// unwind what's been done, but don't care about success - what more could we do?
snprintf(cmd, sizeof(cmd),
"-%s FORWARD -i %s -o %s -m state --state ESTABLISHED,RELATED -j ACCEPT",
(!add ? "A" : "D"),
extIface, intIface);
return -1;
}
// add this if we are the first added nat
if (add && natCount == 0) {
snprintf(cmd, sizeof(cmd), "-t nat -A POSTROUTING -o %s -j MASQUERADE", extIface);
if (runIptablesCmd(cmd)) {
// unwind what's been done, but don't care about success - what more could we do?
setDefaults();;
return -1;
}
}
if (add) {
natCount++;
} else {
natCount--;
}
return 0;
}
int NatController::enableNat(const char *intIface, const char *extIface) {
return doNatCommands(intIface, extIface, true);
}
int NatController::disableNat(const char *intIface, const char *extIface) {
return doNatCommands(intIface, extIface, false);
}