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);
}
Esempio n. 10
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);
    // 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;

}