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