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