NatController.cpp revision ddb9f6eb8d8c35f46c1e3da68f375b85903e85c9
1/* 2 * Copyright (C) 2008 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 17#include <stdlib.h> 18#include <sys/wait.h> 19#include <errno.h> 20#include <sys/socket.h> 21#include <sys/stat.h> 22#include <fcntl.h> 23#include <netinet/in.h> 24#include <arpa/inet.h> 25 26#define LOG_TAG "NatController" 27#include <cutils/log.h> 28 29#include "NatController.h" 30 31extern "C" int logwrap(int argc, const char **argv, int background); 32 33static char IPTABLES_PATH[] = "/system/bin/iptables"; 34static char OEM_SCRIPT_PATH[] = "/system/bin/oem-iptables-init.sh"; 35 36NatController::NatController() : mOemChainsExist(false) { 37 natCount = 0; 38 39 setDefaults(); 40 41 if (0 == access(OEM_SCRIPT_PATH, R_OK | X_OK)) { 42 // The call to oemCleanupHooks() is superfluous when done on bootup, 43 // but is needed for the case where netd has crashed/stopped and is 44 // restarted. 45 if (!oemCleanupHooks() && !oemSetupHooks() && !oemInitChains()) { 46 mOemChainsExist = true; 47 } 48 } 49} 50 51NatController::~NatController() { 52} 53 54int NatController::runIptablesCmd(const char *cmd) { 55 char buffer[255]; 56 57 strncpy(buffer, cmd, sizeof(buffer)-1); 58 59 const char *args[16]; 60 char *next = buffer; 61 char *tmp; 62 63 args[0] = IPTABLES_PATH; 64 args[1] = "--verbose"; 65 int i = 2; 66 67 while ((tmp = strsep(&next, " "))) { 68 args[i++] = tmp; 69 if (i == 16) { 70 LOGE("iptables argument overflow"); 71 errno = E2BIG; 72 return -1; 73 } 74 } 75 args[i] = NULL; 76 77 return logwrap(i, args, 0); 78} 79 80int NatController::setDefaults() { 81 82 if (runIptablesCmd("-P INPUT ACCEPT")) 83 return -1; 84 if (runIptablesCmd("-P OUTPUT ACCEPT")) 85 return -1; 86 if (runIptablesCmd("-P FORWARD DROP")) 87 return -1; 88 if (runIptablesCmd("-F FORWARD")) 89 return -1; 90 91 if (runIptablesCmd("-t nat -F PREROUTING")) 92 return -1; 93 if (runIptablesCmd("-t nat -F OUTPUT")) 94 return -1; 95 if (runIptablesCmd("-t nat -F POSTROUTING")) 96 return -1; 97 98 return 0; 99} 100 101int NatController::oemSetupHooks() { 102 // Order is important! 103 // -N to create the chain (no-op if already exist). 104 // -D to delete any pre-existing jump rule, to prevent dupes (no-op if doesn't exist) 105 // -I to insert our jump rule into the default chain 106 107 runIptablesCmd("-N oem_out"); 108 runIptablesCmd("-D OUTPUT -j oem_out"); 109 if (runIptablesCmd("-I OUTPUT -j oem_out")) 110 return -1; 111 112 runIptablesCmd("-N oem_fwd"); 113 runIptablesCmd("-D FORWARD -j oem_fwd"); 114 if (runIptablesCmd("-I FORWARD -j oem_fwd")) 115 return -1; 116 117 runIptablesCmd("-t nat -N oem_nat_pre"); 118 runIptablesCmd("-t nat -D PREROUTING -j oem_nat_pre"); 119 if (runIptablesCmd("-t nat -I PREROUTING -j oem_nat_pre")) 120 return -1; 121 122 return 0; 123} 124 125int NatController::oemCleanupHooks() { 126 // Order is important! 127 // -D to remove ref to the chain 128 // -F to empty the chain 129 // -X to delete the chain 130 131 runIptablesCmd("-D OUTPUT -j oem_out"); 132 runIptablesCmd("-F oem_out"); 133 runIptablesCmd("-X oem_out"); 134 135 runIptablesCmd("-D FORWARD -j oem_fwd"); 136 runIptablesCmd("-F oem_fwd"); 137 runIptablesCmd("-X oem_fwd"); 138 139 runIptablesCmd("-t nat -D PREROUTING -j oem_nat_pre"); 140 runIptablesCmd("-t nat -F oem_nat_pre"); 141 runIptablesCmd("-t nat -X oem_nat_pre"); 142 143 return 0; 144} 145 146// This method should only be called when netd starts up. The OEM chains are 147// intended to be static, so there's no need to flush and recreate them every 148// time setDefaults() is called. 149int NatController::oemInitChains() { 150 int ret = system(OEM_SCRIPT_PATH); 151 if ((-1 == ret) || (0 != WEXITSTATUS(ret))) { 152 LOGE("%s failed: %s", OEM_SCRIPT_PATH, strerror(errno)); 153 return -1; 154 } 155 return 0; 156} 157 158bool NatController::interfaceExists(const char *iface) { 159 // XXX: Implement this 160 return true; 161} 162 163int NatController::doNatCommands(const char *intIface, const char *extIface, bool add) { 164 char cmd[255]; 165 166 // handle decrement to 0 case (do reset to defaults) and erroneous dec below 0 167 if (add == false) { 168 if (natCount <= 1) { 169 int ret = setDefaults(); 170 if (ret == 0) { 171 natCount=0; 172 } 173 if (mOemChainsExist) 174 oemSetupHooks(); 175 return ret; 176 } 177 } 178 179 if (!interfaceExists(intIface) || !interfaceExists (extIface)) { 180 LOGE("Invalid interface specified"); 181 errno = ENODEV; 182 return -1; 183 } 184 185 snprintf(cmd, sizeof(cmd), 186 "-%s FORWARD -i %s -o %s -m state --state ESTABLISHED,RELATED -j ACCEPT", 187 (add ? "A" : "D"), 188 extIface, intIface); 189 if (runIptablesCmd(cmd)) { 190 return -1; 191 } 192 193 snprintf(cmd, sizeof(cmd), 194 "-%s FORWARD -i %s -o %s -m state --state INVALID -j DROP", 195 (add ? "A" : "D"), 196 intIface, extIface); 197 if (runIptablesCmd(cmd)) { 198 snprintf(cmd, sizeof(cmd), 199 "-%s FORWARD -i %s -o %s -m state --state ESTABLISHED,RELATED -j ACCEPT", 200 (!add ? "A" : "D"), 201 extIface, intIface); 202 return -1; 203 } 204 205 snprintf(cmd, sizeof(cmd), "-%s FORWARD -i %s -o %s -j ACCEPT", (add ? "A" : "D"), 206 intIface, extIface); 207 if (runIptablesCmd(cmd)) { 208 // unwind what's been done, but don't care about success - what more could we do? 209 snprintf(cmd, sizeof(cmd), 210 "-%s FORWARD -i %s -o %s -m state --state INVALID -j DROP", 211 (!add ? "A" : "D"), 212 intIface, extIface); 213 runIptablesCmd(cmd); 214 215 snprintf(cmd, sizeof(cmd), 216 "-%s FORWARD -i %s -o %s -m state --state ESTABLISHED,RELATED -j ACCEPT", 217 (!add ? "A" : "D"), 218 extIface, intIface); 219 return -1; 220 } 221 222 // add this if we are the first added nat 223 if (add && natCount == 0) { 224 snprintf(cmd, sizeof(cmd), "-t nat -A POSTROUTING -o %s -j MASQUERADE", extIface); 225 if (runIptablesCmd(cmd)) { 226 // unwind what's been done, but don't care about success - what more could we do? 227 setDefaults();; 228 oemSetupHooks(); 229 return -1; 230 } 231 } 232 233 if (add) { 234 natCount++; 235 } else { 236 natCount--; 237 } 238 return 0; 239} 240 241int NatController::enableNat(const char *intIface, const char *extIface) { 242 return doNatCommands(intIface, extIface, true); 243} 244 245int NatController::disableNat(const char *intIface, const char *extIface) { 246 return doNatCommands(intIface, extIface, false); 247} 248