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 <errno.h> 19 #include <fcntl.h> 20 #include <string.h> 21 22 #include <sys/socket.h> 23 #include <sys/stat.h> 24 #include <sys/types.h> 25 #include <sys/wait.h> 26 27 #include <netinet/in.h> 28 #include <arpa/inet.h> 29 30 #define LOG_TAG "SecondaryTablController" 31 #include <cutils/log.h> 32 #include <cutils/properties.h> 33 #include <logwrap/logwrap.h> 34 35 #include "ResponseCode.h" 36 #include "NetdConstants.h" 37 #include "SecondaryTableController.h" 38 39 const char* SecondaryTableController::LOCAL_MANGLE_OUTPUT = "st_mangle_OUTPUT"; 40 const char* SecondaryTableController::LOCAL_MANGLE_POSTROUTING = "st_mangle_POSTROUTING"; 41 const char* SecondaryTableController::LOCAL_MANGLE_EXEMPT = "st_mangle_EXEMPT"; 42 const char* SecondaryTableController::LOCAL_MANGLE_IFACE_FORMAT = "st_mangle_%s_OUTPUT"; 43 const char* SecondaryTableController::LOCAL_NAT_POSTROUTING = "st_nat_POSTROUTING"; 44 const char* SecondaryTableController::LOCAL_FILTER_OUTPUT = "st_filter_OUTPUT"; 45 46 SecondaryTableController::SecondaryTableController(UidMarkMap *map) : mUidMarkMap(map) { 47 int i; 48 for (i=0; i < INTERFACES_TRACKED; i++) { 49 mInterfaceTable[i][0] = 0; 50 // TODO - use a hashtable or other prebuilt container class 51 mInterfaceRuleCount[i] = 0; 52 } 53 } 54 55 SecondaryTableController::~SecondaryTableController() { 56 } 57 58 int SecondaryTableController::setupIptablesHooks() { 59 int res = execIptables(V4V6, 60 "-t", 61 "mangle", 62 "-F", 63 LOCAL_MANGLE_OUTPUT, 64 NULL); 65 res |= execIptables(V4V6, 66 "-t", 67 "mangle", 68 "-F", 69 LOCAL_MANGLE_EXEMPT, 70 NULL); 71 // rule for skipping anything marked with the PROTECT_MARK 72 char protect_mark_str[11]; 73 snprintf(protect_mark_str, sizeof(protect_mark_str), "%d", PROTECT_MARK); 74 res |= execIptables(V4V6, 75 "-t", 76 "mangle", 77 "-A", 78 LOCAL_MANGLE_OUTPUT, 79 "-m", 80 "mark", 81 "--mark", 82 protect_mark_str, 83 "-j", 84 "RETURN", 85 NULL); 86 87 // protect the legacy VPN daemons from routes. 88 // TODO: Remove this when legacy VPN's are removed. 89 res |= execIptables(V4V6, 90 "-t", 91 "mangle", 92 "-A", 93 LOCAL_MANGLE_OUTPUT, 94 "-m", 95 "owner", 96 "--uid-owner", 97 "vpn", 98 "-j", 99 "RETURN", 100 NULL); 101 return res; 102 } 103 104 int SecondaryTableController::findTableNumber(const char *iface) { 105 int i; 106 for (i = 0; i < INTERFACES_TRACKED; i++) { 107 // compare through the final null, hence +1 108 if (strncmp(iface, mInterfaceTable[i], IFNAMSIZ + 1) == 0) { 109 return i; 110 } 111 } 112 return -1; 113 } 114 115 int SecondaryTableController::addRoute(SocketClient *cli, char *iface, char *dest, int prefix, 116 char *gateway) { 117 int tableIndex = findTableNumber(iface); 118 if (tableIndex == -1) { 119 tableIndex = findTableNumber(""); // look for an empty slot 120 if (tableIndex == -1) { 121 ALOGE("Max number of NATed interfaces reached"); 122 errno = ENODEV; 123 cli->sendMsg(ResponseCode::OperationFailed, "Max number NATed", true); 124 return -1; 125 } 126 strncpy(mInterfaceTable[tableIndex], iface, IFNAMSIZ); 127 // Ensure null termination even if truncation happened 128 mInterfaceTable[tableIndex][IFNAMSIZ] = 0; 129 } 130 131 return modifyRoute(cli, ADD, iface, dest, prefix, gateway, tableIndex); 132 } 133 134 int SecondaryTableController::modifyRoute(SocketClient *cli, const char *action, char *iface, 135 char *dest, int prefix, char *gateway, int tableIndex) { 136 char dest_str[44]; // enough to store an IPv6 address + 3 character bitmask 137 char tableIndex_str[11]; 138 int ret; 139 140 // IP tool doesn't like "::" - the equiv of 0.0.0.0 that it accepts for ipv4 141 snprintf(dest_str, sizeof(dest_str), "%s/%d", dest, prefix); 142 snprintf(tableIndex_str, sizeof(tableIndex_str), "%d", tableIndex + BASE_TABLE_NUMBER); 143 144 if (strcmp("::", gateway) == 0) { 145 const char *cmd[] = { 146 IP_PATH, 147 "route", 148 action, 149 dest_str, 150 "dev", 151 iface, 152 "table", 153 tableIndex_str 154 }; 155 ret = runCmd(ARRAY_SIZE(cmd), cmd); 156 } else { 157 const char *cmd[] = { 158 IP_PATH, 159 "route", 160 action, 161 dest_str, 162 "via", 163 gateway, 164 "dev", 165 iface, 166 "table", 167 tableIndex_str 168 }; 169 ret = runCmd(ARRAY_SIZE(cmd), cmd); 170 } 171 172 if (ret) { 173 ALOGE("ip route %s failed: %s route %s %s/%d via %s dev %s table %d", action, 174 IP_PATH, action, dest, prefix, gateway, iface, tableIndex+BASE_TABLE_NUMBER); 175 errno = ENODEV; 176 cli->sendMsg(ResponseCode::OperationFailed, "ip route modification failed", true); 177 return -1; 178 } 179 180 if (strcmp(action, ADD) == 0) { 181 mInterfaceRuleCount[tableIndex]++; 182 } else { 183 if (--mInterfaceRuleCount[tableIndex] < 1) { 184 mInterfaceRuleCount[tableIndex] = 0; 185 mInterfaceTable[tableIndex][0] = 0; 186 } 187 } 188 modifyRuleCount(tableIndex, action); 189 cli->sendMsg(ResponseCode::CommandOkay, "Route modified", false); 190 return 0; 191 } 192 193 void SecondaryTableController::modifyRuleCount(int tableIndex, const char *action) { 194 if (strcmp(action, ADD) == 0) { 195 mInterfaceRuleCount[tableIndex]++; 196 } else { 197 if (--mInterfaceRuleCount[tableIndex] < 1) { 198 mInterfaceRuleCount[tableIndex] = 0; 199 mInterfaceTable[tableIndex][0] = 0; 200 } 201 } 202 } 203 204 int SecondaryTableController::verifyTableIndex(int tableIndex) { 205 if ((tableIndex < 0) || 206 (tableIndex >= INTERFACES_TRACKED) || 207 (mInterfaceTable[tableIndex][0] == 0)) { 208 return -1; 209 } else { 210 return 0; 211 } 212 } 213 214 const char *SecondaryTableController::getVersion(const char *addr) { 215 if (strchr(addr, ':') != NULL) { 216 return "-6"; 217 } else { 218 return "-4"; 219 } 220 } 221 222 IptablesTarget SecondaryTableController::getIptablesTarget(const char *addr) { 223 if (strchr(addr, ':') != NULL) { 224 return V6; 225 } else { 226 return V4; 227 } 228 } 229 230 int SecondaryTableController::removeRoute(SocketClient *cli, char *iface, char *dest, int prefix, 231 char *gateway) { 232 int tableIndex = findTableNumber(iface); 233 if (tableIndex == -1) { 234 ALOGE("Interface not found"); 235 errno = ENODEV; 236 cli->sendMsg(ResponseCode::OperationFailed, "Interface not found", true); 237 return -1; 238 } 239 240 return modifyRoute(cli, DEL, iface, dest, prefix, gateway, tableIndex); 241 } 242 243 int SecondaryTableController::modifyFromRule(int tableIndex, const char *action, 244 const char *addr) { 245 char tableIndex_str[11]; 246 247 if (verifyTableIndex(tableIndex)) { 248 return -1; 249 } 250 251 snprintf(tableIndex_str, sizeof(tableIndex_str), "%d", tableIndex + 252 BASE_TABLE_NUMBER); 253 const char *cmd[] = { 254 IP_PATH, 255 getVersion(addr), 256 "rule", 257 action, 258 "from", 259 addr, 260 "table", 261 tableIndex_str 262 }; 263 if (runCmd(ARRAY_SIZE(cmd), cmd)) { 264 return -1; 265 } 266 267 modifyRuleCount(tableIndex, action); 268 return 0; 269 } 270 271 int SecondaryTableController::modifyLocalRoute(int tableIndex, const char *action, 272 const char *iface, const char *addr) { 273 char tableIndex_str[11]; 274 275 if (verifyTableIndex(tableIndex)) { 276 return -1; 277 } 278 279 modifyRuleCount(tableIndex, action); // some del's will fail as the iface is already gone. 280 281 snprintf(tableIndex_str, sizeof(tableIndex_str), "%d", tableIndex + 282 BASE_TABLE_NUMBER); 283 const char *cmd[] = { 284 IP_PATH, 285 "route", 286 action, 287 addr, 288 "dev", 289 iface, 290 "table", 291 tableIndex_str 292 }; 293 294 return runCmd(ARRAY_SIZE(cmd), cmd); 295 } 296 int SecondaryTableController::addFwmarkRule(const char *iface) { 297 return setFwmarkRule(iface, true); 298 } 299 300 int SecondaryTableController::removeFwmarkRule(const char *iface) { 301 return setFwmarkRule(iface, false); 302 } 303 304 int SecondaryTableController::setFwmarkRule(const char *iface, bool add) { 305 int tableIndex = findTableNumber(iface); 306 if (tableIndex == -1) { 307 tableIndex = findTableNumber(""); // look for an empty slot 308 if (tableIndex == -1) { 309 ALOGE("Max number of NATed interfaces reached"); 310 errno = ENODEV; 311 return -1; 312 } 313 strncpy(mInterfaceTable[tableIndex], iface, IFNAMSIZ); 314 // Ensure null termination even if truncation happened 315 mInterfaceTable[tableIndex][IFNAMSIZ] = 0; 316 } 317 int mark = tableIndex + BASE_TABLE_NUMBER; 318 char mark_str[11]; 319 int ret; 320 321 //fail fast if any rules already exist for this interface 322 if (mUidMarkMap->anyRulesForMark(mark)) { 323 errno = EBUSY; 324 return -1; 325 } 326 327 snprintf(mark_str, sizeof(mark_str), "%d", mark); 328 //add the catch all route to the tun. Route rules will make sure the right packets hit the table 329 const char *route_cmd[] = { 330 IP_PATH, 331 "route", 332 add ? "add" : "del", 333 "default", 334 "dev", 335 iface, 336 "table", 337 mark_str 338 }; 339 ret = runCmd(ARRAY_SIZE(route_cmd), route_cmd); 340 341 const char *fwmark_cmd[] = { 342 IP_PATH, 343 "rule", 344 add ? "add" : "del", 345 "prio", 346 RULE_PRIO, 347 "fwmark", 348 mark_str, 349 "table", 350 mark_str 351 }; 352 ret = runCmd(ARRAY_SIZE(fwmark_cmd), fwmark_cmd); 353 if (ret) return ret; 354 355 //add rules for v6 356 const char *route6_cmd[] = { 357 IP_PATH, 358 "-6", 359 "route", 360 add ? "add" : "del", 361 "default", 362 "dev", 363 iface, 364 "table", 365 mark_str 366 }; 367 ret = runCmd(ARRAY_SIZE(route6_cmd), route6_cmd); 368 369 const char *fwmark6_cmd[] = { 370 IP_PATH, 371 "-6", 372 "rule", 373 add ? "add" : "del", 374 "prio", 375 RULE_PRIO, 376 "fwmark", 377 mark_str, 378 "table", 379 mark_str 380 }; 381 ret = runCmd(ARRAY_SIZE(fwmark6_cmd), fwmark6_cmd); 382 383 384 if (ret) return ret; 385 386 //create the route rule chain 387 char chain_str[IFNAMSIZ + 18]; 388 snprintf(chain_str, sizeof(chain_str), LOCAL_MANGLE_IFACE_FORMAT, iface); 389 //code split due to ordering requirements 390 if (add) { 391 ret = execIptables(V4V6, 392 "-t", 393 "mangle", 394 "-N", 395 chain_str, 396 NULL); 397 //set up the rule for sending premarked packets to the VPN chain 398 //Insert these at the top of the chain so they trigger before any UID rules 399 ret |= execIptables(V4V6, 400 "-t", 401 "mangle", 402 "-I", 403 LOCAL_MANGLE_OUTPUT, 404 "3", 405 "-m", 406 "mark", 407 "--mark", 408 mark_str, 409 "-g", 410 chain_str, 411 NULL); 412 //add a rule to clear the mark in the VPN chain 413 //packets marked with SO_MARK already have the iface's mark set but unless they match a 414 //route they should hit the network instead of the VPN 415 ret |= execIptables(V4V6, 416 "-t", 417 "mangle", 418 "-A", 419 chain_str, 420 "-j", 421 "MARK", 422 "--set-mark", 423 "0", 424 NULL); 425 426 /* Best effort, because some kernels might not have the needed TCPMSS */ 427 execIptables(V4V6, 428 "-t", 429 "mangle", 430 "-A", 431 LOCAL_MANGLE_POSTROUTING, 432 "-p", "tcp", "-o", iface, "--tcp-flags", "SYN,RST", "SYN", 433 "-j", 434 "TCPMSS", 435 "--clamp-mss-to-pmtu", 436 NULL); 437 438 } else { 439 ret = execIptables(V4V6, 440 "-t", 441 "mangle", 442 "-D", 443 LOCAL_MANGLE_OUTPUT, 444 "-m", 445 "mark", 446 "--mark", 447 mark_str, 448 "-g", 449 chain_str, 450 NULL); 451 452 //clear and delete the chain 453 ret |= execIptables(V4V6, 454 "-t", 455 "mangle", 456 "-F", 457 chain_str, 458 NULL); 459 460 ret |= execIptables(V4V6, 461 "-t", 462 "mangle", 463 "-X", 464 chain_str, 465 NULL); 466 467 /* Best effort, because some kernels might not have the needed TCPMSS */ 468 execIptables(V4V6, 469 "-t", 470 "mangle", 471 "-D", 472 LOCAL_MANGLE_POSTROUTING, 473 "-p", "tcp", "-o", iface, "--tcp-flags", "SYN,RST", "SYN", 474 "-j", 475 "TCPMSS", 476 "--clamp-mss-to-pmtu", 477 NULL); 478 } 479 480 //set up the needed source IP rewriting 481 //NOTE: Without ipv6 NAT in the kernel <3.7 only support V4 NAT 482 ret = execIptables(V4, 483 "-t", 484 "nat", 485 add ? "-A" : "-D", 486 LOCAL_NAT_POSTROUTING, 487 "-o", 488 iface, 489 "-m", 490 "mark", 491 "--mark", 492 mark_str, 493 "-j", 494 "MASQUERADE", 495 NULL); 496 497 if (ret) return ret; 498 499 //try and set up for ipv6. ipv6 nat came in the kernel only in 3.7, so this can fail 500 ret = execIptables(V6, 501 "-t", 502 "nat", 503 add ? "-A" : "-D", 504 LOCAL_NAT_POSTROUTING, 505 "-o", 506 iface, 507 "-m", 508 "mark", 509 "--mark", 510 mark_str, 511 "-j", 512 "MASQUERADE", 513 NULL); 514 if (ret) { 515 //Without V6 NAT we can't do V6 over VPNs. 516 ret = execIptables(V6, 517 "-t", 518 "filter", 519 add ? "-A" : "-D", 520 LOCAL_FILTER_OUTPUT, 521 "-m", 522 "mark", 523 "--mark", 524 mark_str, 525 "-j", 526 "REJECT", 527 NULL); 528 } 529 return ret; 530 531 } 532 533 int SecondaryTableController::addFwmarkRoute(const char* iface, const char *dest, int prefix) { 534 return setFwmarkRoute(iface, dest, prefix, true); 535 } 536 537 int SecondaryTableController::removeFwmarkRoute(const char* iface, const char *dest, int prefix) { 538 return setFwmarkRoute(iface, dest, prefix, true); 539 } 540 541 int SecondaryTableController::setFwmarkRoute(const char* iface, const char *dest, int prefix, 542 bool add) { 543 int tableIndex = findTableNumber(iface); 544 if (tableIndex == -1) { 545 errno = EINVAL; 546 return -1; 547 } 548 int mark = tableIndex + BASE_TABLE_NUMBER; 549 char mark_str[11] = {0}; 550 char chain_str[IFNAMSIZ + 18]; 551 char dest_str[44]; // enough to store an IPv6 address + 3 character bitmask 552 553 snprintf(mark_str, sizeof(mark_str), "%d", mark); 554 snprintf(chain_str, sizeof(chain_str), LOCAL_MANGLE_IFACE_FORMAT, iface); 555 snprintf(dest_str, sizeof(dest_str), "%s/%d", dest, prefix); 556 return execIptables(getIptablesTarget(dest), 557 "-t", 558 "mangle", 559 add ? "-A" : "-D", 560 chain_str, 561 "-d", 562 dest_str, 563 "-j", 564 "MARK", 565 "--set-mark", 566 mark_str, 567 NULL); 568 } 569 570 int SecondaryTableController::addUidRule(const char *iface, int uid_start, int uid_end) { 571 return setUidRule(iface, uid_start, uid_end, true); 572 } 573 574 int SecondaryTableController::removeUidRule(const char *iface, int uid_start, int uid_end) { 575 return setUidRule(iface, uid_start, uid_end, false); 576 } 577 578 int SecondaryTableController::setUidRule(const char *iface, int uid_start, int uid_end, bool add) { 579 int tableIndex = findTableNumber(iface); 580 if (tableIndex == -1) { 581 errno = EINVAL; 582 return -1; 583 } 584 int mark = tableIndex + BASE_TABLE_NUMBER; 585 if (add) { 586 if (!mUidMarkMap->add(uid_start, uid_end, mark)) { 587 errno = EINVAL; 588 return -1; 589 } 590 } else { 591 if (!mUidMarkMap->remove(uid_start, uid_end, mark)) { 592 errno = EINVAL; 593 return -1; 594 } 595 } 596 char uid_str[24] = {0}; 597 char chain_str[IFNAMSIZ + 18]; 598 snprintf(uid_str, sizeof(uid_str), "%d-%d", uid_start, uid_end); 599 snprintf(chain_str, sizeof(chain_str), LOCAL_MANGLE_IFACE_FORMAT, iface); 600 return execIptables(V4V6, 601 "-t", 602 "mangle", 603 add ? "-A" : "-D", 604 LOCAL_MANGLE_OUTPUT, 605 "-m", 606 "owner", 607 "--uid-owner", 608 uid_str, 609 "-g", 610 chain_str, 611 NULL); 612 } 613 614 int SecondaryTableController::addHostExemption(const char *host) { 615 return setHostExemption(host, true); 616 } 617 618 int SecondaryTableController::removeHostExemption(const char *host) { 619 return setHostExemption(host, false); 620 } 621 622 int SecondaryTableController::setHostExemption(const char *host, bool add) { 623 IptablesTarget target = !strcmp(getVersion(host), "-4") ? V4 : V6; 624 char protect_mark_str[11]; 625 snprintf(protect_mark_str, sizeof(protect_mark_str), "%d", PROTECT_MARK); 626 int ret = execIptables(target, 627 "-t", 628 "mangle", 629 add ? "-A" : "-D", 630 LOCAL_MANGLE_EXEMPT, 631 "-d", 632 host, 633 "-j", 634 "MARK", 635 "--set-mark", 636 protect_mark_str, 637 NULL); 638 const char *cmd[] = { 639 IP_PATH, 640 getVersion(host), 641 "rule", 642 add ? "add" : "del", 643 "prio", 644 EXEMPT_PRIO, 645 "to", 646 host, 647 "table", 648 "main" 649 }; 650 ret |= runCmd(ARRAY_SIZE(cmd), cmd); 651 return ret; 652 } 653 654 void SecondaryTableController::getUidMark(SocketClient *cli, int uid) { 655 int mark = mUidMarkMap->getMark(uid); 656 char mark_str[11]; 657 snprintf(mark_str, sizeof(mark_str), "%d", mark); 658 cli->sendMsg(ResponseCode::GetMarkResult, mark_str, false); 659 } 660 661 void SecondaryTableController::getProtectMark(SocketClient *cli) { 662 char protect_mark_str[11]; 663 snprintf(protect_mark_str, sizeof(protect_mark_str), "%d", PROTECT_MARK); 664 cli->sendMsg(ResponseCode::GetMarkResult, protect_mark_str, false); 665 } 666 667 int SecondaryTableController::runCmd(int argc, const char **argv) { 668 int ret = 0; 669 670 ret = android_fork_execvp(argc, (char **)argv, NULL, false, false); 671 return ret; 672 } 673