NatController.cpp revision 3c20787d7935c2016e8e3cc49d8f15647c12c41c
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 34extern "C" int system_nosh(const char *command); 35 36static char IPTABLES_PATH[] = "/system/bin/iptables"; 37static char IP_PATH[] = "/system/bin/ip"; 38 39NatController::NatController(SecondaryTableController *ctrl) { 40 secondaryTableCtrl = ctrl; 41 setDefaults(); 42} 43 44NatController::~NatController() { 45} 46 47int 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 64int 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 91bool NatController::checkInterface(const char *iface) { 92 if (strlen(iface) > IFNAMSIZ) return false; 93 return true; 94} 95 96const 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 106int 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 181int 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 229int 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