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