NatController.cpp revision 56afacf838d24cf8e54d2cf0d8ab9182ab704125
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 "NetworkController.h" 36#include "SecondaryTableController.h" 37#include "NetdConstants.h" 38 39const char* NatController::LOCAL_FORWARD = "natctrl_FORWARD"; 40const char* NatController::LOCAL_NAT_POSTROUTING = "natctrl_nat_POSTROUTING"; 41const char* NatController::LOCAL_TETHER_COUNTERS_CHAIN = "natctrl_tether_counters"; 42 43NatController::NatController(SecondaryTableController *table_ctrl, NetworkController* net_ctrl) : 44 mSecondaryTableCtrl(table_ctrl), mNetCtrl(net_ctrl) { 45} 46 47NatController::~NatController() { 48} 49 50struct CommandsAndArgs { 51 /* The array size doesn't really matter as the compiler will barf if too many initializers are specified. */ 52 const char *cmd[32]; 53 bool checkRes; 54}; 55 56int NatController::runCmd(int argc, const char **argv) { 57 int res; 58 59 res = android_fork_execvp(argc, (char **)argv, NULL, false, false); 60 61#if !LOG_NDEBUG 62 std::string full_cmd = argv[0]; 63 argc--; argv++; 64 /* 65 * HACK: Sometimes runCmd() is called with a ridcously large value (32) 66 * and it works because the argv[] contains a NULL after the last 67 * true argv. So here we use the NULL argv[] to terminate when the argc 68 * is horribly wrong, and argc for the normal cases. 69 */ 70 for (; argc && argv[0]; argc--, argv++) { 71 full_cmd += " "; 72 full_cmd += argv[0]; 73 } 74 ALOGV("runCmd(%s) res=%d", full_cmd.c_str(), res); 75#endif 76 return res; 77} 78 79int NatController::setupIptablesHooks() { 80 int res; 81 res = setDefaults(); 82 if (res < 0) { 83 return res; 84 } 85 86 struct CommandsAndArgs defaultCommands[] = { 87 /* 88 * Chain for tethering counters. 89 * This chain is reached via --goto, and then RETURNS. 90 */ 91 {{IPTABLES_PATH, "-F", LOCAL_TETHER_COUNTERS_CHAIN,}, 0}, 92 {{IPTABLES_PATH, "-X", LOCAL_TETHER_COUNTERS_CHAIN,}, 0}, 93 {{IPTABLES_PATH, "-N", LOCAL_TETHER_COUNTERS_CHAIN,}, 1}, 94 }; 95 for (unsigned int cmdNum = 0; cmdNum < ARRAY_SIZE(defaultCommands); cmdNum++) { 96 if (runCmd(ARRAY_SIZE(defaultCommands[cmdNum].cmd), defaultCommands[cmdNum].cmd) && 97 defaultCommands[cmdNum].checkRes) { 98 return -1; 99 } 100 } 101 ifacePairList.clear(); 102 103 return 0; 104} 105 106int NatController::setDefaults() { 107 /* 108 * The following only works because: 109 * - the defaultsCommands[].cmd array is padded with NULL, and 110 * - the 1st argc of runCmd() will just be the max for the CommandsAndArgs[].cmd, and 111 * - internally it will be memcopied to an array and terminated with a NULL. 112 */ 113 struct CommandsAndArgs defaultCommands[] = { 114 {{IPTABLES_PATH, "-F", LOCAL_FORWARD,}, 1}, 115 {{IPTABLES_PATH, "-A", LOCAL_FORWARD, "-j", "DROP"}, 1}, 116 {{IPTABLES_PATH, "-t", "nat", "-F", LOCAL_NAT_POSTROUTING}, 1}, 117 {{IP_PATH, "rule", "flush"}, 0}, 118 {{IP_PATH, "-6", "rule", "flush"}, 0}, 119 {{IP_PATH, "rule", "add", "from", "all", "lookup", "default", "prio", "32767"}, 0}, 120 {{IP_PATH, "rule", "add", "from", "all", "lookup", "main", "prio", "32766"}, 0}, 121 {{IP_PATH, "-6", "rule", "add", "from", "all", "lookup", "default", "prio", "32767"}, 0}, 122 {{IP_PATH, "-6", "rule", "add", "from", "all", "lookup", "main", "prio", "32766"}, 0}, 123 {{IP_PATH, "route", "flush", "cache"}, 0}, 124 }; 125 for (unsigned int cmdNum = 0; cmdNum < ARRAY_SIZE(defaultCommands); cmdNum++) { 126 if (runCmd(ARRAY_SIZE(defaultCommands[cmdNum].cmd), defaultCommands[cmdNum].cmd) && 127 defaultCommands[cmdNum].checkRes) { 128 return -1; 129 } 130 } 131 132 natCount = 0; 133 134 return 0; 135} 136 137bool NatController::checkInterface(const char *iface) { 138 if (strlen(iface) > IFNAMSIZ) return false; 139 return true; 140} 141 142int NatController::routesOp(bool add, const char *intIface, const char *extIface, char **argv, int addrCount) { 143 unsigned netId = mNetCtrl->getNetworkId(extIface); 144 int ret = 0; 145 146 for (int i = 0; i < addrCount; i++) { 147 if (add) { 148 ret |= mSecondaryTableCtrl->modifyFromRule(netId, ADD, argv[5+i]); 149 ret |= mSecondaryTableCtrl->modifyLocalRoute(netId, ADD, intIface, argv[5+i]); 150 } else { 151 ret |= mSecondaryTableCtrl->modifyLocalRoute(netId, DEL, intIface, argv[5+i]); 152 ret |= mSecondaryTableCtrl->modifyFromRule(netId, DEL, argv[5+i]); 153 } 154 } 155 const char *cmd[] = { 156 IP_PATH, 157 "route", 158 "flush", 159 "cache" 160 }; 161 runCmd(ARRAY_SIZE(cmd), cmd); 162 return ret; 163} 164 165// 0 1 2 3 4 5 166// nat enable intface extface addrcnt nated-ipaddr/prelength 167int NatController::enableNat(const int argc, char **argv) { 168 int addrCount = atoi(argv[4]); 169 const char *intIface = argv[2]; 170 const char *extIface = argv[3]; 171 172 ALOGV("enableNat(intIface=<%s>, extIface=<%s>)",intIface, extIface); 173 174 if (!checkInterface(intIface) || !checkInterface(extIface)) { 175 ALOGE("Invalid interface specified"); 176 errno = ENODEV; 177 return -1; 178 } 179 180 /* Bug: b/9565268. "enableNat wlan0 wlan0". For now we fail until java-land is fixed */ 181 if (!strcmp(intIface, extIface)) { 182 ALOGE("Duplicate interface specified: %s %s", intIface, extIface); 183 errno = EINVAL; 184 return -1; 185 } 186 187 if (argc < 5 + addrCount) { 188 ALOGE("Missing Argument"); 189 errno = EINVAL; 190 return -1; 191 } 192 if (routesOp(true, intIface, extIface, argv, addrCount)) { 193 ALOGE("Error setting route rules"); 194 routesOp(false, intIface, extIface, argv, addrCount); 195 errno = ENODEV; 196 return -1; 197 } 198 199 // add this if we are the first added nat 200 if (natCount == 0) { 201 const char *cmd[] = { 202 IPTABLES_PATH, 203 "-t", 204 "nat", 205 "-A", 206 LOCAL_NAT_POSTROUTING, 207 "-o", 208 extIface, 209 "-j", 210 "MASQUERADE" 211 }; 212 if (runCmd(ARRAY_SIZE(cmd), cmd)) { 213 ALOGE("Error seting postroute rule: iface=%s", extIface); 214 // unwind what's been done, but don't care about success - what more could we do? 215 routesOp(false, intIface, extIface, argv, addrCount); 216 setDefaults(); 217 return -1; 218 } 219 } 220 221 222 if (setForwardRules(true, intIface, extIface) != 0) { 223 ALOGE("Error setting forward rules"); 224 routesOp(false, intIface, extIface, argv, addrCount); 225 if (natCount == 0) { 226 setDefaults(); 227 } 228 errno = ENODEV; 229 return -1; 230 } 231 232 /* Always make sure the drop rule is at the end */ 233 const char *cmd1[] = { 234 IPTABLES_PATH, 235 "-D", 236 LOCAL_FORWARD, 237 "-j", 238 "DROP" 239 }; 240 runCmd(ARRAY_SIZE(cmd1), cmd1); 241 const char *cmd2[] = { 242 IPTABLES_PATH, 243 "-A", 244 LOCAL_FORWARD, 245 "-j", 246 "DROP" 247 }; 248 runCmd(ARRAY_SIZE(cmd2), cmd2); 249 250 natCount++; 251 return 0; 252} 253 254bool NatController::checkTetherCountingRuleExist(const char *pair_name) { 255 std::list<std::string>::iterator it; 256 257 for (it = ifacePairList.begin(); it != ifacePairList.end(); it++) { 258 if (*it == pair_name) { 259 /* We already have this counter */ 260 return true; 261 } 262 } 263 return false; 264} 265 266int NatController::setTetherCountingRules(bool add, const char *intIface, const char *extIface) { 267 268 /* We only ever add tethering quota rules so that they stick. */ 269 if (!add) { 270 return 0; 271 } 272 char *pair_name; 273 asprintf(&pair_name, "%s_%s", intIface, extIface); 274 275 if (checkTetherCountingRuleExist(pair_name)) { 276 free(pair_name); 277 return 0; 278 } 279 const char *cmd2b[] = { 280 IPTABLES_PATH, 281 "-A", 282 LOCAL_TETHER_COUNTERS_CHAIN, 283 "-i", 284 intIface, 285 "-o", 286 extIface, 287 "-j", 288 "RETURN" 289 }; 290 291 if (runCmd(ARRAY_SIZE(cmd2b), cmd2b) && add) { 292 free(pair_name); 293 return -1; 294 } 295 ifacePairList.push_front(pair_name); 296 free(pair_name); 297 298 asprintf(&pair_name, "%s_%s", extIface, intIface); 299 if (checkTetherCountingRuleExist(pair_name)) { 300 free(pair_name); 301 return 0; 302 } 303 304 const char *cmd3b[] = { 305 IPTABLES_PATH, 306 "-A", 307 LOCAL_TETHER_COUNTERS_CHAIN, 308 "-i", 309 extIface, 310 "-o", 311 intIface, 312 "-j", 313 "RETURN" 314 }; 315 316 if (runCmd(ARRAY_SIZE(cmd3b), cmd3b) && add) { 317 // unwind what's been done, but don't care about success - what more could we do? 318 free(pair_name); 319 return -1; 320 } 321 ifacePairList.push_front(pair_name); 322 free(pair_name); 323 return 0; 324} 325 326int NatController::setForwardRules(bool add, const char *intIface, const char *extIface) { 327 const char *cmd1[] = { 328 IPTABLES_PATH, 329 add ? "-A" : "-D", 330 LOCAL_FORWARD, 331 "-i", 332 extIface, 333 "-o", 334 intIface, 335 "-m", 336 "state", 337 "--state", 338 "ESTABLISHED,RELATED", 339 "-g", 340 LOCAL_TETHER_COUNTERS_CHAIN 341 }; 342 int rc = 0; 343 344 if (runCmd(ARRAY_SIZE(cmd1), cmd1) && add) { 345 return -1; 346 } 347 348 const char *cmd2[] = { 349 IPTABLES_PATH, 350 add ? "-A" : "-D", 351 LOCAL_FORWARD, 352 "-i", 353 intIface, 354 "-o", 355 extIface, 356 "-m", 357 "state", 358 "--state", 359 "INVALID", 360 "-j", 361 "DROP" 362 }; 363 364 const char *cmd3[] = { 365 IPTABLES_PATH, 366 add ? "-A" : "-D", 367 LOCAL_FORWARD, 368 "-i", 369 intIface, 370 "-o", 371 extIface, 372 "-g", 373 LOCAL_TETHER_COUNTERS_CHAIN 374 }; 375 376 if (runCmd(ARRAY_SIZE(cmd2), cmd2) && add) { 377 // bail on error, but only if adding 378 rc = -1; 379 goto err_invalid_drop; 380 } 381 382 if (runCmd(ARRAY_SIZE(cmd3), cmd3) && add) { 383 // unwind what's been done, but don't care about success - what more could we do? 384 rc = -1; 385 goto err_return; 386 } 387 388 if (setTetherCountingRules(add, intIface, extIface) && add) { 389 rc = -1; 390 goto err_return; 391 } 392 393 return 0; 394 395err_return: 396 cmd2[1] = "-D"; 397 runCmd(ARRAY_SIZE(cmd2), cmd2); 398err_invalid_drop: 399 cmd1[1] = "-D"; 400 runCmd(ARRAY_SIZE(cmd1), cmd1); 401 return rc; 402} 403 404// nat disable intface extface 405// 0 1 2 3 4 5 406// nat enable intface extface addrcnt nated-ipaddr/prelength 407int NatController::disableNat(const int argc, char **argv) { 408 int addrCount = atoi(argv[4]); 409 const char *intIface = argv[2]; 410 const char *extIface = argv[3]; 411 412 if (!checkInterface(intIface) || !checkInterface(extIface)) { 413 ALOGE("Invalid interface specified"); 414 errno = ENODEV; 415 return -1; 416 } 417 418 if (argc < 5 + addrCount) { 419 ALOGE("Missing Argument"); 420 errno = EINVAL; 421 return -1; 422 } 423 424 setForwardRules(false, intIface, extIface); 425 routesOp(false, intIface, extIface, argv, addrCount); 426 if (--natCount <= 0) { 427 // handle decrement to 0 case (do reset to defaults) and erroneous dec below 0 428 setDefaults(); 429 } 430 return 0; 431} 432