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 #define LOG_NDEBUG 0
     18 
     19 #include <stdlib.h>
     20 #include <errno.h>
     21 #include <sys/socket.h>
     22 #include <sys/stat.h>
     23 #include <sys/wait.h>
     24 #include <fcntl.h>
     25 #include <netinet/in.h>
     26 #include <arpa/inet.h>
     27 #include <string.h>
     28 #include <cutils/properties.h>
     29 
     30 #define LOG_TAG "NatController"
     31 #include <cutils/log.h>
     32 #include <logwrap/logwrap.h>
     33 
     34 #include "NatController.h"
     35 #include "SecondaryTableController.h"
     36 #include "NetdConstants.h"
     37 
     38 const char* NatController::LOCAL_FORWARD = "natctrl_FORWARD";
     39 const char* NatController::LOCAL_NAT_POSTROUTING = "natctrl_nat_POSTROUTING";
     40 const char* NatController::LOCAL_TETHER_COUNTERS_CHAIN = "natctrl_tether_counters";
     41 
     42 NatController::NatController(SecondaryTableController *ctrl) {
     43     secondaryTableCtrl = ctrl;
     44 }
     45 
     46 NatController::~NatController() {
     47 }
     48 
     49 struct CommandsAndArgs {
     50     /* The array size doesn't really matter as the compiler will barf if too many initializers are specified. */
     51     const char *cmd[32];
     52     bool checkRes;
     53 };
     54 
     55 int NatController::runCmd(int argc, const char **argv) {
     56     int res;
     57 
     58     res = android_fork_execvp(argc, (char **)argv, NULL, false, false);
     59 
     60 #if !LOG_NDEBUG
     61     std::string full_cmd = argv[0];
     62     argc--; argv++;
     63     /*
     64      * HACK: Sometimes runCmd() is called with a ridcously large value (32)
     65      * and it works because the argv[] contains a NULL after the last
     66      * true argv. So here we use the NULL argv[] to terminate when the argc
     67      * is horribly wrong, and argc for the normal cases.
     68      */
     69     for (; argc && argv[0]; argc--, argv++) {
     70         full_cmd += " ";
     71         full_cmd += argv[0];
     72     }
     73     ALOGV("runCmd(%s) res=%d", full_cmd.c_str(), res);
     74 #endif
     75     return res;
     76 }
     77 
     78 int NatController::setupIptablesHooks() {
     79     int res;
     80     res = setDefaults();
     81     if (res < 0) {
     82         return res;
     83     }
     84 
     85     struct CommandsAndArgs defaultCommands[] = {
     86         /*
     87          * Chain for tethering counters.
     88          * This chain is reached via --goto, and then RETURNS.
     89          */
     90         {{IPTABLES_PATH, "-F", LOCAL_TETHER_COUNTERS_CHAIN,}, 0},
     91         {{IPTABLES_PATH, "-X", LOCAL_TETHER_COUNTERS_CHAIN,}, 0},
     92         {{IPTABLES_PATH, "-N", LOCAL_TETHER_COUNTERS_CHAIN,}, 1},
     93     };
     94     for (unsigned int cmdNum = 0; cmdNum < ARRAY_SIZE(defaultCommands); cmdNum++) {
     95         if (runCmd(ARRAY_SIZE(defaultCommands[cmdNum].cmd), defaultCommands[cmdNum].cmd) &&
     96             defaultCommands[cmdNum].checkRes) {
     97                 return -1;
     98         }
     99     }
    100 
    101     return 0;
    102 }
    103 
    104 int NatController::setDefaults() {
    105     /*
    106      * The following only works because:
    107      *  - the defaultsCommands[].cmd array is padded with NULL, and
    108      *  - the 1st argc of runCmd() will just be the max for the CommandsAndArgs[].cmd, and
    109      *  - internally it will be memcopied to an array and terminated with a NULL.
    110      */
    111     struct CommandsAndArgs defaultCommands[] = {
    112         {{IPTABLES_PATH, "-F", LOCAL_FORWARD,}, 1},
    113         {{IPTABLES_PATH, "-A", LOCAL_FORWARD, "-j", "DROP"}, 1},
    114         {{IPTABLES_PATH, "-t", "nat", "-F", LOCAL_NAT_POSTROUTING}, 1},
    115         {{IP_PATH, "rule", "flush"}, 0},
    116         {{IP_PATH, "-6", "rule", "flush"}, 0},
    117         {{IP_PATH, "rule", "add", "from", "all", "lookup", "default", "prio", "32767"}, 0},
    118         {{IP_PATH, "rule", "add", "from", "all", "lookup", "main", "prio", "32766"}, 0},
    119         {{IP_PATH, "-6", "rule", "add", "from", "all", "lookup", "default", "prio", "32767"}, 0},
    120         {{IP_PATH, "-6", "rule", "add", "from", "all", "lookup", "main", "prio", "32766"}, 0},
    121         {{IP_PATH, "route", "flush", "cache"}, 0},
    122     };
    123     for (unsigned int cmdNum = 0; cmdNum < ARRAY_SIZE(defaultCommands); cmdNum++) {
    124         if (runCmd(ARRAY_SIZE(defaultCommands[cmdNum].cmd), defaultCommands[cmdNum].cmd) &&
    125             defaultCommands[cmdNum].checkRes) {
    126                 return -1;
    127         }
    128     }
    129 
    130     natCount = 0;
    131 
    132     return 0;
    133 }
    134 
    135 bool NatController::checkInterface(const char *iface) {
    136     if (strlen(iface) > IFNAMSIZ) return false;
    137     return true;
    138 }
    139 
    140 int NatController::routesOp(bool add, const char *intIface, const char *extIface, char **argv, int addrCount) {
    141     int tableNumber = secondaryTableCtrl->findTableNumber(extIface);
    142     int ret = 0;
    143 
    144     if (tableNumber != -1) {
    145         for (int i = 0; i < addrCount; i++) {
    146             if (add) {
    147                 ret |= secondaryTableCtrl->modifyFromRule(tableNumber, ADD, argv[5+i]);
    148                 ret |= secondaryTableCtrl->modifyLocalRoute(tableNumber, ADD, intIface, argv[5+i]);
    149             } else {
    150                 ret |= secondaryTableCtrl->modifyLocalRoute(tableNumber, DEL, intIface, argv[5+i]);
    151                 ret |= secondaryTableCtrl->modifyFromRule(tableNumber, DEL, argv[5+i]);
    152             }
    153         }
    154         const char *cmd[] = {
    155                 IP_PATH,
    156                 "route",
    157                 "flush",
    158                 "cache"
    159         };
    160         runCmd(ARRAY_SIZE(cmd), cmd);
    161     }
    162     return ret;
    163 }
    164 
    165 //  0    1       2       3       4            5
    166 // nat enable intface extface addrcnt nated-ipaddr/prelength
    167 int NatController::enableNat(const int argc, char **argv) {
    168     int i;
    169     int addrCount = atoi(argv[4]);
    170     const char *intIface = argv[2];
    171     const char *extIface = argv[3];
    172     int tableNumber;
    173 
    174     ALOGV("enableNat(intIface=<%s>, extIface=<%s>)",intIface, extIface);
    175 
    176     if (!checkInterface(intIface) || !checkInterface(extIface)) {
    177         ALOGE("Invalid interface specified");
    178         errno = ENODEV;
    179         return -1;
    180     }
    181 
    182     /* Bug: b/9565268. "enableNat wlan0 wlan0". For now we fail until java-land is fixed */
    183     if (!strcmp(intIface, extIface)) {
    184         ALOGE("Duplicate interface specified: %s %s", intIface, extIface);
    185         errno = EINVAL;
    186         return -1;
    187     }
    188 
    189     if (argc < 5 + addrCount) {
    190         ALOGE("Missing Argument");
    191         errno = EINVAL;
    192         return -1;
    193     }
    194     if (routesOp(true, intIface, extIface, argv, addrCount)) {
    195         ALOGE("Error setting route rules");
    196         routesOp(false, intIface, extIface, argv, addrCount);
    197         errno = ENODEV;
    198         return -1;
    199     }
    200 
    201     // add this if we are the first added nat
    202     if (natCount == 0) {
    203         const char *cmd[] = {
    204                 IPTABLES_PATH,
    205                 "-t",
    206                 "nat",
    207                 "-A",
    208                 LOCAL_NAT_POSTROUTING,
    209                 "-o",
    210                 extIface,
    211                 "-j",
    212                 "MASQUERADE"
    213         };
    214         if (runCmd(ARRAY_SIZE(cmd), cmd)) {
    215             ALOGE("Error seting postroute rule: iface=%s", extIface);
    216             // unwind what's been done, but don't care about success - what more could we do?
    217             routesOp(false, intIface, extIface, argv, addrCount);
    218             setDefaults();
    219             return -1;
    220         }
    221     }
    222 
    223 
    224     if (setForwardRules(true, intIface, extIface) != 0) {
    225         ALOGE("Error setting forward rules");
    226         routesOp(false, intIface, extIface, argv, addrCount);
    227         if (natCount == 0) {
    228             setDefaults();
    229         }
    230         errno = ENODEV;
    231         return -1;
    232     }
    233 
    234     /* Always make sure the drop rule is at the end */
    235     const char *cmd1[] = {
    236             IPTABLES_PATH,
    237             "-D",
    238             LOCAL_FORWARD,
    239             "-j",
    240             "DROP"
    241     };
    242     runCmd(ARRAY_SIZE(cmd1), cmd1);
    243     const char *cmd2[] = {
    244             IPTABLES_PATH,
    245             "-A",
    246             LOCAL_FORWARD,
    247             "-j",
    248             "DROP"
    249     };
    250     runCmd(ARRAY_SIZE(cmd2), cmd2);
    251 
    252     natCount++;
    253     return 0;
    254 }
    255 
    256 int NatController::setTetherCountingRules(bool add, const char *intIface, const char *extIface) {
    257 
    258     /* We only ever add tethering quota rules so that they stick. */
    259     if (!add) {
    260         return 0;
    261     }
    262     char *quota_name, *proc_path;
    263     int quota_fd;
    264     asprintf(&quota_name, "%s_%s", intIface, extIface);
    265 
    266     asprintf(&proc_path, "/proc/net/xt_quota/%s", quota_name);
    267     quota_fd = open(proc_path, O_RDONLY);
    268     if (quota_fd >= 0) {
    269         /* quota for iface pair already exists */
    270         free(proc_path);
    271         free(quota_name);
    272         return 0;
    273     }
    274     close(quota_fd);
    275     free(proc_path);
    276 
    277     const char *cmd2b[] = {
    278             IPTABLES_PATH,
    279             "-A",
    280             LOCAL_TETHER_COUNTERS_CHAIN,
    281             "-i",
    282             intIface,
    283             "-o",
    284             extIface,
    285             "-m",
    286             "quota2",
    287             "--name",
    288             quota_name,
    289             "--grow",
    290             "-j",
    291           "RETURN"
    292     };
    293 
    294     if (runCmd(ARRAY_SIZE(cmd2b), cmd2b) && add) {
    295         free(quota_name);
    296         return -1;
    297     }
    298     free(quota_name);
    299 
    300     asprintf(&quota_name, "%s_%s", extIface, intIface);
    301     asprintf(&proc_path, "/proc/net/xt_quota/%s", quota_name);
    302     quota_fd = open(proc_path, O_RDONLY);
    303     if (quota_fd >= 0) {
    304         /* quota for iface pair already exists */
    305         free(proc_path);
    306         free(quota_name);
    307         return 0;
    308     }
    309     close(quota_fd);
    310     free(proc_path);
    311 
    312     const char *cmd3b[] = {
    313             IPTABLES_PATH,
    314             "-A",
    315             LOCAL_TETHER_COUNTERS_CHAIN,
    316             "-i",
    317             extIface,
    318             "-o",
    319             intIface,
    320             "-m",
    321             "quota2",
    322             "--name",
    323             quota_name,
    324             "--grow",
    325             "-j",
    326             "RETURN"
    327     };
    328 
    329     if (runCmd(ARRAY_SIZE(cmd3b), cmd3b) && add) {
    330         // unwind what's been done, but don't care about success - what more could we do?
    331         free(quota_name);
    332         return -1;
    333     }
    334     free(quota_name);
    335     return 0;
    336 }
    337 
    338 int NatController::setForwardRules(bool add, const char *intIface, const char *extIface) {
    339     const char *cmd1[] = {
    340             IPTABLES_PATH,
    341             add ? "-A" : "-D",
    342             LOCAL_FORWARD,
    343             "-i",
    344             extIface,
    345             "-o",
    346             intIface,
    347             "-m",
    348             "state",
    349             "--state",
    350             "ESTABLISHED,RELATED",
    351             "-g",
    352             LOCAL_TETHER_COUNTERS_CHAIN
    353     };
    354     int rc = 0;
    355 
    356     if (runCmd(ARRAY_SIZE(cmd1), cmd1) && add) {
    357         return -1;
    358     }
    359 
    360     const char *cmd2[] = {
    361             IPTABLES_PATH,
    362             add ? "-A" : "-D",
    363             LOCAL_FORWARD,
    364             "-i",
    365             intIface,
    366             "-o",
    367             extIface,
    368             "-m",
    369             "state",
    370             "--state",
    371             "INVALID",
    372             "-j",
    373             "DROP"
    374     };
    375 
    376     const char *cmd3[] = {
    377             IPTABLES_PATH,
    378             add ? "-A" : "-D",
    379             LOCAL_FORWARD,
    380             "-i",
    381             intIface,
    382             "-o",
    383             extIface,
    384             "-g",
    385             LOCAL_TETHER_COUNTERS_CHAIN
    386     };
    387 
    388     if (runCmd(ARRAY_SIZE(cmd2), cmd2) && add) {
    389         // bail on error, but only if adding
    390         rc = -1;
    391         goto err_invalid_drop;
    392     }
    393 
    394     if (runCmd(ARRAY_SIZE(cmd3), cmd3) && add) {
    395         // unwind what's been done, but don't care about success - what more could we do?
    396         rc = -1;
    397         goto err_return;
    398     }
    399 
    400     if (setTetherCountingRules(add, intIface, extIface) && add) {
    401         rc = -1;
    402         goto err_return;
    403     }
    404 
    405     return 0;
    406 
    407 err_return:
    408     cmd2[1] = "-D";
    409     runCmd(ARRAY_SIZE(cmd2), cmd2);
    410 err_invalid_drop:
    411     cmd1[1] = "-D";
    412     runCmd(ARRAY_SIZE(cmd1), cmd1);
    413     return rc;
    414 }
    415 
    416 // nat disable intface extface
    417 //  0    1       2       3       4            5
    418 // nat enable intface extface addrcnt nated-ipaddr/prelength
    419 int NatController::disableNat(const int argc, char **argv) {
    420     int i;
    421     int addrCount = atoi(argv[4]);
    422     const char *intIface = argv[2];
    423     const char *extIface = argv[3];
    424     int tableNumber;
    425 
    426     if (!checkInterface(intIface) || !checkInterface(extIface)) {
    427         ALOGE("Invalid interface specified");
    428         errno = ENODEV;
    429         return -1;
    430     }
    431 
    432     if (argc < 5 + addrCount) {
    433         ALOGE("Missing Argument");
    434         errno = EINVAL;
    435         return -1;
    436     }
    437 
    438     setForwardRules(false, intIface, extIface);
    439     routesOp(false, intIface, extIface, argv, addrCount);
    440     if (--natCount <= 0) {
    441         // handle decrement to 0 case (do reset to defaults) and erroneous dec below 0
    442         setDefaults();
    443     }
    444     return 0;
    445 }
    446