Merge changes Ibb8d33b7,Ie168fe1f,I9f699b63 into main
* changes:
Prepare exposing Network{Request,Caps}.forbiddenCapabilities
Add a keep connected for test reason
Improvements to CSTest : legacy type, wait for LOST, permissions
diff --git a/Tethering/apex/Android.bp b/Tethering/apex/Android.bp
index 9757daa..9132857 100644
--- a/Tethering/apex/Android.bp
+++ b/Tethering/apex/Android.bp
@@ -96,6 +96,7 @@
},
binaries: [
"clatd",
+ "netbpfload",
"ot-daemon",
],
canned_fs_config: "canned_fs_config",
diff --git a/Tethering/apex/canned_fs_config b/Tethering/apex/canned_fs_config
index 5a03347..1f5fcfa 100644
--- a/Tethering/apex/canned_fs_config
+++ b/Tethering/apex/canned_fs_config
@@ -1,2 +1,3 @@
/bin/for-system 0 1000 0750
/bin/for-system/clatd 1029 1029 06755
+/bin/netbpfload 0 0 0750
diff --git a/Tethering/common/TetheringLib/api/current.txt b/Tethering/common/TetheringLib/api/current.txt
index d802177..14191eb 100644
--- a/Tethering/common/TetheringLib/api/current.txt
+++ b/Tethering/common/TetheringLib/api/current.txt
@@ -1 +1,3 @@
// Signature format: 2.0
+// - add-additional-overrides=no
+// - migrating=Migration in progress see b/299366704
diff --git a/Tethering/common/TetheringLib/api/module-lib-current.txt b/Tethering/common/TetheringLib/api/module-lib-current.txt
index 460c216..f09b26d 100644
--- a/Tethering/common/TetheringLib/api/module-lib-current.txt
+++ b/Tethering/common/TetheringLib/api/module-lib-current.txt
@@ -1,4 +1,6 @@
// Signature format: 2.0
+// - add-additional-overrides=no
+// - migrating=Migration in progress see b/299366704
package android.net {
public final class TetheringConstants {
diff --git a/Tethering/common/TetheringLib/api/module-lib-removed.txt b/Tethering/common/TetheringLib/api/module-lib-removed.txt
index d802177..14191eb 100644
--- a/Tethering/common/TetheringLib/api/module-lib-removed.txt
+++ b/Tethering/common/TetheringLib/api/module-lib-removed.txt
@@ -1 +1,3 @@
// Signature format: 2.0
+// - add-additional-overrides=no
+// - migrating=Migration in progress see b/299366704
diff --git a/Tethering/common/TetheringLib/api/removed.txt b/Tethering/common/TetheringLib/api/removed.txt
index d802177..14191eb 100644
--- a/Tethering/common/TetheringLib/api/removed.txt
+++ b/Tethering/common/TetheringLib/api/removed.txt
@@ -1 +1,3 @@
// Signature format: 2.0
+// - add-additional-overrides=no
+// - migrating=Migration in progress see b/299366704
diff --git a/Tethering/common/TetheringLib/api/system-current.txt b/Tethering/common/TetheringLib/api/system-current.txt
index 844ff64..83cee25 100644
--- a/Tethering/common/TetheringLib/api/system-current.txt
+++ b/Tethering/common/TetheringLib/api/system-current.txt
@@ -1,4 +1,6 @@
// Signature format: 2.0
+// - add-additional-overrides=no
+// - migrating=Migration in progress see b/299366704
package android.net {
public final class TetheredClient implements android.os.Parcelable {
diff --git a/Tethering/common/TetheringLib/api/system-removed.txt b/Tethering/common/TetheringLib/api/system-removed.txt
index d802177..14191eb 100644
--- a/Tethering/common/TetheringLib/api/system-removed.txt
+++ b/Tethering/common/TetheringLib/api/system-removed.txt
@@ -1 +1,3 @@
// Signature format: 2.0
+// - add-additional-overrides=no
+// - migrating=Migration in progress see b/299366704
diff --git a/bpf_progs/bpf_net_helpers.h b/bpf_progs/bpf_net_helpers.h
index 0a31861..f3c7de5 100644
--- a/bpf_progs/bpf_net_helpers.h
+++ b/bpf_progs/bpf_net_helpers.h
@@ -91,14 +91,14 @@
#define INGRESS ((struct egress_bool){ .egress = false })
#define EGRESS ((struct egress_bool){ .egress = true })
-// constants for passing in to 'bool downstream'
-static const bool UPSTREAM = false;
-static const bool DOWNSTREAM = true;
+struct stream_bool { bool down; };
+#define UPSTREAM ((struct stream_bool){ .down = false })
+#define DOWNSTREAM ((struct stream_bool){ .down = true })
struct rawip_bool { bool rawip; };
#define ETHER ((struct rawip_bool){ .rawip = false })
#define RAWIP ((struct rawip_bool){ .rawip = true })
-// constants for passing in to 'bool updatetime'
-static const bool NO_UPDATETIME = false;
-static const bool UPDATETIME = true;
+struct updatetime_bool { bool updatetime; };
+#define NO_UPDATETIME ((struct updatetime_bool){ .updatetime = false })
+#define UPDATETIME ((struct updatetime_bool){ .updatetime = true })
diff --git a/bpf_progs/offload.c b/bpf_progs/offload.c
index 3682ac5..35b8eea 100644
--- a/bpf_progs/offload.c
+++ b/bpf_progs/offload.c
@@ -126,7 +126,7 @@
static inline __always_inline int do_forward6(struct __sk_buff* skb,
const struct rawip_bool rawip,
- const bool downstream,
+ const struct stream_bool stream,
const struct kver_uint kver) {
const bool is_ethernet = !rawip.rawip;
@@ -188,7 +188,7 @@
TC_PUNT(NON_GLOBAL_DST);
// In the upstream direction do not forward traffic within the same /64 subnet.
- if (!downstream && (src32 == dst32) && (ip6->saddr.s6_addr32[1] == ip6->daddr.s6_addr32[1]))
+ if (!stream.down && (src32 == dst32) && (ip6->saddr.s6_addr32[1] == ip6->daddr.s6_addr32[1]))
TC_PUNT(LOCAL_SRC_DST);
TetherDownstream6Key kd = {
@@ -200,15 +200,15 @@
.iif = skb->ifindex,
.src64 = 0,
};
- if (is_ethernet) __builtin_memcpy(downstream ? kd.dstMac : ku.dstMac, eth->h_dest, ETH_ALEN);
+ if (is_ethernet) __builtin_memcpy(stream.down ? kd.dstMac : ku.dstMac, eth->h_dest, ETH_ALEN);
- Tether6Value* v = downstream ? bpf_tether_downstream6_map_lookup_elem(&kd)
- : bpf_tether_upstream6_map_lookup_elem(&ku);
+ Tether6Value* v = stream.down ? bpf_tether_downstream6_map_lookup_elem(&kd)
+ : bpf_tether_upstream6_map_lookup_elem(&ku);
// If we don't find any offload information then simply let the core stack handle it...
if (!v) return TC_ACT_PIPE;
- uint32_t stat_and_limit_k = downstream ? skb->ifindex : v->oif;
+ uint32_t stat_and_limit_k = stream.down ? skb->ifindex : v->oif;
TetherStatsValue* stat_v = bpf_tether_stats_map_lookup_elem(&stat_and_limit_k);
@@ -253,7 +253,7 @@
// We do this even if TX interface is RAWIP and thus does not need an ethernet header,
// because this is easier and the kernel will strip extraneous ethernet header.
if (bpf_skb_change_head(skb, sizeof(struct ethhdr), /*flags*/ 0)) {
- __sync_fetch_and_add(downstream ? &stat_v->rxErrors : &stat_v->txErrors, 1);
+ __sync_fetch_and_add(stream.down ? &stat_v->rxErrors : &stat_v->txErrors, 1);
TC_PUNT(CHANGE_HEAD_FAILED);
}
@@ -265,7 +265,7 @@
// I do not believe this can ever happen, but keep the verifier happy...
if (data + sizeof(struct ethhdr) + sizeof(*ip6) > data_end) {
- __sync_fetch_and_add(downstream ? &stat_v->rxErrors : &stat_v->txErrors, 1);
+ __sync_fetch_and_add(stream.down ? &stat_v->rxErrors : &stat_v->txErrors, 1);
TC_DROP(TOO_SHORT);
}
};
@@ -285,8 +285,8 @@
// (-ENOTSUPP) if it isn't.
bpf_csum_update(skb, 0xFFFF - ntohs(old_hl) + ntohs(new_hl));
- __sync_fetch_and_add(downstream ? &stat_v->rxPackets : &stat_v->txPackets, packets);
- __sync_fetch_and_add(downstream ? &stat_v->rxBytes : &stat_v->txBytes, L3_bytes);
+ __sync_fetch_and_add(stream.down ? &stat_v->rxPackets : &stat_v->txPackets, packets);
+ __sync_fetch_and_add(stream.down ? &stat_v->rxBytes : &stat_v->txBytes, L3_bytes);
// Overwrite any mac header with the new one
// For a rawip tx interface it will simply be a bunch of zeroes and later stripped.
@@ -361,8 +361,8 @@
static inline __always_inline int do_forward4_bottom(struct __sk_buff* skb,
const int l2_header_size, void* data, const void* data_end,
struct ethhdr* eth, struct iphdr* ip, const struct rawip_bool rawip,
- const bool downstream, const bool updatetime, const bool is_tcp,
- const struct kver_uint kver) {
+ const struct stream_bool stream, const struct updatetime_bool updatetime,
+ const bool is_tcp, const struct kver_uint kver) {
const bool is_ethernet = !rawip.rawip;
struct tcphdr* tcph = is_tcp ? (void*)(ip + 1) : NULL;
struct udphdr* udph = is_tcp ? NULL : (void*)(ip + 1);
@@ -421,13 +421,13 @@
};
if (is_ethernet) __builtin_memcpy(k.dstMac, eth->h_dest, ETH_ALEN);
- Tether4Value* v = downstream ? bpf_tether_downstream4_map_lookup_elem(&k)
- : bpf_tether_upstream4_map_lookup_elem(&k);
+ Tether4Value* v = stream.down ? bpf_tether_downstream4_map_lookup_elem(&k)
+ : bpf_tether_upstream4_map_lookup_elem(&k);
// If we don't find any offload information then simply let the core stack handle it...
if (!v) return TC_ACT_PIPE;
- uint32_t stat_and_limit_k = downstream ? skb->ifindex : v->oif;
+ uint32_t stat_and_limit_k = stream.down ? skb->ifindex : v->oif;
TetherStatsValue* stat_v = bpf_tether_stats_map_lookup_elem(&stat_and_limit_k);
@@ -472,7 +472,7 @@
// We do this even if TX interface is RAWIP and thus does not need an ethernet header,
// because this is easier and the kernel will strip extraneous ethernet header.
if (bpf_skb_change_head(skb, sizeof(struct ethhdr), /*flags*/ 0)) {
- __sync_fetch_and_add(downstream ? &stat_v->rxErrors : &stat_v->txErrors, 1);
+ __sync_fetch_and_add(stream.down ? &stat_v->rxErrors : &stat_v->txErrors, 1);
TC_PUNT(CHANGE_HEAD_FAILED);
}
@@ -486,7 +486,7 @@
// I do not believe this can ever happen, but keep the verifier happy...
if (data + sizeof(struct ethhdr) + sizeof(*ip) + (is_tcp ? sizeof(*tcph) : sizeof(*udph)) > data_end) {
- __sync_fetch_and_add(downstream ? &stat_v->rxErrors : &stat_v->txErrors, 1);
+ __sync_fetch_and_add(stream.down ? &stat_v->rxErrors : &stat_v->txErrors, 1);
TC_DROP(TOO_SHORT);
}
};
@@ -538,10 +538,10 @@
// This requires the bpf_ktime_get_boot_ns() helper which was added in 5.8,
// and backported to all Android Common Kernel 4.14+ trees.
- if (updatetime) v->last_used = bpf_ktime_get_boot_ns();
+ if (updatetime.updatetime) v->last_used = bpf_ktime_get_boot_ns();
- __sync_fetch_and_add(downstream ? &stat_v->rxPackets : &stat_v->txPackets, packets);
- __sync_fetch_and_add(downstream ? &stat_v->rxBytes : &stat_v->txBytes, L3_bytes);
+ __sync_fetch_and_add(stream.down ? &stat_v->rxPackets : &stat_v->txPackets, packets);
+ __sync_fetch_and_add(stream.down ? &stat_v->rxBytes : &stat_v->txBytes, L3_bytes);
// Redirect to forwarded interface.
//
@@ -554,8 +554,8 @@
static inline __always_inline int do_forward4(struct __sk_buff* skb,
const struct rawip_bool rawip,
- const bool downstream,
- const bool updatetime,
+ const struct stream_bool stream,
+ const struct updatetime_bool updatetime,
const struct kver_uint kver) {
const bool is_ethernet = !rawip.rawip;
@@ -616,16 +616,16 @@
// in such a situation we can only support TCP. This also has the added nice benefit of
// using a separate error counter, and thus making it obvious which version of the program
// is loaded.
- if (!updatetime && ip->protocol != IPPROTO_TCP) TC_PUNT(NON_TCP);
+ if (!updatetime.updatetime && ip->protocol != IPPROTO_TCP) TC_PUNT(NON_TCP);
// We do not support offloading anything besides IPv4 TCP and UDP, due to need for NAT,
// but no need to check this if !updatetime due to check immediately above.
- if (updatetime && (ip->protocol != IPPROTO_TCP) && (ip->protocol != IPPROTO_UDP))
+ if (updatetime.updatetime && (ip->protocol != IPPROTO_TCP) && (ip->protocol != IPPROTO_UDP))
TC_PUNT(NON_TCP_UDP);
// We want to make sure that the compiler will, in the !updatetime case, entirely optimize
// out all the non-tcp logic. Also note that at this point is_udp === !is_tcp.
- const bool is_tcp = !updatetime || (ip->protocol == IPPROTO_TCP);
+ const bool is_tcp = !updatetime.updatetime || (ip->protocol == IPPROTO_TCP);
// This is a bit of a hack to make things easier on the bpf verifier.
// (In particular I believe the Linux 4.14 kernel's verifier can get confused later on about
@@ -646,10 +646,10 @@
// if the underlying requisite kernel support (bpf_ktime_get_boot_ns) was backported.
if (is_tcp) {
return do_forward4_bottom(skb, l2_header_size, data, data_end, eth, ip,
- rawip, downstream, updatetime, /* is_tcp */ true, kver);
+ rawip, stream, updatetime, /* is_tcp */ true, kver);
} else {
return do_forward4_bottom(skb, l2_header_size, data, data_end, eth, ip,
- rawip, downstream, updatetime, /* is_tcp */ false, kver);
+ rawip, stream, updatetime, /* is_tcp */ false, kver);
}
}
@@ -808,16 +808,17 @@
DEFINE_BPF_MAP_GRW(tether_dev_map, DEVMAP_HASH, uint32_t, uint32_t, 64, TETHERING_GID)
static inline __always_inline int do_xdp_forward6(struct xdp_md *ctx, const struct rawip_bool rawip,
- const bool downstream) {
+ const struct stream_bool stream) {
return XDP_PASS;
}
static inline __always_inline int do_xdp_forward4(struct xdp_md *ctx, const struct rawip_bool rawip,
- const bool downstream) {
+ const struct stream_bool stream) {
return XDP_PASS;
}
-static inline __always_inline int do_xdp_forward_ether(struct xdp_md *ctx, const bool downstream) {
+static inline __always_inline int do_xdp_forward_ether(struct xdp_md *ctx,
+ const struct stream_bool stream) {
const void* data = (void*)(long)ctx->data;
const void* data_end = (void*)(long)ctx->data_end;
const struct ethhdr* eth = data;
@@ -826,15 +827,16 @@
if ((void*)(eth + 1) > data_end) return XDP_PASS;
if (eth->h_proto == htons(ETH_P_IPV6))
- return do_xdp_forward6(ctx, ETHER, downstream);
+ return do_xdp_forward6(ctx, ETHER, stream);
if (eth->h_proto == htons(ETH_P_IP))
- return do_xdp_forward4(ctx, ETHER, downstream);
+ return do_xdp_forward4(ctx, ETHER, stream);
// Anything else we don't know how to handle...
return XDP_PASS;
}
-static inline __always_inline int do_xdp_forward_rawip(struct xdp_md *ctx, const bool downstream) {
+static inline __always_inline int do_xdp_forward_rawip(struct xdp_md *ctx,
+ const struct stream_bool stream) {
const void* data = (void*)(long)ctx->data;
const void* data_end = (void*)(long)ctx->data_end;
@@ -842,8 +844,8 @@
if (data_end - data < 1) return XDP_PASS;
const uint8_t v = (*(uint8_t*)data) >> 4;
- if (v == 6) return do_xdp_forward6(ctx, RAWIP, downstream);
- if (v == 4) return do_xdp_forward4(ctx, RAWIP, downstream);
+ if (v == 6) return do_xdp_forward6(ctx, RAWIP, stream);
+ if (v == 4) return do_xdp_forward4(ctx, RAWIP, stream);
// Anything else we don't know how to handle...
return XDP_PASS;
diff --git a/framework-t/api/current.txt b/framework-t/api/current.txt
index 86745d4..e4b211f 100644
--- a/framework-t/api/current.txt
+++ b/framework-t/api/current.txt
@@ -1,4 +1,6 @@
// Signature format: 2.0
+// - add-additional-overrides=no
+// - migrating=Migration in progress see b/299366704
package android.app.usage {
public final class NetworkStats implements java.lang.AutoCloseable {
diff --git a/framework-t/api/module-lib-current.txt b/framework-t/api/module-lib-current.txt
index 5a8d47b..fd42a37 100644
--- a/framework-t/api/module-lib-current.txt
+++ b/framework-t/api/module-lib-current.txt
@@ -1,4 +1,6 @@
// Signature format: 2.0
+// - add-additional-overrides=no
+// - migrating=Migration in progress see b/299366704
package android.app.usage {
public class NetworkStatsManager {
diff --git a/framework-t/api/module-lib-removed.txt b/framework-t/api/module-lib-removed.txt
index d802177..14191eb 100644
--- a/framework-t/api/module-lib-removed.txt
+++ b/framework-t/api/module-lib-removed.txt
@@ -1 +1,3 @@
// Signature format: 2.0
+// - add-additional-overrides=no
+// - migrating=Migration in progress see b/299366704
diff --git a/framework-t/api/removed.txt b/framework-t/api/removed.txt
index 1ba87d8..d9d243d 100644
--- a/framework-t/api/removed.txt
+++ b/framework-t/api/removed.txt
@@ -1,4 +1,6 @@
// Signature format: 2.0
+// - add-additional-overrides=no
+// - migrating=Migration in progress see b/299366704
package android.net {
public class TrafficStats {
diff --git a/framework-t/api/system-current.txt b/framework-t/api/system-current.txt
index 53ad834..9bdb595 100644
--- a/framework-t/api/system-current.txt
+++ b/framework-t/api/system-current.txt
@@ -1,4 +1,6 @@
// Signature format: 2.0
+// - add-additional-overrides=no
+// - migrating=Migration in progress see b/299366704
package android.app.usage {
public class NetworkStatsManager {
diff --git a/framework-t/api/system-removed.txt b/framework-t/api/system-removed.txt
index d802177..14191eb 100644
--- a/framework-t/api/system-removed.txt
+++ b/framework-t/api/system-removed.txt
@@ -1 +1,3 @@
// Signature format: 2.0
+// - add-additional-overrides=no
+// - migrating=Migration in progress see b/299366704
diff --git a/framework/api/current.txt b/framework/api/current.txt
index 6860c3c..4f00977 100644
--- a/framework/api/current.txt
+++ b/framework/api/current.txt
@@ -1,4 +1,6 @@
// Signature format: 2.0
+// - add-additional-overrides=no
+// - migrating=Migration in progress see b/299366704
package android.net {
public class CaptivePortal implements android.os.Parcelable {
diff --git a/framework/api/module-lib-current.txt b/framework/api/module-lib-current.txt
index 193bd92..ac57c10 100644
--- a/framework/api/module-lib-current.txt
+++ b/framework/api/module-lib-current.txt
@@ -1,4 +1,6 @@
// Signature format: 2.0
+// - add-additional-overrides=no
+// - migrating=Migration in progress see b/299366704
package android.net {
public final class ConnectivityFrameworkInitializer {
diff --git a/framework/api/module-lib-removed.txt b/framework/api/module-lib-removed.txt
index d802177..14191eb 100644
--- a/framework/api/module-lib-removed.txt
+++ b/framework/api/module-lib-removed.txt
@@ -1 +1,3 @@
// Signature format: 2.0
+// - add-additional-overrides=no
+// - migrating=Migration in progress see b/299366704
diff --git a/framework/api/removed.txt b/framework/api/removed.txt
index 303a1e6..f5da46a 100644
--- a/framework/api/removed.txt
+++ b/framework/api/removed.txt
@@ -1,4 +1,6 @@
// Signature format: 2.0
+// - add-additional-overrides=no
+// - migrating=Migration in progress see b/299366704
package android.net {
public class ConnectivityManager {
diff --git a/framework/api/system-current.txt b/framework/api/system-current.txt
index 4a2ed8a..cd120e9 100644
--- a/framework/api/system-current.txt
+++ b/framework/api/system-current.txt
@@ -1,4 +1,6 @@
// Signature format: 2.0
+// - add-additional-overrides=no
+// - migrating=Migration in progress see b/299366704
package android.net {
public class CaptivePortal implements android.os.Parcelable {
diff --git a/framework/api/system-removed.txt b/framework/api/system-removed.txt
index d802177..14191eb 100644
--- a/framework/api/system-removed.txt
+++ b/framework/api/system-removed.txt
@@ -1 +1,3 @@
// Signature format: 2.0
+// - add-additional-overrides=no
+// - migrating=Migration in progress see b/299366704
diff --git a/netbpfload/Android.bp b/netbpfload/Android.bp
index cc4f5d0..5480ef7 100644
--- a/netbpfload/Android.bp
+++ b/netbpfload/Android.bp
@@ -36,6 +36,13 @@
"loader.cpp",
"NetBpfLoad.cpp",
],
-
- init_rc: ["netbpfload.rc"],
+ apex_available: [
+ "com.android.tethering",
+ "//apex_available:platform",
+ ],
+ // really should be Android 14/U (34), but we cannot include binaries built
+ // against newer sdk in the apex, which still targets 30(R):
+ // module "netbpfload" variant "android_x86_apex30": should support
+ // min_sdk_version(30) for "com.android.tethering": newer SDK(34).
+ min_sdk_version: "30",
}
diff --git a/netbpfload/NetBpfLoad.cpp b/netbpfload/NetBpfLoad.cpp
index 7d9c48e..b44a0bc 100644
--- a/netbpfload/NetBpfLoad.cpp
+++ b/netbpfload/NetBpfLoad.cpp
@@ -65,46 +65,34 @@
abort(); // can only hit this if permissions (likely selinux) are screwed up
}
-constexpr unsigned long long kTetheringApexDomainBitmask =
- domainToBitmask(domain::tethering) |
- domainToBitmask(domain::net_private) |
- domainToBitmask(domain::net_shared) |
- domainToBitmask(domain::netd_readonly) |
- domainToBitmask(domain::netd_shared);
-
const android::bpf::Location locations[] = {
// S+ Tethering mainline module (network_stack): tether offload
{
.dir = "/apex/com.android.tethering/etc/bpf/",
.prefix = "tethering/",
- .allowedDomainBitmask = kTetheringApexDomainBitmask,
},
// 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/",
.prefix = "netd_shared/",
- .allowedDomainBitmask = kTetheringApexDomainBitmask,
},
// 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/",
.prefix = "netd_readonly/",
- .allowedDomainBitmask = kTetheringApexDomainBitmask,
},
// T+ Tethering mainline module (shared with system server)
{
.dir = "/apex/com.android.tethering/etc/bpf/net_shared/",
.prefix = "net_shared/",
- .allowedDomainBitmask = kTetheringApexDomainBitmask,
},
// T+ Tethering mainline module (not shared, just network_stack)
{
.dir = "/apex/com.android.tethering/etc/bpf/net_private/",
.prefix = "net_private/",
- .allowedDomainBitmask = kTetheringApexDomainBitmask,
},
};
@@ -247,13 +235,6 @@
if (createSysFsBpfSubDir(location.prefix)) return 1;
}
- // Note: there's no actual src dir for fs_bpf_loader .o's,
- // so it is not listed in 'locations[].prefix'.
- // This is because this is primarily meant for triggering genfscon rules,
- // and as such this will likely always be the case.
- // Thus we need to manually create the /sys/fs/bpf/loader subdirectory.
- if (createSysFsBpfSubDir("loader")) return 1;
-
// Load all ELF objects, create programs and maps, and pin them
for (const auto& location : locations) {
if (loadAllElfObjects(location) != 0) {
diff --git a/netbpfload/loader.cpp b/netbpfload/loader.cpp
index 9aeb184..c534b2c 100644
--- a/netbpfload/loader.cpp
+++ b/netbpfload/loader.cpp
@@ -621,8 +621,7 @@
}
static int createMaps(const char* elfPath, ifstream& elfFile, vector<unique_fd>& mapFds,
- const char* prefix, const unsigned long long allowedDomainBitmask,
- const size_t sizeOfBpfMapDef) {
+ const char* prefix, const size_t sizeOfBpfMapDef) {
int ret;
vector<char> mdData;
vector<struct bpf_map_def> md;
@@ -733,11 +732,6 @@
domain selinux_context = getDomainFromSelinuxContext(md[i].selinux_context);
if (specified(selinux_context)) {
- if (!inDomainBitmask(selinux_context, allowedDomainBitmask)) {
- ALOGE("map %s has invalid selinux_context of %d (allowed bitmask 0x%llx)",
- mapNames[i].c_str(), selinux_context, allowedDomainBitmask);
- return -EINVAL;
- }
ALOGI("map %s selinux_context [%-32s] -> %d -> '%s' (%s)", mapNames[i].c_str(),
md[i].selinux_context, selinux_context, lookupSelinuxContext(selinux_context),
lookupPinSubdir(selinux_context));
@@ -746,11 +740,6 @@
domain pin_subdir = getDomainFromPinSubdir(md[i].pin_subdir);
if (unrecognized(pin_subdir)) return -ENOTDIR;
if (specified(pin_subdir)) {
- if (!inDomainBitmask(pin_subdir, allowedDomainBitmask)) {
- ALOGE("map %s has invalid pin_subdir of %d (allowed bitmask 0x%llx)",
- mapNames[i].c_str(), pin_subdir, allowedDomainBitmask);
- return -EINVAL;
- }
ALOGI("map %s pin_subdir [%-32s] -> %d -> '%s'", mapNames[i].c_str(), md[i].pin_subdir,
pin_subdir, lookupPinSubdir(pin_subdir));
}
@@ -921,7 +910,7 @@
}
static int loadCodeSections(const char* elfPath, vector<codeSection>& cs, const string& license,
- const char* prefix, const unsigned long long allowedDomainBitmask) {
+ const char* prefix) {
unsigned kvers = kernelVersion();
if (!kvers) {
@@ -980,22 +969,12 @@
if (unrecognized(pin_subdir)) return -ENOTDIR;
if (specified(selinux_context)) {
- if (!inDomainBitmask(selinux_context, allowedDomainBitmask)) {
- ALOGE("prog %s has invalid selinux_context of %d (allowed bitmask 0x%llx)",
- name.c_str(), selinux_context, allowedDomainBitmask);
- return -EINVAL;
- }
ALOGI("prog %s selinux_context [%-32s] -> %d -> '%s' (%s)", name.c_str(),
cs[i].prog_def->selinux_context, selinux_context,
lookupSelinuxContext(selinux_context), lookupPinSubdir(selinux_context));
}
if (specified(pin_subdir)) {
- if (!inDomainBitmask(pin_subdir, allowedDomainBitmask)) {
- ALOGE("prog %s has invalid pin_subdir of %d (allowed bitmask 0x%llx)", name.c_str(),
- pin_subdir, allowedDomainBitmask);
- return -EINVAL;
- }
ALOGI("prog %s pin_subdir [%-32s] -> %d -> '%s'", name.c_str(),
cs[i].prog_def->pin_subdir, pin_subdir, lookupPinSubdir(pin_subdir));
}
@@ -1185,8 +1164,7 @@
/* Just for future debugging */
if (0) dumpAllCs(cs);
- ret = createMaps(elfPath, elfFile, mapFds, location.prefix, location.allowedDomainBitmask,
- sizeOfBpfMapDef);
+ ret = createMaps(elfPath, elfFile, mapFds, location.prefix, sizeOfBpfMapDef);
if (ret) {
ALOGE("Failed to create maps: (ret=%d) in %s", ret, elfPath);
return ret;
@@ -1197,8 +1175,7 @@
applyMapRelo(elfFile, mapFds, cs);
- ret = loadCodeSections(elfPath, cs, string(license.data()), location.prefix,
- location.allowedDomainBitmask);
+ ret = loadCodeSections(elfPath, cs, string(license.data()), location.prefix);
if (ret) ALOGE("Failed to load programs, loadCodeSections ret=%d", ret);
return ret;
diff --git a/netbpfload/loader.h b/netbpfload/loader.h
index 6402cea..b884637 100644
--- a/netbpfload/loader.h
+++ b/netbpfload/loader.h
@@ -64,18 +64,9 @@
return d != domain::unspecified;
}
-static constexpr unsigned long long domainToBitmask(domain d) {
- return specified(d) ? 1uLL << (static_cast<int>(d) - 1) : 0;
-}
-
-static constexpr bool inDomainBitmask(domain d, unsigned long long v) {
- return domainToBitmask(d) & v;
-}
-
struct Location {
const char* const dir = "";
const char* const prefix = "";
- unsigned long long allowedDomainBitmask = 0;
};
// BPF loader implementation. Loads an eBPF ELF object
diff --git a/netbpfload/netbpfload.rc b/netbpfload/netbpfload.rc
deleted file mode 100644
index 20fbb9f..0000000
--- a/netbpfload/netbpfload.rc
+++ /dev/null
@@ -1,85 +0,0 @@
-# zygote-start is what officially starts netd (see //system/core/rootdir/init.rc)
-# However, on some hardware it's started from post-fs-data as well, which is just
-# a tad earlier. There's no benefit to that though, since on 4.9+ P+ devices netd
-# will just block until bpfloader finishes and sets the bpf.progs_loaded property.
-#
-# It is important that we start netbpfload after:
-# - /sys/fs/bpf is already mounted,
-# - apex (incl. rollback) is initialized (so that in the future we can load bpf
-# programs shipped as part of apex mainline modules)
-# - logd is ready for us to log stuff
-#
-# At the same time we want to be as early as possible to reduce races and thus
-# failures (before memory is fragmented, and cpu is busy running tons of other
-# stuff) and we absolutely want to be before netd and the system boot slot is
-# considered to have booted successfully.
-#
-on load_bpf_programs
- exec_start netbpfload
-
-service netbpfload /system/bin/netbpfload
- capabilities CHOWN SYS_ADMIN NET_ADMIN
- # The following group memberships are a workaround for lack of DAC_OVERRIDE
- # and allow us to open (among other things) files that we created and are
- # no longer root owned (due to CHOWN) but still have group read access to
- # one of the following groups. This is not perfect, but a more correct
- # solution requires significantly more effort to implement.
- group root graphics network_stack net_admin net_bw_acct net_bw_stats net_raw system
- user root
- #
- # Set RLIMIT_MEMLOCK to 1GiB for netbpfload
- #
- # Actually only 8MiB would be needed if netbpfload ran as its own uid.
- #
- # However, while the rlimit is per-thread, the accounting is system wide.
- # So, for example, if the graphics stack has already allocated 10MiB of
- # memlock data before netbpfload even gets a chance to run, it would fail
- # if its memlock rlimit is only 8MiB - since there would be none left for it.
- #
- # netbpfload succeeding is critical to system health, since a failure will
- # cause netd crashloop and thus system server crashloop... and the only
- # recovery is a full kernel reboot.
- #
- # We've had issues where devices would sometimes (rarely) boot into
- # a crashloop because netbpfload would occasionally lose a boot time
- # race against the graphics stack's boot time locked memory allocation.
- #
- # Thus netbpfload's memlock has to be 8MB higher then the locked memory
- # consumption of the root uid anywhere else in the system...
- # But we don't know what that is for all possible devices...
- #
- # Ideally, we'd simply grant netbpfload the IPC_LOCK capability and it
- # would simply ignore it's memlock rlimit... but it turns that this
- # capability is not even checked by the kernel's bpf system call.
- #
- # As such we simply use 1GiB as a reasonable approximation of infinity.
- #
- rlimit memlock 1073741824 1073741824
- oneshot
- #
- # How to debug bootloops caused by 'netbpfload-failed'.
- #
- # 1. On some lower RAM devices (like wembley) you may need to first enable developer mode
- # (from the Settings app UI), and change the developer option "Logger buffer sizes"
- # from the default (wembley: 64kB) to the maximum (1M) per log buffer.
- # Otherwise buffer will overflow before you manage to dump it and you'll get useless logs.
- #
- # 2. comment out 'reboot_on_failure reboot,netbpfload-failed' below
- # 3. rebuild/reflash/reboot
- # 4. as the device is booting up capture netbpfload logs via:
- # adb logcat -s 'NetBpfLoad:*' 'NetBpfLoader:*'
- #
- # something like:
- # $ adb reboot; sleep 1; adb wait-for-device; adb root; sleep 1; adb wait-for-device; adb logcat -s 'NetBpfLoad:*' 'NetBpfLoader:*'
- # will take care of capturing logs as early as possible
- #
- # 5. look through the logs from the kernel's bpf verifier that netbpfload dumps out,
- # it usually makes sense to search back from the end and find the particular
- # bpf verifier failure that caused netbpfload to terminate early with an error code.
- # This will probably be something along the lines of 'too many jumps' or
- # 'cannot prove return value is 0 or 1' or 'unsupported / unknown operation / helper',
- # 'invalid bpf_context access', etc.
- #
- reboot_on_failure reboot,netbpfload-failed
- # we're not really updatable, but want to be able to load bpf programs shipped in apexes
- updatable
diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index eb03157..16f0c44 100755
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -418,7 +418,6 @@
import com.android.server.connectivity.UidRangeUtils;
import com.android.server.connectivity.Vpn;
import com.android.server.connectivity.VpnProfileStore;
-import com.android.server.net.LockdownVpnTracker;
import com.android.server.net.NetworkPinner;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRunner;
@@ -1500,14 +1499,13 @@
private int mVpnType = VpnManager.TYPE_VPN_SERVICE;
private UnderlyingNetworkInfo mUnderlyingNetworkInfo;
- // These ConditionVariables allow tests to wait for LegacyVpnRunner to be stopped/started.
+ // This ConditionVariable allow tests to wait for LegacyVpnRunner to be started.
// TODO: this scheme is ad-hoc and error-prone because it does not fail if, for example, the
// test expects two starts in a row, or even if the production code calls start twice in a
// row. find a better solution. Simply putting a method to create a LegacyVpnRunner into
// Vpn.Dependencies doesn't work because LegacyVpnRunner is not a static class and has
// extensive access into the internals of Vpn.
private ConditionVariable mStartLegacyVpnCv = new ConditionVariable();
- private ConditionVariable mStopVpnRunnerCv = new ConditionVariable();
public MockVpn(int userId) {
super(startHandlerThreadAndReturnLooper(), mServiceContext,
@@ -1676,12 +1674,6 @@
public void expectStartLegacyVpnRunner() {
assertTrue("startLegacyVpnRunner not called after " + TIMEOUT_MS + " ms",
mStartLegacyVpnCv.block(TIMEOUT_MS));
-
- // startLegacyVpn calls stopVpnRunnerPrivileged, which will open mStopVpnRunnerCv, just
- // before calling startLegacyVpnRunner. Restore mStopVpnRunnerCv, so the test can expect
- // that the VpnRunner is stopped and immediately restarted by calling
- // expectStartLegacyVpnRunner() and expectStopVpnRunnerPrivileged() back-to-back.
- mStopVpnRunnerCv = new ConditionVariable();
}
@Override
@@ -1692,12 +1684,6 @@
mStartLegacyVpnCv = new ConditionVariable();
}
mVpnRunner = null;
- mStopVpnRunnerCv.open();
- }
-
- public void expectStopVpnRunnerPrivileged() {
- assertTrue("stopVpnRunnerPrivileged not called after " + TIMEOUT_MS + " ms",
- mStopVpnRunnerCv.block(TIMEOUT_MS));
}
@Override
@@ -10238,74 +10224,28 @@
// Pretend lockdown VPN was configured.
final VpnProfile profile = setupLegacyLockdownVpn();
- // LockdownVpnTracker disables the Vpn teardown code and enables lockdown.
- // Check the VPN's state before it does so.
- assertTrue(mMockVpn.getEnableTeardown());
- assertFalse(mMockVpn.getLockdown());
-
- // VMSHandlerThread was used inside VpnManagerService and taken into LockDownVpnTracker.
- // VpnManagerService was decoupled from this test but this handlerThread is still required
- // in LockDownVpnTracker. Keep it until LockDownVpnTracker related verification is moved to
- // its own test.
- final HandlerThread VMSHandlerThread = new HandlerThread("TestVpnManagerService");
- VMSHandlerThread.start();
-
- // LockdownVpnTracker is created from VpnManagerService but VpnManagerService is decoupled
- // from ConnectivityServiceTest. Create it directly to simulate LockdownVpnTracker is
- // created.
- // TODO: move LockdownVpnTracker related tests to its own test.
- // Lockdown VPN disables teardown and enables lockdown.
- final LockdownVpnTracker lockdownVpnTracker = new LockdownVpnTracker(mServiceContext,
- VMSHandlerThread.getThreadHandler(), mMockVpn, profile);
- lockdownVpnTracker.init();
- assertFalse(mMockVpn.getEnableTeardown());
- assertTrue(mMockVpn.getLockdown());
+ // Init lockdown state to simulate LockdownVpnTracker behavior.
+ mCm.setLegacyLockdownVpnEnabled(true);
+ mMockVpn.setEnableTeardown(false);
+ mMockVpn.setLockdown(true);
// Bring up a network.
- // Expect nothing to happen because the network does not have an IPv4 default route: legacy
- // VPN only supports IPv4.
final LinkProperties cellLp = new LinkProperties();
cellLp.setInterfaceName("rmnet0");
- cellLp.addLinkAddress(new LinkAddress("2001:db8::1/64"));
- cellLp.addRoute(new RouteInfo(new IpPrefix("::/0"), null, "rmnet0"));
- mCellAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp);
- mCellAgent.connect(false /* validated */);
- callback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellAgent);
- defaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellAgent);
- systemDefaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellAgent);
- waitForIdle();
- assertNull(mMockVpn.getAgent());
-
- // Add an IPv4 address. Ideally the VPN should start, but it doesn't because nothing calls
- // LockdownVpnTracker#handleStateChangedLocked. This is a bug.
- // TODO: consider fixing this.
cellLp.addLinkAddress(new LinkAddress("192.0.2.2/25"));
cellLp.addRoute(new RouteInfo(new IpPrefix("0.0.0.0/0"), null, "rmnet0"));
- mCellAgent.sendLinkProperties(cellLp);
- callback.expect(LINK_PROPERTIES_CHANGED, mCellAgent);
- defaultCallback.expect(LINK_PROPERTIES_CHANGED, mCellAgent);
- systemDefaultCallback.expect(LINK_PROPERTIES_CHANGED, mCellAgent);
- waitForIdle();
- assertNull(mMockVpn.getAgent());
-
- // Disconnect, then try again with a network that supports IPv4 at connection time.
- // Expect lockdown VPN to come up.
- ExpectedBroadcast b1 = expectConnectivityAction(TYPE_MOBILE, DetailedState.DISCONNECTED);
- mCellAgent.disconnect();
- callback.expect(LOST, mCellAgent);
- defaultCallback.expect(LOST, mCellAgent);
- systemDefaultCallback.expect(LOST, mCellAgent);
- b1.expectBroadcast();
-
// When lockdown VPN is active, the NetworkInfo state in CONNECTIVITY_ACTION is overwritten
// with the state of the VPN network. So expect a CONNECTING broadcast.
- b1 = expectConnectivityAction(TYPE_MOBILE, DetailedState.CONNECTING);
+ ExpectedBroadcast b1 = expectConnectivityAction(TYPE_MOBILE, DetailedState.CONNECTING);
mCellAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp);
mCellAgent.connect(false /* validated */);
callback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellAgent);
defaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellAgent);
systemDefaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellAgent);
b1.expectBroadcast();
+ // Simulate LockdownVpnTracker attempting to start the VPN since it received the
+ // systemDefault callback.
+ mMockVpn.startLegacyVpnPrivileged(profile, mCellAgent.getNetwork(), cellLp);
assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
assertNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED);
@@ -10359,23 +10299,25 @@
b1 = expectConnectivityAction(TYPE_MOBILE, DetailedState.DISCONNECTED);
// Wifi is CONNECTING because the VPN isn't up yet.
b2 = expectConnectivityAction(TYPE_WIFI, DetailedState.CONNECTING);
- ExpectedBroadcast b3 = expectConnectivityAction(TYPE_VPN, DetailedState.DISCONNECTED);
mWiFiAgent.connect(false /* validated */);
+ // Wifi is not blocked since VPN network is still connected.
+ callback.expectAvailableCallbacksUnvalidated(mWiFiAgent);
+ defaultCallback.assertNoCallback();
+ systemDefaultCallback.expectAvailableCallbacksUnvalidated(mWiFiAgent);
b1.expectBroadcast();
b2.expectBroadcast();
- b3.expectBroadcast();
- mMockVpn.expectStopVpnRunnerPrivileged();
- mMockVpn.expectStartLegacyVpnRunner();
- // TODO: why is wifi not blocked? Is it because when this callback is sent, the VPN is still
- // connected, so the network is not considered blocked by the lockdown UID ranges? But the
- // fact that a VPN is connected should only result in the VPN itself being unblocked, not
- // any other network. Bug in isUidBlockedByVpn?
- callback.expectAvailableCallbacksUnvalidated(mWiFiAgent);
+ // Simulate LockdownVpnTracker restarting the VPN since it received the systemDefault
+ // callback with different network.
+ final ExpectedBroadcast b3 = expectConnectivityAction(TYPE_VPN, DetailedState.DISCONNECTED);
+ mMockVpn.stopVpnRunnerPrivileged();
+ mMockVpn.startLegacyVpnPrivileged(profile, mWiFiAgent.getNetwork(), wifiLp);
+ mMockVpn.expectStartLegacyVpnRunner();
callback.expect(LOST, mMockVpn);
defaultCallback.expect(LOST, mMockVpn);
defaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mWiFiAgent);
- systemDefaultCallback.expectAvailableCallbacksUnvalidated(mWiFiAgent);
+ systemDefaultCallback.assertNoCallback();
+ b3.expectBroadcast();
// While the VPN is reconnecting on the new network, everything is blocked.
assertActiveNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED);
@@ -10420,15 +10362,22 @@
b2 = expectConnectivityAction(TYPE_VPN, DetailedState.DISCONNECTED);
mWiFiAgent.disconnect();
callback.expect(LOST, mWiFiAgent);
+ callback.expectCaps(mMockVpn, c -> !c.hasTransport(TRANSPORT_WIFI));
+ defaultCallback.expectCaps(mMockVpn, c -> !c.hasTransport(TRANSPORT_WIFI));
+ systemDefaultCallback.expect(LOST, mWiFiAgent);
+ // TODO: There should only be one LOST callback. Since the WIFI network is underlying a VPN
+ // network, ConnectivityService#propagateUnderlyingNetworkCapabilities() causes a rematch to
+ // occur. Notably, this happens before setting the satisfiers of its network requests to
+ // null. Since the satisfiers are set to null in the rematch, an extra LOST callback is
+ // called.
systemDefaultCallback.expect(LOST, mWiFiAgent);
b1.expectBroadcast();
- callback.expectCaps(mMockVpn, c -> !c.hasTransport(TRANSPORT_WIFI));
- mMockVpn.expectStopVpnRunnerPrivileged();
+ mMockVpn.stopVpnRunnerPrivileged();
callback.expect(LOST, mMockVpn);
+ defaultCallback.expect(LOST, mMockVpn);
b2.expectBroadcast();
- VMSHandlerThread.quitSafely();
- VMSHandlerThread.join();
+ assertNoCallbacks(callback, defaultCallback, systemDefaultCallback);
}
@Test @IgnoreUpTo(Build.VERSION_CODES.S_V2)
diff --git a/tests/unit/java/com/android/server/connectivity/VpnTest.java b/tests/unit/java/com/android/server/connectivity/VpnTest.java
index 56346ad..d674767 100644
--- a/tests/unit/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/unit/java/com/android/server/connectivity/VpnTest.java
@@ -1839,6 +1839,22 @@
// a subsequent CL.
}
+ @Test
+ public void testStartLegacyVpnIpv6() throws Exception {
+ setMockedUsers(PRIMARY_USER);
+ final Vpn vpn = createVpn(PRIMARY_USER.id);
+ final LinkProperties lp = new LinkProperties();
+ lp.setInterfaceName(EGRESS_IFACE);
+ lp.addLinkAddress(new LinkAddress("2001:db8::1/64"));
+ final RouteInfo defaultRoute = new RouteInfo(
+ new IpPrefix(Inet6Address.ANY, 0), null, EGRESS_IFACE);
+ lp.addRoute(defaultRoute);
+
+ // IllegalStateException thrown since legacy VPN only supports IPv4.
+ assertThrows(IllegalStateException.class,
+ () -> vpn.startLegacyVpn(mVpnProfile, EGRESS_NETWORK, lp));
+ }
+
private Vpn startLegacyVpn(final Vpn vpn, final VpnProfile vpnProfile) throws Exception {
setMockedUsers(PRIMARY_USER);