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 <errno.h> 18#include <stdio.h> 19#include <stdlib.h> 20#include <string.h> 21 22#define LOG_TAG "FirewallController" 23#define LOG_NDEBUG 0 24 25#include <cutils/log.h> 26#include <private/android_filesystem_config.h> 27 28#include "NetdConstants.h" 29#include "FirewallController.h" 30 31const char* FirewallController::TABLE = "filter"; 32 33const char* FirewallController::LOCAL_INPUT = "fw_INPUT"; 34const char* FirewallController::LOCAL_OUTPUT = "fw_OUTPUT"; 35const char* FirewallController::LOCAL_FORWARD = "fw_FORWARD"; 36 37const char* FirewallController::LOCAL_DOZABLE = "fw_dozable"; 38const char* FirewallController::LOCAL_STANDBY = "fw_standby"; 39 40// ICMPv6 types that are required for any form of IPv6 connectivity to work. Note that because the 41// fw_dozable chain is called from both INPUT and OUTPUT, this includes both packets that we need 42// to be able to send (e.g., RS, NS), and packets that we need to receive (e.g., RA, NA). 43const char* FirewallController::ICMPV6_TYPES[] = { 44 "packet-too-big", 45 "router-solicitation", 46 "router-advertisement", 47 "neighbour-solicitation", 48 "neighbour-advertisement", 49 "redirect", 50}; 51 52FirewallController::FirewallController(void) { 53 // If no rules are set, it's in BLACKLIST mode 54 mFirewallType = BLACKLIST; 55} 56 57int FirewallController::setupIptablesHooks(void) { 58 int res = 0; 59 // child chains are created but not attached, they will be attached explicitly. 60 FirewallType firewallType = getFirewallType(DOZABLE); 61 res |= createChain(LOCAL_DOZABLE, LOCAL_INPUT, firewallType); 62 63 firewallType = getFirewallType(STANDBY); 64 res |= createChain(LOCAL_STANDBY, LOCAL_INPUT, firewallType); 65 66 return res; 67} 68 69int FirewallController::enableFirewall(FirewallType ftype) { 70 int res = 0; 71 if (mFirewallType != ftype) { 72 // flush any existing rules 73 disableFirewall(); 74 75 if (ftype == WHITELIST) { 76 // create default rule to drop all traffic 77 res |= execIptables(V4V6, "-A", LOCAL_INPUT, "-j", "DROP", NULL); 78 res |= execIptables(V4V6, "-A", LOCAL_OUTPUT, "-j", "REJECT", NULL); 79 res |= execIptables(V4V6, "-A", LOCAL_FORWARD, "-j", "REJECT", NULL); 80 } 81 82 // Set this after calling disableFirewall(), since it defaults to WHITELIST there 83 mFirewallType = ftype; 84 } 85 return res; 86} 87 88int FirewallController::disableFirewall(void) { 89 int res = 0; 90 91 mFirewallType = WHITELIST; 92 93 // flush any existing rules 94 res |= execIptables(V4V6, "-F", LOCAL_INPUT, NULL); 95 res |= execIptables(V4V6, "-F", LOCAL_OUTPUT, NULL); 96 res |= execIptables(V4V6, "-F", LOCAL_FORWARD, NULL); 97 98 return res; 99} 100 101int FirewallController::enableChildChains(ChildChain chain, bool enable) { 102 int res = 0; 103 const char* name; 104 switch(chain) { 105 case DOZABLE: 106 name = LOCAL_DOZABLE; 107 break; 108 case STANDBY: 109 name = LOCAL_STANDBY; 110 break; 111 default: 112 return res; 113 } 114 115 if (enable) { 116 res |= attachChain(name, LOCAL_INPUT); 117 res |= attachChain(name, LOCAL_OUTPUT); 118 } else { 119 res |= detachChain(name, LOCAL_INPUT); 120 res |= detachChain(name, LOCAL_OUTPUT); 121 } 122 return res; 123} 124 125int FirewallController::isFirewallEnabled(void) { 126 // TODO: verify that rules are still in place near top 127 return -1; 128} 129 130int FirewallController::setInterfaceRule(const char* iface, FirewallRule rule) { 131 if (mFirewallType == BLACKLIST) { 132 // Unsupported in BLACKLIST mode 133 return -1; 134 } 135 136 if (!isIfaceName(iface)) { 137 errno = ENOENT; 138 return -1; 139 } 140 141 const char* op; 142 if (rule == ALLOW) { 143 op = "-I"; 144 } else { 145 op = "-D"; 146 } 147 148 int res = 0; 149 res |= execIptables(V4V6, op, LOCAL_INPUT, "-i", iface, "-j", "RETURN", NULL); 150 res |= execIptables(V4V6, op, LOCAL_OUTPUT, "-o", iface, "-j", "RETURN", NULL); 151 return res; 152} 153 154int FirewallController::setEgressSourceRule(const char* addr, FirewallRule rule) { 155 if (mFirewallType == BLACKLIST) { 156 // Unsupported in BLACKLIST mode 157 return -1; 158 } 159 160 IptablesTarget target = V4; 161 if (strchr(addr, ':')) { 162 target = V6; 163 } 164 165 const char* op; 166 if (rule == ALLOW) { 167 op = "-I"; 168 } else { 169 op = "-D"; 170 } 171 172 int res = 0; 173 res |= execIptables(target, op, LOCAL_INPUT, "-d", addr, "-j", "RETURN", NULL); 174 res |= execIptables(target, op, LOCAL_OUTPUT, "-s", addr, "-j", "RETURN", NULL); 175 return res; 176} 177 178int FirewallController::setEgressDestRule(const char* addr, int protocol, int port, 179 FirewallRule rule) { 180 if (mFirewallType == BLACKLIST) { 181 // Unsupported in BLACKLIST mode 182 return -1; 183 } 184 185 IptablesTarget target = V4; 186 if (strchr(addr, ':')) { 187 target = V6; 188 } 189 190 char protocolStr[16]; 191 sprintf(protocolStr, "%d", protocol); 192 193 char portStr[16]; 194 sprintf(portStr, "%d", port); 195 196 const char* op; 197 if (rule == ALLOW) { 198 op = "-I"; 199 } else { 200 op = "-D"; 201 } 202 203 int res = 0; 204 res |= execIptables(target, op, LOCAL_INPUT, "-s", addr, "-p", protocolStr, 205 "--sport", portStr, "-j", "RETURN", NULL); 206 res |= execIptables(target, op, LOCAL_OUTPUT, "-d", addr, "-p", protocolStr, 207 "--dport", portStr, "-j", "RETURN", NULL); 208 return res; 209} 210 211FirewallType FirewallController::getFirewallType(ChildChain chain) { 212 switch(chain) { 213 case DOZABLE: 214 return WHITELIST; 215 case STANDBY: 216 return BLACKLIST; 217 case NONE: 218 return mFirewallType; 219 default: 220 return BLACKLIST; 221 } 222} 223 224int FirewallController::setUidRule(ChildChain chain, int uid, FirewallRule rule) { 225 char uidStr[16]; 226 sprintf(uidStr, "%d", uid); 227 228 const char* op; 229 const char* target; 230 FirewallType firewallType = getFirewallType(chain); 231 if (firewallType == WHITELIST) { 232 target = "RETURN"; 233 op = (rule == ALLOW)? "-I" : "-D"; 234 } else { // BLACKLIST mode 235 target = "DROP"; 236 op = (rule == DENY)? "-I" : "-D"; 237 } 238 239 int res = 0; 240 switch(chain) { 241 case DOZABLE: 242 res |= execIptables(V4V6, op, LOCAL_DOZABLE, "-m", "owner", "--uid-owner", 243 uidStr, "-j", target, NULL); 244 break; 245 case STANDBY: 246 res |= execIptables(V4V6, op, LOCAL_STANDBY, "-m", "owner", "--uid-owner", 247 uidStr, "-j", target, NULL); 248 break; 249 case NONE: 250 res |= execIptables(V4V6, op, LOCAL_INPUT, "-m", "owner", "--uid-owner", uidStr, 251 "-j", target, NULL); 252 res |= execIptables(V4V6, op, LOCAL_OUTPUT, "-m", "owner", "--uid-owner", uidStr, 253 "-j", target, NULL); 254 break; 255 default: 256 ALOGW("Unknown child chain: %d", chain); 257 break; 258 } 259 return res; 260} 261 262int FirewallController::attachChain(const char* childChain, const char* parentChain) { 263 return execIptables(V4V6, "-t", TABLE, "-A", parentChain, "-j", childChain, NULL); 264} 265 266int FirewallController::detachChain(const char* childChain, const char* parentChain) { 267 return execIptables(V4V6, "-t", TABLE, "-D", parentChain, "-j", childChain, NULL); 268} 269 270int FirewallController::createChain(const char* childChain, 271 const char* parentChain, FirewallType type) { 272 // Order is important, otherwise later steps may fail. 273 execIptablesSilently(V4V6, "-t", TABLE, "-D", parentChain, "-j", childChain, NULL); 274 execIptablesSilently(V4V6, "-t", TABLE, "-F", childChain, NULL); 275 execIptablesSilently(V4V6, "-t", TABLE, "-X", childChain, NULL); 276 int res = 0; 277 res |= execIptables(V4V6, "-t", TABLE, "-N", childChain, NULL); 278 if (type == WHITELIST) { 279 // Allow ICMPv6 packets necessary to make IPv6 connectivity work. http://b/23158230 . 280 for (size_t i = 0; i < ARRAY_SIZE(ICMPV6_TYPES); i++) { 281 res |= execIptables(V6, "-A", childChain, "-p", "icmpv6", "--icmpv6-type", 282 ICMPV6_TYPES[i], "-j", "RETURN", NULL); 283 } 284 285 // create default white list for system uid range 286 char uidStr[16]; 287 sprintf(uidStr, "0-%d", AID_APP - 1); 288 res |= execIptables(V4V6, "-A", childChain, "-m", "owner", "--uid-owner", 289 uidStr, "-j", "RETURN", NULL); 290 291 // create default rule to drop all traffic 292 res |= execIptables(V4V6, "-A", childChain, "-j", "DROP", NULL); 293 } 294 return res; 295} 296