blob: ca1c63c4b7acab52dd7c2a9f96aa0368958897be [file] [log] [blame]
Patrick Rohr776c40c2022-01-12 21:05:26 +01001/*
2 * Copyright (C) 2022 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#define LOG_TAG "TcUtils"
18
19#include "tcutils/tcutils.h"
20
21#include "scopeguard.h"
22
23#include <android/log.h>
24#include <arpa/inet.h>
25#include <cerrno>
26#include <cstdio>
27#include <cstring>
28#include <libgen.h>
29#include <linux/if_arp.h>
30#include <linux/if_ether.h>
31#include <linux/netlink.h>
32#include <linux/pkt_cls.h>
33#include <linux/pkt_sched.h>
34#include <linux/rtnetlink.h>
35#include <net/if.h>
36#include <stdarg.h>
37#include <sys/socket.h>
38#include <sys/utsname.h>
39#include <unistd.h>
40#include <utility>
41
42#define BPF_FD_JUST_USE_INT
43#include <BpfSyscallWrappers.h>
44#undef BPF_FD_JUST_USE_INT
45
46// The maximum length of TCA_BPF_NAME. Sync from net/sched/cls_bpf.c.
47#define CLS_BPF_NAME_LEN 256
48
49// Classifier name. See cls_bpf_ops in net/sched/cls_bpf.c.
50#define CLS_BPF_KIND_NAME "bpf"
51
52namespace android {
53namespace {
54
55void logError(const char *fmt...) {
56 va_list args;
57 va_start(args, fmt);
58 __android_log_vprint(ANDROID_LOG_ERROR, LOG_TAG, fmt, args);
59 va_end(args);
60}
61
62const sockaddr_nl KERNEL_NLADDR = {AF_NETLINK, 0, 0, 0};
63const uint16_t NETLINK_REQUEST_FLAGS = NLM_F_REQUEST | NLM_F_ACK;
64
65int sendAndProcessNetlinkResponse(const void *req, int len) {
66 // TODO: use unique_fd instead of ScopeGuard
67 int fd = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE);
68 if (fd == -1) {
69 int error = errno;
70 logError("socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE): %d",
71 error);
72 return -error;
73 }
74 auto scopeGuard = base::make_scope_guard([fd] { close(fd); });
75
76 static constexpr int on = 1;
77 if (setsockopt(fd, SOL_NETLINK, NETLINK_CAP_ACK, &on, sizeof(on))) {
78 int error = errno;
79 logError("setsockopt(fd, SOL_NETLINK, NETLINK_CAP_ACK, 1): %d", error);
80 return -error;
81 }
82
83 // this is needed to get valid strace netlink parsing, it allocates the pid
84 if (bind(fd, (const struct sockaddr *)&KERNEL_NLADDR,
85 sizeof(KERNEL_NLADDR))) {
86 int error = errno;
87 logError("bind(fd, {AF_NETLINK, 0, 0}: %d)", error);
88 return -error;
89 }
90
91 // we do not want to receive messages from anyone besides the kernel
92 if (connect(fd, (const struct sockaddr *)&KERNEL_NLADDR,
93 sizeof(KERNEL_NLADDR))) {
94 int error = errno;
95 logError("connect(fd, {AF_NETLINK, 0, 0}): %d", error);
96 return -error;
97 }
98
99 int rv = send(fd, req, len, 0);
100
101 if (rv == -1) {
102 int error = errno;
103 logError("send(fd, req, len, 0) failed: %d", error);
104 return -error;
105 }
106
107 if (rv != len) {
108 logError("send(fd, req, len = %d, 0) returned invalid message size %d", len,
109 rv);
110 return -EMSGSIZE;
111 }
112
113 struct {
114 nlmsghdr h;
115 nlmsgerr e;
116 char buf[256];
117 } resp = {};
118
119 rv = recv(fd, &resp, sizeof(resp), MSG_TRUNC);
120
121 if (rv == -1) {
122 int error = errno;
123 logError("recv() failed: %d", error);
124 return -error;
125 }
126
127 if (rv < (int)NLMSG_SPACE(sizeof(struct nlmsgerr))) {
128 logError("recv() returned short packet: %d", rv);
129 return -EBADMSG;
130 }
131
132 if (resp.h.nlmsg_len != (unsigned)rv) {
133 logError("recv() returned invalid header length: %d != %d",
134 resp.h.nlmsg_len, rv);
135 return -EBADMSG;
136 }
137
138 if (resp.h.nlmsg_type != NLMSG_ERROR) {
139 logError("recv() did not return NLMSG_ERROR message: %d",
140 resp.h.nlmsg_type);
141 return -ENOMSG;
142 }
143
144 if (resp.e.error) {
145 logError("NLMSG_ERROR message return error: %d", resp.e.error);
146 }
147 return resp.e.error; // returns 0 on success
148}
149
150int hardwareAddressType(const char *interface) {
151 int fd = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);
152 if (fd < 0)
153 return -errno;
154 auto scopeGuard = base::make_scope_guard([fd] { close(fd); });
155
156 struct ifreq ifr = {};
157 // We use strncpy() instead of strlcpy() since kernel has to be able
158 // to handle non-zero terminated junk passed in by userspace anyway,
159 // and this way too long interface names (more than IFNAMSIZ-1 = 15
160 // characters plus terminating NULL) will not get truncated to 15
161 // characters and zero-terminated and thus potentially erroneously
162 // match a truncated interface if one were to exist.
163 strncpy(ifr.ifr_name, interface, sizeof(ifr.ifr_name));
164
165 if (ioctl(fd, SIOCGIFHWADDR, &ifr, sizeof(ifr))) {
166 return -errno;
167 }
168 return ifr.ifr_hwaddr.sa_family;
169}
170
171// -----------------------------------------------------------------------------
172// TODO - just use BpfUtils.h once that is available in sc-mainline-prod and has
173// kernelVersion()
174//
175// In the mean time copying verbatim from:
176// system/bpf/libbpf_android/include/bpf/BpfUtils.h
177// and
178// system/bpf/libbpf_android/BpfUtils.cpp
179
180#define KVER(a, b, c) (((a) << 24) + ((b) << 16) + (c))
181
182unsigned kernelVersion() {
183 struct utsname buf;
184 int ret = uname(&buf);
185 if (ret)
186 return 0;
187
188 unsigned kver_major;
189 unsigned kver_minor;
190 unsigned kver_sub;
191 char discard;
192 ret = sscanf(buf.release, "%u.%u.%u%c", &kver_major, &kver_minor, &kver_sub,
193 &discard);
194 // Check the device kernel version
195 if (ret < 3)
196 return 0;
197
198 return KVER(kver_major, kver_minor, kver_sub);
199}
200
201bool isAtLeastKernelVersion(unsigned major, unsigned minor, unsigned sub) {
202 return kernelVersion() >= KVER(major, minor, sub);
203}
204// -----------------------------------------------------------------------------
205
206} // namespace
207
208int isEthernet(const char *iface, bool &isEthernet) {
209 int rv = hardwareAddressType(iface);
210 if (rv < 0) {
211 logError("Get hardware address type of interface %s failed: %s", iface,
212 strerror(-rv));
213 return -rv;
214 }
215
216 // Backwards compatibility with pre-GKI kernels that use various custom
217 // ARPHRD_* for their cellular interface
218 switch (rv) {
219 // ARPHRD_PUREIP on at least some Mediatek Android kernels
220 // example: wembley with 4.19 kernel
221 case 520:
222 // in Linux 4.14+ rmnet support was upstreamed and ARHRD_RAWIP became 519,
223 // but it is 530 on at least some Qualcomm Android 4.9 kernels with rmnet
224 // example: Pixel 3 family
225 case 530:
226 // >5.4 kernels are GKI2.0 and thus upstream compatible, however 5.10
227 // shipped with Android S, so (for safety) let's limit ourselves to
228 // >5.10, ie. 5.11+ as a guarantee we're on Android T+ and thus no
229 // longer need this non-upstream compatibility logic
230 static bool is_pre_5_11_kernel = !isAtLeastKernelVersion(5, 11, 0);
231 if (is_pre_5_11_kernel)
232 return false;
233 }
234
235 switch (rv) {
236 case ARPHRD_ETHER:
237 isEthernet = true;
238 return 0;
239 case ARPHRD_NONE:
240 case ARPHRD_PPP:
241 case ARPHRD_RAWIP:
242 isEthernet = false;
243 return 0;
244 default:
245 logError("Unknown hardware address type %d on interface %s", rv, iface);
246 return -ENOENT;
247 }
248}
249
250// tc filter add dev .. in/egress prio 1 protocol ipv6/ip bpf object-pinned
251// /sys/fs/bpf/... direct-action
252int tcAddBpfFilter(int ifIndex, bool ingress, uint16_t prio, uint16_t proto,
253 const char *bpfProgPath) {
254 const int bpfFd = bpf::retrieveProgram(bpfProgPath);
255 if (bpfFd == -1) {
256 logError("retrieveProgram failed: %d", errno);
257 return -errno;
258 }
259 auto scopeGuard = base::make_scope_guard([bpfFd] { close(bpfFd); });
260
261 struct {
262 nlmsghdr n;
263 tcmsg t;
264 struct {
265 nlattr attr;
266 // The maximum classifier name length is defined in
267 // tcf_proto_ops in include/net/sch_generic.h.
268 char str[NLMSG_ALIGN(sizeof(CLS_BPF_KIND_NAME))];
269 } kind;
270 struct {
271 nlattr attr;
272 struct {
273 nlattr attr;
274 __u32 u32;
275 } fd;
276 struct {
277 nlattr attr;
278 char str[NLMSG_ALIGN(CLS_BPF_NAME_LEN)];
279 } name;
280 struct {
281 nlattr attr;
282 __u32 u32;
283 } flags;
284 } options;
285 } req = {
286 .n =
287 {
288 .nlmsg_len = sizeof(req),
289 .nlmsg_type = RTM_NEWTFILTER,
290 .nlmsg_flags = NETLINK_REQUEST_FLAGS | NLM_F_EXCL | NLM_F_CREATE,
291 },
292 .t =
293 {
294 .tcm_family = AF_UNSPEC,
295 .tcm_ifindex = ifIndex,
296 .tcm_handle = TC_H_UNSPEC,
297 .tcm_parent = TC_H_MAKE(TC_H_CLSACT, ingress ? TC_H_MIN_INGRESS
298 : TC_H_MIN_EGRESS),
299 .tcm_info =
300 static_cast<__u32>((static_cast<uint16_t>(prio) << 16) |
301 htons(static_cast<uint16_t>(proto))),
302 },
303 .kind =
304 {
305 .attr =
306 {
307 .nla_len = sizeof(req.kind),
308 .nla_type = TCA_KIND,
309 },
310 .str = CLS_BPF_KIND_NAME,
311 },
312 .options =
313 {
314 .attr =
315 {
316 .nla_len = sizeof(req.options),
317 .nla_type = NLA_F_NESTED | TCA_OPTIONS,
318 },
319 .fd =
320 {
321 .attr =
322 {
323 .nla_len = sizeof(req.options.fd),
324 .nla_type = TCA_BPF_FD,
325 },
326 .u32 = static_cast<__u32>(bpfFd),
327 },
328 .name =
329 {
330 .attr =
331 {
332 .nla_len = sizeof(req.options.name),
333 .nla_type = TCA_BPF_NAME,
334 },
335 // Visible via 'tc filter show', but
336 // is overwritten by strncpy below
337 .str = "placeholder",
338 },
339 .flags =
340 {
341 .attr =
342 {
343 .nla_len = sizeof(req.options.flags),
344 .nla_type = TCA_BPF_FLAGS,
345 },
346 .u32 = TCA_BPF_FLAG_ACT_DIRECT,
347 },
348 },
349 };
350
351 snprintf(req.options.name.str, sizeof(req.options.name.str), "%s:[*fsobj]",
352 basename(bpfProgPath));
353
354 int error = sendAndProcessNetlinkResponse(&req, sizeof(req));
355 return error;
356}
357
358// tc filter del dev .. in/egress prio .. protocol ..
359int tcDeleteFilter(int ifIndex, bool ingress, uint16_t prio, uint16_t proto) {
360 const struct {
361 nlmsghdr n;
362 tcmsg t;
363 } req = {
364 .n =
365 {
366 .nlmsg_len = sizeof(req),
367 .nlmsg_type = RTM_DELTFILTER,
368 .nlmsg_flags = NETLINK_REQUEST_FLAGS,
369 },
370 .t =
371 {
372 .tcm_family = AF_UNSPEC,
373 .tcm_ifindex = ifIndex,
374 .tcm_handle = TC_H_UNSPEC,
375 .tcm_parent = TC_H_MAKE(TC_H_CLSACT, ingress ? TC_H_MIN_INGRESS
376 : TC_H_MIN_EGRESS),
377 .tcm_info =
378 static_cast<__u32>((static_cast<uint16_t>(prio) << 16) |
379 htons(static_cast<uint16_t>(proto))),
380 },
381 };
382
383 return sendAndProcessNetlinkResponse(&req, sizeof(req));
384}
385
386} // namespace android