Home | History | Annotate | Download | only in netd
      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