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 const char* NatController::LOCAL_TETHER_COUNTERS_CHAIN = "natctrl_tether_counters";
41
NatController(SecondaryTableController * ctrl)42 NatController::NatController(SecondaryTableController *ctrl) {
43 secondaryTableCtrl = ctrl;
44 }
45
~NatController()46 NatController::~NatController() {
47 }
48
49 struct CommandsAndArgs {
50 /* The array size doesn't really matter as the compiler will barf if too many initializers are specified. */
51 const char *cmd[32];
52 bool checkRes;
53 };
54
runCmd(int argc,const char ** argv)55 int NatController::runCmd(int argc, const char **argv) {
56 int res;
57
58 res = android_fork_execvp(argc, (char **)argv, NULL, false, false);
59
60 #if !LOG_NDEBUG
61 std::string full_cmd = argv[0];
62 argc--; argv++;
63 /*
64 * HACK: Sometimes runCmd() is called with a ridcously large value (32)
65 * and it works because the argv[] contains a NULL after the last
66 * true argv. So here we use the NULL argv[] to terminate when the argc
67 * is horribly wrong, and argc for the normal cases.
68 */
69 for (; argc && argv[0]; argc--, argv++) {
70 full_cmd += " ";
71 full_cmd += argv[0];
72 }
73 ALOGV("runCmd(%s) res=%d", full_cmd.c_str(), res);
74 #endif
75 return res;
76 }
77
setupIptablesHooks()78 int NatController::setupIptablesHooks() {
79 int res;
80 res = setDefaults();
81 if (res < 0) {
82 return res;
83 }
84
85 struct CommandsAndArgs defaultCommands[] = {
86 /*
87 * Chain for tethering counters.
88 * This chain is reached via --goto, and then RETURNS.
89 */
90 {{IPTABLES_PATH, "-F", LOCAL_TETHER_COUNTERS_CHAIN,}, 0},
91 {{IPTABLES_PATH, "-X", LOCAL_TETHER_COUNTERS_CHAIN,}, 0},
92 {{IPTABLES_PATH, "-N", LOCAL_TETHER_COUNTERS_CHAIN,}, 1},
93 };
94 for (unsigned int cmdNum = 0; cmdNum < ARRAY_SIZE(defaultCommands); cmdNum++) {
95 if (runCmd(ARRAY_SIZE(defaultCommands[cmdNum].cmd), defaultCommands[cmdNum].cmd) &&
96 defaultCommands[cmdNum].checkRes) {
97 return -1;
98 }
99 }
100
101 return 0;
102 }
103
setDefaults()104 int NatController::setDefaults() {
105 /*
106 * The following only works because:
107 * - the defaultsCommands[].cmd array is padded with NULL, and
108 * - the 1st argc of runCmd() will just be the max for the CommandsAndArgs[].cmd, and
109 * - internally it will be memcopied to an array and terminated with a NULL.
110 */
111 struct CommandsAndArgs defaultCommands[] = {
112 {{IPTABLES_PATH, "-F", LOCAL_FORWARD,}, 1},
113 {{IPTABLES_PATH, "-A", LOCAL_FORWARD, "-j", "DROP"}, 1},
114 {{IPTABLES_PATH, "-t", "nat", "-F", LOCAL_NAT_POSTROUTING}, 1},
115 {{IP_PATH, "rule", "flush"}, 0},
116 {{IP_PATH, "-6", "rule", "flush"}, 0},
117 {{IP_PATH, "rule", "add", "from", "all", "lookup", "default", "prio", "32767"}, 0},
118 {{IP_PATH, "rule", "add", "from", "all", "lookup", "main", "prio", "32766"}, 0},
119 {{IP_PATH, "-6", "rule", "add", "from", "all", "lookup", "default", "prio", "32767"}, 0},
120 {{IP_PATH, "-6", "rule", "add", "from", "all", "lookup", "main", "prio", "32766"}, 0},
121 {{IP_PATH, "route", "flush", "cache"}, 0},
122 };
123 for (unsigned int cmdNum = 0; cmdNum < ARRAY_SIZE(defaultCommands); cmdNum++) {
124 if (runCmd(ARRAY_SIZE(defaultCommands[cmdNum].cmd), defaultCommands[cmdNum].cmd) &&
125 defaultCommands[cmdNum].checkRes) {
126 return -1;
127 }
128 }
129
130 natCount = 0;
131
132 return 0;
133 }
134
checkInterface(const char * iface)135 bool NatController::checkInterface(const char *iface) {
136 if (strlen(iface) > IFNAMSIZ) return false;
137 return true;
138 }
139
routesOp(bool add,const char * intIface,const char * extIface,char ** argv,int addrCount)140 int NatController::routesOp(bool add, const char *intIface, const char *extIface, char **argv, int addrCount) {
141 int tableNumber = secondaryTableCtrl->findTableNumber(extIface);
142 int ret = 0;
143
144 if (tableNumber != -1) {
145 for (int i = 0; i < addrCount; i++) {
146 if (add) {
147 ret |= secondaryTableCtrl->modifyFromRule(tableNumber, ADD, argv[5+i]);
148 ret |= secondaryTableCtrl->modifyLocalRoute(tableNumber, ADD, intIface, argv[5+i]);
149 } else {
150 ret |= secondaryTableCtrl->modifyLocalRoute(tableNumber, DEL, intIface, argv[5+i]);
151 ret |= secondaryTableCtrl->modifyFromRule(tableNumber, DEL, argv[5+i]);
152 }
153 }
154 const char *cmd[] = {
155 IP_PATH,
156 "route",
157 "flush",
158 "cache"
159 };
160 runCmd(ARRAY_SIZE(cmd), cmd);
161 }
162 return ret;
163 }
164
165 // 0 1 2 3 4 5
166 // nat enable intface extface addrcnt nated-ipaddr/prelength
enableNat(const int argc,char ** argv)167 int NatController::enableNat(const int argc, char **argv) {
168 int i;
169 int addrCount = atoi(argv[4]);
170 const char *intIface = argv[2];
171 const char *extIface = argv[3];
172 int tableNumber;
173
174 ALOGV("enableNat(intIface=<%s>, extIface=<%s>)",intIface, extIface);
175
176 if (!checkInterface(intIface) || !checkInterface(extIface)) {
177 ALOGE("Invalid interface specified");
178 errno = ENODEV;
179 return -1;
180 }
181
182 /* Bug: b/9565268. "enableNat wlan0 wlan0". For now we fail until java-land is fixed */
183 if (!strcmp(intIface, extIface)) {
184 ALOGE("Duplicate interface specified: %s %s", intIface, extIface);
185 errno = EINVAL;
186 return -1;
187 }
188
189 if (argc < 5 + addrCount) {
190 ALOGE("Missing Argument");
191 errno = EINVAL;
192 return -1;
193 }
194 if (routesOp(true, intIface, extIface, argv, addrCount)) {
195 ALOGE("Error setting route rules");
196 routesOp(false, intIface, extIface, argv, addrCount);
197 errno = ENODEV;
198 return -1;
199 }
200
201 // add this if we are the first added nat
202 if (natCount == 0) {
203 const char *cmd[] = {
204 IPTABLES_PATH,
205 "-t",
206 "nat",
207 "-A",
208 LOCAL_NAT_POSTROUTING,
209 "-o",
210 extIface,
211 "-j",
212 "MASQUERADE"
213 };
214 if (runCmd(ARRAY_SIZE(cmd), cmd)) {
215 ALOGE("Error seting postroute rule: iface=%s", extIface);
216 // unwind what's been done, but don't care about success - what more could we do?
217 routesOp(false, intIface, extIface, argv, addrCount);
218 setDefaults();
219 return -1;
220 }
221 }
222
223
224 if (setForwardRules(true, intIface, extIface) != 0) {
225 ALOGE("Error setting forward rules");
226 routesOp(false, intIface, extIface, argv, addrCount);
227 if (natCount == 0) {
228 setDefaults();
229 }
230 errno = ENODEV;
231 return -1;
232 }
233
234 /* Always make sure the drop rule is at the end */
235 const char *cmd1[] = {
236 IPTABLES_PATH,
237 "-D",
238 LOCAL_FORWARD,
239 "-j",
240 "DROP"
241 };
242 runCmd(ARRAY_SIZE(cmd1), cmd1);
243 const char *cmd2[] = {
244 IPTABLES_PATH,
245 "-A",
246 LOCAL_FORWARD,
247 "-j",
248 "DROP"
249 };
250 runCmd(ARRAY_SIZE(cmd2), cmd2);
251
252 natCount++;
253 return 0;
254 }
255
setTetherCountingRules(bool add,const char * intIface,const char * extIface)256 int NatController::setTetherCountingRules(bool add, const char *intIface, const char *extIface) {
257
258 /* We only ever add tethering quota rules so that they stick. */
259 if (!add) {
260 return 0;
261 }
262 char *quota_name, *proc_path;
263 int quota_fd;
264 asprintf("a_name, "%s_%s", intIface, extIface);
265
266 asprintf(&proc_path, "/proc/net/xt_quota/%s", quota_name);
267 quota_fd = open(proc_path, O_RDONLY);
268 if (quota_fd >= 0) {
269 /* quota for iface pair already exists */
270 free(proc_path);
271 free(quota_name);
272 return 0;
273 }
274 close(quota_fd);
275 free(proc_path);
276
277 const char *cmd2b[] = {
278 IPTABLES_PATH,
279 "-A",
280 LOCAL_TETHER_COUNTERS_CHAIN,
281 "-i",
282 intIface,
283 "-o",
284 extIface,
285 "-m",
286 "quota2",
287 "--name",
288 quota_name,
289 "--grow",
290 "-j",
291 "RETURN"
292 };
293
294 if (runCmd(ARRAY_SIZE(cmd2b), cmd2b) && add) {
295 free(quota_name);
296 return -1;
297 }
298 free(quota_name);
299
300 asprintf("a_name, "%s_%s", extIface, intIface);
301 asprintf(&proc_path, "/proc/net/xt_quota/%s", quota_name);
302 quota_fd = open(proc_path, O_RDONLY);
303 if (quota_fd >= 0) {
304 /* quota for iface pair already exists */
305 free(proc_path);
306 free(quota_name);
307 return 0;
308 }
309 close(quota_fd);
310 free(proc_path);
311
312 const char *cmd3b[] = {
313 IPTABLES_PATH,
314 "-A",
315 LOCAL_TETHER_COUNTERS_CHAIN,
316 "-i",
317 extIface,
318 "-o",
319 intIface,
320 "-m",
321 "quota2",
322 "--name",
323 quota_name,
324 "--grow",
325 "-j",
326 "RETURN"
327 };
328
329 if (runCmd(ARRAY_SIZE(cmd3b), cmd3b) && add) {
330 // unwind what's been done, but don't care about success - what more could we do?
331 free(quota_name);
332 return -1;
333 }
334 free(quota_name);
335 return 0;
336 }
337
setForwardRules(bool add,const char * intIface,const char * extIface)338 int NatController::setForwardRules(bool add, const char *intIface, const char *extIface) {
339 const char *cmd1[] = {
340 IPTABLES_PATH,
341 add ? "-A" : "-D",
342 LOCAL_FORWARD,
343 "-i",
344 extIface,
345 "-o",
346 intIface,
347 "-m",
348 "state",
349 "--state",
350 "ESTABLISHED,RELATED",
351 "-g",
352 LOCAL_TETHER_COUNTERS_CHAIN
353 };
354 int rc = 0;
355
356 if (runCmd(ARRAY_SIZE(cmd1), cmd1) && add) {
357 return -1;
358 }
359
360 const char *cmd2[] = {
361 IPTABLES_PATH,
362 add ? "-A" : "-D",
363 LOCAL_FORWARD,
364 "-i",
365 intIface,
366 "-o",
367 extIface,
368 "-m",
369 "state",
370 "--state",
371 "INVALID",
372 "-j",
373 "DROP"
374 };
375
376 const char *cmd3[] = {
377 IPTABLES_PATH,
378 add ? "-A" : "-D",
379 LOCAL_FORWARD,
380 "-i",
381 intIface,
382 "-o",
383 extIface,
384 "-g",
385 LOCAL_TETHER_COUNTERS_CHAIN
386 };
387
388 if (runCmd(ARRAY_SIZE(cmd2), cmd2) && add) {
389 // bail on error, but only if adding
390 rc = -1;
391 goto err_invalid_drop;
392 }
393
394 if (runCmd(ARRAY_SIZE(cmd3), cmd3) && add) {
395 // unwind what's been done, but don't care about success - what more could we do?
396 rc = -1;
397 goto err_return;
398 }
399
400 if (setTetherCountingRules(add, intIface, extIface) && add) {
401 rc = -1;
402 goto err_return;
403 }
404
405 return 0;
406
407 err_return:
408 cmd2[1] = "-D";
409 runCmd(ARRAY_SIZE(cmd2), cmd2);
410 err_invalid_drop:
411 cmd1[1] = "-D";
412 runCmd(ARRAY_SIZE(cmd1), cmd1);
413 return rc;
414 }
415
416 // nat disable intface extface
417 // 0 1 2 3 4 5
418 // nat enable intface extface addrcnt nated-ipaddr/prelength
disableNat(const int argc,char ** argv)419 int NatController::disableNat(const int argc, char **argv) {
420 int i;
421 int addrCount = atoi(argv[4]);
422 const char *intIface = argv[2];
423 const char *extIface = argv[3];
424 int tableNumber;
425
426 if (!checkInterface(intIface) || !checkInterface(extIface)) {
427 ALOGE("Invalid interface specified");
428 errno = ENODEV;
429 return -1;
430 }
431
432 if (argc < 5 + addrCount) {
433 ALOGE("Missing Argument");
434 errno = EINVAL;
435 return -1;
436 }
437
438 setForwardRules(false, intIface, extIface);
439 routesOp(false, intIface, extIface, argv, addrCount);
440 if (--natCount <= 0) {
441 // handle decrement to 0 case (do reset to defaults) and erroneous dec below 0
442 setDefaults();
443 }
444 return 0;
445 }
446