Robert Greenwalt | c462177 | 2012-01-31 12:46:45 -0800 | [diff] [blame] | 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 | |
Dan Albert | aa1be2b | 2015-01-06 09:36:17 -0800 | [diff] [blame^] | 17 | #include <ctype.h> |
| 18 | #include <errno.h> |
Lorenzo Colitti | 70afde6 | 2013-03-04 17:58:40 +0900 | [diff] [blame] | 19 | #include <fcntl.h> |
Lorenzo Colitti | ba25df9 | 2014-06-18 00:22:17 +0900 | [diff] [blame] | 20 | #include <netdb.h> |
Dan Albert | aa1be2b | 2015-01-06 09:36:17 -0800 | [diff] [blame^] | 21 | #include <net/if.h> |
Lorenzo Colitti | ba25df9 | 2014-06-18 00:22:17 +0900 | [diff] [blame] | 22 | #include <netinet/in.h> |
| 23 | #include <stdlib.h> |
Jeff Sharkey | 8e188ed | 2012-07-12 18:32:03 -0700 | [diff] [blame] | 24 | #include <string.h> |
Rom Lemarchand | 838ef64 | 2013-01-24 15:14:41 -0800 | [diff] [blame] | 25 | #include <sys/wait.h> |
Jeff Sharkey | 8e188ed | 2012-07-12 18:32:03 -0700 | [diff] [blame] | 26 | |
Jeff Sharkey | bec6d04 | 2012-09-06 15:45:56 -0700 | [diff] [blame] | 27 | #define LOG_TAG "Netd" |
| 28 | |
Jeff Sharkey | 8e188ed | 2012-07-12 18:32:03 -0700 | [diff] [blame] | 29 | #include <cutils/log.h> |
Rom Lemarchand | 838ef64 | 2013-01-24 15:14:41 -0800 | [diff] [blame] | 30 | #include <logwrap/logwrap.h> |
Jeff Sharkey | 8e188ed | 2012-07-12 18:32:03 -0700 | [diff] [blame] | 31 | |
Robert Greenwalt | c462177 | 2012-01-31 12:46:45 -0800 | [diff] [blame] | 32 | #include "NetdConstants.h" |
| 33 | |
| 34 | const char * const OEM_SCRIPT_PATH = "/system/bin/oem-iptables-init.sh"; |
| 35 | const char * const IPTABLES_PATH = "/system/bin/iptables"; |
JP Abgrall | 0031cea | 2012-04-17 16:38:23 -0700 | [diff] [blame] | 36 | const char * const IP6TABLES_PATH = "/system/bin/ip6tables"; |
Robert Greenwalt | c462177 | 2012-01-31 12:46:45 -0800 | [diff] [blame] | 37 | const char * const TC_PATH = "/system/bin/tc"; |
| 38 | const char * const IP_PATH = "/system/bin/ip"; |
| 39 | const char * const ADD = "add"; |
| 40 | const char * const DEL = "del"; |
Jeff Sharkey | 8e188ed | 2012-07-12 18:32:03 -0700 | [diff] [blame] | 41 | |
Rom Lemarchand | 838ef64 | 2013-01-24 15:14:41 -0800 | [diff] [blame] | 42 | static void logExecError(const char* argv[], int res, int status) { |
Jeff Sharkey | 8e188ed | 2012-07-12 18:32:03 -0700 | [diff] [blame] | 43 | const char** argp = argv; |
| 44 | std::string args = ""; |
| 45 | while (*argp) { |
| 46 | args += *argp; |
| 47 | args += ' '; |
| 48 | argp++; |
| 49 | } |
Rom Lemarchand | 838ef64 | 2013-01-24 15:14:41 -0800 | [diff] [blame] | 50 | ALOGE("exec() res=%d, status=%d for %s", res, status, args.c_str()); |
| 51 | } |
| 52 | |
| 53 | static int execIptablesCommand(int argc, const char *argv[], bool silent) { |
| 54 | int res; |
| 55 | int status; |
| 56 | |
| 57 | res = android_fork_execvp(argc, (char **)argv, &status, false, |
| 58 | !silent); |
| 59 | if (res || !WIFEXITED(status) || WEXITSTATUS(status)) { |
| 60 | if (!silent) { |
| 61 | logExecError(argv, res, status); |
| 62 | } |
| 63 | if (res) |
| 64 | return res; |
| 65 | if (!WIFEXITED(status)) |
| 66 | return ECHILD; |
| 67 | } |
| 68 | return WEXITSTATUS(status); |
Jeff Sharkey | 8e188ed | 2012-07-12 18:32:03 -0700 | [diff] [blame] | 69 | } |
| 70 | |
| 71 | static int execIptables(IptablesTarget target, bool silent, va_list args) { |
| 72 | /* Read arguments from incoming va_list; we expect the list to be NULL terminated. */ |
| 73 | std::list<const char*> argsList; |
| 74 | argsList.push_back(NULL); |
| 75 | const char* arg; |
| 76 | do { |
| 77 | arg = va_arg(args, const char *); |
| 78 | argsList.push_back(arg); |
| 79 | } while (arg); |
| 80 | |
| 81 | int i = 0; |
| 82 | const char* argv[argsList.size()]; |
| 83 | std::list<const char*>::iterator it; |
| 84 | for (it = argsList.begin(); it != argsList.end(); it++, i++) { |
| 85 | argv[i] = *it; |
| 86 | } |
| 87 | |
| 88 | int res = 0; |
| 89 | if (target == V4 || target == V4V6) { |
| 90 | argv[0] = IPTABLES_PATH; |
Rom Lemarchand | 838ef64 | 2013-01-24 15:14:41 -0800 | [diff] [blame] | 91 | res |= execIptablesCommand(argsList.size(), argv, silent); |
Jeff Sharkey | 8e188ed | 2012-07-12 18:32:03 -0700 | [diff] [blame] | 92 | } |
| 93 | if (target == V6 || target == V4V6) { |
| 94 | argv[0] = IP6TABLES_PATH; |
Rom Lemarchand | 838ef64 | 2013-01-24 15:14:41 -0800 | [diff] [blame] | 95 | res |= execIptablesCommand(argsList.size(), argv, silent); |
Jeff Sharkey | 8e188ed | 2012-07-12 18:32:03 -0700 | [diff] [blame] | 96 | } |
| 97 | return res; |
| 98 | } |
| 99 | |
| 100 | int execIptables(IptablesTarget target, ...) { |
| 101 | va_list args; |
| 102 | va_start(args, target); |
| 103 | int res = execIptables(target, false, args); |
| 104 | va_end(args); |
| 105 | return res; |
| 106 | } |
| 107 | |
| 108 | int execIptablesSilently(IptablesTarget target, ...) { |
| 109 | va_list args; |
| 110 | va_start(args, target); |
| 111 | int res = execIptables(target, true, args); |
| 112 | va_end(args); |
| 113 | return res; |
| 114 | } |
Lorenzo Colitti | 70afde6 | 2013-03-04 17:58:40 +0900 | [diff] [blame] | 115 | |
| 116 | int writeFile(const char *path, const char *value, int size) { |
| 117 | int fd = open(path, O_WRONLY); |
| 118 | if (fd < 0) { |
| 119 | ALOGE("Failed to open %s: %s", path, strerror(errno)); |
| 120 | return -1; |
| 121 | } |
| 122 | |
| 123 | if (write(fd, value, size) != size) { |
| 124 | ALOGE("Failed to write %s: %s", path, strerror(errno)); |
| 125 | close(fd); |
| 126 | return -1; |
| 127 | } |
| 128 | close(fd); |
| 129 | return 0; |
| 130 | } |
Dmitry Shmidt | 6d6c0e6 | 2013-06-11 16:18:06 -0700 | [diff] [blame] | 131 | |
| 132 | int readFile(const char *path, char *buf, int *sizep) |
| 133 | { |
| 134 | int fd = open(path, O_RDONLY); |
| 135 | int size; |
| 136 | |
| 137 | if (fd < 0) { |
| 138 | ALOGE("Failed to open %s: %s", path, strerror(errno)); |
| 139 | return -1; |
| 140 | } |
| 141 | |
| 142 | size = read(fd, buf, *sizep); |
| 143 | if (size < 0) { |
| 144 | ALOGE("Failed to write %s: %s", path, strerror(errno)); |
| 145 | close(fd); |
| 146 | return -1; |
| 147 | } |
| 148 | *sizep = size; |
| 149 | close(fd); |
| 150 | return 0; |
| 151 | } |
JP Abgrall | 69261cb | 2014-06-19 18:35:24 -0700 | [diff] [blame] | 152 | |
| 153 | /* |
| 154 | * Check an interface name for plausibility. This should e.g. help against |
| 155 | * directory traversal. |
| 156 | */ |
| 157 | bool isIfaceName(const char *name) { |
| 158 | size_t i; |
| 159 | size_t name_len = strlen(name); |
| 160 | if ((name_len == 0) || (name_len > IFNAMSIZ)) { |
| 161 | return false; |
| 162 | } |
| 163 | |
| 164 | /* First character must be alphanumeric */ |
| 165 | if (!isalnum(name[0])) { |
| 166 | return false; |
| 167 | } |
| 168 | |
| 169 | for (i = 1; i < name_len; i++) { |
| 170 | if (!isalnum(name[i]) && (name[i] != '_') && (name[i] != '-') && (name[i] != ':')) { |
| 171 | return false; |
| 172 | } |
| 173 | } |
| 174 | |
| 175 | return true; |
| 176 | } |
Lorenzo Colitti | ba25df9 | 2014-06-18 00:22:17 +0900 | [diff] [blame] | 177 | |
| 178 | int parsePrefix(const char *prefix, uint8_t *family, void *address, int size, uint8_t *prefixlen) { |
| 179 | if (!prefix || !family || !address || !prefixlen) { |
| 180 | return -EFAULT; |
| 181 | } |
| 182 | |
| 183 | // Find the '/' separating address from prefix length. |
| 184 | const char *slash = strchr(prefix, '/'); |
| 185 | const char *prefixlenString = slash + 1; |
| 186 | if (!slash || !*prefixlenString) |
| 187 | return -EINVAL; |
| 188 | |
| 189 | // Convert the prefix length to a uint8_t. |
| 190 | char *endptr; |
| 191 | unsigned templen; |
| 192 | templen = strtoul(prefixlenString, &endptr, 10); |
| 193 | if (*endptr || templen > 255) { |
| 194 | return -EINVAL; |
| 195 | } |
| 196 | *prefixlen = templen; |
| 197 | |
| 198 | // Copy the address part of the prefix to a local buffer. We have to copy |
| 199 | // because inet_pton and getaddrinfo operate on null-terminated address |
| 200 | // strings, but prefix is const and has '/' after the address. |
| 201 | std::string addressString(prefix, slash - prefix); |
| 202 | |
| 203 | // Parse the address. |
| 204 | addrinfo *res; |
| 205 | addrinfo hints = { |
| 206 | .ai_flags = AI_NUMERICHOST, |
| 207 | }; |
| 208 | int ret = getaddrinfo(addressString.c_str(), NULL, &hints, &res); |
| 209 | if (ret || !res) { |
| 210 | return -EINVAL; // getaddrinfo return values are not errno values. |
| 211 | } |
| 212 | |
| 213 | // Convert the address string to raw address bytes. |
| 214 | void *rawAddress; |
| 215 | int rawLength; |
| 216 | switch (res[0].ai_family) { |
| 217 | case AF_INET: { |
| 218 | if (*prefixlen > 32) { |
| 219 | return -EINVAL; |
| 220 | } |
| 221 | sockaddr_in *sin = (sockaddr_in *) res[0].ai_addr; |
| 222 | rawAddress = &sin->sin_addr; |
| 223 | rawLength = 4; |
| 224 | break; |
| 225 | } |
| 226 | case AF_INET6: { |
| 227 | if (*prefixlen > 128) { |
| 228 | return -EINVAL; |
| 229 | } |
| 230 | sockaddr_in6 *sin6 = (sockaddr_in6 *) res[0].ai_addr; |
| 231 | rawAddress = &sin6->sin6_addr; |
| 232 | rawLength = 16; |
| 233 | break; |
| 234 | } |
| 235 | default: { |
| 236 | freeaddrinfo(res); |
| 237 | return -EAFNOSUPPORT; |
| 238 | } |
| 239 | } |
| 240 | |
| 241 | if (rawLength > size) { |
| 242 | freeaddrinfo(res); |
| 243 | return -ENOSPC; |
| 244 | } |
| 245 | |
| 246 | *family = res[0].ai_family; |
| 247 | memcpy(address, rawAddress, rawLength); |
| 248 | freeaddrinfo(res); |
| 249 | |
| 250 | return rawLength; |
| 251 | } |