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