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_NAT_POSTROUTING = "st_nat_POSTROUTING"; 42 43 SecondaryTableController::SecondaryTableController(UidMarkMap *map) : mUidMarkMap(map) { 44 int i; 45 for (i=0; i < INTERFACES_TRACKED; i++) { 46 mInterfaceTable[i][0] = 0; 47 // TODO - use a hashtable or other prebuilt container class 48 mInterfaceRuleCount[i] = 0; 49 } 50 } 51 52 SecondaryTableController::~SecondaryTableController() { 53 } 54 55 int SecondaryTableController::setupIptablesHooks() { 56 int res = execIptables(V4V6, 57 "-t", 58 "mangle", 59 "-F", 60 LOCAL_MANGLE_OUTPUT, 61 NULL); 62 // Do not mark sockets that have already been marked elsewhere(for example in DNS or protect). 63 res |= execIptables(V4V6, 64 "-t", 65 "mangle", 66 "-A", 67 LOCAL_MANGLE_OUTPUT, 68 "-m", 69 "mark", 70 "!", 71 "--mark", 72 "0", 73 "-j", 74 "RETURN", 75 NULL); 76 77 // protect the legacy VPN daemons from routes. 78 // TODO: Remove this when legacy VPN's are removed. 79 res |= execIptables(V4V6, 80 "-t", 81 "mangle", 82 "-A", 83 LOCAL_MANGLE_OUTPUT, 84 "-m", 85 "owner", 86 "--uid-owner", 87 "vpn", 88 "-j", 89 "RETURN", 90 NULL); 91 return res; 92 } 93 94 int SecondaryTableController::findTableNumber(const char *iface) { 95 int i; 96 for (i = 0; i < INTERFACES_TRACKED; i++) { 97 // compare through the final null, hence +1 98 if (strncmp(iface, mInterfaceTable[i], IFNAMSIZ + 1) == 0) { 99 return i; 100 } 101 } 102 return -1; 103 } 104 105 int SecondaryTableController::addRoute(SocketClient *cli, char *iface, char *dest, int prefix, 106 char *gateway) { 107 int tableIndex = findTableNumber(iface); 108 if (tableIndex == -1) { 109 tableIndex = findTableNumber(""); // look for an empty slot 110 if (tableIndex == -1) { 111 ALOGE("Max number of NATed interfaces reached"); 112 errno = ENODEV; 113 cli->sendMsg(ResponseCode::OperationFailed, "Max number NATed", true); 114 return -1; 115 } 116 strncpy(mInterfaceTable[tableIndex], iface, IFNAMSIZ); 117 // Ensure null termination even if truncation happened 118 mInterfaceTable[tableIndex][IFNAMSIZ] = 0; 119 } 120 121 return modifyRoute(cli, ADD, iface, dest, prefix, gateway, tableIndex); 122 } 123 124 int SecondaryTableController::modifyRoute(SocketClient *cli, const char *action, char *iface, 125 char *dest, int prefix, char *gateway, int tableIndex) { 126 char dest_str[44]; // enough to store an IPv6 address + 3 character bitmask 127 char tableIndex_str[11]; 128 int ret; 129 130 // IP tool doesn't like "::" - the equiv of 0.0.0.0 that it accepts for ipv4 131 snprintf(dest_str, sizeof(dest_str), "%s/%d", dest, prefix); 132 snprintf(tableIndex_str, sizeof(tableIndex_str), "%d", tableIndex + BASE_TABLE_NUMBER); 133 134 if (strcmp("::", gateway) == 0) { 135 const char *cmd[] = { 136 IP_PATH, 137 "route", 138 action, 139 dest_str, 140 "dev", 141 iface, 142 "table", 143 tableIndex_str 144 }; 145 ret = runCmd(ARRAY_SIZE(cmd), cmd); 146 } else { 147 const char *cmd[] = { 148 IP_PATH, 149 "route", 150 action, 151 dest_str, 152 "via", 153 gateway, 154 "dev", 155 iface, 156 "table", 157 tableIndex_str 158 }; 159 ret = runCmd(ARRAY_SIZE(cmd), cmd); 160 } 161 162 if (ret) { 163 ALOGE("ip route %s failed: %s route %s %s/%d via %s dev %s table %d", action, 164 IP_PATH, action, dest, prefix, gateway, iface, tableIndex+BASE_TABLE_NUMBER); 165 errno = ENODEV; 166 cli->sendMsg(ResponseCode::OperationFailed, "ip route modification failed", true); 167 return -1; 168 } 169 170 if (strcmp(action, ADD) == 0) { 171 mInterfaceRuleCount[tableIndex]++; 172 } else { 173 if (--mInterfaceRuleCount[tableIndex] < 1) { 174 mInterfaceRuleCount[tableIndex] = 0; 175 mInterfaceTable[tableIndex][0] = 0; 176 } 177 } 178 modifyRuleCount(tableIndex, action); 179 cli->sendMsg(ResponseCode::CommandOkay, "Route modified", false); 180 return 0; 181 } 182 183 void SecondaryTableController::modifyRuleCount(int tableIndex, const char *action) { 184 if (strcmp(action, ADD) == 0) { 185 mInterfaceRuleCount[tableIndex]++; 186 } else { 187 if (--mInterfaceRuleCount[tableIndex] < 1) { 188 mInterfaceRuleCount[tableIndex] = 0; 189 mInterfaceTable[tableIndex][0] = 0; 190 } 191 } 192 } 193 194 int SecondaryTableController::verifyTableIndex(int tableIndex) { 195 if ((tableIndex < 0) || 196 (tableIndex >= INTERFACES_TRACKED) || 197 (mInterfaceTable[tableIndex][0] == 0)) { 198 return -1; 199 } else { 200 return 0; 201 } 202 } 203 204 const char *SecondaryTableController::getVersion(const char *addr) { 205 if (strchr(addr, ':') != NULL) { 206 return "-6"; 207 } else { 208 return "-4"; 209 } 210 } 211 212 IptablesTarget SecondaryTableController::getIptablesTarget(const char *addr) { 213 if (strchr(addr, ':') != NULL) { 214 return V6; 215 } else { 216 return V4; 217 } 218 } 219 220 int SecondaryTableController::removeRoute(SocketClient *cli, char *iface, char *dest, int prefix, 221 char *gateway) { 222 int tableIndex = findTableNumber(iface); 223 if (tableIndex == -1) { 224 ALOGE("Interface not found"); 225 errno = ENODEV; 226 cli->sendMsg(ResponseCode::OperationFailed, "Interface not found", true); 227 return -1; 228 } 229 230 return modifyRoute(cli, DEL, iface, dest, prefix, gateway, tableIndex); 231 } 232 233 int SecondaryTableController::modifyFromRule(int tableIndex, const char *action, 234 const char *addr) { 235 char tableIndex_str[11]; 236 237 if (verifyTableIndex(tableIndex)) { 238 return -1; 239 } 240 241 snprintf(tableIndex_str, sizeof(tableIndex_str), "%d", tableIndex + 242 BASE_TABLE_NUMBER); 243 const char *cmd[] = { 244 IP_PATH, 245 getVersion(addr), 246 "rule", 247 action, 248 "from", 249 addr, 250 "table", 251 tableIndex_str 252 }; 253 if (runCmd(ARRAY_SIZE(cmd), cmd)) { 254 return -1; 255 } 256 257 modifyRuleCount(tableIndex, action); 258 return 0; 259 } 260 261 int SecondaryTableController::modifyLocalRoute(int tableIndex, const char *action, 262 const char *iface, const char *addr) { 263 char tableIndex_str[11]; 264 265 if (verifyTableIndex(tableIndex)) { 266 return -1; 267 } 268 269 modifyRuleCount(tableIndex, action); // some del's will fail as the iface is already gone. 270 271 snprintf(tableIndex_str, sizeof(tableIndex_str), "%d", tableIndex + 272 BASE_TABLE_NUMBER); 273 const char *cmd[] = { 274 IP_PATH, 275 "route", 276 action, 277 addr, 278 "dev", 279 iface, 280 "table", 281 tableIndex_str 282 }; 283 284 return runCmd(ARRAY_SIZE(cmd), cmd); 285 } 286 int SecondaryTableController::addFwmarkRule(const char *iface) { 287 return setFwmarkRule(iface, true); 288 } 289 290 int SecondaryTableController::removeFwmarkRule(const char *iface) { 291 return setFwmarkRule(iface, false); 292 } 293 294 int SecondaryTableController::setFwmarkRule(const char *iface, bool add) { 295 int tableIndex = findTableNumber(iface); 296 if (tableIndex == -1) { 297 tableIndex = findTableNumber(""); // look for an empty slot 298 if (tableIndex == -1) { 299 ALOGE("Max number of NATed interfaces reached"); 300 errno = ENODEV; 301 return -1; 302 } 303 strncpy(mInterfaceTable[tableIndex], iface, IFNAMSIZ); 304 // Ensure null termination even if truncation happened 305 mInterfaceTable[tableIndex][IFNAMSIZ] = 0; 306 } 307 int mark = tableIndex + BASE_TABLE_NUMBER; 308 char mark_str[11]; 309 int ret; 310 311 //fail fast if any rules already exist for this interface 312 if (mUidMarkMap->anyRulesForMark(mark)) { 313 errno = EBUSY; 314 return -1; 315 } 316 317 snprintf(mark_str, sizeof(mark_str), "%d", mark); 318 // Flush any marked routes we added 319 if (!add) { 320 // iproute2 rule del will delete anything that matches, but only one rule at a time. 321 // So clearing the rules requires a bunch of calls. 322 // ip rule del will fail once there are no remaining rules that match. 323 const char *v4_cmd[] = { 324 IP_PATH, 325 "-4", 326 "rule", 327 "del", 328 "fwmark", 329 mark_str, 330 "table", 331 mark_str 332 }; 333 while(!runCmd(ARRAY_SIZE(v4_cmd), v4_cmd)) {} 334 335 const char *v6_cmd[] = { 336 IP_PATH, 337 "-6", 338 "rule", 339 "del", 340 "fwmark", 341 mark_str, 342 "table", 343 mark_str 344 }; 345 while(!runCmd(ARRAY_SIZE(v6_cmd), v6_cmd)) {} 346 } 347 // Add a route to the table to send all traffic to iface. 348 // We only need a default route because this table is only selected if a packet matches an 349 // IP rule that checks both the route and the mark. 350 const char *route_cmd[] = { 351 IP_PATH, 352 "route", 353 add ? "add" : "del", 354 "default", 355 "dev", 356 iface, 357 "table", 358 mark_str 359 }; 360 ret = runCmd(ARRAY_SIZE(route_cmd), route_cmd); 361 // The command might fail during delete if the iface is gone 362 if (add && ret) return ret; 363 364 // As above for IPv6 365 const char *route6_cmd[] = { 366 IP_PATH, 367 "-6", 368 "route", 369 add ? "add" : "del", 370 "default", 371 "dev", 372 iface, 373 "table", 374 mark_str 375 }; 376 ret = runCmd(ARRAY_SIZE(route6_cmd), route6_cmd); 377 // The command might fail during delete if the iface is gone 378 if (add && ret) return ret; 379 380 /* Best effort, because some kernels might not have the needed TCPMSS */ 381 execIptables(V4V6, 382 "-t", 383 "mangle", 384 add ? "-A" : "-D", 385 LOCAL_MANGLE_POSTROUTING, 386 "-p", "tcp", "-o", iface, "--tcp-flags", "SYN,RST", "SYN", 387 "-j", 388 "TCPMSS", 389 "--clamp-mss-to-pmtu", 390 NULL); 391 392 // Because the mark gets set after the intial routing decision the source IP address is that 393 // of the original out interface. The only way to change the source IP address to that of the 394 // VPN iface is using source NAT. 395 // TODO: Remove this when we get the mark set correctly before the first routing pass. 396 ret = execIptables(V4, 397 "-t", 398 "nat", 399 add ? "-A" : "-D", 400 LOCAL_NAT_POSTROUTING, 401 "-o", 402 iface, 403 "-m", 404 "mark", 405 "--mark", 406 mark_str, 407 "-j", 408 "MASQUERADE", 409 NULL); 410 411 if (ret) return ret; 412 413 // Try and set up NAT for IPv6 as well. This was only added in Linux 3.7 so this may fail. 414 ret = execIptables(V6, 415 "-t", 416 "nat", 417 add ? "-A" : "-D", 418 LOCAL_NAT_POSTROUTING, 419 "-o", 420 iface, 421 "-m", 422 "mark", 423 "--mark", 424 mark_str, 425 "-j", 426 "MASQUERADE", 427 NULL); 428 if (ret) { 429 // Without V6 NAT we can't do V6 over VPNs. If an IPv6 packet matches a VPN rule, then it 430 // will go out on the VPN interface, but without NAT, it will have the wrong source 431 // address. So reject all these packets. 432 // Due to rule application by the time the connection hits the output filter chain the 433 // routing pass based on the new mark has not yet happened. Reject in ip instead. 434 // TODO: Make the VPN code refuse to install IPv6 routes until we don't need IPv6 NAT. 435 const char *reject_cmd[] = { 436 IP_PATH, 437 "-6", 438 "route", 439 add ? "replace" : "del", 440 "unreachable", 441 "default", 442 "table", 443 mark_str 444 }; 445 ret = runCmd(ARRAY_SIZE(reject_cmd), reject_cmd); 446 // The command might fail during delete if the iface is gone 447 if (add && ret) return ret; 448 449 } 450 return 0; 451 452 } 453 454 int SecondaryTableController::addFwmarkRoute(const char* iface, const char *dest, int prefix) { 455 return setFwmarkRoute(iface, dest, prefix, true); 456 } 457 458 int SecondaryTableController::removeFwmarkRoute(const char* iface, const char *dest, int prefix) { 459 return setFwmarkRoute(iface, dest, prefix, true); 460 } 461 462 int SecondaryTableController::setFwmarkRoute(const char* iface, const char *dest, int prefix, 463 bool add) { 464 int tableIndex = findTableNumber(iface); 465 if (tableIndex == -1) { 466 errno = EINVAL; 467 return -1; 468 } 469 int mark = tableIndex + BASE_TABLE_NUMBER; 470 char mark_str[11] = {0}; 471 char dest_str[44]; // enough to store an IPv6 address + 3 character bitmask 472 473 snprintf(mark_str, sizeof(mark_str), "%d", mark); 474 snprintf(dest_str, sizeof(dest_str), "%s/%d", dest, prefix); 475 const char *rule_cmd[] = { 476 IP_PATH, 477 getVersion(dest_str), 478 "rule", 479 add ? "add" : "del", 480 "prio", 481 RULE_PRIO, 482 "to", 483 dest_str, 484 "fwmark", 485 mark_str, 486 "table", 487 mark_str 488 }; 489 return runCmd(ARRAY_SIZE(rule_cmd), rule_cmd); 490 } 491 492 int SecondaryTableController::addUidRule(const char *iface, int uid_start, int uid_end) { 493 return setUidRule(iface, uid_start, uid_end, true); 494 } 495 496 int SecondaryTableController::removeUidRule(const char *iface, int uid_start, int uid_end) { 497 return setUidRule(iface, uid_start, uid_end, false); 498 } 499 500 int SecondaryTableController::setUidRule(const char *iface, int uid_start, int uid_end, bool add) { 501 int tableIndex = findTableNumber(iface); 502 if (tableIndex == -1) { 503 errno = EINVAL; 504 return -1; 505 } 506 int mark = tableIndex + BASE_TABLE_NUMBER; 507 if (add) { 508 if (!mUidMarkMap->add(uid_start, uid_end, mark)) { 509 errno = EINVAL; 510 return -1; 511 } 512 } else { 513 if (!mUidMarkMap->remove(uid_start, uid_end, mark)) { 514 errno = EINVAL; 515 return -1; 516 } 517 } 518 char uid_str[24] = {0}; 519 snprintf(uid_str, sizeof(uid_str), "%d-%d", uid_start, uid_end); 520 char mark_str[11] = {0}; 521 snprintf(mark_str, sizeof(mark_str), "%d", mark); 522 return execIptables(V4V6, 523 "-t", 524 "mangle", 525 add ? "-A" : "-D", 526 LOCAL_MANGLE_OUTPUT, 527 "-m", 528 "owner", 529 "--uid-owner", 530 uid_str, 531 "-j", 532 "MARK", 533 "--set-mark", 534 mark_str, 535 NULL); 536 } 537 538 int SecondaryTableController::addHostExemption(const char *host) { 539 return setHostExemption(host, true); 540 } 541 542 int SecondaryTableController::removeHostExemption(const char *host) { 543 return setHostExemption(host, false); 544 } 545 546 int SecondaryTableController::setHostExemption(const char *host, bool add) { 547 const char *cmd[] = { 548 IP_PATH, 549 getVersion(host), 550 "rule", 551 add ? "add" : "del", 552 "prio", 553 EXEMPT_PRIO, 554 "to", 555 host, 556 "table", 557 "main" 558 }; 559 return runCmd(ARRAY_SIZE(cmd), cmd); 560 } 561 562 void SecondaryTableController::getUidMark(SocketClient *cli, int uid) { 563 int mark = mUidMarkMap->getMark(uid); 564 char mark_str[11]; 565 snprintf(mark_str, sizeof(mark_str), "%d", mark); 566 cli->sendMsg(ResponseCode::GetMarkResult, mark_str, false); 567 } 568 569 void SecondaryTableController::getProtectMark(SocketClient *cli) { 570 char protect_mark_str[11]; 571 snprintf(protect_mark_str, sizeof(protect_mark_str), "%d", PROTECT_MARK); 572 cli->sendMsg(ResponseCode::GetMarkResult, protect_mark_str, false); 573 } 574 575 int SecondaryTableController::runCmd(int argc, const char **argv) { 576 int ret = 0; 577 578 ret = android_fork_execvp(argc, (char **)argv, NULL, false, false); 579 return ret; 580 } 581