Pierre Imai | 904ce3a | 2016-02-18 13:13:12 +0900 | [diff] [blame] | 1 | /* |
| 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 | * |
Lorenzo Colitti | 93a0d64 | 2019-06-26 22:31:03 +0900 | [diff] [blame] | 10 | * Unless required by applicable law or agreed to in writing, software |
Pierre Imai | 904ce3a | 2016-02-18 13:13:12 +0900 | [diff] [blame] | 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 | |
Maciej Żenczykowski | 8d6ed4a | 2020-05-29 00:41:22 -0700 | [diff] [blame] | 18 | #include <arpa/inet.h> |
Pierre Imai | 904ce3a | 2016-02-18 13:13:12 +0900 | [diff] [blame] | 19 | #include <errno.h> |
Maciej Żenczykowski | 8d6ed4a | 2020-05-29 00:41:22 -0700 | [diff] [blame] | 20 | #include <linux/if_packet.h> |
| 21 | #include <linux/if_tun.h> |
| 22 | #include <net/if.h> |
| 23 | #include <poll.h> |
Maciej Żenczykowski | 6b47926 | 2020-05-29 23:51:18 +0000 | [diff] [blame] | 24 | #include <sched.h> |
Chenbo Feng | 6616547 | 2018-07-23 19:05:56 -0700 | [diff] [blame] | 25 | #include <sys/capability.h> |
Maciej Żenczykowski | 8d6ed4a | 2020-05-29 00:41:22 -0700 | [diff] [blame] | 26 | #include <sys/ioctl.h> |
| 27 | #include <sys/socket.h> |
| 28 | #include <sys/types.h> |
Pierre Imai | 904ce3a | 2016-02-18 13:13:12 +0900 | [diff] [blame] | 29 | |
| 30 | #include <gtest/gtest.h> |
Pierre Imai | beedec3 | 2016-04-13 06:44:51 +0900 | [diff] [blame] | 31 | |
Maciej Żenczykowski | 8d6ed4a | 2020-05-29 00:41:22 -0700 | [diff] [blame] | 32 | #include <android-base/unique_fd.h> |
| 33 | |
| 34 | #define LOG_TAG "NetdTest" |
| 35 | #include "bpf/BpfMap.h" |
| 36 | #include "netdbpf/bpf_shared.h" |
| 37 | |
| 38 | #include "OffloadUtils.h" |
| 39 | |
| 40 | namespace android { |
| 41 | namespace net { |
| 42 | |
| 43 | using base::unique_fd; |
| 44 | |
Chenbo Feng | 6616547 | 2018-07-23 19:05:56 -0700 | [diff] [blame] | 45 | TEST(NetUtilsWrapperTest, TestFileCapabilities) { |
| 46 | errno = 0; |
| 47 | ASSERT_EQ(NULL, cap_get_file("/system/bin/netutils-wrapper-1.0")); |
| 48 | ASSERT_EQ(ENODATA, errno); |
| 49 | } |
Maciej Żenczykowski | 5a91482 | 2019-05-02 21:57:10 -0700 | [diff] [blame] | 50 | |
| 51 | TEST(NetdSELinuxTest, CheckProperMTULabels) { |
| 52 | // Since we expect the egrep regexp to filter everything out, |
| 53 | // we thus expect no matches and thus a return code of 1 |
| 54 | // NOLINTNEXTLINE(cert-env33-c) |
| 55 | ASSERT_EQ(W_EXITCODE(1, 0), system("ls -Z /sys/class/net/*/mtu | egrep -q -v " |
| 56 | "'^u:object_r:sysfs_net:s0 /sys/class/net/'")); |
| 57 | } |
Maciej Żenczykowski | 6b47926 | 2020-05-29 23:51:18 +0000 | [diff] [blame] | 58 | |
| 59 | // Trivial thread function that simply immediately terminates successfully. |
| 60 | static int thread(void*) { |
| 61 | return 0; |
| 62 | } |
| 63 | |
| 64 | typedef int (*thread_t)(void*); |
| 65 | |
| 66 | static void nsTest(int flags, bool success, thread_t newThread) { |
| 67 | // We need a minimal stack, but not clear if it will grow up or down, |
| 68 | // So allocate 2 pages and give a pointer to the middle. |
| 69 | static char stack[PAGE_SIZE * 2]; |
| 70 | errno = 0; |
| 71 | // VFORK: if thread is successfully created, then kernel will wait for it |
| 72 | // to terminate before we resume -> hence static stack is safe to reuse. |
| 73 | int tid = clone(newThread, &stack[PAGE_SIZE], flags | CLONE_VFORK, NULL); |
| 74 | if (success) { |
| 75 | ASSERT_EQ(errno, 0); |
| 76 | ASSERT_GE(tid, 0); |
| 77 | } else { |
| 78 | ASSERT_EQ(errno, EINVAL); |
| 79 | ASSERT_EQ(tid, -1); |
| 80 | } |
| 81 | } |
| 82 | |
| 83 | // Test kernel configuration option CONFIG_NAMESPACES=y |
Maciej Zenczykowski | effd1f0 | 2020-05-30 01:10:29 +0000 | [diff] [blame] | 84 | TEST(NetdNamespaceTest, CheckMountNamespaceSupport) { |
Maciej Żenczykowski | 6b47926 | 2020-05-29 23:51:18 +0000 | [diff] [blame] | 85 | nsTest(CLONE_NEWNS, true, thread); |
| 86 | } |
| 87 | |
| 88 | // Test kernel configuration option CONFIG_UTS_NS=y |
Maciej Zenczykowski | effd1f0 | 2020-05-30 01:10:29 +0000 | [diff] [blame] | 89 | TEST(NetdNamespaceTest, CheckUTSNamespaceSupport) { |
Maciej Żenczykowski | 6b47926 | 2020-05-29 23:51:18 +0000 | [diff] [blame] | 90 | nsTest(CLONE_NEWUTS, true, thread); |
| 91 | } |
| 92 | |
| 93 | // Test kernel configuration option CONFIG_NET_NS=y |
Maciej Zenczykowski | effd1f0 | 2020-05-30 01:10:29 +0000 | [diff] [blame] | 94 | TEST(NetdNamespaceTest, CheckNetworkNamespaceSupport) { |
Maciej Żenczykowski | 6b47926 | 2020-05-29 23:51:18 +0000 | [diff] [blame] | 95 | nsTest(CLONE_NEWNET, true, thread); |
| 96 | } |
| 97 | |
| 98 | // Test kernel configuration option CONFIG_USER_NS=n |
Maciej Zenczykowski | effd1f0 | 2020-05-30 01:10:29 +0000 | [diff] [blame] | 99 | TEST(NetdNamespaceTest, /*DISABLED_*/ CheckNoUserNamespaceSupport) { |
Maciej Żenczykowski | 6b47926 | 2020-05-29 23:51:18 +0000 | [diff] [blame] | 100 | nsTest(CLONE_NEWUSER, false, thread); |
| 101 | } |
| 102 | |
| 103 | // Test for all of the above |
Maciej Zenczykowski | effd1f0 | 2020-05-30 01:10:29 +0000 | [diff] [blame] | 104 | TEST(NetdNamespaceTest, CheckFullNamespaceSupport) { |
Maciej Żenczykowski | 6b47926 | 2020-05-29 23:51:18 +0000 | [diff] [blame] | 105 | nsTest(CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWNET, true, thread); |
| 106 | } |
Maciej Żenczykowski | 8d6ed4a | 2020-05-29 00:41:22 -0700 | [diff] [blame] | 107 | |
| 108 | // Test for presence of kernel patch: |
| 109 | // ANDROID: net: bpf: permit redirect from ingress L3 to egress L2 devices at near max mtu |
| 110 | // on 4.14+ kernels. |
| 111 | TEST(NetdBpfTest, testBpfSkbChangeHeadAboveMTU) { |
| 112 | SKIP_IF_EXTENDED_BPF_NOT_SUPPORTED; |
| 113 | |
| 114 | constexpr int mtu = 1500; |
| 115 | |
| 116 | errno = 0; |
| 117 | |
| 118 | // Amusingly can't use SIOC... on tun/tap fds. |
| 119 | int rv = socket(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0); |
| 120 | ASSERT_EQ(errno, 0); |
| 121 | ASSERT_GE(rv, 3); |
| 122 | unique_fd unixfd(rv); |
| 123 | |
| 124 | rv = open("/dev/net/tun", O_RDWR | O_NONBLOCK); |
| 125 | ASSERT_EQ(errno, 0); |
| 126 | ASSERT_GE(rv, 3); |
| 127 | unique_fd tun(rv); |
| 128 | |
| 129 | rv = open("/dev/net/tun", O_RDWR | O_NONBLOCK); |
| 130 | ASSERT_EQ(errno, 0); |
| 131 | ASSERT_GE(rv, 3); |
| 132 | unique_fd tap(rv); |
| 133 | |
| 134 | struct ifreq tun_ifr = { |
| 135 | .ifr_flags = IFF_TUN | IFF_NO_PI, |
| 136 | .ifr_name = "tun_bpftest", |
| 137 | }; |
| 138 | |
| 139 | struct ifreq tap_ifr = { |
| 140 | .ifr_flags = IFF_TAP | IFF_NO_PI, |
| 141 | .ifr_name = "tap_bpftest", |
| 142 | }; |
| 143 | |
| 144 | rv = ioctl(tun, TUNSETIFF, &tun_ifr); |
| 145 | ASSERT_EQ(errno, 0); |
| 146 | ASSERT_EQ(rv, 0); |
| 147 | |
| 148 | rv = ioctl(tap, TUNSETIFF, &tap_ifr); |
| 149 | ASSERT_EQ(errno, 0); |
| 150 | ASSERT_EQ(rv, 0); |
| 151 | |
| 152 | // prevents kernel from sending us spurious ipv6 packets |
| 153 | rv = open("/proc/sys/net/ipv6/conf/tap_bpftest/disable_ipv6", O_WRONLY); |
| 154 | ASSERT_EQ(errno, 0); |
| 155 | ASSERT_GE(rv, 3); |
| 156 | unique_fd f(rv); |
| 157 | |
| 158 | rv = write(f, "1\n", 2); |
| 159 | ASSERT_EQ(errno, 0); |
| 160 | ASSERT_EQ(rv, 2); |
| 161 | |
| 162 | rv = close(f.release()); |
| 163 | ASSERT_EQ(errno, 0); |
| 164 | ASSERT_EQ(rv, 0); |
| 165 | |
| 166 | int tunif = if_nametoindex(tun_ifr.ifr_name); |
| 167 | ASSERT_GE(tunif, 2); |
| 168 | |
| 169 | int tapif = if_nametoindex(tap_ifr.ifr_name); |
| 170 | ASSERT_GE(tapif, 2); |
| 171 | |
| 172 | tun_ifr.ifr_mtu = mtu; |
| 173 | rv = ioctl(unixfd, SIOCSIFMTU, &tun_ifr); |
| 174 | ASSERT_EQ(errno, 0); |
| 175 | ASSERT_EQ(rv, 0); |
| 176 | |
| 177 | tap_ifr.ifr_mtu = mtu; |
| 178 | rv = ioctl(unixfd, SIOCSIFMTU, &tap_ifr); |
| 179 | ASSERT_EQ(errno, 0); |
| 180 | ASSERT_EQ(rv, 0); |
| 181 | |
| 182 | rv = ioctl(unixfd, SIOCGIFFLAGS, &tun_ifr); |
| 183 | ASSERT_EQ(errno, 0); |
| 184 | ASSERT_EQ(rv, 0); |
| 185 | |
| 186 | rv = ioctl(unixfd, SIOCGIFFLAGS, &tap_ifr); |
| 187 | ASSERT_EQ(errno, 0); |
| 188 | ASSERT_EQ(rv, 0); |
| 189 | |
| 190 | tun_ifr.ifr_flags |= IFF_UP | IFF_RUNNING; |
| 191 | |
| 192 | tap_ifr.ifr_flags |= IFF_UP | IFF_RUNNING; |
| 193 | |
| 194 | rv = ioctl(unixfd, SIOCSIFFLAGS, &tun_ifr); |
| 195 | ASSERT_EQ(errno, 0); |
| 196 | ASSERT_EQ(rv, 0); |
| 197 | |
| 198 | rv = ioctl(unixfd, SIOCSIFFLAGS, &tap_ifr); |
| 199 | ASSERT_EQ(errno, 0); |
| 200 | ASSERT_EQ(rv, 0); |
| 201 | |
| 202 | rv = tcQdiscAddDevClsact(tunif); |
| 203 | ASSERT_EQ(rv, 0); |
| 204 | |
| 205 | int bpfFd = getTetherIngressProgFd(/* ethernet */ false); |
| 206 | ASSERT_EQ(errno, 0); |
| 207 | ASSERT_GE(bpfFd, 3); |
| 208 | |
| 209 | rv = tcFilterAddDevIngressTether(tunif, bpfFd, /* ethernet*/ false); |
| 210 | ASSERT_EQ(rv, 0); |
| 211 | |
| 212 | bpf::BpfMap<TetherIngressKey, TetherIngressValue> bpfIngressMap; |
| 213 | bpf::BpfMap<uint32_t, TetherStatsValue> bpfStatsMap; |
| 214 | bpf::BpfMap<uint32_t, uint64_t> bpfLimitMap; |
| 215 | |
| 216 | rv = getTetherIngressMapFd(); |
| 217 | ASSERT_GE(rv, 3); |
| 218 | bpfIngressMap.reset(rv); |
| 219 | |
| 220 | rv = getTetherStatsMapFd(); |
| 221 | ASSERT_GE(rv, 3); |
| 222 | bpfStatsMap.reset(rv); |
| 223 | |
| 224 | rv = getTetherLimitMapFd(); |
| 225 | ASSERT_GE(rv, 3); |
| 226 | bpfLimitMap.reset(rv); |
| 227 | |
| 228 | TetherIngressKey key = { |
| 229 | .iif = static_cast<uint32_t>(tunif), |
| 230 | //.neigh6 = , |
| 231 | }; |
| 232 | |
| 233 | ethhdr hdr = { |
| 234 | .h_proto = htons(ETH_P_IPV6), |
| 235 | }; |
| 236 | |
| 237 | TetherIngressValue value = { |
| 238 | .oif = static_cast<uint32_t>(tapif), |
| 239 | .macHeader = hdr, |
| 240 | .pmtu = mtu, |
| 241 | }; |
| 242 | |
| 243 | #define ASSERT_OK(status) ASSERT_TRUE((status).ok()) |
| 244 | |
| 245 | ASSERT_OK(bpfIngressMap.writeValue(key, value, BPF_ANY)); |
| 246 | |
| 247 | uint32_t k = tunif; |
| 248 | TetherStatsValue stats = {}; |
| 249 | ASSERT_OK(bpfStatsMap.writeValue(k, stats, BPF_NOEXIST)); |
| 250 | |
| 251 | uint64_t limit = ~0uLL; |
| 252 | ASSERT_OK(bpfLimitMap.writeValue(k, limit, BPF_NOEXIST)); |
| 253 | |
Maciej Żenczykowski | a28a1da | 2020-06-03 10:18:45 +0000 | [diff] [blame] | 254 | // minimal 'acceptable' 40-byte hoplimit 255 IPv6 packet, src ip 2000:: |
Maciej Żenczykowski | 8d6ed4a | 2020-05-29 00:41:22 -0700 | [diff] [blame] | 255 | uint8_t pkt[mtu] = { |
Maciej Żenczykowski | a28a1da | 2020-06-03 10:18:45 +0000 | [diff] [blame] | 256 | 0x60, 0, 0, 0, 0, 40, 0, 255, 0x20, |
Maciej Żenczykowski | 8d6ed4a | 2020-05-29 00:41:22 -0700 | [diff] [blame] | 257 | }; |
| 258 | |
| 259 | // Iterate over all packet sizes from minimal ipv6 packet to mtu. |
| 260 | // Tethering ebpf program should forward the packet from tun to tap interface. |
| 261 | // TUN is L3, TAP is L2, so it will add a 14 byte ethernet header. |
Maciej Żenczykowski | 834fd1a | 2020-05-30 02:12:31 -0700 | [diff] [blame] | 262 | for (int pkt_size = 40; pkt_size <= mtu; ++pkt_size) { |
Maciej Żenczykowski | 8d6ed4a | 2020-05-29 00:41:22 -0700 | [diff] [blame] | 263 | rv = write(tun, pkt, pkt_size); |
| 264 | ASSERT_EQ(errno, 0); |
| 265 | ASSERT_EQ(rv, pkt_size); |
| 266 | |
| 267 | struct pollfd p = { |
| 268 | .fd = tap, |
| 269 | .events = POLLIN, |
| 270 | }; |
| 271 | |
| 272 | rv = poll(&p, 1, 1000 /*milliseconds*/); |
| 273 | if (rv == 0) { |
| 274 | // we hit a timeout at this packet size, log it |
| 275 | EXPECT_EQ(pkt_size, -1); |
| 276 | // this particular packet size is where it fails without the oneline kernel fix |
| 277 | if (pkt_size + ETH_HLEN == mtu + 1) EXPECT_EQ("detected missing kernel patch", ""); |
| 278 | break; |
| 279 | } |
| 280 | EXPECT_EQ(errno, 0); |
| 281 | EXPECT_EQ(rv, 1); |
| 282 | EXPECT_EQ(p.revents, POLLIN); |
| 283 | |
| 284 | // use a buffer 1 byte larger then what we expect so we don't simply get truncated down |
| 285 | uint8_t buf[ETH_HLEN + mtu + 1]; |
| 286 | rv = read(tap, buf, sizeof(buf)); |
| 287 | EXPECT_EQ(errno, 0); |
| 288 | EXPECT_EQ(rv, ETH_HLEN + pkt_size); |
| 289 | errno = 0; |
| 290 | if (rv < 0) break; |
| 291 | } |
| 292 | |
| 293 | ASSERT_OK(bpfIngressMap.deleteValue(key)); |
| 294 | ASSERT_OK(bpfStatsMap.deleteValue(k)); |
| 295 | ASSERT_OK(bpfLimitMap.deleteValue(k)); |
| 296 | } |
| 297 | |
| 298 | } // namespace net |
| 299 | } // namespace android |