blob: 2f1437ca5400c8f26dcf1f6e566ab78c5b4a65b9 [file] [log] [blame]
Lorenzo Colitti8464e1e2016-02-05 00:57:26 +09001/*
2 * Copyright (C) 2016 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 <netdb.h>
19#include <string.h>
20#include <netinet/in.h>
21#include <netinet/tcp.h>
22#include <sys/socket.h>
23#include <sys/uio.h>
24
25#include <linux/netlink.h>
26#include <linux/sock_diag.h>
27#include <linux/inet_diag.h>
28
29#define LOG_TAG "Netd"
30
31#include <cutils/log.h>
32
33#include "NetdConstants.h"
34#include "SockDiag.h"
35
36#ifndef SOCK_DESTROY
37#define SOCK_DESTROY 21
38#endif
39
40namespace {
41
42struct AddrinfoDeleter {
43 void operator()(addrinfo *a) { if (a) freeaddrinfo(a); }
44};
45
46typedef std::unique_ptr<addrinfo, AddrinfoDeleter> ScopedAddrinfo;
47
48int checkError(int fd) {
49 struct {
50 nlmsghdr h;
51 nlmsgerr err;
52 } __attribute__((__packed__)) ack;
53 ssize_t bytesread = recv(fd, &ack, sizeof(ack), MSG_DONTWAIT | MSG_PEEK);
54 if (bytesread == -1) {
55 // Read failed (error), or nothing to read (good).
56 return (errno == EAGAIN) ? 0 : -errno;
57 } else if (bytesread == (ssize_t) sizeof(ack) && ack.h.nlmsg_type == NLMSG_ERROR) {
58 // We got an error. Consume it.
59 recv(fd, &ack, sizeof(ack), 0);
60 return ack.err.error;
61 } else {
62 // The kernel replied with something. Leave it to the caller.
63 return 0;
64 }
65}
66
67} // namespace
68
69bool SockDiag::open() {
70 if (hasSocks()) {
71 return false;
72 }
73
74 mSock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_INET_DIAG);
75 mWriteSock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_INET_DIAG);
76 if (!hasSocks()) {
77 closeSocks();
78 return false;
79 }
80
81 sockaddr_nl nl = { .nl_family = AF_NETLINK };
82 if ((connect(mSock, reinterpret_cast<sockaddr *>(&nl), sizeof(nl)) == -1) ||
83 (connect(mWriteSock, reinterpret_cast<sockaddr *>(&nl), sizeof(nl)) == -1)) {
84 closeSocks();
85 return false;
86 }
87
88 return true;
89}
90
91int SockDiag::sendDumpRequest(uint8_t proto, uint8_t family, const char *addrstr) {
92 addrinfo hints = { .ai_flags = AI_NUMERICHOST };
93 addrinfo *res;
94 in6_addr mapped = { .s6_addr32 = { 0, 0, htonl(0xffff), 0 } };
95 int ret;
96
97 // TODO: refactor the netlink parsing code out of system/core, bring it into netd, and stop
98 // doing string conversions when they're not necessary.
99 if ((ret = getaddrinfo(addrstr, nullptr, &hints, &res)) != 0) {
100 return -EINVAL;
101 }
102
103 // So we don't have to call freeaddrinfo on every failure path.
104 ScopedAddrinfo resP(res);
105
106 void *addr;
107 uint8_t addrlen;
108 if (res->ai_family == AF_INET && family == AF_INET) {
109 in_addr& ina = reinterpret_cast<sockaddr_in*>(res->ai_addr)->sin_addr;
110 addr = &ina;
111 addrlen = sizeof(ina);
112 } else if (res->ai_family == AF_INET && family == AF_INET6) {
113 in_addr& ina = reinterpret_cast<sockaddr_in*>(res->ai_addr)->sin_addr;
114 mapped.s6_addr32[3] = ina.s_addr;
115 addr = &mapped;
116 addrlen = sizeof(mapped);
117 } else if (res->ai_family == AF_INET6 && family == AF_INET6) {
118 in6_addr& in6a = reinterpret_cast<sockaddr_in6*>(res->ai_addr)->sin6_addr;
119 addr = &in6a;
120 addrlen = sizeof(in6a);
121 } else {
122 return -EAFNOSUPPORT;
123 }
124
125 uint8_t prefixlen = addrlen * 8;
126 uint8_t yesjump = sizeof(inet_diag_bc_op) + sizeof(inet_diag_hostcond) + addrlen;
127 uint8_t nojump = yesjump + 4;
128 uint32_t states = ~(1 << TCP_TIME_WAIT);
129
130 struct {
131 nlmsghdr nlh;
132 inet_diag_req_v2 req;
133 nlattr nla;
134 inet_diag_bc_op op;
135 inet_diag_hostcond cond;
136 } __attribute__((__packed__)) request = {
137 .nlh = {
138 .nlmsg_type = SOCK_DIAG_BY_FAMILY,
139 .nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP,
140 },
141 .req = {
142 .sdiag_family = family,
143 .sdiag_protocol = proto,
144 .idiag_states = states,
145 },
146 .nla = {
147 .nla_type = INET_DIAG_REQ_BYTECODE,
148 },
149 .op = {
150 INET_DIAG_BC_S_COND,
151 yesjump,
152 nojump,
153 },
154 .cond = {
155 family,
156 prefixlen,
157 -1,
158 {}
159 },
160 };
161
162 request.nlh.nlmsg_len = sizeof(request) + addrlen;
163 request.nla.nla_len = sizeof(request.nla) + sizeof(request.op) + sizeof(request.cond) + addrlen;
164
165 struct iovec iov[] = {
166 { &request, sizeof(request) },
167 { addr, addrlen },
168 };
169
170 if (writev(mSock, iov, ARRAY_SIZE(iov)) != (int) request.nlh.nlmsg_len) {
171 return -errno;
172 }
173
174 return checkError(mSock);
175}
176
177int SockDiag::readDiagMsg(uint8_t proto, SockDiag::DumpCallback callback) {
178 char buf[kBufferSize];
179
180 ssize_t bytesread;
181 do {
182 bytesread = read(mSock, buf, sizeof(buf));
183
184 if (bytesread < 0) {
185 return -errno;
186 }
187
188 uint32_t len = bytesread;
189 for (nlmsghdr *nlh = reinterpret_cast<nlmsghdr *>(buf);
190 NLMSG_OK(nlh, len);
191 nlh = NLMSG_NEXT(nlh, len)) {
192 switch (nlh->nlmsg_type) {
193 case NLMSG_DONE:
194 callback(proto, NULL);
195 return 0;
196 case NLMSG_ERROR: {
197 nlmsgerr *err = reinterpret_cast<nlmsgerr *>(NLMSG_DATA(nlh));
198 return err->error;
199 }
200 default:
201 inet_diag_msg *msg = reinterpret_cast<inet_diag_msg *>(NLMSG_DATA(nlh));
202 callback(proto, msg);
203 }
204 }
205 } while (bytesread > 0);
206
207 return 0;
208}
209
210int SockDiag::sockDestroy(uint8_t proto, const inet_diag_msg *msg) {
211 DestroyRequest request = {
212 .nlh = {
213 .nlmsg_type = SOCK_DESTROY,
214 .nlmsg_flags = NLM_F_REQUEST,
215 },
216 .req = {
217 .sdiag_family = msg->idiag_family,
218 .sdiag_protocol = proto,
219 .idiag_states = (uint32_t) (1 << msg->idiag_state),
220 .id = msg->id,
221 },
222 };
223 request.nlh.nlmsg_len = sizeof(request);
224
225 if (write(mWriteSock, &request, sizeof(request)) < (ssize_t) sizeof(request)) {
226 return -errno;
227 }
228
229 return checkError(mWriteSock);
230}