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