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