• 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 <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 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 
runCmd(const char * path,const char * cmd)48 int NatController::runCmd(const char *path, const char *cmd) {
49     char *buffer;
50     size_t len = strnlen(cmd, 255);
51     int res;
52 
53     if (len == 255) {
54         ALOGE("command too long");
55         errno = E2BIG;
56         return -1;
57     }
58 
59     asprintf(&buffer, "%s %s", path, cmd);
60     res = system_nosh(buffer);
61     ALOGV("runCmd() buffer='%s' res=%d", buffer, res);
62     free(buffer);
63     return res;
64 }
65 
setupIptablesHooks()66 int NatController::setupIptablesHooks() {
67     setDefaults();
68     return 0;
69 }
70 
setDefaults()71 int NatController::setDefaults() {
72     if (runCmd(IPTABLES_PATH, "-F natctrl_FORWARD"))
73         return -1;
74     if (runCmd(IPTABLES_PATH, "-t nat -F natctrl_nat_POSTROUTING"))
75         return -1;
76 
77     runCmd(IP_PATH, "rule flush");
78     runCmd(IP_PATH, "-6 rule flush");
79     runCmd(IP_PATH, "rule add from all lookup default prio 32767");
80     runCmd(IP_PATH, "rule add from all lookup main prio 32766");
81     runCmd(IP_PATH, "-6 rule add from all lookup default prio 32767");
82     runCmd(IP_PATH, "-6 rule add from all lookup main prio 32766");
83     runCmd(IP_PATH, "route flush cache");
84 
85     natCount = 0;
86 
87     return 0;
88 }
89 
checkInterface(const char * iface)90 bool NatController::checkInterface(const char *iface) {
91     if (strlen(iface) > IFNAMSIZ) return false;
92     return true;
93 }
94 
95 //  0    1       2       3       4            5
96 // nat enable intface extface addrcnt nated-ipaddr/prelength
enableNat(const int argc,char ** argv)97 int NatController::enableNat(const int argc, char **argv) {
98     char cmd[255];
99     int i;
100     int addrCount = atoi(argv[4]);
101     int ret = 0;
102     const char *intIface = argv[2];
103     const char *extIface = argv[3];
104     int tableNumber;
105 
106     if (!checkInterface(intIface) || !checkInterface(extIface)) {
107         ALOGE("Invalid interface specified");
108         errno = ENODEV;
109         return -1;
110     }
111 
112     if (argc < 5 + addrCount) {
113         ALOGE("Missing Argument");
114         errno = EINVAL;
115         return -1;
116     }
117 
118     tableNumber = secondaryTableCtrl->findTableNumber(extIface);
119     if (tableNumber != -1) {
120         for(i = 0; i < addrCount; i++) {
121             ret |= secondaryTableCtrl->modifyFromRule(tableNumber, ADD, argv[5+i]);
122 
123             ret |= secondaryTableCtrl->modifyLocalRoute(tableNumber, ADD, intIface, argv[5+i]);
124         }
125         runCmd(IP_PATH, "route flush cache");
126     }
127 
128     if (ret != 0 || setForwardRules(true, intIface, extIface) != 0) {
129         if (tableNumber != -1) {
130             for (i = 0; i < addrCount; i++) {
131                 secondaryTableCtrl->modifyLocalRoute(tableNumber, DEL, intIface, argv[5+i]);
132 
133                 secondaryTableCtrl->modifyFromRule(tableNumber, DEL, argv[5+i]);
134             }
135             runCmd(IP_PATH, "route flush cache");
136         }
137         ALOGE("Error setting forward rules");
138         errno = ENODEV;
139         return -1;
140     }
141 
142     /* Always make sure the drop rule is at the end */
143     snprintf(cmd, sizeof(cmd), "-D natctrl_FORWARD -j DROP");
144     runCmd(IPTABLES_PATH, cmd);
145     snprintf(cmd, sizeof(cmd), "-A natctrl_FORWARD -j DROP");
146     runCmd(IPTABLES_PATH, cmd);
147 
148 
149     natCount++;
150     // add this if we are the first added nat
151     if (natCount == 1) {
152         snprintf(cmd, sizeof(cmd), "-t nat -A natctrl_nat_POSTROUTING -o %s -j MASQUERADE", extIface);
153         if (runCmd(IPTABLES_PATH, cmd)) {
154             ALOGE("Error seting postroute rule: %s", cmd);
155             // unwind what's been done, but don't care about success - what more could we do?
156             for (i = 0; i < addrCount; i++) {
157                 secondaryTableCtrl->modifyLocalRoute(tableNumber, DEL, intIface, argv[5+i]);
158 
159                 secondaryTableCtrl->modifyFromRule(tableNumber, DEL, argv[5+i]);
160             }
161             setDefaults();
162             return -1;
163         }
164     }
165 
166     return 0;
167 }
168 
setForwardRules(bool add,const char * intIface,const char * extIface)169 int NatController::setForwardRules(bool add, const char *intIface, const char * extIface) {
170     char cmd[255];
171 
172     snprintf(cmd, sizeof(cmd),
173              "-%s natctrl_FORWARD -i %s -o %s -m state --state ESTABLISHED,RELATED -j RETURN",
174              (add ? "A" : "D"),
175              extIface, intIface);
176     if (runCmd(IPTABLES_PATH, cmd) && add) {
177         return -1;
178     }
179 
180     snprintf(cmd, sizeof(cmd),
181             "-%s natctrl_FORWARD -i %s -o %s -m state --state INVALID -j DROP",
182             (add ? "A" : "D"),
183             intIface, extIface);
184     if (runCmd(IPTABLES_PATH, cmd) && add) {
185         // bail on error, but only if adding
186         snprintf(cmd, sizeof(cmd),
187                 "-%s natctrl_FORWARD -i %s -o %s -m state --state ESTABLISHED,RELATED -j RETURN",
188                 (!add ? "A" : "D"),
189                 extIface, intIface);
190         runCmd(IPTABLES_PATH, cmd);
191         return -1;
192     }
193 
194     snprintf(cmd, sizeof(cmd), "-%s natctrl_FORWARD -i %s -o %s -j RETURN", (add ? "A" : "D"),
195             intIface, extIface);
196     if (runCmd(IPTABLES_PATH, cmd) && add) {
197         // unwind what's been done, but don't care about success - what more could we do?
198         snprintf(cmd, sizeof(cmd),
199                 "-%s natctrl_FORWARD -i %s -o %s -m state --state INVALID -j DROP",
200                 (!add ? "A" : "D"),
201                 intIface, extIface);
202         runCmd(IPTABLES_PATH, cmd);
203 
204         snprintf(cmd, sizeof(cmd),
205                  "-%s natctrl_FORWARD -i %s -o %s -m state --state ESTABLISHED,RELATED -j RETURN",
206                  (!add ? "A" : "D"),
207                  extIface, intIface);
208         runCmd(IPTABLES_PATH, cmd);
209         return -1;
210     }
211 
212     return 0;
213 }
214 
215 // nat disable intface extface
216 //  0    1       2       3       4            5
217 // nat enable intface extface addrcnt nated-ipaddr/prelength
disableNat(const int argc,char ** argv)218 int NatController::disableNat(const int argc, char **argv) {
219     char cmd[255];
220     int i;
221     int addrCount = atoi(argv[4]);
222     const char *intIface = argv[2];
223     const char *extIface = argv[3];
224     int tableNumber;
225 
226     if (!checkInterface(intIface) || !checkInterface(extIface)) {
227         ALOGE("Invalid interface specified");
228         errno = ENODEV;
229         return -1;
230     }
231 
232     if (argc < 5 + addrCount) {
233         ALOGE("Missing Argument");
234         errno = EINVAL;
235         return -1;
236     }
237 
238     setForwardRules(false, intIface, extIface);
239 
240     tableNumber = secondaryTableCtrl->findTableNumber(extIface);
241     if (tableNumber != -1) {
242         for (i = 0; i < addrCount; i++) {
243             secondaryTableCtrl->modifyLocalRoute(tableNumber, DEL, intIface, argv[5+i]);
244 
245             secondaryTableCtrl->modifyFromRule(tableNumber, DEL, argv[5+i]);
246         }
247 
248         runCmd(IP_PATH, "route flush cache");
249     }
250 
251     if (--natCount <= 0) {
252         // handle decrement to 0 case (do reset to defaults) and erroneous dec below 0
253         setDefaults();
254     }
255     return 0;
256 }
257