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 
     41 NatController::NatController(SecondaryTableController *ctrl) {
     42     secondaryTableCtrl = ctrl;
     43 }
     44 
     45 NatController::~NatController() {
     46 }
     47 
     48 struct CommandsAndArgs {
     49     /* The array size doesn't really matter as the compiler will barf if too many initializers are specified. */
     50     const char *cmd[32];
     51     bool checkRes;
     52 };
     53 
     54 int NatController::runCmd(int argc, const char **argv) {
     55     int res;
     56 
     57     res = android_fork_execvp(argc, (char **)argv, NULL, false, false);
     58     ALOGV("runCmd() res=%d", res);
     59     return res;
     60 }
     61 
     62 int NatController::setupIptablesHooks() {
     63     setDefaults();
     64     return 0;
     65 }
     66 
     67 int NatController::setDefaults() {
     68     struct CommandsAndArgs defaultCommands[] = {
     69         {{IPTABLES_PATH, "-F", "natctrl_FORWARD",}, 1},
     70         {{IPTABLES_PATH, "-A", "natctrl_FORWARD", "-j", "DROP"}, 1},
     71         {{IPTABLES_PATH, "-t", "nat", "-F", "natctrl_nat_POSTROUTING"}, 1},
     72         {{IP_PATH, "rule", "flush"}, 0},
     73         {{IP_PATH, "-6", "rule", "flush"}, 0},
     74         {{IP_PATH, "rule", "add", "from", "all", "lookup", "default", "prio", "32767"}, 0},
     75         {{IP_PATH, "rule", "add", "from", "all", "lookup", "main", "prio", "32766"}, 0},
     76         {{IP_PATH, "-6", "rule", "add", "from", "all", "lookup", "default", "prio", "32767"}, 0},
     77         {{IP_PATH, "-6", "rule", "add", "from", "all", "lookup", "main", "prio", "32766"}, 0},
     78         {{IP_PATH, "route", "flush", "cache"}, 0},
     79     };
     80     for (unsigned int cmdNum = 0; cmdNum < ARRAY_SIZE(defaultCommands); cmdNum++) {
     81         if (runCmd(ARRAY_SIZE(defaultCommands[cmdNum].cmd), defaultCommands[cmdNum].cmd) &&
     82             defaultCommands[cmdNum].checkRes) {
     83                 return -1;
     84         }
     85     }
     86 
     87     natCount = 0;
     88 
     89     return 0;
     90 }
     91 
     92 bool NatController::checkInterface(const char *iface) {
     93     if (strlen(iface) > IFNAMSIZ) return false;
     94     return true;
     95 }
     96 
     97 int NatController::routesOp(bool add, const char *intIface, const char *extIface, char **argv, int addrCount) {
     98     int tableNumber = secondaryTableCtrl->findTableNumber(extIface);
     99     int ret = 0;
    100 
    101     if (tableNumber != -1) {
    102         for (int i = 0; i < addrCount; i++) {
    103             if (add) {
    104                 ret |= secondaryTableCtrl->modifyFromRule(tableNumber, ADD, argv[5+i]);
    105                 ret |= secondaryTableCtrl->modifyLocalRoute(tableNumber, ADD, intIface, argv[5+i]);
    106             } else {
    107                 ret |= secondaryTableCtrl->modifyLocalRoute(tableNumber, DEL, intIface, argv[5+i]);
    108                 ret |= secondaryTableCtrl->modifyFromRule(tableNumber, DEL, argv[5+i]);
    109             }
    110         }
    111         const char *cmd[] = {
    112                 IP_PATH,
    113                 "route",
    114                 "flush",
    115                 "cache"
    116         };
    117         runCmd(ARRAY_SIZE(cmd), cmd);
    118     }
    119     return ret;
    120 }
    121 
    122 //  0    1       2       3       4            5
    123 // nat enable intface extface addrcnt nated-ipaddr/prelength
    124 int NatController::enableNat(const int argc, char **argv) {
    125     int i;
    126     int addrCount = atoi(argv[4]);
    127     const char *intIface = argv[2];
    128     const char *extIface = argv[3];
    129     int tableNumber;
    130 
    131     if (!checkInterface(intIface) || !checkInterface(extIface)) {
    132         ALOGE("Invalid interface specified");
    133         errno = ENODEV;
    134         return -1;
    135     }
    136 
    137     if (argc < 5 + addrCount) {
    138         ALOGE("Missing Argument");
    139         errno = EINVAL;
    140         return -1;
    141     }
    142     if (routesOp(true, intIface, extIface, argv, addrCount)) {
    143         ALOGE("Error setting route rules");
    144         routesOp(false, intIface, extIface, argv, addrCount);
    145         errno = ENODEV;
    146         return -1;
    147     }
    148 
    149     // add this if we are the first added nat
    150     if (natCount == 0) {
    151         const char *cmd[] = {
    152                 IPTABLES_PATH,
    153                 "-t",
    154                 "nat",
    155                 "-A",
    156                 "natctrl_nat_POSTROUTING",
    157                 "-o",
    158                 extIface,
    159                 "-j",
    160                 "MASQUERADE"
    161         };
    162         if (runCmd(ARRAY_SIZE(cmd), cmd)) {
    163             ALOGE("Error seting postroute rule: iface=%s", extIface);
    164             // unwind what's been done, but don't care about success - what more could we do?
    165             routesOp(false, intIface, extIface, argv, addrCount);
    166             setDefaults();
    167             return -1;
    168         }
    169     }
    170 
    171 
    172     if (setForwardRules(true, intIface, extIface) != 0) {
    173         ALOGE("Error setting forward rules");
    174         routesOp(false, intIface, extIface, argv, addrCount);
    175         if (natCount == 0) {
    176             setDefaults();
    177         }
    178         errno = ENODEV;
    179         return -1;
    180     }
    181 
    182     /* Always make sure the drop rule is at the end */
    183     const char *cmd1[] = {
    184             IPTABLES_PATH,
    185             "-D",
    186             "natctrl_FORWARD",
    187             "-j",
    188             "DROP"
    189     };
    190     runCmd(ARRAY_SIZE(cmd1), cmd1);
    191     const char *cmd2[] = {
    192             IPTABLES_PATH,
    193             "-A",
    194             "natctrl_FORWARD",
    195             "-j",
    196             "DROP"
    197     };
    198     runCmd(ARRAY_SIZE(cmd2), cmd2);
    199 
    200     natCount++;
    201     return 0;
    202 }
    203 
    204 int NatController::setForwardRules(bool add, const char *intIface, const char * extIface) {
    205     const char *cmd1[] = {
    206             IPTABLES_PATH,
    207             add ? "-A" : "-D",
    208             "natctrl_FORWARD",
    209             "-i",
    210             extIface,
    211             "-o",
    212             intIface,
    213             "-m",
    214             "state",
    215             "--state",
    216             "ESTABLISHED,RELATED",
    217             "-j",
    218             "RETURN"
    219     };
    220     int rc = 0;
    221 
    222     if (runCmd(ARRAY_SIZE(cmd1), cmd1) && add) {
    223         return -1;
    224     }
    225 
    226     const char *cmd2[] = {
    227             IPTABLES_PATH,
    228             add ? "-A" : "-D",
    229             "natctrl_FORWARD",
    230             "-i",
    231             intIface,
    232             "-o",
    233             extIface,
    234             "-m",
    235             "state",
    236             "--state",
    237             "INVALID",
    238             "-j",
    239             "DROP"
    240     };
    241 
    242     const char *cmd3[] = {
    243             IPTABLES_PATH,
    244             add ? "-A" : "-D",
    245             "natctrl_FORWARD",
    246             "-i",
    247             intIface,
    248             "-o",
    249             extIface,
    250             "-j",
    251             "RETURN"
    252     };
    253 
    254     if (runCmd(ARRAY_SIZE(cmd2), cmd2) && add) {
    255         // bail on error, but only if adding
    256         rc = -1;
    257         goto err_invalid_drop;
    258     }
    259 
    260     if (runCmd(ARRAY_SIZE(cmd3), cmd3) && add) {
    261         // unwind what's been done, but don't care about success - what more could we do?
    262         rc = -1;
    263         goto err_return;
    264     }
    265 
    266     return 0;
    267 
    268 err_return:
    269     cmd2[1] = "-D";
    270     runCmd(ARRAY_SIZE(cmd2), cmd2);
    271 err_invalid_drop:
    272     cmd1[1] = "-D";
    273     runCmd(ARRAY_SIZE(cmd1), cmd1);
    274     return rc;
    275 }
    276 
    277 // nat disable intface extface
    278 //  0    1       2       3       4            5
    279 // nat enable intface extface addrcnt nated-ipaddr/prelength
    280 int NatController::disableNat(const int argc, char **argv) {
    281     int i;
    282     int addrCount = atoi(argv[4]);
    283     const char *intIface = argv[2];
    284     const char *extIface = argv[3];
    285     int tableNumber;
    286 
    287     if (!checkInterface(intIface) || !checkInterface(extIface)) {
    288         ALOGE("Invalid interface specified");
    289         errno = ENODEV;
    290         return -1;
    291     }
    292 
    293     if (argc < 5 + addrCount) {
    294         ALOGE("Missing Argument");
    295         errno = EINVAL;
    296         return -1;
    297     }
    298 
    299     setForwardRules(false, intIface, extIface);
    300     routesOp(false, intIface, extIface, argv, addrCount);
    301     if (--natCount <= 0) {
    302         // handle decrement to 0 case (do reset to defaults) and erroneous dec below 0
    303         setDefaults();
    304     }
    305     return 0;
    306 }
    307