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