• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "NetdConstants.h"
36  #include "RouteController.h"
37  
38  const char* NatController::LOCAL_FORWARD = "natctrl_FORWARD";
39  const char* NatController::LOCAL_MANGLE_FORWARD = "natctrl_mangle_FORWARD";
40  const char* NatController::LOCAL_NAT_POSTROUTING = "natctrl_nat_POSTROUTING";
41  const char* NatController::LOCAL_TETHER_COUNTERS_CHAIN = "natctrl_tether_counters";
42  
NatController()43  NatController::NatController() {
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           * First chain is for tethering counters.
88           * This chain is reached via --goto, and then RETURNS.
89           *
90           * Second chain is used to limit downstream mss to the upstream pmtu
91           * so we don't end up fragmenting every large packet tethered devices
92           * send.  Note this feature requires kernel support with flag
93           * CONFIG_NETFILTER_XT_TARGET_TCPMSS=y, which not all builds will have,
94           * so the final rule is allowed to fail.
95           * Bug 17629786 asks to make the failure more obvious, or even fatal
96           * so that all builds eventually gain the performance improvement.
97           */
98          {{IPTABLES_PATH, "-w", "-F", LOCAL_TETHER_COUNTERS_CHAIN,}, 0},
99          {{IPTABLES_PATH, "-w", "-X", LOCAL_TETHER_COUNTERS_CHAIN,}, 0},
100          {{IPTABLES_PATH, "-w", "-N", LOCAL_TETHER_COUNTERS_CHAIN,}, 1},
101          {{IPTABLES_PATH, "-w", "-t", "mangle", "-A", LOCAL_MANGLE_FORWARD, "-p", "tcp", "--tcp-flags",
102                  "SYN", "SYN", "-j", "TCPMSS", "--clamp-mss-to-pmtu"}, 0},
103      };
104      for (unsigned int cmdNum = 0; cmdNum < ARRAY_SIZE(defaultCommands); cmdNum++) {
105          if (runCmd(ARRAY_SIZE(defaultCommands[cmdNum].cmd), defaultCommands[cmdNum].cmd) &&
106              defaultCommands[cmdNum].checkRes) {
107                  return -1;
108          }
109      }
110      ifacePairList.clear();
111  
112      return 0;
113  }
114  
setDefaults()115  int NatController::setDefaults() {
116      /*
117       * The following only works because:
118       *  - the defaultsCommands[].cmd array is padded with NULL, and
119       *  - the 1st argc of runCmd() will just be the max for the CommandsAndArgs[].cmd, and
120       *  - internally it will be memcopied to an array and terminated with a NULL.
121       */
122      struct CommandsAndArgs defaultCommands[] = {
123          {{IPTABLES_PATH, "-w", "-F", LOCAL_FORWARD,}, 1},
124          {{IPTABLES_PATH, "-w", "-A", LOCAL_FORWARD, "-j", "DROP"}, 1},
125          {{IPTABLES_PATH, "-w", "-t", "nat", "-F", LOCAL_NAT_POSTROUTING}, 1},
126      };
127      for (unsigned int cmdNum = 0; cmdNum < ARRAY_SIZE(defaultCommands); cmdNum++) {
128          if (runCmd(ARRAY_SIZE(defaultCommands[cmdNum].cmd), defaultCommands[cmdNum].cmd) &&
129              defaultCommands[cmdNum].checkRes) {
130                  return -1;
131          }
132      }
133  
134      natCount = 0;
135  
136      return 0;
137  }
138  
enableNat(const char * intIface,const char * extIface)139  int NatController::enableNat(const char* intIface, const char* extIface) {
140      ALOGV("enableNat(intIface=<%s>, extIface=<%s>)",intIface, extIface);
141  
142      if (!isIfaceName(intIface) || !isIfaceName(extIface)) {
143          errno = ENODEV;
144          return -1;
145      }
146  
147      /* Bug: b/9565268. "enableNat wlan0 wlan0". For now we fail until java-land is fixed */
148      if (!strcmp(intIface, extIface)) {
149          ALOGE("Duplicate interface specified: %s %s", intIface, extIface);
150          errno = EINVAL;
151          return -1;
152      }
153  
154      // add this if we are the first added nat
155      if (natCount == 0) {
156          const char *cmd[] = {
157                  IPTABLES_PATH,
158                  "-w",
159                  "-t",
160                  "nat",
161                  "-A",
162                  LOCAL_NAT_POSTROUTING,
163                  "-o",
164                  extIface,
165                  "-j",
166                  "MASQUERADE"
167          };
168          if (runCmd(ARRAY_SIZE(cmd), cmd)) {
169              ALOGE("Error setting postroute rule: iface=%s", extIface);
170              // unwind what's been done, but don't care about success - what more could we do?
171              setDefaults();
172              return -1;
173          }
174      }
175  
176      if (setForwardRules(true, intIface, extIface) != 0) {
177          ALOGE("Error setting forward rules");
178          if (natCount == 0) {
179              setDefaults();
180          }
181          errno = ENODEV;
182          return -1;
183      }
184  
185      /* Always make sure the drop rule is at the end */
186      const char *cmd1[] = {
187              IPTABLES_PATH,
188              "-w",
189              "-D",
190              LOCAL_FORWARD,
191              "-j",
192              "DROP"
193      };
194      runCmd(ARRAY_SIZE(cmd1), cmd1);
195      const char *cmd2[] = {
196              IPTABLES_PATH,
197              "-w",
198              "-A",
199              LOCAL_FORWARD,
200              "-j",
201              "DROP"
202      };
203      runCmd(ARRAY_SIZE(cmd2), cmd2);
204  
205      natCount++;
206      return 0;
207  }
208  
checkTetherCountingRuleExist(const char * pair_name)209  bool NatController::checkTetherCountingRuleExist(const char *pair_name) {
210      std::list<std::string>::iterator it;
211  
212      for (it = ifacePairList.begin(); it != ifacePairList.end(); it++) {
213          if (*it == pair_name) {
214              /* We already have this counter */
215              return true;
216          }
217      }
218      return false;
219  }
220  
setTetherCountingRules(bool add,const char * intIface,const char * extIface)221  int NatController::setTetherCountingRules(bool add, const char *intIface, const char *extIface) {
222  
223      /* We only ever add tethering quota rules so that they stick. */
224      if (!add) {
225          return 0;
226      }
227      char *pair_name;
228      asprintf(&pair_name, "%s_%s", intIface, extIface);
229  
230      if (checkTetherCountingRuleExist(pair_name)) {
231          free(pair_name);
232          return 0;
233      }
234      const char *cmd2b[] = {
235              IPTABLES_PATH,
236              "-w",
237              "-A",
238              LOCAL_TETHER_COUNTERS_CHAIN,
239              "-i",
240              intIface,
241              "-o",
242              extIface,
243              "-j",
244            "RETURN"
245      };
246  
247      if (runCmd(ARRAY_SIZE(cmd2b), cmd2b) && add) {
248          free(pair_name);
249          return -1;
250      }
251      ifacePairList.push_front(pair_name);
252      free(pair_name);
253  
254      asprintf(&pair_name, "%s_%s", extIface, intIface);
255      if (checkTetherCountingRuleExist(pair_name)) {
256          free(pair_name);
257          return 0;
258      }
259  
260      const char *cmd3b[] = {
261              IPTABLES_PATH,
262              "-w",
263              "-A",
264              LOCAL_TETHER_COUNTERS_CHAIN,
265              "-i",
266              extIface,
267              "-o",
268              intIface,
269              "-j",
270              "RETURN"
271      };
272  
273      if (runCmd(ARRAY_SIZE(cmd3b), cmd3b) && add) {
274          // unwind what's been done, but don't care about success - what more could we do?
275          free(pair_name);
276          return -1;
277      }
278      ifacePairList.push_front(pair_name);
279      free(pair_name);
280      return 0;
281  }
282  
setForwardRules(bool add,const char * intIface,const char * extIface)283  int NatController::setForwardRules(bool add, const char *intIface, const char *extIface) {
284      const char *cmd1[] = {
285              IPTABLES_PATH,
286              "-w",
287              add ? "-A" : "-D",
288              LOCAL_FORWARD,
289              "-i",
290              extIface,
291              "-o",
292              intIface,
293              "-m",
294              "state",
295              "--state",
296              "ESTABLISHED,RELATED",
297              "-g",
298              LOCAL_TETHER_COUNTERS_CHAIN
299      };
300      int rc = 0;
301  
302      if (runCmd(ARRAY_SIZE(cmd1), cmd1) && add) {
303          return -1;
304      }
305  
306      const char *cmd2[] = {
307              IPTABLES_PATH,
308              "-w",
309              add ? "-A" : "-D",
310              LOCAL_FORWARD,
311              "-i",
312              intIface,
313              "-o",
314              extIface,
315              "-m",
316              "state",
317              "--state",
318              "INVALID",
319              "-j",
320              "DROP"
321      };
322  
323      const char *cmd3[] = {
324              IPTABLES_PATH,
325              "-w",
326              add ? "-A" : "-D",
327              LOCAL_FORWARD,
328              "-i",
329              intIface,
330              "-o",
331              extIface,
332              "-g",
333              LOCAL_TETHER_COUNTERS_CHAIN
334      };
335  
336      if (runCmd(ARRAY_SIZE(cmd2), cmd2) && add) {
337          // bail on error, but only if adding
338          rc = -1;
339          goto err_invalid_drop;
340      }
341  
342      if (runCmd(ARRAY_SIZE(cmd3), cmd3) && add) {
343          // unwind what's been done, but don't care about success - what more could we do?
344          rc = -1;
345          goto err_return;
346      }
347  
348      if (setTetherCountingRules(add, intIface, extIface) && add) {
349          rc = -1;
350          goto err_return;
351      }
352  
353      return 0;
354  
355  err_return:
356      cmd2[2] = "-D";
357      runCmd(ARRAY_SIZE(cmd2), cmd2);
358  err_invalid_drop:
359      cmd1[2] = "-D";
360      runCmd(ARRAY_SIZE(cmd1), cmd1);
361      return rc;
362  }
363  
disableNat(const char * intIface,const char * extIface)364  int NatController::disableNat(const char* intIface, const char* extIface) {
365      if (!isIfaceName(intIface) || !isIfaceName(extIface)) {
366          errno = ENODEV;
367          return -1;
368      }
369  
370      setForwardRules(false, intIface, extIface);
371      if (--natCount <= 0) {
372          // handle decrement to 0 case (do reset to defaults) and erroneous dec below 0
373          setDefaults();
374      }
375      return 0;
376  }
377