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