blob: 076a7693c8f30c4e8b06bb1fd9c62b4de213d518 [file] [log] [blame]
Sreeram Ramachandranf4cfad32014-05-21 08:54:07 -07001/*
2 * Copyright (C) 2014 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
Sreeram Ramachandranefbe05d2014-05-21 11:41:39 -070017#include "NetdClient.h"
18
Michal Karpinski4b9b78a2016-10-06 19:33:55 +010019#include <arpa/inet.h>
Dan Albertaa1be2b2015-01-06 09:36:17 -080020#include <errno.h>
Michal Karpinski4b9b78a2016-10-06 19:33:55 +010021#include <math.h>
Dan Albertaa1be2b2015-01-06 09:36:17 -080022#include <sys/socket.h>
23#include <unistd.h>
24
25#include <atomic>
26
Sreeram Ramachandran4d4c8b72014-06-20 11:59:40 -070027#include "Fwmark.h"
Sreeram Ramachandranf4cfad32014-05-21 08:54:07 -070028#include "FwmarkClient.h"
29#include "FwmarkCommand.h"
Sreeram Ramachandranefbe05d2014-05-21 11:41:39 -070030#include "resolv_netid.h"
Robin Leecc544162016-09-21 16:31:33 +090031#include "Stopwatch.h"
Sreeram Ramachandranf4cfad32014-05-21 08:54:07 -070032
Sreeram Ramachandranf4cfad32014-05-21 08:54:07 -070033namespace {
34
Sreeram Ramachandran9fa2b132014-06-03 12:51:08 -070035std::atomic_uint netIdForProcess(NETID_UNSET);
36std::atomic_uint netIdForResolv(NETID_UNSET);
Sreeram Ramachandran5fc27572014-05-21 13:08:34 -070037
38typedef int (*Accept4FunctionType)(int, sockaddr*, socklen_t*, int);
39typedef int (*ConnectFunctionType)(int, const sockaddr*, socklen_t);
40typedef int (*SocketFunctionType)(int, int, int);
41typedef unsigned (*NetIdForResolvFunctionType)(unsigned);
42
43// These variables are only modified at startup (when libc.so is loaded) and never afterwards, so
44// it's okay that they are read later at runtime without a lock.
45Accept4FunctionType libcAccept4 = 0;
46ConnectFunctionType libcConnect = 0;
47SocketFunctionType libcSocket = 0;
48
Sreeram Ramachandran31f42102014-06-20 11:51:48 -070049int closeFdAndSetErrno(int fd, int error) {
Sreeram Ramachandranf4cfad32014-05-21 08:54:07 -070050 close(fd);
Sreeram Ramachandran3a069e62014-06-22 11:02:57 -070051 errno = -error;
Sreeram Ramachandranf4cfad32014-05-21 08:54:07 -070052 return -1;
53}
54
Sreeram Ramachandran5fc27572014-05-21 13:08:34 -070055int netdClientAccept4(int sockfd, sockaddr* addr, socklen_t* addrlen, int flags) {
56 int acceptedSocket = libcAccept4(sockfd, addr, addrlen, flags);
Sreeram Ramachandranf4cfad32014-05-21 08:54:07 -070057 if (acceptedSocket == -1) {
58 return -1;
59 }
Sreeram Ramachandran5fc27572014-05-21 13:08:34 -070060 int family;
61 if (addr) {
62 family = addr->sa_family;
63 } else {
64 socklen_t familyLen = sizeof(family);
65 if (getsockopt(acceptedSocket, SOL_SOCKET, SO_DOMAIN, &family, &familyLen) == -1) {
Sreeram Ramachandran3a069e62014-06-22 11:02:57 -070066 return closeFdAndSetErrno(acceptedSocket, -errno);
Sreeram Ramachandranf4cfad32014-05-21 08:54:07 -070067 }
Sreeram Ramachandranf4cfad32014-05-21 08:54:07 -070068 }
Sreeram Ramachandran5fc27572014-05-21 13:08:34 -070069 if (FwmarkClient::shouldSetFwmark(family)) {
Chenbo Feng9944ba82017-10-10 17:33:20 -070070 FwmarkCommand command = {FwmarkCommand::ON_ACCEPT, 0, 0, 0};
Michal Karpinski4b9b78a2016-10-06 19:33:55 +010071 if (int error = FwmarkClient().send(&command, acceptedSocket, nullptr)) {
Sreeram Ramachandran31f42102014-06-20 11:51:48 -070072 return closeFdAndSetErrno(acceptedSocket, error);
Sreeram Ramachandranf4cfad32014-05-21 08:54:07 -070073 }
74 }
75 return acceptedSocket;
76}
77
Sreeram Ramachandran5fc27572014-05-21 13:08:34 -070078int netdClientConnect(int sockfd, const sockaddr* addr, socklen_t addrlen) {
Michal Karpinski4b9b78a2016-10-06 19:33:55 +010079 const bool shouldSetFwmark = (sockfd >= 0) && addr
80 && FwmarkClient::shouldSetFwmark(addr->sa_family);
81 if (shouldSetFwmark) {
Chenbo Feng9944ba82017-10-10 17:33:20 -070082 FwmarkCommand command = {FwmarkCommand::ON_CONNECT, 0, 0, 0};
Michal Karpinski4b9b78a2016-10-06 19:33:55 +010083 if (int error = FwmarkClient().send(&command, sockfd, nullptr)) {
Sreeram Ramachandran3a069e62014-06-22 11:02:57 -070084 errno = -error;
Sreeram Ramachandran5fc27572014-05-21 13:08:34 -070085 return -1;
86 }
87 }
Michal Karpinski4b9b78a2016-10-06 19:33:55 +010088 // Latency measurement does not include time of sending commands to Fwmark
89 Stopwatch s;
Hugo Benichi794c5c72016-10-31 15:07:23 +090090 const int ret = libcConnect(sockfd, addr, addrlen);
Michal Karpinski4b9b78a2016-10-06 19:33:55 +010091 // Save errno so it isn't clobbered by sending ON_CONNECT_COMPLETE
92 const int connectErrno = errno;
93 const unsigned latencyMs = lround(s.timeTaken());
94 // Send an ON_CONNECT_COMPLETE command that includes sockaddr and connect latency for reporting
95 if (shouldSetFwmark && FwmarkClient::shouldReportConnectComplete(addr->sa_family)) {
Hugo Benichi794c5c72016-10-31 15:07:23 +090096 FwmarkConnectInfo connectInfo(ret == 0 ? 0 : connectErrno, latencyMs, addr);
Michal Karpinski4b9b78a2016-10-06 19:33:55 +010097 // TODO: get the netId from the socket mark once we have continuous benchmark runs
98 FwmarkCommand command = {FwmarkCommand::ON_CONNECT_COMPLETE, /* netId (ignored) */ 0,
Chenbo Feng9944ba82017-10-10 17:33:20 -070099 /* uid (filled in by the server) */ 0, 0};
Michal Karpinski4b9b78a2016-10-06 19:33:55 +0100100 // Ignore return value since it's only used for logging
101 FwmarkClient().send(&command, sockfd, &connectInfo);
102 }
103 errno = connectErrno;
104 return ret;
Sreeram Ramachandran5fc27572014-05-21 13:08:34 -0700105}
106
107int netdClientSocket(int domain, int type, int protocol) {
108 int socketFd = libcSocket(domain, type, protocol);
109 if (socketFd == -1) {
110 return -1;
111 }
112 unsigned netId = netIdForProcess;
113 if (netId != NETID_UNSET && FwmarkClient::shouldSetFwmark(domain)) {
Sreeram Ramachandrand36c49c2014-07-02 14:49:33 -0700114 if (int error = setNetworkForSocket(netId, socketFd)) {
Sreeram Ramachandran31f42102014-06-20 11:51:48 -0700115 return closeFdAndSetErrno(socketFd, error);
Sreeram Ramachandran5fc27572014-05-21 13:08:34 -0700116 }
117 }
118 return socketFd;
119}
Sreeram Ramachandranefbe05d2014-05-21 11:41:39 -0700120
121unsigned getNetworkForResolv(unsigned netId) {
122 if (netId != NETID_UNSET) {
123 return netId;
124 }
125 netId = netIdForProcess;
126 if (netId != NETID_UNSET) {
127 return netId;
128 }
129 return netIdForResolv;
130}
131
Sreeram Ramachandran31f42102014-06-20 11:51:48 -0700132int setNetworkForTarget(unsigned netId, std::atomic_uint* target) {
Sreeram Ramachandranefbe05d2014-05-21 11:41:39 -0700133 if (netId == NETID_UNSET) {
134 *target = netId;
Sreeram Ramachandran31f42102014-06-20 11:51:48 -0700135 return 0;
Sreeram Ramachandranefbe05d2014-05-21 11:41:39 -0700136 }
137 // Verify that we are allowed to use |netId|, by creating a socket and trying to have it marked
Sreeram Ramachandran27560452014-05-30 19:59:51 -0700138 // with the netId. Call libcSocket() directly; else the socket creation (via netdClientSocket())
139 // might itself cause another check with the fwmark server, which would be wasteful.
140 int socketFd;
141 if (libcSocket) {
Nick Kralevich53ea9ca2015-01-31 13:54:00 -0800142 socketFd = libcSocket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);
Sreeram Ramachandran27560452014-05-30 19:59:51 -0700143 } else {
Nick Kralevich53ea9ca2015-01-31 13:54:00 -0800144 socketFd = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);
Sreeram Ramachandran27560452014-05-30 19:59:51 -0700145 }
Sreeram Ramachandranefbe05d2014-05-21 11:41:39 -0700146 if (socketFd < 0) {
Sreeram Ramachandran3a069e62014-06-22 11:02:57 -0700147 return -errno;
Sreeram Ramachandranefbe05d2014-05-21 11:41:39 -0700148 }
Sreeram Ramachandran31f42102014-06-20 11:51:48 -0700149 int error = setNetworkForSocket(netId, socketFd);
150 if (!error) {
Sreeram Ramachandranefbe05d2014-05-21 11:41:39 -0700151 *target = netId;
152 }
Sreeram Ramachandran31f42102014-06-20 11:51:48 -0700153 close(socketFd);
154 return error;
Sreeram Ramachandranefbe05d2014-05-21 11:41:39 -0700155}
156
Remi NGUYEN VANeabc5da2018-04-24 18:54:58 +0900157int checkSocket(int socketFd) {
158 if (socketFd < 0) {
159 return -EBADF;
160 }
161 int family;
162 socklen_t familyLen = sizeof(family);
163 if (getsockopt(socketFd, SOL_SOCKET, SO_DOMAIN, &family, &familyLen) == -1) {
164 return -errno;
165 }
166 if (!FwmarkClient::shouldSetFwmark(family)) {
167 return -EAFNOSUPPORT;
168 }
169 return 0;
170}
171
Sreeram Ramachandranf4cfad32014-05-21 08:54:07 -0700172} // namespace
173
Remi NGUYEN VANeabc5da2018-04-24 18:54:58 +0900174#define CHECK_SOCKET_IS_MARKABLE(sock) \
175 do { \
176 int err; \
177 if ((err = checkSocket(sock)) != 0) { \
178 return err; \
179 } \
180 } while (false);
181
Sreeram Ramachandran5fc27572014-05-21 13:08:34 -0700182// accept() just calls accept4(..., 0), so there's no need to handle accept() separately.
183extern "C" void netdClientInitAccept4(Accept4FunctionType* function) {
184 if (function && *function) {
185 libcAccept4 = *function;
186 *function = netdClientAccept4;
187 }
188}
189
Sreeram Ramachandranf4cfad32014-05-21 08:54:07 -0700190extern "C" void netdClientInitConnect(ConnectFunctionType* function) {
191 if (function && *function) {
192 libcConnect = *function;
193 *function = netdClientConnect;
194 }
195}
196
Sreeram Ramachandran5fc27572014-05-21 13:08:34 -0700197extern "C" void netdClientInitSocket(SocketFunctionType* function) {
Sreeram Ramachandranf4cfad32014-05-21 08:54:07 -0700198 if (function && *function) {
Sreeram Ramachandran5fc27572014-05-21 13:08:34 -0700199 libcSocket = *function;
200 *function = netdClientSocket;
Sreeram Ramachandranf4cfad32014-05-21 08:54:07 -0700201 }
202}
Sreeram Ramachandranefbe05d2014-05-21 11:41:39 -0700203
204extern "C" void netdClientInitNetIdForResolv(NetIdForResolvFunctionType* function) {
205 if (function) {
206 *function = getNetworkForResolv;
207 }
208}
209
Sreeram Ramachandran4d4c8b72014-06-20 11:59:40 -0700210extern "C" int getNetworkForSocket(unsigned* netId, int socketFd) {
211 if (!netId || socketFd < 0) {
Sreeram Ramachandran3a069e62014-06-22 11:02:57 -0700212 return -EBADF;
Sreeram Ramachandran4d4c8b72014-06-20 11:59:40 -0700213 }
214 Fwmark fwmark;
215 socklen_t fwmarkLen = sizeof(fwmark.intValue);
216 if (getsockopt(socketFd, SOL_SOCKET, SO_MARK, &fwmark.intValue, &fwmarkLen) == -1) {
Sreeram Ramachandran3a069e62014-06-22 11:02:57 -0700217 return -errno;
Sreeram Ramachandran4d4c8b72014-06-20 11:59:40 -0700218 }
219 *netId = fwmark.netId;
220 return 0;
221}
222
Sreeram Ramachandranefbe05d2014-05-21 11:41:39 -0700223extern "C" unsigned getNetworkForProcess() {
224 return netIdForProcess;
225}
226
Sreeram Ramachandran31f42102014-06-20 11:51:48 -0700227extern "C" int setNetworkForSocket(unsigned netId, int socketFd) {
Remi NGUYEN VANeabc5da2018-04-24 18:54:58 +0900228 CHECK_SOCKET_IS_MARKABLE(socketFd);
Chenbo Feng9944ba82017-10-10 17:33:20 -0700229 FwmarkCommand command = {FwmarkCommand::SELECT_NETWORK, netId, 0, 0};
Michal Karpinski4b9b78a2016-10-06 19:33:55 +0100230 return FwmarkClient().send(&command, socketFd, nullptr);
Sreeram Ramachandranefbe05d2014-05-21 11:41:39 -0700231}
232
Sreeram Ramachandran31f42102014-06-20 11:51:48 -0700233extern "C" int setNetworkForProcess(unsigned netId) {
Sreeram Ramachandranefbe05d2014-05-21 11:41:39 -0700234 return setNetworkForTarget(netId, &netIdForProcess);
235}
236
Sreeram Ramachandran31f42102014-06-20 11:51:48 -0700237extern "C" int setNetworkForResolv(unsigned netId) {
Sreeram Ramachandranefbe05d2014-05-21 11:41:39 -0700238 return setNetworkForTarget(netId, &netIdForResolv);
239}
Sreeram Ramachandrand794e582014-06-19 10:03:07 -0700240
Sreeram Ramachandran31f42102014-06-20 11:51:48 -0700241extern "C" int protectFromVpn(int socketFd) {
Sreeram Ramachandrand794e582014-06-19 10:03:07 -0700242 if (socketFd < 0) {
Sreeram Ramachandran3a069e62014-06-22 11:02:57 -0700243 return -EBADF;
Sreeram Ramachandrand794e582014-06-19 10:03:07 -0700244 }
Chenbo Feng9944ba82017-10-10 17:33:20 -0700245 FwmarkCommand command = {FwmarkCommand::PROTECT_FROM_VPN, 0, 0, 0};
Michal Karpinski4b9b78a2016-10-06 19:33:55 +0100246 return FwmarkClient().send(&command, socketFd, nullptr);
Sreeram Ramachandrana69d9472014-07-11 16:27:02 -0700247}
248
249extern "C" int setNetworkForUser(uid_t uid, int socketFd) {
Remi NGUYEN VANeabc5da2018-04-24 18:54:58 +0900250 CHECK_SOCKET_IS_MARKABLE(socketFd);
Chenbo Feng9944ba82017-10-10 17:33:20 -0700251 FwmarkCommand command = {FwmarkCommand::SELECT_FOR_USER, 0, uid, 0};
Michal Karpinski4b9b78a2016-10-06 19:33:55 +0100252 return FwmarkClient().send(&command, socketFd, nullptr);
Paul Jensend1df5972015-05-06 07:29:56 -0400253}
254
255extern "C" int queryUserAccess(uid_t uid, unsigned netId) {
Chenbo Feng9944ba82017-10-10 17:33:20 -0700256 FwmarkCommand command = {FwmarkCommand::QUERY_USER_ACCESS, netId, uid, 0};
257 return FwmarkClient().send(&command, -1, nullptr);
258}
259
260extern "C" int tagSocket(int socketFd, uint32_t tag, uid_t uid) {
Remi NGUYEN VANeabc5da2018-04-24 18:54:58 +0900261 CHECK_SOCKET_IS_MARKABLE(socketFd);
Chenbo Feng9944ba82017-10-10 17:33:20 -0700262 FwmarkCommand command = {FwmarkCommand::TAG_SOCKET, 0, uid, tag};
263 return FwmarkClient().send(&command, socketFd, nullptr);
264}
265
266extern "C" int untagSocket(int socketFd) {
Remi NGUYEN VANeabc5da2018-04-24 18:54:58 +0900267 CHECK_SOCKET_IS_MARKABLE(socketFd);
Chenbo Feng9944ba82017-10-10 17:33:20 -0700268 FwmarkCommand command = {FwmarkCommand::UNTAG_SOCKET, 0, 0, 0};
269 return FwmarkClient().send(&command, socketFd, nullptr);
270}
271
272extern "C" int setCounterSet(uint32_t counterSet, uid_t uid) {
273 FwmarkCommand command = {FwmarkCommand::SET_COUNTERSET, 0, uid, counterSet};
274 return FwmarkClient().send(&command, -1, nullptr);
275}
276
277extern "C" int deleteTagData(uint32_t tag, uid_t uid) {
278 FwmarkCommand command = {FwmarkCommand::DELETE_TAGDATA, 0, uid, tag};
Michal Karpinski4b9b78a2016-10-06 19:33:55 +0100279 return FwmarkClient().send(&command, -1, nullptr);
Sreeram Ramachandrand794e582014-06-19 10:03:07 -0700280}