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