Merge changes I24816b66,I84ddec9a,Iae64b900,I3bd83e19,Idc910478 into main

* changes:
  NetBpfLoad: introduce APEXROOT/BPFROOT defines
  NetBpfLoad: sort BPF_PROG_LOAD bpf_attr req field order
  NetBpfLoad: remove spurious header includes
  dscpPolicy: switch to bool
  bpf: git mv tests/mts bpf/tests/mts
This commit is contained in:
Maciej Żenczykowski 2024-08-27 17:57:50 +00:00 • committed by Gerrit Code Review
commit 9734ebfcc7
7 changed files with 35 additions and 52 deletions

View file

@ -17,7 +17,6 @@
#define LOG_TAG "NetBpfLoad"
#include <arpa/inet.h>
#include <cstdlib>
#include <dirent.h>
#include <elf.h>
#include <errno.h>
@ -26,8 +25,6 @@
#include <fstream>
#include <inttypes.h>
#include <iostream>
#include <linux/bpf.h>
#include <linux/elf.h>
#include <linux/unistd.h>
#include <log/log.h>
#include <net/if.h>
@ -993,13 +990,13 @@ static int loadCodeSections(const char* elfPath, vector<codeSection>& cs, const
union bpf_attr req = {
.prog_type = cs[i].type,
.kern_version = kvers,
.license = ptr_to_u64(license.c_str()),
.insns = ptr_to_u64(cs[i].data.data()),
.insn_cnt = static_cast<__u32>(cs[i].data.size() / sizeof(struct bpf_insn)),
.insns = ptr_to_u64(cs[i].data.data()),
.license = ptr_to_u64(license.c_str()),
.log_level = 1,
.log_buf = ptr_to_u64(log_buf.data()),
.log_size = static_cast<__u32>(log_buf.size()),
.log_buf = ptr_to_u64(log_buf.data()),
.kern_version = kvers,
.expected_attach_type = cs[i].attach_type,
};
if (isAtLeastKernelVersion(4, 15, 0))
@ -1155,33 +1152,35 @@ static bool exists(const char* const path) {
abort(); // can only hit this if permissions (likely selinux) are screwed up
}
#define APEXROOT "/apex/com.android.tethering"
#define BPFROOT APEXROOT "/etc/bpf"
const Location locations[] = {
// S+ Tethering mainline module (network_stack): tether offload
{
.dir = "/apex/com.android.tethering/etc/bpf/",
.dir = BPFROOT "/",
.prefix = "tethering/",
},
// T+ Tethering mainline module (shared with netd & system server)
// netutils_wrapper (for iptables xt_bpf) has access to programs
{
.dir = "/apex/com.android.tethering/etc/bpf/netd_shared/",
.dir = BPFROOT "/netd_shared/",
.prefix = "netd_shared/",
},
// T+ Tethering mainline module (shared with netd & system server)
// netutils_wrapper has no access, netd has read only access
{
.dir = "/apex/com.android.tethering/etc/bpf/netd_readonly/",
.dir = BPFROOT "/netd_readonly/",
.prefix = "netd_readonly/",
},
// T+ Tethering mainline module (shared with system server)
{
.dir = "/apex/com.android.tethering/etc/bpf/net_shared/",
.dir = BPFROOT "/net_shared/",
.prefix = "net_shared/",
},
// T+ Tethering mainline module (not shared, just network_stack)
{
.dir = "/apex/com.android.tethering/etc/bpf/net_private/",
.dir = BPFROOT "/net_private/",
.prefix = "net_private/",
},
};

View file

@ -155,19 +155,19 @@ static inline __always_inline void match_policy(struct __sk_buff* skb, const boo
int score = 0;
if (policy->present_fields & PROTO_MASK_FLAG) {
if (policy->match_proto) {
if (protocol != policy->proto) continue;
score += 0xFFFF;
}
if (policy->present_fields & SRC_IP_MASK_FLAG) {
if (policy->match_src_ip) {
if (v6_not_equal(src_ip, policy->src_ip)) continue;
score += 0xFFFF;
}
if (policy->present_fields & DST_IP_MASK_FLAG) {
if (policy->match_dst_ip) {
if (v6_not_equal(dst_ip, policy->dst_ip)) continue;
score += 0xFFFF;
}
if (policy->present_fields & SRC_PORT_MASK_FLAG) {
if (policy->match_src_port) {
if (sport != policy->src_port) continue;
score += 0xFFFF;
}

View file

@ -17,11 +17,6 @@
#define CACHE_MAP_SIZE 1024
#define MAX_POLICIES 16
#define SRC_IP_MASK_FLAG 1
#define DST_IP_MASK_FLAG 2
#define SRC_PORT_MASK_FLAG 4
#define PROTO_MASK_FLAG 8
#define STRUCT_SIZE(name, size) _Static_assert(sizeof(name) == (size), "Incorrect struct size.")
// Retrieve the first (ie. high) 64 bits of an IPv6 address (in network order)
@ -46,10 +41,12 @@ typedef struct {
uint16_t dst_port_end;
uint8_t proto;
int8_t dscp_val; // -1 none, or 0..63 DSCP value
uint8_t present_fields;
uint8_t pad[3];
bool match_src_ip;
bool match_dst_ip;
bool match_src_port;
bool match_proto;
} DscpPolicy;
STRUCT_SIZE(DscpPolicy, 2 * 16 + 4 + 3 * 2 + 3 * 1 + 3); // 48
STRUCT_SIZE(DscpPolicy, 2 * 16 + 4 + 3 * 2 + 6 * 1); // 48
typedef struct {
struct in6_addr src_ip;
@ -61,4 +58,4 @@ typedef struct {
int8_t dscp_val; // -1 none, or 0..63 DSCP value
uint8_t pad[2];
} RuleEntry;
STRUCT_SIZE(RuleEntry, 2 * 16 + 1 * 4 + 2 * 2 + 2 * 1 + 2); // 44
STRUCT_SIZE(RuleEntry, 2 * 16 + 4 + 2 * 2 + 4 * 1); // 44

45
bpf/tests/mts/Android.bp Normal file
View file

@ -0,0 +1,45 @@
// Copyright (C) 2022 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
package {
default_team: "trendy_team_fwk_core_networking",
default_applicable_licenses: ["Android-Apache-2.0"],
}
cc_test {
name: "bpf_existence_test",
test_suites: [
"general-tests",
"mts-tethering",
],
defaults: [
"connectivity-mainline-presubmit-cc-defaults",
],
require_root: true,
header_libs: [
"bpf_headers",
],
version_script: ":connectivity_mainline_test_map",
stl: "libc++_static",
static_libs: [
"libbase",
"libmodules-utils-build",
],
srcs: [
"bpf_existence_test.cpp",
],
compile_multilib: "both",
min_sdk_version: "30", // Ensure test runs on R and above.
}

2
bpf/tests/mts/OWNERS Normal file
View file

@ -0,0 +1,2 @@
# Bug template url: http://b/new?component=31808
# TODO: move bug template config to common owners file once b/226427845 is resolved

View file

@ -0,0 +1,229 @@
/*
* Copyright 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* bpf_existence_test.cpp - checks that the device has expected BPF programs and maps
*/
#include <cstdint>
#include <set>
#include <string>
#include <android-modules-utils/sdk_level.h>
#include <bpf/BpfUtils.h>
#include <gtest/gtest.h>
using std::find;
using std::set;
using std::string;
using android::bpf::isAtLeastKernelVersion;
using android::modules::sdklevel::IsAtLeastR;
using android::modules::sdklevel::IsAtLeastS;
using android::modules::sdklevel::IsAtLeastT;
using android::modules::sdklevel::IsAtLeastU;
using android::modules::sdklevel::IsAtLeastV;
#define PLATFORM "/sys/fs/bpf/"
#define TETHERING "/sys/fs/bpf/tethering/"
#define PRIVATE "/sys/fs/bpf/net_private/"
#define SHARED "/sys/fs/bpf/net_shared/"
#define NETD_RO "/sys/fs/bpf/netd_readonly/"
#define NETD "/sys/fs/bpf/netd_shared/"
class BpfExistenceTest : public ::testing::Test {
};
// Part of Android R platform (for 4.9+), but mainlined in S
static const set<string> PLATFORM_ONLY_IN_R = {
PLATFORM "map_offload_tether_ingress_map",
PLATFORM "map_offload_tether_limit_map",
PLATFORM "map_offload_tether_stats_map",
PLATFORM "prog_offload_schedcls_ingress_tether_ether",
PLATFORM "prog_offload_schedcls_ingress_tether_rawip",
};
// Provided by *current* mainline module for S+ devices
static const set<string> MAINLINE_FOR_S_PLUS = {
TETHERING "map_offload_tether_dev_map",
TETHERING "map_offload_tether_downstream4_map",
TETHERING "map_offload_tether_downstream64_map",
TETHERING "map_offload_tether_downstream6_map",
TETHERING "map_offload_tether_error_map",
TETHERING "map_offload_tether_limit_map",
TETHERING "map_offload_tether_stats_map",
TETHERING "map_offload_tether_upstream4_map",
TETHERING "map_offload_tether_upstream6_map",
TETHERING "map_test_bitmap",
TETHERING "map_test_tether_downstream6_map",
TETHERING "map_test_tether2_downstream6_map",
TETHERING "map_test_tether3_downstream6_map",
TETHERING "prog_offload_schedcls_tether_downstream4_ether",
TETHERING "prog_offload_schedcls_tether_downstream4_rawip",
TETHERING "prog_offload_schedcls_tether_downstream6_ether",
TETHERING "prog_offload_schedcls_tether_downstream6_rawip",
TETHERING "prog_offload_schedcls_tether_upstream4_ether",
TETHERING "prog_offload_schedcls_tether_upstream4_rawip",
TETHERING "prog_offload_schedcls_tether_upstream6_ether",
TETHERING "prog_offload_schedcls_tether_upstream6_rawip",
};
// Provided by *current* mainline module for S+ devices with 5.10+ kernels
static const set<string> MAINLINE_FOR_S_5_10_PLUS = {
TETHERING "prog_test_xdp_drop_ipv4_udp_ether",
};
// Provided by *current* mainline module for T+ devices
static const set<string> MAINLINE_FOR_T_PLUS = {
SHARED "map_block_blocked_ports_map",
SHARED "map_clatd_clat_egress4_map",
SHARED "map_clatd_clat_ingress6_map",
SHARED "map_dscpPolicy_ipv4_dscp_policies_map",
SHARED "map_dscpPolicy_ipv6_dscp_policies_map",
SHARED "map_dscpPolicy_socket_policy_cache_map",
NETD "map_netd_app_uid_stats_map",
NETD "map_netd_configuration_map",
NETD "map_netd_cookie_tag_map",
NETD "map_netd_data_saver_enabled_map",
NETD "map_netd_iface_index_name_map",
NETD "map_netd_iface_stats_map",
NETD "map_netd_ingress_discard_map",
NETD "map_netd_stats_map_A",
NETD "map_netd_stats_map_B",
NETD "map_netd_uid_counterset_map",
NETD "map_netd_uid_owner_map",
NETD "map_netd_uid_permission_map",
SHARED "prog_clatd_schedcls_egress4_clat_rawip",
SHARED "prog_clatd_schedcls_ingress6_clat_ether",
SHARED "prog_clatd_schedcls_ingress6_clat_rawip",
NETD "prog_netd_cgroupskb_egress_stats",
NETD "prog_netd_cgroupskb_ingress_stats",
NETD "prog_netd_schedact_ingress_account",
NETD "prog_netd_skfilter_allowlist_xtbpf",
NETD "prog_netd_skfilter_denylist_xtbpf",
NETD "prog_netd_skfilter_egress_xtbpf",
NETD "prog_netd_skfilter_ingress_xtbpf",
};
// Provided by *current* mainline module for T+ devices with 4.14+ kernels
static const set<string> MAINLINE_FOR_T_4_14_PLUS = {
NETD "prog_netd_cgroupsock_inet_create",
};
// Provided by *current* mainline module for T+ devices with 5.4+ kernels
static const set<string> MAINLINE_FOR_T_4_19_PLUS = {
NETD_RO "prog_block_bind4_block_port",
NETD_RO "prog_block_bind6_block_port",
};
// Provided by *current* mainline module for T+ devices with 5.15+ kernels
static const set<string> MAINLINE_FOR_T_5_15_PLUS = {
SHARED "prog_dscpPolicy_schedcls_set_dscp_ether",
};
// Provided by *current* mainline module for U+ devices
static const set<string> MAINLINE_FOR_U_PLUS = {
NETD "map_netd_packet_trace_enabled_map",
};
// Provided by *current* mainline module for U+ devices with 5.10+ kernels
static const set<string> MAINLINE_FOR_U_5_10_PLUS = {
NETD "map_netd_packet_trace_ringbuf",
};
// Provided by *current* mainline module for V+ devices
static const set<string> MAINLINE_FOR_V_PLUS = {
NETD "prog_netd_connect4_inet4_connect",
NETD "prog_netd_connect6_inet6_connect",
NETD "prog_netd_recvmsg4_udp4_recvmsg",
NETD "prog_netd_recvmsg6_udp6_recvmsg",
NETD "prog_netd_sendmsg4_udp4_sendmsg",
NETD "prog_netd_sendmsg6_udp6_sendmsg",
};
// Provided by *current* mainline module for V+ devices with 5.4+ kernels
static const set<string> MAINLINE_FOR_V_5_4_PLUS = {
NETD "prog_netd_getsockopt_prog",
NETD "prog_netd_setsockopt_prog",
};
// Provided by *current* mainline module for U+ devices with 5.10+ kernels
static const set<string> MAINLINE_FOR_V_5_10_PLUS = {
NETD "prog_netd_cgroupsockrelease_inet_release",
};
static void addAll(set<string>& a, const set<string>& b) {
a.insert(b.begin(), b.end());
}
#define DO_EXPECT(B, V) addAll((B) ? mustExist : mustNotExist, (V))
TEST_F(BpfExistenceTest, TestPrograms) {
// Only unconfined root is guaranteed to be able to access everything in /sys/fs/bpf.
ASSERT_EQ(0, getuid()) << "This test must run as root.";
set<string> mustExist;
set<string> mustNotExist;
// We do not actually check the platform P/Q (netd) and Q (clatd) things
// and only verify the mainline module relevant R+ offload maps & progs.
//
// The goal of this test is to verify compatibility with the tethering mainline module,
// and not to test the platform itself, which may have been modified by vendor or oems,
// so we should only test for the removal of stuff that was mainline'd,
// and for the presence of mainline stuff.
// Note: Q is no longer supported by mainline
ASSERT_TRUE(IsAtLeastR());
// R can potentially run on pre-4.9 kernel non-eBPF capable devices.
DO_EXPECT(IsAtLeastR() && !IsAtLeastS() && isAtLeastKernelVersion(4, 9, 0), PLATFORM_ONLY_IN_R);
// S requires Linux Kernel 4.9+ and thus requires eBPF support.
if (IsAtLeastS()) ASSERT_TRUE(isAtLeastKernelVersion(4, 9, 0));
DO_EXPECT(IsAtLeastS(), MAINLINE_FOR_S_PLUS);
DO_EXPECT(IsAtLeastS() && isAtLeastKernelVersion(5, 10, 0), MAINLINE_FOR_S_5_10_PLUS);
// Nothing added or removed in SCv2.
// T still only requires Linux Kernel 4.9+.
DO_EXPECT(IsAtLeastT(), MAINLINE_FOR_T_PLUS);
DO_EXPECT(IsAtLeastT() && isAtLeastKernelVersion(4, 14, 0), MAINLINE_FOR_T_4_14_PLUS);
DO_EXPECT(IsAtLeastT() && isAtLeastKernelVersion(4, 19, 0), MAINLINE_FOR_T_4_19_PLUS);
DO_EXPECT(IsAtLeastT() && isAtLeastKernelVersion(5, 15, 0), MAINLINE_FOR_T_5_15_PLUS);
// U requires Linux Kernel 4.14+, but nothing (as yet) added or removed in U.
if (IsAtLeastU()) ASSERT_TRUE(isAtLeastKernelVersion(4, 14, 0));
DO_EXPECT(IsAtLeastU(), MAINLINE_FOR_U_PLUS);
DO_EXPECT(IsAtLeastU() && isAtLeastKernelVersion(5, 10, 0), MAINLINE_FOR_U_5_10_PLUS);
// V requires Linux Kernel 4.19+, but nothing (as yet) added or removed in V.
if (IsAtLeastV()) ASSERT_TRUE(isAtLeastKernelVersion(4, 19, 0));
DO_EXPECT(IsAtLeastV(), MAINLINE_FOR_V_PLUS);
DO_EXPECT(IsAtLeastV() && isAtLeastKernelVersion(5, 4, 0), MAINLINE_FOR_V_5_4_PLUS);
DO_EXPECT(IsAtLeastV() && isAtLeastKernelVersion(5, 10, 0), MAINLINE_FOR_V_5_10_PLUS);
for (const auto& file : mustExist) {
EXPECT_EQ(0, access(file.c_str(), R_OK)) << file << " does not exist";
}
for (const auto& file : mustNotExist) {
int ret = access(file.c_str(), R_OK);
int err = errno;
EXPECT_EQ(-1, ret) << file << " unexpectedly exists";
if (ret == -1) {
EXPECT_EQ(ENOENT, err) << " accessing " << file << " failed with errno " << err;
}
}
}

View file

@ -55,13 +55,17 @@ public class DscpPolicyValue extends Struct {
@Field(order = 7, type = Type.S8)
public final byte dscp;
@Field(order = 8, type = Type.U8, padding = 3)
public final short mask;
@Field(order = 8, type = Type.Bool)
public final boolean match_src_ip;
private static final int SRC_IP_MASK = 0x1;
private static final int DST_IP_MASK = 0x02;
private static final int SRC_PORT_MASK = 0x4;
private static final int PROTO_MASK = 0x8;
@Field(order = 9, type = Type.Bool)
public final boolean match_dst_ip;
@Field(order = 10, type = Type.Bool)
public final boolean match_src_port;
@Field(order = 11, type = Type.Bool)
public final boolean match_proto;
private boolean ipEmpty(final byte[] ip) {
for (int i = 0; i < ip.length; i++) {
@ -98,24 +102,6 @@ public class DscpPolicyValue extends Struct {
private static final byte[] EMPTY_ADDRESS_FIELD =
InetAddress.parseNumericAddress("::").getAddress();
private short makeMask(final byte[] src46, final byte[] dst46, final int srcPort,
final int dstPortStart, final short proto, final byte dscp) {
short mask = 0;
if (src46 != EMPTY_ADDRESS_FIELD) {
mask |= SRC_IP_MASK;
}
if (dst46 != EMPTY_ADDRESS_FIELD) {
mask |= DST_IP_MASK;
}
if (srcPort != -1) {
mask |= SRC_PORT_MASK;
}
if (proto != -1) {
mask |= PROTO_MASK;
}
return mask;
}
private DscpPolicyValue(final InetAddress src46, final InetAddress dst46, final int ifIndex,
final int srcPort, final int dstPortStart, final int dstPortEnd, final short proto,
final byte dscp) {
@ -131,9 +117,10 @@ public class DscpPolicyValue extends Struct {
this.proto = proto != -1 ? proto : 0;
this.dscp = dscp;
// Use member variables for IP since byte[] is needed and api variables for everything else
// so -1 is passed into mask if parameter is not present.
this.mask = makeMask(this.src46, this.dst46, srcPort, dstPortStart, proto, dscp);
this.match_src_ip = (this.src46 != EMPTY_ADDRESS_FIELD);
this.match_dst_ip = (this.dst46 != EMPTY_ADDRESS_FIELD);
this.match_src_port = (srcPort != -1);
this.match_proto = (proto != -1);
}
public DscpPolicyValue(final InetAddress src46, final InetAddress dst46, final int ifIndex,