Home | History | Annotate | Download | only in server
      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 "NetdConstants.h"
     36 #include "RouteController.h"
     37 
     38 const char* NatController::LOCAL_FORWARD = "natctrl_FORWARD";
     39 const char* NatController::LOCAL_MANGLE_FORWARD = "natctrl_mangle_FORWARD";
     40 const char* NatController::LOCAL_NAT_POSTROUTING = "natctrl_nat_POSTROUTING";
     41 const char* NatController::LOCAL_TETHER_COUNTERS_CHAIN = "natctrl_tether_counters";
     42 
     43 NatController::NatController() {
     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          * First chain is for tethering counters.
     88          * This chain is reached via --goto, and then RETURNS.
     89          *
     90          * Second chain is used to limit downstream mss to the upstream pmtu
     91          * so we don't end up fragmenting every large packet tethered devices
     92          * send.  Note this feature requires kernel support with flag
     93          * CONFIG_NETFILTER_XT_TARGET_TCPMSS=y, which not all builds will have,
     94          * so the final rule is allowed to fail.
     95          * Bug 17629786 asks to make the failure more obvious, or even fatal
     96          * so that all builds eventually gain the performance improvement.
     97          */
     98         {{IPTABLES_PATH, "-F", LOCAL_TETHER_COUNTERS_CHAIN,}, 0},
     99         {{IPTABLES_PATH, "-X", LOCAL_TETHER_COUNTERS_CHAIN,}, 0},
    100         {{IPTABLES_PATH, "-N", LOCAL_TETHER_COUNTERS_CHAIN,}, 1},
    101         {{IPTABLES_PATH, "-t", "mangle", "-A", LOCAL_MANGLE_FORWARD, "-p", "tcp", "--tcp-flags",
    102                 "SYN", "SYN", "-j", "TCPMSS", "--clamp-mss-to-pmtu"}, 0},
    103     };
    104     for (unsigned int cmdNum = 0; cmdNum < ARRAY_SIZE(defaultCommands); cmdNum++) {
    105         if (runCmd(ARRAY_SIZE(defaultCommands[cmdNum].cmd), defaultCommands[cmdNum].cmd) &&
    106             defaultCommands[cmdNum].checkRes) {
    107                 return -1;
    108         }
    109     }
    110     ifacePairList.clear();
    111 
    112     return 0;
    113 }
    114 
    115 int NatController::setDefaults() {
    116     /*
    117      * The following only works because:
    118      *  - the defaultsCommands[].cmd array is padded with NULL, and
    119      *  - the 1st argc of runCmd() will just be the max for the CommandsAndArgs[].cmd, and
    120      *  - internally it will be memcopied to an array and terminated with a NULL.
    121      */
    122     struct CommandsAndArgs defaultCommands[] = {
    123         {{IPTABLES_PATH, "-F", LOCAL_FORWARD,}, 1},
    124         {{IPTABLES_PATH, "-A", LOCAL_FORWARD, "-j", "DROP"}, 1},
    125         {{IPTABLES_PATH, "-t", "nat", "-F", LOCAL_NAT_POSTROUTING}, 1},
    126     };
    127     for (unsigned int cmdNum = 0; cmdNum < ARRAY_SIZE(defaultCommands); cmdNum++) {
    128         if (runCmd(ARRAY_SIZE(defaultCommands[cmdNum].cmd), defaultCommands[cmdNum].cmd) &&
    129             defaultCommands[cmdNum].checkRes) {
    130                 return -1;
    131         }
    132     }
    133 
    134     natCount = 0;
    135 
    136     return 0;
    137 }
    138 
    139 int NatController::enableNat(const char* intIface, const char* extIface) {
    140     ALOGV("enableNat(intIface=<%s>, extIface=<%s>)",intIface, extIface);
    141 
    142     if (!isIfaceName(intIface) || !isIfaceName(extIface)) {
    143         errno = ENODEV;
    144         return -1;
    145     }
    146 
    147     /* Bug: b/9565268. "enableNat wlan0 wlan0". For now we fail until java-land is fixed */
    148     if (!strcmp(intIface, extIface)) {
    149         ALOGE("Duplicate interface specified: %s %s", intIface, extIface);
    150         errno = EINVAL;
    151         return -1;
    152     }
    153 
    154     // add this if we are the first added nat
    155     if (natCount == 0) {
    156         const char *cmd[] = {
    157                 IPTABLES_PATH,
    158                 "-t",
    159                 "nat",
    160                 "-A",
    161                 LOCAL_NAT_POSTROUTING,
    162                 "-o",
    163                 extIface,
    164                 "-j",
    165                 "MASQUERADE"
    166         };
    167         if (runCmd(ARRAY_SIZE(cmd), cmd)) {
    168             ALOGE("Error setting postroute rule: iface=%s", extIface);
    169             // unwind what's been done, but don't care about success - what more could we do?
    170             setDefaults();
    171             return -1;
    172         }
    173     }
    174 
    175     if (setForwardRules(true, intIface, extIface) != 0) {
    176         ALOGE("Error setting forward rules");
    177         if (natCount == 0) {
    178             setDefaults();
    179         }
    180         errno = ENODEV;
    181         return -1;
    182     }
    183 
    184     /* Always make sure the drop rule is at the end */
    185     const char *cmd1[] = {
    186             IPTABLES_PATH,
    187             "-D",
    188             LOCAL_FORWARD,
    189             "-j",
    190             "DROP"
    191     };
    192     runCmd(ARRAY_SIZE(cmd1), cmd1);
    193     const char *cmd2[] = {
    194             IPTABLES_PATH,
    195             "-A",
    196             LOCAL_FORWARD,
    197             "-j",
    198             "DROP"
    199     };
    200     runCmd(ARRAY_SIZE(cmd2), cmd2);
    201 
    202     if (int ret = RouteController::enableTethering(intIface, extIface)) {
    203         ALOGE("failed to add tethering rule for iif=%s oif=%s", intIface, extIface);
    204         if (natCount == 0) {
    205             setDefaults();
    206         }
    207         errno = -ret;
    208         return -1;
    209     }
    210 
    211     natCount++;
    212     return 0;
    213 }
    214 
    215 bool NatController::checkTetherCountingRuleExist(const char *pair_name) {
    216     std::list<std::string>::iterator it;
    217 
    218     for (it = ifacePairList.begin(); it != ifacePairList.end(); it++) {
    219         if (*it == pair_name) {
    220             /* We already have this counter */
    221             return true;
    222         }
    223     }
    224     return false;
    225 }
    226 
    227 int NatController::setTetherCountingRules(bool add, const char *intIface, const char *extIface) {
    228 
    229     /* We only ever add tethering quota rules so that they stick. */
    230     if (!add) {
    231         return 0;
    232     }
    233     char *pair_name;
    234     asprintf(&pair_name, "%s_%s", intIface, extIface);
    235 
    236     if (checkTetherCountingRuleExist(pair_name)) {
    237         free(pair_name);
    238         return 0;
    239     }
    240     const char *cmd2b[] = {
    241             IPTABLES_PATH,
    242             "-A",
    243             LOCAL_TETHER_COUNTERS_CHAIN,
    244             "-i",
    245             intIface,
    246             "-o",
    247             extIface,
    248             "-j",
    249           "RETURN"
    250     };
    251 
    252     if (runCmd(ARRAY_SIZE(cmd2b), cmd2b) && add) {
    253         free(pair_name);
    254         return -1;
    255     }
    256     ifacePairList.push_front(pair_name);
    257     free(pair_name);
    258 
    259     asprintf(&pair_name, "%s_%s", extIface, intIface);
    260     if (checkTetherCountingRuleExist(pair_name)) {
    261         free(pair_name);
    262         return 0;
    263     }
    264 
    265     const char *cmd3b[] = {
    266             IPTABLES_PATH,
    267             "-A",
    268             LOCAL_TETHER_COUNTERS_CHAIN,
    269             "-i",
    270             extIface,
    271             "-o",
    272             intIface,
    273             "-j",
    274             "RETURN"
    275     };
    276 
    277     if (runCmd(ARRAY_SIZE(cmd3b), cmd3b) && add) {
    278         // unwind what's been done, but don't care about success - what more could we do?
    279         free(pair_name);
    280         return -1;
    281     }
    282     ifacePairList.push_front(pair_name);
    283     free(pair_name);
    284     return 0;
    285 }
    286 
    287 int NatController::setForwardRules(bool add, const char *intIface, const char *extIface) {
    288     const char *cmd1[] = {
    289             IPTABLES_PATH,
    290             add ? "-A" : "-D",
    291             LOCAL_FORWARD,
    292             "-i",
    293             extIface,
    294             "-o",
    295             intIface,
    296             "-m",
    297             "state",
    298             "--state",
    299             "ESTABLISHED,RELATED",
    300             "-g",
    301             LOCAL_TETHER_COUNTERS_CHAIN
    302     };
    303     int rc = 0;
    304 
    305     if (runCmd(ARRAY_SIZE(cmd1), cmd1) && add) {
    306         return -1;
    307     }
    308 
    309     const char *cmd2[] = {
    310             IPTABLES_PATH,
    311             add ? "-A" : "-D",
    312             LOCAL_FORWARD,
    313             "-i",
    314             intIface,
    315             "-o",
    316             extIface,
    317             "-m",
    318             "state",
    319             "--state",
    320             "INVALID",
    321             "-j",
    322             "DROP"
    323     };
    324 
    325     const char *cmd3[] = {
    326             IPTABLES_PATH,
    327             add ? "-A" : "-D",
    328             LOCAL_FORWARD,
    329             "-i",
    330             intIface,
    331             "-o",
    332             extIface,
    333             "-g",
    334             LOCAL_TETHER_COUNTERS_CHAIN
    335     };
    336 
    337     if (runCmd(ARRAY_SIZE(cmd2), cmd2) && add) {
    338         // bail on error, but only if adding
    339         rc = -1;
    340         goto err_invalid_drop;
    341     }
    342 
    343     if (runCmd(ARRAY_SIZE(cmd3), cmd3) && add) {
    344         // unwind what's been done, but don't care about success - what more could we do?
    345         rc = -1;
    346         goto err_return;
    347     }
    348 
    349     if (setTetherCountingRules(add, intIface, extIface) && add) {
    350         rc = -1;
    351         goto err_return;
    352     }
    353 
    354     return 0;
    355 
    356 err_return:
    357     cmd2[1] = "-D";
    358     runCmd(ARRAY_SIZE(cmd2), cmd2);
    359 err_invalid_drop:
    360     cmd1[1] = "-D";
    361     runCmd(ARRAY_SIZE(cmd1), cmd1);
    362     return rc;
    363 }
    364 
    365 int NatController::disableNat(const char* intIface, const char* extIface) {
    366     if (!isIfaceName(intIface) || !isIfaceName(extIface)) {
    367         errno = ENODEV;
    368         return -1;
    369     }
    370 
    371     if (int ret = RouteController::disableTethering(intIface, extIface)) {
    372         ALOGE("failed to remove tethering rule for iif=%s oif=%s", intIface, extIface);
    373         errno = -ret;
    374         return -1;
    375     }
    376 
    377     setForwardRules(false, intIface, extIface);
    378     if (--natCount <= 0) {
    379         // handle decrement to 0 case (do reset to defaults) and erroneous dec below 0
    380         setDefaults();
    381     }
    382     return 0;
    383 }
    384