OSDN Git Service

am 6aac1ccd: Merge "system: netd: prevent infinite loop"
[android-x86/system-netd.git] / SecondaryTableController.cpp
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 #include <stdlib.h>
18 #include <errno.h>
19 #include <fcntl.h>
20 #include <string.h>
21
22 #include <sys/socket.h>
23 #include <sys/stat.h>
24 #include <sys/types.h>
25 #include <sys/wait.h>
26
27 #include <netinet/in.h>
28 #include <arpa/inet.h>
29 #include <resolv_netid.h>
30
31 #define LOG_TAG "SecondaryTablController"
32 #include <cutils/log.h>
33 #include <cutils/properties.h>
34 #include <logwrap/logwrap.h>
35
36 #include "ResponseCode.h"
37 #include "NetdConstants.h"
38 #include "SecondaryTableController.h"
39
40 const char* SecondaryTableController::LOCAL_MANGLE_OUTPUT = "st_mangle_OUTPUT";
41 const char* SecondaryTableController::LOCAL_MANGLE_POSTROUTING = "st_mangle_POSTROUTING";
42 const char* SecondaryTableController::LOCAL_NAT_POSTROUTING = "st_nat_POSTROUTING";
43
44 SecondaryTableController::SecondaryTableController(NetworkController* controller) :
45         mNetCtrl(controller) {
46 }
47
48 SecondaryTableController::~SecondaryTableController() {
49 }
50
51 int SecondaryTableController::setupIptablesHooks() {
52     int res = execIptables(V4V6,
53             "-t",
54             "mangle",
55             "-F",
56             LOCAL_MANGLE_OUTPUT,
57             NULL);
58     // Do not mark sockets that have already been marked elsewhere(for example in DNS or protect).
59     res |= execIptables(V4V6,
60             "-t",
61             "mangle",
62             "-A",
63             LOCAL_MANGLE_OUTPUT,
64             "-m",
65             "mark",
66             "!",
67             "--mark",
68             "0",
69             "-j",
70             "RETURN",
71             NULL);
72
73     // protect the legacy VPN daemons from routes.
74     // TODO: Remove this when legacy VPN's are removed.
75     res |= execIptables(V4V6,
76             "-t",
77             "mangle",
78             "-A",
79             LOCAL_MANGLE_OUTPUT,
80             "-m",
81             "owner",
82             "--uid-owner",
83             "vpn",
84             "-j",
85             "RETURN",
86             NULL);
87     return res;
88 }
89
90 int SecondaryTableController::addRoute(SocketClient *cli, char *iface, char *dest, int prefix,
91         char *gateway) {
92     return modifyRoute(cli, ADD, iface, dest, prefix, gateway, mNetCtrl->getNetworkId(iface));
93 }
94
95 int SecondaryTableController::modifyRoute(SocketClient *cli, const char *action, char *iface,
96         char *dest, int prefix, char *gateway, unsigned netId) {
97     char dest_str[44]; // enough to store an IPv6 address + 3 character bitmask
98     char tableIndex_str[11];
99     int ret;
100
101     //  IP tool doesn't like "::" - the equiv of 0.0.0.0 that it accepts for ipv4
102     snprintf(dest_str, sizeof(dest_str), "%s/%d", dest, prefix);
103     snprintf(tableIndex_str, sizeof(tableIndex_str), "%u", netId + BASE_TABLE_NUMBER);
104
105     if (strcmp("::", gateway) == 0) {
106         const char *cmd[] = {
107                 IP_PATH,
108                 "route",
109                 action,
110                 dest_str,
111                 "dev",
112                 iface,
113                 "table",
114                 tableIndex_str
115         };
116         ret = runCmd(ARRAY_SIZE(cmd), cmd);
117     } else {
118         const char *cmd[] = {
119                 IP_PATH,
120                 "route",
121                 action,
122                 dest_str,
123                 "via",
124                 gateway,
125                 "dev",
126                 iface,
127                 "table",
128                 tableIndex_str
129         };
130         ret = runCmd(ARRAY_SIZE(cmd), cmd);
131     }
132
133     if (ret) {
134         ALOGE("ip route %s failed: %s route %s %s/%d via %s dev %s table %u", action,
135                 IP_PATH, action, dest, prefix, gateway, iface, netId + BASE_TABLE_NUMBER);
136         errno = ENODEV;
137         cli->sendMsg(ResponseCode::OperationFailed, "ip route modification failed", true);
138         return -1;
139     }
140
141     modifyRuleCount(netId, action);
142     cli->sendMsg(ResponseCode::CommandOkay, "Route modified", false);
143     return 0;
144 }
145
146 void SecondaryTableController::modifyRuleCount(unsigned netId, const char *action) {
147     if (strcmp(action, ADD) == 0) {
148         if (mNetIdRuleCount.count(netId) == 0)
149             mNetIdRuleCount[netId] = 0;
150         mNetIdRuleCount[netId]++;
151     } else {
152         if (mNetIdRuleCount.count(netId) > 0) {
153             if (--mNetIdRuleCount[netId] < 1) {
154                 mNetIdRuleCount.erase(mNetIdRuleCount.find(netId));
155             }
156         }
157     }
158 }
159
160 const char *SecondaryTableController::getVersion(const char *addr) {
161     if (strchr(addr, ':') != NULL) {
162         return "-6";
163     } else {
164         return "-4";
165     }
166 }
167
168 IptablesTarget SecondaryTableController::getIptablesTarget(const char *addr) {
169     if (strchr(addr, ':') != NULL) {
170         return V6;
171     } else {
172         return V4;
173     }
174 }
175
176 int SecondaryTableController::removeRoute(SocketClient *cli, char *iface, char *dest, int prefix,
177         char *gateway) {
178     return modifyRoute(cli, DEL, iface, dest, prefix, gateway, mNetCtrl->getNetworkId(iface));
179 }
180
181 int SecondaryTableController::modifyFromRule(unsigned netId, const char *action,
182         const char *addr) {
183     char tableIndex_str[11];
184
185     snprintf(tableIndex_str, sizeof(tableIndex_str), "%u", netId + BASE_TABLE_NUMBER);
186     const char *cmd[] = {
187             IP_PATH,
188             getVersion(addr),
189             "rule",
190             action,
191             "from",
192             addr,
193             "table",
194             tableIndex_str
195     };
196     if (runCmd(ARRAY_SIZE(cmd), cmd)) {
197         return -1;
198     }
199
200     modifyRuleCount(netId, action);
201     return 0;
202 }
203
204 int SecondaryTableController::modifyLocalRoute(unsigned netId, const char *action,
205         const char *iface, const char *addr) {
206     char tableIndex_str[11];
207
208     modifyRuleCount(netId, action); // some del's will fail as the iface is already gone.
209     snprintf(tableIndex_str, sizeof(tableIndex_str), "%u", netId + BASE_TABLE_NUMBER);
210     const char *cmd[] = {
211             IP_PATH,
212             "route",
213             action,
214             addr,
215             "dev",
216             iface,
217             "table",
218             tableIndex_str
219     };
220
221     return runCmd(ARRAY_SIZE(cmd), cmd);
222 }
223 int SecondaryTableController::addFwmarkRule(const char *iface) {
224     return setFwmarkRule(iface, true);
225 }
226
227 int SecondaryTableController::removeFwmarkRule(const char *iface) {
228     return setFwmarkRule(iface, false);
229 }
230
231 int SecondaryTableController::setFwmarkRule(const char *iface, bool add) {
232     unsigned netId = mNetCtrl->getNetworkId(iface);
233
234     // Fail fast if any rules already exist for this interface
235     if (mNetIdRuleCount.count(netId) > 0) {
236         errno = EBUSY;
237         return -1;
238     }
239
240     int ret;
241     char mark_str[11];
242     snprintf(mark_str, sizeof(mark_str), "%u", netId + BASE_TABLE_NUMBER);
243     // Flush any marked routes we added
244     if (!add) {
245         // iproute2 rule del will delete anything that matches, but only one rule at a time.
246         // So clearing the rules requires a bunch of calls.
247         // ip rule del will fail once there are no remaining rules that match.
248         const char *v4_cmd[] = {
249             IP_PATH,
250             "-4",
251             "rule",
252             "del",
253             "fwmark",
254             mark_str,
255             "table",
256             mark_str
257         };
258         while(!runCmd(ARRAY_SIZE(v4_cmd), v4_cmd)) {}
259
260         const char *v6_cmd[] = {
261             IP_PATH,
262             "-6",
263             "rule",
264             "del",
265             "fwmark",
266             mark_str,
267             "table",
268             mark_str
269         };
270         while(!runCmd(ARRAY_SIZE(v6_cmd), v6_cmd)) {}
271     }
272     // Add a route to the table to send all traffic to iface.
273     // We only need a default route because this table is only selected if a packet matches an
274     // IP rule that checks both the route and the mark.
275     const char *route_cmd[] = {
276         IP_PATH,
277         "route",
278         add ? "add" : "del",
279         "default",
280         "dev",
281         iface,
282         "table",
283         mark_str
284     };
285     ret = runCmd(ARRAY_SIZE(route_cmd), route_cmd);
286     // The command might fail during delete if the iface is gone
287     if (add && ret) return ret;
288
289     // As above for IPv6
290     const char *route6_cmd[] = {
291         IP_PATH,
292         "-6",
293         "route",
294         add ? "add" : "del",
295         "default",
296         "dev",
297         iface,
298         "table",
299         mark_str
300     };
301     ret = runCmd(ARRAY_SIZE(route6_cmd), route6_cmd);
302     // The command might fail during delete if the iface is gone
303     if (add && ret) return ret;
304
305     /* Best effort, because some kernels might not have the needed TCPMSS */
306     execIptables(V4V6,
307             "-t",
308             "mangle",
309             add ? "-A" : "-D",
310             LOCAL_MANGLE_POSTROUTING,
311             "-p", "tcp", "-o", iface, "--tcp-flags", "SYN,RST", "SYN",
312             "-j",
313             "TCPMSS",
314             "--clamp-mss-to-pmtu",
315             NULL);
316
317     // Because the mark gets set after the intial routing decision the source IP address is that
318     // of the original out interface. The only way to change the source IP address to that of the
319     // VPN iface is using source NAT.
320     // TODO: Remove this when we get the mark set correctly before the first routing pass.
321     ret = execIptables(V4,
322             "-t",
323             "nat",
324             add ? "-A" : "-D",
325             LOCAL_NAT_POSTROUTING,
326             "-o",
327             iface,
328             "-m",
329             "mark",
330             "--mark",
331             mark_str,
332             "-j",
333             "MASQUERADE",
334             NULL);
335
336     if (ret) return ret;
337
338     // Try and set up NAT for IPv6 as well. This was only added in Linux 3.7 so this may fail.
339     ret = execIptables(V6,
340             "-t",
341             "nat",
342             add ? "-A" : "-D",
343             LOCAL_NAT_POSTROUTING,
344             "-o",
345             iface,
346             "-m",
347             "mark",
348             "--mark",
349             mark_str,
350             "-j",
351             "MASQUERADE",
352             NULL);
353     if (ret) {
354         // Without V6 NAT we can't do V6 over VPNs. If an IPv6 packet matches a VPN rule, then it
355         // will go out on the VPN interface, but without NAT, it will have the wrong source
356         // address. So reject all these packets.
357         // Due to rule application by the time the connection hits the output filter chain the
358         // routing pass based on the new mark has not yet happened. Reject in ip instead.
359         // TODO: Make the VPN code refuse to install IPv6 routes until we don't need IPv6 NAT.
360         const char *reject_cmd[] = {
361             IP_PATH,
362             "-6",
363             "route",
364             add ? "replace" : "del",
365             "unreachable",
366             "default",
367             "table",
368             mark_str
369         };
370         ret = runCmd(ARRAY_SIZE(reject_cmd), reject_cmd);
371         // The command might fail during delete if the iface is gone
372         if (add && ret) return ret;
373
374     }
375     return 0;
376
377 }
378
379 int SecondaryTableController::addFwmarkRoute(const char* iface, const char *dest, int prefix) {
380     return setFwmarkRoute(iface, dest, prefix, true);
381 }
382
383 int SecondaryTableController::removeFwmarkRoute(const char* iface, const char *dest, int prefix) {
384     return setFwmarkRoute(iface, dest, prefix, false);
385 }
386
387 int SecondaryTableController::setFwmarkRoute(const char* iface, const char *dest, int prefix,
388                                              bool add) {
389     unsigned netId = mNetCtrl->getNetworkId(iface);
390     char mark_str[11] = {0};
391     char dest_str[44]; // enough to store an IPv6 address + 3 character bitmask
392
393     snprintf(mark_str, sizeof(mark_str), "%u", netId + BASE_TABLE_NUMBER);
394     snprintf(dest_str, sizeof(dest_str), "%s/%d", dest, prefix);
395     const char *rule_cmd[] = {
396         IP_PATH,
397         getVersion(dest_str),
398         "rule",
399         add ? "add" : "del",
400         "prio",
401         RULE_PRIO,
402         "to",
403         dest_str,
404         "fwmark",
405         mark_str,
406         "table",
407         mark_str
408     };
409     return runCmd(ARRAY_SIZE(rule_cmd), rule_cmd);
410 }
411
412 int SecondaryTableController::addUidRule(const char *iface, int uid_start, int uid_end) {
413     return setUidRule(iface, uid_start, uid_end, true);
414 }
415
416 int SecondaryTableController::removeUidRule(const char *iface, int uid_start, int uid_end) {
417     return setUidRule(iface, uid_start, uid_end, false);
418 }
419
420 int SecondaryTableController::setUidRule(const char *iface, int uid_start, int uid_end, bool add) {
421     unsigned netId = mNetCtrl->getNetworkId(iface);
422     if (!mNetCtrl->setNetworkForUidRange(uid_start, uid_end, add ? netId : 0, false)) {
423         errno = EINVAL;
424         return -1;
425     }
426
427     char uid_str[24] = {0};
428     snprintf(uid_str, sizeof(uid_str), "%d-%d", uid_start, uid_end);
429     char mark_str[11] = {0};
430     snprintf(mark_str, sizeof(mark_str), "%u", netId + BASE_TABLE_NUMBER);
431     return execIptables(V4V6,
432             "-t",
433             "mangle",
434             add ? "-A" : "-D",
435             LOCAL_MANGLE_OUTPUT,
436             "-m",
437             "owner",
438             "--uid-owner",
439             uid_str,
440             "-j",
441             "MARK",
442             "--set-mark",
443             mark_str,
444             NULL);
445 }
446
447 int SecondaryTableController::addHostExemption(const char *host) {
448     return setHostExemption(host, true);
449 }
450
451 int SecondaryTableController::removeHostExemption(const char *host) {
452     return setHostExemption(host, false);
453 }
454
455 int SecondaryTableController::setHostExemption(const char *host, bool add) {
456     const char *cmd[] = {
457         IP_PATH,
458         getVersion(host),
459         "rule",
460         add ? "add" : "del",
461         "prio",
462         EXEMPT_PRIO,
463         "to",
464         host,
465         "table",
466         "main"
467     };
468     return runCmd(ARRAY_SIZE(cmd), cmd);
469 }
470
471 void SecondaryTableController::getUidMark(SocketClient *cli, int uid) {
472     unsigned netId = mNetCtrl->getNetwork(uid, NETID_UNSET, NetworkController::PID_UNSPECIFIED,
473             false);
474     char mark_str[11];
475     snprintf(mark_str, sizeof(mark_str), "%u", netId + BASE_TABLE_NUMBER);
476     cli->sendMsg(ResponseCode::GetMarkResult, mark_str, false);
477 }
478
479 void SecondaryTableController::getProtectMark(SocketClient *cli) {
480     char protect_mark_str[11];
481     snprintf(protect_mark_str, sizeof(protect_mark_str), "%d", PROTECT_MARK);
482     cli->sendMsg(ResponseCode::GetMarkResult, protect_mark_str, false);
483 }
484
485 int SecondaryTableController::runCmd(int argc, const char **argv) {
486     int ret = 0;
487
488     ret = android_fork_execvp(argc, (char **)argv, NULL, false, false);
489     return ret;
490 }