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 <android-base/stringprintf.h>
     32 #include <cutils/log.h>
     33 #include <logwrap/logwrap.h>
     34 
     35 #include "NetdConstants.h"
     36 #include "NatController.h"
     37 #include "NetdConstants.h"
     38 #include "RouteController.h"
     39 
     40 using android::base::StringPrintf;
     41 
     42 const char* NatController::LOCAL_FORWARD = "natctrl_FORWARD";
     43 const char* NatController::LOCAL_MANGLE_FORWARD = "natctrl_mangle_FORWARD";
     44 const char* NatController::LOCAL_NAT_POSTROUTING = "natctrl_nat_POSTROUTING";
     45 const char* NatController::LOCAL_RAW_PREROUTING = "natctrl_raw_PREROUTING";
     46 const char* NatController::LOCAL_TETHER_COUNTERS_CHAIN = "natctrl_tether_counters";
     47 
     48 auto NatController::execFunction = android_fork_execvp;
     49 auto NatController::iptablesRestoreFunction = execIptablesRestore;
     50 
     51 NatController::NatController() {
     52 }
     53 
     54 NatController::~NatController() {
     55 }
     56 
     57 struct CommandsAndArgs {
     58     /* The array size doesn't really matter as the compiler will barf if too many initializers are specified. */
     59     const char *cmd[32];
     60     bool checkRes;
     61 };
     62 
     63 int NatController::runCmd(int argc, const char **argv) {
     64     int res;
     65 
     66     res = execFunction(argc, (char **)argv, NULL, false, false);
     67 
     68 #if !LOG_NDEBUG
     69     std::string full_cmd = argv[0];
     70     argc--; argv++;
     71     /*
     72      * HACK: Sometimes runCmd() is called with a ridcously large value (32)
     73      * and it works because the argv[] contains a NULL after the last
     74      * true argv. So here we use the NULL argv[] to terminate when the argc
     75      * is horribly wrong, and argc for the normal cases.
     76      */
     77     for (; argc && argv[0]; argc--, argv++) {
     78         full_cmd += " ";
     79         full_cmd += argv[0];
     80     }
     81     ALOGV("runCmd(%s) res=%d", full_cmd.c_str(), res);
     82 #endif
     83     return res;
     84 }
     85 
     86 int NatController::setupIptablesHooks() {
     87     int res;
     88     res = setDefaults();
     89     if (res < 0) {
     90         return res;
     91     }
     92 
     93     // Used to limit downstream mss to the upstream pmtu so we don't end up fragmenting every large
     94     // packet tethered devices send. This is IPv4-only, because in IPv6 we send the MTU in the RA.
     95     // This is no longer optional and tethering will fail to start if it fails.
     96     std::string mssRewriteCommand = StringPrintf(
     97         "*mangle\n"
     98         "-A %s -p tcp --tcp-flags SYN SYN -j TCPMSS --clamp-mss-to-pmtu\n"
     99         "COMMIT\n", LOCAL_MANGLE_FORWARD);
    100 
    101     // This is for tethering counters. This chain is reached via --goto, and then RETURNS.
    102     std::string defaultCommands = StringPrintf(
    103         "*filter\n"
    104         ":%s -\n"
    105         "COMMIT\n", LOCAL_TETHER_COUNTERS_CHAIN);
    106 
    107     res = iptablesRestoreFunction(V4, mssRewriteCommand);
    108     if (res < 0) {
    109         return res;
    110     }
    111 
    112     res = iptablesRestoreFunction(V4V6, defaultCommands);
    113     if (res < 0) {
    114         return res;
    115     }
    116 
    117     ifacePairList.clear();
    118 
    119     return 0;
    120 }
    121 
    122 int NatController::setDefaults() {
    123     std::string v4Cmd = StringPrintf(
    124         "*filter\n"
    125         ":%s -\n"
    126         "-A %s -j DROP\n"
    127         "COMMIT\n"
    128         "*nat\n"
    129         ":%s -\n"
    130         "COMMIT\n", LOCAL_FORWARD, LOCAL_FORWARD, LOCAL_NAT_POSTROUTING);
    131 
    132     std::string v6Cmd = StringPrintf(
    133         "*filter\n"
    134         ":%s -\n"
    135         "COMMIT\n"
    136         "*raw\n"
    137         ":%s -\n"
    138         "COMMIT\n", LOCAL_FORWARD, LOCAL_RAW_PREROUTING);
    139 
    140     int res = iptablesRestoreFunction(V4, v4Cmd);
    141     if (res < 0) {
    142         return res;
    143     }
    144 
    145     res = iptablesRestoreFunction(V6, v6Cmd);
    146     if (res < 0) {
    147         return res;
    148     }
    149 
    150     natCount = 0;
    151 
    152     return 0;
    153 }
    154 
    155 int NatController::enableNat(const char* intIface, const char* extIface) {
    156     ALOGV("enableNat(intIface=<%s>, extIface=<%s>)",intIface, extIface);
    157 
    158     if (!isIfaceName(intIface) || !isIfaceName(extIface)) {
    159         errno = ENODEV;
    160         return -1;
    161     }
    162 
    163     /* Bug: b/9565268. "enableNat wlan0 wlan0". For now we fail until java-land is fixed */
    164     if (!strcmp(intIface, extIface)) {
    165         ALOGE("Duplicate interface specified: %s %s", intIface, extIface);
    166         errno = EINVAL;
    167         return -1;
    168     }
    169 
    170     // add this if we are the first added nat
    171     if (natCount == 0) {
    172         const char *v4Cmd[] = {
    173                 IPTABLES_PATH,
    174                 "-w",
    175                 "-t",
    176                 "nat",
    177                 "-A",
    178                 LOCAL_NAT_POSTROUTING,
    179                 "-o",
    180                 extIface,
    181                 "-j",
    182                 "MASQUERADE"
    183         };
    184 
    185         /*
    186          * IPv6 tethering doesn't need the state-based conntrack rules, so
    187          * it unconditionally jumps to the tether counters chain all the time.
    188          */
    189         const char *v6Cmd[] = {IP6TABLES_PATH, "-w", "-A", LOCAL_FORWARD,
    190                                "-g", LOCAL_TETHER_COUNTERS_CHAIN};
    191 
    192         if (runCmd(ARRAY_SIZE(v4Cmd), v4Cmd) || runCmd(ARRAY_SIZE(v6Cmd), v6Cmd)) {
    193             ALOGE("Error setting postroute rule: iface=%s", extIface);
    194             // unwind what's been done, but don't care about success - what more could we do?
    195             setDefaults();
    196             return -1;
    197         }
    198     }
    199 
    200     if (setForwardRules(true, intIface, extIface) != 0) {
    201         ALOGE("Error setting forward rules");
    202         if (natCount == 0) {
    203             setDefaults();
    204         }
    205         errno = ENODEV;
    206         return -1;
    207     }
    208 
    209     /* Always make sure the drop rule is at the end */
    210     const char *cmd1[] = {
    211             IPTABLES_PATH,
    212             "-w",
    213             "-D",
    214             LOCAL_FORWARD,
    215             "-j",
    216             "DROP"
    217     };
    218     runCmd(ARRAY_SIZE(cmd1), cmd1);
    219     const char *cmd2[] = {
    220             IPTABLES_PATH,
    221             "-w",
    222             "-A",
    223             LOCAL_FORWARD,
    224             "-j",
    225             "DROP"
    226     };
    227     runCmd(ARRAY_SIZE(cmd2), cmd2);
    228 
    229     natCount++;
    230     return 0;
    231 }
    232 
    233 bool NatController::checkTetherCountingRuleExist(const char *pair_name) {
    234     std::list<std::string>::iterator it;
    235 
    236     for (it = ifacePairList.begin(); it != ifacePairList.end(); it++) {
    237         if (*it == pair_name) {
    238             /* We already have this counter */
    239             return true;
    240         }
    241     }
    242     return false;
    243 }
    244 
    245 int NatController::setTetherCountingRules(bool add, const char *intIface, const char *extIface) {
    246 
    247     /* We only ever add tethering quota rules so that they stick. */
    248     if (!add) {
    249         return 0;
    250     }
    251     char *pair_name;
    252     asprintf(&pair_name, "%s_%s", intIface, extIface);
    253 
    254     if (checkTetherCountingRuleExist(pair_name)) {
    255         free(pair_name);
    256         return 0;
    257     }
    258     const char *cmd2b[] = {
    259         IPTABLES_PATH,
    260         "-w", "-A", LOCAL_TETHER_COUNTERS_CHAIN, "-i", intIface, "-o", extIface, "-j", "RETURN"
    261     };
    262 
    263     const char *cmd2c[] = {
    264         IP6TABLES_PATH,
    265         "-w", "-A", LOCAL_TETHER_COUNTERS_CHAIN, "-i", intIface, "-o", extIface, "-j", "RETURN"
    266     };
    267 
    268     if (runCmd(ARRAY_SIZE(cmd2b), cmd2b) || runCmd(ARRAY_SIZE(cmd2c), cmd2c)) {
    269         free(pair_name);
    270         return -1;
    271     }
    272     ifacePairList.push_front(pair_name);
    273     free(pair_name);
    274 
    275     asprintf(&pair_name, "%s_%s", extIface, intIface);
    276     if (checkTetherCountingRuleExist(pair_name)) {
    277         free(pair_name);
    278         return 0;
    279     }
    280 
    281     const char *cmd3b[] = {
    282         IPTABLES_PATH,
    283         "-w", "-A", LOCAL_TETHER_COUNTERS_CHAIN, "-i", extIface, "-o", intIface, "-j", "RETURN"
    284     };
    285 
    286     const char *cmd3c[] = {
    287         IP6TABLES_PATH,
    288         "-w", "-A", LOCAL_TETHER_COUNTERS_CHAIN, "-i", extIface, "-o", intIface, "-j", "RETURN"
    289     };
    290 
    291     if (runCmd(ARRAY_SIZE(cmd3b), cmd3b) || runCmd(ARRAY_SIZE(cmd3c), cmd3c)) {
    292         // unwind what's been done, but don't care about success - what more could we do?
    293         free(pair_name);
    294         return -1;
    295     }
    296     ifacePairList.push_front(pair_name);
    297     free(pair_name);
    298     return 0;
    299 }
    300 
    301 int NatController::setForwardRules(bool add, const char *intIface, const char *extIface) {
    302     const char *cmd1[] = {
    303             IPTABLES_PATH,
    304             "-w",
    305             add ? "-A" : "-D",
    306             LOCAL_FORWARD,
    307             "-i",
    308             extIface,
    309             "-o",
    310             intIface,
    311             "-m",
    312             "state",
    313             "--state",
    314             "ESTABLISHED,RELATED",
    315             "-g",
    316             LOCAL_TETHER_COUNTERS_CHAIN
    317     };
    318     int rc = 0;
    319 
    320     if (runCmd(ARRAY_SIZE(cmd1), cmd1) && add) {
    321         return -1;
    322     }
    323 
    324     const char *cmd2[] = {
    325             IPTABLES_PATH,
    326             "-w",
    327             add ? "-A" : "-D",
    328             LOCAL_FORWARD,
    329             "-i",
    330             intIface,
    331             "-o",
    332             extIface,
    333             "-m",
    334             "state",
    335             "--state",
    336             "INVALID",
    337             "-j",
    338             "DROP"
    339     };
    340 
    341     const char *cmd3[] = {
    342             IPTABLES_PATH,
    343             "-w",
    344             add ? "-A" : "-D",
    345             LOCAL_FORWARD,
    346             "-i",
    347             intIface,
    348             "-o",
    349             extIface,
    350             "-g",
    351             LOCAL_TETHER_COUNTERS_CHAIN
    352     };
    353 
    354     const char *cmd4[] = {
    355             IP6TABLES_PATH,
    356             "-w",
    357             "-t",
    358             "raw",
    359             add ? "-A" : "-D",
    360             LOCAL_RAW_PREROUTING,
    361             "-i",
    362             intIface,
    363             "-m",
    364             "rpfilter",
    365             "--invert",
    366             "!",
    367             "-s",
    368             "fe80::/64",
    369             "-j",
    370             "DROP"
    371     };
    372 
    373     if (runCmd(ARRAY_SIZE(cmd2), cmd2) && add) {
    374         // bail on error, but only if adding
    375         rc = -1;
    376         goto err_invalid_drop;
    377     }
    378 
    379     if (runCmd(ARRAY_SIZE(cmd3), cmd3) && add) {
    380         // unwind what's been done, but don't care about success - what more could we do?
    381         rc = -1;
    382         goto err_return;
    383     }
    384 
    385     if (runCmd(ARRAY_SIZE(cmd4), cmd4) && add) {
    386         rc = -1;
    387         goto err_rpfilter;
    388     }
    389 
    390     if (setTetherCountingRules(add, intIface, extIface) && add) {
    391         rc = -1;
    392         goto err_return;
    393     }
    394 
    395     return 0;
    396 
    397 err_rpfilter:
    398     cmd3[2] = "-D";
    399     runCmd(ARRAY_SIZE(cmd3), cmd3);
    400 err_return:
    401     cmd2[2] = "-D";
    402     runCmd(ARRAY_SIZE(cmd2), cmd2);
    403 err_invalid_drop:
    404     cmd1[2] = "-D";
    405     runCmd(ARRAY_SIZE(cmd1), cmd1);
    406     return rc;
    407 }
    408 
    409 int NatController::disableNat(const char* intIface, const char* extIface) {
    410     if (!isIfaceName(intIface) || !isIfaceName(extIface)) {
    411         errno = ENODEV;
    412         return -1;
    413     }
    414 
    415     setForwardRules(false, intIface, extIface);
    416     if (--natCount <= 0) {
    417         // handle decrement to 0 case (do reset to defaults) and erroneous dec below 0
    418         setDefaults();
    419     }
    420     return 0;
    421 }
    422