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#define LOG_NDEBUG 0 18 19#include <stdlib.h> 20#include <errno.h> 21#include <sys/socket.h> 22#include <sys/stat.h> 23#include <sys/wait.h> 24#include <fcntl.h> 25#include <netinet/in.h> 26#include <arpa/inet.h> 27#include <string.h> 28#include <cutils/properties.h> 29 30#define LOG_TAG "NatController" 31#include <cutils/log.h> 32#include <logwrap/logwrap.h> 33 34#include "NatController.h" 35#include "NetdConstants.h" 36#include "RouteController.h" 37 38const char* NatController::LOCAL_FORWARD = "natctrl_FORWARD"; 39const char* NatController::LOCAL_MANGLE_FORWARD = "natctrl_mangle_FORWARD"; 40const char* NatController::LOCAL_NAT_POSTROUTING = "natctrl_nat_POSTROUTING"; 41const char* NatController::LOCAL_TETHER_COUNTERS_CHAIN = "natctrl_tether_counters"; 42 43NatController::NatController() { 44} 45 46NatController::~NatController() { 47} 48 49struct CommandsAndArgs { 50 /* The array size doesn't really matter as the compiler will barf if too many initializers are specified. */ 51 const char *cmd[32]; 52 bool checkRes; 53}; 54 55int NatController::runCmd(int argc, const char **argv) { 56 int res; 57 58 res = android_fork_execvp(argc, (char **)argv, NULL, false, false); 59 60#if !LOG_NDEBUG 61 std::string full_cmd = argv[0]; 62 argc--; argv++; 63 /* 64 * HACK: Sometimes runCmd() is called with a ridcously large value (32) 65 * and it works because the argv[] contains a NULL after the last 66 * true argv. So here we use the NULL argv[] to terminate when the argc 67 * is horribly wrong, and argc for the normal cases. 68 */ 69 for (; argc && argv[0]; argc--, argv++) { 70 full_cmd += " "; 71 full_cmd += argv[0]; 72 } 73 ALOGV("runCmd(%s) res=%d", full_cmd.c_str(), res); 74#endif 75 return res; 76} 77 78int NatController::setupIptablesHooks() { 79 int res; 80 res = setDefaults(); 81 if (res < 0) { 82 return res; 83 } 84 85 struct CommandsAndArgs defaultCommands[] = { 86 /* 87 * First chain is for tethering counters. 88 * This chain is reached via --goto, and then RETURNS. 89 * 90 * Second chain is used to limit downstream mss to the upstream pmtu 91 * so we don't end up fragmenting every large packet tethered devices 92 * send. Note this feature requires kernel support with flag 93 * CONFIG_NETFILTER_XT_TARGET_TCPMSS=y, which not all builds will have, 94 * so the final rule is allowed to fail. 95 * Bug 17629786 asks to make the failure more obvious, or even fatal 96 * so that all builds eventually gain the performance improvement. 97 */ 98 {{IPTABLES_PATH, "-w", "-F", LOCAL_TETHER_COUNTERS_CHAIN,}, 0}, 99 {{IPTABLES_PATH, "-w", "-X", LOCAL_TETHER_COUNTERS_CHAIN,}, 0}, 100 {{IPTABLES_PATH, "-w", "-N", LOCAL_TETHER_COUNTERS_CHAIN,}, 1}, 101 {{IPTABLES_PATH, "-w", "-t", "mangle", "-A", LOCAL_MANGLE_FORWARD, "-p", "tcp", "--tcp-flags", 102 "SYN", "SYN", "-j", "TCPMSS", "--clamp-mss-to-pmtu"}, 0}, 103 }; 104 for (unsigned int cmdNum = 0; cmdNum < ARRAY_SIZE(defaultCommands); cmdNum++) { 105 if (runCmd(ARRAY_SIZE(defaultCommands[cmdNum].cmd), defaultCommands[cmdNum].cmd) && 106 defaultCommands[cmdNum].checkRes) { 107 return -1; 108 } 109 } 110 ifacePairList.clear(); 111 112 return 0; 113} 114 115int NatController::setDefaults() { 116 /* 117 * The following only works because: 118 * - the defaultsCommands[].cmd array is padded with NULL, and 119 * - the 1st argc of runCmd() will just be the max for the CommandsAndArgs[].cmd, and 120 * - internally it will be memcopied to an array and terminated with a NULL. 121 */ 122 struct CommandsAndArgs defaultCommands[] = { 123 {{IPTABLES_PATH, "-w", "-F", LOCAL_FORWARD,}, 1}, 124 {{IPTABLES_PATH, "-w", "-A", LOCAL_FORWARD, "-j", "DROP"}, 1}, 125 {{IPTABLES_PATH, "-w", "-t", "nat", "-F", LOCAL_NAT_POSTROUTING}, 1}, 126 }; 127 for (unsigned int cmdNum = 0; cmdNum < ARRAY_SIZE(defaultCommands); cmdNum++) { 128 if (runCmd(ARRAY_SIZE(defaultCommands[cmdNum].cmd), defaultCommands[cmdNum].cmd) && 129 defaultCommands[cmdNum].checkRes) { 130 return -1; 131 } 132 } 133 134 natCount = 0; 135 136 return 0; 137} 138 139int NatController::enableNat(const char* intIface, const char* extIface) { 140 ALOGV("enableNat(intIface=<%s>, extIface=<%s>)",intIface, extIface); 141 142 if (!isIfaceName(intIface) || !isIfaceName(extIface)) { 143 errno = ENODEV; 144 return -1; 145 } 146 147 /* Bug: b/9565268. "enableNat wlan0 wlan0". For now we fail until java-land is fixed */ 148 if (!strcmp(intIface, extIface)) { 149 ALOGE("Duplicate interface specified: %s %s", intIface, extIface); 150 errno = EINVAL; 151 return -1; 152 } 153 154 // add this if we are the first added nat 155 if (natCount == 0) { 156 const char *cmd[] = { 157 IPTABLES_PATH, 158 "-w", 159 "-t", 160 "nat", 161 "-A", 162 LOCAL_NAT_POSTROUTING, 163 "-o", 164 extIface, 165 "-j", 166 "MASQUERADE" 167 }; 168 if (runCmd(ARRAY_SIZE(cmd), cmd)) { 169 ALOGE("Error setting postroute rule: iface=%s", extIface); 170 // unwind what's been done, but don't care about success - what more could we do? 171 setDefaults(); 172 return -1; 173 } 174 } 175 176 if (setForwardRules(true, intIface, extIface) != 0) { 177 ALOGE("Error setting forward rules"); 178 if (natCount == 0) { 179 setDefaults(); 180 } 181 errno = ENODEV; 182 return -1; 183 } 184 185 /* Always make sure the drop rule is at the end */ 186 const char *cmd1[] = { 187 IPTABLES_PATH, 188 "-w", 189 "-D", 190 LOCAL_FORWARD, 191 "-j", 192 "DROP" 193 }; 194 runCmd(ARRAY_SIZE(cmd1), cmd1); 195 const char *cmd2[] = { 196 IPTABLES_PATH, 197 "-w", 198 "-A", 199 LOCAL_FORWARD, 200 "-j", 201 "DROP" 202 }; 203 runCmd(ARRAY_SIZE(cmd2), cmd2); 204 205 natCount++; 206 return 0; 207} 208 209bool NatController::checkTetherCountingRuleExist(const char *pair_name) { 210 std::list<std::string>::iterator it; 211 212 for (it = ifacePairList.begin(); it != ifacePairList.end(); it++) { 213 if (*it == pair_name) { 214 /* We already have this counter */ 215 return true; 216 } 217 } 218 return false; 219} 220 221int NatController::setTetherCountingRules(bool add, const char *intIface, const char *extIface) { 222 223 /* We only ever add tethering quota rules so that they stick. */ 224 if (!add) { 225 return 0; 226 } 227 char *pair_name; 228 asprintf(&pair_name, "%s_%s", intIface, extIface); 229 230 if (checkTetherCountingRuleExist(pair_name)) { 231 free(pair_name); 232 return 0; 233 } 234 const char *cmd2b[] = { 235 IPTABLES_PATH, 236 "-w", 237 "-A", 238 LOCAL_TETHER_COUNTERS_CHAIN, 239 "-i", 240 intIface, 241 "-o", 242 extIface, 243 "-j", 244 "RETURN" 245 }; 246 247 if (runCmd(ARRAY_SIZE(cmd2b), cmd2b) && add) { 248 free(pair_name); 249 return -1; 250 } 251 ifacePairList.push_front(pair_name); 252 free(pair_name); 253 254 asprintf(&pair_name, "%s_%s", extIface, intIface); 255 if (checkTetherCountingRuleExist(pair_name)) { 256 free(pair_name); 257 return 0; 258 } 259 260 const char *cmd3b[] = { 261 IPTABLES_PATH, 262 "-w", 263 "-A", 264 LOCAL_TETHER_COUNTERS_CHAIN, 265 "-i", 266 extIface, 267 "-o", 268 intIface, 269 "-j", 270 "RETURN" 271 }; 272 273 if (runCmd(ARRAY_SIZE(cmd3b), cmd3b) && add) { 274 // unwind what's been done, but don't care about success - what more could we do? 275 free(pair_name); 276 return -1; 277 } 278 ifacePairList.push_front(pair_name); 279 free(pair_name); 280 return 0; 281} 282 283int NatController::setForwardRules(bool add, const char *intIface, const char *extIface) { 284 const char *cmd1[] = { 285 IPTABLES_PATH, 286 "-w", 287 add ? "-A" : "-D", 288 LOCAL_FORWARD, 289 "-i", 290 extIface, 291 "-o", 292 intIface, 293 "-m", 294 "state", 295 "--state", 296 "ESTABLISHED,RELATED", 297 "-g", 298 LOCAL_TETHER_COUNTERS_CHAIN 299 }; 300 int rc = 0; 301 302 if (runCmd(ARRAY_SIZE(cmd1), cmd1) && add) { 303 return -1; 304 } 305 306 const char *cmd2[] = { 307 IPTABLES_PATH, 308 "-w", 309 add ? "-A" : "-D", 310 LOCAL_FORWARD, 311 "-i", 312 intIface, 313 "-o", 314 extIface, 315 "-m", 316 "state", 317 "--state", 318 "INVALID", 319 "-j", 320 "DROP" 321 }; 322 323 const char *cmd3[] = { 324 IPTABLES_PATH, 325 "-w", 326 add ? "-A" : "-D", 327 LOCAL_FORWARD, 328 "-i", 329 intIface, 330 "-o", 331 extIface, 332 "-g", 333 LOCAL_TETHER_COUNTERS_CHAIN 334 }; 335 336 if (runCmd(ARRAY_SIZE(cmd2), cmd2) && add) { 337 // bail on error, but only if adding 338 rc = -1; 339 goto err_invalid_drop; 340 } 341 342 if (runCmd(ARRAY_SIZE(cmd3), cmd3) && add) { 343 // unwind what's been done, but don't care about success - what more could we do? 344 rc = -1; 345 goto err_return; 346 } 347 348 if (setTetherCountingRules(add, intIface, extIface) && add) { 349 rc = -1; 350 goto err_return; 351 } 352 353 return 0; 354 355err_return: 356 cmd2[2] = "-D"; 357 runCmd(ARRAY_SIZE(cmd2), cmd2); 358err_invalid_drop: 359 cmd1[2] = "-D"; 360 runCmd(ARRAY_SIZE(cmd1), cmd1); 361 return rc; 362} 363 364int NatController::disableNat(const char* intIface, const char* extIface) { 365 if (!isIfaceName(intIface) || !isIfaceName(extIface)) { 366 errno = ENODEV; 367 return -1; 368 } 369 370 setForwardRules(false, intIface, extIface); 371 if (--natCount <= 0) { 372 // handle decrement to 0 case (do reset to defaults) and erroneous dec below 0 373 setDefaults(); 374 } 375 return 0; 376} 377