int SecondaryTableController::setUidRule(const char *iface, int uid_start, int uid_end, bool add) { unsigned netId = mNetCtrl->getNetworkId(iface); if (!mNetCtrl->setNetworkForUidRange(uid_start, uid_end, add ? netId : 0, false)) { errno = EINVAL; return -1; } char uid_str[24] = {0}; snprintf(uid_str, sizeof(uid_str), "%d-%d", uid_start, uid_end); char mark_str[11] = {0}; snprintf(mark_str, sizeof(mark_str), "%u", netId + BASE_TABLE_NUMBER); return execIptables(V4V6, "-t", "mangle", add ? "-A" : "-D", LOCAL_MANGLE_OUTPUT, "-m", "owner", "--uid-owner", uid_str, "-j", "MARK", "--set-mark", mark_str, NULL); }
int execIptablesSilently(IptablesTarget target, ...) { va_list args; va_start(args, target); int res = execIptables(target, true, args); va_end(args); return res; }
int execIptables(IptablesTarget target, ...) { va_list args; va_start(args, target); int res = execIptables(target, false, args); va_end(args); return res; }
int SecondaryTableController::setHostExemption(const char *host, bool add) { IptablesTarget target = !strcmp(getVersion(host), "-4") ? V4 : V6; char protect_mark_str[11]; snprintf(protect_mark_str, sizeof(protect_mark_str), "%d", PROTECT_MARK); int ret = execIptables(target, "-t", "mangle", add ? "-A" : "-D", LOCAL_MANGLE_EXEMPT, "-d", host, "-j", "MARK", "--set-mark", protect_mark_str, NULL); const char *cmd[] = { IP_PATH, getVersion(host), "rule", add ? "add" : "del", "prio", EXEMPT_PRIO, "to", host, "table", "main" }; ret |= runCmd(ARRAY_SIZE(cmd), cmd); return ret; }
int SecondaryTableController::setupIptablesHooks() { int res = execIptables(V4V6, "-t", "mangle", "-F", LOCAL_MANGLE_OUTPUT, NULL); res |= execIptables(V4V6, "-t", "mangle", "-F", LOCAL_MANGLE_EXEMPT, NULL); // rule for skipping anything marked with the PROTECT_MARK char protect_mark_str[11]; snprintf(protect_mark_str, sizeof(protect_mark_str), "%d", PROTECT_MARK); res |= execIptables(V4V6, "-t", "mangle", "-A", LOCAL_MANGLE_OUTPUT, "-m", "mark", "--mark", protect_mark_str, "-j", "RETURN", NULL); // protect the legacy VPN daemons from routes. // TODO: Remove this when legacy VPN's are removed. res |= execIptables(V4V6, "-t", "mangle", "-A", LOCAL_MANGLE_OUTPUT, "-m", "owner", "--uid-owner", "vpn", "-j", "RETURN", NULL); return res; }
static void createChildChains(IptablesTarget target, const char* table, const char* parentChain, const char** childChains) { const char** childChain = childChains; do { // Order is important: // -D to delete any pre-existing jump rule (removes references // that would prevent -X from working) // -F to flush any existing chain // -X to delete any existing chain // -N to create the chain // -A to append the chain to parent execIptablesSilently(target, "-t", table, "-D", parentChain, "-j", *childChain, NULL); execIptablesSilently(target, "-t", table, "-F", *childChain, NULL); execIptablesSilently(target, "-t", table, "-X", *childChain, NULL); execIptables(target, "-t", table, "-N", *childChain, NULL); execIptables(target, "-t", table, "-A", parentChain, "-j", *childChain, NULL); } while (*(++childChain) != NULL); }
int SecondaryTableController::setupIptablesHooks() { int res = execIptables(V4V6, "-t", "mangle", "-F", LOCAL_MANGLE_OUTPUT, NULL); // Do not mark sockets that have already been marked elsewhere(for example in DNS or protect). res |= execIptables(V4V6, "-t", "mangle", "-A", LOCAL_MANGLE_OUTPUT, "-m", "mark", "!", "--mark", "0", "-j", "RETURN", NULL); // protect the legacy VPN daemons from routes. // TODO: Remove this when legacy VPN's are removed. res |= execIptables(V4V6, "-t", "mangle", "-A", LOCAL_MANGLE_OUTPUT, "-m", "owner", "--uid-owner", "vpn", "-j", "RETURN", NULL); return res; }
int SecondaryTableController::setFwmarkRoute(const char* iface, const char *dest, int prefix, bool add) { unsigned netId = mNetCtrl->getNetworkId(iface); char mark_str[11] = {0}; char chain_str[IFNAMSIZ + 18]; char dest_str[44]; // enough to store an IPv6 address + 3 character bitmask snprintf(mark_str, sizeof(mark_str), "%u", netId + BASE_TABLE_NUMBER); snprintf(chain_str, sizeof(chain_str), LOCAL_MANGLE_IFACE_FORMAT, iface); snprintf(dest_str, sizeof(dest_str), "%s/%d", dest, prefix); return execIptables(getIptablesTarget(dest), "-t", "mangle", add ? "-A" : "-D", chain_str, "-d", dest_str, "-j", "MARK", "--set-mark", mark_str, NULL); }
int SecondaryTableController::setUidRule(const char *iface, int uid_start, int uid_end, bool add) { unsigned netId = mNetCtrl->getNetworkId(iface); if (!mNetCtrl->setNetworkForUidRange(uid_start, uid_end, add ? netId : 0, false)) { errno = EINVAL; return -1; } char uid_str[24] = {0}; char chain_str[IFNAMSIZ + 18]; snprintf(uid_str, sizeof(uid_str), "%d-%d", uid_start, uid_end); snprintf(chain_str, sizeof(chain_str), LOCAL_MANGLE_IFACE_FORMAT, iface); return execIptables(V4V6, "-t", "mangle", add ? "-A" : "-D", LOCAL_MANGLE_OUTPUT, "-m", "owner", "--uid-owner", uid_str, "-g", chain_str, NULL); }
int SecondaryTableController::setFwmarkRule(const char *iface, bool add) { unsigned netId = mNetCtrl->getNetworkId(iface); // Fail fast if any rules already exist for this interface if (mNetIdRuleCount.count(netId) > 0) { errno = EBUSY; return -1; } int ret; char mark_str[11]; snprintf(mark_str, sizeof(mark_str), "%u", netId + BASE_TABLE_NUMBER); //add the catch all route to the tun. Route rules will make sure the right packets hit the table const char *route_cmd[] = { IP_PATH, "route", add ? "add" : "del", "default", "dev", iface, "table", mark_str }; ret = runCmd(ARRAY_SIZE(route_cmd), route_cmd); // The command might fail during delete if the iface is gone if (add && ret) return ret; // As above for IPv6 const char *route6_cmd[] = { IP_PATH, "-6", "route", add ? "add" : "del", "default", "dev", iface, "table", mark_str }; ret = runCmd(ARRAY_SIZE(route6_cmd), route6_cmd); // The command might fail during delete if the iface is gone if (add && ret) return ret; /* Best effort, because some kernels might not have the needed TCPMSS */ execIptables(V4V6, "-t", "mangle", add ? "-A" : "-D", LOCAL_MANGLE_POSTROUTING, "-p", "tcp", "-o", iface, "--tcp-flags", "SYN,RST", "SYN", "-j", "TCPMSS", "--clamp-mss-to-pmtu", NULL); // Because the mark gets set after the intial routing decision the source IP address is that // of the original out interface. The only way to change the source IP address to that of the // VPN iface is using source NAT. // TODO: Remove this when we get the mark set correctly before the first routing pass. ret = execIptables(V4, "-t", "nat", add ? "-A" : "-D", LOCAL_NAT_POSTROUTING, "-o", iface, "-m", "mark", "--mark", mark_str, "-j", "MASQUERADE", NULL); if (ret) return ret; // Try and set up NAT for IPv6 as well. This was only added in Linux 3.7 so this may fail. ret = execIptables(V6, "-t", "nat", add ? "-A" : "-D", LOCAL_NAT_POSTROUTING, "-o", iface, "-m", "mark", "--mark", mark_str, "-j", "MASQUERADE", NULL); if (ret) { // Without V6 NAT we can't do V6 over VPNs. If an IPv6 packet matches a VPN rule, then it // will go out on the VPN interface, but without NAT, it will have the wrong source // address. So reject all these packets. // Due to rule application by the time the connection hits the output filter chain the // routing pass based on the new mark has not yet happened. Reject in ip instead. // TODO: Make the VPN code refuse to install IPv6 routes until we don't need IPv6 NAT. const char *reject_cmd[] = { IP_PATH, "-6", "route", add ? "replace" : "del", "unreachable", "default", "table", mark_str }; ret = runCmd(ARRAY_SIZE(reject_cmd), reject_cmd); // The command might fail during delete if the iface is gone if (add && ret) return ret; } return 0; }
int SecondaryTableController::setFwmarkRule(const char *iface, bool add) { unsigned netId = mNetCtrl->getNetworkId(iface); // Fail fast if any rules already exist for this interface if (mNetIdRuleCount.count(netId) > 0) { errno = EBUSY; return -1; } int ret; char mark_str[11]; snprintf(mark_str, sizeof(mark_str), "%u", netId + BASE_TABLE_NUMBER); //add the catch all route to the tun. Route rules will make sure the right packets hit the table const char *route_cmd[] = { IP_PATH, "route", add ? "add" : "del", "default", "dev", iface, "table", mark_str }; ret = runCmd(ARRAY_SIZE(route_cmd), route_cmd); const char *fwmark_cmd[] = { IP_PATH, "rule", add ? "add" : "del", "prio", RULE_PRIO, "fwmark", mark_str, "table", mark_str }; ret = runCmd(ARRAY_SIZE(fwmark_cmd), fwmark_cmd); if (ret) return ret; //add rules for v6 const char *route6_cmd[] = { IP_PATH, "-6", "route", add ? "add" : "del", "default", "dev", iface, "table", mark_str }; ret = runCmd(ARRAY_SIZE(route6_cmd), route6_cmd); const char *fwmark6_cmd[] = { IP_PATH, "-6", "rule", add ? "add" : "del", "prio", RULE_PRIO, "fwmark", mark_str, "table", mark_str }; ret = runCmd(ARRAY_SIZE(fwmark6_cmd), fwmark6_cmd); if (ret) return ret; //create the route rule chain char chain_str[IFNAMSIZ + 18]; snprintf(chain_str, sizeof(chain_str), LOCAL_MANGLE_IFACE_FORMAT, iface); //code split due to ordering requirements if (add) { ret = execIptables(V4V6, "-t", "mangle", "-N", chain_str, NULL); //set up the rule for sending premarked packets to the VPN chain //Insert these at the top of the chain so they trigger before any UID rules ret |= execIptables(V4V6, "-t", "mangle", "-I", LOCAL_MANGLE_OUTPUT, "3", "-m", "mark", "--mark", mark_str, "-g", chain_str, NULL); //add a rule to clear the mark in the VPN chain //packets marked with SO_MARK already have the iface's mark set but unless they match a //route they should hit the network instead of the VPN ret |= execIptables(V4V6, "-t", "mangle", "-A", chain_str, "-j", "MARK", "--set-mark", "0", NULL); } else { ret = execIptables(V4V6, "-t", "mangle", "-D", LOCAL_MANGLE_OUTPUT, "-m", "mark", "--mark", mark_str, "-g", chain_str, NULL); //clear and delete the chain ret |= execIptables(V4V6, "-t", "mangle", "-F", chain_str, NULL); ret |= execIptables(V4V6, "-t", "mangle", "-X", chain_str, NULL); } //set up the needed source IP rewriting //NOTE: Without ipv6 NAT in the kernel <3.7 only support V4 NAT ret = execIptables(V4, "-t", "nat", add ? "-A" : "-D", LOCAL_NAT_POSTROUTING, "-o", iface, "-m", "mark", "--mark", mark_str, "-j", "MASQUERADE", NULL); if (ret) return ret; //try and set up for ipv6. ipv6 nat came in the kernel only in 3.7, so this can fail ret = execIptables(V6, "-t", "nat", add ? "-A" : "-D", LOCAL_NAT_POSTROUTING, "-o", iface, "-m", "mark", "--mark", mark_str, "-j", "MASQUERADE", NULL); if (ret) { //Without V6 NAT we can't do V6 over VPNs. ret = execIptables(V6, "-t", "filter", add ? "-A" : "-D", LOCAL_FILTER_OUTPUT, "-m", "mark", "--mark", mark_str, "-j", "REJECT", NULL); } return ret; }