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 <sys/socket.h>
     20 #include <sys/stat.h>
     21 #include <fcntl.h>
     22 #include <netinet/in.h>
     23 #include <arpa/inet.h>
     24 #include <string.h>
     25 #include <cutils/properties.h>
     26 
     27 #define LOG_TAG "NatController"
     28 #include <cutils/log.h>
     29 
     30 #include "NatController.h"
     31 #include "SecondaryTableController.h"
     32 
     33 extern "C" int system_nosh(const char *command);
     34 
     35 static char IPTABLES_PATH[] = "/system/bin/iptables";
     36 static char IP_PATH[] = "/system/bin/ip";
     37 
     38 NatController::NatController(SecondaryTableController *ctrl) {
     39     secondaryTableCtrl = ctrl;
     40     setDefaults();
     41 }
     42 
     43 NatController::~NatController() {
     44 }
     45 
     46 int NatController::runCmd(const char *path, const char *cmd) {
     47     char *buffer;
     48     size_t len = strnlen(cmd, 255);
     49     int res;
     50 
     51     if (len == 255) {
     52         LOGE("command too long");
     53         errno = E2BIG;
     54         return -1;
     55     }
     56 
     57     asprintf(&buffer, "%s %s", path, cmd);
     58     res = system_nosh(buffer);
     59     free(buffer);
     60     return res;
     61 }
     62 
     63 int NatController::setDefaults() {
     64 
     65     if (runCmd(IPTABLES_PATH, "-P INPUT ACCEPT"))
     66         return -1;
     67     if (runCmd(IPTABLES_PATH, "-P OUTPUT ACCEPT"))
     68         return -1;
     69     if (runCmd(IPTABLES_PATH, "-P FORWARD DROP"))
     70         return -1;
     71     if (runCmd(IPTABLES_PATH, "-F FORWARD"))
     72         return -1;
     73     if (runCmd(IPTABLES_PATH, "-t nat -F"))
     74         return -1;
     75 
     76     runCmd(IP_PATH, "rule flush");
     77     runCmd(IP_PATH, "-6 rule flush");
     78     runCmd(IP_PATH, "rule add from all lookup default prio 32767");
     79     runCmd(IP_PATH, "rule add from all lookup main prio 32766");
     80     runCmd(IP_PATH, "-6 rule add from all lookup default prio 32767");
     81     runCmd(IP_PATH, "-6 rule add from all lookup main prio 32766");
     82     runCmd(IP_PATH, "route flush cache");
     83 
     84     natCount = 0;
     85     return 0;
     86 }
     87 
     88 bool NatController::checkInterface(const char *iface) {
     89     if (strlen(iface) > MAX_IFACE_LENGTH) return false;
     90     return true;
     91 }
     92 
     93 const char *NatController::getVersion(const char *addr) {
     94     if (strchr(addr, ':') != NULL) {
     95         return "-6";
     96     } else {
     97         return "-4";
     98     }
     99 }
    100 
    101 //  0    1       2       3       4            5
    102 // nat enable intface extface addrcnt nated-ipaddr/prelength
    103 int NatController::enableNat(const int argc, char **argv) {
    104     char cmd[255];
    105     int i;
    106     int addrCount = atoi(argv[4]);
    107     int ret = 0;
    108     const char *intIface = argv[2];
    109     const char *extIface = argv[3];
    110     int tableNumber;
    111 
    112     if (!checkInterface(intIface) || !checkInterface(extIface)) {
    113         LOGE("Invalid interface specified");
    114         errno = ENODEV;
    115         return -1;
    116     }
    117 
    118     if (argc < 5 + addrCount) {
    119         LOGE("Missing Argument");
    120         errno = EINVAL;
    121         return -1;
    122     }
    123 
    124     tableNumber = secondaryTableCtrl->findTableNumber(extIface);
    125     if (tableNumber != -1) {
    126         for(i = 0; i < addrCount && ret == 0; i++) {
    127             snprintf(cmd, sizeof(cmd), "%s rule add from %s table %d", getVersion(argv[5+i]),
    128                     argv[5+i], tableNumber + BASE_TABLE_NUMBER);
    129             ret |= runCmd(IP_PATH, cmd);
    130             if (ret) LOGE("IP rule %s got %d", cmd, ret);
    131 
    132             snprintf(cmd, sizeof(cmd), "route add %s dev %s table %d", argv[5+i], intIface,
    133                     tableNumber + BASE_TABLE_NUMBER);
    134             ret |= runCmd(IP_PATH, cmd);
    135             if (ret) LOGE("IP route %s got %d", cmd, ret);
    136         }
    137         runCmd(IP_PATH, "route flush cache");
    138     }
    139 
    140     if (ret != 0 || setForwardRules(true, intIface, extIface) != 0) {
    141         if (tableNumber != -1) {
    142             for (i = 0; i < addrCount; i++) {
    143                 snprintf(cmd, sizeof(cmd), "route del %s dev %s table %d", argv[5+i], intIface,
    144                         tableNumber + BASE_TABLE_NUMBER);
    145                 runCmd(IP_PATH, cmd);
    146 
    147                 snprintf(cmd, sizeof(cmd), "%s rule del from %s table %d", getVersion(argv[5+i]),
    148                         argv[5+i], tableNumber + BASE_TABLE_NUMBER);
    149                 runCmd(IP_PATH, cmd);
    150             }
    151             runCmd(IP_PATH, "route flush cache");
    152         }
    153         LOGE("Error setting forward rules");
    154         errno = ENODEV;
    155         return -1;
    156     }
    157 
    158     natCount++;
    159     // add this if we are the first added nat
    160     if (natCount == 1) {
    161         snprintf(cmd, sizeof(cmd), "-t nat -A POSTROUTING -o %s -j MASQUERADE", extIface);
    162         if (runCmd(IPTABLES_PATH, cmd)) {
    163             LOGE("Error seting postroute rule: %s", cmd);
    164             // unwind what's been done, but don't care about success - what more could we do?
    165             for (i = 0; i < addrCount; i++) {
    166                 snprintf(cmd, sizeof(cmd), "route del %s dev %s table %d", argv[5+i], intIface,
    167                         tableNumber + BASE_TABLE_NUMBER);
    168                 runCmd(IP_PATH, cmd);
    169             }
    170             setDefaults();
    171             return -1;
    172         }
    173     }
    174 
    175     return 0;
    176 }
    177 
    178 int NatController::setForwardRules(bool add, const char *intIface, const char * extIface) {
    179     char cmd[255];
    180 
    181     snprintf(cmd, sizeof(cmd),
    182              "-%s FORWARD -i %s -o %s -m state --state ESTABLISHED,RELATED -j ACCEPT",
    183              (add ? "A" : "D"),
    184              extIface, intIface);
    185     if (runCmd(IPTABLES_PATH, cmd) && add) {
    186         return -1;
    187     }
    188 
    189     snprintf(cmd, sizeof(cmd),
    190             "-%s FORWARD -i %s -o %s -m state --state INVALID -j DROP",
    191             (add ? "A" : "D"),
    192             intIface, extIface);
    193     if (runCmd(IPTABLES_PATH, cmd) && add) {
    194         // bail on error, but only if adding
    195         snprintf(cmd, sizeof(cmd),
    196                 "-%s FORWARD -i %s -o %s -m state --state ESTABLISHED,RELATED -j ACCEPT",
    197                 (!add ? "A" : "D"),
    198                 extIface, intIface);
    199         runCmd(IPTABLES_PATH, cmd);
    200         return -1;
    201     }
    202 
    203     snprintf(cmd, sizeof(cmd), "-%s FORWARD -i %s -o %s -j ACCEPT", (add ? "A" : "D"),
    204             intIface, extIface);
    205     if (runCmd(IPTABLES_PATH, cmd) && add) {
    206         // unwind what's been done, but don't care about success - what more could we do?
    207         snprintf(cmd, sizeof(cmd),
    208                 "-%s FORWARD -i %s -o %s -m state --state INVALID -j DROP",
    209                 (!add ? "A" : "D"),
    210                 intIface, extIface);
    211         runCmd(IPTABLES_PATH, cmd);
    212 
    213         snprintf(cmd, sizeof(cmd),
    214                  "-%s FORWARD -i %s -o %s -m state --state ESTABLISHED,RELATED -j ACCEPT",
    215                  (!add ? "A" : "D"),
    216                  extIface, intIface);
    217         runCmd(IPTABLES_PATH, cmd);
    218         return -1;
    219     }
    220     return 0;
    221 }
    222 
    223 // nat disable intface extface
    224 //  0    1       2       3       4            5
    225 // nat enable intface extface addrcnt nated-ipaddr/prelength
    226 int NatController::disableNat(const int argc, char **argv) {
    227     char cmd[255];
    228     int i;
    229     int addrCount = atoi(argv[4]);
    230     const char *intIface = argv[2];
    231     const char *extIface = argv[3];
    232     int tableNumber;
    233 
    234     if (!checkInterface(intIface) || !checkInterface(extIface)) {
    235         LOGE("Invalid interface specified");
    236         errno = ENODEV;
    237         return -1;
    238     }
    239 
    240     if (argc < 5 + addrCount) {
    241         LOGE("Missing Argument");
    242         errno = EINVAL;
    243         return -1;
    244     }
    245 
    246     setForwardRules(false, intIface, extIface);
    247 
    248     tableNumber = secondaryTableCtrl->findTableNumber(extIface);
    249     if (tableNumber != -1) {
    250         for (i = 0; i < addrCount; i++) {
    251             snprintf(cmd, sizeof(cmd), "route del %s dev %s table %d", argv[5+i], intIface,
    252                     tableNumber + BASE_TABLE_NUMBER);
    253             // if the interface has gone down these will be gone already and give errors
    254             // ignore them.
    255             runCmd(IP_PATH, cmd);
    256 
    257             snprintf(cmd, sizeof(cmd), "%s rule del from %s table %d", getVersion(argv[5+i]),
    258                     argv[5+i], tableNumber + BASE_TABLE_NUMBER);
    259             runCmd(IP_PATH, cmd);
    260         }
    261 
    262         runCmd(IP_PATH, "route flush cache");
    263     }
    264 
    265     if (--natCount <= 0) {
    266         char bootmode[PROPERTY_VALUE_MAX] = {0};
    267         property_get("ro.bootmode", bootmode, "unknown");
    268         if (0 != strcmp("bp-tools", bootmode)) {
    269             // handle decrement to 0 case (do reset to defaults) and erroneous dec below 0
    270             setDefaults();
    271         }
    272         natCount = 0;
    273     }
    274     return 0;
    275 }
    276