/* * Copyright (C) 2012 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #define LOG_TAG "Netd" #include #include #include "NetdConstants.h" const char * const OEM_SCRIPT_PATH = "/system/bin/oem-iptables-init.sh"; const char * const IPTABLES_PATH = "/system/bin/iptables"; const char * const IP6TABLES_PATH = "/system/bin/ip6tables"; const char * const IPTABLES_RESTORE_PATH = "/system/bin/iptables-restore"; const char * const IP6TABLES_RESTORE_PATH = "/system/bin/ip6tables-restore"; const char * const TC_PATH = "/system/bin/tc"; const char * const IP_PATH = "/system/bin/ip"; const char * const ADD = "add"; const char * const DEL = "del"; static void logExecError(const char* argv[], int res, int status) { const char** argp = argv; std::string args = ""; while (*argp) { args += *argp; args += ' '; argp++; } ALOGE("exec() res=%d, status=%d for %s", res, status, args.c_str()); } static int execIptablesCommand(int argc, const char *argv[], bool silent) { int res; int status; res = android_fork_execvp(argc, (char **)argv, &status, false, !silent); if (res || !WIFEXITED(status) || WEXITSTATUS(status)) { if (!silent) { logExecError(argv, res, status); } if (res) return res; if (!WIFEXITED(status)) return ECHILD; } return WEXITSTATUS(status); } static int execIptables(IptablesTarget target, bool silent, va_list args) { /* Read arguments from incoming va_list; we expect the list to be NULL terminated. */ std::list argsList; argsList.push_back(NULL); const char* arg; // Wait to avoid failure due to another process holding the lock argsList.push_back("-w"); do { arg = va_arg(args, const char *); argsList.push_back(arg); } while (arg); int i = 0; const char* argv[argsList.size()]; std::list::iterator it; for (it = argsList.begin(); it != argsList.end(); it++, i++) { argv[i] = *it; } int res = 0; if (target == V4 || target == V4V6) { argv[0] = IPTABLES_PATH; res |= execIptablesCommand(argsList.size(), argv, silent); } if (target == V6 || target == V4V6) { argv[0] = IP6TABLES_PATH; res |= execIptablesCommand(argsList.size(), argv, silent); } return res; } int execIptables(IptablesTarget target, ...) { va_list args; va_start(args, target); int res = execIptables(target, false, args); va_end(args); return res; } int execIptablesSilently(IptablesTarget target, ...) { va_list args; va_start(args, target); int res = execIptables(target, true, args); va_end(args); return res; } static int execIptablesRestoreCommand(const char *cmd, const std::string& commands) { const char *argv[] = { cmd, "--noflush", // Don't flush the whole table. "-w", // Wait instead of failing if the lock is held. }; AndroidForkExecvpOption opt[1] = { { .opt_type = FORK_EXECVP_OPTION_INPUT, .opt_input.input = reinterpret_cast(commands.c_str()), .opt_input.input_len = commands.size(), } }; int status = 0; int res = android_fork_execvp_ext( ARRAY_SIZE(argv), (char**)argv, &status, false /* ignore_int_quit */, LOG_NONE, false /* abbreviated */, NULL /* file_path */, opt, ARRAY_SIZE(opt)); if (res || status) { ALOGE("%s failed with res=%d, status=%d", argv[0], res, status); return -1; } return 0; } int execIptablesRestore(IptablesTarget target, const std::string& commands) { int res = 0; if (target == V4 || target == V4V6) { res |= execIptablesRestoreCommand(IPTABLES_RESTORE_PATH, commands); } if (target == V6 || target == V4V6) { res |= execIptablesRestoreCommand(IP6TABLES_RESTORE_PATH, commands); } return res; } /* * Check an interface name for plausibility. This should e.g. help against * directory traversal. */ bool isIfaceName(const char *name) { size_t i; size_t name_len = strlen(name); if ((name_len == 0) || (name_len > IFNAMSIZ)) { return false; } /* First character must be alphanumeric */ if (!isalnum(name[0])) { return false; } for (i = 1; i < name_len; i++) { if (!isalnum(name[i]) && (name[i] != '_') && (name[i] != '-') && (name[i] != ':')) { return false; } } return true; } int parsePrefix(const char *prefix, uint8_t *family, void *address, int size, uint8_t *prefixlen) { if (!prefix || !family || !address || !prefixlen) { return -EFAULT; } // Find the '/' separating address from prefix length. const char *slash = strchr(prefix, '/'); const char *prefixlenString = slash + 1; if (!slash || !*prefixlenString) return -EINVAL; // Convert the prefix length to a uint8_t. char *endptr; unsigned templen; templen = strtoul(prefixlenString, &endptr, 10); if (*endptr || templen > 255) { return -EINVAL; } *prefixlen = templen; // Copy the address part of the prefix to a local buffer. We have to copy // because inet_pton and getaddrinfo operate on null-terminated address // strings, but prefix is const and has '/' after the address. std::string addressString(prefix, slash - prefix); // Parse the address. addrinfo *res; addrinfo hints = { .ai_flags = AI_NUMERICHOST, }; int ret = getaddrinfo(addressString.c_str(), NULL, &hints, &res); if (ret || !res) { return -EINVAL; // getaddrinfo return values are not errno values. } // Convert the address string to raw address bytes. void *rawAddress; int rawLength; switch (res[0].ai_family) { case AF_INET: { if (*prefixlen > 32) { return -EINVAL; } sockaddr_in *sin = (sockaddr_in *) res[0].ai_addr; rawAddress = &sin->sin_addr; rawLength = 4; break; } case AF_INET6: { if (*prefixlen > 128) { return -EINVAL; } sockaddr_in6 *sin6 = (sockaddr_in6 *) res[0].ai_addr; rawAddress = &sin6->sin6_addr; rawLength = 16; break; } default: { freeaddrinfo(res); return -EAFNOSUPPORT; } } if (rawLength > size) { freeaddrinfo(res); return -ENOSPC; } *family = res[0].ai_family; memcpy(address, rawAddress, rawLength); freeaddrinfo(res); return rawLength; }