• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012 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 <set>
18 
19 #include <errno.h>
20 #include <limits.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <cstdint>
25 
26 #define LOG_TAG "FirewallController"
27 #define LOG_NDEBUG 0
28 
29 #include <android-base/file.h>
30 #include <android-base/stringprintf.h>
31 #include <android-base/strings.h>
32 #include <log/log.h>
33 
34 #include "Controllers.h"
35 #include "FirewallController.h"
36 #include "NetdConstants.h"
37 #include "bpf/BpfUtils.h"
38 
39 using android::base::Join;
40 using android::base::StringAppendF;
41 using android::base::StringPrintf;
42 
43 namespace android {
44 namespace net {
45 
46 auto FirewallController::execIptablesRestore = ::execIptablesRestore;
47 
48 const char* FirewallController::TABLE = "filter";
49 
50 const char* FirewallController::LOCAL_INPUT = "fw_INPUT";
51 const char* FirewallController::LOCAL_OUTPUT = "fw_OUTPUT";
52 const char* FirewallController::LOCAL_FORWARD = "fw_FORWARD";
53 
54 // ICMPv6 types that are required for any form of IPv6 connectivity to work. Note that because the
55 // fw_dozable chain is called from both INPUT and OUTPUT, this includes both packets that we need
56 // to be able to send (e.g., RS, NS), and packets that we need to receive (e.g., RA, NA).
57 const char* FirewallController::ICMPV6_TYPES[] = {
58     "packet-too-big",
59     "router-solicitation",
60     "router-advertisement",
61     "neighbour-solicitation",
62     "neighbour-advertisement",
63     "redirect",
64 };
65 
FirewallController(void)66 FirewallController::FirewallController(void) {
67     // If no rules are set, it's in DENYLIST mode
68     mFirewallType = DENYLIST;
69     mIfaceRules = {};
70 }
71 
setupIptablesHooks(void)72 int FirewallController::setupIptablesHooks(void) {
73     return flushRules();
74 }
75 
setFirewallType(FirewallType ftype)76 int FirewallController::setFirewallType(FirewallType ftype) {
77     int res = 0;
78     if (mFirewallType != ftype) {
79         // flush any existing rules
80         resetFirewall();
81 
82         if (ftype == ALLOWLIST) {
83             // create default rule to drop all traffic
84             std::string command =
85                 "*filter\n"
86                 "-A fw_INPUT -j DROP\n"
87                 "-A fw_OUTPUT -j REJECT\n"
88                 "-A fw_FORWARD -j REJECT\n"
89                 "COMMIT\n";
90             res = execIptablesRestore(V4V6, command.c_str());
91         }
92 
93         // Set this after calling disableFirewall(), since it defaults to ALLOWLIST there
94         mFirewallType = ftype;
95     }
96     return res ? -EREMOTEIO : 0;
97 }
98 
flushRules()99 int FirewallController::flushRules() {
100     std::string command =
101             "*filter\n"
102             ":fw_INPUT -\n"
103             ":fw_OUTPUT -\n"
104             ":fw_FORWARD -\n"
105             "-6 -A fw_OUTPUT ! -o lo -s ::1 -j DROP\n"
106             "COMMIT\n";
107 
108     return (execIptablesRestore(V4V6, command.c_str()) == 0) ? 0 : -EREMOTEIO;
109 }
110 
resetFirewall(void)111 int FirewallController::resetFirewall(void) {
112     mFirewallType = ALLOWLIST;
113     mIfaceRules.clear();
114     return flushRules();
115 }
116 
isFirewallEnabled(void)117 int FirewallController::isFirewallEnabled(void) {
118     // TODO: verify that rules are still in place near top
119     return -1;
120 }
121 
setInterfaceRule(const char * iface,FirewallRule rule)122 int FirewallController::setInterfaceRule(const char* iface, FirewallRule rule) {
123     if (mFirewallType == DENYLIST) {
124         // Unsupported in DENYLIST mode
125         return -EINVAL;
126     }
127 
128     if (!isIfaceName(iface)) {
129         errno = ENOENT;
130         return -ENOENT;
131     }
132 
133     // Only delete rules if we actually added them, because otherwise our iptables-restore
134     // processes will terminate with "no such rule" errors and cause latency penalties while we
135     // spin up new ones.
136     const char* op;
137     if (rule == ALLOW && mIfaceRules.find(iface) == mIfaceRules.end()) {
138         op = "-I";
139         mIfaceRules.insert(iface);
140     } else if (rule == DENY && mIfaceRules.find(iface) != mIfaceRules.end()) {
141         op = "-D";
142         mIfaceRules.erase(iface);
143     } else {
144         return 0;
145     }
146 
147     std::string command = Join(std::vector<std::string> {
148         "*filter",
149         StringPrintf("%s fw_INPUT -i %s -j RETURN", op, iface),
150         StringPrintf("%s fw_OUTPUT -o %s -j RETURN", op, iface),
151         "COMMIT\n"
152     }, "\n");
153     return (execIptablesRestore(V4V6, command) == 0) ? 0 : -EREMOTEIO;
154 }
155 
156 /* static */
makeCriticalCommands(IptablesTarget target,const char * chainName)157 std::string FirewallController::makeCriticalCommands(IptablesTarget target, const char* chainName) {
158     // Allow ICMPv6 packets necessary to make IPv6 connectivity work. http://b/23158230 .
159     std::string commands;
160     if (target == V6) {
161         for (size_t i = 0; i < ARRAY_SIZE(ICMPV6_TYPES); i++) {
162             StringAppendF(&commands, "-A %s -p icmpv6 --icmpv6-type %s -j RETURN\n",
163                    chainName, ICMPV6_TYPES[i]);
164         }
165     }
166     return commands;
167 }
168 
169 }  // namespace net
170 }  // namespace android
171