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
NatController(SecondaryTableController * ctrl)41 NatController::NatController(SecondaryTableController *ctrl) {
42 secondaryTableCtrl = ctrl;
43 }
44
~NatController()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
runCmd(int argc,const char ** argv)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
setupIptablesHooks()62 int NatController::setupIptablesHooks() {
63 setDefaults();
64 return 0;
65 }
66
setDefaults()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
checkInterface(const char * iface)92 bool NatController::checkInterface(const char *iface) {
93 if (strlen(iface) > IFNAMSIZ) return false;
94 return true;
95 }
96
routesOp(bool add,const char * intIface,const char * extIface,char ** argv,int addrCount)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
enableNat(const int argc,char ** argv)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
setForwardRules(bool add,const char * intIface,const char * extIface)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
disableNat(const int argc,char ** argv)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