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