diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2021-07-15 01:32:26 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2021-07-15 01:32:26 +0000 |
commit | 6deef8577df2a8ebe6629ffaea613675f23dc96e (patch) | |
tree | 389d5f648f0e28282ce9b077d1c0ff513f88b78c | |
parent | 003524e75b847376b361f310443405bdb6d37c6d (diff) | |
parent | 163acedd1d32a31aed0b323f93dc45db9fb5d0bf (diff) | |
download | netd-android12-mainline-conscrypt-release.tar.gz |
Snap for 7550844 from 163acedd1d32a31aed0b323f93dc45db9fb5d0bf to mainline-conscrypt-releaseandroid-mainline-12.0.0_r8android-mainline-12.0.0_r25android12-mainline-conscrypt-release
Change-Id: Ia6b8f05aecc20a5310b105262b265709d183ae89
136 files changed, 5754 insertions, 3216 deletions
@@ -1,6 +1,27 @@ +package { + default_applicable_licenses: ["system_netd_license"], +} + +// Added automatically by a large-scale-change +// See: http://go/android-license-faq +license { + name: "system_netd_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "NOTICE", + ], +} + cc_library_headers { name: "libnetd_client_headers", export_include_dirs: ["include"], + apex_available: [ + "//apex_available:platform", + "com.android.tethering", + ], } cc_library_headers { @@ -38,12 +59,22 @@ cc_defaults { "google-*", "misc-*", "performance-*", + "-bugprone-macro-parentheses", "-bugprone-narrowing-conversions", // lots of unsigned -> int conversions + "-bugprone-unhandled-self-assignment", // found in DnsResolver/stats.pb.h + "-cert-dcl50-cpp", "-cert-err34-c", // TODO: re-enable after removing atoi() and sscanf() calls + "-cert-oop54-cpp", // found in DnsResolver/stats.pb.h + "-google-default-arguments", + "-google-explicit-constructor", + "-google-global-names-in-headers", "-google-readability-*", // Too pedantic "-google-runtime-int", // Too many unavoidable warnings due to strtol() "-google-runtime-references", // Grandfathered usage of pass by non-const reference "-misc-non-private-member-variables-in-classes", // Also complains about structs + "-performance-noexcept-move-constructor", + "-performance-unnecessary-value-param", + "-performance-no-int-to-ptr", ], tidy_flags: [ "-warnings-as-errors=" diff --git a/MODULE_LICENSE_APACHE2 b/MODULE_LICENSE_APACHE2 deleted file mode 100644 index e69de29b..00000000 --- a/MODULE_LICENSE_APACHE2 +++ /dev/null @@ -8,3 +8,4 @@ nuccachen@google.com reminv@google.com satk@google.com xiaom@google.com +yumike@google.com
\ No newline at end of file diff --git a/bpf_progs/Android.bp b/bpf_progs/Android.bp index 8ce924b9..1311ca90 100644 --- a/bpf_progs/Android.bp +++ b/bpf_progs/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "system_netd_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["system_netd_license"], +} + cc_library_headers { name: "netd_bpf_progs_headers", export_include_dirs: ["."], @@ -47,16 +56,3 @@ bpf { "system/netd/libnetdutils/include", ], } - -bpf { - name: "offload.o", - srcs: ["offload.c"], - cflags: [ - "-Wall", - "-Werror", - ], - include_dirs: [ - "system/netd/libnetdbpf/include", - "system/netd/libnetdutils/include", - ], -} diff --git a/bpf_progs/bpf_net_helpers.h b/bpf_progs/bpf_net_helpers.h index d4dc5f85..d978f3a2 100644 --- a/bpf_progs/bpf_net_helpers.h +++ b/bpf_progs/bpf_net_helpers.h @@ -26,9 +26,15 @@ static uint64_t (*bpf_get_socket_cookie)(struct __sk_buff* skb) = (void*)BPF_FUNC_get_socket_cookie; static uint32_t (*bpf_get_socket_uid)(struct __sk_buff* skb) = (void*)BPF_FUNC_get_socket_uid; + +static int (*bpf_skb_pull_data)(struct __sk_buff* skb, __u32 len) = (void*)BPF_FUNC_skb_pull_data; + static int (*bpf_skb_load_bytes)(struct __sk_buff* skb, int off, void* to, int len) = (void*)BPF_FUNC_skb_load_bytes; +static int (*bpf_skb_store_bytes)(struct __sk_buff* skb, __u32 offset, const void* from, __u32 len, + __u64 flags) = (void*)BPF_FUNC_skb_store_bytes; + static int64_t (*bpf_csum_diff)(__be32* from, __u32 from_size, __be32* to, __u32 to_size, __wsum seed) = (void*)BPF_FUNC_csum_diff; @@ -41,6 +47,8 @@ static int (*bpf_l3_csum_replace)(struct __sk_buff* skb, __u32 offset, __u64 fro static int (*bpf_l4_csum_replace)(struct __sk_buff* skb, __u32 offset, __u64 from, __u64 to, __u64 flags) = (void*)BPF_FUNC_l4_csum_replace; static int (*bpf_redirect)(__u32 ifindex, __u64 flags) = (void*)BPF_FUNC_redirect; +static int (*bpf_redirect_map)(const struct bpf_map_def* map, __u32 key, + __u64 flags) = (void*)BPF_FUNC_redirect_map; static int (*bpf_skb_change_head)(struct __sk_buff* skb, __u32 head_room, __u64 flags) = (void*)BPF_FUNC_skb_change_head; @@ -58,4 +66,10 @@ static inline __always_inline __unused bool is_received_skb(struct __sk_buff* sk skb->pkt_type == PACKET_MULTICAST; } +// try to make the first 'len' header bytes readable via direct packet access +static inline __always_inline void try_make_readable(struct __sk_buff* skb, int len) { + if (len > skb->len) len = skb->len; + if (skb->data_end - skb->data < len) bpf_skb_pull_data(skb, len); +} + #endif // NETDBPF_BPF_NET_HELPERS_H diff --git a/bpf_progs/clatd.c b/bpf_progs/clatd.c index e7586928..31e05222 100644 --- a/bpf_progs/clatd.c +++ b/bpf_progs/clatd.c @@ -37,7 +37,7 @@ // From kernel:include/net/ip.h #define IP_DF 0x4000 // Flag: "Don't Fragment" -DEFINE_BPF_MAP(clat_ingress_map, HASH, ClatIngressKey, ClatIngressValue, 16) +DEFINE_BPF_MAP(clat_ingress6_map, HASH, ClatIngress6Key, ClatIngress6Value, 16) static inline __always_inline int nat64(struct __sk_buff* skb, bool is_ethernet) { const int l2_header_size = is_ethernet ? sizeof(struct ethhdr) : 0; @@ -46,6 +46,9 @@ static inline __always_inline int nat64(struct __sk_buff* skb, bool is_ethernet) const struct ethhdr* const eth = is_ethernet ? data : NULL; // used iff is_ethernet const struct ipv6hdr* const ip6 = is_ethernet ? (void*)(eth + 1) : data; + // Require ethernet dst mac address to be our unicast address. + if (is_ethernet && (skb->pkt_type != PACKET_HOST)) return TC_ACT_OK; + // Must be meta-ethernet IPv6 frame if (skb->protocol != htons(ETH_P_IPV6)) return TC_ACT_OK; @@ -72,7 +75,7 @@ static inline __always_inline int nat64(struct __sk_buff* skb, bool is_ethernet) return TC_ACT_OK; } - ClatIngressKey k = { + ClatIngress6Key k = { .iif = skb->ifindex, .pfx96.in6_u.u6_addr32 = { @@ -83,7 +86,7 @@ static inline __always_inline int nat64(struct __sk_buff* skb, bool is_ethernet) .local6 = ip6->daddr, }; - ClatIngressValue* v = bpf_clat_ingress_map_lookup_elem(&k); + ClatIngress6Value* v = bpf_clat_ingress6_map_lookup_elem(&k); if (!v) return TC_ACT_OK; @@ -176,25 +179,25 @@ static inline __always_inline int nat64(struct __sk_buff* skb, bool is_ethernet) return TC_ACT_OK; } -SEC("schedcls/ingress/clat_ether") -int sched_cls_ingress_clat_ether(struct __sk_buff* skb) { +DEFINE_BPF_PROG("schedcls/ingress6/clat_ether", AID_ROOT, AID_ROOT, sched_cls_ingress6_clat_ether) +(struct __sk_buff* skb) { return nat64(skb, true); } -SEC("schedcls/ingress/clat_rawip") -int sched_cls_ingress_clat_rawip(struct __sk_buff* skb) { +DEFINE_BPF_PROG("schedcls/ingress6/clat_rawip", AID_ROOT, AID_ROOT, sched_cls_ingress6_clat_rawip) +(struct __sk_buff* skb) { return nat64(skb, false); } -DEFINE_BPF_MAP(clat_egress_map, HASH, ClatEgressKey, ClatEgressValue, 16) +DEFINE_BPF_MAP(clat_egress4_map, HASH, ClatEgress4Key, ClatEgress4Value, 16) -SEC("schedcls/egress/clat_ether") -int sched_cls_egress_clat_ether(struct __sk_buff* skb) { +DEFINE_BPF_PROG("schedcls/egress4/clat_ether", AID_ROOT, AID_ROOT, sched_cls_egress4_clat_ether) +(struct __sk_buff* skb) { return TC_ACT_OK; } -SEC("schedcls/egress/clat_rawip") -int sched_cls_egress_clat_rawip(struct __sk_buff* skb) { +DEFINE_BPF_PROG("schedcls/egress4/clat_rawip", AID_ROOT, AID_ROOT, sched_cls_egress4_clat_rawip) +(struct __sk_buff* skb) { void* data = (void*)(long)skb->data; const void* data_end = (void*)(long)skb->data_end; const struct iphdr* const ip4 = data; @@ -248,12 +251,12 @@ int sched_cls_egress_clat_rawip(struct __sk_buff* skb) { return TC_ACT_OK; } - ClatEgressKey k = { + ClatEgress4Key k = { .iif = skb->ifindex, .local4.s_addr = ip4->saddr, }; - ClatEgressValue* v = bpf_clat_egress_map_lookup_elem(&k); + ClatEgress4Value* v = bpf_clat_egress4_map_lookup_elem(&k); if (!v) return TC_ACT_OK; diff --git a/bpf_progs/netd.c b/bpf_progs/netd.c index f3470280..e9e1477f 100644 --- a/bpf_progs/netd.c +++ b/bpf_progs/netd.c @@ -203,9 +203,12 @@ static inline int bpf_owner_match(struct __sk_buff* skb, uint32_t uid, int direc if ((enabledRules & POWERSAVE_MATCH) && !(uidRules & POWERSAVE_MATCH)) { return BPF_DROP; } + if ((enabledRules & RESTRICTED_MATCH) && !(uidRules & RESTRICTED_MATCH)) { + return BPF_DROP; + } } if (direction == BPF_INGRESS && (uidRules & IIF_MATCH)) { - // Drops packets not coming from lo nor the whitelisted interface + // Drops packets not coming from lo nor the allowlisted interface if (allowed_iif && skb->ifindex != 1 && skb->ifindex != allowed_iif) { return BPF_DROP_UNLESS_DNS; } @@ -280,13 +283,13 @@ static __always_inline inline int bpf_traffic_account(struct __sk_buff* skb, int return match; } -SEC("cgroupskb/ingress/stats") -int bpf_cgroup_ingress(struct __sk_buff* skb) { +DEFINE_BPF_PROG("cgroupskb/ingress/stats", AID_ROOT, AID_ROOT, bpf_cgroup_ingress) +(struct __sk_buff* skb) { return bpf_traffic_account(skb, BPF_INGRESS); } -SEC("cgroupskb/egress/stats") -int bpf_cgroup_egress(struct __sk_buff* skb) { +DEFINE_BPF_PROG("cgroupskb/egress/stats", AID_ROOT, AID_ROOT, bpf_cgroup_egress) +(struct __sk_buff* skb) { return bpf_traffic_account(skb, BPF_EGRESS); } @@ -315,7 +318,7 @@ DEFINE_BPF_PROG("skfilter/ingress/xtbpf", AID_ROOT, AID_NET_ADMIN, xt_bpf_ingres return BPF_MATCH; } -DEFINE_BPF_PROG("skfilter/whitelist/xtbpf", AID_ROOT, AID_NET_ADMIN, xt_bpf_whitelist_prog) +DEFINE_BPF_PROG("skfilter/allowlist/xtbpf", AID_ROOT, AID_NET_ADMIN, xt_bpf_allowlist_prog) (struct __sk_buff* skb) { uint32_t sock_uid = bpf_get_socket_uid(skb); if (is_system_uid(sock_uid)) return BPF_MATCH; @@ -327,16 +330,16 @@ DEFINE_BPF_PROG("skfilter/whitelist/xtbpf", AID_ROOT, AID_NET_ADMIN, xt_bpf_whit if ((sock_uid == 65534) && !bpf_get_socket_cookie(skb) && is_received_skb(skb)) return BPF_MATCH; - UidOwnerValue* whitelistMatch = bpf_uid_owner_map_lookup_elem(&sock_uid); - if (whitelistMatch) return whitelistMatch->rule & HAPPY_BOX_MATCH ? BPF_MATCH : BPF_NOMATCH; + UidOwnerValue* allowlistMatch = bpf_uid_owner_map_lookup_elem(&sock_uid); + if (allowlistMatch) return allowlistMatch->rule & HAPPY_BOX_MATCH ? BPF_MATCH : BPF_NOMATCH; return BPF_NOMATCH; } -DEFINE_BPF_PROG("skfilter/blacklist/xtbpf", AID_ROOT, AID_NET_ADMIN, xt_bpf_blacklist_prog) +DEFINE_BPF_PROG("skfilter/denylist/xtbpf", AID_ROOT, AID_NET_ADMIN, xt_bpf_denylist_prog) (struct __sk_buff* skb) { uint32_t sock_uid = bpf_get_socket_uid(skb); - UidOwnerValue* blacklistMatch = bpf_uid_owner_map_lookup_elem(&sock_uid); - if (blacklistMatch) return blacklistMatch->rule & PENALTY_BOX_MATCH ? BPF_MATCH : BPF_NOMATCH; + UidOwnerValue* denylistMatch = bpf_uid_owner_map_lookup_elem(&sock_uid); + if (denylistMatch) return denylistMatch->rule & PENALTY_BOX_MATCH ? BPF_MATCH : BPF_NOMATCH; return BPF_NOMATCH; } diff --git a/bpf_progs/offload.c b/bpf_progs/offload.c deleted file mode 100644 index 16dbe1dd..00000000 --- a/bpf_progs/offload.c +++ /dev/null @@ -1,204 +0,0 @@ -/* - * Copyright (C) 2020 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. - */ - -#include <linux/if.h> -#include <linux/ip.h> -#include <linux/ipv6.h> -#include <linux/pkt_cls.h> -#include <linux/tcp.h> - -#include "bpf_helpers.h" -#include "bpf_net_helpers.h" -#include "netdbpf/bpf_shared.h" - -DEFINE_BPF_MAP_GRW(tether_ingress_map, HASH, TetherIngressKey, TetherIngressValue, 64, - AID_NETWORK_STACK) - -// Tethering stats, indexed by upstream interface. -DEFINE_BPF_MAP_GRW(tether_stats_map, HASH, uint32_t, TetherStatsValue, 16, AID_NETWORK_STACK) - -// Tethering data limit, indexed by upstream interface. -// (tethering allowed when stats[iif].rxBytes + stats[iif].txBytes < limit[iif]) -DEFINE_BPF_MAP_GRW(tether_limit_map, HASH, uint32_t, uint64_t, 16, AID_NETWORK_STACK) - -static inline __always_inline int do_forward(struct __sk_buff* skb, bool is_ethernet) { - int l2_header_size = is_ethernet ? sizeof(struct ethhdr) : 0; - void* data = (void*)(long)skb->data; - const void* data_end = (void*)(long)skb->data_end; - struct ethhdr* eth = is_ethernet ? data : NULL; // used iff is_ethernet - struct ipv6hdr* ip6 = is_ethernet ? (void*)(eth + 1) : data; - - // Must be meta-ethernet IPv6 frame - if (skb->protocol != htons(ETH_P_IPV6)) return TC_ACT_OK; - - // Must have (ethernet and) ipv6 header - if (data + l2_header_size + sizeof(*ip6) > data_end) return TC_ACT_OK; - - // Ethertype - if present - must be IPv6 - if (is_ethernet && (eth->h_proto != htons(ETH_P_IPV6))) return TC_ACT_OK; - - // IP version must be 6 - if (ip6->version != 6) return TC_ACT_OK; - - // Cannot decrement during forward if already zero or would be zero, - // Let the kernel's stack handle these cases and generate appropriate ICMP errors. - if (ip6->hop_limit <= 1) return TC_ACT_OK; - - // Protect against forwarding packets sourced from ::1 or fe80::/64 or other weirdness. - __be32 src32 = ip6->saddr.s6_addr32[0]; - if (src32 != htonl(0x0064ff9b) && // 64:ff9b:/32 incl. XLAT464 WKP - (src32 & htonl(0xe0000000)) != htonl(0x20000000)) // 2000::/3 Global Unicast - return TC_ACT_OK; - - TetherIngressKey k = { - .iif = skb->ifindex, - .neigh6 = ip6->daddr, - }; - - TetherIngressValue* v = bpf_tether_ingress_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_OK; - - uint32_t stat_and_limit_k = skb->ifindex; - - TetherStatsValue* stat_v = bpf_tether_stats_map_lookup_elem(&stat_and_limit_k); - - // If we don't have anywhere to put stats, then abort... - if (!stat_v) return TC_ACT_OK; - - uint64_t* limit_v = bpf_tether_limit_map_lookup_elem(&stat_and_limit_k); - - // If we don't have a limit, then abort... - if (!limit_v) return TC_ACT_OK; - - // Required IPv6 minimum mtu is 1280, below that not clear what we should do, abort... - const int pmtu = v->pmtu; - if (pmtu < IPV6_MIN_MTU) return TC_ACT_OK; - - // Approximate handling of TCP/IPv6 overhead for incoming LRO/GRO packets: default - // outbound path mtu of 1500 is not necessarily correct, but worst case we simply - // undercount, which is still better then not accounting for this overhead at all. - // Note: this really shouldn't be device/path mtu at all, but rather should be - // derived from this particular connection's mss (ie. from gro segment size). - // This would require a much newer kernel with newer ebpf accessors. - // (This is also blindly assuming 12 bytes of tcp timestamp option in tcp header) - uint64_t packets = 1; - uint64_t bytes = skb->len; - if (bytes > pmtu) { - const int tcp_overhead = sizeof(struct ipv6hdr) + sizeof(struct tcphdr) + 12; - const int mss = pmtu - tcp_overhead; - const uint64_t payload = bytes - tcp_overhead; - packets = (payload + mss - 1) / mss; - bytes = tcp_overhead * packets + payload; - } - - // Are we past the limit? If so, then abort... - // Note: will not overflow since u64 is 936 years even at 5Gbps. - // Do not drop here. Offload is just that, whenever we fail to handle - // a packet we let the core stack deal with things. - // (The core stack needs to handle limits correctly anyway, - // since we don't offload all traffic in both directions) - if (stat_v->rxBytes + stat_v->txBytes + bytes > *limit_v) return TC_ACT_OK; - - if (!is_ethernet) { - is_ethernet = true; - l2_header_size = sizeof(struct ethhdr); - // Try to inject an ethernet header, and simply return if we fail - if (bpf_skb_change_head(skb, l2_header_size, /*flags*/ 0)) { - __sync_fetch_and_add(&stat_v->rxErrors, 1); - return TC_ACT_OK; - } - - // bpf_skb_change_head() invalidates all pointers - reload them - data = (void*)(long)skb->data; - data_end = (void*)(long)skb->data_end; - eth = data; - ip6 = (void*)(eth + 1); - - // I do not believe this can ever happen, but keep the verifier happy... - if (data + l2_header_size + sizeof(*ip6) > data_end) return TC_ACT_SHOT; - }; - - // CHECKSUM_COMPLETE is a 16-bit one's complement sum, - // thus corrections for it need to be done in 16-byte chunks at even offsets. - // IPv6 nexthdr is at offset 6, while hop limit is at offset 7 - uint8_t old_hl = ip6->hop_limit; - --ip6->hop_limit; - uint8_t new_hl = ip6->hop_limit; - - // bpf_csum_update() always succeeds if the skb is CHECKSUM_COMPLETE and returns an error - // (-ENOTSUPP) if it isn't. - bpf_csum_update(skb, 0xFFFF - ntohs(old_hl) + ntohs(new_hl)); - - __sync_fetch_and_add(&stat_v->rxPackets, packets); - __sync_fetch_and_add(&stat_v->rxBytes, bytes); - - // Overwrite any mac header with the new one - *eth = v->macHeader; - - // Redirect to forwarded interface. - // - // Note that bpf_redirect() cannot fail unless you pass invalid flags. - // The redirect actually happens after the ebpf program has already terminated, - // and can fail for example for mtu reasons at that point in time, but there's nothing - // we can do about it here. - return bpf_redirect(v->oif, 0 /* this is effectively BPF_F_EGRESS */); -} - -SEC("schedcls/ingress/tether_ether") -int sched_cls_ingress_tether_ether(struct __sk_buff* skb) { - return do_forward(skb, true); -} - -// Note: section names must be unique to prevent programs from appending to each other, -// so instead the bpf loader will strip everything past the final $ symbol when actually -// pinning the program into the filesystem. -// -// bpf_skb_change_head() is only present on 4.14+ and 2 trivial kernel patches are needed: -// ANDROID: net: bpf: Allow TC programs to call BPF_FUNC_skb_change_head -// ANDROID: net: bpf: permit redirect from ingress L3 to egress L2 devices at near max mtu -// (the first of those has already been upstreamed) -// -// 5.4 kernel support was only added to Android Common Kernel in R, -// and thus a 5.4 kernel always supports this. -// -// Hence, this mandatory (must load successfully) implementation for 5.4+ kernels: -DEFINE_BPF_PROG_KVER("schedcls/ingress/tether_rawip$5_4", AID_ROOT, AID_ROOT, - sched_cls_ingress_tether_rawip_5_4, KVER(5, 4, 0)) -(struct __sk_buff* skb) { - return do_forward(skb, false); -} - -// and this identical optional (may fail to load) implementation for [4.14..5.4) patched kernels: -DEFINE_OPTIONAL_BPF_PROG_KVER_RANGE("schedcls/ingress/tether_rawip$4_14", AID_ROOT, AID_ROOT, - sched_cls_ingress_tether_rawip_4_14, KVER(4, 14, 0), - KVER(5, 4, 0)) -(struct __sk_buff* skb) { - return do_forward(skb, false); -} - -// and define a no-op stub for [4.9,4.14) and unpatched [4.14,5.4) kernels. -// (if the above real 4.14+ program loaded successfully, then bpfloader will have already pinned -// it at the same location this one would be pinned at and will thus skip loading this stub) -DEFINE_BPF_PROG_KVER_RANGE("schedcls/ingress/tether_rawip$stub", AID_ROOT, AID_ROOT, - sched_cls_ingress_tether_rawip_stub, KVER_NONE, KVER(5, 4, 0)) -(struct __sk_buff* skb) { - return TC_ACT_OK; -} - -LICENSE("Apache 2.0"); -CRITICAL("netd"); diff --git a/client/Android.bp b/client/Android.bp index 53afc977..b8eb56c4 100644 --- a/client/Android.bp +++ b/client/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "system_netd_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["system_netd_license"], +} + cc_library { name: "libnetd_client", srcs: [ @@ -22,7 +31,6 @@ cc_library { "dnsproxyd_protocol_headers", // NETID_USE_LOCAL_NAMESERVERS "libnetd_client_headers", "libbase_headers", // for unique_fd.h - "libnetd_resolv_headers", ], export_header_lib_headers: ["libnetd_client_headers"], include_dirs: [ @@ -32,6 +40,10 @@ cc_library { sanitize: { cfi: true, }, + apex_available: [ + "//apex_available:platform", + "com.android.tethering", + ], } cc_test { @@ -44,16 +56,13 @@ cc_test { include_dirs: [ "system/netd/include", ], - header_libs: [ - "libnetd_resolv_headers", - ], static_libs: [ "libgmock", "libbase", "libnetd_client", ], sanitize: { - address: true, + address: false, recover: [ "all" ], }, } diff --git a/include/binder_utils/BinderUtil.h b/include/binder_utils/BinderUtil.h index 6b41465a..469fa681 100644 --- a/include/binder_utils/BinderUtil.h +++ b/include/binder_utils/BinderUtil.h @@ -16,9 +16,8 @@ #pragma once -#include <android-base/stringprintf.h> -#include <json/value.h> -#include <json/writer.h> +#include <android-base/strings.h> +#include <fmt/format.h> #ifdef ANDROID_BINDER_STATUS_H #define IS_BINDER_OK(__ex__) (__ex__ == ::android::binder::Status::EX_NONE) @@ -59,31 +58,26 @@ std::string exceptionToString(int32_t exception) { using LogFn = std::function<void(const std::string& msg)>; -void binderCallLogFn(const Json::Value& logTransaction, const LogFn& logFn) { +template <typename LogType> +void binderCallLogFn(const LogType& log, const LogFn& logFn) { using namespace std::string_literals; bool hasReturnArgs; std::string output; - const Json::Value& returnArgs = logTransaction["_aidl_return"]; - const Json::Value& inputArgsArray = logTransaction["input_args"]; - hasReturnArgs = !returnArgs.empty(); - output.append(logTransaction["method_name"].asString() + "("s); + hasReturnArgs = !log.result.empty(); + output.append(log.method_name + "("s); // input args - Json::FastWriter fastWriter; - fastWriter.omitEndingLineFeed(); - for (Json::Value::ArrayIndex i = 0; i < inputArgsArray.size(); ++i) { - std::string value = fastWriter.write(inputArgsArray[i]["value"]); - output.append(value); - if (i != inputArgsArray.size() - 1) { + for (size_t i = 0; i < log.input_args.size(); ++i) { + output.append(log.input_args[i].second); + if (i != log.input_args.size() - 1) { output.append(", "s); } } output.append(")"s); - const int exceptionCode = - TO_EXCEPTION(logTransaction["binder_status"]["exception_code"].asInt()); + const int exceptionCode = TO_EXCEPTION(log.exception_code); if (hasReturnArgs || !IS_BINDER_OK(exceptionCode)) { output.append(" -> "s); @@ -92,18 +86,17 @@ void binderCallLogFn(const Json::Value& logTransaction, const LogFn& logFn) { // return status if (!IS_BINDER_OK(exceptionCode)) { // an exception occurred - const int errCode = logTransaction["binder_status"]["service_specific_error_code"].asInt(); - output.append(::android::base::StringPrintf( - "%s(%d, \"%s\")", exceptionToString(exceptionCode).c_str(), - (errCode != 0) ? errCode : exceptionCode, - logTransaction["binder_status"]["exception_message"].asString().c_str())); + const int errCode = log.service_specific_error_code; + output.append(fmt::format("{}({}, \"{}\")", exceptionToString(exceptionCode), + (errCode != 0) ? errCode : exceptionCode, log.exception_message)); } // return args if (hasReturnArgs) { - output.append(::android::base::StringPrintf("{%s}", fastWriter.write(returnArgs).c_str())); + output.append("{" + log.result + "}"); } // duration time - output.append( - ::android::base::StringPrintf(" <%.2fms>", logTransaction["duration_ms"].asFloat())); - logFn(output); + output.append(fmt::format(" <{:.2f}ms>", log.duration_ms)); + + // escape newline characters to avoid multiline log entries + logFn(::android::base::StringReplace(output, "\n", "\\n", true)); } diff --git a/libnetdbpf/Android.bp b/libnetdbpf/Android.bp index 72e0fec1..7c0207ce 100644 --- a/libnetdbpf/Android.bp +++ b/libnetdbpf/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "system_netd_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["system_netd_license"], +} + cc_library { name: "libnetdbpf", vendor_available: false, diff --git a/libnetdbpf/BpfNetworkStatsTest.cpp b/libnetdbpf/BpfNetworkStatsTest.cpp index a9581ce7..fb8f0ec6 100644 --- a/libnetdbpf/BpfNetworkStatsTest.cpp +++ b/libnetdbpf/BpfNetworkStatsTest.cpp @@ -78,7 +78,6 @@ class BpfNetworkStatsHelperTest : public testing::Test { BpfMap<uint32_t, StatsValue> mFakeIfaceStatsMap; void SetUp() { - SKIP_IF_BPF_NOT_SUPPORTED; ASSERT_EQ(0, setrlimitForTest()); mFakeCookieTagMap = BpfMap<uint64_t, UidTagValue>(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE, 0); @@ -140,8 +139,6 @@ class BpfNetworkStatsHelperTest : public testing::Test { // TEST to verify the behavior of bpf map when cocurrent deletion happens when // iterating the same map. TEST_F(BpfNetworkStatsHelperTest, TestIterateMapWithDeletion) { - SKIP_IF_BPF_NOT_SUPPORTED; - for (int i = 0; i < 5; i++) { uint64_t cookie = i + 1; UidTagValue tag = {.uid = TEST_UID1, .tag = TEST_TAG}; @@ -171,8 +168,6 @@ TEST_F(BpfNetworkStatsHelperTest, TestIterateMapWithDeletion) { } TEST_F(BpfNetworkStatsHelperTest, TestBpfIterateMap) { - SKIP_IF_BPF_NOT_SUPPORTED; - for (int i = 0; i < 5; i++) { uint64_t cookie = i + 1; UidTagValue tag = {.uid = TEST_UID1, .tag = TEST_TAG}; @@ -193,8 +188,6 @@ TEST_F(BpfNetworkStatsHelperTest, TestBpfIterateMap) { } TEST_F(BpfNetworkStatsHelperTest, TestUidStatsNoTraffic) { - SKIP_IF_BPF_NOT_SUPPORTED; - StatsValue value1 = { .rxPackets = 0, .rxBytes = 0, @@ -207,8 +200,6 @@ TEST_F(BpfNetworkStatsHelperTest, TestUidStatsNoTraffic) { } TEST_F(BpfNetworkStatsHelperTest, TestGetUidStatsTotal) { - SKIP_IF_BPF_NOT_SUPPORTED; - updateIfaceMap(IFACE_NAME1, IFACE_INDEX1); updateIfaceMap(IFACE_NAME2, IFACE_INDEX2); updateIfaceMap(IFACE_NAME3, IFACE_INDEX3); @@ -249,8 +240,6 @@ TEST_F(BpfNetworkStatsHelperTest, TestGetUidStatsTotal) { } TEST_F(BpfNetworkStatsHelperTest, TestGetIfaceStatsInternal) { - SKIP_IF_BPF_NOT_SUPPORTED; - updateIfaceMap(IFACE_NAME1, IFACE_INDEX1); updateIfaceMap(IFACE_NAME2, IFACE_INDEX2); updateIfaceMap(IFACE_NAME3, IFACE_INDEX3); @@ -294,8 +283,6 @@ TEST_F(BpfNetworkStatsHelperTest, TestGetIfaceStatsInternal) { } TEST_F(BpfNetworkStatsHelperTest, TestGetStatsDetail) { - SKIP_IF_BPF_NOT_SUPPORTED; - updateIfaceMap(IFACE_NAME1, IFACE_INDEX1); updateIfaceMap(IFACE_NAME2, IFACE_INDEX2); StatsValue value1 = { @@ -331,8 +318,6 @@ TEST_F(BpfNetworkStatsHelperTest, TestGetStatsDetail) { } TEST_F(BpfNetworkStatsHelperTest, TestGetStatsWithSkippedIface) { - SKIP_IF_BPF_NOT_SUPPORTED; - updateIfaceMap(IFACE_NAME1, IFACE_INDEX1); updateIfaceMap(IFACE_NAME2, IFACE_INDEX2); StatsValue value1 = { @@ -368,8 +353,6 @@ TEST_F(BpfNetworkStatsHelperTest, TestGetStatsWithSkippedIface) { } TEST_F(BpfNetworkStatsHelperTest, TestUnkownIfaceError) { - SKIP_IF_BPF_NOT_SUPPORTED; - updateIfaceMap(IFACE_NAME1, IFACE_INDEX1); StatsValue value1 = { .rxPackets = TEST_PACKET0, @@ -412,8 +395,6 @@ TEST_F(BpfNetworkStatsHelperTest, TestUnkownIfaceError) { } TEST_F(BpfNetworkStatsHelperTest, TestGetIfaceStatsDetail) { - SKIP_IF_BPF_NOT_SUPPORTED; - updateIfaceMap(IFACE_NAME1, IFACE_INDEX1); updateIfaceMap(IFACE_NAME2, IFACE_INDEX2); updateIfaceMap(IFACE_NAME3, IFACE_INDEX3); @@ -451,8 +432,6 @@ TEST_F(BpfNetworkStatsHelperTest, TestGetIfaceStatsDetail) { } TEST_F(BpfNetworkStatsHelperTest, TestGetStatsSortedAndGrouped) { - SKIP_IF_BPF_NOT_SUPPORTED; - // Create iface indexes with duplicate iface name. updateIfaceMap(IFACE_NAME1, IFACE_INDEX1); updateIfaceMap(IFACE_NAME2, IFACE_INDEX2); @@ -543,8 +522,6 @@ TEST_F(BpfNetworkStatsHelperTest, TestGetStatsSortedAndGrouped) { // Test to verify that subtract overflow will not be triggered by the compare function invoked from // sorting. See http:/b/119193941. TEST_F(BpfNetworkStatsHelperTest, TestGetStatsSortAndOverflow) { - SKIP_IF_BPF_NOT_SUPPORTED; - updateIfaceMap(IFACE_NAME1, IFACE_INDEX1); StatsValue value1 = { diff --git a/libnetdbpf/include/netdbpf/bpf_shared.h b/libnetdbpf/include/netdbpf/bpf_shared.h index 6f318799..2fcb612d 100644 --- a/libnetdbpf/include/netdbpf/bpf_shared.h +++ b/libnetdbpf/include/netdbpf/bpf_shared.h @@ -14,20 +14,26 @@ * limitations under the License. */ -#ifndef NETDBPF_BPF_SHARED_H -#define NETDBPF_BPF_SHARED_H +#pragma once +#include <linux/if.h> #include <linux/if_ether.h> #include <linux/in.h> #include <linux/in6.h> #include <netdutils/UidConstants.h> -// This header file is shared by eBPF kernel programs and netd +// This header file is shared by eBPF kernel programs (C) and netd (C++) and +// some of the maps are also accessed directly from Java mainline module code. +// +// Hence: explicitly pad all relevant structures and assert that their size +// is the sum of the sizes of their fields. +#define STRUCT_SIZE(name, size) _Static_assert(sizeof(name) == (size), "Incorrect struct size.") typedef struct { uint32_t uid; uint32_t tag; } UidTagValue; +STRUCT_SIZE(UidTagValue, 2 * 4); // 8 typedef struct { uint32_t uid; @@ -35,6 +41,7 @@ typedef struct { uint32_t counterSet; uint32_t ifaceIndex; } StatsKey; +STRUCT_SIZE(StatsKey, 4 * 4); // 16 typedef struct { uint64_t rxPackets; @@ -42,10 +49,12 @@ typedef struct { uint64_t txPackets; uint64_t txBytes; } StatsValue; +STRUCT_SIZE(StatsValue, 4 * 8); // 32 typedef struct { char name[IFNAMSIZ]; } IfaceValue; +STRUCT_SIZE(IfaceValue, 16); typedef struct { uint64_t rxBytes; @@ -89,26 +98,26 @@ const int IFACE_STATS_MAP_SIZE = 1000; const int CONFIGURATION_MAP_SIZE = 2; const int UID_OWNER_MAP_SIZE = 2000; -#define BPF_PATH "/sys/fs/bpf" - -#define BPF_EGRESS_PROG_PATH BPF_PATH "/prog_netd_cgroupskb_egress_stats" -#define BPF_INGRESS_PROG_PATH BPF_PATH "/prog_netd_cgroupskb_ingress_stats" -#define XT_BPF_INGRESS_PROG_PATH BPF_PATH "/prog_netd_skfilter_ingress_xtbpf" -#define XT_BPF_EGRESS_PROG_PATH BPF_PATH "/prog_netd_skfilter_egress_xtbpf" -#define XT_BPF_WHITELIST_PROG_PATH BPF_PATH "/prog_netd_skfilter_whitelist_xtbpf" -#define XT_BPF_BLACKLIST_PROG_PATH BPF_PATH "/prog_netd_skfilter_blacklist_xtbpf" -#define CGROUP_SOCKET_PROG_PATH BPF_PATH "/prog_netd_cgroupsock_inet_create" - -#define COOKIE_TAG_MAP_PATH BPF_PATH "/map_netd_cookie_tag_map" -#define UID_COUNTERSET_MAP_PATH BPF_PATH "/map_netd_uid_counterset_map" -#define APP_UID_STATS_MAP_PATH BPF_PATH "/map_netd_app_uid_stats_map" -#define STATS_MAP_A_PATH BPF_PATH "/map_netd_stats_map_A" -#define STATS_MAP_B_PATH BPF_PATH "/map_netd_stats_map_B" -#define IFACE_INDEX_NAME_MAP_PATH BPF_PATH "/map_netd_iface_index_name_map" -#define IFACE_STATS_MAP_PATH BPF_PATH "/map_netd_iface_stats_map" -#define CONFIGURATION_MAP_PATH BPF_PATH "/map_netd_configuration_map" -#define UID_OWNER_MAP_PATH BPF_PATH "/map_netd_uid_owner_map" -#define UID_PERMISSION_MAP_PATH BPF_PATH "/map_netd_uid_permission_map" +#define BPF_PATH "/sys/fs/bpf/" + +#define BPF_EGRESS_PROG_PATH BPF_PATH "prog_netd_cgroupskb_egress_stats" +#define BPF_INGRESS_PROG_PATH BPF_PATH "prog_netd_cgroupskb_ingress_stats" +#define XT_BPF_INGRESS_PROG_PATH BPF_PATH "prog_netd_skfilter_ingress_xtbpf" +#define XT_BPF_EGRESS_PROG_PATH BPF_PATH "prog_netd_skfilter_egress_xtbpf" +#define XT_BPF_ALLOWLIST_PROG_PATH BPF_PATH "prog_netd_skfilter_allowlist_xtbpf" +#define XT_BPF_DENYLIST_PROG_PATH BPF_PATH "prog_netd_skfilter_denylist_xtbpf" +#define CGROUP_SOCKET_PROG_PATH BPF_PATH "prog_netd_cgroupsock_inet_create" + +#define COOKIE_TAG_MAP_PATH BPF_PATH "map_netd_cookie_tag_map" +#define UID_COUNTERSET_MAP_PATH BPF_PATH "map_netd_uid_counterset_map" +#define APP_UID_STATS_MAP_PATH BPF_PATH "map_netd_app_uid_stats_map" +#define STATS_MAP_A_PATH BPF_PATH "map_netd_stats_map_A" +#define STATS_MAP_B_PATH BPF_PATH "map_netd_stats_map_B" +#define IFACE_INDEX_NAME_MAP_PATH BPF_PATH "map_netd_iface_index_name_map" +#define IFACE_STATS_MAP_PATH BPF_PATH "map_netd_iface_stats_map" +#define CONFIGURATION_MAP_PATH BPF_PATH "map_netd_configuration_map" +#define UID_OWNER_MAP_PATH BPF_PATH "map_netd_uid_owner_map" +#define UID_PERMISSION_MAP_PATH BPF_PATH "map_netd_uid_permission_map" enum UidOwnerMatchType { NO_MATCH = 0, @@ -117,7 +126,8 @@ enum UidOwnerMatchType { DOZABLE_MATCH = (1 << 2), STANDBY_MATCH = (1 << 3), POWERSAVE_MATCH = (1 << 4), - IIF_MATCH = (1 << 5), + RESTRICTED_MATCH = (1 << 5), + IIF_MATCH = (1 << 6), }; enum BpfPermissionMatch { @@ -141,84 +151,55 @@ typedef struct { // Allowed interface index. Only applicable if IIF_MATCH is set in the rule bitmask above. uint32_t iif; // A bitmask of enum values in UidOwnerMatchType. - uint8_t rule; + uint32_t rule; } UidOwnerValue; +STRUCT_SIZE(UidOwnerValue, 2 * 4); // 8 #define UID_RULES_CONFIGURATION_KEY 1 #define CURRENT_STATS_MAP_CONFIGURATION_KEY 2 -#define CLAT_INGRESS_PROG_RAWIP_NAME "prog_clatd_schedcls_ingress_clat_rawip" -#define CLAT_INGRESS_PROG_ETHER_NAME "prog_clatd_schedcls_ingress_clat_ether" +#define CLAT_INGRESS6_PROG_RAWIP_NAME "prog_clatd_schedcls_ingress6_clat_rawip" +#define CLAT_INGRESS6_PROG_ETHER_NAME "prog_clatd_schedcls_ingress6_clat_ether" -#define CLAT_INGRESS_PROG_RAWIP_PATH BPF_PATH "/" CLAT_INGRESS_PROG_RAWIP_NAME -#define CLAT_INGRESS_PROG_ETHER_PATH BPF_PATH "/" CLAT_INGRESS_PROG_ETHER_NAME +#define CLAT_INGRESS6_PROG_RAWIP_PATH BPF_PATH CLAT_INGRESS6_PROG_RAWIP_NAME +#define CLAT_INGRESS6_PROG_ETHER_PATH BPF_PATH CLAT_INGRESS6_PROG_ETHER_NAME -#define CLAT_INGRESS_MAP_PATH BPF_PATH "/map_clatd_clat_ingress_map" +#define CLAT_INGRESS6_MAP_PATH BPF_PATH "map_clatd_clat_ingress6_map" typedef struct { uint32_t iif; // The input interface index struct in6_addr pfx96; // The source /96 nat64 prefix, bottom 32 bits must be 0 struct in6_addr local6; // The full 128-bits of the destination IPv6 address -} ClatIngressKey; +} ClatIngress6Key; +STRUCT_SIZE(ClatIngress6Key, 4 + 2 * 16); // 36 typedef struct { uint32_t oif; // The output interface to redirect to (0 means don't redirect) struct in_addr local4; // The destination IPv4 address -} ClatIngressValue; +} ClatIngress6Value; +STRUCT_SIZE(ClatIngress6Value, 4 + 4); // 8 -#define CLAT_EGRESS_PROG_RAWIP_NAME "prog_clatd_schedcls_egress_clat_rawip" -#define CLAT_EGRESS_PROG_ETHER_NAME "prog_clatd_schedcls_egress_clat_ether" +#define CLAT_EGRESS4_PROG_RAWIP_NAME "prog_clatd_schedcls_egress4_clat_rawip" +#define CLAT_EGRESS4_PROG_ETHER_NAME "prog_clatd_schedcls_egress4_clat_ether" -#define CLAT_EGRESS_PROG_RAWIP_PATH BPF_PATH "/" CLAT_EGRESS_PROG_RAWIP_NAME -#define CLAT_EGRESS_PROG_ETHER_PATH BPF_PATH "/" CLAT_EGRESS_PROG_ETHER_NAME +#define CLAT_EGRESS4_PROG_RAWIP_PATH BPF_PATH CLAT_EGRESS4_PROG_RAWIP_NAME +#define CLAT_EGRESS4_PROG_ETHER_PATH BPF_PATH CLAT_EGRESS4_PROG_ETHER_NAME -#define CLAT_EGRESS_MAP_PATH BPF_PATH "/map_clatd_clat_egress_map" +#define CLAT_EGRESS4_MAP_PATH BPF_PATH "map_clatd_clat_egress4_map" typedef struct { uint32_t iif; // The input interface index struct in_addr local4; // The source IPv4 address -} ClatEgressKey; +} ClatEgress4Key; +STRUCT_SIZE(ClatEgress4Key, 4 + 4); // 8 typedef struct { uint32_t oif; // The output interface to redirect to struct in6_addr local6; // The full 128-bits of the source IPv6 address struct in6_addr pfx96; // The destination /96 nat64 prefix, bottom 32 bits must be 0 bool oifIsEthernet; // Whether the output interface requires ethernet header -} ClatEgressValue; + uint8_t pad[3]; +} ClatEgress4Value; +STRUCT_SIZE(ClatEgress4Value, 4 + 2 * 16 + 1 + 3); // 40 -#define TETHER_INGRESS_PROG_RAWIP_NAME "prog_offload_schedcls_ingress_tether_rawip" -#define TETHER_INGRESS_PROG_ETHER_NAME "prog_offload_schedcls_ingress_tether_ether" - -#define TETHER_INGRESS_PROG_RAWIP_PATH BPF_PATH "/" TETHER_INGRESS_PROG_RAWIP_NAME -#define TETHER_INGRESS_PROG_ETHER_PATH BPF_PATH "/" TETHER_INGRESS_PROG_ETHER_NAME - -#define TETHER_INGRESS_MAP_PATH BPF_PATH "/map_offload_tether_ingress_map" - -typedef struct { - uint32_t iif; // The input interface index - struct in6_addr neigh6; // The destination IPv6 address -} TetherIngressKey; - -typedef struct { - uint32_t oif; // The output interface to redirect to - // For now tethering offload only needs to support downstreams that use 6-byte MAC addresses, - // because all downstream types that are currently supported (WiFi, USB, Bluetooth and - // Ethernet) have 6-byte MAC addresses. - struct ethhdr macHeader; // includes dst/src mac and ethertype - uint16_t pmtu; // The maximum L3 output path/route mtu -} TetherIngressValue; - -#define TETHER_STATS_MAP_PATH BPF_PATH "/map_offload_tether_stats_map" - -typedef struct { - uint64_t rxPackets; - uint64_t rxBytes; - uint64_t rxErrors; - uint64_t txPackets; - uint64_t txBytes; - uint64_t txErrors; -} TetherStatsValue; - -#define TETHER_LIMIT_MAP_PATH BPF_PATH "/map_offload_tether_limit_map" - -#endif // NETDBPF_BPF_SHARED_H +#undef STRUCT_SIZE diff --git a/libnetdutils/Android.bp b/libnetdutils/Android.bp index 00bdc74f..31f2c53b 100644 --- a/libnetdutils/Android.bp +++ b/libnetdutils/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "system_netd_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["system_netd_license"], +} + cc_library { name: "libnetdutils", srcs: [ @@ -44,7 +53,6 @@ cc_test { "InternetAddressesTest.cpp", "LogTest.cpp", "MemBlockTest.cpp", - "OperationLimiterTest.cpp", "SliceTest.cpp", "StatusTest.cpp", "SyscallsTest.cpp", diff --git a/libnetdutils/OperationLimiterTest.cpp b/libnetdutils/OperationLimiterTest.cpp deleted file mode 100644 index 8d72d752..00000000 --- a/libnetdutils/OperationLimiterTest.cpp +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (C) 2018 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. - */ - -#include "netdutils/OperationLimiter.h" - -#include <gtest/gtest-spi.h> - -namespace android { -namespace netdutils { - -TEST(OperationLimiter, limits) { - OperationLimiter<int> limiter(3); - - EXPECT_TRUE(limiter.start(42)); - EXPECT_TRUE(limiter.start(42)); - EXPECT_TRUE(limiter.start(42)); - - // Limit reached... calling any number of times should have no effect. - EXPECT_FALSE(limiter.start(42)); - EXPECT_FALSE(limiter.start(42)); - EXPECT_FALSE(limiter.start(42)); - - // Finishing a single operations is enough for starting a new one... - limiter.finish(42); - EXPECT_TRUE(limiter.start(42)); - - // ...but not two! - EXPECT_FALSE(limiter.start(42)); - - // Different ids should still have quota... - EXPECT_TRUE(limiter.start(666)); - limiter.finish(666); - - // Finish all pending operations - limiter.finish(42); - limiter.finish(42); - limiter.finish(42); -} - -TEST(OperationLimiter, finishWithoutStart) { - OperationLimiter<int> limiter(1); - - // Will output a LOG(FATAL_WITHOUT_ABORT), but we have no way to probe this. - limiter.finish(42); - - // This will ensure that the finish() above didn't set a negative value. - EXPECT_TRUE(limiter.start(42)); - EXPECT_FALSE(limiter.start(42)); -} - -TEST(OperationLimiter, destroyWithActiveOperations) { - // The death message doesn't seem to be captured on Android. - EXPECT_DEBUG_DEATH({ - OperationLimiter<int> limiter(3); - limiter.start(42); - }, "" /* "active operations */); -} - -} // namespace netdutils -} // namespace android diff --git a/libnetdutils/include/netdutils/OperationLimiter.h b/libnetdutils/include/netdutils/OperationLimiter.h deleted file mode 100644 index 992a8498..00000000 --- a/libnetdutils/include/netdutils/OperationLimiter.h +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (C) 2018 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. - */ - -#ifndef NETUTILS_OPERATIONLIMITER_H -#define NETUTILS_OPERATIONLIMITER_H - -#include <mutex> -#include <unordered_map> - -#include <android-base/logging.h> -#include <android-base/thread_annotations.h> - -namespace android { -namespace netdutils { - -// Tracks the number of operations in progress on behalf of a particular key or -// ID, rejecting further attempts to start new operations after a configurable -// limit has been reached. -// -// The intended usage pattern is: -// OperationLimiter<UserId> connections_per_user; -// ... -// // Before opening a new connection -// if (!limiter.start(user)) { -// return error; -// } else { -// // open the connection -// // ...do some work... -// // close the connection -// limiter.finish(user); -// } -// -// This class is thread-safe. -template<typename KeyType> -class OperationLimiter { -public: - explicit OperationLimiter(int limit) : mLimitPerKey(limit) {} - - ~OperationLimiter() { - DCHECK(mCounters.empty()) - << "Destroying OperationLimiter with active operations"; - } - - // Returns false if |key| has reached the maximum number of concurrent - // operations, otherwise increments the counter and returns true. - // - // Note: each successful start(key) must be matched by exactly one call to - // finish(key). - bool start(KeyType key) EXCLUDES(mMutex) { - std::lock_guard lock(mMutex); - auto& cnt = mCounters[key]; // operator[] creates new entries as needed. - if (cnt >= mLimitPerKey) { - // Oh, no! - return false; - } - ++cnt; - return true; - } - - // Decrements the number of operations in progress accounted to |key|. - // See usage notes on start(). - void finish(KeyType key) EXCLUDES(mMutex) { - std::lock_guard lock(mMutex); - auto it = mCounters.find(key); - if (it == mCounters.end()) { - LOG(FATAL_WITHOUT_ABORT) << "Decremented non-existent counter for key=" << key; - return; - } - auto& cnt = it->second; - --cnt; - if (cnt <= 0) { - // Cleanup counters once they drop down to zero. - mCounters.erase(it); - } - } - -private: - // Protects access to the map below. - std::mutex mMutex; - - // Tracks the number of outstanding queries by key. - std::unordered_map<KeyType, int> mCounters GUARDED_BY(mMutex); - - // Maximum number of outstanding queries from a single key. - const int mLimitPerKey; -}; - -} // namespace netdutils -} // namespace android - -#endif // NETUTILS_OPERATIONLIMITER_H diff --git a/netutils_wrappers/Android.bp b/netutils_wrappers/Android.bp index af7d8c3b..fdf802f4 100644 --- a/netutils_wrappers/Android.bp +++ b/netutils_wrappers/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "system_netd_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["system_netd_license"], +} + cc_binary { name: "netutils-wrapper-1.0", defaults: ["netd_defaults"], @@ -23,6 +32,7 @@ cc_binary { ], sanitize: { cfi: true, + memtag_heap: true, }, } diff --git a/netutils_wrappers/NetUtilsWrapper-1.0.cpp b/netutils_wrappers/NetUtilsWrapper-1.0.cpp index cdc454e3..d81b0ec6 100644 --- a/netutils_wrappers/NetUtilsWrapper-1.0.cpp +++ b/netutils_wrappers/NetUtilsWrapper-1.0.cpp @@ -73,8 +73,6 @@ const char *EXPECTED_REGEXPS[] = { // Other activities observed on current devices. In future releases, these should be supported // in a way that is less likely to interfere with general Android networking behaviour. CMD "tc qdisc del dev root", - CMD "ip( -4| -6)? rule .* goto 13000 prio 11999", - CMD "ip( -4| -6)? rule .* prio 25000", CMD "ip(6)?tables -w .* -j " VENDOR_CHAIN, CMD "iptables -w -t mangle -[AD] PREROUTING -m socket --nowildcard --restore-skmark -j ACCEPT", CMD "ndc network interface (add|remove) oem[0-9]+$", // Invalid command: no interface removed. diff --git a/server/Android.bp b/server/Android.bp index 36ad42c3..9c89041a 100644 --- a/server/Android.bp +++ b/server/Android.bp @@ -1,8 +1,18 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "system_netd_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["system_netd_license"], +} + java_library { name: "netd_aidl_interface-lateststable-java", sdk_version: "system_current", + min_sdk_version: "29", static_libs: [ - "netd_aidl_interface-java", + "netd_aidl_interface-V7-java", ], apex_available: [ "//apex_available:platform", // used from services.net @@ -12,6 +22,35 @@ java_library { ], } +cc_library_static { + name: "netd_event_listener_interface-lateststable-ndk_platform", + whole_static_libs: [ + "netd_event_listener_interface-V1-ndk_platform", + ], + apex_available: [ + "com.android.resolv", + ], + min_sdk_version: "29", +} + +cc_library_static { + name: "netd_aidl_interface-lateststable-ndk_platform", + whole_static_libs: [ + "netd_aidl_interface-V7-ndk_platform", + ], + apex_available: [ + "com.android.resolv", + ], + min_sdk_version: "29", +} + +cc_library_static { + name: "netd_aidl_interface-lateststable-cpp", + whole_static_libs: [ + "netd_aidl_interface-V7-cpp", + ], +} + aidl_interface { name: "netd_aidl_interface", local_include_dir: "binder", @@ -21,11 +60,16 @@ aidl_interface { "binder/android/net/INetdUnsolicitedEventListener.aidl", "binder/android/net/InterfaceConfigurationParcel.aidl", "binder/android/net/MarkMaskParcel.aidl", + "binder/android/net/NativeNetworkConfig.aidl", + "binder/android/net/NativeNetworkType.aidl", + "binder/android/net/NativeVpnType.aidl", "binder/android/net/RouteInfoParcel.aidl", "binder/android/net/TetherConfigParcel.aidl", "binder/android/net/TetherOffloadRuleParcel.aidl", "binder/android/net/TetherStatsParcel.aidl", "binder/android/net/UidRangeParcel.aidl", + // Add new AIDL classes in android.net.netd.aidl to consist with other network modules. + "binder/android/net/netd/aidl/**/*.aidl", ], backend: { cpp: { @@ -43,20 +87,32 @@ aidl_interface { // this is part of updatable modules(NetworkStack) which targets 29(Q) min_sdk_version: "29", }, + ndk: { + apex_available: [ + "//apex_available:platform", + ], + // This is necessary for the DnsResovler tests to run in Android Q. + // Soong would recognize this value and produce the Q compatible aidl library. + min_sdk_version: "29", + }, }, versions: [ "1", "2", "3", "4", + "5", + "6", + "7", ], } java_library { name: "netd_event_listener_interface-lateststable-java", sdk_version: "system_current", + min_sdk_version: "29", static_libs: [ - "netd_event_listener_interface-java", + "netd_event_listener_interface-V1-java", ], apex_available: [ "//apex_available:platform", @@ -81,6 +137,15 @@ aidl_interface { ], min_sdk_version: "29", }, + java: { + apex_available: [ + "//apex_available:platform", + "com.android.bluetooth.updatable", + "com.android.wifi", + "com.android.tethering", + ], + min_sdk_version: "29", + }, }, } @@ -98,27 +163,6 @@ aidl_interface { ], } -// Convenience build target for the version of the netd stable AIDL interface used by the platform. -// This exists to ensure that all non-updatable code in the system server uses the same version. -// Mixing different versions of generated classes results in code non-deterministically(?) using one -// of the compiled-in versions, and potentially crashing when code compiled against a newer version -// ends up using a generated class from an older version and calls methods that don't exist. -// This must be a frozen version on REL builds and can be -unstable on development builds. -// See http://b/143560726 for an example. -java_library { - name: "netd_aidl_interfaces-platform-java", - static_libs: [ - "netd_aidl_interface-java", - "netd_event_listener_interface-java", - ], - // TODO: remove bluetooth, which doesn't actually use netd at all. - apex_available: [ - "//apex_available:platform", // due to the dependency from services.net - "com.android.bluetooth.updatable", - ], - sdk_version: "system_current", -} - // These are used in netd_integration_test // TODO: fold these into a cc_library_static after converting netd/server to Android.bp filegroup { @@ -177,8 +221,9 @@ cc_library_static { "libpcap", "libqtaguid", "libssl", - "netd_aidl_interface-cpp", - "netd_event_listener_interface-cpp", + "libsysutils", + "netd_aidl_interface-V7-cpp", + "netd_event_listener_interface-V1-cpp", ], aidl: { export_aidl_headers: true, @@ -198,7 +243,6 @@ cc_binary { "bpfloader", "clatd.o", "netd.o", - "offload.o", ], shared_libs: [ "android.system.net.netd@1.0", @@ -209,7 +253,6 @@ cc_binary { "libcutils", "libdl", "libhidlbase", - "libjsoncpp", "liblog", "libmdnssd", "libnetd_resolv", @@ -222,8 +265,8 @@ cc_binary { "libselinux", "libsysutils", "libutils", - "netd_aidl_interface-cpp", - "netd_event_listener_interface-cpp", + "netd_aidl_interface-V7-cpp", + "netd_event_listener_interface-V1-cpp", "oemnetd_aidl_interface-cpp", ], static_libs: [ @@ -245,6 +288,7 @@ cc_binary { "PhysicalNetwork.cpp", "PppController.cpp", "Process.cpp", + "UnreachableNetwork.cpp", "VirtualNetwork.cpp", "main.cpp", "oem_iptables_hook.cpp", @@ -271,8 +315,8 @@ cc_binary { "liblog", "libutils", "libbinder", - "dnsresolver_aidl_interface-cpp", - "netd_aidl_interface-cpp", + "dnsresolver_aidl_interface-V7-cpp", + "netd_aidl_interface-V6-cpp", ], srcs: [ "ndc.cpp", @@ -281,6 +325,7 @@ cc_binary { ], sanitize: { cfi: true, + memtag_heap: true, }, } @@ -318,8 +363,8 @@ cc_test { "libnetd_server", "libnetd_test_tun_interface", "libqtaguid", - "netd_aidl_interface-unstable-cpp", - "netd_event_listener_interface-cpp", + "netd_aidl_interface-V7-cpp", + "netd_event_listener_interface-V1-cpp", ], shared_libs: [ "libbase", @@ -334,4 +379,5 @@ cc_test { "libsysutils", "libutils", ], + // tidy: false, // cuts test build time by almost 1 minute } diff --git a/server/BandwidthController.cpp b/server/BandwidthController.cpp index e1ce56f2..1b46234a 100644 --- a/server/BandwidthController.cpp +++ b/server/BandwidthController.cpp @@ -70,6 +70,7 @@ const char BandwidthController::LOCAL_GLOBAL_ALERT[] = "bw_global_alert"; auto BandwidthController::iptablesRestoreFunction = execIptablesRestoreWithOutput; using android::base::Join; +using android::base::StartsWith; using android::base::StringAppendF; using android::base::StringPrintf; using android::net::FirewallController; @@ -83,9 +84,6 @@ namespace { const char ALERT_GLOBAL_NAME[] = "globalAlert"; const std::string NEW_CHAIN_COMMAND = "-N "; -const char NAUGHTY_CHAIN[] = "bw_penalty_box"; -const char NICE_CHAIN[] = "bw_happy_box"; - /** * Some comments about the rules: * * Ordering @@ -120,12 +118,12 @@ const char NICE_CHAIN[] = "bw_happy_box"; * iptables -A bw_costly_iface0 -j bw_penalty_box * * * Penalty box, happy box and data saver. - * - bw_penalty box is a blacklist of apps that are rejected. - * - bw_happy_box is a whitelist of apps. It always includes all system apps + * - bw_penalty box is a denylist of apps that are rejected. + * - bw_happy_box is an allowlist of apps. It always includes all system apps * - bw_data_saver implements data usage restrictions. - * - Via the UI the user can add and remove apps from the whitelist and - * blacklist, and turn on/off data saver. - * - The blacklist takes precedence over the whitelist and the whitelist + * - Via the UI the user can add and remove apps from the allowlist and + * denylist, and turn on/off data saver. + * - The denylist takes precedence over the allowlist and the allowlist * takes precedence over data saver. * * * bw_penalty_box handling: @@ -149,12 +147,8 @@ const char NICE_CHAIN[] = "bw_happy_box"; */ const std::string COMMIT_AND_CLOSE = "COMMIT\n"; -const std::string HAPPY_BOX_MATCH_WHITELIST_COMMAND = - StringPrintf("-I bw_happy_box -m owner --uid-owner %d-%d -j RETURN", 0, MAX_SYSTEM_UID); -const std::string BPF_HAPPY_BOX_MATCH_WHITELIST_COMMAND = StringPrintf( - "-I bw_happy_box -m bpf --object-pinned %s -j RETURN", XT_BPF_WHITELIST_PROG_PATH); -const std::string BPF_PENALTY_BOX_MATCH_BLACKLIST_COMMAND = StringPrintf( - "-I bw_penalty_box -m bpf --object-pinned %s -j REJECT", XT_BPF_BLACKLIST_PROG_PATH); +const std::string BPF_PENALTY_BOX_MATCH_DENYLIST_COMMAND = StringPrintf( + "-I bw_penalty_box -m bpf --object-pinned %s -j REJECT", XT_BPF_DENYLIST_PROG_PATH); static const std::vector<std::string> IPT_FLUSH_COMMANDS = { /* @@ -212,7 +206,8 @@ static const uint32_t uidBillingMask = Fwmark::getUidBillingMask(); * See go/ipsec-data-accounting for more information. */ -std::vector<std::string> getBasicAccountingCommands(const bool useBpf) { +std::vector<std::string> getBasicAccountingCommands() { + // clang-format off std::vector<std::string> ipt_basic_accounting_commands = { "*filter", @@ -221,29 +216,14 @@ std::vector<std::string> getBasicAccountingCommands(const bool useBpf) { "-A bw_INPUT -p esp -j RETURN", StringPrintf("-A bw_INPUT -m mark --mark 0x%x/0x%x -j RETURN", uidBillingMask, uidBillingMask), - // This is ingress application UID xt_qtaguid (pre-ebpf) accounting, - // for bpf this is handled out of cgroup hooks instead. - useBpf ? "" : "-A bw_INPUT -m owner --socket-exists", StringPrintf("-A bw_INPUT -j MARK --or-mark 0x%x", uidBillingMask), - "-A bw_OUTPUT -j bw_global_alert", - // Prevents IPSec double counting (Tunnel mode and Transport mode, - // respectively) - useBpf ? "" : "-A bw_OUTPUT -o " IPSEC_IFACE_PREFIX "+ -j RETURN", - useBpf ? "" : "-A bw_OUTPUT -m policy --pol ipsec --dir out -j RETURN", - // Don't count clat traffic, as it has already been counted (and subject to - // costly / happy_box / data_saver / penalty_box etc. based on the real UID) - // on the stacked interface. - useBpf ? "" : "-A bw_OUTPUT -m owner --uid-owner clat -j RETURN", - // This is egress application UID xt_qtaguid (pre-ebpf) accounting, - // for bpf this is handled out of cgroup hooks instead. - useBpf ? "" : "-A bw_OUTPUT -m owner --socket-exists", - "-A bw_costly_shared -j bw_penalty_box", - useBpf ? BPF_PENALTY_BOX_MATCH_BLACKLIST_COMMAND : "", - "-A bw_penalty_box -j bw_happy_box", "-A bw_happy_box -j bw_data_saver", + ("-I bw_penalty_box -m bpf --object-pinned " XT_BPF_DENYLIST_PROG_PATH " -j REJECT"), + "-A bw_penalty_box -j bw_happy_box", + "-A bw_happy_box -j bw_data_saver", "-A bw_data_saver -j RETURN", - useBpf ? BPF_HAPPY_BOX_MATCH_WHITELIST_COMMAND : HAPPY_BOX_MATCH_WHITELIST_COMMAND, + ("-I bw_happy_box -m bpf --object-pinned " XT_BPF_ALLOWLIST_PROG_PATH " -j RETURN"), "COMMIT", "*raw", @@ -262,8 +242,7 @@ std::vector<std::string> getBasicAccountingCommands(const bool useBpf) { // // Hence we will never double count and additional corrections are not needed. // We can simply take the sum of base and stacked (+20B/pkt) interface counts. - useBpf ? "-A bw_raw_PREROUTING -m bpf --object-pinned " XT_BPF_INGRESS_PROG_PATH - : "-A bw_raw_PREROUTING -m owner --socket-exists", + ("-A bw_raw_PREROUTING -m bpf --object-pinned " XT_BPF_INGRESS_PROG_PATH), "COMMIT", "*mangle", @@ -279,22 +258,14 @@ std::vector<std::string> getBasicAccountingCommands(const bool useBpf) { // This is egress interface accounting: we account 464xlat traffic only on // the clat interface (as offloaded packets never hit base interface's ip6tables) // and later sum base and stacked with overhead (+20B/pkt) in higher layers - useBpf ? "-A bw_mangle_POSTROUTING -m bpf --object-pinned " XT_BPF_EGRESS_PROG_PATH - : "-A bw_mangle_POSTROUTING -m owner --socket-exists", + ("-A bw_mangle_POSTROUTING -m bpf --object-pinned " XT_BPF_EGRESS_PROG_PATH), COMMIT_AND_CLOSE}; + // clang-format on return ipt_basic_accounting_commands; } -std::vector<std::string> toStrVec(int num, const char* const strs[]) { - return std::vector<std::string>(strs, strs + num); -} - } // namespace -void BandwidthController::setBpfEnabled(bool isEnabled) { - mBpfSupported = isEnabled; -} - BandwidthController::BandwidthController() { } @@ -321,7 +292,7 @@ int BandwidthController::enableBandwidthControl() { flushCleanTables(false); - std::string commands = Join(getBasicAccountingCommands(mBpfSupported), '\n'); + std::string commands = Join(getBasicAccountingCommands(), '\n'); return iptablesRestoreFunction(V4V6, commands, nullptr); } @@ -352,63 +323,29 @@ int BandwidthController::enableDataSaver(bool enable) { return ret; } -// TODO: Remove after removing these commands in CommandListener -int BandwidthController::addNaughtyApps(int numUids, const char* const appUids[]) { - return manipulateSpecialApps(toStrVec(numUids, appUids), NAUGHTY_CHAIN, - IptJumpReject, IptOpInsert); +int BandwidthController::addNaughtyApps(const std::vector<uint32_t>& appUids) { + return manipulateSpecialApps(appUids, PENALTY_BOX_MATCH, IptOpInsert); } -// TODO: Remove after removing these commands in CommandListener -int BandwidthController::removeNaughtyApps(int numUids, const char* const appUids[]) { - return manipulateSpecialApps(toStrVec(numUids, appUids), NAUGHTY_CHAIN, - IptJumpReject, IptOpDelete); +int BandwidthController::removeNaughtyApps(const std::vector<uint32_t>& appUids) { + return manipulateSpecialApps(appUids, PENALTY_BOX_MATCH, IptOpDelete); } -// TODO: Remove after removing these commands in CommandListener -int BandwidthController::addNiceApps(int numUids, const char* const appUids[]) { - return manipulateSpecialApps(toStrVec(numUids, appUids), NICE_CHAIN, - IptJumpReturn, IptOpInsert); +int BandwidthController::addNiceApps(const std::vector<uint32_t>& appUids) { + return manipulateSpecialApps(appUids, HAPPY_BOX_MATCH, IptOpInsert); } -// TODO: Remove after removing these commands in CommandListener -int BandwidthController::removeNiceApps(int numUids, const char* const appUids[]) { - return manipulateSpecialApps(toStrVec(numUids, appUids), NICE_CHAIN, - IptJumpReturn, IptOpDelete); +int BandwidthController::removeNiceApps(const std::vector<uint32_t>& appUids) { + return manipulateSpecialApps(appUids, HAPPY_BOX_MATCH, IptOpDelete); } -int BandwidthController::addNaughtyApps(const std::vector<std::string>& appStrUid) { - return manipulateSpecialApps(appStrUid, NAUGHTY_CHAIN, IptJumpReject, IptOpInsert); -} - -int BandwidthController::removeNaughtyApps(const std::vector<std::string>& appStrUid) { - return manipulateSpecialApps(appStrUid, NAUGHTY_CHAIN, IptJumpReject, IptOpDelete); -} - -int BandwidthController::addNiceApps(const std::vector<std::string>& appStrUid) { - return manipulateSpecialApps(appStrUid, NICE_CHAIN, IptJumpReturn, IptOpInsert); -} - -int BandwidthController::removeNiceApps(const std::vector<std::string>& appStrUid) { - return manipulateSpecialApps(appStrUid, NICE_CHAIN, IptJumpReturn, IptOpDelete); -} - -int BandwidthController::manipulateSpecialApps(const std::vector<std::string>& appStrUids, - const std::string& chain, IptJumpOp jumpHandling, - IptOp op) { - if (mBpfSupported) { - Status status = gCtls->trafficCtrl.updateUidOwnerMap(appStrUids, jumpHandling, op); - if (!isOk(status)) { - ALOGE("unable to update the Bandwidth Uid Map: %s", toString(status).c_str()); - } - return status.code(); - } - std::string cmd = "*filter\n"; - for (const auto& appStrUid : appStrUids) { - StringAppendF(&cmd, "%s %s -m owner --uid-owner %s%s\n", opToString(op), chain.c_str(), - appStrUid.c_str(), jumpToString(jumpHandling)); +int BandwidthController::manipulateSpecialApps(const std::vector<uint32_t>& appUids, + UidOwnerMatchType matchType, IptOp op) { + Status status = gCtls->trafficCtrl.updateUidOwnerMap(appUids, matchType, op); + if (!isOk(status)) { + ALOGE("unable to update the Bandwidth Uid Map: %s", toString(status).c_str()); } - StringAppendF(&cmd, "COMMIT\n"); - return iptablesRestoreFunction(V4V6, cmd, nullptr); + return status.code(); } int BandwidthController::setInterfaceSharedQuota(const std::string& iface, int64_t maxBytes) { @@ -835,11 +772,11 @@ void BandwidthController::parseAndFlushCostlyTables(const std::string& ruleList, // Find and flush all rules starting with "-N bw_costly_<iface>" except "-N bw_costly_shared". while (std::getline(stream, rule, '\n')) { - if (rule.find(NEW_CHAIN_COMMAND) != 0) continue; + if (!StartsWith(rule, NEW_CHAIN_COMMAND)) continue; chainName = rule.substr(NEW_CHAIN_COMMAND.size()); ALOGV("parse chainName=<%s> orig line=<%s>", chainName.c_str(), rule.c_str()); - if (chainName.find("bw_costly_") != 0 || chainName == std::string("bw_costly_shared")) { + if (!StartsWith(chainName, "bw_costly_") || chainName == std::string("bw_costly_shared")) { continue; } @@ -874,8 +811,6 @@ inline const char *BandwidthController::jumpToString(IptJumpOp jumpHandling) { * For port-unreachable (default), TCP should consider as an abort (RFC1122). */ switch (jumpHandling) { - case IptJumpNoAdd: - return ""; case IptJumpReject: return " -j REJECT"; case IptJumpReturn: diff --git a/server/BandwidthController.h b/server/BandwidthController.h index b8691dcc..414e91b8 100644 --- a/server/BandwidthController.h +++ b/server/BandwidthController.h @@ -24,6 +24,7 @@ #include <mutex> #include "NetdConstants.h" +#include "netdbpf/bpf_shared.h" class BandwidthController { public: @@ -32,7 +33,6 @@ public: BandwidthController(); int setupIptablesHooks(); - void setBpfEnabled(bool isEnabled); int enableBandwidthControl(); int disableBandwidthControl(); @@ -46,16 +46,10 @@ public: int getInterfaceQuota(const std::string& iface, int64_t* bytes); int removeInterfaceQuota(const std::string& iface); - // TODO: Remove after removing these commands in CommandListener - int addNaughtyApps(int numUids, const char* const appUids[]); - int removeNaughtyApps(int numUids, const char* const appUids[]); - int addNiceApps(int numUids, const char* const appUids[]); - int removeNiceApps(int numUids, const char* const appUids[]); - - int addNaughtyApps(const std::vector<std::string>& appStrUid); - int removeNaughtyApps(const std::vector<std::string>& appStrUid); - int addNiceApps(const std::vector<std::string>& appStrUid); - int removeNiceApps(const std::vector<std::string>& appStrUid); + int addNaughtyApps(const std::vector<uint32_t>& appUids); + int removeNaughtyApps(const std::vector<uint32_t>& appUids); + int addNiceApps(const std::vector<uint32_t>& appUids); + int removeNiceApps(const std::vector<uint32_t>& appUids); int setGlobalAlert(int64_t bytes); int removeGlobalAlert(); @@ -75,7 +69,7 @@ public: static const char LOCAL_MANGLE_POSTROUTING[]; static const char LOCAL_GLOBAL_ALERT[]; - enum IptJumpOp { IptJumpReject, IptJumpReturn, IptJumpNoAdd }; + enum IptJumpOp { IptJumpReject, IptJumpReturn }; enum IptOp { IptOpInsert, IptOpDelete }; private: @@ -96,8 +90,8 @@ public: std::string makeDataSaverCommand(IptablesTarget target, bool enable); - int manipulateSpecialApps(const std::vector<std::string>& appStrUids, const std::string& chain, - IptJumpOp jumpHandling, IptOp appOp); + int manipulateSpecialApps(const std::vector<uint32_t>& appStrUids, UidOwnerMatchType matchType, + IptOp appOp); int runIptablesAlertCmd(IptOp op, const std::string& alertName, int64_t bytes); int runIptablesAlertFwdCmd(IptOp op, const std::string& alertName, int64_t bytes); @@ -132,8 +126,6 @@ public: static const char *opToString(IptOp op); static const char *jumpToString(IptJumpOp jumpHandling); - bool mBpfSupported = false; - int64_t mSharedQuotaBytes = 0; int64_t mSharedAlertBytes = 0; int64_t mGlobalAlertBytes = 0; diff --git a/server/BandwidthControllerTest.cpp b/server/BandwidthControllerTest.cpp index 115a0da5..d635daf0 100644 --- a/server/BandwidthControllerTest.cpp +++ b/server/BandwidthControllerTest.cpp @@ -50,73 +50,6 @@ using android::net::TunInterface; using android::netdutils::UniqueFile; using android::netdutils::status::ok; -const std::string ACCOUNT_RULES_WITHOUT_BPF = - "*filter\n" - "-A bw_INPUT -j bw_global_alert\n" - "-A bw_INPUT -p esp -j RETURN\n" - "-A bw_INPUT -m mark --mark 0x100000/0x100000 -j RETURN\n" - "-A bw_INPUT -m owner --socket-exists\n" - "-A bw_INPUT -j MARK --or-mark 0x100000\n" - "-A bw_OUTPUT -j bw_global_alert\n" - "-A bw_OUTPUT -o ipsec+ -j RETURN\n" - "-A bw_OUTPUT -m policy --pol ipsec --dir out -j RETURN\n" - "-A bw_OUTPUT -m owner --uid-owner clat -j RETURN\n" - "-A bw_OUTPUT -m owner --socket-exists\n" - "-A bw_costly_shared -j bw_penalty_box\n" - "\n" - "-A bw_penalty_box -j bw_happy_box\n" - "-A bw_happy_box -j bw_data_saver\n" - "-A bw_data_saver -j RETURN\n" - "-I bw_happy_box -m owner --uid-owner 0-9999 -j RETURN\n" - "COMMIT\n" - "*raw\n" - "-A bw_raw_PREROUTING -i ipsec+ -j RETURN\n" - "-A bw_raw_PREROUTING -m policy --pol ipsec --dir in -j RETURN\n" - "-A bw_raw_PREROUTING -m owner --socket-exists\n" - "COMMIT\n" - "*mangle\n" - "-A bw_mangle_POSTROUTING -o ipsec+ -j RETURN\n" - "-A bw_mangle_POSTROUTING -m policy --pol ipsec --dir out -j RETURN\n" - "-A bw_mangle_POSTROUTING -j MARK --set-mark 0x0/0x100000\n" - "-A bw_mangle_POSTROUTING -m owner --uid-owner clat -j RETURN\n" - "-A bw_mangle_POSTROUTING -m owner --socket-exists\n" - "COMMIT\n"; - -const std::string ACCOUNT_RULES_WITH_BPF = - "*filter\n" - "-A bw_INPUT -j bw_global_alert\n" - "-A bw_INPUT -p esp -j RETURN\n" - "-A bw_INPUT -m mark --mark 0x100000/0x100000 -j RETURN\n" - "\n" - "-A bw_INPUT -j MARK --or-mark 0x100000\n" - "-A bw_OUTPUT -j bw_global_alert\n" - "\n" - "\n" - "\n" - "\n" - "-A bw_costly_shared -j bw_penalty_box\n" + - StringPrintf("-I bw_penalty_box -m bpf --object-pinned %s -j REJECT\n", - XT_BPF_BLACKLIST_PROG_PATH) + - "-A bw_penalty_box -j bw_happy_box\n" - "-A bw_happy_box -j bw_data_saver\n" - "-A bw_data_saver -j RETURN\n" + - StringPrintf("-I bw_happy_box -m bpf --object-pinned %s -j RETURN\n", - XT_BPF_WHITELIST_PROG_PATH) + - "COMMIT\n" - "*raw\n" - "-A bw_raw_PREROUTING -i ipsec+ -j RETURN\n" - "-A bw_raw_PREROUTING -m policy --pol ipsec --dir in -j RETURN\n" + - StringPrintf("-A bw_raw_PREROUTING -m bpf --object-pinned %s\n", XT_BPF_INGRESS_PROG_PATH) + - "COMMIT\n" - "*mangle\n" - "-A bw_mangle_POSTROUTING -o ipsec+ -j RETURN\n" - "-A bw_mangle_POSTROUTING -m policy --pol ipsec --dir out -j RETURN\n" - "-A bw_mangle_POSTROUTING -j MARK --set-mark 0x0/0x100000\n" - "-A bw_mangle_POSTROUTING -m owner --uid-owner clat -j RETURN\n" + - StringPrintf("-A bw_mangle_POSTROUTING -m bpf --object-pinned %s\n", - XT_BPF_EGRESS_PROG_PATH) + - "COMMIT\n"; - class BandwidthControllerTest : public IptablesBaseTest { protected: BandwidthControllerTest() { @@ -195,23 +128,6 @@ protected: EXPECT_CALL(mSyscalls, fclose(dummyFile)).WillOnce(Return(ok)); } - void checkBandwithControl(bool useBpf) { - // Pretend no bw_costly_shared_<iface> rules already exist... - addIptablesRestoreOutput( - "-P OUTPUT ACCEPT\n" - "-N bw_costly_shared\n" - "-N unrelated\n"); - - // ... so none are flushed or deleted. - std::string expectedClean = ""; - - std::string expectedAccounting = - useBpf ? ACCOUNT_RULES_WITH_BPF : ACCOUNT_RULES_WITHOUT_BPF; - mBw.setBpfEnabled(useBpf); - mBw.enableBandwidthControl(); - expectSetupCommands(expectedClean, expectedAccounting); - } - StrictMock<android::netdutils::ScopedMockSyscalls> mSyscalls; }; @@ -247,12 +163,46 @@ TEST_F(BandwidthControllerTest, TestCheckUidBillingMask) { EXPECT_TRUE(isPowerOfTwo); } -TEST_F(BandwidthControllerTest, TestEnableBandwidthControlWithBpf) { - checkBandwithControl(true); -} +TEST_F(BandwidthControllerTest, TestEnableBandwidthControl) { + // Pretend no bw_costly_shared_<iface> rules already exist... + addIptablesRestoreOutput( + "-P OUTPUT ACCEPT\n" + "-N bw_costly_shared\n" + "-N unrelated\n"); + + // ... so none are flushed or deleted. + // clang-format off + static const std::string expectedClean = ""; + static const std::string expectedAccounting = + "*filter\n" + "-A bw_INPUT -j bw_global_alert\n" + "-A bw_INPUT -p esp -j RETURN\n" + "-A bw_INPUT -m mark --mark 0x100000/0x100000 -j RETURN\n" + "-A bw_INPUT -j MARK --or-mark 0x100000\n" + "-A bw_OUTPUT -j bw_global_alert\n" + "-A bw_costly_shared -j bw_penalty_box\n" + "-I bw_penalty_box -m bpf --object-pinned " XT_BPF_DENYLIST_PROG_PATH " -j REJECT\n" + "-A bw_penalty_box -j bw_happy_box\n" + "-A bw_happy_box -j bw_data_saver\n" + "-A bw_data_saver -j RETURN\n" + "-I bw_happy_box -m bpf --object-pinned " XT_BPF_ALLOWLIST_PROG_PATH " -j RETURN\n" + "COMMIT\n" + "*raw\n" + "-A bw_raw_PREROUTING -i ipsec+ -j RETURN\n" + "-A bw_raw_PREROUTING -m policy --pol ipsec --dir in -j RETURN\n" + "-A bw_raw_PREROUTING -m bpf --object-pinned " XT_BPF_INGRESS_PROG_PATH "\n" + "COMMIT\n" + "*mangle\n" + "-A bw_mangle_POSTROUTING -o ipsec+ -j RETURN\n" + "-A bw_mangle_POSTROUTING -m policy --pol ipsec --dir out -j RETURN\n" + "-A bw_mangle_POSTROUTING -j MARK --set-mark 0x0/0x100000\n" + "-A bw_mangle_POSTROUTING -m owner --uid-owner clat -j RETURN\n" + "-A bw_mangle_POSTROUTING -m bpf --object-pinned " XT_BPF_EGRESS_PROG_PATH "\n" + "COMMIT\n"; + // clang-format on -TEST_F(BandwidthControllerTest, TestEnableBandwidthControlWithoutBpf) { - checkBandwithControl(false); + mBw.enableBandwidthControl(); + expectSetupCommands(expectedClean, expectedAccounting); } TEST_F(BandwidthControllerTest, TestDisableBandwidthControl) { @@ -512,24 +462,3 @@ TEST_F(BandwidthControllerTest, CostlyAlert) { expectIptablesRestoreCommands(expected); } -TEST_F(BandwidthControllerTest, ManipulateSpecialApps) { - std::vector<const char *> appUids = { "1000", "1001", "10012" }; - - std::vector<std::string> expected = { - "*filter\n" - "-I bw_happy_box -m owner --uid-owner 1000 -j RETURN\n" - "-I bw_happy_box -m owner --uid-owner 1001 -j RETURN\n" - "-I bw_happy_box -m owner --uid-owner 10012 -j RETURN\n" - "COMMIT\n"}; - EXPECT_EQ(0, mBw.addNiceApps(appUids.size(), const_cast<char**>(&appUids[0]))); - expectIptablesRestoreCommands(expected); - - expected = { - "*filter\n" - "-D bw_penalty_box -m owner --uid-owner 1000 -j REJECT\n" - "-D bw_penalty_box -m owner --uid-owner 1001 -j REJECT\n" - "-D bw_penalty_box -m owner --uid-owner 10012 -j REJECT\n" - "COMMIT\n"}; - EXPECT_EQ(0, mBw.removeNaughtyApps(appUids.size(), const_cast<char**>(&appUids[0]))); - expectIptablesRestoreCommands(expected); -} diff --git a/server/ClatdController.cpp b/server/ClatdController.cpp index 90afa3a4..847a6db6 100644 --- a/server/ClatdController.cpp +++ b/server/ClatdController.cpp @@ -73,51 +73,23 @@ namespace net { void ClatdController::init(void) { std::lock_guard guard(mutex); - // TODO: should refactor into separate function for testability - if (!bpf::isBpfSupported()) { - ALOGI("Pre-4.9 kernel or pre-P api shipping level - disabling clat ebpf."); - mClatEbpfMode = ClatEbpfDisabled; - return; - } - - // We know the device initially shipped with at least P..., - // but did it ship with at least Q? - - uint64_t api_level = base::GetUintProperty<uint64_t>("ro.product.first_api_level", 0); - if (api_level == 0) { - ALOGE("Cannot determine initial API level of the device."); - api_level = base::GetUintProperty<uint64_t>("ro.build.version.sdk", 0); - } - - // Note: MINIMUM_API_REQUIRED is for eBPF as a whole and is thus P - if (api_level > bpf::MINIMUM_API_REQUIRED) { - ALOGI("4.9+ kernel and device shipped with Q+ - clat ebpf should work."); - mClatEbpfMode = ClatEbpfEnabled; - } else { - // We cannot guarantee that 4.9-P kernels will include NET_CLS_BPF support. - ALOGI("4.9+ kernel and device shipped with P - clat ebpf might work."); - mClatEbpfMode = ClatEbpfMaybe; - } - - int rv = getClatEgressMapFd(); + int rv = getClatEgress4MapFd(); if (rv < 0) { - ALOGE("getClatEgressMapFd() failure: %s", strerror(-rv)); - mClatEbpfMode = ClatEbpfDisabled; + ALOGE("getClatEgress4MapFd() failure: %s", strerror(-rv)); return; } - mClatEgressMap.reset(rv); + mClatEgress4Map.reset(rv); - rv = getClatIngressMapFd(); + rv = getClatIngress6MapFd(); if (rv < 0) { - ALOGE("getClatIngressMapFd() failure: %s", strerror(-rv)); - mClatEbpfMode = ClatEbpfDisabled; - mClatEgressMap.reset(-1); + ALOGE("getClatIngress6MapFd() failure: %s", strerror(-rv)); + mClatEgress4Map.reset(-1); return; } - mClatIngressMap.reset(rv); + mClatIngress6Map.reset(rv); - mClatEgressMap.clear(); - mClatIngressMap.clear(); + mClatEgress4Map.clear(); + mClatIngress6Map.clear(); } bool ClatdController::isIpv4AddressFree(in_addr_t addr) { @@ -226,8 +198,6 @@ int ClatdController::generateIpv6Address(const char* iface, const in_addr v4, } void ClatdController::maybeStartBpf(const ClatdTracker& tracker) { - if (mClatEbpfMode == ClatEbpfDisabled) return; - auto isEthernet = android::net::isEthernet(tracker.iface); if (!isEthernet.ok()) { ALOGE("isEthernet(%s[%d]) failure: %s", tracker.iface, tracker.ifIndex, @@ -236,54 +206,54 @@ void ClatdController::maybeStartBpf(const ClatdTracker& tracker) { } // This program will be attached to the v4-* interface which is a TUN and thus always rawip. - int rv = getClatEgressProgFd(RAWIP); + int rv = getClatEgress4ProgFd(RAWIP); if (rv < 0) { - ALOGE("getClatEgressProgFd(RAWIP) failure: %s", strerror(-rv)); + ALOGE("getClatEgress4ProgFd(RAWIP) failure: %s", strerror(-rv)); return; } unique_fd txRawIpProgFd(rv); - rv = getClatIngressProgFd(isEthernet.value()); + rv = getClatIngress6ProgFd(isEthernet.value()); if (rv < 0) { - ALOGE("getClatIngressProgFd(%d) failure: %s", isEthernet.value(), strerror(-rv)); + ALOGE("getClatIngress6ProgFd(%d) failure: %s", isEthernet.value(), strerror(-rv)); return; } unique_fd rxProgFd(rv); - ClatEgressKey txKey = { + ClatEgress4Key txKey = { .iif = tracker.v4ifIndex, .local4 = tracker.v4, }; - ClatEgressValue txValue = { + ClatEgress4Value txValue = { .oif = tracker.ifIndex, .local6 = tracker.v6, .pfx96 = tracker.pfx96, .oifIsEthernet = isEthernet.value(), }; - auto ret = mClatEgressMap.writeValue(txKey, txValue, BPF_ANY); + auto ret = mClatEgress4Map.writeValue(txKey, txValue, BPF_ANY); if (!ret.ok()) { - ALOGE("mClatEgressMap.writeValue failure: %s", strerror(ret.error().code())); + ALOGE("mClatEgress4Map.writeValue failure: %s", strerror(ret.error().code())); return; } - ClatIngressKey rxKey = { + ClatIngress6Key rxKey = { .iif = tracker.ifIndex, .pfx96 = tracker.pfx96, .local6 = tracker.v6, }; - ClatIngressValue rxValue = { + ClatIngress6Value rxValue = { // TODO: move all the clat code to eBPF and remove the tun interface entirely. .oif = tracker.v4ifIndex, .local4 = tracker.v4, }; - ret = mClatIngressMap.writeValue(rxKey, rxValue, BPF_ANY); + ret = mClatIngress6Map.writeValue(rxKey, rxValue, BPF_ANY); if (!ret.ok()) { - ALOGE("mClatIngressMap.writeValue failure: %s", strerror(ret.error().code())); - ret = mClatEgressMap.deleteValue(txKey); + ALOGE("mClatIngress6Map.writeValue failure: %s", strerror(ret.error().code())); + ret = mClatEgress4Map.deleteValue(txKey); if (!ret.ok()) - ALOGE("mClatEgressMap.deleteValue failure: %s", strerror(ret.error().code())); + ALOGE("mClatEgress4Map.deleteValue failure: %s", strerror(ret.error().code())); return; } @@ -298,47 +268,37 @@ void ClatdController::maybeStartBpf(const ClatdTracker& tracker) { if (rv) { ALOGE("tcQdiscAddDevClsact(%d[%s]) failure: %s", tracker.v4ifIndex, tracker.v4iface, strerror(-rv)); - ret = mClatEgressMap.deleteValue(txKey); + ret = mClatEgress4Map.deleteValue(txKey); if (!ret.ok()) - ALOGE("mClatEgressMap.deleteValue failure: %s", strerror(ret.error().code())); - ret = mClatIngressMap.deleteValue(rxKey); + ALOGE("mClatEgress4Map.deleteValue failure: %s", strerror(ret.error().code())); + ret = mClatIngress6Map.deleteValue(rxKey); if (!ret.ok()) - ALOGE("mClatIngressMap.deleteValue failure: %s", strerror(ret.error().code())); + ALOGE("mClatIngress6Map.deleteValue failure: %s", strerror(ret.error().code())); return; } rv = tcFilterAddDevEgressClatIpv4(tracker.v4ifIndex, txRawIpProgFd, RAWIP); if (rv) { - if ((rv == -ENOENT) && (mClatEbpfMode == ClatEbpfMaybe)) { - ALOGI("tcFilterAddDevEgressClatIpv4(%d[%s], RAWIP): %s", tracker.v4ifIndex, - tracker.v4iface, strerror(-rv)); - } else { - ALOGE("tcFilterAddDevEgressClatIpv4(%d[%s], RAWIP) failure: %s", tracker.v4ifIndex, - tracker.v4iface, strerror(-rv)); - } + ALOGE("tcFilterAddDevEgressClatIpv4(%d[%s], RAWIP) failure: %s", tracker.v4ifIndex, + tracker.v4iface, strerror(-rv)); // The v4- interface clsact is not deleted for unwinding error because once it is created // with interface addition, the lifetime is till interface deletion. Moreover, the clsact // has no clat filter now. It should not break anything. - ret = mClatEgressMap.deleteValue(txKey); + ret = mClatEgress4Map.deleteValue(txKey); if (!ret.ok()) - ALOGE("mClatEgressMap.deleteValue failure: %s", strerror(ret.error().code())); - ret = mClatIngressMap.deleteValue(rxKey); + ALOGE("mClatEgress4Map.deleteValue failure: %s", strerror(ret.error().code())); + ret = mClatIngress6Map.deleteValue(rxKey); if (!ret.ok()) - ALOGE("mClatIngressMap.deleteValue failure: %s", strerror(ret.error().code())); + ALOGE("mClatIngress6Map.deleteValue failure: %s", strerror(ret.error().code())); return; } rv = tcFilterAddDevIngressClatIpv6(tracker.ifIndex, rxProgFd, isEthernet.value()); if (rv) { - if ((rv == -ENOENT) && (mClatEbpfMode == ClatEbpfMaybe)) { - ALOGI("tcFilterAddDevIngressClatIpv6(%d[%s], %d): %s", tracker.ifIndex, tracker.iface, - isEthernet.value(), strerror(-rv)); - } else { - ALOGE("tcFilterAddDevIngressClatIpv6(%d[%s], %d) failure: %s", tracker.ifIndex, - tracker.iface, isEthernet.value(), strerror(-rv)); - } + ALOGE("tcFilterAddDevIngressClatIpv6(%d[%s], %d) failure: %s", tracker.ifIndex, + tracker.iface, isEthernet.value(), strerror(-rv)); rv = tcFilterDelDevEgressClatIpv4(tracker.v4ifIndex); if (rv) { ALOGE("tcFilterDelDevEgressClatIpv4(%d[%s]) failure: %s", tracker.v4ifIndex, @@ -348,12 +308,12 @@ void ClatdController::maybeStartBpf(const ClatdTracker& tracker) { // The v4- interface clsact is not deleted. See the reason in the error unwinding code of // the egress filter attaching of v4- tun interface. - ret = mClatEgressMap.deleteValue(txKey); + ret = mClatEgress4Map.deleteValue(txKey); if (!ret.ok()) - ALOGE("mClatEgressMap.deleteValue failure: %s", strerror(ret.error().code())); - ret = mClatIngressMap.deleteValue(rxKey); + ALOGE("mClatEgress4Map.deleteValue failure: %s", strerror(ret.error().code())); + ret = mClatIngress6Map.deleteValue(rxKey); if (!ret.ok()) - ALOGE("mClatIngressMap.deleteValue failure: %s", strerror(ret.error().code())); + ALOGE("mClatIngress6Map.deleteValue failure: %s", strerror(ret.error().code())); return; } @@ -372,8 +332,6 @@ void ClatdController::setIptablesDropRule(bool add, const char* iface, const cha } void ClatdController::maybeStopBpf(const ClatdTracker& tracker) { - if (mClatEbpfMode == ClatEbpfDisabled) return; - int rv = tcFilterDelDevIngressClatIpv6(tracker.ifIndex); if (rv < 0) { ALOGE("tcFilterDelDevIngressClatIpv6(%d[%s]) failure: %s", tracker.ifIndex, tracker.iface, @@ -389,22 +347,22 @@ void ClatdController::maybeStopBpf(const ClatdTracker& tracker) { // We cleanup the maps last, so scanning through them can be used to // determine what still needs cleanup. - ClatEgressKey txKey = { + ClatEgress4Key txKey = { .iif = tracker.v4ifIndex, .local4 = tracker.v4, }; - auto ret = mClatEgressMap.deleteValue(txKey); - if (!ret.ok()) ALOGE("mClatEgressMap.deleteValue failure: %s", strerror(ret.error().code())); + auto ret = mClatEgress4Map.deleteValue(txKey); + if (!ret.ok()) ALOGE("mClatEgress4Map.deleteValue failure: %s", strerror(ret.error().code())); - ClatIngressKey rxKey = { + ClatIngress6Key rxKey = { .iif = tracker.ifIndex, .pfx96 = tracker.pfx96, .local6 = tracker.v6, }; - ret = mClatIngressMap.deleteValue(rxKey); - if (!ret.ok()) ALOGE("mClatIngressMap.deleteValue failure: %s", strerror(ret.error().code())); + ret = mClatIngress6Map.deleteValue(rxKey); + if (!ret.ok()) ALOGE("mClatIngress6Map.deleteValue failure: %s", strerror(ret.error().code())); } // Finds the tracker of the clatd running on interface |interface|, or nullptr if clatd has not been @@ -619,14 +577,14 @@ int ClatdController::stopClatd(const std::string& interface) { } void ClatdController::dumpEgress(DumpWriter& dw) { - if (!mClatEgressMap.isValid()) return; // if unsupported just don't dump anything + if (!mClatEgress4Map.isValid()) return; // if unsupported just don't dump anything ScopedIndent bpfIndent(dw); dw.println("BPF egress map: iif(iface) v4Addr -> v6Addr nat64Prefix oif(iface)"); ScopedIndent bpfDetailIndent(dw); - const auto printClatMap = [&dw](const ClatEgressKey& key, const ClatEgressValue& value, - const BpfMap<ClatEgressKey, ClatEgressValue>&) { + const auto printClatMap = [&dw](const ClatEgress4Key& key, const ClatEgress4Value& value, + const BpfMap<ClatEgress4Key, ClatEgress4Value>&) { char iifStr[IFNAMSIZ] = "?"; char local4Str[INET_ADDRSTRLEN] = "?"; char local6Str[INET6_ADDRSTRLEN] = "?"; @@ -643,21 +601,21 @@ void ClatdController::dumpEgress(DumpWriter& dw) { pfx96Str, value.oif, oifStr, value.oifIsEthernet ? "ether" : "rawip"); return Result<void>(); }; - auto res = mClatEgressMap.iterateWithValue(printClatMap); + auto res = mClatEgress4Map.iterateWithValue(printClatMap); if (!res.ok()) { dw.println("Error printing BPF map: %s", res.error().message().c_str()); } } void ClatdController::dumpIngress(DumpWriter& dw) { - if (!mClatIngressMap.isValid()) return; // if unsupported just don't dump anything + if (!mClatIngress6Map.isValid()) return; // if unsupported just don't dump anything ScopedIndent bpfIndent(dw); dw.println("BPF ingress map: iif(iface) nat64Prefix v6Addr -> v4Addr oif(iface)"); ScopedIndent bpfDetailIndent(dw); - const auto printClatMap = [&dw](const ClatIngressKey& key, const ClatIngressValue& value, - const BpfMap<ClatIngressKey, ClatIngressValue>&) { + const auto printClatMap = [&dw](const ClatIngress6Key& key, const ClatIngress6Value& value, + const BpfMap<ClatIngress6Key, ClatIngress6Value>&) { char iifStr[IFNAMSIZ] = "?"; char pfx96Str[INET6_ADDRSTRLEN] = "?"; char local6Str[INET6_ADDRSTRLEN] = "?"; @@ -674,7 +632,7 @@ void ClatdController::dumpIngress(DumpWriter& dw) { value.oif, oifStr); return Result<void>(); }; - auto res = mClatIngressMap.iterateWithValue(printClatMap); + auto res = mClatIngress6Map.iterateWithValue(printClatMap); if (!res.ok()) { dw.println("Error printing BPF map: %s", res.error().message().c_str()); } diff --git a/server/ClatdController.h b/server/ClatdController.h index 2c139580..4b7c60bb 100644 --- a/server/ClatdController.h +++ b/server/ClatdController.h @@ -89,19 +89,8 @@ class ClatdController { in6_addr* v6); static void makeChecksumNeutral(in6_addr* v6, const in_addr v4, const in6_addr& nat64Prefix); - enum eClatEbpfMode { - ClatEbpfDisabled, // <4.9 kernel || <P api shipping level -- will not work - ClatEbpfMaybe, // >=4.9 kernel && P api shipping level -- might work - ClatEbpfEnabled, // >=4.9 kernel && >=Q api shipping level -- must work - }; - eClatEbpfMode mClatEbpfMode GUARDED_BY(mutex); - eClatEbpfMode getEbpfMode() EXCLUDES(mutex) { - std::lock_guard guard(mutex); - return mClatEbpfMode; - } - - bpf::BpfMap<ClatEgressKey, ClatEgressValue> mClatEgressMap GUARDED_BY(mutex); - bpf::BpfMap<ClatIngressKey, ClatIngressValue> mClatIngressMap GUARDED_BY(mutex); + bpf::BpfMap<ClatEgress4Key, ClatEgress4Value> mClatEgress4Map GUARDED_BY(mutex); + bpf::BpfMap<ClatIngress6Key, ClatIngress6Value> mClatIngress6Map GUARDED_BY(mutex); void maybeStartBpf(const ClatdTracker& tracker) REQUIRES(mutex); void maybeStopBpf(const ClatdTracker& tracker) REQUIRES(mutex); diff --git a/server/ClatdControllerTest.cpp b/server/ClatdControllerTest.cpp index dc71a033..d3a2aa73 100644 --- a/server/ClatdControllerTest.cpp +++ b/server/ClatdControllerTest.cpp @@ -69,7 +69,6 @@ class ClatdControllerTest : public IptablesBaseTest { protected: ClatdController mClatdCtrl; - bool isEbpfDisabled() { return mClatdCtrl.getEbpfMode() == ClatdController::ClatEbpfDisabled; } void setIptablesDropRule(bool a, const char* b, const char* c, const char* d) { std::lock_guard guard(mClatdCtrl.mutex); return mClatdCtrl.setIptablesDropRule(a, b, c, d); diff --git a/server/Controllers.cpp b/server/Controllers.cpp index 5af8a91e..1f2bac22 100644 --- a/server/Controllers.cpp +++ b/server/Controllers.cpp @@ -285,10 +285,15 @@ void Controllers::init() { netdutils::Status tcStatus = trafficCtrl.start(); if (!isOk(tcStatus)) { gLog.error("Failed to start trafficcontroller: (%s)", toString(tcStatus).c_str()); + gLog.error("CRITICAL: sleeping 60 seconds, netd exiting with failure, crash loop likely!"); + // The expected reason we get here is a major kernel or other code bug, as such + // the probability that things will succeed on restart of netd is pretty small. + // So, let's wait a minute to at least try to limit the log spam a little bit. + sleep(60); + exit(1); } gLog.info("Initializing traffic control: %" PRId64 "us", s.getTimeAndResetUs()); - bandwidthCtrl.setBpfEnabled(trafficCtrl.getBpfEnabled()); bandwidthCtrl.enableBandwidthControl(); gLog.info("Enabling bandwidth control: %" PRId64 "us", s.getTimeAndResetUs()); diff --git a/server/DummyNetwork.cpp b/server/DummyNetwork.cpp index 6cd6791d..186072db 100644 --- a/server/DummyNetwork.cpp +++ b/server/DummyNetwork.cpp @@ -14,17 +14,22 @@ * limitations under the License. */ -#include "DummyNetwork.h" +#define LOG_TAG "Netd" -#include "RouteController.h" +#include "DummyNetwork.h" -#define LOG_TAG "Netd" -#include "log/log.h" #include "errno.h" namespace android { namespace net { +// The dummy network is used to blackhole or reject traffic. +// It has an IPv4 and an IPv6 default route that point to a dummy interface +// which drops packets. It is used for system purposes only. Applications +// cannot use multinetwork APIs such as Network#bindSocket or +// android_setsocknetwork to send packets on the dummy network. +// Any attempt to do so will fail with ENETUNREACH. + const char* DummyNetwork::INTERFACE_NAME = "dummy0"; DummyNetwork::DummyNetwork(unsigned netId) : Network(netId) { @@ -34,17 +39,5 @@ DummyNetwork::DummyNetwork(unsigned netId) : Network(netId) { DummyNetwork::~DummyNetwork() { } -Network::Type DummyNetwork::getType() const { - return DUMMY; -} - -int DummyNetwork::addInterface(const std::string& /* interface */) { - return -EINVAL; -} - -int DummyNetwork::removeInterface(const std::string& /* interface */) { - return -EINVAL; -} - } // namespace net } // namespace android diff --git a/server/DummyNetwork.h b/server/DummyNetwork.h index 5823ce54..8f9960b2 100644 --- a/server/DummyNetwork.h +++ b/server/DummyNetwork.h @@ -27,9 +27,7 @@ class DummyNetwork : public Network { virtual ~DummyNetwork(); private: - Type getType() const override; - [[nodiscard]] int addInterface(const std::string& interface) override; - [[nodiscard]] int removeInterface(const std::string& interface) override; + std::string getTypeString() const override { return "DUMMY"; }; }; } // namespace android::net diff --git a/server/FirewallController.cpp b/server/FirewallController.cpp index 3c070ce9..0a0f8d82 100644 --- a/server/FirewallController.cpp +++ b/server/FirewallController.cpp @@ -53,10 +53,6 @@ constexpr const uid_t kDefaultMaximumUid = UID_MAX - 1; // UID_MAX defined as U // Proc file containing the uid mapping for the user namespace of the current process. const char kUidMapProcFile[] = "/proc/self/uid_map"; -bool getBpfOwnerStatus() { - return gCtls->trafficCtrl.getBpfEnabled(); -} - } // namespace namespace android { @@ -73,6 +69,7 @@ const char* FirewallController::LOCAL_FORWARD = "fw_FORWARD"; const char* FirewallController::LOCAL_DOZABLE = "fw_dozable"; const char* FirewallController::LOCAL_STANDBY = "fw_standby"; const char* FirewallController::LOCAL_POWERSAVE = "fw_powersave"; +const char* FirewallController::LOCAL_RESTRICTED = "fw_restricted"; // ICMPv6 types that are required for any form of IPv6 connectivity to work. Note that because the // fw_dozable chain is called from both INPUT and OUTPUT, this includes both packets that we need @@ -87,20 +84,22 @@ const char* FirewallController::ICMPV6_TYPES[] = { }; FirewallController::FirewallController(void) : mMaxUid(discoverMaximumValidUid(kUidMapProcFile)) { - // If no rules are set, it's in BLACKLIST mode - mFirewallType = BLACKLIST; + // If no rules are set, it's in DENYLIST mode + mFirewallType = DENYLIST; mIfaceRules = {}; } int FirewallController::setupIptablesHooks(void) { int res = 0; - mUseBpfOwnerMatch = getBpfOwnerStatus(); + // mUseBpfOwnerMatch should be removed, but it is still depended upon by test code. + mUseBpfOwnerMatch = true; if (mUseBpfOwnerMatch) { return res; } res |= createChain(LOCAL_DOZABLE, getFirewallType(DOZABLE)); res |= createChain(LOCAL_STANDBY, getFirewallType(STANDBY)); res |= createChain(LOCAL_POWERSAVE, getFirewallType(POWERSAVE)); + res |= createChain(LOCAL_RESTRICTED, getFirewallType(RESTRICTED)); return res; } @@ -110,7 +109,7 @@ int FirewallController::setFirewallType(FirewallType ftype) { // flush any existing rules resetFirewall(); - if (ftype == WHITELIST) { + if (ftype == ALLOWLIST) { // create default rule to drop all traffic std::string command = "*filter\n" @@ -121,14 +120,14 @@ int FirewallController::setFirewallType(FirewallType ftype) { res = execIptablesRestore(V4V6, command.c_str()); } - // Set this after calling disableFirewall(), since it defaults to WHITELIST there + // Set this after calling disableFirewall(), since it defaults to ALLOWLIST there mFirewallType = ftype; } return res ? -EREMOTEIO : 0; } int FirewallController::resetFirewall(void) { - mFirewallType = WHITELIST; + mFirewallType = ALLOWLIST; mIfaceRules.clear(); // flush any existing rules @@ -155,6 +154,9 @@ int FirewallController::enableChildChains(ChildChain chain, bool enable) { case POWERSAVE: name = LOCAL_POWERSAVE; break; + case RESTRICTED: + name = LOCAL_RESTRICTED; + break; default: return res; } @@ -178,8 +180,8 @@ int FirewallController::isFirewallEnabled(void) { } int FirewallController::setInterfaceRule(const char* iface, FirewallRule rule) { - if (mFirewallType == BLACKLIST) { - // Unsupported in BLACKLIST mode + if (mFirewallType == DENYLIST) { + // Unsupported in DENYLIST mode return -EINVAL; } @@ -214,15 +216,17 @@ int FirewallController::setInterfaceRule(const char* iface, FirewallRule rule) { FirewallType FirewallController::getFirewallType(ChildChain chain) { switch(chain) { case DOZABLE: - return WHITELIST; + return ALLOWLIST; case STANDBY: - return BLACKLIST; + return DENYLIST; case POWERSAVE: - return WHITELIST; + return ALLOWLIST; + case RESTRICTED: + return ALLOWLIST; case NONE: return mFirewallType; default: - return BLACKLIST; + return DENYLIST; } } @@ -230,11 +234,11 @@ int FirewallController::setUidRule(ChildChain chain, int uid, FirewallRule rule) const char* op; const char* target; FirewallType firewallType = getFirewallType(chain); - if (firewallType == WHITELIST) { + if (firewallType == ALLOWLIST) { target = "RETURN"; // When adding, insert RETURN rules at the front, before the catch-all DROP at the end. op = (rule == ALLOW)? "-I" : "-D"; - } else { // BLACKLIST mode + } else { // DENYLIST mode target = "DROP"; // When adding, append DROP rules at the end, after the RETURN rule that matches TCP RSTs. op = (rule == DENY)? "-A" : "-D"; @@ -243,16 +247,19 @@ int FirewallController::setUidRule(ChildChain chain, int uid, FirewallRule rule) std::vector<std::string> chainNames; switch(chain) { case DOZABLE: - chainNames = { LOCAL_DOZABLE }; + chainNames = {LOCAL_DOZABLE}; break; case STANDBY: - chainNames = { LOCAL_STANDBY }; + chainNames = {LOCAL_STANDBY}; break; case POWERSAVE: - chainNames = { LOCAL_POWERSAVE }; + chainNames = {LOCAL_POWERSAVE}; + break; + case RESTRICTED: + chainNames = {LOCAL_RESTRICTED}; break; case NONE: - chainNames = { LOCAL_INPUT, LOCAL_OUTPUT }; + chainNames = {LOCAL_INPUT, LOCAL_OUTPUT}; break; default: ALOGW("Unknown child chain: %d", chain); @@ -274,7 +281,7 @@ int FirewallController::setUidRule(ChildChain chain, int uid, FirewallRule rule) int FirewallController::createChain(const char* chain, FirewallType type) { static const std::vector<int32_t> NO_UIDS; - return replaceUidChain(chain, type == WHITELIST, NO_UIDS); + return replaceUidChain(chain, type == ALLOWLIST, NO_UIDS); } /* static */ @@ -290,18 +297,18 @@ std::string FirewallController::makeCriticalCommands(IptablesTarget target, cons return commands; } -std::string FirewallController::makeUidRules(IptablesTarget target, const char *name, - bool isWhitelist, const std::vector<int32_t>& uids) { +std::string FirewallController::makeUidRules(IptablesTarget target, const char* name, + bool isAllowlist, const std::vector<int32_t>& uids) { std::string commands; StringAppendF(&commands, "*filter\n:%s -\n", name); - // Whitelist chains have UIDs at the beginning, and new UIDs are added with '-I'. - if (isWhitelist) { + // Allowlist chains have UIDs at the beginning, and new UIDs are added with '-I'. + if (isAllowlist) { for (auto uid : uids) { StringAppendF(&commands, "-A %s -m owner --uid-owner %d -j RETURN\n", name, uid); } - // Always whitelist system UIDs. + // Always allowlist system UIDs. StringAppendF(&commands, "-A %s -m owner --uid-owner %d-%d -j RETURN\n", name, 0, MAX_SYSTEM_UID); @@ -310,7 +317,7 @@ std::string FirewallController::makeUidRules(IptablesTarget target, const char * StringAppendF(&commands, "-A %s -m owner ! --uid-owner %d-%u -j RETURN\n", name, 0, mMaxUid); - // Always whitelist traffic with protocol ESP, or no known socket - required for IPSec + // Always allowlist traffic with protocol ESP, or no known socket - required for IPSec StringAppendF(&commands, "-A %s -p esp -j RETURN\n", name); } @@ -322,20 +329,20 @@ std::string FirewallController::makeUidRules(IptablesTarget target, const char * // access. Both incoming and outgoing RSTs are allowed. StringAppendF(&commands, "-A %s -p tcp --tcp-flags RST RST -j RETURN\n", name); - if (isWhitelist) { + if (isAllowlist) { commands.append(makeCriticalCommands(target, name)); } - // Blacklist chains have UIDs at the end, and new UIDs are added with '-A'. - if (!isWhitelist) { + // Denylist chains have UIDs at the end, and new UIDs are added with '-A'. + if (!isAllowlist) { for (auto uid : uids) { StringAppendF(&commands, "-A %s -m owner --uid-owner %d -j DROP\n", name, uid); } } - // If it's a whitelist chain, add a default DROP at the end. This is not necessary for a - // blacklist chain, because all user-defined chains implicitly RETURN at the end. - if (isWhitelist) { + // If it's an allowlist chain, add a default DROP at the end. This is not necessary for a + // denylist chain, because all user-defined chains implicitly RETURN at the end. + if (isAllowlist) { StringAppendF(&commands, "-A %s -j DROP\n", name); } @@ -344,13 +351,13 @@ std::string FirewallController::makeUidRules(IptablesTarget target, const char * return commands; } -int FirewallController::replaceUidChain( - const std::string &name, bool isWhitelist, const std::vector<int32_t>& uids) { +int FirewallController::replaceUidChain(const std::string& name, bool isAllowlist, + const std::vector<int32_t>& uids) { if (mUseBpfOwnerMatch) { - return gCtls->trafficCtrl.replaceUidOwnerMap(name, isWhitelist, uids); + return gCtls->trafficCtrl.replaceUidOwnerMap(name, isAllowlist, uids); } - std::string commands4 = makeUidRules(V4, name.c_str(), isWhitelist, uids); - std::string commands6 = makeUidRules(V6, name.c_str(), isWhitelist, uids); + std::string commands4 = makeUidRules(V4, name.c_str(), isAllowlist, uids); + std::string commands6 = makeUidRules(V6, name.c_str(), isAllowlist, uids); return execIptablesRestore(V4, commands4.c_str()) | execIptablesRestore(V6, commands6.c_str()); } @@ -398,4 +405,4 @@ uid_t FirewallController::discoverMaximumValidUid(const std::string& fileName) { } } // namespace net -} // namespace android
\ No newline at end of file +} // namespace android diff --git a/server/FirewallController.h b/server/FirewallController.h index 620f1969..6cabfb52 100644 --- a/server/FirewallController.h +++ b/server/FirewallController.h @@ -33,16 +33,17 @@ namespace net { enum FirewallRule { ALLOW = INetd::FIREWALL_RULE_ALLOW, DENY = INetd::FIREWALL_RULE_DENY }; -// WHITELIST means the firewall denies all by default, uids must be explicitly ALLOWed -// BLACKLIST means the firewall allows all by default, uids must be explicitly DENYed +// ALLOWLIST means the firewall denies all by default, uids must be explicitly ALLOWed +// DENYLIST means the firewall allows all by default, uids must be explicitly DENYed -enum FirewallType { WHITELIST = INetd::FIREWALL_WHITELIST, BLACKLIST = INetd::FIREWALL_BLACKLIST }; +enum FirewallType { ALLOWLIST = INetd::FIREWALL_ALLOWLIST, DENYLIST = INetd::FIREWALL_DENYLIST }; enum ChildChain { NONE = INetd::FIREWALL_CHAIN_NONE, DOZABLE = INetd::FIREWALL_CHAIN_DOZABLE, STANDBY = INetd::FIREWALL_CHAIN_STANDBY, POWERSAVE = INetd::FIREWALL_CHAIN_POWERSAVE, + RESTRICTED = INetd::FIREWALL_CHAIN_RESTRICTED, INVALID_CHAIN }; @@ -85,6 +86,7 @@ public: static const char* LOCAL_DOZABLE; static const char* LOCAL_STANDBY; static const char* LOCAL_POWERSAVE; + static const char* LOCAL_RESTRICTED; static const char* ICMPV6_TYPES[]; @@ -92,7 +94,7 @@ public: protected: friend class FirewallControllerTest; - std::string makeUidRules(IptablesTarget target, const char *name, bool isWhitelist, + std::string makeUidRules(IptablesTarget target, const char* name, bool isAllowlist, const std::vector<int32_t>& uids); static int (*execIptablesRestore)(IptablesTarget target, const std::string& commands); diff --git a/server/FirewallControllerTest.cpp b/server/FirewallControllerTest.cpp index 1b53fb85..df6ca82d 100644 --- a/server/FirewallControllerTest.cpp +++ b/server/FirewallControllerTest.cpp @@ -56,61 +56,58 @@ protected: } }; -TEST_F(FirewallControllerTest, TestCreateWhitelistChain) { +TEST_F(FirewallControllerTest, TestCreateAllowlistChain) { std::vector<std::string> expectedRestore4 = { - "*filter", - ":fw_whitelist -", - "-A fw_whitelist -m owner --uid-owner 0-9999 -j RETURN", - "-A fw_whitelist -m owner ! --uid-owner 0-4294967294 -j RETURN", - "-A fw_whitelist -p esp -j RETURN", - "-A fw_whitelist -i lo -j RETURN", - "-A fw_whitelist -o lo -j RETURN", - "-A fw_whitelist -p tcp --tcp-flags RST RST -j RETURN", - "-A fw_whitelist -j DROP", - "COMMIT\n" - }; + "*filter", + ":fw_allowlist -", + "-A fw_allowlist -m owner --uid-owner 0-9999 -j RETURN", + "-A fw_allowlist -m owner ! --uid-owner 0-4294967294 -j RETURN", + "-A fw_allowlist -p esp -j RETURN", + "-A fw_allowlist -i lo -j RETURN", + "-A fw_allowlist -o lo -j RETURN", + "-A fw_allowlist -p tcp --tcp-flags RST RST -j RETURN", + "-A fw_allowlist -j DROP", + "COMMIT\n"}; std::vector<std::string> expectedRestore6 = { - "*filter", - ":fw_whitelist -", - "-A fw_whitelist -m owner --uid-owner 0-9999 -j RETURN", - "-A fw_whitelist -m owner ! --uid-owner 0-4294967294 -j RETURN", - "-A fw_whitelist -p esp -j RETURN", - "-A fw_whitelist -i lo -j RETURN", - "-A fw_whitelist -o lo -j RETURN", - "-A fw_whitelist -p tcp --tcp-flags RST RST -j RETURN", - "-A fw_whitelist -p icmpv6 --icmpv6-type packet-too-big -j RETURN", - "-A fw_whitelist -p icmpv6 --icmpv6-type router-solicitation -j RETURN", - "-A fw_whitelist -p icmpv6 --icmpv6-type router-advertisement -j RETURN", - "-A fw_whitelist -p icmpv6 --icmpv6-type neighbour-solicitation -j RETURN", - "-A fw_whitelist -p icmpv6 --icmpv6-type neighbour-advertisement -j RETURN", - "-A fw_whitelist -p icmpv6 --icmpv6-type redirect -j RETURN", - "-A fw_whitelist -j DROP", - "COMMIT\n" - }; + "*filter", + ":fw_allowlist -", + "-A fw_allowlist -m owner --uid-owner 0-9999 -j RETURN", + "-A fw_allowlist -m owner ! --uid-owner 0-4294967294 -j RETURN", + "-A fw_allowlist -p esp -j RETURN", + "-A fw_allowlist -i lo -j RETURN", + "-A fw_allowlist -o lo -j RETURN", + "-A fw_allowlist -p tcp --tcp-flags RST RST -j RETURN", + "-A fw_allowlist -p icmpv6 --icmpv6-type packet-too-big -j RETURN", + "-A fw_allowlist -p icmpv6 --icmpv6-type router-solicitation -j RETURN", + "-A fw_allowlist -p icmpv6 --icmpv6-type router-advertisement -j RETURN", + "-A fw_allowlist -p icmpv6 --icmpv6-type neighbour-solicitation -j RETURN", + "-A fw_allowlist -p icmpv6 --icmpv6-type neighbour-advertisement -j RETURN", + "-A fw_allowlist -p icmpv6 --icmpv6-type redirect -j RETURN", + "-A fw_allowlist -j DROP", + "COMMIT\n"}; std::vector<std::pair<IptablesTarget, std::string>> expectedRestoreCommands = { {V4, Join(expectedRestore4, '\n')}, {V6, Join(expectedRestore6, '\n')}, }; - createChain("fw_whitelist", WHITELIST); + createChain("fw_allowlist", ALLOWLIST); expectIptablesRestoreCommands(expectedRestoreCommands); } -TEST_F(FirewallControllerTest, TestCreateBlacklistChain) { +TEST_F(FirewallControllerTest, TestCreateDenylistChain) { std::vector<std::string> expectedRestore = { - "*filter", - ":fw_blacklist -", - "-A fw_blacklist -i lo -j RETURN", - "-A fw_blacklist -o lo -j RETURN", - "-A fw_blacklist -p tcp --tcp-flags RST RST -j RETURN", - "COMMIT\n" - }; + "*filter", + ":fw_denylist -", + "-A fw_denylist -i lo -j RETURN", + "-A fw_denylist -o lo -j RETURN", + "-A fw_denylist -p tcp --tcp-flags RST RST -j RETURN", + "COMMIT\n"}; std::vector<std::pair<IptablesTarget, std::string>> expectedRestoreCommands = { {V4, Join(expectedRestore, '\n')}, {V6, Join(expectedRestore, '\n')}, }; - createChain("fw_blacklist", BLACKLIST); + createChain("fw_denylist", DENYLIST); expectIptablesRestoreCommands(expectedRestoreCommands); } @@ -162,50 +159,50 @@ TEST_F(FirewallControllerTest, TestSetFirewallRule) { expectIptablesRestoreCommands(expected); } -TEST_F(FirewallControllerTest, TestReplaceWhitelistUidRule) { +TEST_F(FirewallControllerTest, TestReplaceAllowlistUidRule) { std::string expected = "*filter\n" - ":FW_whitechain -\n" - "-A FW_whitechain -m owner --uid-owner 10023 -j RETURN\n" - "-A FW_whitechain -m owner --uid-owner 10059 -j RETURN\n" - "-A FW_whitechain -m owner --uid-owner 10124 -j RETURN\n" - "-A FW_whitechain -m owner --uid-owner 10111 -j RETURN\n" - "-A FW_whitechain -m owner --uid-owner 110122 -j RETURN\n" - "-A FW_whitechain -m owner --uid-owner 210153 -j RETURN\n" - "-A FW_whitechain -m owner --uid-owner 210024 -j RETURN\n" - "-A FW_whitechain -m owner --uid-owner 0-9999 -j RETURN\n" - "-A FW_whitechain -m owner ! --uid-owner 0-4294967294 -j RETURN\n" - "-A FW_whitechain -p esp -j RETURN\n" - "-A FW_whitechain -i lo -j RETURN\n" - "-A FW_whitechain -o lo -j RETURN\n" - "-A FW_whitechain -p tcp --tcp-flags RST RST -j RETURN\n" - "-A FW_whitechain -p icmpv6 --icmpv6-type packet-too-big -j RETURN\n" - "-A FW_whitechain -p icmpv6 --icmpv6-type router-solicitation -j RETURN\n" - "-A FW_whitechain -p icmpv6 --icmpv6-type router-advertisement -j RETURN\n" - "-A FW_whitechain -p icmpv6 --icmpv6-type neighbour-solicitation -j RETURN\n" - "-A FW_whitechain -p icmpv6 --icmpv6-type neighbour-advertisement -j RETURN\n" - "-A FW_whitechain -p icmpv6 --icmpv6-type redirect -j RETURN\n" - "-A FW_whitechain -j DROP\n" + ":FW_allowchain -\n" + "-A FW_allowchain -m owner --uid-owner 10023 -j RETURN\n" + "-A FW_allowchain -m owner --uid-owner 10059 -j RETURN\n" + "-A FW_allowchain -m owner --uid-owner 10124 -j RETURN\n" + "-A FW_allowchain -m owner --uid-owner 10111 -j RETURN\n" + "-A FW_allowchain -m owner --uid-owner 110122 -j RETURN\n" + "-A FW_allowchain -m owner --uid-owner 210153 -j RETURN\n" + "-A FW_allowchain -m owner --uid-owner 210024 -j RETURN\n" + "-A FW_allowchain -m owner --uid-owner 0-9999 -j RETURN\n" + "-A FW_allowchain -m owner ! --uid-owner 0-4294967294 -j RETURN\n" + "-A FW_allowchain -p esp -j RETURN\n" + "-A FW_allowchain -i lo -j RETURN\n" + "-A FW_allowchain -o lo -j RETURN\n" + "-A FW_allowchain -p tcp --tcp-flags RST RST -j RETURN\n" + "-A FW_allowchain -p icmpv6 --icmpv6-type packet-too-big -j RETURN\n" + "-A FW_allowchain -p icmpv6 --icmpv6-type router-solicitation -j RETURN\n" + "-A FW_allowchain -p icmpv6 --icmpv6-type router-advertisement -j RETURN\n" + "-A FW_allowchain -p icmpv6 --icmpv6-type neighbour-solicitation -j RETURN\n" + "-A FW_allowchain -p icmpv6 --icmpv6-type neighbour-advertisement -j RETURN\n" + "-A FW_allowchain -p icmpv6 --icmpv6-type redirect -j RETURN\n" + "-A FW_allowchain -j DROP\n" "COMMIT\n"; std::vector<int32_t> uids = { 10023, 10059, 10124, 10111, 110122, 210153, 210024 }; - EXPECT_EQ(expected, makeUidRules(V6, "FW_whitechain", true, uids)); + EXPECT_EQ(expected, makeUidRules(V6, "FW_allowchain", true, uids)); } -TEST_F(FirewallControllerTest, TestReplaceBlacklistUidRule) { +TEST_F(FirewallControllerTest, TestReplaceDenylistUidRule) { std::string expected = "*filter\n" - ":FW_blackchain -\n" - "-A FW_blackchain -i lo -j RETURN\n" - "-A FW_blackchain -o lo -j RETURN\n" - "-A FW_blackchain -p tcp --tcp-flags RST RST -j RETURN\n" - "-A FW_blackchain -m owner --uid-owner 10023 -j DROP\n" - "-A FW_blackchain -m owner --uid-owner 10059 -j DROP\n" - "-A FW_blackchain -m owner --uid-owner 10124 -j DROP\n" + ":FW_denychain -\n" + "-A FW_denychain -i lo -j RETURN\n" + "-A FW_denychain -o lo -j RETURN\n" + "-A FW_denychain -p tcp --tcp-flags RST RST -j RETURN\n" + "-A FW_denychain -m owner --uid-owner 10023 -j DROP\n" + "-A FW_denychain -m owner --uid-owner 10059 -j DROP\n" + "-A FW_denychain -m owner --uid-owner 10124 -j DROP\n" "COMMIT\n"; std::vector<int32_t> uids = { 10023, 10059, 10124 }; - EXPECT_EQ(expected, makeUidRules(V4 ,"FW_blackchain", false, uids)); + EXPECT_EQ(expected, makeUidRules(V4, "FW_denychain", false, uids)); } TEST_F(FirewallControllerTest, TestEnableChildChains) { @@ -251,10 +248,10 @@ TEST_F(FirewallControllerTest, TestFirewall) { EXPECT_EQ(0, mFw.resetFirewall()); expectIptablesRestoreCommands(disableCommands); - EXPECT_EQ(0, mFw.setFirewallType(BLACKLIST)); + EXPECT_EQ(0, mFw.setFirewallType(DENYLIST)); expectIptablesRestoreCommands(disableCommands); - EXPECT_EQ(0, mFw.setFirewallType(BLACKLIST)); + EXPECT_EQ(0, mFw.setFirewallType(DENYLIST)); expectIptablesRestoreCommands(noCommands); std::vector<std::string> disableEnableCommands; @@ -263,7 +260,7 @@ TEST_F(FirewallControllerTest, TestFirewall) { disableEnableCommands.insert( disableEnableCommands.end(), enableCommands.begin(), enableCommands.end()); - EXPECT_EQ(0, mFw.setFirewallType(WHITELIST)); + EXPECT_EQ(0, mFw.setFirewallType(ALLOWLIST)); expectIptablesRestoreCommands(disableEnableCommands); std::vector<std::string> ifaceCommands = { @@ -290,15 +287,15 @@ TEST_F(FirewallControllerTest, TestFirewall) { EXPECT_EQ(0, mFw.setInterfaceRule("rmnet_data0", DENY)); expectIptablesRestoreCommands(noCommands); - EXPECT_EQ(0, mFw.setFirewallType(WHITELIST)); + EXPECT_EQ(0, mFw.setFirewallType(ALLOWLIST)); expectIptablesRestoreCommands(noCommands); EXPECT_EQ(0, mFw.resetFirewall()); expectIptablesRestoreCommands(disableCommands); - // TODO: calling resetFirewall and then setFirewallType(WHITELIST) does + // TODO: calling resetFirewall and then setFirewallType(ALLOWLIST) does // nothing. This seems like a clear bug. - EXPECT_EQ(0, mFw.setFirewallType(WHITELIST)); + EXPECT_EQ(0, mFw.setFirewallType(ALLOWLIST)); expectIptablesRestoreCommands(noCommands); } diff --git a/server/IdletimerController.cpp b/server/IdletimerController.cpp index 103e7cd6..cc86d1bd 100644 --- a/server/IdletimerController.cpp +++ b/server/IdletimerController.cpp @@ -39,8 +39,8 @@ * # For notifications to work the lable name must match the name of a valid interface. * # If the label name does match an interface, the rules will be a no-op. * - * iptables -t raw -A idletimer_PREROUTING -i rmnet0 -j IDLETIMER --timeout 5 --label test-chain --send_nl_msg 1 - * iptables -t mangle -A idletimer_POSTROUTING -o rmnet0 -j IDLETIMER --timeout 5 --label test-chain --send_nl_msg 1 + * iptables -t raw -A idletimer_PREROUTING -i rmnet0 -j IDLETIMER --timeout 5 --label test-chain --send_nl_msg + * iptables -t mangle -A idletimer_POSTROUTING -o rmnet0 -j IDLETIMER --timeout 5 --label test-chain --send_nl_msg * * iptables -nxvL -t raw * iptables -nxvL -t mangle @@ -147,14 +147,14 @@ int IdletimerController::modifyInterfaceIdletimer(IptOp op, const char *iface, const char *addRemove = (op == IptOpAdd) ? "-A" : "-D"; std::vector<std::string> cmds = { - "*raw", - StringPrintf("%s %s -i %s -j IDLETIMER --timeout %u --label %s --send_nl_msg 1", - addRemove, LOCAL_RAW_PREROUTING, iface, timeout, classLabel), - "COMMIT", - "*mangle", - StringPrintf("%s %s -o %s -j IDLETIMER --timeout %u --label %s --send_nl_msg 1", - addRemove, LOCAL_MANGLE_POSTROUTING, iface, timeout, classLabel), - "COMMIT\n", + "*raw", + StringPrintf("%s %s -i %s -j IDLETIMER --timeout %u --label %s --send_nl_msg", + addRemove, LOCAL_RAW_PREROUTING, iface, timeout, classLabel), + "COMMIT", + "*mangle", + StringPrintf("%s %s -o %s -j IDLETIMER --timeout %u --label %s --send_nl_msg", + addRemove, LOCAL_MANGLE_POSTROUTING, iface, timeout, classLabel), + "COMMIT\n", }; return (execIptablesRestore(V4V6, Join(cmds, '\n')) == 0) ? 0 : -EREMOTEIO; diff --git a/server/IdletimerControllerTest.cpp b/server/IdletimerControllerTest.cpp index 30c22987..68d6108c 100644 --- a/server/IdletimerControllerTest.cpp +++ b/server/IdletimerControllerTest.cpp @@ -43,14 +43,16 @@ TEST_F(IdletimerControllerTest, TestSetupIptablesHooks) { const std::vector<std::string> makeAddRemoveCommands(bool add) { const char *op = add ? "-A" : "-D"; std::vector<std::string> cmds = { - "*raw", - StringPrintf("%s idletimer_raw_PREROUTING -i wlan0 -j IDLETIMER" - " --timeout 12345 --label hello --send_nl_msg 1", op), - "COMMIT", - "*mangle", - StringPrintf("%s idletimer_mangle_POSTROUTING -o wlan0 -j IDLETIMER" - " --timeout 12345 --label hello --send_nl_msg 1", op), - "COMMIT\n", + "*raw", + StringPrintf("%s idletimer_raw_PREROUTING -i wlan0 -j IDLETIMER" + " --timeout 12345 --label hello --send_nl_msg", + op), + "COMMIT", + "*mangle", + StringPrintf("%s idletimer_mangle_POSTROUTING -o wlan0 -j IDLETIMER" + " --timeout 12345 --label hello --send_nl_msg", + op), + "COMMIT\n", }; return { Join(cmds, '\n') }; } diff --git a/server/IptablesRestoreControllerTest.cpp b/server/IptablesRestoreControllerTest.cpp index 20f61835..3881124a 100644 --- a/server/IptablesRestoreControllerTest.cpp +++ b/server/IptablesRestoreControllerTest.cpp @@ -40,7 +40,14 @@ #define XT_LOCK_ATTEMPTS 10 #define XT_LOCK_POLL_INTERVAL_MS 100 +#define PROC_STAT_MIN_ELEMENTS 52U +#define PROC_STAT_RSS_INDEX 23U + +#define IPTABLES_COMM "(iptables-restor)" +#define IP6TABLES_COMM "(ip6tables-resto)" + using android::base::Join; +using android::base::StringAppendF; using android::base::StringPrintf; using android::netdutils::ScopedMockSyscalls; using android::netdutils::Stopwatch; @@ -75,10 +82,29 @@ public: return con.getIpRestorePid(type); }; + const std::string getProcStatPath(pid_t pid) { return StringPrintf("/proc/%d/stat", pid); } + + std::vector<std::string> parseProcStat(int fd, const std::string& path) { + std::vector<std::string> procStat; + + char statBuf[1024]; + EXPECT_NE(-1, read(fd, statBuf, sizeof(statBuf))) + << "Could not read from " << path << ": " << strerror(errno); + + std::stringstream stream(statBuf); + std::string item; + while (std::getline(stream, item, ' ')) { + procStat.push_back(item); + } + + EXPECT_LE(PROC_STAT_MIN_ELEMENTS, procStat.size()) << "Too few elements in " << path; + return procStat; + } + void expectNoIptablesRestoreProcess(pid_t pid) { // We can't readlink /proc/PID/exe, because zombie processes don't have it. // Parse /proc/PID/stat instead. - std::string statPath = StringPrintf("/proc/%d/stat", pid); + std::string statPath = getProcStatPath(pid); int fd = open(statPath.c_str(), O_RDONLY | O_CLOEXEC); if (fd == -1) { // ENOENT means the process is gone (expected). @@ -89,14 +115,28 @@ public: // If the PID exists, it's possible (though very unlikely) that the PID was reused. Check the // binary name as well, to ensure the test isn't flaky. - char statBuf[1024]; - ASSERT_NE(-1, read(fd, statBuf, sizeof(statBuf))) - << "Could not read from " << statPath << ": " << strerror(errno); + std::vector<std::string> procStat = parseProcStat(fd, statPath); + EXPECT_FALSE(procStat[1] == IPTABLES_COMM || procStat[1] == IP6TABLES_COMM) + << "Previous iptables-restore or ip6tables-restore pid " << pid + << " still alive: " << Join(procStat, " "); + close(fd); + } + + int getRssPages(pid_t pid) { + std::string statPath = getProcStatPath(pid); + int fd = open(statPath.c_str(), O_RDONLY | O_CLOEXEC); + EXPECT_NE(-1, fd) << "Unexpected error opening " << statPath << ": " << strerror(errno); + if (fd == -1) return 0; + + const auto& procStat = parseProcStat(fd, statPath); + close(fd); - std::string statString(statBuf); - EXPECT_FALSE(statString.find("iptables-restor") || statString.find("ip6tables-resto")) - << "Previous iptables-restore pid " << pid << " still alive: " << statString; + if (procStat.size() < PROC_STAT_MIN_ELEMENTS) return 0; + EXPECT_TRUE(procStat[1] == IPTABLES_COMM || procStat[1] == IP6TABLES_COMM) + << statPath << " is for unexpected process: " << procStat[1]; + + return std::atoi(procStat[PROC_STAT_RSS_INDEX].c_str()); } int createTestChain() { @@ -217,7 +257,7 @@ TEST_F(IptablesRestoreControllerTest, TestRestartOnProcessDeath) { TEST_F(IptablesRestoreControllerTest, TestCommandTimeout) { // Don't wait 10 seconds for this test to fail. - setRetryParameters(3, 50); + setRetryParameters(3, 100); // Expected contents of the chain. std::vector<std::string> expectedLines = { @@ -299,3 +339,44 @@ TEST_F(IptablesRestoreControllerTest, TestStartup) { EXPECT_EQ(-1, con.execute(V4V6, "malformed command\n", nullptr)); EXPECT_EQ(0, con.execute(V4V6, "#Test\n", nullptr)); } + +TEST_F(IptablesRestoreControllerTest, TestMemoryLeak) { + std::string cmd = "*filter\n"; + + // Keep command within PIPE_BUF (4096) just to make sure. Each line is 60 bytes including \n: + // -I netd_unit_test_9999 -p udp -m udp --sport 12345 -j DROP + for (int i = 0; i < 33; i++) { + StringAppendF(&cmd, "-I %s -p udp -m udp --sport 12345 -j DROP\n", mChainName.c_str()); + StringAppendF(&cmd, "-D %s -p udp -m udp --sport 12345 -j DROP\n", mChainName.c_str()); + } + StringAppendF(&cmd, "COMMIT\n"); + ASSERT_GE(4096U, cmd.size()); + + // Run the command once in case it causes the first allocations for these iptables-restore + // processes, and check they don't crash. + pid_t pid4 = getIpRestorePid(IptablesRestoreController::IPTABLES_PROCESS); + pid_t pid6 = getIpRestorePid(IptablesRestoreController::IP6TABLES_PROCESS); + std::string output; + EXPECT_EQ(0, con.execute(IptablesTarget::V4V6, cmd, nullptr)); + EXPECT_EQ(pid4, getIpRestorePid(IptablesRestoreController::IPTABLES_PROCESS)); + EXPECT_EQ(pid6, getIpRestorePid(IptablesRestoreController::IP6TABLES_PROCESS)); + + // Check how much RAM the processes are using. + int pages4 = getRssPages(pid4); + ASSERT_NE(0, pages4); + int pages6 = getRssPages(pid6); + ASSERT_NE(0, pages6); + + // Run the command a few times and check that it doesn't crash. + for (int i = 0; i < 10; i++) { + EXPECT_EQ(0, con.execute(IptablesTarget::V4V6, cmd, nullptr)); + } + EXPECT_EQ(pid4, getIpRestorePid(IptablesRestoreController::IPTABLES_PROCESS)); + EXPECT_EQ(pid6, getIpRestorePid(IptablesRestoreController::IP6TABLES_PROCESS)); + + // Don't allow a leak of more than 25 pages (100kB). + // This is more than enough for accuracy: the leak in b/162925719 fails with: + // Expected: (25U) >= (getRssPages(pid4) - pages4), actual: 5 vs 66 + EXPECT_GE(25, getRssPages(pid4) - pages4) << "iptables-restore leaked too many pages"; + EXPECT_GE(25, getRssPages(pid6) - pages6) << "ip6tables-restore leaked too many pages"; +} diff --git a/server/LocalNetwork.cpp b/server/LocalNetwork.cpp index 91fda3ca..7127b3fe 100644 --- a/server/LocalNetwork.cpp +++ b/server/LocalNetwork.cpp @@ -14,11 +14,12 @@ * limitations under the License. */ +#define LOG_TAG "Netd" + #include "LocalNetwork.h" #include "RouteController.h" -#define LOG_TAG "Netd" #include "log/log.h" namespace android { @@ -30,10 +31,6 @@ LocalNetwork::LocalNetwork(unsigned netId) : Network(netId) { LocalNetwork::~LocalNetwork() { } -Network::Type LocalNetwork::getType() const { - return LOCAL; -} - int LocalNetwork::addInterface(const std::string& interface) { if (hasInterface(interface)) { return 0; diff --git a/server/LocalNetwork.h b/server/LocalNetwork.h index 7a39a9d1..c774067c 100644 --- a/server/LocalNetwork.h +++ b/server/LocalNetwork.h @@ -26,7 +26,7 @@ public: virtual ~LocalNetwork(); private: - Type getType() const override; + std::string getTypeString() const override { return "LOCAL"; }; [[nodiscard]] int addInterface(const std::string& interface) override; [[nodiscard]] int removeInterface(const std::string& interface) override; }; diff --git a/server/NdcDispatcher.cpp b/server/NdcDispatcher.cpp index 48f7d9f5..80ad7fb6 100644 --- a/server/NdcDispatcher.cpp +++ b/server/NdcDispatcher.cpp @@ -747,13 +747,13 @@ int NdcDispatcher::FirewallCmd::parseRule(const char* arg) { } int NdcDispatcher::FirewallCmd::parseFirewallType(const char* arg) { - if (!strcmp(arg, "whitelist")) { - return INetd::FIREWALL_WHITELIST; - } else if (!strcmp(arg, "blacklist")) { - return INetd::FIREWALL_BLACKLIST; + if (!strcmp(arg, "allowlist")) { + return INetd::FIREWALL_ALLOWLIST; + } else if (!strcmp(arg, "denylist")) { + return INetd::FIREWALL_DENYLIST; } else { LOG(LOGLEVEL) << "failed to parse firewall type " << arg; - return INetd::FIREWALL_BLACKLIST; + return INetd::FIREWALL_DENYLIST; } } @@ -764,6 +764,8 @@ int NdcDispatcher::FirewallCmd::parseChildChain(const char* arg) { return INetd::FIREWALL_CHAIN_STANDBY; } else if (!strcmp(arg, "powersave")) { return INetd::FIREWALL_CHAIN_POWERSAVE; + } else if (!strcmp(arg, "restricted")) { + return INetd::FIREWALL_CHAIN_RESTRICTED; } else if (!strcmp(arg, "none")) { return INetd::FIREWALL_CHAIN_NONE; } else { @@ -781,7 +783,7 @@ int NdcDispatcher::FirewallCmd::runCommand(NdcClient* cli, int argc, char** argv if (!strcmp(argv[1], "enable")) { if (argc != 3) { cli->sendMsg(ResponseCode::CommandSyntaxError, - "Usage: firewall enable <whitelist|blacklist>", false); + "Usage: firewall enable <allowlist|denylist>", false); return 0; } int res = !mNetd->firewallSetFirewallType(parseFirewallType(argv[2])).isOk(); @@ -1047,9 +1049,12 @@ int NdcDispatcher::NetworkCommand::runCommand(NdcClient* cli, int argc, char** a return syntaxError(cli, "Missing argument"); } unsigned netId = stringToNetId(argv[2]); - if (argc == 6 && !strcmp(argv[3], "vpn")) { + if (argc == 5 && !strcmp(argv[3], "vpn")) { bool secure = strtol(argv[4], nullptr, 2); +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" if (Status status = mNetd->networkCreateVpn(netId, secure); !status.isOk()) { +#pragma clang diagnostic pop return operationError(cli, "createVirtualNetwork() failed", status.serviceSpecificErrorCode()); } @@ -1063,7 +1068,10 @@ int NdcDispatcher::NetworkCommand::runCommand(NdcClient* cli, int argc, char** a return syntaxError(cli, "Unknown permission"); } } +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" if (Status status = mNetd->networkCreatePhysical(netId, permission); !status.isOk()) { +#pragma clang diagnostic pop return operationError(cli, "createPhysicalNetwork() failed", status.serviceSpecificErrorCode()); } diff --git a/server/NetdConstants.cpp b/server/NetdConstants.cpp index 80282b35..f3898f59 100644 --- a/server/NetdConstants.cpp +++ b/server/NetdConstants.cpp @@ -66,7 +66,8 @@ bool isIfaceName(const std::string& name) { } for (i = 1; i < name.size(); i++) { - if (!isalnum(name[i]) && (name[i] != '_') && (name[i] != '-') && (name[i] != ':')) { + if (!isalnum(name[i]) && (name[i] != '_') && (name[i] != '-') + && (name[i] != ':') && (name[i] != '.')) { return false; } } diff --git a/server/NetdNativeService.cpp b/server/NetdNativeService.cpp index 3bf879bf..1f5dc976 100644 --- a/server/NetdNativeService.cpp +++ b/server/NetdNativeService.cpp @@ -52,9 +52,11 @@ using android::base::StringPrintf; using android::base::WriteStringToFile; +using android::net::NativeNetworkType; using android::net::TetherOffloadRuleParcel; using android::net::TetherStatsParcel; using android::net::UidRangeParcel; +using android::net::netd::aidl::NativeUidRangeConfig; using android::netdutils::DumpWriter; using android::netdutils::ScopedIndent; using android::os::ParcelFileDescriptor; @@ -170,8 +172,9 @@ bool contains(const Vector<String16>& words, const String16& word) { NetdNativeService::NetdNativeService() { // register log callback to BnNetd::logFunc - BnNetd::logFunc = std::bind(binderCallLogFn, std::placeholders::_1, - [](const std::string& msg) { gLog.info("%s", msg.c_str()); }); + BnNetd::logFunc = [](const auto& log) { + binderCallLogFn(log, [](const std::string& msg) { gLog.info("%s", msg.c_str()); }); + }; } status_t NetdNativeService::start() { @@ -268,9 +271,11 @@ binder::Status NetdNativeService::isAlive(bool *alive) { } binder::Status NetdNativeService::firewallReplaceUidChain(const std::string& chainName, - bool isWhitelist, const std::vector<int32_t>& uids, bool *ret) { + bool isAllowlist, + const std::vector<int32_t>& uids, + bool* ret) { NETD_LOCKING_RPC(gCtls->firewallCtrl.lock, PERM_NETWORK_STACK, PERM_MAINLINE_NETWORK_STACK); - int err = gCtls->firewallCtrl.replaceUidChain(chainName, isWhitelist, uids); + int err = gCtls->firewallCtrl.replaceUidChain(chainName, isAllowlist, uids); *ret = (err == 0); return binder::Status::ok(); } @@ -316,41 +321,60 @@ binder::Status NetdNativeService::bandwidthSetGlobalAlert(int64_t bytes) { binder::Status NetdNativeService::bandwidthAddNaughtyApp(int32_t uid) { NETD_LOCKING_RPC(gCtls->bandwidthCtrl.lock, PERM_NETWORK_STACK, PERM_MAINLINE_NETWORK_STACK); - std::vector<std::string> appStrUids = {std::to_string(abs(uid))}; - int res = gCtls->bandwidthCtrl.addNaughtyApps(appStrUids); + std::vector<uint32_t> appUids = {static_cast<uint32_t>(abs(uid))}; + int res = gCtls->bandwidthCtrl.addNaughtyApps(appUids); return statusFromErrcode(res); } binder::Status NetdNativeService::bandwidthRemoveNaughtyApp(int32_t uid) { NETD_LOCKING_RPC(gCtls->bandwidthCtrl.lock, PERM_NETWORK_STACK, PERM_MAINLINE_NETWORK_STACK); - std::vector<std::string> appStrUids = {std::to_string(abs(uid))}; - int res = gCtls->bandwidthCtrl.removeNaughtyApps(appStrUids); + std::vector<uint32_t> appUids = {static_cast<uint32_t>(abs(uid))}; + int res = gCtls->bandwidthCtrl.removeNaughtyApps(appUids); return statusFromErrcode(res); } binder::Status NetdNativeService::bandwidthAddNiceApp(int32_t uid) { NETD_LOCKING_RPC(gCtls->bandwidthCtrl.lock, PERM_NETWORK_STACK, PERM_MAINLINE_NETWORK_STACK); - std::vector<std::string> appStrUids = {std::to_string(abs(uid))}; - int res = gCtls->bandwidthCtrl.addNiceApps(appStrUids); + std::vector<uint32_t> appUids = {static_cast<uint32_t>(abs(uid))}; + int res = gCtls->bandwidthCtrl.addNiceApps(appUids); return statusFromErrcode(res); } binder::Status NetdNativeService::bandwidthRemoveNiceApp(int32_t uid) { NETD_LOCKING_RPC(gCtls->bandwidthCtrl.lock, PERM_NETWORK_STACK, PERM_MAINLINE_NETWORK_STACK); - std::vector<std::string> appStrUids = {std::to_string(abs(uid))}; - int res = gCtls->bandwidthCtrl.removeNiceApps(appStrUids); + std::vector<uint32_t> appUids = {static_cast<uint32_t>(abs(uid))}; + int res = gCtls->bandwidthCtrl.removeNiceApps(appUids); return statusFromErrcode(res); } +// TODO: Remove this function when there are no users. Currently, it is still used by DNS resolver +// tests. binder::Status NetdNativeService::networkCreatePhysical(int32_t netId, int32_t permission) { ENFORCE_NETWORK_STACK_PERMISSIONS(); int ret = gCtls->netCtrl.createPhysicalNetwork(netId, convertPermission(permission)); return statusFromErrcode(ret); } +// TODO: Remove this function when there are no users. Currently, it is still used by DNS resolver +// tests. binder::Status NetdNativeService::networkCreateVpn(int32_t netId, bool secure) { ENFORCE_NETWORK_STACK_PERMISSIONS(); - int ret = gCtls->netCtrl.createVirtualNetwork(netId, secure); + // The value of vpnType does not matter here, because it is not used in AOSP and is only + // implemented by OEMs. Also, the RPC is going to deprecate. Just pick a value defined in INetd + // as default. + int ret = gCtls->netCtrl.createVirtualNetwork(netId, secure, NativeVpnType::LEGACY); + return statusFromErrcode(ret); +} + +binder::Status NetdNativeService::networkCreate(const NativeNetworkConfig& config) { + ENFORCE_NETWORK_STACK_PERMISSIONS(); + int ret = -EINVAL; + if (config.networkType == NativeNetworkType::PHYSICAL) { + ret = gCtls->netCtrl.createPhysicalNetwork(config.netId, + convertPermission(config.permission)); + } else if (config.networkType == NativeNetworkType::VIRTUAL) { + ret = gCtls->netCtrl.createVirtualNetwork(config.netId, config.secure, config.vpnType); + } return statusFromErrcode(ret); } @@ -377,7 +401,8 @@ binder::Status NetdNativeService::networkAddUidRanges( int32_t netId, const std::vector<UidRangeParcel>& uidRangeArray) { // NetworkController::addUsersToNetwork is thread-safe. ENFORCE_NETWORK_STACK_PERMISSIONS(); - int ret = gCtls->netCtrl.addUsersToNetwork(netId, UidRanges(uidRangeArray)); + int ret = gCtls->netCtrl.addUsersToNetwork(netId, UidRanges(uidRangeArray), + UidRanges::DEFAULT_SUB_PRIORITY); return statusFromErrcode(ret); } @@ -385,7 +410,22 @@ binder::Status NetdNativeService::networkRemoveUidRanges( int32_t netId, const std::vector<UidRangeParcel>& uidRangeArray) { // NetworkController::removeUsersFromNetwork is thread-safe. ENFORCE_NETWORK_STACK_PERMISSIONS(); - int ret = gCtls->netCtrl.removeUsersFromNetwork(netId, UidRanges(uidRangeArray)); + int ret = gCtls->netCtrl.removeUsersFromNetwork(netId, UidRanges(uidRangeArray), + UidRanges::DEFAULT_SUB_PRIORITY); + return statusFromErrcode(ret); +} + +binder::Status NetdNativeService::networkAddUidRangesParcel(const NativeUidRangeConfig& config) { + ENFORCE_NETWORK_STACK_PERMISSIONS(); + int ret = gCtls->netCtrl.addUsersToNetwork(config.netId, UidRanges(config.uidRanges), + config.subPriority); + return statusFromErrcode(ret); +} + +binder::Status NetdNativeService::networkRemoveUidRangesParcel(const NativeUidRangeConfig& config) { + ENFORCE_NETWORK_STACK_PERMISSIONS(); + int ret = gCtls->netCtrl.removeUsersFromNetwork(config.netId, UidRanges(config.uidRanges), + config.subPriority); return statusFromErrcode(ret); } @@ -1120,7 +1160,7 @@ binder::Status NetdNativeService::networkClearPermissionForUser(const std::vecto return binder::Status::ok(); } -binder::Status NetdNativeService::NetdNativeService::networkSetProtectAllow(int32_t uid) { +binder::Status NetdNativeService::networkSetProtectAllow(int32_t uid) { ENFORCE_NETWORK_STACK_PERMISSIONS(); std::vector<uid_t> uids = {(uid_t) uid}; gCtls->netCtrl.allowProtect(uids); @@ -1251,65 +1291,43 @@ binder::Status NetdNativeService::getFwmarkForNetwork(int32_t netId, MarkMaskPar return binder::Status::ok(); } -binder::Status NetdNativeService::tetherOffloadRuleAdd(const TetherOffloadRuleParcel& rule) { +// TODO: remark @deprecated in INetd.aidl. +binder::Status NetdNativeService::tetherOffloadRuleAdd(const TetherOffloadRuleParcel& /* rule */) { + // deprecated ENFORCE_NETWORK_STACK_PERMISSIONS(); - - return asBinderStatus(gCtls->tetherCtrl.addOffloadRule(rule)); + return binder::Status::fromExceptionCode(binder::Status::EX_UNSUPPORTED_OPERATION); } -binder::Status NetdNativeService::tetherOffloadRuleRemove(const TetherOffloadRuleParcel& rule) { +// TODO: remark @deprecated in INetd.aidl. +binder::Status NetdNativeService::tetherOffloadRuleRemove( + const TetherOffloadRuleParcel& /* rule */) { + // deprecated ENFORCE_NETWORK_STACK_PERMISSIONS(); - - return asBinderStatus(gCtls->tetherCtrl.removeOffloadRule(rule)); -} - -namespace { - -constexpr const char UNUSED_IFNAME[] = ""; - -TetherStatsParcel toTetherStatsParcel(const TetherController::TetherOffloadStats& stats) { - TetherStatsParcel result; - result.iface = UNUSED_IFNAME; - result.rxBytes = stats.rxBytes; - result.rxPackets = stats.rxPackets; - result.txBytes = stats.txBytes; - result.txPackets = stats.txPackets; - result.ifIndex = stats.ifIndex; - return result; + return binder::Status::fromExceptionCode(binder::Status::EX_UNSUPPORTED_OPERATION); } -} // namespace - +// TODO: remark @deprecated in INetd.aidl. binder::Status NetdNativeService::tetherOffloadGetStats( - std::vector<TetherStatsParcel>* tetherStatsParcelVec) { + std::vector<TetherStatsParcel>* /* tetherStatsParcelVec */) { + // deprecated NETD_LOCKING_RPC(gCtls->tetherCtrl.lock, PERM_NETWORK_STACK, PERM_MAINLINE_NETWORK_STACK); - - tetherStatsParcelVec->clear(); - const auto& statsList = gCtls->tetherCtrl.getTetherOffloadStats(); - if (!isOk(statsList)) { - return asBinderStatus(statsList); - } - for (const auto& stats : statsList.value()) { - tetherStatsParcelVec->push_back(toTetherStatsParcel(stats)); - } - return binder::Status::ok(); + return binder::Status::fromExceptionCode(binder::Status::EX_UNSUPPORTED_OPERATION); } -binder::Status NetdNativeService::tetherOffloadSetInterfaceQuota(int ifIndex, int64_t quotaBytes) { +// TODO: remark @deprecated in INetd.aidl. +binder::Status NetdNativeService::tetherOffloadSetInterfaceQuota(int /* ifIndex */, + int64_t /* quotaBytes */) { + // deprecated NETD_LOCKING_RPC(gCtls->tetherCtrl.lock, PERM_NETWORK_STACK, PERM_MAINLINE_NETWORK_STACK); - int res = gCtls->tetherCtrl.setTetherOffloadInterfaceQuota(ifIndex, quotaBytes); - return statusFromErrcode(res); + return binder::Status::fromExceptionCode(binder::Status::EX_UNSUPPORTED_OPERATION); } +// TODO: remark @deprecated in INetd.aidl. binder::Status NetdNativeService::tetherOffloadGetAndClearStats( - int ifIndex, android::net::TetherStatsParcel* tetherStats) { + int /* ifIndex */, android::net::TetherStatsParcel* /* tetherStats */) { + // deprecated NETD_LOCKING_RPC(gCtls->tetherCtrl.lock, PERM_NETWORK_STACK, PERM_MAINLINE_NETWORK_STACK); - const auto& stats = gCtls->tetherCtrl.getAndClearTetherOffloadStats(ifIndex); - if (!stats.ok()) { - return asBinderStatus(stats); - } - *tetherStats = toTetherStatsParcel(stats.value()); - return binder::Status::ok(); + return binder::Status::fromExceptionCode(binder::Status::EX_UNSUPPORTED_OPERATION); } } // namespace net diff --git a/server/NetdNativeService.h b/server/NetdNativeService.h index 7b7f9b33..9779f368 100644 --- a/server/NetdNativeService.h +++ b/server/NetdNativeService.h @@ -37,9 +37,8 @@ class NetdNativeService : public BinderService<NetdNativeService>, public BnNetd binder::Status isAlive(bool *alive) override; // Firewall commands. - binder::Status firewallReplaceUidChain( - const std::string& chainName, bool isWhitelist, - const std::vector<int32_t>& uids, bool *ret) override; + binder::Status firewallReplaceUidChain(const std::string& chainName, bool isAllowlist, + const std::vector<int32_t>& uids, bool* ret) override; binder::Status firewallSetFirewallType(int32_t firewallType) override; binder::Status firewallSetInterfaceRule(const std::string& ifName, int32_t firewallRule) override; @@ -65,6 +64,7 @@ class NetdNativeService : public BinderService<NetdNativeService>, public BnNetd // Network and routing commands. binder::Status networkCreatePhysical(int32_t netId, int32_t permission) override; binder::Status networkCreateVpn(int32_t netId, bool secure) override; + binder::Status networkCreate(const NativeNetworkConfig& config) override; binder::Status networkDestroy(int32_t netId) override; binder::Status networkAddInterface(int32_t netId, const std::string& iface) override; @@ -74,6 +74,10 @@ class NetdNativeService : public BinderService<NetdNativeService>, public BnNetd const std::vector<UidRangeParcel>& uids) override; binder::Status networkRemoveUidRanges(int32_t netId, const std::vector<UidRangeParcel>& uids) override; + binder::Status networkAddUidRangesParcel( + const netd::aidl::NativeUidRangeConfig& uidRangesConfig) override; + binder::Status networkRemoveUidRangesParcel( + const netd::aidl::NativeUidRangeConfig& uidRangesConfig) override; binder::Status networkRejectNonSecureVpn(bool enable, const std::vector<UidRangeParcel>& uids) override; binder::Status networkAddRouteParcel(int32_t netId, const RouteInfoParcel& route) override; diff --git a/server/NetlinkHandler.cpp b/server/NetlinkHandler.cpp index 7fb34379..525bb2d6 100644 --- a/server/NetlinkHandler.cpp +++ b/server/NetlinkHandler.cpp @@ -155,7 +155,11 @@ void NetlinkHandler::onEvent(NetlinkEvent *evt) { if (shouldDestroy) { SockDiag sd; if (sd.open()) { - int ret = sd.destroySockets(addrstr); + // Pass the interface index iff. destroying sockets on a link-local address. + // This cannot use an interface name as the interface might no longer exist. + int destroyIfaceIndex = + std::string_view(addrstr).starts_with("fe80:") ? ifaceIndex : 0; + int ret = sd.destroySockets(addrstr, destroyIfaceIndex); if (ret < 0) { ALOGE("Error destroying sockets: %s", strerror(-ret)); } diff --git a/server/Network.cpp b/server/Network.cpp index eb2a233a..72a15454 100644 --- a/server/Network.cpp +++ b/server/Network.cpp @@ -13,10 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#define LOG_TAG "Netd" #include "Network.h" -#define LOG_TAG "Netd" +#include "RouteController.h" +#include "SockDiag.h" #include "log/log.h" #include <android-base/strings.h> @@ -59,25 +61,7 @@ std::string Network::toString() const { const char kSeparator[] = " "; std::stringstream repr; - repr << mNetId; - - repr << kSeparator; - switch (getType()) { - case DUMMY: - repr << "DUMMY"; - break; - case LOCAL: - repr << "LOCAL"; - break; - case PHYSICAL: - repr << "PHYSICAL"; - break; - case VIRTUAL: - repr << "VIRTUAL"; - break; - default: - repr << "unknown"; - } + repr << mNetId << kSeparator << getTypeString(); if (mInterfaces.size() > 0) { repr << kSeparator << android::base::Join(mInterfaces, ","); @@ -86,9 +70,73 @@ std::string Network::toString() const { return repr.str(); } +std::string Network::uidRangesToString() const { + if (mUidRangeMap.empty()) { + return ""; + } + + std::ostringstream result; + for (auto it = mUidRangeMap.begin(); it != mUidRangeMap.end(); ++it) { + result << "prio " << it->first << " " << it->second.toString(); + if (std::next(it) != mUidRangeMap.end()) result << "; "; + } + return result.str(); +} + +// Check if the user has been added to this network. If yes, the highest priority of matching +// setting is returned by subPriority. Thus caller can make choice among several matching +// networks. +bool Network::appliesToUser(uid_t uid, uint32_t* subPriority) const { + for (const auto& [priority, uidRanges] : mUidRangeMap) { + if (uidRanges.hasUid(uid)) { + *subPriority = priority; + return true; + } + } + return false; +} + +void Network::addToUidRangeMap(const UidRanges& uidRanges, uint32_t subPriority) { + auto iter = mUidRangeMap.find(subPriority); + if (iter != mUidRangeMap.end()) { + iter->second.add(uidRanges); + } else { + mUidRangeMap[subPriority] = uidRanges; + } +} + +void Network::removeFromUidRangeMap(const UidRanges& uidRanges, uint32_t subPriority) { + auto iter = mUidRangeMap.find(subPriority); + if (iter != mUidRangeMap.end()) { + iter->second.remove(uidRanges); + if (iter->second.empty()) { + mUidRangeMap.erase(subPriority); + } + } else { + ALOGW("uidRanges with priority %u not found", subPriority); + } +} -Network::Network(unsigned netId) : mNetId(netId) { +bool Network::canAddUidRanges(const UidRanges& uidRanges, uint32_t subPriority) const { + if (uidRanges.overlapsSelf()) { + ALOGE("uid range %s overlaps self", uidRanges.toString().c_str()); + return false; + } + + auto iter = mUidRangeMap.find(subPriority); + if (iter != mUidRangeMap.end() && uidRanges.overlaps(iter->second)) { + ALOGE("uid range %s overlaps priority %u %s", uidRanges.toString().c_str(), subPriority, + iter->second.toString().c_str()); + return false; + } + return true; } +bool Network::isSecure() const { + return mSecure; +} + +Network::Network(unsigned netId, bool secure) : mNetId(netId), mSecure(secure) {} + } // namespace net } // namespace android diff --git a/server/Network.h b/server/Network.h index 8417f340..aa1b21a1 100644 --- a/server/Network.h +++ b/server/Network.h @@ -17,45 +17,67 @@ #pragma once #include "NetdConstants.h" +#include "UidRanges.h" #include <set> #include <string> namespace android::net { +typedef std::map<uint32_t, UidRanges> UidRangeMap; + // A Network represents a collection of interfaces participating as a single administrative unit. class Network { public: - enum Type { - DUMMY, - LOCAL, - PHYSICAL, - VIRTUAL, - }; - // You MUST ensure that no interfaces are still assigned to this network, say by calling // clearInterfaces(), before deleting it. This is because interface removal may fail. If we // automatically removed interfaces in the destructor, you wouldn't know if it failed. virtual ~Network(); - virtual Type getType() const = 0; + virtual std::string getTypeString() const = 0; unsigned getNetId() const; bool hasInterface(const std::string& interface) const; const std::set<std::string>& getInterfaces() const; // These return 0 on success or negative errno on failure. - [[nodiscard]] virtual int addInterface(const std::string& interface) = 0; - [[nodiscard]] virtual int removeInterface(const std::string& interface) = 0; + [[nodiscard]] virtual int addInterface(const std::string&) { return -EINVAL; } + [[nodiscard]] virtual int removeInterface(const std::string&) { return -EINVAL; } [[nodiscard]] int clearInterfaces(); std::string toString() const; + std::string uidRangesToString() const; + bool appliesToUser(uid_t uid, uint32_t* subPriority) const; + [[nodiscard]] virtual int addUsers(const UidRanges&, uint32_t /*subPriority*/) { + return -EINVAL; + }; + [[nodiscard]] virtual int removeUsers(const UidRanges&, uint32_t /*subPriority*/) { + return -EINVAL; + }; + bool isSecure() const; + virtual bool isPhysical() { return false; } + virtual bool isUnreachable() { return false; } + virtual bool isVirtual() { return false; } + virtual bool canAddUsers() { return false; } + virtual bool isValidSubPriority(uint32_t /*priority*/) { return false; } + virtual void addToUidRangeMap(const UidRanges& uidRanges, uint32_t subPriority); + virtual void removeFromUidRangeMap(const UidRanges& uidRanges, uint32_t subPriority); protected: - explicit Network(unsigned netId); + explicit Network(unsigned netId, bool mSecure = false); + bool canAddUidRanges(const UidRanges& uidRanges, uint32_t subPriority) const; const unsigned mNetId; std::set<std::string> mInterfaces; + // Each subsidiary priority maps to a set of UID ranges of a feature. + std::map<uint32_t, UidRanges> mUidRangeMap; + const bool mSecure; + +private: + enum Action { + REMOVE, + ADD, + }; }; } // namespace android::net diff --git a/server/NetworkController.cpp b/server/NetworkController.cpp index 20ae44b2..602639cb 100644 --- a/server/NetworkController.cpp +++ b/server/NetworkController.cpp @@ -39,6 +39,7 @@ #include "OffloadUtils.h" #include "PhysicalNetwork.h" #include "RouteController.h" +#include "UnreachableNetwork.h" #include "VirtualNetwork.h" #include "netdutils/DumpWriter.h" #include "netid_client.h" @@ -128,7 +129,7 @@ int NetworkController::DelegateImpl::removeFallthrough(const std::string& physic int NetworkController::DelegateImpl::modifyFallthrough(const std::string& physicalInterface, Permission permission, bool add) { for (const auto& entry : mNetworkController->mNetworks) { - if (entry.second->getType() == Network::VIRTUAL) { + if (entry.second->isVirtual()) { if (int ret = modifyFallthrough(entry.first, physicalInterface, permission, add)) { return ret; } @@ -140,24 +141,25 @@ int NetworkController::DelegateImpl::modifyFallthrough(const std::string& physic NetworkController::NetworkController() : mDelegateImpl(new NetworkController::DelegateImpl(this)), mDefaultNetId(NETID_UNSET), mProtectableUsers({AID_VPN}) { + gLog.info("enter NetworkController ctor"); mNetworks[LOCAL_NET_ID] = new LocalNetwork(LOCAL_NET_ID); mNetworks[DUMMY_NET_ID] = new DummyNetwork(DUMMY_NET_ID); + mNetworks[UNREACHABLE_NET_ID] = new UnreachableNetwork(UNREACHABLE_NET_ID); // Clear all clsact stubs on all interfaces. // TODO: perhaps only remove the clsact on the interface which is added by // RouteController::addInterfaceToPhysicalNetwork. Currently, the netd only // attach the clsact to the interface for the physical network. - if (bpf::isBpfSupported()) { - const auto& ifaces = InterfaceController::getIfaceNames(); - if (isOk(ifaces)) { - for (const std::string& iface : ifaces.value()) { - if (int ifIndex = if_nametoindex(iface.c_str())) { - // Ignore the error because the interface might not have a clsact. - tcQdiscDelDevClsact(ifIndex); - } + const auto& ifaces = InterfaceController::getIfaceNames(); + if (isOk(ifaces)) { + for (const std::string& iface : ifaces.value()) { + if (int ifIndex = if_nametoindex(iface.c_str())) { + // Ignore the error because the interface might not have a clsact. + tcQdiscDelDevClsact(ifIndex); } } } + gLog.info("leave NetworkController ctor"); } unsigned NetworkController::getDefaultNetwork() const { @@ -178,7 +180,7 @@ int NetworkController::setDefaultNetwork(unsigned netId) { ALOGE("no such netId %u", netId); return -ENONET; } - if (network->getType() != Network::PHYSICAL) { + if (!network->isPhysical()) { ALOGE("cannot set default to non-physical network with netId %u", netId); return -EINVAL; } @@ -189,7 +191,7 @@ int NetworkController::setDefaultNetwork(unsigned netId) { if (mDefaultNetId != NETID_UNSET) { Network* network = getNetworkLocked(mDefaultNetId); - if (!network || network->getType() != Network::PHYSICAL) { + if (!network || !network->isPhysical()) { ALOGE("cannot find previously set default network with netId %u", mDefaultNetId); return -ESRCH; } @@ -207,13 +209,16 @@ uint32_t NetworkController::getNetworkForDnsLocked(unsigned* netId, uid_t uid) c fwmark.protectedFromVpn = true; fwmark.permission = PERMISSION_SYSTEM; + Network* appDefaultNetwork = getPhysicalOrUnreachableNetworkForUserLocked(uid); + unsigned defaultNetId = appDefaultNetwork ? appDefaultNetwork->getNetId() : mDefaultNetId; + // Common case: there is no VPN that applies to the user, and the query did not specify a netId. // Therefore, it is safe to set the explicit bit on this query and skip all the complex logic // below. While this looks like a special case, it is actually the one that handles the vast // majority of DNS queries. // TODO: untangle this code. if (*netId == NETID_UNSET && getVirtualNetworkForUserLocked(uid) == nullptr) { - *netId = mDefaultNetId; + *netId = defaultNetId; fwmark.netId = *netId; fwmark.explicitlySelected = true; return fwmark.intValue; @@ -229,8 +234,8 @@ uint32_t NetworkController::getNetworkForDnsLocked(unsigned* netId, uid_t uid) c // servers (through the default network). Otherwise, the query is guaranteed to fail. // http://b/29498052 Network *network = getNetworkLocked(*netId); - if (network && network->getType() == Network::VIRTUAL && !resolv_has_nameservers(*netId)) { - *netId = mDefaultNetId; + if (network && network->isVirtual() && !resolv_has_nameservers(*netId)) { + *netId = defaultNetId; } } else { // If the user is subject to a VPN and the VPN provides DNS servers, use those servers @@ -243,7 +248,7 @@ uint32_t NetworkController::getNetworkForDnsLocked(unsigned* netId, uid_t uid) c } else { // TODO: return an error instead of silently doing the DNS lookup on the wrong network. // http://b/27560555 - *netId = mDefaultNetId; + *netId = defaultNetId; } } fwmark.netId = *netId; @@ -251,17 +256,22 @@ uint32_t NetworkController::getNetworkForDnsLocked(unsigned* netId, uid_t uid) c } // Returns the NetId that a given UID would use if no network is explicitly selected. Specifically, -// the VPN that applies to the UID if any; otherwise, the default network. +// the VPN that applies to the UID if any; Otherwise, the default network for UID; Otherwise the +// unreachable network that applies to the UID; lastly, the default network. unsigned NetworkController::getNetworkForUser(uid_t uid) const { ScopedRLock lock(mRWLock); if (VirtualNetwork* virtualNetwork = getVirtualNetworkForUserLocked(uid)) { return virtualNetwork->getNetId(); } + if (Network* network = getPhysicalOrUnreachableNetworkForUserLocked(uid)) { + return network->getNetId(); + } return mDefaultNetId; } // Returns the NetId that will be set when a socket connect()s. This is the bypassable VPN that -// applies to the user if any; otherwise, the default network. +// applies to the user if any; otherwise, the default network that applies to user if any; lastly, +// the default network. // // In general, we prefer to always set the default network's NetId in connect(), so that if the VPN // is a split-tunnel and disappears later, the socket continues working (since the default network's @@ -274,11 +284,21 @@ unsigned NetworkController::getNetworkForUser(uid_t uid) const { // traffic to the default network. But it does mean that if the bypassable VPN goes away (and thus // the fallthrough rules also go away), the socket that used to fallthrough to the default network // will stop working. +// +// Per-app physical default networks behave the same as bypassable VPNs: when a socket is connected +// on one of these networks, we mark the socket with the netId of the network. This ensures that if +// the per-app default network changes, sockets established on the previous network are still +// routed to that network, assuming the network's UID ranges still apply to the UID. While this +// means that fallthrough to the default network does not work, physical networks not expected +// ever to be split tunnels. unsigned NetworkController::getNetworkForConnectLocked(uid_t uid) const { VirtualNetwork* virtualNetwork = getVirtualNetworkForUserLocked(uid); if (virtualNetwork && !virtualNetwork->isSecure()) { return virtualNetwork->getNetId(); } + if (Network* network = getPhysicalOrUnreachableNetworkForUserLocked(uid)) { + return network->getNetId(); + } return mDefaultNetId; } @@ -357,7 +377,7 @@ bool NetworkController::isVirtualNetwork(unsigned netId) const { bool NetworkController::isVirtualNetworkLocked(unsigned netId) const { Network* network = getNetworkLocked(netId); - return network && network->getType() == Network::VIRTUAL; + return network && network->isVirtual(); } int NetworkController::createPhysicalNetworkLocked(unsigned netId, Permission permission) { @@ -417,7 +437,7 @@ int NetworkController::createPhysicalOemNetwork(Permission permission, unsigned return ret; } -int NetworkController::createVirtualNetwork(unsigned netId, bool secure) { +int NetworkController::createVirtualNetwork(unsigned netId, bool secure, NativeVpnType vpnType) { ScopedWLock lock(mRWLock); if (!(MIN_NET_ID <= netId && netId <= MAX_NET_ID)) { @@ -430,6 +450,11 @@ int NetworkController::createVirtualNetwork(unsigned netId, bool secure) { return -EEXIST; } + if (vpnType < NativeVpnType::SERVICE || NativeVpnType::OEM < vpnType) { + ALOGE("invalid vpnType %d", static_cast<int>(vpnType)); + return -EINVAL; + } + if (int ret = modifyFallthroughLocked(netId, true)) { return ret; } @@ -440,8 +465,8 @@ int NetworkController::createVirtualNetwork(unsigned netId, bool secure) { int NetworkController::destroyNetwork(unsigned netId) { ScopedWLock lock(mRWLock); - if (netId == LOCAL_NET_ID) { - ALOGE("cannot destroy local network"); + if (netId == LOCAL_NET_ID || netId == UNREACHABLE_NET_ID) { + ALOGE("cannot destroy local or unreachable network"); return -EINVAL; } if (!isValidNetworkLocked(netId)) { @@ -466,7 +491,7 @@ int NetworkController::destroyNetwork(unsigned netId) { } } mDefaultNetId = NETID_UNSET; - } else if (network->getType() == Network::VIRTUAL) { + } else if (network->isVirtual()) { if (int err = modifyFallthroughLocked(netId, false)) { if (!ret) { ret = err; @@ -562,7 +587,7 @@ int NetworkController::setPermissionForNetworks(Permission permission, ALOGE("no such netId %u", netId); return -ENONET; } - if (network->getType() != Network::PHYSICAL) { + if (!network->isPhysical()) { ALOGE("cannot set permissions on non-physical network with netId %u", netId); return -EINVAL; } @@ -574,39 +599,41 @@ int NetworkController::setPermissionForNetworks(Permission permission, return 0; } -int NetworkController::addUsersToNetwork(unsigned netId, const UidRanges& uidRanges) { - ScopedWLock lock(mRWLock); - Network* network = getNetworkLocked(netId); +namespace { + +int isWrongNetworkForUidRanges(unsigned netId, Network* network) { if (!network) { ALOGE("no such netId %u", netId); return -ENONET; } - if (network->getType() != Network::VIRTUAL) { - ALOGE("cannot add users to non-virtual network with netId %u", netId); + if (!network->canAddUsers()) { + ALOGE("cannot add/remove users to/from %s network %u", network->getTypeString().c_str(), + netId); return -EINVAL; } - if (int ret = static_cast<VirtualNetwork*>(network)->addUsers(uidRanges, mProtectableUsers)) { - return ret; - } return 0; } -int NetworkController::removeUsersFromNetwork(unsigned netId, const UidRanges& uidRanges) { +} // namespace + +int NetworkController::addUsersToNetwork(unsigned netId, const UidRanges& uidRanges, + uint32_t subPriority) { ScopedWLock lock(mRWLock); Network* network = getNetworkLocked(netId); - if (!network) { - ALOGE("no such netId %u", netId); - return -ENONET; - } - if (network->getType() != Network::VIRTUAL) { - ALOGE("cannot remove users from non-virtual network with netId %u", netId); - return -EINVAL; + if (int ret = isWrongNetworkForUidRanges(netId, network)) { + return ret; } - if (int ret = static_cast<VirtualNetwork*>(network)->removeUsers(uidRanges, - mProtectableUsers)) { + return network->addUsers(uidRanges, subPriority); +} + +int NetworkController::removeUsersFromNetwork(unsigned netId, const UidRanges& uidRanges, + uint32_t subPriority) { + ScopedWLock lock(mRWLock); + Network* network = getNetworkLocked(netId); + if (int ret = isWrongNetworkForUidRanges(netId, network)) { return ret; } - return 0; + return network->removeUsers(uidRanges, subPriority); } int NetworkController::addRoute(unsigned netId, const char* interface, const char* destination, @@ -709,12 +736,17 @@ void NetworkController::dump(DumpWriter& dw) { for (const auto& i : mNetworks) { Network* network = i.second; dw.println(network->toString()); - if (network->getType() == Network::PHYSICAL) { + if (network->isPhysical()) { dw.incIndent(); Permission permission = reinterpret_cast<PhysicalNetwork*>(network)->getPermission(); dw.println("Required permission: %s", permissionToName(permission)); dw.decIndent(); } + if (const auto& str = network->uidRangesToString(); !str.empty()) { + dw.incIndent(); + dw.println(str); + dw.decIndent(); + } dw.blankline(); } dw.decIndent(); @@ -751,17 +783,34 @@ Network* NetworkController::getNetworkLocked(unsigned netId) const { } VirtualNetwork* NetworkController::getVirtualNetworkForUserLocked(uid_t uid) const { - for (const auto& entry : mNetworks) { - if (entry.second->getType() == Network::VIRTUAL) { - VirtualNetwork* virtualNetwork = static_cast<VirtualNetwork*>(entry.second); - if (virtualNetwork->appliesToUser(uid)) { - return virtualNetwork; - } + uint32_t subPriority; + for (const auto& [_, network] : mNetworks) { + if (network->isVirtual() && network->appliesToUser(uid, &subPriority)) { + return static_cast<VirtualNetwork*>(network); } } return nullptr; } +// Returns a network with the highest subsidiary priority among physical and unreachable networks +// that applies to uid. For a single subsidiary priority, an uid should belong to only one network. +// If the uid apply to different network with the same priority at the same time, the behavior is +// undefined. That is a configuration error. +Network* NetworkController::getPhysicalOrUnreachableNetworkForUserLocked(uid_t uid) const { + Network* bestNetwork = nullptr; + unsigned bestSubPriority = UidRanges::LOWEST_SUB_PRIORITY + 1; + for (const auto& [netId, network] : mNetworks) { + uint32_t subPriority; + if (!network->isPhysical() && !network->isUnreachable()) continue; + if (!network->appliesToUser(uid, &subPriority)) continue; + if (subPriority < bestSubPriority) { + bestNetwork = network; + bestSubPriority = subPriority; + } + } + return bestNetwork; +} + Permission NetworkController::getPermissionForUserLocked(uid_t uid) const { auto iter = mUsers.find(uid); if (iter != mUsers.end()) { @@ -781,18 +830,35 @@ int NetworkController::checkUserNetworkAccessLocked(uid_t uid, unsigned netId) c if (uid == INVALID_UID) { return -EREMOTEIO; } + // If the UID has PERMISSION_SYSTEM, it can use whatever network it wants. Permission userPermission = getPermissionForUserLocked(uid); if ((userPermission & PERMISSION_SYSTEM) == PERMISSION_SYSTEM) { return 0; } - if (network->getType() == Network::VIRTUAL) { - return static_cast<VirtualNetwork*>(network)->appliesToUser(uid) ? 0 : -EPERM; + // If the UID wants to use a VPN, it can do so if and only if the VPN applies to the UID. + uint32_t subPriority; + if (network->isVirtual()) { + return network->appliesToUser(uid, &subPriority) ? 0 : -EPERM; } + // If a VPN applies to the UID, and the VPN is secure (i.e., not bypassable), then the UID can + // only select a different network if it has the ability to protect its sockets. VirtualNetwork* virtualNetwork = getVirtualNetworkForUserLocked(uid); if (virtualNetwork && virtualNetwork->isSecure() && mProtectableUsers.find(uid) == mProtectableUsers.end()) { return -EPERM; } + // If the UID wants to use a physical network and it has a UID range that includes the UID, the + // UID has permission to use it regardless of whether the permission bits match. + if (network->isPhysical() && network->appliesToUser(uid, &subPriority)) { + return 0; + } + // Only apps that are configured as "no default network" can use the unreachable network. + if (network->isUnreachable()) { + return network->appliesToUser(uid, &subPriority) ? 0 : -EPERM; + } + // Check whether the UID's permission bits are sufficient to use the network. + // Because the permission of the system default network is PERMISSION_NONE(0x0), apps can always + // pass the check here when using the system default network. Permission networkPermission = static_cast<PhysicalNetwork*>(network)->getPermission(); return ((userPermission & networkPermission) == networkPermission) ? 0 : -EACCES; } @@ -849,7 +915,7 @@ int NetworkController::modifyFallthroughLocked(unsigned vpnNetId, bool add) { ALOGE("cannot find previously set default network with netId %u", mDefaultNetId); return -ESRCH; } - if (network->getType() != Network::PHYSICAL) { + if (!network->isPhysical()) { ALOGE("inconceivable! default network must be a physical network"); return -EINVAL; } @@ -867,7 +933,7 @@ void NetworkController::updateTcpSocketMonitorPolling() { bool physicalNetworkExists = false; for (const auto& entry : mNetworks) { const auto& network = entry.second; - if (network->getType() == Network::PHYSICAL && network->getNetId() >= MIN_NET_ID) { + if (network->isPhysical() && network->getNetId() >= MIN_NET_ID) { physicalNetworkExists = true; break; } diff --git a/server/NetworkController.h b/server/NetworkController.h index ff49c025..a61ac39f 100644 --- a/server/NetworkController.h +++ b/server/NetworkController.h @@ -22,6 +22,8 @@ #include "NetdConstants.h" #include "Permission.h" +#include "PhysicalNetwork.h" +#include "UnreachableNetwork.h" #include "android/net/INetd.h" #include "netdutils/DumpWriter.h" @@ -82,11 +84,12 @@ class VirtualNetwork; */ class NetworkController { public: - // NetIds 52..98 are reserved for future use. + // NetIds 53..98 are reserved for future use. static constexpr int MIN_OEM_ID = 1; static constexpr int MAX_OEM_ID = 50; static constexpr int LOCAL_NET_ID = INetd::LOCAL_NET_ID; - static constexpr int DUMMY_NET_ID = 51; + static constexpr int DUMMY_NET_ID = INetd::DUMMY_NET_ID; + static constexpr int UNREACHABLE_NET_ID = INetd::UNREACHABLE_NET_ID; // Route mode for modify route enum RouteOperation { ROUTE_ADD, ROUTE_UPDATE, ROUTE_REMOVE }; @@ -104,7 +107,7 @@ public: [[nodiscard]] int createPhysicalNetwork(unsigned netId, Permission permission); [[nodiscard]] int createPhysicalOemNetwork(Permission permission, unsigned* netId); - [[nodiscard]] int createVirtualNetwork(unsigned netId, bool secure); + [[nodiscard]] int createVirtualNetwork(unsigned netId, bool secure, NativeVpnType vpnType); [[nodiscard]] int destroyNetwork(unsigned netId); [[nodiscard]] int addInterfaceToNetwork(unsigned netId, const char* interface); @@ -116,8 +119,10 @@ public: [[nodiscard]] int setPermissionForNetworks(Permission permission, const std::vector<unsigned>& netIds); - [[nodiscard]] int addUsersToNetwork(unsigned netId, const UidRanges& uidRanges); - [[nodiscard]] int removeUsersFromNetwork(unsigned netId, const UidRanges& uidRanges); + [[nodiscard]] int addUsersToNetwork(unsigned netId, const UidRanges& uidRanges, + uint32_t subPriority); + [[nodiscard]] int removeUsersFromNetwork(unsigned netId, const UidRanges& uidRanges, + uint32_t subPriority); // |nexthop| can be NULL (to indicate a directly-connected route), "unreachable" (to indicate a // route that's blocked), "throw" (to indicate the lack of a match), or a regular IP address. @@ -151,13 +156,12 @@ public: // set to a non-NETID_UNSET value if the user already has indicated a preference. Returns the // fwmark value to set on the socket when performing the DNS request. uint32_t getNetworkForDnsLocked(unsigned* netId, uid_t uid) const; - - unsigned getNetworkForUserLocked(uid_t uid) const; unsigned getNetworkForConnectLocked(uid_t uid) const; unsigned getNetworkForInterfaceLocked(const char* interface) const; bool canProtectLocked(uid_t uid) const; bool isVirtualNetworkLocked(unsigned netId) const; VirtualNetwork* getVirtualNetworkForUserLocked(uid_t uid) const; + Network* getPhysicalOrUnreachableNetworkForUserLocked(uid_t uid) const; Permission getPermissionForUserLocked(uid_t uid) const; int checkUserNetworkAccessLocked(uid_t uid, unsigned netId) const; [[nodiscard]] int createPhysicalNetworkLocked(unsigned netId, Permission permission); diff --git a/server/OemNetdListener.cpp b/server/OemNetdListener.cpp index fb07a805..9c44cf23 100644 --- a/server/OemNetdListener.cpp +++ b/server/OemNetdListener.cpp @@ -24,16 +24,10 @@ namespace internal { namespace net { ::android::sp<::android::IBinder> OemNetdListener::getListener() { - static OemNetdListener listener; - return listener.getIBinder(); -} - -::android::sp<::android::IBinder> OemNetdListener::getIBinder() { - std::lock_guard lock(mMutex); - if (mIBinder == nullptr) { - mIBinder = ::android::IInterface::asBinder(this); - } - return mIBinder; + // Thread-safe initialization. + static ::android::sp<OemNetdListener> listener = ::android::sp<OemNetdListener>::make(); + static ::android::sp<::android::IBinder> sIBinder = ::android::IInterface::asBinder(listener); + return sIBinder; } ::android::binder::Status OemNetdListener::isAlive(bool* alive) { diff --git a/server/OemNetdListener.h b/server/OemNetdListener.h index 4fb4fb78..b94827b3 100644 --- a/server/OemNetdListener.h +++ b/server/OemNetdListener.h @@ -43,14 +43,10 @@ class OemNetdListener : public BnOemNetd { const ::android::sp<IOemNetdUnsolicitedEventListener>& listener) override; private: - std::mutex mMutex; std::mutex mOemUnsolicitedMutex; - ::android::sp<::android::IBinder> mIBinder GUARDED_BY(mMutex); OemUnsolListenerMap mOemUnsolListenerMap GUARDED_BY(mOemUnsolicitedMutex); - ::android::sp<::android::IBinder> getIBinder() EXCLUDES(mMutex); - void registerOemUnsolicitedEventListenerInternal( const ::android::sp<IOemNetdUnsolicitedEventListener>& listener) EXCLUDES(mOemUnsolicitedMutex); @@ -64,4 +60,4 @@ class OemNetdListener : public BnOemNetd { } // namespace android } // namespace com -#endif // NETD_SERVER_OEM_NETD_LISTENER_H
\ No newline at end of file +#endif // NETD_SERVER_OEM_NETD_LISTENER_H diff --git a/server/OffloadUtils.cpp b/server/OffloadUtils.cpp index a743458c..0d9869fa 100644 --- a/server/OffloadUtils.cpp +++ b/server/OffloadUtils.cpp @@ -38,7 +38,7 @@ namespace net { using std::max; -int hardwareAddressType(const std::string& interface) { +static int doSIOCGIF(const std::string& interface, int opt) { base::unique_fd ufd(socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0)); if (ufd < 0) { @@ -56,9 +56,19 @@ int hardwareAddressType(const std::string& interface) { // match a truncated interface if one were to exist. strncpy(ifr.ifr_name, interface.c_str(), sizeof(ifr.ifr_name)); - if (ioctl(ufd, SIOCGIFHWADDR, &ifr, sizeof(ifr))) return -errno; + if (ioctl(ufd, opt, &ifr, sizeof(ifr))) return -errno; + + if (opt == SIOCGIFHWADDR) return ifr.ifr_hwaddr.sa_family; + if (opt == SIOCGIFMTU) return ifr.ifr_mtu; + return -EINVAL; +} + +int hardwareAddressType(const std::string& interface) { + return doSIOCGIF(interface, SIOCGIFHWADDR); +} - return ifr.ifr_hwaddr.sa_family; +int deviceMTU(const std::string& interface) { + return doSIOCGIF(interface, SIOCGIFMTU); } base::Result<bool> isEthernet(const std::string& interface) { @@ -194,10 +204,12 @@ int doTcQdiscClsact(int ifIndex, uint16_t nlMsgType, uint16_t nlMsgFlags) { return sendAndProcessNetlinkResponse(&req, sizeof(req)); } -// tc filter add dev .. in/egress prio 1 protocol ipv6/ip bpf object-pinned /sys/fs/bpf/... +// The priority of clat hook - must be after tethering. +constexpr uint16_t PRIO_CLAT = 4; + +// tc filter add dev .. in/egress prio 4 protocol ipv6/ip bpf object-pinned /sys/fs/bpf/... // direct-action -int tcFilterAddDevBpf(int ifIndex, bool ingress, uint16_t prio, uint16_t proto, int bpfFd, - bool ethernet) { +int tcFilterAddDevBpf(int ifIndex, bool ingress, uint16_t proto, int bpfFd, bool ethernet) { // This is the name of the filter we're attaching (ie. this is the 'bpf' // packet classifier enabled by kernel config option CONFIG_NET_CLS_BPF. // @@ -213,40 +225,28 @@ int tcFilterAddDevBpf(int ifIndex, bool ingress, uint16_t prio, uint16_t proto, #define FSOBJ_SUFFIX ":[*fsobj]" // This macro expands (from header files) to: - // prog_clatd_schedcls_ingress_clat_rawip:[*fsobj] + // prog_clatd_schedcls_ingress6_clat_rawip:[*fsobj] // and is the name of the pinned ingress ebpf program for ARPHRD_RAWIP interfaces. // (also compatible with anything that has 0 size L2 header) - static constexpr char name_clat_rx_rawip[] = CLAT_INGRESS_PROG_RAWIP_NAME FSOBJ_SUFFIX; + static constexpr char name_clat_rx_rawip[] = CLAT_INGRESS6_PROG_RAWIP_NAME FSOBJ_SUFFIX; // This macro expands (from header files) to: - // prog_clatd_schedcls_ingress_clat_ether:[*fsobj] + // prog_clatd_schedcls_ingress6_clat_ether:[*fsobj] // and is the name of the pinned ingress ebpf program for ARPHRD_ETHER interfaces. // (also compatible with anything that has standard ethernet header) - static constexpr char name_clat_rx_ether[] = CLAT_INGRESS_PROG_ETHER_NAME FSOBJ_SUFFIX; + static constexpr char name_clat_rx_ether[] = CLAT_INGRESS6_PROG_ETHER_NAME FSOBJ_SUFFIX; // This macro expands (from header files) to: - // prog_clatd_schedcls_egress_clat_rawip:[*fsobj] + // prog_clatd_schedcls_egress4_clat_rawip:[*fsobj] // and is the name of the pinned egress ebpf program for ARPHRD_RAWIP interfaces. // (also compatible with anything that has 0 size L2 header) - static constexpr char name_clat_tx_rawip[] = CLAT_EGRESS_PROG_RAWIP_NAME FSOBJ_SUFFIX; + static constexpr char name_clat_tx_rawip[] = CLAT_EGRESS4_PROG_RAWIP_NAME FSOBJ_SUFFIX; // This macro expands (from header files) to: - // prog_clatd_schedcls_egress_clat_ether:[*fsobj] + // prog_clatd_schedcls_egress4_clat_ether:[*fsobj] // and is the name of the pinned egress ebpf program for ARPHRD_ETHER interfaces. // (also compatible with anything that has standard ethernet header) - static constexpr char name_clat_tx_ether[] = CLAT_EGRESS_PROG_ETHER_NAME FSOBJ_SUFFIX; - - // This macro expands (from header files) to: - // prog_offload_schedcls_ingress_tether_rawip:[*fsobj] - // and is the name of the pinned ingress ebpf program for ARPHRD_RAWIP interfaces. - // (also compatible with anything that has 0 size L2 header) - static constexpr char name_tether_rawip[] = TETHER_INGRESS_PROG_RAWIP_NAME FSOBJ_SUFFIX; - - // This macro expands (from header files) to: - // prog_offload_schedcls_ingress_tether_ether:[*fsobj] - // and is the name of the pinned ingress ebpf program for ARPHRD_ETHER interfaces. - // (also compatible with anything that has standard ethernet header) - static constexpr char name_tether_ether[] = TETHER_INGRESS_PROG_ETHER_NAME FSOBJ_SUFFIX; + static constexpr char name_clat_tx_ether[] = CLAT_EGRESS4_PROG_ETHER_NAME FSOBJ_SUFFIX; #undef FSOBJ_SUFFIX @@ -259,16 +259,12 @@ int tcFilterAddDevBpf(int ifIndex, bool ingress, uint16_t prio, uint16_t proto, sizeof(name_clat_rx_ether), sizeof(name_clat_tx_rawip), sizeof(name_clat_tx_ether), - sizeof(name_tether_rawip), - sizeof(name_tether_ether), }); // These are not compile time constants: 'name' is used in strncpy below const char* const name_clat_rx = ethernet ? name_clat_rx_ether : name_clat_rx_rawip; const char* const name_clat_tx = ethernet ? name_clat_tx_ether : name_clat_tx_rawip; - const char* const name_clat = ingress ? name_clat_rx : name_clat_tx; - const char* const name_tether = ethernet ? name_tether_ether : name_tether_rawip; - const char* const name = (prio == PRIO_TETHER) ? name_tether : name_clat; + const char* const name = ingress ? name_clat_rx : name_clat_tx; struct { nlmsghdr n; @@ -306,7 +302,7 @@ int tcFilterAddDevBpf(int ifIndex, bool ingress, uint16_t prio, uint16_t proto, .tcm_handle = TC_H_UNSPEC, .tcm_parent = TC_H_MAKE(TC_H_CLSACT, ingress ? TC_H_MIN_INGRESS : TC_H_MIN_EGRESS), - .tcm_info = static_cast<__u32>((prio << 16) | htons(proto)), + .tcm_info = static_cast<__u32>((PRIO_CLAT << 16) | htons(proto)), }, .kind = { @@ -322,7 +318,7 @@ int tcFilterAddDevBpf(int ifIndex, bool ingress, uint16_t prio, uint16_t proto, .attr = { .nla_len = sizeof(req.options), - .nla_type = TCA_OPTIONS, + .nla_type = NLA_F_NESTED | TCA_OPTIONS, }, .fd = { @@ -362,8 +358,8 @@ int tcFilterAddDevBpf(int ifIndex, bool ingress, uint16_t prio, uint16_t proto, return sendAndProcessNetlinkResponse(&req, sizeof(req)); } -// tc filter del dev .. in/egress prio .. protocol .. -int tcFilterDelDev(int ifIndex, bool ingress, uint16_t prio, uint16_t proto) { +// tc filter del dev .. in/egress prio 4 protocol .. +int tcFilterDelDev(int ifIndex, bool ingress, uint16_t proto) { const struct { nlmsghdr n; tcmsg t; @@ -381,7 +377,7 @@ int tcFilterDelDev(int ifIndex, bool ingress, uint16_t prio, uint16_t proto) { .tcm_handle = TC_H_UNSPEC, .tcm_parent = TC_H_MAKE(TC_H_CLSACT, ingress ? TC_H_MIN_INGRESS : TC_H_MIN_EGRESS), - .tcm_info = static_cast<__u32>((prio << 16) | htons(proto)), + .tcm_info = static_cast<__u32>((PRIO_CLAT << 16) | htons(proto)), }, }; diff --git a/server/OffloadUtils.h b/server/OffloadUtils.h index 818fd39d..684ffb35 100644 --- a/server/OffloadUtils.h +++ b/server/OffloadUtils.h @@ -19,6 +19,7 @@ #include <android-base/result.h> #include <errno.h> #include <linux/if_ether.h> +#include <linux/if_link.h> #include <linux/rtnetlink.h> #include <string> @@ -38,55 +39,33 @@ constexpr bool ETHER = true; constexpr bool EGRESS = false; constexpr bool INGRESS = true; -// The priority of clat/tether hooks - smaller is higher priority. -constexpr uint16_t PRIO_CLAT = 1; -constexpr uint16_t PRIO_TETHER = 2; - // this returns an ARPHRD_* constant or a -errno int hardwareAddressType(const std::string& interface); -base::Result<bool> isEthernet(const std::string& interface); - -inline int getClatEgressMapFd(void) { - const int fd = bpf::mapRetrieveRW(CLAT_EGRESS_MAP_PATH); - return (fd == -1) ? -errno : fd; -} - -inline int getClatEgressProgFd(bool with_ethernet_header) { - const int fd = bpf::retrieveProgram(with_ethernet_header ? CLAT_EGRESS_PROG_ETHER_PATH - : CLAT_EGRESS_PROG_RAWIP_PATH); - return (fd == -1) ? -errno : fd; -} - -inline int getClatIngressMapFd(void) { - const int fd = bpf::mapRetrieveRW(CLAT_INGRESS_MAP_PATH); - return (fd == -1) ? -errno : fd; -} +// return MTU or -errno +int deviceMTU(const std::string& interface); -inline int getClatIngressProgFd(bool with_ethernet_header) { - const int fd = bpf::retrieveProgram(with_ethernet_header ? CLAT_INGRESS_PROG_ETHER_PATH - : CLAT_INGRESS_PROG_RAWIP_PATH); - return (fd == -1) ? -errno : fd; -} +base::Result<bool> isEthernet(const std::string& interface); -inline int getTetherIngressMapFd(void) { - const int fd = bpf::mapRetrieveRW(TETHER_INGRESS_MAP_PATH); +inline int getClatEgress4MapFd(void) { + const int fd = bpf::mapRetrieveRW(CLAT_EGRESS4_MAP_PATH); return (fd == -1) ? -errno : fd; } -inline int getTetherIngressProgFd(bool with_ethernet_header) { - const int fd = bpf::retrieveProgram(with_ethernet_header ? TETHER_INGRESS_PROG_ETHER_PATH - : TETHER_INGRESS_PROG_RAWIP_PATH); +inline int getClatEgress4ProgFd(bool with_ethernet_header) { + const int fd = bpf::retrieveProgram(with_ethernet_header ? CLAT_EGRESS4_PROG_ETHER_PATH + : CLAT_EGRESS4_PROG_RAWIP_PATH); return (fd == -1) ? -errno : fd; } -inline int getTetherStatsMapFd(void) { - const int fd = bpf::mapRetrieveRW(TETHER_STATS_MAP_PATH); +inline int getClatIngress6MapFd(void) { + const int fd = bpf::mapRetrieveRW(CLAT_INGRESS6_MAP_PATH); return (fd == -1) ? -errno : fd; } -inline int getTetherLimitMapFd(void) { - const int fd = bpf::mapRetrieveRW(TETHER_LIMIT_MAP_PATH); +inline int getClatIngress6ProgFd(bool with_ethernet_header) { + const int fd = bpf::retrieveProgram(with_ethernet_header ? CLAT_INGRESS6_PROG_ETHER_PATH + : CLAT_INGRESS6_PROG_RAWIP_PATH); return (fd == -1) ? -errno : fd; } @@ -104,42 +83,31 @@ inline int tcQdiscDelDevClsact(int ifIndex) { return doTcQdiscClsact(ifIndex, RTM_DELQDISC, 0); } -// tc filter add dev .. in/egress prio 1 protocol ipv6/ip bpf object-pinned /sys/fs/bpf/... +// tc filter add dev .. in/egress prio 4 protocol ipv6/ip bpf object-pinned /sys/fs/bpf/... // direct-action -int tcFilterAddDevBpf(int ifIndex, bool ingress, uint16_t prio, uint16_t proto, int bpfFd, - bool ethernet); +int tcFilterAddDevBpf(int ifIndex, bool ingress, uint16_t proto, int bpfFd, bool ethernet); -// tc filter add dev .. ingress prio 1 protocol ipv6 bpf object-pinned /sys/fs/bpf/... direct-action +// tc filter add dev .. ingress prio 4 protocol ipv6 bpf object-pinned /sys/fs/bpf/... direct-action inline int tcFilterAddDevIngressClatIpv6(int ifIndex, int bpfFd, bool ethernet) { - return tcFilterAddDevBpf(ifIndex, INGRESS, PRIO_CLAT, ETH_P_IPV6, bpfFd, ethernet); + return tcFilterAddDevBpf(ifIndex, INGRESS, ETH_P_IPV6, bpfFd, ethernet); } -// tc filter add dev .. egress prio 1 protocol ip bpf object-pinned /sys/fs/bpf/... direct-action +// tc filter add dev .. egress prio 4 protocol ip bpf object-pinned /sys/fs/bpf/... direct-action inline int tcFilterAddDevEgressClatIpv4(int ifIndex, int bpfFd, bool ethernet) { - return tcFilterAddDevBpf(ifIndex, EGRESS, PRIO_CLAT, ETH_P_IP, bpfFd, ethernet); -} - -// tc filter add dev .. ingress prio 2 protocol ipv6 bpf object-pinned /sys/fs/bpf/... direct-action -inline int tcFilterAddDevIngressTether(int ifIndex, int bpfFd, bool ethernet) { - return tcFilterAddDevBpf(ifIndex, INGRESS, PRIO_TETHER, ETH_P_IPV6, bpfFd, ethernet); + return tcFilterAddDevBpf(ifIndex, EGRESS, ETH_P_IP, bpfFd, ethernet); } // tc filter del dev .. in/egress prio .. protocol .. -int tcFilterDelDev(int ifIndex, bool ingress, uint16_t prio, uint16_t proto); +int tcFilterDelDev(int ifIndex, bool ingress, uint16_t proto); -// tc filter del dev .. ingress prio 1 protocol ipv6 +// tc filter del dev .. ingress prio 4 protocol ipv6 inline int tcFilterDelDevIngressClatIpv6(int ifIndex) { - return tcFilterDelDev(ifIndex, INGRESS, PRIO_CLAT, ETH_P_IPV6); + return tcFilterDelDev(ifIndex, INGRESS, ETH_P_IPV6); } -// tc filter del dev .. egress prio 1 protocol ip +// tc filter del dev .. egress prio 4 protocol ip inline int tcFilterDelDevEgressClatIpv4(int ifIndex) { - return tcFilterDelDev(ifIndex, EGRESS, PRIO_CLAT, ETH_P_IP); -} - -// tc filter del dev .. ingress prio 2 protocol ipv6 -inline int tcFilterDelDevIngressTether(int ifIndex) { - return tcFilterDelDev(ifIndex, INGRESS, PRIO_TETHER, ETH_P_IPV6); + return tcFilterDelDev(ifIndex, EGRESS, ETH_P_IP); } } // namespace net diff --git a/server/OffloadUtilsTest.cpp b/server/OffloadUtilsTest.cpp index 1760c1d1..16c108b4 100644 --- a/server/OffloadUtilsTest.cpp +++ b/server/OffloadUtilsTest.cpp @@ -98,160 +98,60 @@ TEST_F(OffloadUtilsTest, IsEthernetOfCellular) { ASSERT_FALSE(res.value()); } -TEST_F(OffloadUtilsTest, GetClatEgressMapFd) { - SKIP_IF_BPF_NOT_SUPPORTED; - - int fd = getClatEgressMapFd(); - ASSERT_GE(fd, 3); // 0,1,2 - stdin/out/err, thus fd >= 3 - EXPECT_EQ(FD_CLOEXEC, fcntl(fd, F_GETFD)); - close(fd); +TEST_F(OffloadUtilsTest, DeviceMTUOfNonExistingIf) { + ASSERT_EQ(-ENODEV, deviceMTU("not_existing_if")); } -TEST_F(OffloadUtilsTest, GetClatEgressRawIpProgFd) { - SKIP_IF_BPF_NOT_SUPPORTED; - - int fd = getClatEgressProgFd(RAWIP); - ASSERT_GE(fd, 3); - EXPECT_EQ(FD_CLOEXEC, fcntl(fd, F_GETFD)); - close(fd); +TEST_F(OffloadUtilsTest, DeviceMTUofLoopback) { + ASSERT_EQ(65536, deviceMTU("lo")); } -TEST_F(OffloadUtilsTest, GetClatEgressEtherProgFd) { - SKIP_IF_BPF_NOT_SUPPORTED; - - int fd = getClatEgressProgFd(ETHER); - ASSERT_GE(fd, 3); - EXPECT_EQ(FD_CLOEXEC, fcntl(fd, F_GETFD)); - close(fd); -} - -TEST_F(OffloadUtilsTest, GetClatIngressMapFd) { - SKIP_IF_BPF_NOT_SUPPORTED; - - int fd = getClatIngressMapFd(); +TEST_F(OffloadUtilsTest, GetClatEgress4MapFd) { + int fd = getClatEgress4MapFd(); ASSERT_GE(fd, 3); // 0,1,2 - stdin/out/err, thus fd >= 3 EXPECT_EQ(FD_CLOEXEC, fcntl(fd, F_GETFD)); close(fd); } -TEST_F(OffloadUtilsTest, GetClatIngressRawIpProgFd) { - SKIP_IF_BPF_NOT_SUPPORTED; - - int fd = getClatIngressProgFd(RAWIP); +TEST_F(OffloadUtilsTest, GetClatEgress4RawIpProgFd) { + int fd = getClatEgress4ProgFd(RAWIP); ASSERT_GE(fd, 3); EXPECT_EQ(FD_CLOEXEC, fcntl(fd, F_GETFD)); close(fd); } -TEST_F(OffloadUtilsTest, GetClatIngressEtherProgFd) { - SKIP_IF_BPF_NOT_SUPPORTED; - - int fd = getClatIngressProgFd(ETHER); +TEST_F(OffloadUtilsTest, GetClatEgress4EtherProgFd) { + int fd = getClatEgress4ProgFd(ETHER); ASSERT_GE(fd, 3); EXPECT_EQ(FD_CLOEXEC, fcntl(fd, F_GETFD)); close(fd); } -TEST_F(OffloadUtilsTest, GetTetherIngressMapFd) { - SKIP_IF_BPF_NOT_SUPPORTED; - - int fd = getTetherIngressMapFd(); +TEST_F(OffloadUtilsTest, GetClatIngress6MapFd) { + int fd = getClatIngress6MapFd(); ASSERT_GE(fd, 3); // 0,1,2 - stdin/out/err, thus fd >= 3 EXPECT_EQ(FD_CLOEXEC, fcntl(fd, F_GETFD)); close(fd); } -TEST_F(OffloadUtilsTest, GetTetherIngressRawIpProgFd) { - // Currently only implementing downstream direction offload. - // RX Rawip -> TX Ether requires header adjustments and thus 4.14. - SKIP_IF_EXTENDED_BPF_NOT_SUPPORTED; - - int fd = getTetherIngressProgFd(RAWIP); +TEST_F(OffloadUtilsTest, GetClatIngress6RawIpProgFd) { + int fd = getClatIngress6ProgFd(RAWIP); ASSERT_GE(fd, 3); EXPECT_EQ(FD_CLOEXEC, fcntl(fd, F_GETFD)); close(fd); } -TEST_F(OffloadUtilsTest, GetTetherIngressEtherProgFd) { - // Currently only implementing downstream direction offload. - // RX Ether -> TX Ether does not require header adjustments - SKIP_IF_BPF_NOT_SUPPORTED; - - int fd = getTetherIngressProgFd(ETHER); +TEST_F(OffloadUtilsTest, GetClatIngress6EtherProgFd) { + int fd = getClatIngress6ProgFd(ETHER); ASSERT_GE(fd, 3); EXPECT_EQ(FD_CLOEXEC, fcntl(fd, F_GETFD)); close(fd); } -TEST_F(OffloadUtilsTest, GetTetherStatsMapFd) { - SKIP_IF_BPF_NOT_SUPPORTED; - - int fd = getTetherStatsMapFd(); - ASSERT_GE(fd, 3); // 0,1,2 - stdin/out/err, thus fd >= 3 - EXPECT_EQ(FD_CLOEXEC, fcntl(fd, F_GETFD)); - close(fd); -} - -TEST_F(OffloadUtilsTest, GetTetherLimitMapFd) { - SKIP_IF_BPF_NOT_SUPPORTED; - - int fd = getTetherLimitMapFd(); - ASSERT_GE(fd, 3); // 0,1,2 - stdin/out/err, thus fd >= 3 - EXPECT_EQ(FD_CLOEXEC, fcntl(fd, F_GETFD)); - close(fd); -} - -// The SKIP_IF_BPF_NOT_SUPPORTED macro is effectively a check for 4.9+ kernel -// combined with a launched on P device. Ie. it's a test for 4.9-P or better. - -// NET_SCH_INGRESS is only enabled starting with 4.9-Q and as such we need -// a separate way to test for this... -int doKernelSupportsNetSchIngress(void) { - // NOLINTNEXTLINE(cert-env33-c) - return system("zcat /proc/config.gz | egrep -q '^CONFIG_NET_SCH_INGRESS=[my]$'"); -} - -// NET_CLS_BPF is only enabled starting with 4.9-Q... -int doKernelSupportsNetClsBpf(void) { - // NOLINTNEXTLINE(cert-env33-c) - return system("zcat /proc/config.gz | egrep -q '^CONFIG_NET_CLS_BPF=[my]$'"); -} - -// Make sure the above functions actually execute correctly rather than failing -// due to missing binary or execution failure... -TEST_F(OffloadUtilsTest, KernelSupportsNetFuncs) { - // Make sure the file is present and readable and decompressable. - // NOLINTNEXTLINE(cert-env33-c) - ASSERT_EQ(W_EXITCODE(0, 0), system("zcat /proc/config.gz > /dev/null")); - - int v = doKernelSupportsNetSchIngress(); - int w = doKernelSupportsNetClsBpf(); - - // They should always either return 0 (match) or 1 (no match), - // anything else is some sort of exec/environment/etc failure. - if (v != W_EXITCODE(1, 0)) ASSERT_EQ(v, W_EXITCODE(0, 0)); - if (w != W_EXITCODE(1, 0)) ASSERT_EQ(w, W_EXITCODE(0, 0)); -} - -// True iff CONFIG_NET_SCH_INGRESS is enabled in /proc/config.gz -bool kernelSupportsNetSchIngress(void) { - return doKernelSupportsNetSchIngress() == W_EXITCODE(0, 0); -} - -// True iff CONFIG_NET_CLS_BPF is enabled in /proc/config.gz -bool kernelSupportsNetClsBpf(void) { - return doKernelSupportsNetClsBpf() == W_EXITCODE(0, 0); -} - // See Linux kernel source in include/net/flow.h #define LOOPBACK_IFINDEX 1 TEST_F(OffloadUtilsTest, AttachReplaceDetachClsactLo) { - // Technically does not depend on ebpf, but does depend on clsact, - // and we do not really care if it works on pre-4.9-Q anyway. - SKIP_IF_BPF_NOT_SUPPORTED; - if (!kernelSupportsNetSchIngress()) return; - // This attaches and detaches a configuration-less and thus no-op clsact // qdisc to loopback interface (and it takes fractions of a second) EXPECT_EQ(0, tcQdiscAddDevClsact(LOOPBACK_IFINDEX)); @@ -261,27 +161,12 @@ TEST_F(OffloadUtilsTest, AttachReplaceDetachClsactLo) { } static void checkAttachDetachBpfFilterClsactLo(const bool ingress, const bool ethernet) { - // This test requires kernel 4.9-Q or better - SKIP_IF_BPF_NOT_SUPPORTED; - if (!kernelSupportsNetSchIngress()) return; - if (!kernelSupportsNetClsBpf()) return; - - const bool extended = - (android::bpf::getBpfSupportLevel() >= android::bpf::BpfLevel::EXTENDED_4_14); // Older kernels return EINVAL instead of ENOENT due to lacking proper error propagation... - const int errNOENT = - (android::bpf::getBpfSupportLevel() >= android::bpf::BpfLevel::EXTENDED_4_19) ? ENOENT - : EINVAL; + const int errNOENT = android::bpf::isAtLeastKernelVersion(4, 19, 0) ? ENOENT : EINVAL; - int clatBpfFd = ingress ? getClatIngressProgFd(ethernet) : getClatEgressProgFd(ethernet); + int clatBpfFd = ingress ? getClatIngress6ProgFd(ethernet) : getClatEgress4ProgFd(ethernet); ASSERT_GE(clatBpfFd, 3); - int tetherBpfFd = -1; - if (extended && ingress) { - tetherBpfFd = getTetherIngressProgFd(ethernet); - ASSERT_GE(tetherBpfFd, 3); - } - // This attaches and detaches a clsact plus ebpf program to loopback // interface, but it should not affect traffic by virtue of us not // actually populating the ebpf control map. @@ -293,10 +178,6 @@ static void checkAttachDetachBpfFilterClsactLo(const bool ingress, const bool et EXPECT_EQ(-errNOENT, tcFilterDelDevEgressClatIpv4(LOOPBACK_IFINDEX)); if (ingress) { EXPECT_EQ(0, tcFilterAddDevIngressClatIpv6(LOOPBACK_IFINDEX, clatBpfFd, ethernet)); - if (extended) { - EXPECT_EQ(0, tcFilterAddDevIngressTether(LOOPBACK_IFINDEX, tetherBpfFd, ethernet)); - EXPECT_EQ(0, tcFilterDelDevIngressTether(LOOPBACK_IFINDEX)); - } EXPECT_EQ(0, tcFilterDelDevIngressClatIpv6(LOOPBACK_IFINDEX)); } else { EXPECT_EQ(0, tcFilterAddDevEgressClatIpv4(LOOPBACK_IFINDEX, clatBpfFd, ethernet)); @@ -308,7 +189,6 @@ static void checkAttachDetachBpfFilterClsactLo(const bool ingress, const bool et EXPECT_EQ(-EINVAL, tcFilterDelDevIngressClatIpv6(LOOPBACK_IFINDEX)); EXPECT_EQ(-EINVAL, tcFilterDelDevEgressClatIpv4(LOOPBACK_IFINDEX)); - if (tetherBpfFd != -1) close(tetherBpfFd); close(clatBpfFd); } diff --git a/server/PhysicalNetwork.cpp b/server/PhysicalNetwork.cpp index 2808fbe7..7b9a19a1 100644 --- a/server/PhysicalNetwork.cpp +++ b/server/PhysicalNetwork.cpp @@ -158,8 +158,36 @@ int PhysicalNetwork::removeAsDefault() { return 0; } -Network::Type PhysicalNetwork::getType() const { - return PHYSICAL; +int PhysicalNetwork::addUsers(const UidRanges& uidRanges, uint32_t subPriority) { + if (!isValidSubPriority(subPriority) || !canAddUidRanges(uidRanges, subPriority)) { + return -EINVAL; + } + + for (const std::string& interface : mInterfaces) { + int ret = RouteController::addUsersToPhysicalNetwork(mNetId, interface.c_str(), + {{subPriority, uidRanges}}); + if (ret) { + ALOGE("failed to add users on interface %s of netId %u", interface.c_str(), mNetId); + return ret; + } + } + addToUidRangeMap(uidRanges, subPriority); + return 0; +} + +int PhysicalNetwork::removeUsers(const UidRanges& uidRanges, uint32_t subPriority) { + if (!isValidSubPriority(subPriority)) return -EINVAL; + + for (const std::string& interface : mInterfaces) { + int ret = RouteController::removeUsersFromPhysicalNetwork(mNetId, interface.c_str(), + {{subPriority, uidRanges}}); + if (ret) { + ALOGE("failed to remove users on interface %s of netId %u", interface.c_str(), mNetId); + return ret; + } + } + removeFromUidRangeMap(uidRanges, subPriority); + return 0; } int PhysicalNetwork::addInterface(const std::string& interface) { @@ -167,7 +195,7 @@ int PhysicalNetwork::addInterface(const std::string& interface) { return 0; } if (int ret = RouteController::addInterfaceToPhysicalNetwork(mNetId, interface.c_str(), - mPermission)) { + mPermission, mUidRangeMap)) { ALOGE("failed to add interface %s to netId %u", interface.c_str(), mNetId); return ret; } @@ -194,7 +222,7 @@ int PhysicalNetwork::removeInterface(const std::string& interface) { // to find the interface index in the cache in cases where the interface is already gone // (e.g. bt-pan). if (int ret = RouteController::removeInterfaceFromPhysicalNetwork(mNetId, interface.c_str(), - mPermission)) { + mPermission, mUidRangeMap)) { ALOGE("failed to remove interface %s from netId %u", interface.c_str(), mNetId); return ret; } @@ -202,4 +230,9 @@ int PhysicalNetwork::removeInterface(const std::string& interface) { return 0; } +bool PhysicalNetwork::isValidSubPriority(uint32_t priority) { + return priority >= UidRanges::DEFAULT_SUB_PRIORITY && + priority <= UidRanges::LOWEST_SUB_PRIORITY; +} + } // namespace android::net diff --git a/server/PhysicalNetwork.h b/server/PhysicalNetwork.h index 07208243..d9461b2e 100644 --- a/server/PhysicalNetwork.h +++ b/server/PhysicalNetwork.h @@ -42,13 +42,18 @@ class PhysicalNetwork : public Network { [[nodiscard]] int addAsDefault(); [[nodiscard]] int removeAsDefault(); + [[nodiscard]] int addUsers(const UidRanges& uidRanges, uint32_t subPriority) override; + [[nodiscard]] int removeUsers(const UidRanges& uidRanges, uint32_t subPriority) override; + bool isPhysical() override { return true; } + bool canAddUsers() override { return true; } private: - Type getType() const override; + std::string getTypeString() const override { return "PHYSICAL"; }; [[nodiscard]] int addInterface(const std::string& interface) override; [[nodiscard]] int removeInterface(const std::string& interface) override; int destroySocketsLackingPermission(Permission permission); void invalidateRouteCache(const std::string& interface); + bool isValidSubPriority(uint32_t priority) override; Delegate* const mDelegate; Permission mPermission; diff --git a/server/RouteController.cpp b/server/RouteController.cpp index 134bbcae..ba305e69 100644 --- a/server/RouteController.cpp +++ b/server/RouteController.cpp @@ -27,14 +27,11 @@ #include <map> -#define LOG_TAG "Netd" - #include "DummyNetwork.h" #include "Fwmark.h" #include "NetdConstants.h" #include "NetlinkCommands.h" #include "OffloadUtils.h" -#include "UidRanges.h" #include <android-base/file.h> #include <android-base/stringprintf.h> @@ -54,23 +51,6 @@ auto RouteController::iptablesRestoreCommandFunction = execIptablesRestoreComman // BEGIN CONSTANTS -------------------------------------------------------------------------------- -const uint32_t RULE_PRIORITY_VPN_OVERRIDE_SYSTEM = 10000; -const uint32_t RULE_PRIORITY_VPN_OVERRIDE_OIF = 10500; -const uint32_t RULE_PRIORITY_VPN_OUTPUT_TO_LOCAL = 11000; -const uint32_t RULE_PRIORITY_SECURE_VPN = 12000; -const uint32_t RULE_PRIORITY_PROHIBIT_NON_VPN = 12500; -const uint32_t RULE_PRIORITY_EXPLICIT_NETWORK = 13000; -const uint32_t RULE_PRIORITY_OUTPUT_INTERFACE = 14000; -const uint32_t RULE_PRIORITY_LEGACY_SYSTEM = 15000; -const uint32_t RULE_PRIORITY_LEGACY_NETWORK = 16000; -const uint32_t RULE_PRIORITY_LOCAL_NETWORK = 17000; -const uint32_t RULE_PRIORITY_TETHERING = 18000; -const uint32_t RULE_PRIORITY_IMPLICIT_NETWORK = 19000; -const uint32_t RULE_PRIORITY_BYPASSABLE_VPN = 20000; -const uint32_t RULE_PRIORITY_VPN_FALLTHROUGH = 21000; -const uint32_t RULE_PRIORITY_DEFAULT_NETWORK = 22000; -const uint32_t RULE_PRIORITY_UNREACHABLE = 32000; - const uint32_t ROUTE_TABLE_LOCAL_NETWORK = 97; const uint32_t ROUTE_TABLE_LEGACY_NETWORK = 98; const uint32_t ROUTE_TABLE_LEGACY_SYSTEM = 99; @@ -131,6 +111,9 @@ rtattr RTATTR_METRICS = { U16_RTA_LENGTH(RTATTR_METRICS_SIZE), RTA_MET uint8_t PADDING_BUFFER[RTA_ALIGNTO] = {0, 0, 0, 0}; +constexpr bool EXPLICIT = true; +constexpr bool IMPLICIT = false; + // END CONSTANTS ---------------------------------------------------------------------------------- static const char* actionName(uint16_t action) { @@ -146,6 +129,8 @@ static const char* familyName(uint8_t family) { } } +static void maybeModifyQdiscClsact(const char* interface, bool add); + // Caller must hold sInterfaceToTableLock. uint32_t RouteController::getRouteTableForInterfaceLocked(const char* interface) { // If we already know the routing table for this interface name, use it. @@ -507,7 +492,7 @@ int modifyIncomingPacketMark(unsigned netId, const char* interface, Permission p // have, if they are subject to this VPN, their traffic has to go through it. Allows the traffic to // bypass the VPN if the protectedFromVpn bit is set. [[nodiscard]] static int modifyVpnUidRangeRule(uint32_t table, uid_t uidStart, uid_t uidEnd, - bool secure, bool add) { + uint32_t subPriority, bool secure, bool add) { Fwmark fwmark; Fwmark mask; @@ -525,8 +510,8 @@ int modifyIncomingPacketMark(unsigned netId, const char* interface, Permission p mask.explicitlySelected = true; } - return modifyIpRule(add ? RTM_NEWRULE : RTM_DELRULE, priority, table, fwmark.intValue, - mask.intValue, IIF_LOOPBACK, OIF_NONE, uidStart, uidEnd); + return modifyIpRule(add ? RTM_NEWRULE : RTM_DELRULE, priority + subPriority, table, + fwmark.intValue, mask.intValue, IIF_LOOPBACK, OIF_NONE, uidStart, uidEnd); } // A rule to allow system apps to send traffic over this VPN even if they are not part of the target @@ -560,7 +545,7 @@ int modifyIncomingPacketMark(unsigned netId, const char* interface, Permission p // modifyNetworkPermission(). [[nodiscard]] static int modifyExplicitNetworkRule(unsigned netId, uint32_t table, Permission permission, uid_t uidStart, - uid_t uidEnd, bool add) { + uid_t uidEnd, uint32_t subPriority, bool add) { Fwmark fwmark; Fwmark mask; @@ -573,8 +558,9 @@ int modifyIncomingPacketMark(unsigned netId, const char* interface, Permission p fwmark.permission = permission; mask.permission = permission; - return modifyIpRule(add ? RTM_NEWRULE : RTM_DELRULE, RULE_PRIORITY_EXPLICIT_NETWORK, table, - fwmark.intValue, mask.intValue, IIF_LOOPBACK, OIF_NONE, uidStart, uidEnd); + return modifyIpRule(add ? RTM_NEWRULE : RTM_DELRULE, + RULE_PRIORITY_EXPLICIT_NETWORK + subPriority, table, fwmark.intValue, + mask.intValue, IIF_LOOPBACK, OIF_NONE, uidStart, uidEnd); } // A rule to route traffic based on a chosen outgoing interface. @@ -583,7 +569,7 @@ int modifyIncomingPacketMark(unsigned netId, const char* interface, Permission p // the outgoing interface (typically for link-local communications). [[nodiscard]] static int modifyOutputInterfaceRules(const char* interface, uint32_t table, Permission permission, uid_t uidStart, - uid_t uidEnd, bool add) { + uid_t uidEnd, uint32_t subPriority, bool add) { Fwmark fwmark; Fwmark mask; @@ -601,8 +587,9 @@ int modifyIncomingPacketMark(unsigned netId, const char* interface, Permission p } } - return modifyIpRule(add ? RTM_NEWRULE : RTM_DELRULE, RULE_PRIORITY_OUTPUT_INTERFACE, table, - fwmark.intValue, mask.intValue, IIF_LOOPBACK, interface, uidStart, uidEnd); + return modifyIpRule(add ? RTM_NEWRULE : RTM_DELRULE, + RULE_PRIORITY_OUTPUT_INTERFACE + subPriority, table, fwmark.intValue, + mask.intValue, IIF_LOOPBACK, interface, uidStart, uidEnd); } // A rule to route traffic based on the chosen network. @@ -682,7 +669,8 @@ int RouteController::modifyVpnFallthroughRule(uint16_t action, unsigned vpnNetId // Add rules to lookup the local network when specified explicitly or otherwise. [[nodiscard]] static int addLocalNetworkRules(unsigned localNetId) { if (int ret = modifyExplicitNetworkRule(localNetId, ROUTE_TABLE_LOCAL_NETWORK, PERMISSION_NONE, - INVALID_UID, INVALID_UID, ACTION_ADD)) { + INVALID_UID, INVALID_UID, + UidRanges::DEFAULT_SUB_PRIORITY, ACTION_ADD)) { return ret; } @@ -713,8 +701,9 @@ int RouteController::configureDummyNetwork() { return -errno; } - if ((ret = modifyOutputInterfaceRules(interface, table, PERMISSION_NONE, - INVALID_UID, INVALID_UID, ACTION_ADD))) { + if ((ret = modifyOutputInterfaceRules(interface, table, PERMISSION_NONE, INVALID_UID, + INVALID_UID, UidRanges::DEFAULT_SUB_PRIORITY, + ACTION_ADD))) { ALOGE("Can't create oif rules for %s: %s", interface, strerror(-ret)); return ret; } @@ -745,27 +734,102 @@ int RouteController::configureDummyNetwork() { if (int ret = modifyIncomingPacketMark(netId, interface, PERMISSION_NONE, add)) { return ret; } + maybeModifyQdiscClsact(interface, add); return modifyOutputInterfaceRules(interface, ROUTE_TABLE_LOCAL_NETWORK, PERMISSION_NONE, - INVALID_UID, INVALID_UID, add); + INVALID_UID, INVALID_UID, UidRanges::DEFAULT_SUB_PRIORITY, + add); +} + +[[nodiscard]] static int modifyUidNetworkRule(unsigned netId, uint32_t table, uid_t uidStart, + uid_t uidEnd, uint32_t subPriority, bool add, + bool explicitSelect) { + if ((uidStart == INVALID_UID) || (uidEnd == INVALID_UID)) { + ALOGE("modifyUidNetworkRule, invalid UIDs (%u, %u)", uidStart, uidEnd); + return -EUSERS; + } + + Fwmark fwmark; + Fwmark mask; + + fwmark.netId = netId; + mask.netId = FWMARK_NET_ID_MASK; + + fwmark.explicitlySelected = explicitSelect; + mask.explicitlySelected = true; + + // Access to this network is controlled by UID rules, not permission bits. + fwmark.permission = PERMISSION_NONE; + mask.permission = PERMISSION_NONE; + + return modifyIpRule(add ? RTM_NEWRULE : RTM_DELRULE, + explicitSelect ? (RULE_PRIORITY_UID_EXPLICIT_NETWORK + subPriority) + : (RULE_PRIORITY_UID_IMPLICIT_NETWORK + subPriority), + table, fwmark.intValue, mask.intValue, IIF_LOOPBACK, OIF_NONE, uidStart, + uidEnd); +} + +[[nodiscard]] static int modifyUidDefaultNetworkRule(uint32_t table, uid_t uidStart, uid_t uidEnd, + uint32_t subPriority, bool add) { + if ((uidStart == INVALID_UID) || (uidEnd == INVALID_UID)) { + ALOGE("modifyUidDefaultNetworkRule, invalid UIDs (%u, %u)", uidStart, uidEnd); + return -EUSERS; + } + + Fwmark fwmark; + Fwmark mask; + + fwmark.netId = NETID_UNSET; + mask.netId = FWMARK_NET_ID_MASK; + + // Access to this network is controlled by UID rules, not permission bits. + fwmark.permission = PERMISSION_NONE; + mask.permission = PERMISSION_NONE; + + return modifyIpRule(add ? RTM_NEWRULE : RTM_DELRULE, + RULE_PRIORITY_UID_DEFAULT_NETWORK + subPriority, table, fwmark.intValue, + mask.intValue, IIF_LOOPBACK, OIF_NONE, uidStart, uidEnd); } /* static */ int RouteController::modifyPhysicalNetwork(unsigned netId, const char* interface, - Permission permission, bool add) { + const UidRangeMap& uidRangeMap, Permission permission, + bool add, bool modifyNonUidBasedRules) { uint32_t table = getRouteTableForInterface(interface); if (table == RT_TABLE_UNSPEC) { return -ESRCH; } + for (const auto& [subPriority, uidRanges] : uidRangeMap) { + for (const UidRangeParcel& range : uidRanges.getRanges()) { + if (int ret = modifyUidNetworkRule(netId, table, range.start, range.stop, subPriority, + add, EXPLICIT)) { + return ret; + } + if (int ret = modifyUidNetworkRule(netId, table, range.start, range.stop, subPriority, + add, IMPLICIT)) { + return ret; + } + if (int ret = modifyUidDefaultNetworkRule(table, range.start, range.stop, subPriority, + add)) { + return ret; + } + } + } + + if (!modifyNonUidBasedRules) { + // we are done. + return 0; + } + if (int ret = modifyIncomingPacketMark(netId, interface, permission, add)) { return ret; } if (int ret = modifyExplicitNetworkRule(netId, table, permission, INVALID_UID, INVALID_UID, - add)) { + UidRanges::DEFAULT_SUB_PRIORITY, add)) { return ret; } if (int ret = modifyOutputInterfaceRules(interface, table, permission, INVALID_UID, INVALID_UID, - add)) { + UidRanges::DEFAULT_SUB_PRIORITY, add)) { return ret; } @@ -795,6 +859,79 @@ int RouteController::modifyPhysicalNetwork(unsigned netId, const char* interface return 0; } +[[nodiscard]] static int modifyUidUnreachableRule(unsigned netId, uid_t uidStart, uid_t uidEnd, + uint32_t subPriority, bool add, + bool explicitSelect) { + if ((uidStart == INVALID_UID) || (uidEnd == INVALID_UID)) { + ALOGE("modifyUidUnreachableRule, invalid UIDs (%u, %u)", uidStart, uidEnd); + return -EUSERS; + } + + Fwmark fwmark; + Fwmark mask; + + fwmark.netId = netId; + mask.netId = FWMARK_NET_ID_MASK; + + fwmark.explicitlySelected = explicitSelect; + mask.explicitlySelected = true; + + // Access to this network is controlled by UID rules, not permission bits. + fwmark.permission = PERMISSION_NONE; + mask.permission = PERMISSION_NONE; + + return modifyIpRule(add ? RTM_NEWRULE : RTM_DELRULE, + explicitSelect ? (RULE_PRIORITY_UID_EXPLICIT_NETWORK + subPriority) + : (RULE_PRIORITY_UID_IMPLICIT_NETWORK + subPriority), + FR_ACT_UNREACHABLE, RT_TABLE_UNSPEC, fwmark.intValue, mask.intValue, + IIF_LOOPBACK, OIF_NONE, uidStart, uidEnd); +} + +[[nodiscard]] static int modifyUidDefaultUnreachableRule(uid_t uidStart, uid_t uidEnd, + uint32_t subPriority, bool add) { + if ((uidStart == INVALID_UID) || (uidEnd == INVALID_UID)) { + ALOGE("modifyUidDefaultUnreachableRule, invalid UIDs (%u, %u)", uidStart, uidEnd); + return -EUSERS; + } + + Fwmark fwmark; + Fwmark mask; + + fwmark.netId = NETID_UNSET; + mask.netId = FWMARK_NET_ID_MASK; + + // Access to this network is controlled by UID rules, not permission bits. + fwmark.permission = PERMISSION_NONE; + mask.permission = PERMISSION_NONE; + + return modifyIpRule(add ? RTM_NEWRULE : RTM_DELRULE, + RULE_PRIORITY_UID_DEFAULT_UNREACHABLE + subPriority, FR_ACT_UNREACHABLE, + RT_TABLE_UNSPEC, fwmark.intValue, mask.intValue, IIF_LOOPBACK, OIF_NONE, + uidStart, uidEnd); +} + +int RouteController::modifyUnreachableNetwork(unsigned netId, const UidRangeMap& uidRangeMap, + bool add) { + for (const auto& [subPriority, uidRanges] : uidRangeMap) { + for (const UidRangeParcel& range : uidRanges.getRanges()) { + if (int ret = modifyUidUnreachableRule(netId, range.start, range.stop, subPriority, add, + EXPLICIT)) { + return ret; + } + if (int ret = modifyUidUnreachableRule(netId, range.start, range.stop, subPriority, add, + IMPLICIT)) { + return ret; + } + if (int ret = modifyUidDefaultUnreachableRule(range.start, range.stop, subPriority, + add)) { + return ret; + } + } + } + + return 0; +} + [[nodiscard]] static int modifyRejectNonSecureNetworkRule(const UidRanges& uidRanges, bool add) { Fwmark fwmark; Fwmark mask; @@ -813,24 +950,27 @@ int RouteController::modifyPhysicalNetwork(unsigned netId, const char* interface } int RouteController::modifyVirtualNetwork(unsigned netId, const char* interface, - const UidRanges& uidRanges, bool secure, bool add, + const UidRangeMap& uidRangeMap, bool secure, bool add, bool modifyNonUidBasedRules) { uint32_t table = getRouteTableForInterface(interface); if (table == RT_TABLE_UNSPEC) { return -ESRCH; } - for (const UidRangeParcel& range : uidRanges.getRanges()) { - if (int ret = modifyVpnUidRangeRule(table, range.start, range.stop, secure, add)) { - return ret; - } - if (int ret = modifyExplicitNetworkRule(netId, table, PERMISSION_NONE, range.start, - range.stop, add)) { - return ret; - } - if (int ret = modifyOutputInterfaceRules(interface, table, PERMISSION_NONE, range.start, - range.stop, add)) { - return ret; + for (const auto& [subPriority, uidRanges] : uidRangeMap) { + for (const UidRangeParcel& range : uidRanges.getRanges()) { + if (int ret = modifyVpnUidRangeRule(table, range.start, range.stop, subPriority, secure, + add)) { + return ret; + } + if (int ret = modifyExplicitNetworkRule(netId, table, PERMISSION_NONE, range.start, + range.stop, subPriority, add)) { + return ret; + } + if (int ret = modifyOutputInterfaceRules(interface, table, PERMISSION_NONE, range.start, + range.stop, subPriority, add)) { + return ret; + } } } @@ -844,7 +984,8 @@ int RouteController::modifyVirtualNetwork(unsigned netId, const char* interface, if (int ret = modifyVpnSystemPermissionRule(netId, table, secure, add)) { return ret; } - return modifyExplicitNetworkRule(netId, table, PERMISSION_NONE, UID_ROOT, UID_ROOT, add); + return modifyExplicitNetworkRule(netId, table, PERMISSION_NONE, UID_ROOT, UID_ROOT, + UidRanges::DEFAULT_SUB_PRIORITY, add); } return 0; @@ -918,9 +1059,7 @@ int RouteController::modifyRoute(uint16_t action, uint16_t flags, const char* in return 0; } -void maybeModifyQdiscClsact(const char* interface, bool add) { - if (!bpf::isBpfSupported()) return; - +static void maybeModifyQdiscClsact(const char* interface, bool add) { // The clsact attaching of v4- tun interface is triggered by ClatdController::maybeStartBpf // because the clat is started before the v4- interface is added to the network and the // clat startup needs to add {in, e}gress filters. @@ -1047,8 +1186,10 @@ int RouteController::removeInterfaceFromLocalNetwork(unsigned netId, const char* } int RouteController::addInterfaceToPhysicalNetwork(unsigned netId, const char* interface, - Permission permission) { - if (int ret = modifyPhysicalNetwork(netId, interface, permission, ACTION_ADD)) { + Permission permission, + const UidRangeMap& uidRangeMap) { + if (int ret = modifyPhysicalNetwork(netId, interface, uidRangeMap, permission, ACTION_ADD, + MODIFY_NON_UID_BASED_RULES)) { return ret; } maybeModifyQdiscClsact(interface, ACTION_ADD); @@ -1057,8 +1198,10 @@ int RouteController::addInterfaceToPhysicalNetwork(unsigned netId, const char* i } int RouteController::removeInterfaceFromPhysicalNetwork(unsigned netId, const char* interface, - Permission permission) { - if (int ret = modifyPhysicalNetwork(netId, interface, permission, ACTION_DEL)) { + Permission permission, + const UidRangeMap& uidRangeMap) { + if (int ret = modifyPhysicalNetwork(netId, interface, uidRangeMap, permission, ACTION_DEL, + MODIFY_NON_UID_BASED_RULES)) { return ret; } if (int ret = flushRoutes(interface)) { @@ -1073,8 +1216,8 @@ int RouteController::removeInterfaceFromPhysicalNetwork(unsigned netId, const ch } int RouteController::addInterfaceToVirtualNetwork(unsigned netId, const char* interface, - bool secure, const UidRanges& uidRanges) { - if (int ret = modifyVirtualNetwork(netId, interface, uidRanges, secure, ACTION_ADD, + bool secure, const UidRangeMap& uidRangeMap) { + if (int ret = modifyVirtualNetwork(netId, interface, uidRangeMap, secure, ACTION_ADD, MODIFY_NON_UID_BASED_RULES)) { return ret; } @@ -1083,8 +1226,9 @@ int RouteController::addInterfaceToVirtualNetwork(unsigned netId, const char* in } int RouteController::removeInterfaceFromVirtualNetwork(unsigned netId, const char* interface, - bool secure, const UidRanges& uidRanges) { - if (int ret = modifyVirtualNetwork(netId, interface, uidRanges, secure, ACTION_DEL, + bool secure, + const UidRangeMap& uidRangeMap) { + if (int ret = modifyVirtualNetwork(netId, interface, uidRangeMap, secure, ACTION_DEL, MODIFY_NON_UID_BASED_RULES)) { return ret; } @@ -1098,11 +1242,16 @@ int RouteController::removeInterfaceFromVirtualNetwork(unsigned netId, const cha int RouteController::modifyPhysicalNetworkPermission(unsigned netId, const char* interface, Permission oldPermission, Permission newPermission) { + // Physical network rules either use permission bits or UIDs, but not both. + // So permission changes don't affect any UID-based rules. + UidRangeMap emptyUidRangeMap; // Add the new rules before deleting the old ones, to avoid race conditions. - if (int ret = modifyPhysicalNetwork(netId, interface, newPermission, ACTION_ADD)) { + if (int ret = modifyPhysicalNetwork(netId, interface, emptyUidRangeMap, newPermission, + ACTION_ADD, MODIFY_NON_UID_BASED_RULES)) { return ret; } - return modifyPhysicalNetwork(netId, interface, oldPermission, ACTION_DEL); + return modifyPhysicalNetwork(netId, interface, emptyUidRangeMap, oldPermission, ACTION_DEL, + MODIFY_NON_UID_BASED_RULES); } int RouteController::addUsersToRejectNonSecureNetworkRule(const UidRanges& uidRanges) { @@ -1114,14 +1263,14 @@ int RouteController::removeUsersFromRejectNonSecureNetworkRule(const UidRanges& } int RouteController::addUsersToVirtualNetwork(unsigned netId, const char* interface, bool secure, - const UidRanges& uidRanges) { - return modifyVirtualNetwork(netId, interface, uidRanges, secure, ACTION_ADD, + const UidRangeMap& uidRangeMap) { + return modifyVirtualNetwork(netId, interface, uidRangeMap, secure, ACTION_ADD, !MODIFY_NON_UID_BASED_RULES); } int RouteController::removeUsersFromVirtualNetwork(unsigned netId, const char* interface, - bool secure, const UidRanges& uidRanges) { - return modifyVirtualNetwork(netId, interface, uidRanges, secure, ACTION_DEL, + bool secure, const UidRangeMap& uidRangeMap) { + return modifyVirtualNetwork(netId, interface, uidRangeMap, secure, ACTION_DEL, !MODIFY_NON_UID_BASED_RULES); } @@ -1171,6 +1320,27 @@ int RouteController::removeVirtualNetworkFallthrough(unsigned vpnNetId, return modifyVpnFallthroughRule(RTM_DELRULE, vpnNetId, physicalInterface, permission); } +int RouteController::addUsersToPhysicalNetwork(unsigned netId, const char* interface, + const UidRangeMap& uidRangeMap) { + return modifyPhysicalNetwork(netId, interface, uidRangeMap, PERMISSION_NONE, ACTION_ADD, + !MODIFY_NON_UID_BASED_RULES); +} + +int RouteController::removeUsersFromPhysicalNetwork(unsigned netId, const char* interface, + const UidRangeMap& uidRangeMap) { + return modifyPhysicalNetwork(netId, interface, uidRangeMap, PERMISSION_NONE, ACTION_DEL, + !MODIFY_NON_UID_BASED_RULES); +} + +int RouteController::addUsersToUnreachableNetwork(unsigned netId, const UidRangeMap& uidRangeMap) { + return modifyUnreachableNetwork(netId, uidRangeMap, ACTION_ADD); +} + +int RouteController::removeUsersFromUnreachableNetwork(unsigned netId, + const UidRangeMap& uidRangeMap) { + return modifyUnreachableNetwork(netId, uidRangeMap, ACTION_DEL); +} + // Protects sInterfaceToTable. std::mutex RouteController::sInterfaceToTableLock; std::map<std::string, uint32_t> RouteController::sInterfaceToTable; diff --git a/server/RouteController.h b/server/RouteController.h index 656fc21d..38d2d621 100644 --- a/server/RouteController.h +++ b/server/RouteController.h @@ -17,6 +17,7 @@ #pragma once #include "NetdConstants.h" // IptablesTarget +#include "Network.h" // UidRangeMap #include "Permission.h" #include <android-base/thread_annotations.h> @@ -28,6 +29,53 @@ namespace android::net { +// clang-format off +const uint32_t RULE_PRIORITY_VPN_OVERRIDE_SYSTEM = 10000; +const uint32_t RULE_PRIORITY_VPN_OVERRIDE_OIF = 11000; +const uint32_t RULE_PRIORITY_VPN_OUTPUT_TO_LOCAL = 12000; +const uint32_t RULE_PRIORITY_SECURE_VPN = 13000; +const uint32_t RULE_PRIORITY_PROHIBIT_NON_VPN = 14000; +// Rules used when applications explicitly select a network that they have permission to use only +// because they are in the list of UID ranges for that network. +// +// Sockets from these UIDs will not match RULE_PRIORITY_EXPLICIT_NETWORK rules because they will +// not have the necessary permission bits in the fwmark. We cannot just give any socket on any of +// these networks the permission bits, because if the UID that created the socket loses access to +// the network, then the socket must not match any rule that selects that network. +const uint32_t RULE_PRIORITY_UID_EXPLICIT_NETWORK = 15000; +const uint32_t RULE_PRIORITY_EXPLICIT_NETWORK = 16000; +const uint32_t RULE_PRIORITY_OUTPUT_INTERFACE = 17000; +const uint32_t RULE_PRIORITY_LEGACY_SYSTEM = 18000; +const uint32_t RULE_PRIORITY_LEGACY_NETWORK = 19000; +const uint32_t RULE_PRIORITY_LOCAL_NETWORK = 20000; +const uint32_t RULE_PRIORITY_TETHERING = 21000; +// Implicit rules for sockets that connected on a given network because the network was the default +// network for the UID. +const uint32_t RULE_PRIORITY_UID_IMPLICIT_NETWORK = 22000; +const uint32_t RULE_PRIORITY_IMPLICIT_NETWORK = 23000; +const uint32_t RULE_PRIORITY_BYPASSABLE_VPN = 24000; +// reserved for RULE_PRIORITY_UID_VPN_FALLTHROUGH = 25000; +const uint32_t RULE_PRIORITY_VPN_FALLTHROUGH = 26000; +const uint32_t RULE_PRIORITY_UID_DEFAULT_NETWORK = 27000; +// Rule used when framework wants to disable default network from specified applications. There will +// be a small interval the same uid range exists in both UID_DEFAULT_UNREACHABLE and +// UID_DEFAULT_NETWORK when framework is switching user preferences. +// +// framework --> netd +// step 1: set uid to unreachable network +// step 2: remove uid from OEM-paid network list +// or +// step 1: add uid to OEM-paid network list +// step 2: remove uid from unreachable network +// +// The priority is lower than UID_DEFAULT_NETWORK. Otherwise, the app will be told by +// ConnectivityService that it has a network in step 1 of the second case. But if it tries to use +// the network, it will not work. That will potentially cause a user-visible error. +const uint32_t RULE_PRIORITY_UID_DEFAULT_UNREACHABLE = 28000; +const uint32_t RULE_PRIORITY_DEFAULT_NETWORK = 29000; +const uint32_t RULE_PRIORITY_UNREACHABLE = 32000; +// clang-format on + class UidRanges; class RouteController { @@ -59,25 +107,29 @@ public: [[nodiscard]] static int removeInterfaceFromLocalNetwork(unsigned netId, const char* interface); [[nodiscard]] static int addInterfaceToPhysicalNetwork(unsigned netId, const char* interface, - Permission permission); + Permission permission, + const UidRangeMap& uidRangeMap); [[nodiscard]] static int removeInterfaceFromPhysicalNetwork(unsigned netId, const char* interface, - Permission permission); + Permission permission, + const UidRangeMap& uidRangeMap); [[nodiscard]] static int addInterfaceToVirtualNetwork(unsigned netId, const char* interface, - bool secure, const UidRanges& uidRanges); + bool secure, + const UidRangeMap& uidRangeMap); [[nodiscard]] static int removeInterfaceFromVirtualNetwork(unsigned netId, const char* interface, bool secure, - const UidRanges& uidRanges); + const UidRangeMap& uidRangeMap); [[nodiscard]] static int modifyPhysicalNetworkPermission(unsigned netId, const char* interface, Permission oldPermission, Permission newPermission); [[nodiscard]] static int addUsersToVirtualNetwork(unsigned netId, const char* interface, - bool secure, const UidRanges& uidRanges); + bool secure, const UidRangeMap& uidRangeMap); [[nodiscard]] static int removeUsersFromVirtualNetwork(unsigned netId, const char* interface, - bool secure, const UidRanges& uidRanges); + bool secure, + const UidRangeMap& uidRangeMap); [[nodiscard]] static int addUsersToRejectNonSecureNetworkRule(const UidRanges& uidRanges); [[nodiscard]] static int removeUsersFromRejectNonSecureNetworkRule(const UidRanges& uidRanges); @@ -108,6 +160,18 @@ public: const char* physicalInterface, Permission permission); + [[nodiscard]] static int addUsersToPhysicalNetwork(unsigned netId, const char* interface, + const UidRangeMap& uidRangeMap); + + [[nodiscard]] static int removeUsersFromPhysicalNetwork(unsigned netId, const char* interface, + const UidRangeMap& uidRangeMap); + + [[nodiscard]] static int addUsersToUnreachableNetwork(unsigned netId, + const UidRangeMap& uidRangeMap); + + [[nodiscard]] static int removeUsersFromUnreachableNetwork(unsigned netId, + const UidRangeMap& uidRangeMap); + // For testing. static int (*iptablesRestoreCommandFunction)(IptablesTarget, const std::string&, const std::string&, std::string *); @@ -125,8 +189,10 @@ private: REQUIRES(sInterfaceToTableLock); static uint32_t getRouteTableForInterface(const char *interface) EXCLUDES(sInterfaceToTableLock); static int modifyDefaultNetwork(uint16_t action, const char* interface, Permission permission); - static int modifyPhysicalNetwork(unsigned netId, const char* interface, Permission permission, - bool add); + static int modifyPhysicalNetwork(unsigned netId, const char* interface, + const UidRangeMap& uidRangeMap, Permission permission, + bool add, bool modifyNonUidBasedRules); + static int modifyUnreachableNetwork(unsigned netId, const UidRangeMap& uidRangeMap, bool add); static int modifyRoute(uint16_t action, uint16_t flags, const char* interface, const char* destination, const char* nexthop, TableType tableType, int mtu); @@ -135,7 +201,7 @@ private: static int modifyVpnFallthroughRule(uint16_t action, unsigned vpnNetId, const char* physicalInterface, Permission permission); static int modifyVirtualNetwork(unsigned netId, const char* interface, - const UidRanges& uidRanges, bool secure, bool add, + const UidRangeMap& uidRangeMap, bool secure, bool add, bool modifyNonUidBasedRules); static void updateTableNamesFile() EXCLUDES(sInterfaceToTableLock); }; diff --git a/server/RouteControllerTest.cpp b/server/RouteControllerTest.cpp index fed15a3a..e85a83c7 100644 --- a/server/RouteControllerTest.cpp +++ b/server/RouteControllerTest.cpp @@ -45,10 +45,10 @@ TEST_F(RouteControllerTest, TestGetRulePriority) { // Expect a rule dump for these two families to contain at least the following priorities. for (int family : {AF_INET, AF_INET6 }) { std::set<uint32_t> expectedPriorities = { - 0, - 15000, // RULE_PRIORITY_LEGACY_SYSTEM - 16000, // RULE_PRIORITY_LEGACY_NETWORK - 32000, // RULE_PRIORITY_UNREACHABLE + 0, + RULE_PRIORITY_LEGACY_SYSTEM, + RULE_PRIORITY_LEGACY_NETWORK, + RULE_PRIORITY_UNREACHABLE, }; NetlinkDumpCallback callback = [&expectedPriorities] (const nlmsghdr *nlh) { diff --git a/server/SockDiag.cpp b/server/SockDiag.cpp index 44bda3b0..b3d9150a 100644 --- a/server/SockDiag.cpp +++ b/server/SockDiag.cpp @@ -31,6 +31,8 @@ #include <cinttypes> +#include <android-base/properties.h> +#include <android-base/stringprintf.h> #include <android-base/strings.h> #include <log/log.h> #include <netdutils/InternetAddresses.h> @@ -46,12 +48,22 @@ namespace android { +using android::base::StringPrintf; using netdutils::ScopedAddrinfo; using netdutils::Stopwatch; namespace net { namespace { +int getAdbPort() { + return android::base::GetIntProperty("service.adb.tcp.port", 0); +} + +bool isAdbSocket(const inet_diag_msg *msg, int adbPort) { + return adbPort > 0 && msg->id.idiag_sport == htons(adbPort) && + (msg->idiag_uid == AID_ROOT || msg->idiag_uid == AID_SHELL); +} + int checkError(int fd) { struct { nlmsghdr h; @@ -294,7 +306,7 @@ int SockDiag::sockDestroy(uint8_t proto, const inet_diag_msg *msg) { return ret; } -int SockDiag::destroySockets(uint8_t proto, int family, const char *addrstr) { +int SockDiag::destroySockets(uint8_t proto, int family, const char* addrstr, int ifindex) { if (!hasSocks()) { return -EBADFD; } @@ -303,28 +315,33 @@ int SockDiag::destroySockets(uint8_t proto, int family, const char *addrstr) { return ret; } - auto destroyAll = [] (uint8_t, const inet_diag_msg*) { return true; }; + auto destroyAll = [ifindex](uint8_t, const inet_diag_msg* msg) { + return ifindex == 0 || ifindex == (int)msg->id.idiag_if; + }; return readDiagMsg(proto, destroyAll); } -int SockDiag::destroySockets(const char *addrstr) { +int SockDiag::destroySockets(const char* addrstr, int ifindex) { Stopwatch s; mSocketsDestroyed = 0; - if (!strchr(addrstr, ':')) { - if (int ret = destroySockets(IPPROTO_TCP, AF_INET, addrstr)) { - ALOGE("Failed to destroy IPv4 sockets on %s: %s", addrstr, strerror(-ret)); + std::string where = addrstr; + if (ifindex) where += StringPrintf(" ifindex %d", ifindex); + + if (!strchr(addrstr, ':')) { // inet_ntop never returns something like ::ffff:192.0.2.1 + if (int ret = destroySockets(IPPROTO_TCP, AF_INET, addrstr, ifindex)) { + ALOGE("Failed to destroy IPv4 sockets on %s: %s", where.c_str(), strerror(-ret)); return ret; } } - if (int ret = destroySockets(IPPROTO_TCP, AF_INET6, addrstr)) { - ALOGE("Failed to destroy IPv6 sockets on %s: %s", addrstr, strerror(-ret)); + if (int ret = destroySockets(IPPROTO_TCP, AF_INET6, addrstr, ifindex)) { + ALOGE("Failed to destroy IPv6 sockets on %s: %s", where.c_str(), strerror(-ret)); return ret; } if (mSocketsDestroyed > 0) { - ALOGI("Destroyed %d sockets on %s in %" PRId64 "us", mSocketsDestroyed, addrstr, + ALOGI("Destroyed %d sockets on %s in %" PRId64 "us", mSocketsDestroyed, where.c_str(), s.timeTakenUs()); } @@ -414,7 +431,8 @@ int SockDiag::destroySockets(const UidRanges& uidRanges, const std::set<uid_t>& return msg != nullptr && uidRanges.hasUid(msg->idiag_uid) && skipUids.find(msg->idiag_uid) == skipUids.end() && - !(excludeLoopback && isLoopbackSocket(msg)); + !(excludeLoopback && isLoopbackSocket(msg)) && + !isAdbSocket(msg, getAdbPort()); }; iovec iov[] = { diff --git a/server/SockDiag.h b/server/SockDiag.h index 745c09e3..240e4e5d 100644 --- a/server/SockDiag.h +++ b/server/SockDiag.h @@ -70,7 +70,7 @@ class SockDiag { int sockDestroy(uint8_t proto, const inet_diag_msg *); // Destroys all sockets on the given IPv4 or IPv6 address. - int destroySockets(const char *addrstr); + int destroySockets(const char* addrstr, int ifindex); // Destroys all sockets for the given protocol and UID. int destroySockets(uint8_t proto, uid_t uid, bool excludeLoopback); // Destroys all "live" (CONNECTED, SYN_SENT, SYN_RECV) TCP sockets for the given UID ranges. @@ -91,7 +91,7 @@ class SockDiag { int mSocketsDestroyed; int sendDumpRequest(uint8_t proto, uint8_t family, uint8_t extensions, uint32_t states, iovec *iov, int iovcnt); - int destroySockets(uint8_t proto, int family, const char *addrstr); + int destroySockets(uint8_t proto, int family, const char* addrstr, int ifindex); int destroyLiveSockets(const DestroyFilter& destroy, const char *what, iovec *iov, int iovcnt); bool hasSocks() { return mSock != -1 && mWriteSock != -1; } void closeSocks() { close(mSock); close(mWriteSock); mSock = mWriteSock = -1; } diff --git a/server/SockDiagTest.cpp b/server/SockDiagTest.cpp index b79471a5..49601aa4 100644 --- a/server/SockDiagTest.cpp +++ b/server/SockDiagTest.cpp @@ -370,7 +370,7 @@ protected: int ret; switch (mode) { case ADDRESS: - ret = mSd.destroySockets("::1"); + ret = mSd.destroySockets("::1", 0 /* ifindex */); EXPECT_LE(0, ret) << ": Failed to destroy sockets on ::1: " << strerror(-ret); break; case UID: diff --git a/server/TetherController.cpp b/server/TetherController.cpp index 2445fb70..325fc413 100644 --- a/server/TetherController.cpp +++ b/server/TetherController.cpp @@ -60,14 +60,11 @@ namespace android { namespace net { -using android::base::Error; using android::base::Join; using android::base::Pipe; using android::base::Result; -using android::base::StringAppendF; using android::base::StringPrintf; using android::base::unique_fd; -using android::net::TetherOffloadRuleParcel; using android::netdutils::DumpWriter; using android::netdutils::ScopedIndent; using android::netdutils::statusFromErrno; @@ -84,10 +81,6 @@ constexpr const char kTcpBeLiberal[] = "/proc/sys/net/netfilter/nf_conntrack_tcp // Chosen to match AID_DNS_TETHER, as made "friendly" by fs_config_generator.py. constexpr const char kDnsmasqUsername[] = "dns_tether"; -// A value used by interface quota indicates there is no limit. -// Sync from frameworks/base/core/java/android/net/netstats/provider/NetworkStatsProvider.java -constexpr int64_t QUOTA_UNLIMITED = -1; - bool writeToFile(const char* filename, const char* value) { int fd = open(filename, O_WRONLY | O_CLOEXEC); if (fd < 0) { @@ -164,12 +157,13 @@ int TetherController::DnsmasqState::sendAllState(int daemonFd) const { } TetherController::TetherController() { + gLog.info("enter TetherController ctor"); if (inBpToolsMode()) { enableForwarding(BP_TOOLS_MODE); } else { setIpFwdEnabled(); } - maybeInitMaps(); + gLog.info("leave TetherController ctor"); } bool TetherController::setIpFwdEnabled() { @@ -202,27 +196,6 @@ bool TetherController::disableForwarding(const char* requester) { return setIpFwdEnabled(); } -void TetherController::maybeInitMaps() { - if (!bpf::isBpfSupported()) return; - - // Open BPF maps, ignoring errors because the device might not support BPF offload. - int fd = getTetherIngressMapFd(); - if (fd >= 0) { - mBpfIngressMap.reset(fd); - mBpfIngressMap.clear(); - } - fd = getTetherStatsMapFd(); - if (fd >= 0) { - mBpfStatsMap.reset(fd); - mBpfStatsMap.clear(); - } - fd = getTetherLimitMapFd(); - if (fd >= 0) { - mBpfLimitMap.reset(fd); - mBpfLimitMap.clear(); - } -} - const std::set<std::string>& TetherController::getIpfwdRequesterList() const { return mForwardingRequests; } @@ -606,8 +579,7 @@ int TetherController::enableNat(const char* intIface, const char* extIface) { } // add this if we are the first enabled nat for this upstream - bool firstDownstreamForThisUpstream = !isAnyForwardingEnabledOnUpstream(extIface); - if (firstDownstreamForThisUpstream) { + if (!isAnyForwardingEnabledOnUpstream(extIface)) { std::vector<std::string> v4Cmds = { "*nat", StringPrintf("-A %s -o %s -j MASQUERADE", LOCAL_NAT_POSTROUTING, extIface), @@ -633,7 +605,6 @@ int TetherController::enableNat(const char* intIface, const char* extIface) { return -ENODEV; } - if (firstDownstreamForThisUpstream) maybeStartBpf(extIface); return 0; } @@ -817,82 +788,10 @@ int TetherController::disableNat(const char* intIface, const char* extIface) { } setForwardRules(false, intIface, extIface); - if (!isAnyForwardingEnabledOnUpstream(extIface)) maybeStopBpf(extIface); if (!isAnyForwardingPairEnabled()) setDefaults(); return 0; } -namespace { -Result<void> validateOffloadRule(const TetherOffloadRuleParcel& rule) { - struct ethhdr hdr; - - if (rule.inputInterfaceIndex <= 0) { - return Error(ENODEV) << "Invalid input interface " << rule.inputInterfaceIndex; - } - if (rule.outputInterfaceIndex <= 0) { - return Error(ENODEV) << "Invalid output interface " << rule.inputInterfaceIndex; - } - if (rule.prefixLength != 128) { - return Error(EINVAL) << "Prefix length must be 128, not " << rule.prefixLength; - } - if (rule.destination.size() != sizeof(in6_addr)) { - return Error(EAFNOSUPPORT) << "Invalid IP address length " << rule.destination.size(); - } - if (rule.srcL2Address.size() != sizeof(hdr.h_source)) { - return Error(ENXIO) << "Invalid L2 src address length " << rule.srcL2Address.size(); - } - if (rule.dstL2Address.size() != sizeof(hdr.h_dest)) { - return Error(ENXIO) << "Invalid L2 dst address length " << rule.dstL2Address.size(); - } - if (rule.pmtu < IPV6_MIN_MTU || rule.pmtu > 0xFFFF) { - return Error(EINVAL) << "Invalid IPv6 path mtu " << rule.pmtu; - } - return Result<void>(); -} -} // namespace - -Result<void> TetherController::addOffloadRule(const TetherOffloadRuleParcel& rule) { - Result<void> res = validateOffloadRule(rule); - if (!res.ok()) return res; - - ethhdr hdr = { - .h_proto = htons(ETH_P_IPV6), - }; - memcpy(&hdr.h_dest, rule.dstL2Address.data(), sizeof(hdr.h_dest)); - memcpy(&hdr.h_source, rule.srcL2Address.data(), sizeof(hdr.h_source)); - - // Only downstream supported for now. - TetherIngressKey key = { - .iif = static_cast<uint32_t>(rule.inputInterfaceIndex), - .neigh6 = *(const in6_addr*)rule.destination.data(), - }; - - TetherIngressValue value = { - .oif = static_cast<uint32_t>(rule.outputInterfaceIndex), - .macHeader = hdr, - .pmtu = static_cast<uint16_t>(rule.pmtu), - }; - - return mBpfIngressMap.writeValue(key, value, BPF_ANY); -} - -Result<void> TetherController::removeOffloadRule(const TetherOffloadRuleParcel& rule) { - Result<void> res = validateOffloadRule(rule); - if (!res.ok()) return res; - - TetherIngressKey key = { - .iif = static_cast<uint32_t>(rule.inputInterfaceIndex), - .neigh6 = *(const in6_addr*)rule.destination.data(), - }; - - Result<void> ret = mBpfIngressMap.deleteValue(key); - - // Silently return success if the rule did not exist. - if (!ret.ok() && ret.error().code() == ENOENT) return {}; - - return ret; -} - void TetherController::addStats(TetherStatsList& statsList, const TetherStats& stats) { for (TetherStats& existing : statsList) { if (existing.addStatsIfMatch(stats)) { @@ -1028,180 +927,6 @@ StatusOr<TetherController::TetherStatsList> TetherController::getTetherStats() { return statsList; } -StatusOr<TetherController::TetherOffloadStatsList> TetherController::getTetherOffloadStats() { - TetherOffloadStatsList statsList; - - const auto processTetherStats = [&statsList](const uint32_t& key, const TetherStatsValue& value, - const BpfMap<uint32_t, TetherStatsValue>&) { - statsList.push_back({.ifIndex = static_cast<int>(key), - .rxBytes = static_cast<int64_t>(value.rxBytes), - .rxPackets = static_cast<int64_t>(value.rxPackets), - .txBytes = static_cast<int64_t>(value.txBytes), - .txPackets = static_cast<int64_t>(value.txPackets)}); - return Result<void>(); - }; - - auto ret = mBpfStatsMap.iterateWithValue(processTetherStats); - if (!ret.ok()) { - // Ignore error to return the remaining tether stats result. - ALOGE("Error processing tether stats from BPF maps: %s", ret.error().message().c_str()); - } - - return statsList; -} - -// Use UINT64_MAX (~0uLL) for unlimited. -Result<void> TetherController::setBpfLimit(uint32_t ifIndex, uint64_t limit) { - // The common case is an update, where the stats already exist, - // hence we read first, even though writing with BPF_NOEXIST - // first would make the code simpler. - uint64_t rxBytes, txBytes; - auto statsEntry = mBpfStatsMap.readValue(ifIndex); - - if (statsEntry.ok()) { - // Ok, there was a stats entry. - rxBytes = statsEntry.value().rxBytes; - txBytes = statsEntry.value().txBytes; - } else if (statsEntry.error().code() == ENOENT) { - // No stats entry - create one with zeroes. - TetherStatsValue stats = {}; - // This function is the *only* thing that can create entries. - auto ret = mBpfStatsMap.writeValue(ifIndex, stats, BPF_NOEXIST); - if (!ret.ok()) { - ALOGE("mBpfStatsMap.writeValue failure: %s", strerror(ret.error().code())); - return ret; - } - rxBytes = 0; - txBytes = 0; - } else { - // Other error while trying to get stats entry. - return statsEntry.error(); - } - - // rxBytes + txBytes won't overflow even at 5gbps for ~936 years. - uint64_t newLimit = rxBytes + txBytes + limit; - - // if adding limit (e.g., if limit is UINT64_MAX) caused overflow: clamp to 'infinity' - if (newLimit < rxBytes + txBytes) newLimit = ~0uLL; - - auto ret = mBpfLimitMap.writeValue(ifIndex, newLimit, BPF_ANY); - if (!ret.ok()) { - ALOGE("mBpfLimitMap.writeValue failure: %s", strerror(ret.error().code())); - return ret; - } - - return {}; -} - -void TetherController::maybeStartBpf(const char* extIface) { - if (!bpf::isBpfSupported()) return; - - // TODO: perhaps ignore IPv4-only interface because IPv4 traffic downstream is not supported. - int ifIndex = if_nametoindex(extIface); - if (!ifIndex) { - ALOGE("Fail to get index for interface %s", extIface); - return; - } - - auto isEthernet = android::net::isEthernet(extIface); - if (!isEthernet.ok()) { - ALOGE("isEthernet(%s[%d]) failure: %s", extIface, ifIndex, - isEthernet.error().message().c_str()); - return; - } - - int rv = getTetherIngressProgFd(isEthernet.value()); - if (rv < 0) { - ALOGE("getTetherIngressProgFd(%d) failure: %s", isEthernet.value(), strerror(-rv)); - return; - } - unique_fd tetherProgFd(rv); - - rv = tcFilterAddDevIngressTether(ifIndex, tetherProgFd, isEthernet.value()); - if (rv) { - ALOGE("tcFilterAddDevIngressTether(%d[%s], %d) failure: %s", ifIndex, extIface, - isEthernet.value(), strerror(-rv)); - return; - } -} - -void TetherController::maybeStopBpf(const char* extIface) { - if (!bpf::isBpfSupported()) return; - - // TODO: perhaps ignore IPv4-only interface because IPv4 traffic downstream is not supported. - int ifIndex = if_nametoindex(extIface); - if (!ifIndex) { - ALOGE("Fail to get index for interface %s", extIface); - return; - } - - int rv = tcFilterDelDevIngressTether(ifIndex); - if (rv < 0) { - ALOGE("tcFilterDelDevIngressTether(%d[%s]) failure: %s", ifIndex, extIface, strerror(-rv)); - } -} - -int TetherController::setTetherOffloadInterfaceQuota(int ifIndex, int64_t maxBytes) { - if (!mBpfStatsMap.isValid() || !mBpfLimitMap.isValid()) return -ENOTSUP; - - if (ifIndex <= 0) return -ENODEV; - - if (maxBytes < QUOTA_UNLIMITED) { - ALOGE("Invalid bytes value. Must be -1 (unlimited) or 0..max_int64."); - return -ERANGE; - } - - // Note that a value of unlimited quota (-1) indicates simply max_uint64. - const auto res = setBpfLimit(static_cast<uint32_t>(ifIndex), static_cast<uint64_t>(maxBytes)); - if (!res.ok()) { - ALOGE("Fail to set quota %" PRId64 " for interface index %d: %s", maxBytes, ifIndex, - strerror(res.error().code())); - return -res.error().code(); - } - - return 0; -} - -Result<TetherController::TetherOffloadStats> TetherController::getAndClearTetherOffloadStats( - int ifIndex) { - if (!mBpfStatsMap.isValid() || !mBpfLimitMap.isValid()) return Error(ENOTSUP); - - if (ifIndex <= 0) { - return Error(ENODEV) << "Invalid interface " << ifIndex; - } - - // getAndClearTetherOffloadStats is called after all offload rules have already been deleted - // for the given upstream interface. Before starting to do cleanup stuff in this function, use - // synchronizeKernelRCU to make sure that all the current running eBPF programs are finished - // on all CPUs, especially the unfinished packet processing. After synchronizeKernelRCU - // returned, we can safely read or delete on the stats map or the limit map. - if (int res = bpf::synchronizeKernelRCU()) { - // Error log but don't return error. Do as much cleanup as possible. - ALOGE("synchronize_rcu() failed: %s", strerror(-res)); - } - - const auto stats = mBpfStatsMap.readValue(ifIndex); - if (!stats.ok()) { - return Error(stats.error().code()) << "Fail to get stats for interface index " << ifIndex; - } - - auto res = mBpfStatsMap.deleteValue(ifIndex); - if (!res.ok()) { - return Error(res.error().code()) << "Fail to delete stats for interface index " << ifIndex; - } - - res = mBpfLimitMap.deleteValue(ifIndex); - if (!res.ok()) { - return Error(res.error().code()) << "Fail to delete limit for interface index " << ifIndex; - } - - return TetherOffloadStats{.ifIndex = static_cast<int>(ifIndex), - .rxBytes = static_cast<int64_t>(stats.value().rxBytes), - .rxPackets = static_cast<int64_t>(stats.value().rxPackets), - .txBytes = static_cast<int64_t>(stats.value().txBytes), - .txPackets = static_cast<int64_t>(stats.value().txPackets)}; -} - void TetherController::dumpIfaces(DumpWriter& dw) { dw.println("Interface pairs:"); @@ -1212,90 +937,6 @@ void TetherController::dumpIfaces(DumpWriter& dw) { } } -namespace { - -std::string l2ToString(const uint8_t* addr, size_t len) { - std::string str; - - if (len == 0) return str; - - StringAppendF(&str, "%02x", addr[0]); - for (size_t i = 1; i < len; i++) { - StringAppendF(&str, ":%02x", addr[i]); - } - - return str; -} - -} // namespace - -void TetherController::dumpBpf(DumpWriter& dw) { - if (!mBpfIngressMap.isValid() || !mBpfStatsMap.isValid() || !mBpfLimitMap.isValid()) { - dw.println("BPF not supported"); - return; - } - - dw.println("BPF ingress map: iif(iface) v6addr -> oif(iface) srcmac dstmac ethertype [pmtu]"); - const auto printIngressMap = [&dw](const TetherIngressKey& key, const TetherIngressValue& value, - const BpfMap<TetherIngressKey, TetherIngressValue>&) { - char addr[INET6_ADDRSTRLEN]; - std::string src = l2ToString(value.macHeader.h_source, sizeof(value.macHeader.h_source)); - std::string dst = l2ToString(value.macHeader.h_dest, sizeof(value.macHeader.h_dest)); - inet_ntop(AF_INET6, &key.neigh6, addr, sizeof(addr)); - - char iifStr[IFNAMSIZ] = "?"; - char oifStr[IFNAMSIZ] = "?"; - if_indextoname(key.iif, iifStr); - if_indextoname(value.oif, oifStr); - dw.println("%u(%s) %s -> %u(%s) %s %s %04x [%u]", key.iif, iifStr, addr, value.oif, oifStr, - src.c_str(), dst.c_str(), ntohs(value.macHeader.h_proto), value.pmtu); - - return Result<void>(); - }; - - dw.incIndent(); - auto ret = mBpfIngressMap.iterateWithValue(printIngressMap); - if (!ret.ok()) { - dw.println("Error printing BPF ingress map: %s", ret.error().message().c_str()); - } - dw.decIndent(); - - dw.println("BPF stats (downlink): iif(iface) -> packets bytes errors"); - const auto printStatsMap = [&dw](const uint32_t& key, const TetherStatsValue& value, - const BpfMap<uint32_t, TetherStatsValue>&) { - char iifStr[IFNAMSIZ] = "?"; - if_indextoname(key, iifStr); - dw.println("%u(%s) -> %" PRIu64 " %" PRIu64 " %" PRIu64, key, iifStr, value.rxPackets, - value.rxBytes, value.rxErrors); - - return Result<void>(); - }; - - dw.incIndent(); - ret = mBpfStatsMap.iterateWithValue(printStatsMap); - if (!ret.ok()) { - dw.println("Error printing BPF stats map: %s", ret.error().message().c_str()); - } - dw.decIndent(); - - dw.println("BPF limit: iif(iface) -> bytes"); - const auto printLimitMap = [&dw](const uint32_t& key, const uint64_t& value, - const BpfMap<uint32_t, uint64_t>&) { - char iifStr[IFNAMSIZ] = "?"; - if_indextoname(key, iifStr); - dw.println("%u(%s) -> %" PRIu64, key, iifStr, value); - - return Result<void>(); - }; - - dw.incIndent(); - ret = mBpfLimitMap.iterateWithValue(printLimitMap); - if (!ret.ok()) { - dw.println("Error printing BPF limit map: %s", ret.error().message().c_str()); - } - dw.decIndent(); -} - void TetherController::dump(DumpWriter& dw) { std::lock_guard guard(lock); @@ -1313,8 +954,6 @@ void TetherController::dump(DumpWriter& dw) { } dumpIfaces(dw); - dw.println(""); - dumpBpf(dw); } } // namespace net diff --git a/server/TetherController.h b/server/TetherController.h index bcd2ee67..585686a0 100644 --- a/server/TetherController.h +++ b/server/TetherController.h @@ -72,11 +72,6 @@ class TetherController { int sendAllState(int daemonFd) const; } mDnsmasqState{}; - // BPF maps, initialized by maybeInitMaps. - bpf::BpfMap<TetherIngressKey, TetherIngressValue> mBpfIngressMap; - bpf::BpfMap<uint32_t, TetherStatsValue> mBpfStatsMap; - bpf::BpfMap<uint32_t, uint64_t> mBpfLimitMap; - public: TetherController(); ~TetherController() = default; @@ -105,11 +100,6 @@ class TetherController { int disableNat(const char* intIface, const char* extIface); int setupIptablesHooks(); - base::Result<void> addOffloadRule(const TetherOffloadRuleParcel& rule); - base::Result<void> removeOffloadRule(const TetherOffloadRuleParcel& rule); - - int setTetherOffloadInterfaceQuota(int ifIndex, int64_t maxBytes); - class TetherStats { public: TetherStats() = default; @@ -138,20 +128,9 @@ class TetherController { } }; - struct TetherOffloadStats { - int ifIndex; - int64_t rxBytes; - int64_t rxPackets; - int64_t txBytes; - int64_t txPackets; - }; - typedef std::vector<TetherStats> TetherStatsList; - typedef std::vector<TetherOffloadStats> TetherOffloadStatsList; netdutils::StatusOr<TetherStatsList> getTetherStats(); - netdutils::StatusOr<TetherOffloadStatsList> getTetherOffloadStats(); - base::Result<TetherOffloadStats> getAndClearTetherOffloadStats(int ifIndex); /* * extraProcessingInfo: contains raw parsed data, and error info. @@ -173,7 +152,6 @@ class TetherController { void dump(netdutils::DumpWriter& dw); void dumpIfaces(netdutils::DumpWriter& dw); - void dumpBpf(netdutils::DumpWriter& dw); private: bool setIpFwdEnabled(); @@ -195,11 +173,6 @@ class TetherController { int setForwardRules(bool set, const char *intIface, const char *extIface); int setTetherCountingRules(bool add, const char *intIface, const char *extIface); - base::Result<void> setBpfLimit(uint32_t ifIndex, uint64_t limit); - void maybeInitMaps(); - void maybeStartBpf(const char* extIface); - void maybeStopBpf(const char* extIface); - static void addStats(TetherStatsList& statsList, const TetherStats& stats); // For testing. diff --git a/server/TetherControllerTest.cpp b/server/TetherControllerTest.cpp index db9892f9..e700f605 100644 --- a/server/TetherControllerTest.cpp +++ b/server/TetherControllerTest.cpp @@ -38,28 +38,13 @@ using android::base::Join; using android::base::StringPrintf; -using android::bpf::BpfMap; using android::netdutils::StatusOr; -using ::testing::Contains; using TetherStats = android::net::TetherController::TetherStats; using TetherStatsList = android::net::TetherController::TetherStatsList; -using TetherOffloadStats = android::net::TetherController::TetherOffloadStats; -using TetherOffloadStatsList = android::net::TetherController::TetherOffloadStatsList; namespace android { namespace net { -constexpr int TEST_MAP_SIZE = 10; - -// Comparison for TetherOffloadStats. Need to override operator== because class TetherOffloadStats -// doesn't have one. -// TODO: once C++20 is used, use default operator== in TetherOffloadStats and remove the overriding -// here. -bool operator==(const TetherOffloadStats& lhs, const TetherOffloadStats& rhs) { - return lhs.ifIndex == rhs.ifIndex && lhs.rxBytes == rhs.rxBytes && lhs.txBytes == rhs.txBytes && - lhs.rxPackets == rhs.rxPackets && lhs.txPackets == rhs.txPackets; -} - class TetherControllerTest : public IptablesBaseTest { public: TetherControllerTest() { @@ -68,38 +53,6 @@ public: protected: TetherController mTetherCtrl; - BpfMap<uint32_t, TetherStatsValue> mFakeTetherStatsMap{BPF_MAP_TYPE_HASH, TEST_MAP_SIZE}; - BpfMap<uint32_t, uint64_t> mFakeTetherLimitMap{BPF_MAP_TYPE_HASH, TEST_MAP_SIZE}; - - void SetUp() { - SKIP_IF_BPF_NOT_SUPPORTED; - - ASSERT_TRUE(mFakeTetherStatsMap.isValid()); - ASSERT_TRUE(mFakeTetherLimitMap.isValid()); - - mTetherCtrl.mBpfStatsMap = mFakeTetherStatsMap; - ASSERT_TRUE(mTetherCtrl.mBpfStatsMap.isValid()); - mTetherCtrl.mBpfLimitMap = mFakeTetherLimitMap; - ASSERT_TRUE(mTetherCtrl.mBpfLimitMap.isValid()); - } - - std::string toString(const TetherOffloadStatsList& statsList) { - std::string result; - for (const auto& stats : statsList) { - result += StringPrintf("%d, %" PRId64 ", %" PRId64 ", %" PRId64 ", %" PRId64 "\n", - stats.ifIndex, stats.rxBytes, stats.rxPackets, stats.txBytes, - stats.txPackets); - } - return result; - } - - void updateMaps(uint32_t ifaceIndex, uint64_t rxBytes, uint64_t rxPackets, uint64_t txBytes, - uint64_t txPackets) { - // {rx, tx}Errors in |tetherStats| are set zero because getTetherStats doesn't use them. - const TetherStatsValue tetherStats = {rxPackets, rxBytes, 0 /*unused*/, - txPackets, txBytes, 0 /*unused*/}; - ASSERT_RESULT_OK(mFakeTetherStatsMap.writeValue(ifaceIndex, tetherStats, BPF_ANY)); - }; int setDefaults() { return mTetherCtrl.setDefaults(); @@ -485,64 +438,5 @@ TEST_F(TetherControllerTest, TestGetTetherStats) { EXPECT_TRUE(std::equal(expectedError.rbegin(), expectedError.rend(), err.rbegin())); } -TEST_F(TetherControllerTest, TestTetherOffloadGetStats) { - SKIP_IF_BPF_NOT_SUPPORTED; - - updateMaps(101, 100, 10, 200, 20); - updateMaps(102, 300, 30, 400, 40); - const TetherOffloadStats expected0{101, 100, 10, 200, 20}; - const TetherOffloadStats expected1{102, 300, 30, 400, 40}; - - const StatusOr<TetherOffloadStatsList> result = mTetherCtrl.getTetherOffloadStats(); - ASSERT_OK(result); - const TetherOffloadStatsList& actual = result.value(); - ASSERT_EQ(2U, actual.size()); - EXPECT_THAT(actual, Contains(expected0)) << toString(actual); - EXPECT_THAT(actual, Contains(expected1)) << toString(actual); - clearIptablesRestoreOutput(); -} - -TEST_F(TetherControllerTest, TestTetherOffloadSetQuota) { - SKIP_IF_BPF_NOT_SUPPORTED; - - const uint32_t ifindex = 100; - const uint64_t minQuota = 0; - const uint64_t maxQuota = std::numeric_limits<int64_t>::max(); - const uint64_t infinityQuota = std::numeric_limits<uint64_t>::max(); - - // Create a stats entry with zeroes in the first time set limit. - ASSERT_EQ(0, mTetherCtrl.setTetherOffloadInterfaceQuota(ifindex, minQuota)); - const StatusOr<TetherOffloadStatsList> result = mTetherCtrl.getTetherOffloadStats(); - ASSERT_OK(result); - const TetherOffloadStatsList& actual = result.value(); - ASSERT_EQ(1U, actual.size()); - EXPECT_THAT(actual, Contains(TetherOffloadStats{ifindex, 0, 0, 0, 0})) << toString(actual); - - // Verify the quota with the boundary {min, max, infinity}. - const uint64_t rxBytes = 1000; - const uint64_t txBytes = 2000; - updateMaps(ifindex, rxBytes, 0 /*unused*/, txBytes, 0 /*unused*/); - - for (const uint64_t quota : {minQuota, maxQuota, infinityQuota}) { - ASSERT_EQ(0, mTetherCtrl.setTetherOffloadInterfaceQuota(ifindex, quota)); - base::Result<uint64_t> result = mFakeTetherLimitMap.readValue(ifindex); - ASSERT_RESULT_OK(result); - - const uint64_t expectedQuota = - (quota == infinityQuota) ? infinityQuota : quota + rxBytes + txBytes; - EXPECT_EQ(expectedQuota, result.value()); - } - - // The valid range of interface index is 1..max_int64. - const uint32_t invalidIfindex = 0; - int ret = mTetherCtrl.setTetherOffloadInterfaceQuota(invalidIfindex /*bad*/, infinityQuota); - ASSERT_EQ(-ENODEV, ret); - - // The valid range of quota is 0..max_int64 or -1 (unlimited). - const uint64_t invalidQuota = std::numeric_limits<int64_t>::min(); - ret = mTetherCtrl.setTetherOffloadInterfaceQuota(ifindex, invalidQuota /*bad*/); - ASSERT_EQ(-ERANGE, ret); -} - } // namespace net } // namespace android diff --git a/server/TrafficController.cpp b/server/TrafficController.cpp index 3839962f..1f678cbf 100644 --- a/server/TrafficController.cpp +++ b/server/TrafficController.cpp @@ -52,13 +52,16 @@ #include "netdutils/DumpWriter.h" #include "qtaguid/qtaguid.h" -using namespace android::bpf; // NOLINT(google-build-using-namespace): grandfathered - namespace android { namespace net { using base::StringPrintf; using base::unique_fd; +using bpf::getSocketCookie; +using bpf::NONEXISTENT_COOKIE; +using bpf::OVERFLOW_COUNTERSET; +using bpf::retrieveProgram; +using bpf::synchronizeKernelRCU; using netdutils::DumpWriter; using netdutils::extract; using netdutils::ScopedIndent; @@ -100,6 +103,7 @@ const std::string uidMatchTypeToString(uint8_t match) { FLAG_MSG_TRANS(matchType, DOZABLE_MATCH, match); FLAG_MSG_TRANS(matchType, STANDBY_MATCH, match); FLAG_MSG_TRANS(matchType, POWERSAVE_MATCH, match); + FLAG_MSG_TRANS(matchType, RESTRICTED_MATCH, match); FLAG_MSG_TRANS(matchType, IIF_MATCH, match); if (match) { return StringPrintf("Unknown match: %u", match); @@ -167,14 +171,11 @@ StatusOr<std::unique_ptr<NetlinkListenerInterface>> TrafficController::makeSkDes } TrafficController::TrafficController() - : mBpfEnabled(isBpfSupported()), - mPerUidStatsEntriesLimit(PER_UID_STATS_ENTRIES_LIMIT), + : mPerUidStatsEntriesLimit(PER_UID_STATS_ENTRIES_LIMIT), mTotalUidStatsEntriesLimit(TOTAL_UID_STATS_ENTRIES_LIMIT) {} TrafficController::TrafficController(uint32_t perUidLimit, uint32_t totalLimit) - : mBpfEnabled(isBpfSupported()), - mPerUidStatsEntriesLimit(perUidLimit), - mTotalUidStatsEntriesLimit(totalLimit) {} + : mPerUidStatsEntriesLimit(perUidLimit), mTotalUidStatsEntriesLimit(totalLimit) {} Status TrafficController::initMaps() { std::lock_guard guard(mMutex); @@ -247,10 +248,6 @@ static Status initPrograms() { } Status TrafficController::start() { - if (!mBpfEnabled) { - return netdutils::status::ok; - } - /* When netd restarts from a crash without total system reboot, the program * is still attached to the cgroup, detach it so the program can be freed * and we can load and attach new program into the target cgroup. @@ -314,11 +311,6 @@ int TrafficController::tagSocket(int sockFd, uint32_t tag, uid_t uid, uid_t call return -EPERM; } - if (!mBpfEnabled) { - if (legacy_tagSocket(sockFd, tag, uid)) return -errno; - return 0; - } - uint64_t sock_cookie = getSocketCookie(sockFd); if (sock_cookie == NONEXISTENT_COOKIE) return -errno; UidTagValue newKey = {.uid = (uint32_t)uid, .tag = tag}; @@ -382,10 +374,6 @@ int TrafficController::tagSocket(int sockFd, uint32_t tag, uid_t uid, uid_t call int TrafficController::untagSocket(int sockFd) { std::lock_guard guard(mMutex); - if (!mBpfEnabled) { - if (legacy_untagSocket(sockFd)) return -errno; - return 0; - } uint64_t sock_cookie = getSocketCookie(sockFd); if (sock_cookie == NONEXISTENT_COOKIE) return -errno; @@ -403,11 +391,6 @@ int TrafficController::setCounterSet(int counterSetNum, uid_t uid, uid_t calling std::lock_guard guard(mMutex); if (!hasUpdateDeviceStatsPermission(callingUid)) return -EPERM; - if (!mBpfEnabled) { - if (legacy_setCounterSet(counterSetNum, uid)) return -errno; - return 0; - } - // The default counter set for all uid is 0, so deleting the current counterset for that uid // will automatically set it to 0. if (counterSetNum == 0) { @@ -436,11 +419,6 @@ int TrafficController::deleteTagData(uint32_t tag, uid_t uid, uid_t callingUid) std::lock_guard guard(mMutex); if (!hasUpdateDeviceStatsPermission(callingUid)) return -EPERM; - if (!mBpfEnabled) { - if (legacy_deleteTagData(tag, uid)) return -errno; - return 0; - } - // First we go through the cookieTagMap to delete the target uid tag combination. Or delete all // the tags related to the uid if the tag is 0. const auto deleteMatchedCookieEntries = [uid, tag](const uint64_t& key, @@ -501,8 +479,6 @@ int TrafficController::deleteTagData(uint32_t tag, uid_t uid, uid_t callingUid) } int TrafficController::addInterface(const char* name, uint32_t ifaceIndex) { - if (!mBpfEnabled) return 0; - IfaceValue iface; if (ifaceIndex == 0) { ALOGE("Unknown interface %s(%d)", name, ifaceIndex); @@ -521,10 +497,10 @@ int TrafficController::addInterface(const char* name, uint32_t ifaceIndex) { Status TrafficController::updateOwnerMapEntry(UidOwnerMatchType match, uid_t uid, FirewallRule rule, FirewallType type) { std::lock_guard guard(mMutex); - if ((rule == ALLOW && type == WHITELIST) || (rule == DENY && type == BLACKLIST)) { - RETURN_IF_NOT_OK(addRule(mUidOwnerMap, uid, match)); - } else if ((rule == ALLOW && type == BLACKLIST) || (rule == DENY && type == WHITELIST)) { - RETURN_IF_NOT_OK(removeRule(mUidOwnerMap, uid, match)); + if ((rule == ALLOW && type == ALLOWLIST) || (rule == DENY && type == DENYLIST)) { + RETURN_IF_NOT_OK(addRule(uid, match)); + } else if ((rule == ALLOW && type == DENYLIST) || (rule == DENY && type == ALLOWLIST)) { + RETURN_IF_NOT_OK(removeRule(uid, match)); } else { //Cannot happen. return statusFromErrno(EINVAL, ""); @@ -532,29 +508,17 @@ Status TrafficController::updateOwnerMapEntry(UidOwnerMatchType match, uid_t uid return netdutils::status::ok; } -UidOwnerMatchType TrafficController::jumpOpToMatch(BandwidthController::IptJumpOp jumpHandling) { - switch (jumpHandling) { - case BandwidthController::IptJumpReject: - return PENALTY_BOX_MATCH; - case BandwidthController::IptJumpReturn: - return HAPPY_BOX_MATCH; - case BandwidthController::IptJumpNoAdd: - return NO_MATCH; - } -} - -Status TrafficController::removeRule(BpfMap<uint32_t, UidOwnerValue>& map, uint32_t uid, - UidOwnerMatchType match) { - auto oldMatch = map.readValue(uid); +Status TrafficController::removeRule(uint32_t uid, UidOwnerMatchType match) { + auto oldMatch = mUidOwnerMap.readValue(uid); if (oldMatch.ok()) { UidOwnerValue newMatch = { .iif = (match == IIF_MATCH) ? 0 : oldMatch.value().iif, .rule = static_cast<uint8_t>(oldMatch.value().rule & ~match), }; if (newMatch.rule == 0) { - RETURN_IF_NOT_OK(map.deleteValue(uid)); + RETURN_IF_NOT_OK(mUidOwnerMap.deleteValue(uid)); } else { - RETURN_IF_NOT_OK(map.writeValue(uid, newMatch, BPF_ANY)); + RETURN_IF_NOT_OK(mUidOwnerMap.writeValue(uid, newMatch, BPF_ANY)); } } else { return statusFromErrno(ENOENT, StringPrintf("uid: %u does not exist in map", uid)); @@ -562,55 +526,42 @@ Status TrafficController::removeRule(BpfMap<uint32_t, UidOwnerValue>& map, uint3 return netdutils::status::ok; } -Status TrafficController::addRule(BpfMap<uint32_t, UidOwnerValue>& map, uint32_t uid, - UidOwnerMatchType match, uint32_t iif) { +Status TrafficController::addRule(uint32_t uid, UidOwnerMatchType match, uint32_t iif) { // iif should be non-zero if and only if match == MATCH_IIF if (match == IIF_MATCH && iif == 0) { return statusFromErrno(EINVAL, "Interface match must have nonzero interface index"); } else if (match != IIF_MATCH && iif != 0) { return statusFromErrno(EINVAL, "Non-interface match must have zero interface index"); } - auto oldMatch = map.readValue(uid); + auto oldMatch = mUidOwnerMap.readValue(uid); if (oldMatch.ok()) { UidOwnerValue newMatch = { .iif = iif ? iif : oldMatch.value().iif, .rule = static_cast<uint8_t>(oldMatch.value().rule | match), }; - RETURN_IF_NOT_OK(map.writeValue(uid, newMatch, BPF_ANY)); + RETURN_IF_NOT_OK(mUidOwnerMap.writeValue(uid, newMatch, BPF_ANY)); } else { UidOwnerValue newMatch = { .iif = iif, .rule = static_cast<uint8_t>(match), }; - RETURN_IF_NOT_OK(map.writeValue(uid, newMatch, BPF_ANY)); + RETURN_IF_NOT_OK(mUidOwnerMap.writeValue(uid, newMatch, BPF_ANY)); } return netdutils::status::ok; } -Status TrafficController::updateUidOwnerMap(const std::vector<std::string>& appStrUids, - BandwidthController::IptJumpOp jumpHandling, +Status TrafficController::updateUidOwnerMap(const std::vector<uint32_t>& appUids, + UidOwnerMatchType matchType, BandwidthController::IptOp op) { std::lock_guard guard(mMutex); - UidOwnerMatchType match = jumpOpToMatch(jumpHandling); - if (match == NO_MATCH) { - return statusFromErrno( - EINVAL, StringPrintf("invalid IptJumpOp: %d, command: %d", jumpHandling, match)); - } - for (const auto& appStrUid : appStrUids) { - char* endPtr; - long uid = strtol(appStrUid.c_str(), &endPtr, 10); - if ((errno == ERANGE && (uid == LONG_MAX || uid == LONG_MIN)) || - (endPtr == appStrUid.c_str()) || (*endPtr != '\0')) { - return statusFromErrno(errno, "invalid uid string:" + appStrUid); - } - + for (uint32_t uid : appUids) { if (op == BandwidthController::IptOpDelete) { - RETURN_IF_NOT_OK(removeRule(mUidOwnerMap, uid, match)); + RETURN_IF_NOT_OK(removeRule(uid, matchType)); } else if (op == BandwidthController::IptOpInsert) { - RETURN_IF_NOT_OK(addRule(mUidOwnerMap, uid, match)); + RETURN_IF_NOT_OK(addRule(uid, matchType)); } else { // Cannot happen. - return statusFromErrno(EINVAL, StringPrintf("invalid IptOp: %d, %d", op, match)); + return statusFromErrno(EINVAL, StringPrintf("invalid IptOp: %d, %d", op, matchType)); } } return netdutils::status::ok; @@ -618,10 +569,6 @@ Status TrafficController::updateUidOwnerMap(const std::vector<std::string>& appS int TrafficController::changeUidOwnerRule(ChildChain chain, uid_t uid, FirewallRule rule, FirewallType type) { - if (!mBpfEnabled) { - ALOGE("bpf is not set up, should use iptables rule"); - return -ENOSYS; - } Status res; switch (chain) { case DOZABLE: @@ -633,6 +580,9 @@ int TrafficController::changeUidOwnerRule(ChildChain chain, uid_t uid, FirewallR case POWERSAVE: res = updateOwnerMapEntry(POWERSAVE_MATCH, uid, rule, type); break; + case RESTRICTED: + res = updateOwnerMapEntry(RESTRICTED_MATCH, uid, rule, type); + break; case NONE: default: return -EINVAL; @@ -660,28 +610,24 @@ Status TrafficController::replaceRulesInMap(const UidOwnerMatchType match, RETURN_IF_NOT_OK(mUidOwnerMap.iterate(getUidsToDelete)); for(auto uid : uidsToDelete) { - RETURN_IF_NOT_OK(removeRule(mUidOwnerMap, uid, match)); + RETURN_IF_NOT_OK(removeRule(uid, match)); } for (auto uid : uids) { - RETURN_IF_NOT_OK(addRule(mUidOwnerMap, uid, match)); + RETURN_IF_NOT_OK(addRule(uid, match)); } return netdutils::status::ok; } Status TrafficController::addUidInterfaceRules(const int iif, const std::vector<int32_t>& uidsToAdd) { - if (!mBpfEnabled) { - ALOGW("UID ingress interface filtering not possible without BPF owner match"); - return statusFromErrno(EOPNOTSUPP, "eBPF not supported"); - } if (!iif) { return statusFromErrno(EINVAL, "Interface rule must specify interface"); } std::lock_guard guard(mMutex); for (auto uid : uidsToAdd) { - netdutils::Status result = addRule(mUidOwnerMap, uid, IIF_MATCH, iif); + netdutils::Status result = addRule(uid, IIF_MATCH, iif); if (!isOk(result)) { ALOGW("addRule failed(%d): uid=%d iif=%d", result.code(), uid, iif); } @@ -690,14 +636,10 @@ Status TrafficController::addUidInterfaceRules(const int iif, } Status TrafficController::removeUidInterfaceRules(const std::vector<int32_t>& uidsToDelete) { - if (!mBpfEnabled) { - ALOGW("UID ingress interface filtering not possible without BPF owner match"); - return statusFromErrno(EOPNOTSUPP, "eBPF not supported"); - } std::lock_guard guard(mMutex); for (auto uid : uidsToDelete) { - netdutils::Status result = removeRule(mUidOwnerMap, uid, IIF_MATCH); + netdutils::Status result = removeRule(uid, IIF_MATCH); if (!isOk(result)) { ALOGW("removeRule failed(%d): uid=%d", result.code(), uid); } @@ -705,10 +647,10 @@ Status TrafficController::removeUidInterfaceRules(const std::vector<int32_t>& ui return netdutils::status::ok; } -int TrafficController::replaceUidOwnerMap(const std::string& name, bool isWhitelist __unused, +int TrafficController::replaceUidOwnerMap(const std::string& name, bool isAllowlist __unused, const std::vector<int32_t>& uids) { - // FirewallRule rule = isWhitelist ? ALLOW : DENY; - // FirewallType type = isWhitelist ? WHITELIST : BLACKLIST; + // FirewallRule rule = isAllowlist ? ALLOW : DENY; + // FirewallType type = isAllowlist ? ALLOWLIST : DENYLIST; Status res; if (!name.compare(FirewallController::LOCAL_DOZABLE)) { res = replaceRulesInMap(DOZABLE_MATCH, uids); @@ -716,6 +658,8 @@ int TrafficController::replaceUidOwnerMap(const std::string& name, bool isWhitel res = replaceRulesInMap(STANDBY_MATCH, uids); } else if (!name.compare(FirewallController::LOCAL_POWERSAVE)) { res = replaceRulesInMap(POWERSAVE_MATCH, uids); + } else if (!name.compare(FirewallController::LOCAL_RESTRICTED)) { + res = replaceRulesInMap(RESTRICTED_MATCH, uids); } else { ALOGE("unknown chain name: %s", name.c_str()); return -EINVAL; @@ -749,6 +693,9 @@ int TrafficController::toggleUidOwnerMap(ChildChain chain, bool enable) { case POWERSAVE: match = POWERSAVE_MATCH; break; + case RESTRICTED: + match = RESTRICTED_MATCH; + break; default: return -EINVAL; } @@ -761,17 +708,9 @@ int TrafficController::toggleUidOwnerMap(ChildChain chain, bool enable) { return -res.code(); } -bool TrafficController::getBpfEnabled() { - return mBpfEnabled; -} - Status TrafficController::swapActiveStatsMap() { std::lock_guard guard(mMutex); - if (!mBpfEnabled) { - return statusFromErrno(EOPNOTSUPP, "This device doesn't have eBPF support"); - } - uint32_t key = CURRENT_STATS_MAP_CONFIGURATION_KEY; auto oldConfiguration = mConfigurationMap.readValue(key); if (!oldConfiguration.ok()) { @@ -814,12 +753,9 @@ void TrafficController::setPermissionForUids(int permission, const std::vector<u // Clean up all permission information for the related uid if all the // packages related to it are uninstalled. mPrivilegedUser.erase(uid); - if (mBpfEnabled) { - Status ret = mUidPermissionMap.deleteValue(uid); - if (!isOk(ret) && ret.code() != ENOENT) { - ALOGE("Failed to clean up the permission for %u: %s", uid, - strerror(ret.code())); - } + Status ret = mUidPermissionMap.deleteValue(uid); + if (!isOk(ret) && ret.code() != ENOENT) { + ALOGE("Failed to clean up the permission for %u: %s", uid, strerror(ret.code())); } } return; @@ -834,10 +770,6 @@ void TrafficController::setPermissionForUids(int permission, const std::vector<u mPrivilegedUser.erase(uid); } - // Skip the bpf map operation if not supported. - if (!mBpfEnabled) { - continue; - } // The map stores all the permissions that the UID has, except if the only permission // the UID has is the INTERNET permission, then the UID should not appear in the map. if (permission != INetd::PERMISSION_INTERNET) { @@ -893,12 +825,6 @@ void TrafficController::dump(DumpWriter& dw, bool verbose) { dw.println("TrafficController"); ScopedIndent indentPreBpfModule(dw); - dw.println("BPF module status: %s", mBpfEnabled ? "enabled" : "disabled"); - dw.println("BPF support level: %s", BpfLevelToString(getBpfSupportLevel()).c_str()); - - if (!mBpfEnabled) { - return; - } dw.blankline(); dw.println("mCookieTagMap status: %s", @@ -928,10 +854,10 @@ void TrafficController::dump(DumpWriter& dw, bool verbose) { getProgramStatus(XT_BPF_INGRESS_PROG_PATH).c_str()); dw.println("xt_bpf egress program status: %s", getProgramStatus(XT_BPF_EGRESS_PROG_PATH).c_str()); - dw.println("xt_bpf bandwidth whitelist program status: %s", - getProgramStatus(XT_BPF_WHITELIST_PROG_PATH).c_str()); - dw.println("xt_bpf bandwidth blacklist program status: %s", - getProgramStatus(XT_BPF_BLACKLIST_PROG_PATH).c_str()); + dw.println("xt_bpf bandwidth allowlist program status: %s", + getProgramStatus(XT_BPF_ALLOWLIST_PROG_PATH).c_str()); + dw.println("xt_bpf bandwidth denylist program status: %s", + getProgramStatus(XT_BPF_DENYLIST_PROG_PATH).c_str()); if (!verbose) { return; diff --git a/server/TrafficController.h b/server/TrafficController.h index a2539a90..2e799598 100644 --- a/server/TrafficController.h +++ b/server/TrafficController.h @@ -78,12 +78,6 @@ class TrafficController { int deleteTagData(uint32_t tag, uid_t uid, uid_t callingUid) EXCLUDES(mMutex); /* - * Check if the current device have the bpf traffic stats accounting service - * running. - */ - bool getBpfEnabled(); - - /* * Swap the stats map config from current active stats map to the idle one. */ netdutils::Status swapActiveStatsMap() EXCLUDES(mMutex); @@ -97,7 +91,7 @@ class TrafficController { int removeUidOwnerRule(const uid_t uid); - int replaceUidOwnerMap(const std::string& name, bool isWhitelist, + int replaceUidOwnerMap(const std::string& name, bool isAllowlist, const std::vector<int32_t>& uids); netdutils::Status updateOwnerMapEntry(UidOwnerMatchType match, uid_t uid, FirewallRule rule, @@ -112,9 +106,9 @@ class TrafficController { EXCLUDES(mMutex); netdutils::Status removeUidInterfaceRules(const std::vector<int32_t>& uids) EXCLUDES(mMutex); - netdutils::Status updateUidOwnerMap(const std::vector<std::string>& appStrUids, - BandwidthController::IptJumpOp jumpHandling, - BandwidthController::IptOp op) EXCLUDES(mMutex); + netdutils::Status updateUidOwnerMap(const std::vector<uint32_t>& appStrUids, + UidOwnerMatchType matchType, BandwidthController::IptOp op) + EXCLUDES(mMutex); static const String16 DUMP_KEYWORD; int toggleUidOwnerMap(ChildChain chain, bool enable) EXCLUDES(mMutex); @@ -183,7 +177,7 @@ class TrafficController { * the map right now: * - Entry with UID_RULES_CONFIGURATION_KEY: * Store the configuration for the current uid rules. It indicates the device - * is in doze/powersave/standby mode. + * is in doze/powersave/standby/restricted mode. * - Entry with CURRENT_STATS_MAP_CONFIGURATION_KEY: * Stores the current live stats map that kernel program is writing to. * Userspace can do scraping and cleaning job on the other one depending on the @@ -203,13 +197,10 @@ class TrafficController { std::unique_ptr<NetlinkListenerInterface> mSkDestroyListener; - netdutils::Status removeRule(BpfMap<uint32_t, UidOwnerValue>& map, uint32_t uid, - UidOwnerMatchType match) REQUIRES(mMutex); - - netdutils::Status addRule(BpfMap<uint32_t, UidOwnerValue>& map, uint32_t uid, - UidOwnerMatchType match, uint32_t iif = 0) REQUIRES(mMutex); + netdutils::Status removeRule(uint32_t uid, UidOwnerMatchType match) REQUIRES(mMutex); - bool mBpfEnabled; + netdutils::Status addRule(uint32_t uid, UidOwnerMatchType match, uint32_t iif = 0) + REQUIRES(mMutex); // mMutex guards all accesses to mConfigurationMap, mUidOwnerMap, mUidPermissionMap, // mStatsMapA, mStatsMapB and mPrivilegedUser. It is designed to solve the following diff --git a/server/TrafficControllerTest.cpp b/server/TrafficControllerTest.cpp index 3cde9be8..159fb086 100644 --- a/server/TrafficControllerTest.cpp +++ b/server/TrafficControllerTest.cpp @@ -16,6 +16,7 @@ * TrafficControllerTest.cpp - unit tests for TrafficController.cpp */ +#include <cstdint> #include <string> #include <vector> @@ -74,7 +75,6 @@ class TrafficControllerTest : public ::testing::Test { void SetUp() { std::lock_guard guard(mTc.mMutex); - SKIP_IF_BPF_NOT_SUPPORTED; ASSERT_EQ(0, setrlimitForTest()); mFakeCookieTagMap.reset(createMap(BPF_MAP_TYPE_HASH, sizeof(uint64_t), sizeof(UidTagValue), @@ -165,30 +165,30 @@ class TrafficControllerTest : public ::testing::Test { void checkUidOwnerRuleForChain(ChildChain chain, UidOwnerMatchType match) { uint32_t uid = TEST_UID; - EXPECT_EQ(0, mTc.changeUidOwnerRule(chain, uid, DENY, BLACKLIST)); + EXPECT_EQ(0, mTc.changeUidOwnerRule(chain, uid, DENY, DENYLIST)); Result<UidOwnerValue> value = mFakeUidOwnerMap.readValue(uid); EXPECT_RESULT_OK(value); EXPECT_TRUE(value.value().rule & match); uid = TEST_UID2; - EXPECT_EQ(0, mTc.changeUidOwnerRule(chain, uid, ALLOW, WHITELIST)); + EXPECT_EQ(0, mTc.changeUidOwnerRule(chain, uid, ALLOW, ALLOWLIST)); value = mFakeUidOwnerMap.readValue(uid); EXPECT_RESULT_OK(value); EXPECT_TRUE(value.value().rule & match); - EXPECT_EQ(0, mTc.changeUidOwnerRule(chain, uid, DENY, WHITELIST)); + EXPECT_EQ(0, mTc.changeUidOwnerRule(chain, uid, DENY, ALLOWLIST)); value = mFakeUidOwnerMap.readValue(uid); EXPECT_FALSE(value.ok()); EXPECT_EQ(ENOENT, value.error().code()); uid = TEST_UID; - EXPECT_EQ(0, mTc.changeUidOwnerRule(chain, uid, ALLOW, BLACKLIST)); + EXPECT_EQ(0, mTc.changeUidOwnerRule(chain, uid, ALLOW, DENYLIST)); value = mFakeUidOwnerMap.readValue(uid); EXPECT_FALSE(value.ok()); EXPECT_EQ(ENOENT, value.error().code()); uid = TEST_UID3; - EXPECT_EQ(-ENOENT, mTc.changeUidOwnerRule(chain, uid, ALLOW, BLACKLIST)); + EXPECT_EQ(-ENOENT, mTc.changeUidOwnerRule(chain, uid, ALLOW, DENYLIST)); value = mFakeUidOwnerMap.readValue(uid); EXPECT_FALSE(value.ok()); EXPECT_EQ(ENOENT, value.error().code()); @@ -211,18 +211,18 @@ class TrafficControllerTest : public ::testing::Test { void checkUidMapReplace(const std::string& name, const std::vector<int32_t>& uids, UidOwnerMatchType match) { - bool isWhitelist = true; - EXPECT_EQ(0, mTc.replaceUidOwnerMap(name, isWhitelist, uids)); + bool isAllowlist = true; + EXPECT_EQ(0, mTc.replaceUidOwnerMap(name, isAllowlist, uids)); checkEachUidValue(uids, match); - isWhitelist = false; - EXPECT_EQ(0, mTc.replaceUidOwnerMap(name, isWhitelist, uids)); + isAllowlist = false; + EXPECT_EQ(0, mTc.replaceUidOwnerMap(name, isAllowlist, uids)); checkEachUidValue(uids, match); } - void expectUidOwnerMapValues(const std::vector<std::string>& appStrUids, uint8_t expectedRule, + + void expectUidOwnerMapValues(const std::vector<uint32_t>& appUids, uint8_t expectedRule, uint32_t expectedIif) { - for (const std::string& strUid : appStrUids) { - uint32_t uid = stoi(strUid); + for (uint32_t uid : appUids) { Result<UidOwnerValue> value = mFakeUidOwnerMap.readValue(uid); EXPECT_RESULT_OK(value); EXPECT_EQ(expectedRule, value.value().rule) @@ -315,8 +315,6 @@ class TrafficControllerTest : public ::testing::Test { }; TEST_F(TrafficControllerTest, TestTagSocketV4) { - SKIP_IF_BPF_NOT_SUPPORTED; - uint64_t sockCookie; int v4socket = setUpSocketAndTag(AF_INET, &sockCookie, TEST_TAG, TEST_UID, TEST_UID); expectUidTag(sockCookie, TEST_UID, TEST_TAG); @@ -326,8 +324,6 @@ TEST_F(TrafficControllerTest, TestTagSocketV4) { } TEST_F(TrafficControllerTest, TestReTagSocket) { - SKIP_IF_BPF_NOT_SUPPORTED; - uint64_t sockCookie; int v4socket = setUpSocketAndTag(AF_INET, &sockCookie, TEST_TAG, TEST_UID, TEST_UID); expectUidTag(sockCookie, TEST_UID, TEST_TAG); @@ -336,8 +332,6 @@ TEST_F(TrafficControllerTest, TestReTagSocket) { } TEST_F(TrafficControllerTest, TestTagTwoSockets) { - SKIP_IF_BPF_NOT_SUPPORTED; - uint64_t sockCookie1; uint64_t sockCookie2; int v4socket1 = setUpSocketAndTag(AF_INET, &sockCookie1, TEST_TAG, TEST_UID, TEST_UID); @@ -351,8 +345,6 @@ TEST_F(TrafficControllerTest, TestTagTwoSockets) { } TEST_F(TrafficControllerTest, TestTagSocketV6) { - SKIP_IF_BPF_NOT_SUPPORTED; - uint64_t sockCookie; int v6socket = setUpSocketAndTag(AF_INET6, &sockCookie, TEST_TAG, TEST_UID, TEST_UID); expectUidTag(sockCookie, TEST_UID, TEST_TAG); @@ -362,16 +354,12 @@ TEST_F(TrafficControllerTest, TestTagSocketV6) { } TEST_F(TrafficControllerTest, TestTagInvalidSocket) { - SKIP_IF_BPF_NOT_SUPPORTED; - int invalidSocket = -1; ASSERT_GT(0, mTc.tagSocket(invalidSocket, TEST_TAG, TEST_UID, TEST_UID)); expectMapEmpty(mFakeCookieTagMap); } TEST_F(TrafficControllerTest, TestTagSocketWithoutPermission) { - SKIP_IF_BPF_NOT_SUPPORTED; - int sock = socket(AF_INET6, SOCK_STREAM | SOCK_CLOEXEC, 0); ASSERT_NE(-1, sock); ASSERT_EQ(-EPERM, mTc.tagSocket(sock, TEST_TAG, TEST_UID, TEST_UID2)); @@ -379,8 +367,6 @@ TEST_F(TrafficControllerTest, TestTagSocketWithoutPermission) { } TEST_F(TrafficControllerTest, TestTagSocketWithPermission) { - SKIP_IF_BPF_NOT_SUPPORTED; - // Grant permission to calling uid. std::vector<uid_t> callingUid = {TEST_UID2}; mTc.setPermissionForUids(INetd::PERMISSION_UPDATE_DEVICE_STATS, callingUid); @@ -399,8 +385,6 @@ TEST_F(TrafficControllerTest, TestTagSocketWithPermission) { } TEST_F(TrafficControllerTest, TestUntagInvalidSocket) { - SKIP_IF_BPF_NOT_SUPPORTED; - int invalidSocket = -1; ASSERT_GT(0, mTc.untagSocket(invalidSocket)); int v4socket = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0); @@ -409,8 +393,6 @@ TEST_F(TrafficControllerTest, TestUntagInvalidSocket) { } TEST_F(TrafficControllerTest, TestTagSocketReachLimitFail) { - SKIP_IF_BPF_NOT_SUPPORTED; - uid_t uid = TEST_UID; StatsKey tagStatsMapKey[4]; for (int i = 0; i < 3; i++) { @@ -422,8 +404,6 @@ TEST_F(TrafficControllerTest, TestTagSocketReachLimitFail) { } TEST_F(TrafficControllerTest, TestTagSocketReachTotalLimitFail) { - SKIP_IF_BPF_NOT_SUPPORTED; - StatsKey tagStatsMapKey[4]; for (int i = 0; i < 4; i++) { uint64_t cookie = TEST_COOKIE + i; @@ -435,8 +415,6 @@ TEST_F(TrafficControllerTest, TestTagSocketReachTotalLimitFail) { } TEST_F(TrafficControllerTest, TestSetCounterSet) { - SKIP_IF_BPF_NOT_SUPPORTED; - uid_t callingUid = TEST_UID2; addPrivilegedUid(callingUid); ASSERT_EQ(0, mTc.setCounterSet(TEST_COUNTERSET, TEST_UID, callingUid)); @@ -450,8 +428,6 @@ TEST_F(TrafficControllerTest, TestSetCounterSet) { } TEST_F(TrafficControllerTest, TestSetCounterSetWithoutPermission) { - SKIP_IF_BPF_NOT_SUPPORTED; - ASSERT_EQ(-EPERM, mTc.setCounterSet(TEST_COUNTERSET, TEST_UID, TEST_UID2)); uid_t uid = TEST_UID; ASSERT_FALSE(mFakeUidCounterSetMap.readValue(uid).ok()); @@ -459,8 +435,6 @@ TEST_F(TrafficControllerTest, TestSetCounterSetWithoutPermission) { } TEST_F(TrafficControllerTest, TestSetInvalidCounterSet) { - SKIP_IF_BPF_NOT_SUPPORTED; - uid_t callingUid = TEST_UID2; addPrivilegedUid(callingUid); ASSERT_GT(0, mTc.setCounterSet(OVERFLOW_COUNTERSET, TEST_UID, callingUid)); @@ -470,8 +444,6 @@ TEST_F(TrafficControllerTest, TestSetInvalidCounterSet) { } TEST_F(TrafficControllerTest, TestDeleteTagDataWithoutPermission) { - SKIP_IF_BPF_NOT_SUPPORTED; - uint64_t cookie = 1; uid_t uid = TEST_UID; uint32_t tag = TEST_TAG; @@ -483,8 +455,6 @@ TEST_F(TrafficControllerTest, TestDeleteTagDataWithoutPermission) { } TEST_F(TrafficControllerTest, TestDeleteTagData) { - SKIP_IF_BPF_NOT_SUPPORTED; - uid_t callingUid = TEST_UID2; addPrivilegedUid(callingUid); uint64_t cookie = 1; @@ -510,8 +480,6 @@ TEST_F(TrafficControllerTest, TestDeleteTagData) { } TEST_F(TrafficControllerTest, TestDeleteAllUidData) { - SKIP_IF_BPF_NOT_SUPPORTED; - uid_t callingUid = TEST_UID2; addPrivilegedUid(callingUid); uint64_t cookie = 1; @@ -529,8 +497,6 @@ TEST_F(TrafficControllerTest, TestDeleteAllUidData) { } TEST_F(TrafficControllerTest, TestDeleteDataWithTwoTags) { - SKIP_IF_BPF_NOT_SUPPORTED; - uid_t callingUid = TEST_UID2; addPrivilegedUid(callingUid); uint64_t cookie1 = 1; @@ -559,8 +525,6 @@ TEST_F(TrafficControllerTest, TestDeleteDataWithTwoTags) { } TEST_F(TrafficControllerTest, TestDeleteDataWithTwoUids) { - SKIP_IF_BPF_NOT_SUPPORTED; - uid_t callingUid = TEST_UID2; addPrivilegedUid(callingUid); uint64_t cookie1 = 1; @@ -602,174 +566,156 @@ TEST_F(TrafficControllerTest, TestDeleteDataWithTwoUids) { } TEST_F(TrafficControllerTest, TestUpdateOwnerMapEntry) { - SKIP_IF_BPF_NOT_SUPPORTED; - uint32_t uid = TEST_UID; - ASSERT_TRUE(isOk(mTc.updateOwnerMapEntry(STANDBY_MATCH, uid, DENY, BLACKLIST))); + ASSERT_TRUE(isOk(mTc.updateOwnerMapEntry(STANDBY_MATCH, uid, DENY, DENYLIST))); Result<UidOwnerValue> value = mFakeUidOwnerMap.readValue(uid); ASSERT_RESULT_OK(value); ASSERT_TRUE(value.value().rule & STANDBY_MATCH); - ASSERT_TRUE(isOk(mTc.updateOwnerMapEntry(DOZABLE_MATCH, uid, ALLOW, WHITELIST))); + ASSERT_TRUE(isOk(mTc.updateOwnerMapEntry(DOZABLE_MATCH, uid, ALLOW, ALLOWLIST))); value = mFakeUidOwnerMap.readValue(uid); ASSERT_RESULT_OK(value); ASSERT_TRUE(value.value().rule & DOZABLE_MATCH); - ASSERT_TRUE(isOk(mTc.updateOwnerMapEntry(DOZABLE_MATCH, uid, DENY, WHITELIST))); + ASSERT_TRUE(isOk(mTc.updateOwnerMapEntry(DOZABLE_MATCH, uid, DENY, ALLOWLIST))); value = mFakeUidOwnerMap.readValue(uid); ASSERT_RESULT_OK(value); ASSERT_FALSE(value.value().rule & DOZABLE_MATCH); - ASSERT_TRUE(isOk(mTc.updateOwnerMapEntry(STANDBY_MATCH, uid, ALLOW, BLACKLIST))); + ASSERT_TRUE(isOk(mTc.updateOwnerMapEntry(STANDBY_MATCH, uid, ALLOW, DENYLIST))); ASSERT_FALSE(mFakeUidOwnerMap.readValue(uid).ok()); uid = TEST_UID2; - ASSERT_FALSE(isOk(mTc.updateOwnerMapEntry(STANDBY_MATCH, uid, ALLOW, BLACKLIST))); + ASSERT_FALSE(isOk(mTc.updateOwnerMapEntry(STANDBY_MATCH, uid, ALLOW, DENYLIST))); ASSERT_FALSE(mFakeUidOwnerMap.readValue(uid).ok()); } TEST_F(TrafficControllerTest, TestChangeUidOwnerRule) { - SKIP_IF_BPF_NOT_SUPPORTED; - checkUidOwnerRuleForChain(DOZABLE, DOZABLE_MATCH); checkUidOwnerRuleForChain(STANDBY, STANDBY_MATCH); checkUidOwnerRuleForChain(POWERSAVE, POWERSAVE_MATCH); - ASSERT_EQ(-EINVAL, mTc.changeUidOwnerRule(NONE, TEST_UID, ALLOW, WHITELIST)); - ASSERT_EQ(-EINVAL, mTc.changeUidOwnerRule(INVALID_CHAIN, TEST_UID, ALLOW, WHITELIST)); + checkUidOwnerRuleForChain(RESTRICTED, RESTRICTED_MATCH); + ASSERT_EQ(-EINVAL, mTc.changeUidOwnerRule(NONE, TEST_UID, ALLOW, ALLOWLIST)); + ASSERT_EQ(-EINVAL, mTc.changeUidOwnerRule(INVALID_CHAIN, TEST_UID, ALLOW, ALLOWLIST)); } TEST_F(TrafficControllerTest, TestReplaceUidOwnerMap) { - SKIP_IF_BPF_NOT_SUPPORTED; - std::vector<int32_t> uids = {TEST_UID, TEST_UID2, TEST_UID3}; checkUidMapReplace("fw_dozable", uids, DOZABLE_MATCH); checkUidMapReplace("fw_standby", uids, STANDBY_MATCH); checkUidMapReplace("fw_powersave", uids, POWERSAVE_MATCH); + checkUidMapReplace("fw_restricted", uids, RESTRICTED_MATCH); ASSERT_EQ(-EINVAL, mTc.replaceUidOwnerMap("unknow", true, uids)); } TEST_F(TrafficControllerTest, TestReplaceSameChain) { - SKIP_IF_BPF_NOT_SUPPORTED; - std::vector<int32_t> uids = {TEST_UID, TEST_UID2, TEST_UID3}; checkUidMapReplace("fw_dozable", uids, DOZABLE_MATCH); std::vector<int32_t> newUids = {TEST_UID2, TEST_UID3}; checkUidMapReplace("fw_dozable", newUids, DOZABLE_MATCH); } -TEST_F(TrafficControllerTest, TestBlacklistUidMatch) { - SKIP_IF_BPF_NOT_SUPPORTED; - - std::vector<std::string> appStrUids = {"1000", "1001", "10012"}; - ASSERT_TRUE(isOk(mTc.updateUidOwnerMap(appStrUids, BandwidthController::IptJumpReject, - BandwidthController::IptOpInsert))); - expectUidOwnerMapValues(appStrUids, PENALTY_BOX_MATCH, 0); - ASSERT_TRUE(isOk(mTc.updateUidOwnerMap(appStrUids, BandwidthController::IptJumpReject, - BandwidthController::IptOpDelete))); +TEST_F(TrafficControllerTest, TestDenylistUidMatch) { + std::vector<uint32_t> appUids = {1000, 1001, 10012}; + ASSERT_TRUE(isOk( + mTc.updateUidOwnerMap(appUids, PENALTY_BOX_MATCH, BandwidthController::IptOpInsert))); + expectUidOwnerMapValues(appUids, PENALTY_BOX_MATCH, 0); + ASSERT_TRUE(isOk( + mTc.updateUidOwnerMap(appUids, PENALTY_BOX_MATCH, BandwidthController::IptOpDelete))); expectMapEmpty(mFakeUidOwnerMap); } -TEST_F(TrafficControllerTest, TestWhitelistUidMatch) { - SKIP_IF_BPF_NOT_SUPPORTED; - - std::vector<std::string> appStrUids = {"1000", "1001", "10012"}; - ASSERT_TRUE(isOk(mTc.updateUidOwnerMap(appStrUids, BandwidthController::IptJumpReturn, - BandwidthController::IptOpInsert))); - expectUidOwnerMapValues(appStrUids, HAPPY_BOX_MATCH, 0); - ASSERT_TRUE(isOk(mTc.updateUidOwnerMap(appStrUids, BandwidthController::IptJumpReturn, - BandwidthController::IptOpDelete))); +TEST_F(TrafficControllerTest, TestAllowlistUidMatch) { + std::vector<uint32_t> appUids = {1000, 1001, 10012}; + ASSERT_TRUE(isOk( + mTc.updateUidOwnerMap(appUids, HAPPY_BOX_MATCH, BandwidthController::IptOpInsert))); + expectUidOwnerMapValues(appUids, HAPPY_BOX_MATCH, 0); + ASSERT_TRUE(isOk( + mTc.updateUidOwnerMap(appUids, HAPPY_BOX_MATCH, BandwidthController::IptOpDelete))); expectMapEmpty(mFakeUidOwnerMap); } TEST_F(TrafficControllerTest, TestReplaceMatchUid) { - SKIP_IF_BPF_NOT_SUPPORTED; + std::vector<uint32_t> appUids = {1000, 1001, 10012}; + // Add appUids to the denylist and expect that their values are all PENALTY_BOX_MATCH. + ASSERT_TRUE(isOk( + mTc.updateUidOwnerMap(appUids, PENALTY_BOX_MATCH, BandwidthController::IptOpInsert))); + expectUidOwnerMapValues(appUids, PENALTY_BOX_MATCH, 0); - std::vector<std::string> appStrUids = {"1000", "1001", "10012"}; - // Add appStrUids to the blacklist and expect that their values are all PENALTY_BOX_MATCH. - ASSERT_TRUE(isOk(mTc.updateUidOwnerMap(appStrUids, BandwidthController::IptJumpReject, - BandwidthController::IptOpInsert))); - expectUidOwnerMapValues(appStrUids, PENALTY_BOX_MATCH, 0); - - // Add the same UIDs to the whitelist and expect that we get PENALTY_BOX_MATCH | + // Add the same UIDs to the allowlist and expect that we get PENALTY_BOX_MATCH | // HAPPY_BOX_MATCH. - ASSERT_TRUE(isOk(mTc.updateUidOwnerMap(appStrUids, BandwidthController::IptJumpReturn, - BandwidthController::IptOpInsert))); - expectUidOwnerMapValues(appStrUids, HAPPY_BOX_MATCH | PENALTY_BOX_MATCH, 0); - - // Remove the same UIDs from the whitelist and check the PENALTY_BOX_MATCH is still there. - ASSERT_TRUE(isOk(mTc.updateUidOwnerMap(appStrUids, BandwidthController::IptJumpReturn, - BandwidthController::IptOpDelete))); - expectUidOwnerMapValues(appStrUids, PENALTY_BOX_MATCH, 0); - - // Remove the same UIDs from the blacklist and check the map is empty. - ASSERT_TRUE(isOk(mTc.updateUidOwnerMap(appStrUids, BandwidthController::IptJumpReject, - BandwidthController::IptOpDelete))); + ASSERT_TRUE(isOk( + mTc.updateUidOwnerMap(appUids, HAPPY_BOX_MATCH, BandwidthController::IptOpInsert))); + expectUidOwnerMapValues(appUids, HAPPY_BOX_MATCH | PENALTY_BOX_MATCH, 0); + + // Remove the same UIDs from the allowlist and check the PENALTY_BOX_MATCH is still there. + ASSERT_TRUE(isOk( + mTc.updateUidOwnerMap(appUids, HAPPY_BOX_MATCH, BandwidthController::IptOpDelete))); + expectUidOwnerMapValues(appUids, PENALTY_BOX_MATCH, 0); + + // Remove the same UIDs from the denylist and check the map is empty. + ASSERT_TRUE(isOk( + mTc.updateUidOwnerMap(appUids, PENALTY_BOX_MATCH, BandwidthController::IptOpDelete))); ASSERT_FALSE(mFakeUidOwnerMap.getFirstKey().ok()); } TEST_F(TrafficControllerTest, TestDeleteWrongMatchSilentlyFails) { - SKIP_IF_BPF_NOT_SUPPORTED; - - std::vector<std::string> appStrUids = {"1000", "1001", "10012"}; + std::vector<uint32_t> appUids = {1000, 1001, 10012}; // If the uid does not exist in the map, trying to delete a rule about it will fail. - ASSERT_FALSE(isOk(mTc.updateUidOwnerMap(appStrUids, BandwidthController::IptJumpReturn, - BandwidthController::IptOpDelete))); + ASSERT_FALSE(isOk( + mTc.updateUidOwnerMap(appUids, HAPPY_BOX_MATCH, BandwidthController::IptOpDelete))); expectMapEmpty(mFakeUidOwnerMap); - // Add blacklist rules for appStrUids. - ASSERT_TRUE(isOk(mTc.updateUidOwnerMap(appStrUids, BandwidthController::IptJumpReturn, - BandwidthController::IptOpInsert))); - expectUidOwnerMapValues(appStrUids, HAPPY_BOX_MATCH, 0); + // Add denylist rules for appUids. + ASSERT_TRUE(isOk( + mTc.updateUidOwnerMap(appUids, HAPPY_BOX_MATCH, BandwidthController::IptOpInsert))); + expectUidOwnerMapValues(appUids, HAPPY_BOX_MATCH, 0); - // Delete (non-existent) blacklist rules for appStrUids, and check that this silently does - // nothing if the uid is in the map but does not have blacklist match. This is required because - // NetworkManagementService will try to remove a uid from blacklist after adding it to the - // whitelist and if the remove fails it will not update the uid status. - ASSERT_TRUE(isOk(mTc.updateUidOwnerMap(appStrUids, BandwidthController::IptJumpReject, - BandwidthController::IptOpDelete))); - expectUidOwnerMapValues(appStrUids, HAPPY_BOX_MATCH, 0); + // Delete (non-existent) denylist rules for appUids, and check that this silently does + // nothing if the uid is in the map but does not have denylist match. This is required because + // NetworkManagementService will try to remove a uid from denylist after adding it to the + // allowlist and if the remove fails it will not update the uid status. + ASSERT_TRUE(isOk( + mTc.updateUidOwnerMap(appUids, PENALTY_BOX_MATCH, BandwidthController::IptOpDelete))); + expectUidOwnerMapValues(appUids, HAPPY_BOX_MATCH, 0); } TEST_F(TrafficControllerTest, TestAddUidInterfaceFilteringRules) { - SKIP_IF_BPF_NOT_SUPPORTED; - int iif0 = 15; ASSERT_TRUE(isOk(mTc.addUidInterfaceRules(iif0, {1000, 1001}))); - expectUidOwnerMapValues({"1000", "1001"}, IIF_MATCH, iif0); + expectUidOwnerMapValues({1000, 1001}, IIF_MATCH, iif0); // Add some non-overlapping new uids. They should coexist with existing rules int iif1 = 16; ASSERT_TRUE(isOk(mTc.addUidInterfaceRules(iif1, {2000, 2001}))); - expectUidOwnerMapValues({"1000", "1001"}, IIF_MATCH, iif0); - expectUidOwnerMapValues({"2000", "2001"}, IIF_MATCH, iif1); + expectUidOwnerMapValues({1000, 1001}, IIF_MATCH, iif0); + expectUidOwnerMapValues({2000, 2001}, IIF_MATCH, iif1); // Overwrite some existing uids int iif2 = 17; ASSERT_TRUE(isOk(mTc.addUidInterfaceRules(iif2, {1000, 2000}))); - expectUidOwnerMapValues({"1001"}, IIF_MATCH, iif0); - expectUidOwnerMapValues({"2001"}, IIF_MATCH, iif1); - expectUidOwnerMapValues({"1000", "2000"}, IIF_MATCH, iif2); + expectUidOwnerMapValues({1001}, IIF_MATCH, iif0); + expectUidOwnerMapValues({2001}, IIF_MATCH, iif1); + expectUidOwnerMapValues({1000, 2000}, IIF_MATCH, iif2); } TEST_F(TrafficControllerTest, TestRemoveUidInterfaceFilteringRules) { - SKIP_IF_BPF_NOT_SUPPORTED; - int iif0 = 15; int iif1 = 16; ASSERT_TRUE(isOk(mTc.addUidInterfaceRules(iif0, {1000, 1001}))); ASSERT_TRUE(isOk(mTc.addUidInterfaceRules(iif1, {2000, 2001}))); - expectUidOwnerMapValues({"1000", "1001"}, IIF_MATCH, iif0); - expectUidOwnerMapValues({"2000", "2001"}, IIF_MATCH, iif1); + expectUidOwnerMapValues({1000, 1001}, IIF_MATCH, iif0); + expectUidOwnerMapValues({2000, 2001}, IIF_MATCH, iif1); // Rmove some uids ASSERT_TRUE(isOk(mTc.removeUidInterfaceRules({1001, 2001}))); - expectUidOwnerMapValues({"1000"}, IIF_MATCH, iif0); - expectUidOwnerMapValues({"2000"}, IIF_MATCH, iif1); + expectUidOwnerMapValues({1000}, IIF_MATCH, iif0); + expectUidOwnerMapValues({2000}, IIF_MATCH, iif1); checkEachUidValue({1000, 2000}, IIF_MATCH); // Make sure there are only two uids remaining // Remove non-existent uids shouldn't fail ASSERT_TRUE(isOk(mTc.removeUidInterfaceRules({2000, 3000}))); - expectUidOwnerMapValues({"1000"}, IIF_MATCH, iif0); + expectUidOwnerMapValues({1000}, IIF_MATCH, iif0); checkEachUidValue({1000}, IIF_MATCH); // Make sure there are only one uid remaining // Remove everything @@ -778,72 +724,65 @@ TEST_F(TrafficControllerTest, TestRemoveUidInterfaceFilteringRules) { } TEST_F(TrafficControllerTest, TestUidInterfaceFilteringRulesCoexistWithExistingMatches) { - SKIP_IF_BPF_NOT_SUPPORTED; - // Set up existing PENALTY_BOX_MATCH rules - ASSERT_TRUE(isOk(mTc.updateUidOwnerMap({"1000", "1001", "10012"}, - BandwidthController::IptJumpReject, + ASSERT_TRUE(isOk(mTc.updateUidOwnerMap({1000, 1001, 10012}, PENALTY_BOX_MATCH, BandwidthController::IptOpInsert))); - expectUidOwnerMapValues({"1000", "1001", "10012"}, PENALTY_BOX_MATCH, 0); + expectUidOwnerMapValues({1000, 1001, 10012}, PENALTY_BOX_MATCH, 0); // Add some partially-overlapping uid owner rules and check result int iif1 = 32; ASSERT_TRUE(isOk(mTc.addUidInterfaceRules(iif1, {10012, 10013, 10014}))); - expectUidOwnerMapValues({"1000", "1001"}, PENALTY_BOX_MATCH, 0); - expectUidOwnerMapValues({"10012"}, PENALTY_BOX_MATCH | IIF_MATCH, iif1); - expectUidOwnerMapValues({"10013", "10014"}, IIF_MATCH, iif1); + expectUidOwnerMapValues({1000, 1001}, PENALTY_BOX_MATCH, 0); + expectUidOwnerMapValues({10012}, PENALTY_BOX_MATCH | IIF_MATCH, iif1); + expectUidOwnerMapValues({10013, 10014}, IIF_MATCH, iif1); // Removing some PENALTY_BOX_MATCH rules should not change uid interface rule - ASSERT_TRUE(isOk(mTc.updateUidOwnerMap({"1001", "10012"}, BandwidthController::IptJumpReject, + ASSERT_TRUE(isOk(mTc.updateUidOwnerMap({1001, 10012}, PENALTY_BOX_MATCH, BandwidthController::IptOpDelete))); - expectUidOwnerMapValues({"1000"}, PENALTY_BOX_MATCH, 0); - expectUidOwnerMapValues({"10012", "10013", "10014"}, IIF_MATCH, iif1); + expectUidOwnerMapValues({1000}, PENALTY_BOX_MATCH, 0); + expectUidOwnerMapValues({10012, 10013, 10014}, IIF_MATCH, iif1); // Remove all uid interface rules ASSERT_TRUE(isOk(mTc.removeUidInterfaceRules({10012, 10013, 10014}))); - expectUidOwnerMapValues({"1000"}, PENALTY_BOX_MATCH, 0); + expectUidOwnerMapValues({1000}, PENALTY_BOX_MATCH, 0); // Make sure these are the only uids left checkEachUidValue({1000}, PENALTY_BOX_MATCH); } TEST_F(TrafficControllerTest, TestUidInterfaceFilteringRulesCoexistWithNewMatches) { - SKIP_IF_BPF_NOT_SUPPORTED; - int iif1 = 56; // Set up existing uid interface rules ASSERT_TRUE(isOk(mTc.addUidInterfaceRules(iif1, {10001, 10002}))); - expectUidOwnerMapValues({"10001", "10002"}, IIF_MATCH, iif1); + expectUidOwnerMapValues({10001, 10002}, IIF_MATCH, iif1); // Add some partially-overlapping doze rules EXPECT_EQ(0, mTc.replaceUidOwnerMap("fw_dozable", true, {10002, 10003})); - expectUidOwnerMapValues({"10001"}, IIF_MATCH, iif1); - expectUidOwnerMapValues({"10002"}, DOZABLE_MATCH | IIF_MATCH, iif1); - expectUidOwnerMapValues({"10003"}, DOZABLE_MATCH, 0); + expectUidOwnerMapValues({10001}, IIF_MATCH, iif1); + expectUidOwnerMapValues({10002}, DOZABLE_MATCH | IIF_MATCH, iif1); + expectUidOwnerMapValues({10003}, DOZABLE_MATCH, 0); // Introduce a third rule type (powersave) on various existing UIDs EXPECT_EQ(0, mTc.replaceUidOwnerMap("fw_powersave", true, {10000, 10001, 10002, 10003})); - expectUidOwnerMapValues({"10000"}, POWERSAVE_MATCH, 0); - expectUidOwnerMapValues({"10001"}, POWERSAVE_MATCH | IIF_MATCH, iif1); - expectUidOwnerMapValues({"10002"}, POWERSAVE_MATCH | DOZABLE_MATCH | IIF_MATCH, iif1); - expectUidOwnerMapValues({"10003"}, POWERSAVE_MATCH | DOZABLE_MATCH, 0); + expectUidOwnerMapValues({10000}, POWERSAVE_MATCH, 0); + expectUidOwnerMapValues({10001}, POWERSAVE_MATCH | IIF_MATCH, iif1); + expectUidOwnerMapValues({10002}, POWERSAVE_MATCH | DOZABLE_MATCH | IIF_MATCH, iif1); + expectUidOwnerMapValues({10003}, POWERSAVE_MATCH | DOZABLE_MATCH, 0); // Remove all doze rules EXPECT_EQ(0, mTc.replaceUidOwnerMap("fw_dozable", true, {})); - expectUidOwnerMapValues({"10000"}, POWERSAVE_MATCH, 0); - expectUidOwnerMapValues({"10001"}, POWERSAVE_MATCH | IIF_MATCH, iif1); - expectUidOwnerMapValues({"10002"}, POWERSAVE_MATCH | IIF_MATCH, iif1); - expectUidOwnerMapValues({"10003"}, POWERSAVE_MATCH, 0); + expectUidOwnerMapValues({10000}, POWERSAVE_MATCH, 0); + expectUidOwnerMapValues({10001}, POWERSAVE_MATCH | IIF_MATCH, iif1); + expectUidOwnerMapValues({10002}, POWERSAVE_MATCH | IIF_MATCH, iif1); + expectUidOwnerMapValues({10003}, POWERSAVE_MATCH, 0); // Remove all powersave rules, expect ownerMap to only have uid interface rules left EXPECT_EQ(0, mTc.replaceUidOwnerMap("fw_powersave", true, {})); - expectUidOwnerMapValues({"10001", "10002"}, IIF_MATCH, iif1); + expectUidOwnerMapValues({10001, 10002}, IIF_MATCH, iif1); // Make sure these are the only uids left checkEachUidValue({10001, 10002}, IIF_MATCH); } TEST_F(TrafficControllerTest, TestGrantInternetPermission) { - SKIP_IF_BPF_NOT_SUPPORTED; - std::vector<uid_t> appUids = {TEST_UID, TEST_UID2, TEST_UID3}; mTc.setPermissionForUids(INetd::PERMISSION_INTERNET, appUids); @@ -852,8 +791,6 @@ TEST_F(TrafficControllerTest, TestGrantInternetPermission) { } TEST_F(TrafficControllerTest, TestRevokeInternetPermission) { - SKIP_IF_BPF_NOT_SUPPORTED; - std::vector<uid_t> appUids = {TEST_UID, TEST_UID2, TEST_UID3}; mTc.setPermissionForUids(INetd::PERMISSION_NONE, appUids); @@ -861,8 +798,6 @@ TEST_F(TrafficControllerTest, TestRevokeInternetPermission) { } TEST_F(TrafficControllerTest, TestPermissionUninstalled) { - SKIP_IF_BPF_NOT_SUPPORTED; - std::vector<uid_t> appUids = {TEST_UID, TEST_UID2, TEST_UID3}; mTc.setPermissionForUids(INetd::PERMISSION_UPDATE_DEVICE_STATS, appUids); @@ -882,8 +817,6 @@ TEST_F(TrafficControllerTest, TestPermissionUninstalled) { } TEST_F(TrafficControllerTest, TestGrantUpdateStatsPermission) { - SKIP_IF_BPF_NOT_SUPPORTED; - std::vector<uid_t> appUids = {TEST_UID, TEST_UID2, TEST_UID3}; mTc.setPermissionForUids(INetd::PERMISSION_UPDATE_DEVICE_STATS, appUids); @@ -896,8 +829,6 @@ TEST_F(TrafficControllerTest, TestGrantUpdateStatsPermission) { } TEST_F(TrafficControllerTest, TestRevokeUpdateStatsPermission) { - SKIP_IF_BPF_NOT_SUPPORTED; - std::vector<uid_t> appUids = {TEST_UID, TEST_UID2, TEST_UID3}; mTc.setPermissionForUids(INetd::PERMISSION_UPDATE_DEVICE_STATS, appUids); @@ -914,8 +845,6 @@ TEST_F(TrafficControllerTest, TestRevokeUpdateStatsPermission) { } TEST_F(TrafficControllerTest, TestGrantWrongPermission) { - SKIP_IF_BPF_NOT_SUPPORTED; - std::vector<uid_t> appUids = {TEST_UID, TEST_UID2, TEST_UID3}; mTc.setPermissionForUids(INetd::PERMISSION_NONE, appUids); @@ -924,8 +853,6 @@ TEST_F(TrafficControllerTest, TestGrantWrongPermission) { } TEST_F(TrafficControllerTest, TestGrantDuplicatePermissionSlientlyFail) { - SKIP_IF_BPF_NOT_SUPPORTED; - std::vector<uid_t> appUids = {TEST_UID, TEST_UID2, TEST_UID3}; mTc.setPermissionForUids(INetd::PERMISSION_INTERNET, appUids); diff --git a/server/UidRanges.cpp b/server/UidRanges.cpp index 883a13f9..093a1e27 100644 --- a/server/UidRanges.cpp +++ b/server/UidRanges.cpp @@ -127,8 +127,35 @@ void UidRanges::remove(const UidRanges& other) { mRanges.erase(end, mRanges.end()); } +bool UidRanges::isOverlapped(const UidRangeParcel& r1, const UidRangeParcel& r2) const { + return (r1.stop >= r2.start) && (r1.start <= r2.stop); +} + +bool UidRanges::overlapsSelf() const { + // Compare each element one by one + for (size_t i = 0; i < mRanges.size(); i++) { + for (size_t j = i + 1; j < mRanges.size(); j++) { + if (isOverlapped(mRanges[i], mRanges[j])) { + return true; + } + } + } + return false; +} + +bool UidRanges::overlaps(const UidRanges& other) const { + for (const auto& thisRange : mRanges) { + for (const auto& inputRange : other.getRanges()) { + if (isOverlapped(thisRange, inputRange)) { + return true; + } + } + } + return false; +} + std::string UidRanges::toString() const { - std::string s("UidRanges{ "); + std::string s("uids{ "); for (const auto &range : mRanges) { if (length(range) == 0) { StringAppendF(&s, "<BAD: %u-%u> ", range.start, range.stop); diff --git a/server/UidRanges.h b/server/UidRanges.h index 28d3c6b4..99e7a99d 100644 --- a/server/UidRanges.h +++ b/server/UidRanges.h @@ -28,6 +28,9 @@ namespace net { class UidRanges { public: + static constexpr int DEFAULT_SUB_PRIORITY = 0; + static constexpr int LOWEST_SUB_PRIORITY = 999; + UidRanges() {} UidRanges(const std::vector<android::net::UidRangeParcel>& ranges); @@ -40,7 +43,16 @@ public: void add(const UidRanges& other); void remove(const UidRanges& other); + // check if 'mRanges' has uid overlap between elements. + bool overlapsSelf() const; + // check if this object has uid overlap with the input object. + bool overlaps(const UidRanges& other) const; + bool empty() const { return mRanges.empty(); } + private: + // a utility to check if two UidRangeParcels have uid overlap. + bool isOverlapped(const UidRangeParcel& r1, const UidRangeParcel& r2) const; + std::vector<UidRangeParcel> mRanges; }; diff --git a/server/UnreachableNetwork.cpp b/server/UnreachableNetwork.cpp new file mode 100644 index 00000000..2f801f0c --- /dev/null +++ b/server/UnreachableNetwork.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2021 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. + */ + +#define LOG_TAG "Netd" + +#include "UnreachableNetwork.h" + +#include "RouteController.h" + +namespace android { +namespace net { + +// The unreachable network is used to reject traffic. It is used for system purposes only. +UnreachableNetwork::UnreachableNetwork(unsigned netId) : Network(netId) {} + +int UnreachableNetwork::addUsers(const UidRanges& uidRanges, uint32_t subPriority) { + if (!isValidSubPriority(subPriority) || !canAddUidRanges(uidRanges, subPriority)) { + return -EINVAL; + } + + int ret = RouteController::addUsersToUnreachableNetwork(mNetId, {{subPriority, uidRanges}}); + if (ret) { + ALOGE("failed to add users to unreachable network"); + return ret; + } + addToUidRangeMap(uidRanges, subPriority); + return 0; +} + +int UnreachableNetwork::removeUsers(const UidRanges& uidRanges, uint32_t subPriority) { + if (!isValidSubPriority(subPriority)) return -EINVAL; + + int ret = + RouteController::removeUsersFromUnreachableNetwork(mNetId, {{subPriority, uidRanges}}); + if (ret) { + ALOGE("failed to remove users from unreachable network"); + return ret; + } + removeFromUidRangeMap(uidRanges, subPriority); + return 0; +} + +bool UnreachableNetwork::isValidSubPriority(uint32_t priority) { + return priority >= UidRanges::DEFAULT_SUB_PRIORITY && + priority <= UidRanges::LOWEST_SUB_PRIORITY; +} + +} // namespace net +} // namespace android diff --git a/server/UnreachableNetwork.h b/server/UnreachableNetwork.h new file mode 100644 index 00000000..f1547d60 --- /dev/null +++ b/server/UnreachableNetwork.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2021 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. + */ + +#pragma once + +#include "Network.h" + +namespace android::net { + +class UnreachableNetwork : public Network { + public: + explicit UnreachableNetwork(unsigned netId); + [[nodiscard]] int addUsers(const UidRanges& uidRanges, uint32_t subPriority) override; + [[nodiscard]] int removeUsers(const UidRanges& uidRanges, uint32_t subPriority) override; + bool isUnreachable() override { return true; } + bool canAddUsers() override { return true; } + + private: + std::string getTypeString() const override { return "UNREACHABLE"; }; + bool isValidSubPriority(uint32_t priority) override; +}; + +} // namespace android::net
\ No newline at end of file diff --git a/server/VirtualNetwork.cpp b/server/VirtualNetwork.cpp index 33791c68..1906e208 100644 --- a/server/VirtualNetwork.cpp +++ b/server/VirtualNetwork.cpp @@ -20,7 +20,6 @@ #include "VirtualNetwork.h" -#include "SockDiag.h" #include "RouteController.h" #include "log/log.h" @@ -28,78 +27,48 @@ namespace android { namespace net { -VirtualNetwork::VirtualNetwork(unsigned netId, bool secure) : Network(netId), mSecure(secure) {} +VirtualNetwork::VirtualNetwork(unsigned netId, bool secure) : Network(netId, secure) {} VirtualNetwork::~VirtualNetwork() {} -bool VirtualNetwork::isSecure() const { - return mSecure; -} - -bool VirtualNetwork::appliesToUser(uid_t uid) const { - return mUidRanges.hasUid(uid); -} - - -int VirtualNetwork::maybeCloseSockets(bool add, const UidRanges& uidRanges, - const std::set<uid_t>& protectableUsers) { - if (!mSecure) { - return 0; - } - - SockDiag sd; - if (!sd.open()) { - return -EBADFD; +int VirtualNetwork::addUsers(const UidRanges& uidRanges, uint32_t subPriority) { + if (!isValidSubPriority(subPriority) || !canAddUidRanges(uidRanges, subPriority)) { + return -EINVAL; } - if (int ret = sd.destroySockets(uidRanges, protectableUsers, true /* excludeLoopback */)) { - ALOGE("Failed to close sockets while %s %s to network %d: %s", - add ? "adding" : "removing", uidRanges.toString().c_str(), mNetId, strerror(-ret)); - return ret; - } - - return 0; -} - -int VirtualNetwork::addUsers(const UidRanges& uidRanges, const std::set<uid_t>& protectableUsers) { - maybeCloseSockets(true, uidRanges, protectableUsers); - for (const std::string& interface : mInterfaces) { - if (int ret = RouteController::addUsersToVirtualNetwork(mNetId, interface.c_str(), mSecure, - uidRanges)) { + int ret = RouteController::addUsersToVirtualNetwork(mNetId, interface.c_str(), mSecure, + {{subPriority, uidRanges}}); + if (ret) { ALOGE("failed to add users on interface %s of netId %u", interface.c_str(), mNetId); return ret; } } - mUidRanges.add(uidRanges); + addToUidRangeMap(uidRanges, subPriority); return 0; } -int VirtualNetwork::removeUsers(const UidRanges& uidRanges, - const std::set<uid_t>& protectableUsers) { - maybeCloseSockets(false, uidRanges, protectableUsers); +int VirtualNetwork::removeUsers(const UidRanges& uidRanges, uint32_t subPriority) { + if (!isValidSubPriority(subPriority)) return -EINVAL; for (const std::string& interface : mInterfaces) { - if (int ret = RouteController::removeUsersFromVirtualNetwork(mNetId, interface.c_str(), - mSecure, uidRanges)) { + int ret = RouteController::removeUsersFromVirtualNetwork(mNetId, interface.c_str(), mSecure, + {{subPriority, uidRanges}}); + if (ret) { ALOGE("failed to remove users on interface %s of netId %u", interface.c_str(), mNetId); return ret; } } - mUidRanges.remove(uidRanges); + removeFromUidRangeMap(uidRanges, subPriority); return 0; } -Network::Type VirtualNetwork::getType() const { - return VIRTUAL; -} - int VirtualNetwork::addInterface(const std::string& interface) { if (hasInterface(interface)) { return 0; } if (int ret = RouteController::addInterfaceToVirtualNetwork(mNetId, interface.c_str(), mSecure, - mUidRanges)) { + mUidRangeMap)) { ALOGE("failed to add interface %s to VPN netId %u", interface.c_str(), mNetId); return ret; } @@ -112,7 +81,7 @@ int VirtualNetwork::removeInterface(const std::string& interface) { return 0; } if (int ret = RouteController::removeInterfaceFromVirtualNetwork(mNetId, interface.c_str(), - mSecure, mUidRanges)) { + mSecure, mUidRangeMap)) { ALOGE("failed to remove interface %s from VPN netId %u", interface.c_str(), mNetId); return ret; } @@ -120,5 +89,10 @@ int VirtualNetwork::removeInterface(const std::string& interface) { return 0; } +bool VirtualNetwork::isValidSubPriority(uint32_t priority) { + // Only supports default subsidiary permissions. + return priority == UidRanges::DEFAULT_SUB_PRIORITY; +} + } // namespace net } // namespace android diff --git a/server/VirtualNetwork.h b/server/VirtualNetwork.h index 20c8dee3..20c9e2c2 100644 --- a/server/VirtualNetwork.h +++ b/server/VirtualNetwork.h @@ -19,7 +19,6 @@ #include <set> #include "Network.h" -#include "UidRanges.h" namespace android::net { @@ -34,23 +33,16 @@ class VirtualNetwork : public Network { public: VirtualNetwork(unsigned netId, bool secure); virtual ~VirtualNetwork(); - - bool isSecure() const; - bool appliesToUser(uid_t uid) const; - - [[nodiscard]] int addUsers(const UidRanges& uidRanges, const std::set<uid_t>& protectableUsers); - [[nodiscard]] int removeUsers(const UidRanges& uidRanges, - const std::set<uid_t>& protectableUsers); + [[nodiscard]] int addUsers(const UidRanges& uidRanges, uint32_t subPriority) override; + [[nodiscard]] int removeUsers(const UidRanges& uidRanges, uint32_t subPriority) override; + bool isVirtual() override { return true; } + bool canAddUsers() override { return true; } private: - Type getType() const override; + std::string getTypeString() const override { return "VIRTUAL"; }; [[nodiscard]] int addInterface(const std::string& interface) override; [[nodiscard]] int removeInterface(const std::string& interface) override; - int maybeCloseSockets(bool add, const UidRanges& uidRanges, - const std::set<uid_t>& protectableUsers); - - const bool mSecure; - UidRanges mUidRanges; + bool isValidSubPriority(uint32_t priority) override; }; } // namespace android::net diff --git a/server/XfrmController.cpp b/server/XfrmController.cpp index 33a2aa21..24168a20 100644 --- a/server/XfrmController.cpp +++ b/server/XfrmController.cpp @@ -646,20 +646,69 @@ netdutils::Status XfrmController::ipSecDeleteSecurityAssociation( return ret; } -netdutils::Status XfrmController::fillXfrmCommonInfo(const std::string& sourceAddress, - const std::string& destinationAddress, - int32_t spi, int32_t markValue, - int32_t markMask, int32_t transformId, - int32_t xfrmInterfaceId, - XfrmCommonInfo* info) { +netdutils::Status XfrmController::ipSecMigrate(int32_t transformId, int32_t selAddrFamily, + int32_t direction, + const std::string& oldSourceAddress, + const std::string& oldDestinationAddress, + const std::string& newSourceAddress, + const std::string& newDestinationAddress, + int32_t xfrmInterfaceId) { + ALOGD("XfrmController:%s, line=%d", __FUNCTION__, __LINE__); + ALOGD("transformId=%d", transformId); + ALOGD("selAddrFamily=%d", selAddrFamily); + ALOGD("direction=%d", direction); + ALOGD("oldSourceAddress=%s", oldSourceAddress.c_str()); + ALOGD("oldDestinationAddress=%s", oldDestinationAddress.c_str()); + ALOGD("newSourceAddress=%s", newSourceAddress.c_str()); + ALOGD("newDestinationAddress=%s", newDestinationAddress.c_str()); + ALOGD("xfrmInterfaceId=%d", xfrmInterfaceId); + + XfrmSocketImpl sock; + Status socketStatus = sock.open(); + if (!socketStatus.ok()) { + ALOGD("Sock open failed for XFRM, line=%d", __LINE__); + return socketStatus; + } + + XfrmMigrateInfo migrateInfo{}; + Status ret = + fillXfrmCommonInfo(oldSourceAddress, oldDestinationAddress, 0 /* spi */, 0 /* mark */, + 0 /* markMask */, transformId, xfrmInterfaceId, &migrateInfo); + + if (!ret.ok()) { + ALOGD("Failed to fill in XfrmCommonInfo, line=%d", __LINE__); + return ret; + } + + migrateInfo.selAddrFamily = selAddrFamily; + migrateInfo.direction = static_cast<XfrmDirection>(direction); + + ret = fillXfrmEndpointPair(newSourceAddress, newDestinationAddress, + &migrateInfo.newEndpointInfo); + if (!ret.ok()) { + ALOGD("Failed to fill in XfrmEndpointPair, line=%d", __LINE__); + return ret; + } + + ret = migrate(migrateInfo, sock); + + if (!ret.ok()) { + ALOGD("Failed to migrate Security Association, line=%d", __LINE__); + } + return ret; +} + +netdutils::Status XfrmController::fillXfrmEndpointPair(const std::string& sourceAddress, + const std::string& destinationAddress, + XfrmEndpointPair* endpointPair) { // Use the addresses to determine the address family and do validation xfrm_address_t sourceXfrmAddr{}, destXfrmAddr{}; StatusOr<int> sourceFamily, destFamily; sourceFamily = convertToXfrmAddr(sourceAddress, &sourceXfrmAddr); destFamily = convertToXfrmAddr(destinationAddress, &destXfrmAddr); if (!isOk(sourceFamily) || !isOk(destFamily)) { - return netdutils::statusFromErrno(EINVAL, "Invalid address " + sourceAddress + "/" + - destinationAddress); + return netdutils::statusFromErrno( + EINVAL, "Invalid address " + sourceAddress + "/" + destinationAddress); } if (destFamily.value() == AF_UNSPEC || @@ -669,10 +718,24 @@ netdutils::Status XfrmController::fillXfrmCommonInfo(const std::string& sourceAd return netdutils::statusFromErrno(EINVAL, "Invalid or mismatched address families"); } - info->addrFamily = destFamily.value(); + endpointPair->addrFamily = destFamily.value(); - info->dstAddr = destXfrmAddr; - info->srcAddr = sourceXfrmAddr; + endpointPair->dstAddr = destXfrmAddr; + endpointPair->srcAddr = sourceXfrmAddr; + + return netdutils::status::ok; +} + +netdutils::Status XfrmController::fillXfrmCommonInfo(const std::string& sourceAddress, + const std::string& destinationAddress, + int32_t spi, int32_t markValue, + int32_t markMask, int32_t transformId, + int32_t xfrmInterfaceId, + XfrmCommonInfo* info) { + Status ret = fillXfrmEndpointPair(sourceAddress, destinationAddress, info); + if (!isOk(ret)) { + return ret; + } return fillXfrmCommonInfo(spi, markValue, markMask, transformId, xfrmInterfaceId, info); } @@ -721,6 +784,7 @@ netdutils::Status XfrmController::ipSecApplyTransportModeTransform( } spInfo.selAddrFamily = spInfo.addrFamily; + spInfo.direction = static_cast<XfrmDirection>(direction); // Allow dual stack sockets. Dual stack sockets are guaranteed to never have an AF_INET source // address; the source address would instead be an IPv4-mapped address. Thus, disallow AF_INET @@ -737,7 +801,7 @@ netdutils::Status XfrmController::ipSecApplyTransportModeTransform( xfrm_user_tmpl tmpl; } policy{}; - fillUserSpInfo(spInfo, static_cast<XfrmDirection>(direction), &policy.info); + fillUserSpInfo(spInfo, &policy.info); fillUserTemplate(spInfo, &policy.tmpl); LOG_HEX("XfrmUserPolicy", reinterpret_cast<char*>(&policy), sizeof(policy)); @@ -853,18 +917,18 @@ netdutils::Status XfrmController::processSecurityPolicy( // separately for IPv4 and IPv6 policies, and thus only need to map a single inner address // family to the outer address families. spInfo.selAddrFamily = selAddrFamily; + spInfo.direction = static_cast<XfrmDirection>(direction); if (msgType == XFRM_MSG_DELPOLICY) { RETURN_IF_NOT_OK(fillXfrmCommonInfo(spi, markValue, markMask, transformId, xfrmInterfaceId, &spInfo)); - return deleteTunnelModeSecurityPolicy(spInfo, sock, static_cast<XfrmDirection>(direction)); + return deleteTunnelModeSecurityPolicy(spInfo, sock); } else { RETURN_IF_NOT_OK(fillXfrmCommonInfo(tmplSrcAddress, tmplDstAddress, spi, markValue, markMask, transformId, xfrmInterfaceId, &spInfo)); - return updateTunnelModeSecurityPolicy(spInfo, sock, static_cast<XfrmDirection>(direction), - msgType); + return updateTunnelModeSecurityPolicy(spInfo, sock, msgType); } } @@ -961,7 +1025,7 @@ netdutils::Status XfrmController::updateSecurityAssociation(const XfrmSaInfo& re len = iov[MARK].iov_len = fillNlAttrXfrmMark(record, &xfrmmark); iov[MARK_PAD].iov_len = NLA_ALIGN(len) - len; - len = iov[OUTPUT_MARK].iov_len = fillNlAttrXfrmOutputMark(record.netId, &xfrmoutputmark); + len = iov[OUTPUT_MARK].iov_len = fillNlAttrXfrmOutputMark(record, &xfrmoutputmark); iov[OUTPUT_MARK_PAD].iov_len = NLA_ALIGN(len) - len; len = iov[ENCAP].iov_len = fillNlAttrXfrmEncapTmpl(record, &encap); @@ -1110,6 +1174,24 @@ netdutils::Status XfrmController::deleteSecurityAssociation(const XfrmCommonInfo return sock.sendMessage(XFRM_MSG_DELSA, NETLINK_REQUEST_FLAGS, 0, &iov); } +netdutils::Status XfrmController::migrate(const XfrmMigrateInfo& record, const XfrmSocket& sock) { + xfrm_userpolicy_id xfrm_policyid{}; + nlattr_xfrm_user_migrate xfrm_migrate{}; + + __kernel_size_t lenPolicyId = fillUserPolicyId(record, &xfrm_policyid); + __kernel_size_t lenXfrmMigrate = fillNlAttrXfrmMigrate(record, &xfrm_migrate); + + std::vector<iovec> iov = { + {nullptr, 0}, // reserved for the eventual addition of a NLMSG_HDR + {&xfrm_policyid, lenPolicyId}, + {kPadBytes, NLMSG_ALIGN(lenPolicyId) - lenPolicyId}, + {&xfrm_migrate, lenXfrmMigrate}, + {kPadBytes, NLMSG_ALIGN(lenXfrmMigrate) - lenXfrmMigrate}, + }; + + return sock.sendMessage(XFRM_MSG_MIGRATE, NETLINK_REQUEST_FLAGS, 0, &iov); +} + netdutils::Status XfrmController::allocateSpi(const XfrmSaInfo& record, uint32_t minSpi, uint32_t maxSpi, uint32_t* outSpi, const XfrmSocket& sock) { @@ -1160,7 +1242,6 @@ netdutils::Status XfrmController::allocateSpi(const XfrmSaInfo& record, uint32_t netdutils::Status XfrmController::updateTunnelModeSecurityPolicy(const XfrmSpInfo& record, const XfrmSocket& sock, - XfrmDirection direction, uint16_t msgType) { xfrm_userpolicy_info userpolicy{}; nlattr_user_tmpl usertmpl{}; @@ -1192,7 +1273,7 @@ netdutils::Status XfrmController::updateTunnelModeSecurityPolicy(const XfrmSpInf }; int len; - len = iov[USERPOLICY].iov_len = fillUserSpInfo(record, direction, &userpolicy); + len = iov[USERPOLICY].iov_len = fillUserSpInfo(record, &userpolicy); iov[USERPOLICY_PAD].iov_len = NLMSG_ALIGN(len) - len; len = iov[USERTMPL].iov_len = fillNlAttrUserTemplate(record, &usertmpl); @@ -1208,8 +1289,7 @@ netdutils::Status XfrmController::updateTunnelModeSecurityPolicy(const XfrmSpInf } netdutils::Status XfrmController::deleteTunnelModeSecurityPolicy(const XfrmSpInfo& record, - const XfrmSocket& sock, - XfrmDirection direction) { + const XfrmSocket& sock) { xfrm_userpolicy_id policyid{}; nlattr_xfrm_mark xfrmmark{}; nlattr_xfrm_interface_id xfrm_if_id{}; @@ -1234,7 +1314,7 @@ netdutils::Status XfrmController::deleteTunnelModeSecurityPolicy(const XfrmSpInf {kPadBytes, 0}, // up to NLATTR_ALIGNTO pad bytes }; - int len = iov[USERPOLICYID].iov_len = fillUserPolicyId(record, direction, &policyid); + int len = iov[USERPOLICYID].iov_len = fillUserPolicyId(record, &policyid); iov[USERPOLICYID_PAD].iov_len = NLMSG_ALIGN(len) - len; len = iov[MARK].iov_len = fillNlAttrXfrmMark(record, &xfrmmark); @@ -1246,15 +1326,14 @@ netdutils::Status XfrmController::deleteTunnelModeSecurityPolicy(const XfrmSpInf return sock.sendMessage(XFRM_MSG_DELPOLICY, NETLINK_REQUEST_FLAGS, 0, &iov); } -int XfrmController::fillUserSpInfo(const XfrmSpInfo& record, XfrmDirection direction, - xfrm_userpolicy_info* usersp) { +int XfrmController::fillUserSpInfo(const XfrmSpInfo& record, xfrm_userpolicy_info* usersp) { fillXfrmSelector(record.selAddrFamily, &usersp->sel); fillXfrmLifetimeDefaults(&usersp->lft); fillXfrmCurLifetimeDefaults(&usersp->curlft); /* if (index) index & 0x3 == dir -- must be true * xfrm_user.c:verify_newpolicy_info() */ usersp->index = 0; - usersp->dir = static_cast<uint8_t>(direction); + usersp->dir = static_cast<uint8_t>(record.direction); usersp->action = XFRM_POLICY_ALLOW; usersp->flags = XFRM_POLICY_LOCALOK; usersp->share = XFRM_SHARE_UNIQUE; @@ -1302,24 +1381,30 @@ int XfrmController::fillNlAttrXfrmMark(const XfrmCommonInfo& record, nlattr_xfrm // This function sets the output mark (or set-mark in newer kernels) to that of the underlying // Network's netid. This allows outbound IPsec Tunnel mode packets to be correctly directed to a -// preselected underlying Network. Packet as marked as protected from VPNs and have a network -// explicitly selected to prevent interference or routing loops. Also set permission flag to -// PERMISSION_SYSTEM to ensure we can use background/restricted networks. Permission to use -// restricted networks is checked in IpSecService. -int XfrmController::fillNlAttrXfrmOutputMark(const __u32 underlyingNetId, +// preselected underlying Network. Outbound packets are marked as protected from VPNs and have a +// network explicitly selected to prevent interference or routing loops. Also sets permission flag +// to PERMISSION_SYSTEM to allow use of background/restricted networks. Inbound packets have all +// the flags and fields cleared to simulate the decapsulated packet being a fresh, unseen packet. +int XfrmController::fillNlAttrXfrmOutputMark(const XfrmSaInfo& record, nlattr_xfrm_output_mark* output_mark) { - // Do not set if we were not given an output mark - if (underlyingNetId == 0) { + // Only set for tunnel mode transforms + if (record.mode != XfrmMode::TUNNEL) { return 0; } Fwmark fwmark; - fwmark.netId = underlyingNetId; - // TODO: Rework this to more accurately follow the underlying network - fwmark.permission = PERMISSION_SYSTEM; - fwmark.explicitlySelected = true; - fwmark.protectedFromVpn = true; + // Only outbound transforms have an underlying network set. + if (record.netId != 0) { + fwmark.netId = record.netId; + fwmark.permission = PERMISSION_SYSTEM; + fwmark.explicitlySelected = true; + fwmark.protectedFromVpn = true; + } + + // Else (inbound transforms), reset to default mark (empty); UID billing for inbound tunnel mode + // transforms are exclusively done on inner packet, and therefore can never have been set. + output_mark->outputMark = fwmark.intValue; int len = NLA_HDRLEN + sizeof(__u32); @@ -1340,11 +1425,28 @@ int XfrmController::fillNlAttrXfrmIntfId(const uint32_t intfIdValue, return len; } -int XfrmController::fillUserPolicyId(const XfrmSpInfo& record, XfrmDirection direction, - xfrm_userpolicy_id* usersp) { +int XfrmController::fillNlAttrXfrmMigrate(const XfrmMigrateInfo& record, + nlattr_xfrm_user_migrate* migrate) { + migrate->migrate.old_daddr = record.dstAddr; + migrate->migrate.old_saddr = record.srcAddr; + migrate->migrate.new_daddr = record.newEndpointInfo.dstAddr; + migrate->migrate.new_saddr = record.newEndpointInfo.srcAddr; + migrate->migrate.proto = IPPROTO_ESP; + migrate->migrate.mode = static_cast<uint8_t>(XfrmMode::TUNNEL); + migrate->migrate.reqid = record.transformId; + migrate->migrate.old_family = record.addrFamily; + migrate->migrate.new_family = record.newEndpointInfo.addrFamily; + + int len = NLA_HDRLEN + sizeof(xfrm_user_migrate); + fillXfrmNlaHdr(&migrate->hdr, XFRMA_MIGRATE, len); + + return len; +} + +int XfrmController::fillUserPolicyId(const XfrmSpInfo& record, xfrm_userpolicy_id* usersp) { // For DELPOLICY, when index is absent, selector is needed to match the policy fillXfrmSelector(record.selAddrFamily, &usersp->sel); - usersp->dir = static_cast<uint8_t>(direction); + usersp->dir = static_cast<uint8_t>(record.direction); return sizeof(*usersp); } diff --git a/server/XfrmController.h b/server/XfrmController.h index 15eef3d7..4f167c5f 100644 --- a/server/XfrmController.h +++ b/server/XfrmController.h @@ -107,15 +107,19 @@ struct XfrmEncap { uint16_t dstPort; }; -// minimally sufficient structure to match either an SA or a Policy -struct XfrmCommonInfo { +struct XfrmEndpointPair { xfrm_address_t dstAddr; // network order xfrm_address_t srcAddr; int addrFamily; // AF_INET or AF_INET6 +}; + +// minimally sufficient structure to match either an SA or a Policy +struct XfrmCommonInfo : XfrmEndpointPair { int transformId; // requestId int spi; xfrm_mark mark; int xfrm_if_id; + XfrmMode mode; }; struct XfrmSaInfo : XfrmCommonInfo { @@ -123,14 +127,18 @@ struct XfrmSaInfo : XfrmCommonInfo { XfrmAlgo crypt; XfrmAlgo aead; int netId; - XfrmMode mode; XfrmEncap encap; }; -struct XfrmSpInfo : XfrmSaInfo { +struct XfrmSpInfo : XfrmCommonInfo { // Address family in XfrmCommonInfo used for template/SA matching, need separate addrFamily // for selectors int selAddrFamily; // AF_INET or AF_INET6 + XfrmDirection direction; +}; + +struct XfrmMigrateInfo : XfrmSpInfo { + XfrmEndpointPair newEndpointInfo; }; /* @@ -260,6 +268,13 @@ public: static netdutils::Status ipSecRemoveTunnelInterface(const std::string& deviceName); + // Only available for Tunnel must already have a matching tunnel SA and policy + static netdutils::Status ipSecMigrate(int32_t transformId, int32_t selAddrFamily, + int32_t direction, const std::string& oldSourceAddress, + const std::string& oldDestinationAddress, + const std::string& newSourceAddress, + const std::string& newDestinationAddress, + int32_t xfrmInterfaceId); void dump(netdutils::DumpWriter& dw); // Some XFRM netlink attributes comprise a header, a struct, and some data @@ -329,6 +344,13 @@ public: __u32 if_id; }; + // Container for the content of an XFRMA_MIGRATE netlink attribute. + // Exposed for testing + struct nlattr_xfrm_user_migrate { + nlattr hdr; + xfrm_user_migrate migrate; + }; + // Exposed for testing struct nlattr_payload_u32 { nlattr hdr; @@ -338,6 +360,9 @@ public: private: static bool isXfrmIntfSupported(); + static netdutils::Status fillXfrmEndpointPair(const std::string& sourceAddress, + const std::string& destinationAddress, + XfrmEndpointPair* info); // helper functions for filling in the XfrmCommonInfo (and XfrmSaInfo) structure static netdutils::Status fillXfrmCommonInfo(const std::string& sourceAddress, const std::string& destinationAddress, int32_t spi, @@ -373,15 +398,15 @@ public: static int fillUserSaId(const XfrmCommonInfo& record, xfrm_usersa_id* said); static void fillUserTemplate(const XfrmSpInfo& record, xfrm_user_tmpl* tmpl); - static int fillUserSpInfo(const XfrmSpInfo& record, XfrmDirection direction, - xfrm_userpolicy_info* usersp); + static int fillUserSpInfo(const XfrmSpInfo& record, xfrm_userpolicy_info* usersp); static int fillNlAttrUserTemplate(const XfrmSpInfo& record, nlattr_user_tmpl* tmpl); - static int fillUserPolicyId(const XfrmSpInfo& record, XfrmDirection direction, - xfrm_userpolicy_id* policy_id); + static int fillUserPolicyId(const XfrmSpInfo& record, xfrm_userpolicy_id* policy_id); static int fillNlAttrXfrmMark(const XfrmCommonInfo& record, nlattr_xfrm_mark* mark); - static int fillNlAttrXfrmOutputMark(const __u32 underlyingNetId, + static int fillNlAttrXfrmOutputMark(const XfrmSaInfo& record, nlattr_xfrm_output_mark* output_mark); static int fillNlAttrXfrmIntfId(const __u32 intf_id_value, nlattr_xfrm_interface_id* intf_id); + static int fillNlAttrXfrmMigrate(const XfrmMigrateInfo& record, + nlattr_xfrm_user_migrate* migrate); static netdutils::Status allocateSpi(const XfrmSaInfo& record, uint32_t minSpi, uint32_t maxSpi, uint32_t* outSpi, const XfrmSocket& sock); @@ -394,11 +419,10 @@ public: int32_t xfrmInterfaceId, int32_t msgType); static netdutils::Status updateTunnelModeSecurityPolicy(const XfrmSpInfo& record, const XfrmSocket& sock, - XfrmDirection direction, uint16_t msgType); static netdutils::Status deleteTunnelModeSecurityPolicy(const XfrmSpInfo& record, - const XfrmSocket& sock, - XfrmDirection direction); + const XfrmSocket& sock); + static netdutils::Status migrate(const XfrmMigrateInfo& record, const XfrmSocket& sock); static netdutils::Status flushInterfaces(); static netdutils::Status flushSaDb(const XfrmSocket& s); static netdutils::Status flushPolicyDb(const XfrmSocket& s); diff --git a/server/aidl_api/netd_aidl_interface/5/.hash b/server/aidl_api/netd_aidl_interface/5/.hash new file mode 100644 index 00000000..a6ced453 --- /dev/null +++ b/server/aidl_api/netd_aidl_interface/5/.hash @@ -0,0 +1 @@ +d97c56dd789cee9eeb5cdcec43a99df0a01873a5 diff --git a/server/aidl_api/netd_aidl_interface/5/android/net/INetd.aidl b/server/aidl_api/netd_aidl_interface/5/android/net/INetd.aidl new file mode 100644 index 00000000..b30748a3 --- /dev/null +++ b/server/aidl_api/netd_aidl_interface/5/android/net/INetd.aidl @@ -0,0 +1,167 @@ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL interface (or parcelable). Do not try to +// edit this file. It looks like you are doing that because you have modified +// an AIDL interface in a backward-incompatible way, e.g., deleting a function +// from an interface or a field from a parcelable and it broke the build. That +// breakage is intended. +// +// You must not make a backward incompatible changes to the AIDL files built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +/* @hide */ +interface INetd { + boolean isAlive(); + boolean firewallReplaceUidChain(in @utf8InCpp String chainName, boolean isAllowlist, in int[] uids); + boolean bandwidthEnableDataSaver(boolean enable); + void networkCreatePhysical(int netId, int permission); + void networkCreateVpn(int netId, boolean secure); + void networkDestroy(int netId); + void networkAddInterface(int netId, in @utf8InCpp String iface); + void networkRemoveInterface(int netId, in @utf8InCpp String iface); + void networkAddUidRanges(int netId, in android.net.UidRangeParcel[] uidRanges); + void networkRemoveUidRanges(int netId, in android.net.UidRangeParcel[] uidRanges); + void networkRejectNonSecureVpn(boolean add, in android.net.UidRangeParcel[] uidRanges); + void socketDestroy(in android.net.UidRangeParcel[] uidRanges, in int[] exemptUids); + boolean tetherApplyDnsInterfaces(); + android.net.TetherStatsParcel[] tetherGetStats(); + void interfaceAddAddress(in @utf8InCpp String ifName, in @utf8InCpp String addrString, int prefixLength); + void interfaceDelAddress(in @utf8InCpp String ifName, in @utf8InCpp String addrString, int prefixLength); + @utf8InCpp String getProcSysNet(int ipversion, int which, in @utf8InCpp String ifname, in @utf8InCpp String parameter); + void setProcSysNet(int ipversion, int which, in @utf8InCpp String ifname, in @utf8InCpp String parameter, in @utf8InCpp String value); + void ipSecSetEncapSocketOwner(in ParcelFileDescriptor socket, int newUid); + int ipSecAllocateSpi(int transformId, in @utf8InCpp String sourceAddress, in @utf8InCpp String destinationAddress, int spi); + void ipSecAddSecurityAssociation(int transformId, int mode, in @utf8InCpp String sourceAddress, in @utf8InCpp String destinationAddress, int underlyingNetId, int spi, int markValue, int markMask, in @utf8InCpp String authAlgo, in byte[] authKey, in int authTruncBits, in @utf8InCpp String cryptAlgo, in byte[] cryptKey, in int cryptTruncBits, in @utf8InCpp String aeadAlgo, in byte[] aeadKey, in int aeadIcvBits, int encapType, int encapLocalPort, int encapRemotePort, int interfaceId); + void ipSecDeleteSecurityAssociation(int transformId, in @utf8InCpp String sourceAddress, in @utf8InCpp String destinationAddress, int spi, int markValue, int markMask, int interfaceId); + void ipSecApplyTransportModeTransform(in ParcelFileDescriptor socket, int transformId, int direction, in @utf8InCpp String sourceAddress, in @utf8InCpp String destinationAddress, int spi); + void ipSecRemoveTransportModeTransform(in ParcelFileDescriptor socket); + void ipSecAddSecurityPolicy(int transformId, int selAddrFamily, int direction, in @utf8InCpp String tmplSrcAddress, in @utf8InCpp String tmplDstAddress, int spi, int markValue, int markMask, int interfaceId); + void ipSecUpdateSecurityPolicy(int transformId, int selAddrFamily, int direction, in @utf8InCpp String tmplSrcAddress, in @utf8InCpp String tmplDstAddress, int spi, int markValue, int markMask, int interfaceId); + void ipSecDeleteSecurityPolicy(int transformId, int selAddrFamily, int direction, int markValue, int markMask, int interfaceId); + void ipSecAddTunnelInterface(in @utf8InCpp String deviceName, in @utf8InCpp String localAddress, in @utf8InCpp String remoteAddress, int iKey, int oKey, int interfaceId); + void ipSecUpdateTunnelInterface(in @utf8InCpp String deviceName, in @utf8InCpp String localAddress, in @utf8InCpp String remoteAddress, int iKey, int oKey, int interfaceId); + void ipSecRemoveTunnelInterface(in @utf8InCpp String deviceName); + void wakeupAddInterface(in @utf8InCpp String ifName, in @utf8InCpp String prefix, int mark, int mask); + void wakeupDelInterface(in @utf8InCpp String ifName, in @utf8InCpp String prefix, int mark, int mask); + void setIPv6AddrGenMode(in @utf8InCpp String ifName, int mode); + void idletimerAddInterface(in @utf8InCpp String ifName, int timeout, in @utf8InCpp String classLabel); + void idletimerRemoveInterface(in @utf8InCpp String ifName, int timeout, in @utf8InCpp String classLabel); + void strictUidCleartextPenalty(int uid, int policyPenalty); + @utf8InCpp String clatdStart(in @utf8InCpp String ifName, in @utf8InCpp String nat64Prefix); + void clatdStop(in @utf8InCpp String ifName); + boolean ipfwdEnabled(); + @utf8InCpp String[] ipfwdGetRequesterList(); + void ipfwdEnableForwarding(in @utf8InCpp String requester); + void ipfwdDisableForwarding(in @utf8InCpp String requester); + void ipfwdAddInterfaceForward(in @utf8InCpp String fromIface, in @utf8InCpp String toIface); + void ipfwdRemoveInterfaceForward(in @utf8InCpp String fromIface, in @utf8InCpp String toIface); + void bandwidthSetInterfaceQuota(in @utf8InCpp String ifName, long bytes); + void bandwidthRemoveInterfaceQuota(in @utf8InCpp String ifName); + void bandwidthSetInterfaceAlert(in @utf8InCpp String ifName, long bytes); + void bandwidthRemoveInterfaceAlert(in @utf8InCpp String ifName); + void bandwidthSetGlobalAlert(long bytes); + void bandwidthAddNaughtyApp(int uid); + void bandwidthRemoveNaughtyApp(int uid); + void bandwidthAddNiceApp(int uid); + void bandwidthRemoveNiceApp(int uid); + void tetherStart(in @utf8InCpp String[] dhcpRanges); + void tetherStop(); + boolean tetherIsEnabled(); + void tetherInterfaceAdd(in @utf8InCpp String ifName); + void tetherInterfaceRemove(in @utf8InCpp String ifName); + @utf8InCpp String[] tetherInterfaceList(); + void tetherDnsSet(int netId, in @utf8InCpp String[] dnsAddrs); + @utf8InCpp String[] tetherDnsList(); + void networkAddRoute(int netId, in @utf8InCpp String ifName, in @utf8InCpp String destination, in @utf8InCpp String nextHop); + void networkRemoveRoute(int netId, in @utf8InCpp String ifName, in @utf8InCpp String destination, in @utf8InCpp String nextHop); + void networkAddLegacyRoute(int netId, in @utf8InCpp String ifName, in @utf8InCpp String destination, in @utf8InCpp String nextHop, int uid); + void networkRemoveLegacyRoute(int netId, in @utf8InCpp String ifName, in @utf8InCpp String destination, in @utf8InCpp String nextHop, int uid); + int networkGetDefault(); + void networkSetDefault(int netId); + void networkClearDefault(); + void networkSetPermissionForNetwork(int netId, int permission); + void networkSetPermissionForUser(int permission, in int[] uids); + void networkClearPermissionForUser(in int[] uids); + void trafficSetNetPermForUids(int permission, in int[] uids); + void networkSetProtectAllow(int uid); + void networkSetProtectDeny(int uid); + boolean networkCanProtect(int uid); + void firewallSetFirewallType(int firewalltype); + void firewallSetInterfaceRule(in @utf8InCpp String ifName, int firewallRule); + void firewallSetUidRule(int childChain, int uid, int firewallRule); + void firewallEnableChildChain(int childChain, boolean enable); + @utf8InCpp String[] interfaceGetList(); + android.net.InterfaceConfigurationParcel interfaceGetCfg(in @utf8InCpp String ifName); + void interfaceSetCfg(in android.net.InterfaceConfigurationParcel cfg); + void interfaceSetIPv6PrivacyExtensions(in @utf8InCpp String ifName, boolean enable); + void interfaceClearAddrs(in @utf8InCpp String ifName); + void interfaceSetEnableIPv6(in @utf8InCpp String ifName, boolean enable); + void interfaceSetMtu(in @utf8InCpp String ifName, int mtu); + void tetherAddForward(in @utf8InCpp String intIface, in @utf8InCpp String extIface); + void tetherRemoveForward(in @utf8InCpp String intIface, in @utf8InCpp String extIface); + void setTcpRWmemorySize(in @utf8InCpp String rmemValues, in @utf8InCpp String wmemValues); + void registerUnsolicitedEventListener(android.net.INetdUnsolicitedEventListener listener); + void firewallAddUidInterfaceRules(in @utf8InCpp String ifName, in int[] uids); + void firewallRemoveUidInterfaceRules(in int[] uids); + void trafficSwapActiveStatsMap(); + IBinder getOemNetd(); + void tetherStartWithConfiguration(in android.net.TetherConfigParcel config); + android.net.MarkMaskParcel getFwmarkForNetwork(int netId); + void networkAddRouteParcel(int netId, in android.net.RouteInfoParcel routeInfo); + void networkUpdateRouteParcel(int netId, in android.net.RouteInfoParcel routeInfo); + void networkRemoveRouteParcel(int netId, in android.net.RouteInfoParcel routeInfo); + void tetherOffloadRuleAdd(in android.net.TetherOffloadRuleParcel rule); + void tetherOffloadRuleRemove(in android.net.TetherOffloadRuleParcel rule); + android.net.TetherStatsParcel[] tetherOffloadGetStats(); + void tetherOffloadSetInterfaceQuota(int ifIndex, long quotaBytes); + android.net.TetherStatsParcel tetherOffloadGetAndClearStats(int ifIndex); + const int IPV4 = 4; + const int IPV6 = 6; + const int CONF = 1; + const int NEIGH = 2; + const String IPSEC_INTERFACE_PREFIX = "ipsec"; + const int IPV6_ADDR_GEN_MODE_EUI64 = 0; + const int IPV6_ADDR_GEN_MODE_NONE = 1; + const int IPV6_ADDR_GEN_MODE_STABLE_PRIVACY = 2; + const int IPV6_ADDR_GEN_MODE_RANDOM = 3; + const int IPV6_ADDR_GEN_MODE_DEFAULT = 0; + const int PENALTY_POLICY_ACCEPT = 1; + const int PENALTY_POLICY_LOG = 2; + const int PENALTY_POLICY_REJECT = 3; + const int LOCAL_NET_ID = 99; + const String NEXTHOP_NONE = ""; + const String NEXTHOP_UNREACHABLE = "unreachable"; + const String NEXTHOP_THROW = "throw"; + const int PERMISSION_NONE = 0; + const int PERMISSION_NETWORK = 1; + const int PERMISSION_SYSTEM = 2; + const int NO_PERMISSIONS = 0; + const int PERMISSION_INTERNET = 4; + const int PERMISSION_UPDATE_DEVICE_STATS = 8; + const int PERMISSION_UNINSTALLED = -1; + const @JavaPassthrough(annotation="@Deprecated") int FIREWALL_WHITELIST = 0; + const int FIREWALL_ALLOWLIST = 0; + const @JavaPassthrough(annotation="@Deprecated") int FIREWALL_BLACKLIST = 1; + const int FIREWALL_DENYLIST = 1; + const int FIREWALL_RULE_ALLOW = 1; + const int FIREWALL_RULE_DENY = 2; + const int FIREWALL_CHAIN_NONE = 0; + const int FIREWALL_CHAIN_DOZABLE = 1; + const int FIREWALL_CHAIN_STANDBY = 2; + const int FIREWALL_CHAIN_POWERSAVE = 3; + const int FIREWALL_CHAIN_RESTRICTED = 4; + const String IF_STATE_UP = "up"; + const String IF_STATE_DOWN = "down"; + const String IF_FLAG_BROADCAST = "broadcast"; + const String IF_FLAG_LOOPBACK = "loopback"; + const String IF_FLAG_POINTOPOINT = "point-to-point"; + const String IF_FLAG_RUNNING = "running"; + const String IF_FLAG_MULTICAST = "multicast"; +} diff --git a/server/aidl_api/netd_aidl_interface/5/android/net/INetdUnsolicitedEventListener.aidl b/server/aidl_api/netd_aidl_interface/5/android/net/INetdUnsolicitedEventListener.aidl new file mode 100644 index 00000000..44593632 --- /dev/null +++ b/server/aidl_api/netd_aidl_interface/5/android/net/INetdUnsolicitedEventListener.aidl @@ -0,0 +1,32 @@ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL interface (or parcelable). Do not try to +// edit this file. It looks like you are doing that because you have modified +// an AIDL interface in a backward-incompatible way, e.g., deleting a function +// from an interface or a field from a parcelable and it broke the build. That +// breakage is intended. +// +// You must not make a backward incompatible changes to the AIDL files built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +/* @hide */ +interface INetdUnsolicitedEventListener { + oneway void onInterfaceClassActivityChanged(boolean isActive, int timerLabel, long timestampNs, int uid); + oneway void onQuotaLimitReached(@utf8InCpp String alertName, @utf8InCpp String ifName); + oneway void onInterfaceDnsServerInfo(@utf8InCpp String ifName, long lifetimeS, in @utf8InCpp String[] servers); + oneway void onInterfaceAddressUpdated(@utf8InCpp String addr, @utf8InCpp String ifName, int flags, int scope); + oneway void onInterfaceAddressRemoved(@utf8InCpp String addr, @utf8InCpp String ifName, int flags, int scope); + oneway void onInterfaceAdded(@utf8InCpp String ifName); + oneway void onInterfaceRemoved(@utf8InCpp String ifName); + oneway void onInterfaceChanged(@utf8InCpp String ifName, boolean up); + oneway void onInterfaceLinkStateChanged(@utf8InCpp String ifName, boolean up); + oneway void onRouteChanged(boolean updated, @utf8InCpp String route, @utf8InCpp String gateway, @utf8InCpp String ifName); + oneway void onStrictCleartextDetected(int uid, @utf8InCpp String hex); +} diff --git a/server/aidl_api/netd_aidl_interface/5/android/net/InterfaceConfigurationParcel.aidl b/server/aidl_api/netd_aidl_interface/5/android/net/InterfaceConfigurationParcel.aidl new file mode 100644 index 00000000..01e0f955 --- /dev/null +++ b/server/aidl_api/netd_aidl_interface/5/android/net/InterfaceConfigurationParcel.aidl @@ -0,0 +1,26 @@ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL interface (or parcelable). Do not try to +// edit this file. It looks like you are doing that because you have modified +// an AIDL interface in a backward-incompatible way, e.g., deleting a function +// from an interface or a field from a parcelable and it broke the build. That +// breakage is intended. +// +// You must not make a backward incompatible changes to the AIDL files built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +/* @hide */ +parcelable InterfaceConfigurationParcel { + @utf8InCpp String ifName; + @utf8InCpp String hwAddr; + @utf8InCpp String ipv4Addr; + int prefixLength; + @utf8InCpp String[] flags; +} diff --git a/server/aidl_api/netd_aidl_interface/5/android/net/MarkMaskParcel.aidl b/server/aidl_api/netd_aidl_interface/5/android/net/MarkMaskParcel.aidl new file mode 100644 index 00000000..62be8384 --- /dev/null +++ b/server/aidl_api/netd_aidl_interface/5/android/net/MarkMaskParcel.aidl @@ -0,0 +1,23 @@ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL interface (or parcelable). Do not try to +// edit this file. It looks like you are doing that because you have modified +// an AIDL interface in a backward-incompatible way, e.g., deleting a function +// from an interface or a field from a parcelable and it broke the build. That +// breakage is intended. +// +// You must not make a backward incompatible changes to the AIDL files built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +/* @hide */ +parcelable MarkMaskParcel { + int mark; + int mask; +} diff --git a/server/aidl_api/netd_aidl_interface/5/android/net/RouteInfoParcel.aidl b/server/aidl_api/netd_aidl_interface/5/android/net/RouteInfoParcel.aidl new file mode 100644 index 00000000..5e0ee62a --- /dev/null +++ b/server/aidl_api/netd_aidl_interface/5/android/net/RouteInfoParcel.aidl @@ -0,0 +1,24 @@ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL interface (or parcelable). Do not try to +// edit this file. It looks like you are doing that because you have modified +// an AIDL interface in a backward-incompatible way, e.g., deleting a function +// from an interface or a field from a parcelable and it broke the build. That +// breakage is intended. +// +// You must not make a backward incompatible changes to the AIDL files built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +parcelable RouteInfoParcel { + @utf8InCpp String destination; + @utf8InCpp String ifName; + @utf8InCpp String nextHop; + int mtu; +} diff --git a/server/aidl_api/netd_aidl_interface/5/android/net/TetherConfigParcel.aidl b/server/aidl_api/netd_aidl_interface/5/android/net/TetherConfigParcel.aidl new file mode 100644 index 00000000..b1364545 --- /dev/null +++ b/server/aidl_api/netd_aidl_interface/5/android/net/TetherConfigParcel.aidl @@ -0,0 +1,23 @@ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL interface (or parcelable). Do not try to +// edit this file. It looks like you are doing that because you have modified +// an AIDL interface in a backward-incompatible way, e.g., deleting a function +// from an interface or a field from a parcelable and it broke the build. That +// breakage is intended. +// +// You must not make a backward incompatible changes to the AIDL files built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +/* @hide */ +parcelable TetherConfigParcel { + boolean usingLegacyDnsProxy; + @utf8InCpp String[] dhcpRanges; +} diff --git a/server/aidl_api/netd_aidl_interface/5/android/net/TetherOffloadRuleParcel.aidl b/server/aidl_api/netd_aidl_interface/5/android/net/TetherOffloadRuleParcel.aidl new file mode 100644 index 00000000..c9d84580 --- /dev/null +++ b/server/aidl_api/netd_aidl_interface/5/android/net/TetherOffloadRuleParcel.aidl @@ -0,0 +1,28 @@ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL interface (or parcelable). Do not try to +// edit this file. It looks like you are doing that because you have modified +// an AIDL interface in a backward-incompatible way, e.g., deleting a function +// from an interface or a field from a parcelable and it broke the build. That +// breakage is intended. +// +// You must not make a backward incompatible changes to the AIDL files built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +/* @hide */ +parcelable TetherOffloadRuleParcel { + int inputInterfaceIndex; + int outputInterfaceIndex; + byte[] destination; + int prefixLength; + byte[] srcL2Address; + byte[] dstL2Address; + int pmtu = 1500; +} diff --git a/server/aidl_api/netd_aidl_interface/5/android/net/TetherStatsParcel.aidl b/server/aidl_api/netd_aidl_interface/5/android/net/TetherStatsParcel.aidl new file mode 100644 index 00000000..0b0960ef --- /dev/null +++ b/server/aidl_api/netd_aidl_interface/5/android/net/TetherStatsParcel.aidl @@ -0,0 +1,27 @@ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL interface (or parcelable). Do not try to +// edit this file. It looks like you are doing that because you have modified +// an AIDL interface in a backward-incompatible way, e.g., deleting a function +// from an interface or a field from a parcelable and it broke the build. That +// breakage is intended. +// +// You must not make a backward incompatible changes to the AIDL files built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +/* @hide */ +parcelable TetherStatsParcel { + @utf8InCpp String iface; + long rxBytes; + long rxPackets; + long txBytes; + long txPackets; + int ifIndex = 0; +} diff --git a/server/aidl_api/netd_aidl_interface/5/android/net/UidRangeParcel.aidl b/server/aidl_api/netd_aidl_interface/5/android/net/UidRangeParcel.aidl new file mode 100644 index 00000000..debc6be2 --- /dev/null +++ b/server/aidl_api/netd_aidl_interface/5/android/net/UidRangeParcel.aidl @@ -0,0 +1,24 @@ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL interface (or parcelable). Do not try to +// edit this file. It looks like you are doing that because you have modified +// an AIDL interface in a backward-incompatible way, e.g., deleting a function +// from an interface or a field from a parcelable and it broke the build. That +// breakage is intended. +// +// You must not make a backward incompatible changes to the AIDL files built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +/* @hide */ +@JavaDerive(equals=true, toString=true) @JavaOnlyImmutable +parcelable UidRangeParcel { + int start; + int stop; +} diff --git a/server/aidl_api/netd_aidl_interface/6/.hash b/server/aidl_api/netd_aidl_interface/6/.hash new file mode 100644 index 00000000..f5acf5d1 --- /dev/null +++ b/server/aidl_api/netd_aidl_interface/6/.hash @@ -0,0 +1 @@ +b08451d9673b09cba84f1fd8740e1fdac64ff7be diff --git a/server/aidl_api/netd_aidl_interface/6/android/net/INetd.aidl b/server/aidl_api/netd_aidl_interface/6/android/net/INetd.aidl new file mode 100644 index 00000000..a7952f28 --- /dev/null +++ b/server/aidl_api/netd_aidl_interface/6/android/net/INetd.aidl @@ -0,0 +1,198 @@ +/** + * Copyright (c) 2016, 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +/* @hide */ +interface INetd { + boolean isAlive(); + boolean firewallReplaceUidChain(in @utf8InCpp String chainName, boolean isAllowlist, in int[] uids); + boolean bandwidthEnableDataSaver(boolean enable); + /** + * @deprecated use networkCreate() instead. + */ + void networkCreatePhysical(int netId, int permission); + /** + * @deprecated use networkCreate() instead. + */ + void networkCreateVpn(int netId, boolean secure); + void networkDestroy(int netId); + void networkAddInterface(int netId, in @utf8InCpp String iface); + void networkRemoveInterface(int netId, in @utf8InCpp String iface); + void networkAddUidRanges(int netId, in android.net.UidRangeParcel[] uidRanges); + void networkRemoveUidRanges(int netId, in android.net.UidRangeParcel[] uidRanges); + void networkRejectNonSecureVpn(boolean add, in android.net.UidRangeParcel[] uidRanges); + void socketDestroy(in android.net.UidRangeParcel[] uidRanges, in int[] exemptUids); + boolean tetherApplyDnsInterfaces(); + android.net.TetherStatsParcel[] tetherGetStats(); + void interfaceAddAddress(in @utf8InCpp String ifName, in @utf8InCpp String addrString, int prefixLength); + void interfaceDelAddress(in @utf8InCpp String ifName, in @utf8InCpp String addrString, int prefixLength); + @utf8InCpp String getProcSysNet(int ipversion, int which, in @utf8InCpp String ifname, in @utf8InCpp String parameter); + void setProcSysNet(int ipversion, int which, in @utf8InCpp String ifname, in @utf8InCpp String parameter, in @utf8InCpp String value); + void ipSecSetEncapSocketOwner(in ParcelFileDescriptor socket, int newUid); + int ipSecAllocateSpi(int transformId, in @utf8InCpp String sourceAddress, in @utf8InCpp String destinationAddress, int spi); + void ipSecAddSecurityAssociation(int transformId, int mode, in @utf8InCpp String sourceAddress, in @utf8InCpp String destinationAddress, int underlyingNetId, int spi, int markValue, int markMask, in @utf8InCpp String authAlgo, in byte[] authKey, in int authTruncBits, in @utf8InCpp String cryptAlgo, in byte[] cryptKey, in int cryptTruncBits, in @utf8InCpp String aeadAlgo, in byte[] aeadKey, in int aeadIcvBits, int encapType, int encapLocalPort, int encapRemotePort, int interfaceId); + void ipSecDeleteSecurityAssociation(int transformId, in @utf8InCpp String sourceAddress, in @utf8InCpp String destinationAddress, int spi, int markValue, int markMask, int interfaceId); + void ipSecApplyTransportModeTransform(in ParcelFileDescriptor socket, int transformId, int direction, in @utf8InCpp String sourceAddress, in @utf8InCpp String destinationAddress, int spi); + void ipSecRemoveTransportModeTransform(in ParcelFileDescriptor socket); + void ipSecAddSecurityPolicy(int transformId, int selAddrFamily, int direction, in @utf8InCpp String tmplSrcAddress, in @utf8InCpp String tmplDstAddress, int spi, int markValue, int markMask, int interfaceId); + void ipSecUpdateSecurityPolicy(int transformId, int selAddrFamily, int direction, in @utf8InCpp String tmplSrcAddress, in @utf8InCpp String tmplDstAddress, int spi, int markValue, int markMask, int interfaceId); + void ipSecDeleteSecurityPolicy(int transformId, int selAddrFamily, int direction, int markValue, int markMask, int interfaceId); + void ipSecAddTunnelInterface(in @utf8InCpp String deviceName, in @utf8InCpp String localAddress, in @utf8InCpp String remoteAddress, int iKey, int oKey, int interfaceId); + void ipSecUpdateTunnelInterface(in @utf8InCpp String deviceName, in @utf8InCpp String localAddress, in @utf8InCpp String remoteAddress, int iKey, int oKey, int interfaceId); + void ipSecRemoveTunnelInterface(in @utf8InCpp String deviceName); + void wakeupAddInterface(in @utf8InCpp String ifName, in @utf8InCpp String prefix, int mark, int mask); + void wakeupDelInterface(in @utf8InCpp String ifName, in @utf8InCpp String prefix, int mark, int mask); + void setIPv6AddrGenMode(in @utf8InCpp String ifName, int mode); + void idletimerAddInterface(in @utf8InCpp String ifName, int timeout, in @utf8InCpp String classLabel); + void idletimerRemoveInterface(in @utf8InCpp String ifName, int timeout, in @utf8InCpp String classLabel); + void strictUidCleartextPenalty(int uid, int policyPenalty); + @utf8InCpp String clatdStart(in @utf8InCpp String ifName, in @utf8InCpp String nat64Prefix); + void clatdStop(in @utf8InCpp String ifName); + boolean ipfwdEnabled(); + @utf8InCpp String[] ipfwdGetRequesterList(); + void ipfwdEnableForwarding(in @utf8InCpp String requester); + void ipfwdDisableForwarding(in @utf8InCpp String requester); + void ipfwdAddInterfaceForward(in @utf8InCpp String fromIface, in @utf8InCpp String toIface); + void ipfwdRemoveInterfaceForward(in @utf8InCpp String fromIface, in @utf8InCpp String toIface); + void bandwidthSetInterfaceQuota(in @utf8InCpp String ifName, long bytes); + void bandwidthRemoveInterfaceQuota(in @utf8InCpp String ifName); + void bandwidthSetInterfaceAlert(in @utf8InCpp String ifName, long bytes); + void bandwidthRemoveInterfaceAlert(in @utf8InCpp String ifName); + void bandwidthSetGlobalAlert(long bytes); + void bandwidthAddNaughtyApp(int uid); + void bandwidthRemoveNaughtyApp(int uid); + void bandwidthAddNiceApp(int uid); + void bandwidthRemoveNiceApp(int uid); + void tetherStart(in @utf8InCpp String[] dhcpRanges); + void tetherStop(); + boolean tetherIsEnabled(); + void tetherInterfaceAdd(in @utf8InCpp String ifName); + void tetherInterfaceRemove(in @utf8InCpp String ifName); + @utf8InCpp String[] tetherInterfaceList(); + void tetherDnsSet(int netId, in @utf8InCpp String[] dnsAddrs); + @utf8InCpp String[] tetherDnsList(); + void networkAddRoute(int netId, in @utf8InCpp String ifName, in @utf8InCpp String destination, in @utf8InCpp String nextHop); + void networkRemoveRoute(int netId, in @utf8InCpp String ifName, in @utf8InCpp String destination, in @utf8InCpp String nextHop); + void networkAddLegacyRoute(int netId, in @utf8InCpp String ifName, in @utf8InCpp String destination, in @utf8InCpp String nextHop, int uid); + void networkRemoveLegacyRoute(int netId, in @utf8InCpp String ifName, in @utf8InCpp String destination, in @utf8InCpp String nextHop, int uid); + int networkGetDefault(); + void networkSetDefault(int netId); + void networkClearDefault(); + void networkSetPermissionForNetwork(int netId, int permission); + void networkSetPermissionForUser(int permission, in int[] uids); + void networkClearPermissionForUser(in int[] uids); + void trafficSetNetPermForUids(int permission, in int[] uids); + void networkSetProtectAllow(int uid); + void networkSetProtectDeny(int uid); + boolean networkCanProtect(int uid); + void firewallSetFirewallType(int firewalltype); + void firewallSetInterfaceRule(in @utf8InCpp String ifName, int firewallRule); + void firewallSetUidRule(int childChain, int uid, int firewallRule); + void firewallEnableChildChain(int childChain, boolean enable); + @utf8InCpp String[] interfaceGetList(); + android.net.InterfaceConfigurationParcel interfaceGetCfg(in @utf8InCpp String ifName); + void interfaceSetCfg(in android.net.InterfaceConfigurationParcel cfg); + void interfaceSetIPv6PrivacyExtensions(in @utf8InCpp String ifName, boolean enable); + void interfaceClearAddrs(in @utf8InCpp String ifName); + void interfaceSetEnableIPv6(in @utf8InCpp String ifName, boolean enable); + void interfaceSetMtu(in @utf8InCpp String ifName, int mtu); + void tetherAddForward(in @utf8InCpp String intIface, in @utf8InCpp String extIface); + void tetherRemoveForward(in @utf8InCpp String intIface, in @utf8InCpp String extIface); + void setTcpRWmemorySize(in @utf8InCpp String rmemValues, in @utf8InCpp String wmemValues); + void registerUnsolicitedEventListener(android.net.INetdUnsolicitedEventListener listener); + void firewallAddUidInterfaceRules(in @utf8InCpp String ifName, in int[] uids); + void firewallRemoveUidInterfaceRules(in int[] uids); + void trafficSwapActiveStatsMap(); + IBinder getOemNetd(); + void tetherStartWithConfiguration(in android.net.TetherConfigParcel config); + android.net.MarkMaskParcel getFwmarkForNetwork(int netId); + void networkAddRouteParcel(int netId, in android.net.RouteInfoParcel routeInfo); + void networkUpdateRouteParcel(int netId, in android.net.RouteInfoParcel routeInfo); + void networkRemoveRouteParcel(int netId, in android.net.RouteInfoParcel routeInfo); + void tetherOffloadRuleAdd(in android.net.TetherOffloadRuleParcel rule); + void tetherOffloadRuleRemove(in android.net.TetherOffloadRuleParcel rule); + android.net.TetherStatsParcel[] tetherOffloadGetStats(); + void tetherOffloadSetInterfaceQuota(int ifIndex, long quotaBytes); + android.net.TetherStatsParcel tetherOffloadGetAndClearStats(int ifIndex); + void networkCreate(in android.net.NativeNetworkConfig config); + const int IPV4 = 4; + const int IPV6 = 6; + const int CONF = 1; + const int NEIGH = 2; + const String IPSEC_INTERFACE_PREFIX = "ipsec"; + const int IPV6_ADDR_GEN_MODE_EUI64 = 0; + const int IPV6_ADDR_GEN_MODE_NONE = 1; + const int IPV6_ADDR_GEN_MODE_STABLE_PRIVACY = 2; + const int IPV6_ADDR_GEN_MODE_RANDOM = 3; + const int IPV6_ADDR_GEN_MODE_DEFAULT = 0; + const int PENALTY_POLICY_ACCEPT = 1; + const int PENALTY_POLICY_LOG = 2; + const int PENALTY_POLICY_REJECT = 3; + const int LOCAL_NET_ID = 99; + const int DUMMY_NET_ID = 51; + const int UNREACHABLE_NET_ID = 52; + const String NEXTHOP_NONE = ""; + const String NEXTHOP_UNREACHABLE = "unreachable"; + const String NEXTHOP_THROW = "throw"; + const int PERMISSION_NONE = 0; + const int PERMISSION_NETWORK = 1; + const int PERMISSION_SYSTEM = 2; + const int NO_PERMISSIONS = 0; + const int PERMISSION_INTERNET = 4; + const int PERMISSION_UPDATE_DEVICE_STATS = 8; + const int PERMISSION_UNINSTALLED = -1; + /** + * @deprecated use FIREWALL_ALLOWLIST. + */ + const int FIREWALL_WHITELIST = 0; + const int FIREWALL_ALLOWLIST = 0; + /** + * @deprecated use FIREWALL_DENYLIST. + */ + const int FIREWALL_BLACKLIST = 1; + const int FIREWALL_DENYLIST = 1; + const int FIREWALL_RULE_ALLOW = 1; + const int FIREWALL_RULE_DENY = 2; + const int FIREWALL_CHAIN_NONE = 0; + const int FIREWALL_CHAIN_DOZABLE = 1; + const int FIREWALL_CHAIN_STANDBY = 2; + const int FIREWALL_CHAIN_POWERSAVE = 3; + const int FIREWALL_CHAIN_RESTRICTED = 4; + const String IF_STATE_UP = "up"; + const String IF_STATE_DOWN = "down"; + const String IF_FLAG_BROADCAST = "broadcast"; + const String IF_FLAG_LOOPBACK = "loopback"; + const String IF_FLAG_POINTOPOINT = "point-to-point"; + const String IF_FLAG_RUNNING = "running"; + const String IF_FLAG_MULTICAST = "multicast"; +} diff --git a/server/aidl_api/netd_aidl_interface/6/android/net/INetdUnsolicitedEventListener.aidl b/server/aidl_api/netd_aidl_interface/6/android/net/INetdUnsolicitedEventListener.aidl new file mode 100644 index 00000000..31775dfd --- /dev/null +++ b/server/aidl_api/netd_aidl_interface/6/android/net/INetdUnsolicitedEventListener.aidl @@ -0,0 +1,48 @@ +/** + * Copyright (c) 2018, 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +/* @hide */ +interface INetdUnsolicitedEventListener { + oneway void onInterfaceClassActivityChanged(boolean isActive, int timerLabel, long timestampNs, int uid); + oneway void onQuotaLimitReached(@utf8InCpp String alertName, @utf8InCpp String ifName); + oneway void onInterfaceDnsServerInfo(@utf8InCpp String ifName, long lifetimeS, in @utf8InCpp String[] servers); + oneway void onInterfaceAddressUpdated(@utf8InCpp String addr, @utf8InCpp String ifName, int flags, int scope); + oneway void onInterfaceAddressRemoved(@utf8InCpp String addr, @utf8InCpp String ifName, int flags, int scope); + oneway void onInterfaceAdded(@utf8InCpp String ifName); + oneway void onInterfaceRemoved(@utf8InCpp String ifName); + oneway void onInterfaceChanged(@utf8InCpp String ifName, boolean up); + oneway void onInterfaceLinkStateChanged(@utf8InCpp String ifName, boolean up); + oneway void onRouteChanged(boolean updated, @utf8InCpp String route, @utf8InCpp String gateway, @utf8InCpp String ifName); + oneway void onStrictCleartextDetected(int uid, @utf8InCpp String hex); +} diff --git a/server/aidl_api/netd_aidl_interface/6/android/net/InterfaceConfigurationParcel.aidl b/server/aidl_api/netd_aidl_interface/6/android/net/InterfaceConfigurationParcel.aidl new file mode 100644 index 00000000..1869d8d4 --- /dev/null +++ b/server/aidl_api/netd_aidl_interface/6/android/net/InterfaceConfigurationParcel.aidl @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2018 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +/* @hide */ +parcelable InterfaceConfigurationParcel { + @utf8InCpp String ifName; + @utf8InCpp String hwAddr; + @utf8InCpp String ipv4Addr; + int prefixLength; + @utf8InCpp String[] flags; +} diff --git a/server/aidl_api/netd_aidl_interface/6/android/net/MarkMaskParcel.aidl b/server/aidl_api/netd_aidl_interface/6/android/net/MarkMaskParcel.aidl new file mode 100644 index 00000000..8ea20d11 --- /dev/null +++ b/server/aidl_api/netd_aidl_interface/6/android/net/MarkMaskParcel.aidl @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2019 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +/* @hide */ +parcelable MarkMaskParcel { + int mark; + int mask; +} diff --git a/server/aidl_api/netd_aidl_interface/6/android/net/NativeNetworkConfig.aidl b/server/aidl_api/netd_aidl_interface/6/android/net/NativeNetworkConfig.aidl new file mode 100644 index 00000000..76562b29 --- /dev/null +++ b/server/aidl_api/netd_aidl_interface/6/android/net/NativeNetworkConfig.aidl @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2021 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +/* @hide */ +@JavaDerive(equals=true, toString=true) @JavaOnlyImmutable +parcelable NativeNetworkConfig { + int netId; + android.net.NativeNetworkType networkType = android.net.NativeNetworkType.PHYSICAL; + int permission; + boolean secure; + android.net.NativeVpnType vpnType = android.net.NativeVpnType.PLATFORM; +} diff --git a/server/aidl_api/netd_aidl_interface/6/android/net/NativeNetworkType.aidl b/server/aidl_api/netd_aidl_interface/6/android/net/NativeNetworkType.aidl new file mode 100644 index 00000000..06c8979d --- /dev/null +++ b/server/aidl_api/netd_aidl_interface/6/android/net/NativeNetworkType.aidl @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2021 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +@Backing(type="int") +enum NativeNetworkType { + PHYSICAL = 0, + VIRTUAL = 1, +} diff --git a/server/aidl_api/netd_aidl_interface/6/android/net/NativeVpnType.aidl b/server/aidl_api/netd_aidl_interface/6/android/net/NativeVpnType.aidl new file mode 100644 index 00000000..8a8be839 --- /dev/null +++ b/server/aidl_api/netd_aidl_interface/6/android/net/NativeVpnType.aidl @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2021 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +@Backing(type="int") +enum NativeVpnType { + SERVICE = 1, + PLATFORM = 2, + LEGACY = 3, + OEM = 4, +} diff --git a/server/aidl_api/netd_aidl_interface/6/android/net/RouteInfoParcel.aidl b/server/aidl_api/netd_aidl_interface/6/android/net/RouteInfoParcel.aidl new file mode 100644 index 00000000..5ef95e67 --- /dev/null +++ b/server/aidl_api/netd_aidl_interface/6/android/net/RouteInfoParcel.aidl @@ -0,0 +1,40 @@ +/** + * Copyright (c) 2020, 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +parcelable RouteInfoParcel { + @utf8InCpp String destination; + @utf8InCpp String ifName; + @utf8InCpp String nextHop; + int mtu; +} diff --git a/server/aidl_api/netd_aidl_interface/6/android/net/TetherConfigParcel.aidl b/server/aidl_api/netd_aidl_interface/6/android/net/TetherConfigParcel.aidl new file mode 100644 index 00000000..7b39c22e --- /dev/null +++ b/server/aidl_api/netd_aidl_interface/6/android/net/TetherConfigParcel.aidl @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2019 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +/* @hide */ +parcelable TetherConfigParcel { + boolean usingLegacyDnsProxy; + @utf8InCpp String[] dhcpRanges; +} diff --git a/server/aidl_api/netd_aidl_interface/6/android/net/TetherOffloadRuleParcel.aidl b/server/aidl_api/netd_aidl_interface/6/android/net/TetherOffloadRuleParcel.aidl new file mode 100644 index 00000000..983e9860 --- /dev/null +++ b/server/aidl_api/netd_aidl_interface/6/android/net/TetherOffloadRuleParcel.aidl @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2020 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +/* @hide */ +parcelable TetherOffloadRuleParcel { + int inputInterfaceIndex; + int outputInterfaceIndex; + byte[] destination; + int prefixLength; + byte[] srcL2Address; + byte[] dstL2Address; + int pmtu = 1500; +} diff --git a/server/aidl_api/netd_aidl_interface/6/android/net/TetherStatsParcel.aidl b/server/aidl_api/netd_aidl_interface/6/android/net/TetherStatsParcel.aidl new file mode 100644 index 00000000..5f1b7226 --- /dev/null +++ b/server/aidl_api/netd_aidl_interface/6/android/net/TetherStatsParcel.aidl @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2018 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +/* @hide */ +parcelable TetherStatsParcel { + @utf8InCpp String iface; + long rxBytes; + long rxPackets; + long txBytes; + long txPackets; + int ifIndex = 0; +} diff --git a/server/aidl_api/netd_aidl_interface/6/android/net/UidRangeParcel.aidl b/server/aidl_api/netd_aidl_interface/6/android/net/UidRangeParcel.aidl new file mode 100644 index 00000000..72e987a2 --- /dev/null +++ b/server/aidl_api/netd_aidl_interface/6/android/net/UidRangeParcel.aidl @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2018 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +/* @hide */ +@JavaDerive(equals=true, toString=true) @JavaOnlyImmutable +parcelable UidRangeParcel { + int start; + int stop; +} diff --git a/server/aidl_api/netd_aidl_interface/7/.hash b/server/aidl_api/netd_aidl_interface/7/.hash new file mode 100644 index 00000000..cad59dfd --- /dev/null +++ b/server/aidl_api/netd_aidl_interface/7/.hash @@ -0,0 +1 @@ +850353de5d19a0dd718f8fd20791f0532e6a34c7 diff --git a/server/aidl_api/netd_aidl_interface/7/android/net/INetd.aidl b/server/aidl_api/netd_aidl_interface/7/android/net/INetd.aidl new file mode 100644 index 00000000..ec03d86b --- /dev/null +++ b/server/aidl_api/netd_aidl_interface/7/android/net/INetd.aidl @@ -0,0 +1,200 @@ +/** + * Copyright (c) 2016, 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +/* @hide */ +interface INetd { + boolean isAlive(); + boolean firewallReplaceUidChain(in @utf8InCpp String chainName, boolean isAllowlist, in int[] uids); + boolean bandwidthEnableDataSaver(boolean enable); + /** + * @deprecated use networkCreate() instead. + */ + void networkCreatePhysical(int netId, int permission); + /** + * @deprecated use networkCreate() instead. + */ + void networkCreateVpn(int netId, boolean secure); + void networkDestroy(int netId); + void networkAddInterface(int netId, in @utf8InCpp String iface); + void networkRemoveInterface(int netId, in @utf8InCpp String iface); + void networkAddUidRanges(int netId, in android.net.UidRangeParcel[] uidRanges); + void networkRemoveUidRanges(int netId, in android.net.UidRangeParcel[] uidRanges); + void networkRejectNonSecureVpn(boolean add, in android.net.UidRangeParcel[] uidRanges); + void socketDestroy(in android.net.UidRangeParcel[] uidRanges, in int[] exemptUids); + boolean tetherApplyDnsInterfaces(); + android.net.TetherStatsParcel[] tetherGetStats(); + void interfaceAddAddress(in @utf8InCpp String ifName, in @utf8InCpp String addrString, int prefixLength); + void interfaceDelAddress(in @utf8InCpp String ifName, in @utf8InCpp String addrString, int prefixLength); + @utf8InCpp String getProcSysNet(int ipversion, int which, in @utf8InCpp String ifname, in @utf8InCpp String parameter); + void setProcSysNet(int ipversion, int which, in @utf8InCpp String ifname, in @utf8InCpp String parameter, in @utf8InCpp String value); + void ipSecSetEncapSocketOwner(in ParcelFileDescriptor socket, int newUid); + int ipSecAllocateSpi(int transformId, in @utf8InCpp String sourceAddress, in @utf8InCpp String destinationAddress, int spi); + void ipSecAddSecurityAssociation(int transformId, int mode, in @utf8InCpp String sourceAddress, in @utf8InCpp String destinationAddress, int underlyingNetId, int spi, int markValue, int markMask, in @utf8InCpp String authAlgo, in byte[] authKey, in int authTruncBits, in @utf8InCpp String cryptAlgo, in byte[] cryptKey, in int cryptTruncBits, in @utf8InCpp String aeadAlgo, in byte[] aeadKey, in int aeadIcvBits, int encapType, int encapLocalPort, int encapRemotePort, int interfaceId); + void ipSecDeleteSecurityAssociation(int transformId, in @utf8InCpp String sourceAddress, in @utf8InCpp String destinationAddress, int spi, int markValue, int markMask, int interfaceId); + void ipSecApplyTransportModeTransform(in ParcelFileDescriptor socket, int transformId, int direction, in @utf8InCpp String sourceAddress, in @utf8InCpp String destinationAddress, int spi); + void ipSecRemoveTransportModeTransform(in ParcelFileDescriptor socket); + void ipSecAddSecurityPolicy(int transformId, int selAddrFamily, int direction, in @utf8InCpp String tmplSrcAddress, in @utf8InCpp String tmplDstAddress, int spi, int markValue, int markMask, int interfaceId); + void ipSecUpdateSecurityPolicy(int transformId, int selAddrFamily, int direction, in @utf8InCpp String tmplSrcAddress, in @utf8InCpp String tmplDstAddress, int spi, int markValue, int markMask, int interfaceId); + void ipSecDeleteSecurityPolicy(int transformId, int selAddrFamily, int direction, int markValue, int markMask, int interfaceId); + void ipSecAddTunnelInterface(in @utf8InCpp String deviceName, in @utf8InCpp String localAddress, in @utf8InCpp String remoteAddress, int iKey, int oKey, int interfaceId); + void ipSecUpdateTunnelInterface(in @utf8InCpp String deviceName, in @utf8InCpp String localAddress, in @utf8InCpp String remoteAddress, int iKey, int oKey, int interfaceId); + void ipSecRemoveTunnelInterface(in @utf8InCpp String deviceName); + void wakeupAddInterface(in @utf8InCpp String ifName, in @utf8InCpp String prefix, int mark, int mask); + void wakeupDelInterface(in @utf8InCpp String ifName, in @utf8InCpp String prefix, int mark, int mask); + void setIPv6AddrGenMode(in @utf8InCpp String ifName, int mode); + void idletimerAddInterface(in @utf8InCpp String ifName, int timeout, in @utf8InCpp String classLabel); + void idletimerRemoveInterface(in @utf8InCpp String ifName, int timeout, in @utf8InCpp String classLabel); + void strictUidCleartextPenalty(int uid, int policyPenalty); + @utf8InCpp String clatdStart(in @utf8InCpp String ifName, in @utf8InCpp String nat64Prefix); + void clatdStop(in @utf8InCpp String ifName); + boolean ipfwdEnabled(); + @utf8InCpp String[] ipfwdGetRequesterList(); + void ipfwdEnableForwarding(in @utf8InCpp String requester); + void ipfwdDisableForwarding(in @utf8InCpp String requester); + void ipfwdAddInterfaceForward(in @utf8InCpp String fromIface, in @utf8InCpp String toIface); + void ipfwdRemoveInterfaceForward(in @utf8InCpp String fromIface, in @utf8InCpp String toIface); + void bandwidthSetInterfaceQuota(in @utf8InCpp String ifName, long bytes); + void bandwidthRemoveInterfaceQuota(in @utf8InCpp String ifName); + void bandwidthSetInterfaceAlert(in @utf8InCpp String ifName, long bytes); + void bandwidthRemoveInterfaceAlert(in @utf8InCpp String ifName); + void bandwidthSetGlobalAlert(long bytes); + void bandwidthAddNaughtyApp(int uid); + void bandwidthRemoveNaughtyApp(int uid); + void bandwidthAddNiceApp(int uid); + void bandwidthRemoveNiceApp(int uid); + void tetherStart(in @utf8InCpp String[] dhcpRanges); + void tetherStop(); + boolean tetherIsEnabled(); + void tetherInterfaceAdd(in @utf8InCpp String ifName); + void tetherInterfaceRemove(in @utf8InCpp String ifName); + @utf8InCpp String[] tetherInterfaceList(); + void tetherDnsSet(int netId, in @utf8InCpp String[] dnsAddrs); + @utf8InCpp String[] tetherDnsList(); + void networkAddRoute(int netId, in @utf8InCpp String ifName, in @utf8InCpp String destination, in @utf8InCpp String nextHop); + void networkRemoveRoute(int netId, in @utf8InCpp String ifName, in @utf8InCpp String destination, in @utf8InCpp String nextHop); + void networkAddLegacyRoute(int netId, in @utf8InCpp String ifName, in @utf8InCpp String destination, in @utf8InCpp String nextHop, int uid); + void networkRemoveLegacyRoute(int netId, in @utf8InCpp String ifName, in @utf8InCpp String destination, in @utf8InCpp String nextHop, int uid); + int networkGetDefault(); + void networkSetDefault(int netId); + void networkClearDefault(); + void networkSetPermissionForNetwork(int netId, int permission); + void networkSetPermissionForUser(int permission, in int[] uids); + void networkClearPermissionForUser(in int[] uids); + void trafficSetNetPermForUids(int permission, in int[] uids); + void networkSetProtectAllow(int uid); + void networkSetProtectDeny(int uid); + boolean networkCanProtect(int uid); + void firewallSetFirewallType(int firewalltype); + void firewallSetInterfaceRule(in @utf8InCpp String ifName, int firewallRule); + void firewallSetUidRule(int childChain, int uid, int firewallRule); + void firewallEnableChildChain(int childChain, boolean enable); + @utf8InCpp String[] interfaceGetList(); + android.net.InterfaceConfigurationParcel interfaceGetCfg(in @utf8InCpp String ifName); + void interfaceSetCfg(in android.net.InterfaceConfigurationParcel cfg); + void interfaceSetIPv6PrivacyExtensions(in @utf8InCpp String ifName, boolean enable); + void interfaceClearAddrs(in @utf8InCpp String ifName); + void interfaceSetEnableIPv6(in @utf8InCpp String ifName, boolean enable); + void interfaceSetMtu(in @utf8InCpp String ifName, int mtu); + void tetherAddForward(in @utf8InCpp String intIface, in @utf8InCpp String extIface); + void tetherRemoveForward(in @utf8InCpp String intIface, in @utf8InCpp String extIface); + void setTcpRWmemorySize(in @utf8InCpp String rmemValues, in @utf8InCpp String wmemValues); + void registerUnsolicitedEventListener(android.net.INetdUnsolicitedEventListener listener); + void firewallAddUidInterfaceRules(in @utf8InCpp String ifName, in int[] uids); + void firewallRemoveUidInterfaceRules(in int[] uids); + void trafficSwapActiveStatsMap(); + IBinder getOemNetd(); + void tetherStartWithConfiguration(in android.net.TetherConfigParcel config); + android.net.MarkMaskParcel getFwmarkForNetwork(int netId); + void networkAddRouteParcel(int netId, in android.net.RouteInfoParcel routeInfo); + void networkUpdateRouteParcel(int netId, in android.net.RouteInfoParcel routeInfo); + void networkRemoveRouteParcel(int netId, in android.net.RouteInfoParcel routeInfo); + void tetherOffloadRuleAdd(in android.net.TetherOffloadRuleParcel rule); + void tetherOffloadRuleRemove(in android.net.TetherOffloadRuleParcel rule); + android.net.TetherStatsParcel[] tetherOffloadGetStats(); + void tetherOffloadSetInterfaceQuota(int ifIndex, long quotaBytes); + android.net.TetherStatsParcel tetherOffloadGetAndClearStats(int ifIndex); + void networkCreate(in android.net.NativeNetworkConfig config); + void networkAddUidRangesParcel(in android.net.netd.aidl.NativeUidRangeConfig uidRangesConfig); + void networkRemoveUidRangesParcel(in android.net.netd.aidl.NativeUidRangeConfig uidRangesConfig); + const int IPV4 = 4; + const int IPV6 = 6; + const int CONF = 1; + const int NEIGH = 2; + const String IPSEC_INTERFACE_PREFIX = "ipsec"; + const int IPV6_ADDR_GEN_MODE_EUI64 = 0; + const int IPV6_ADDR_GEN_MODE_NONE = 1; + const int IPV6_ADDR_GEN_MODE_STABLE_PRIVACY = 2; + const int IPV6_ADDR_GEN_MODE_RANDOM = 3; + const int IPV6_ADDR_GEN_MODE_DEFAULT = 0; + const int PENALTY_POLICY_ACCEPT = 1; + const int PENALTY_POLICY_LOG = 2; + const int PENALTY_POLICY_REJECT = 3; + const int LOCAL_NET_ID = 99; + const int DUMMY_NET_ID = 51; + const int UNREACHABLE_NET_ID = 52; + const String NEXTHOP_NONE = ""; + const String NEXTHOP_UNREACHABLE = "unreachable"; + const String NEXTHOP_THROW = "throw"; + const int PERMISSION_NONE = 0; + const int PERMISSION_NETWORK = 1; + const int PERMISSION_SYSTEM = 2; + const int NO_PERMISSIONS = 0; + const int PERMISSION_INTERNET = 4; + const int PERMISSION_UPDATE_DEVICE_STATS = 8; + const int PERMISSION_UNINSTALLED = -1; + /** + * @deprecated use FIREWALL_ALLOWLIST. + */ + const int FIREWALL_WHITELIST = 0; + const int FIREWALL_ALLOWLIST = 0; + /** + * @deprecated use FIREWALL_DENYLIST. + */ + const int FIREWALL_BLACKLIST = 1; + const int FIREWALL_DENYLIST = 1; + const int FIREWALL_RULE_ALLOW = 1; + const int FIREWALL_RULE_DENY = 2; + const int FIREWALL_CHAIN_NONE = 0; + const int FIREWALL_CHAIN_DOZABLE = 1; + const int FIREWALL_CHAIN_STANDBY = 2; + const int FIREWALL_CHAIN_POWERSAVE = 3; + const int FIREWALL_CHAIN_RESTRICTED = 4; + const String IF_STATE_UP = "up"; + const String IF_STATE_DOWN = "down"; + const String IF_FLAG_BROADCAST = "broadcast"; + const String IF_FLAG_LOOPBACK = "loopback"; + const String IF_FLAG_POINTOPOINT = "point-to-point"; + const String IF_FLAG_RUNNING = "running"; + const String IF_FLAG_MULTICAST = "multicast"; +} diff --git a/server/aidl_api/netd_aidl_interface/7/android/net/INetdUnsolicitedEventListener.aidl b/server/aidl_api/netd_aidl_interface/7/android/net/INetdUnsolicitedEventListener.aidl new file mode 100644 index 00000000..31775dfd --- /dev/null +++ b/server/aidl_api/netd_aidl_interface/7/android/net/INetdUnsolicitedEventListener.aidl @@ -0,0 +1,48 @@ +/** + * Copyright (c) 2018, 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +/* @hide */ +interface INetdUnsolicitedEventListener { + oneway void onInterfaceClassActivityChanged(boolean isActive, int timerLabel, long timestampNs, int uid); + oneway void onQuotaLimitReached(@utf8InCpp String alertName, @utf8InCpp String ifName); + oneway void onInterfaceDnsServerInfo(@utf8InCpp String ifName, long lifetimeS, in @utf8InCpp String[] servers); + oneway void onInterfaceAddressUpdated(@utf8InCpp String addr, @utf8InCpp String ifName, int flags, int scope); + oneway void onInterfaceAddressRemoved(@utf8InCpp String addr, @utf8InCpp String ifName, int flags, int scope); + oneway void onInterfaceAdded(@utf8InCpp String ifName); + oneway void onInterfaceRemoved(@utf8InCpp String ifName); + oneway void onInterfaceChanged(@utf8InCpp String ifName, boolean up); + oneway void onInterfaceLinkStateChanged(@utf8InCpp String ifName, boolean up); + oneway void onRouteChanged(boolean updated, @utf8InCpp String route, @utf8InCpp String gateway, @utf8InCpp String ifName); + oneway void onStrictCleartextDetected(int uid, @utf8InCpp String hex); +} diff --git a/server/aidl_api/netd_aidl_interface/7/android/net/InterfaceConfigurationParcel.aidl b/server/aidl_api/netd_aidl_interface/7/android/net/InterfaceConfigurationParcel.aidl new file mode 100644 index 00000000..1869d8d4 --- /dev/null +++ b/server/aidl_api/netd_aidl_interface/7/android/net/InterfaceConfigurationParcel.aidl @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2018 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +/* @hide */ +parcelable InterfaceConfigurationParcel { + @utf8InCpp String ifName; + @utf8InCpp String hwAddr; + @utf8InCpp String ipv4Addr; + int prefixLength; + @utf8InCpp String[] flags; +} diff --git a/server/aidl_api/netd_aidl_interface/7/android/net/MarkMaskParcel.aidl b/server/aidl_api/netd_aidl_interface/7/android/net/MarkMaskParcel.aidl new file mode 100644 index 00000000..8ea20d11 --- /dev/null +++ b/server/aidl_api/netd_aidl_interface/7/android/net/MarkMaskParcel.aidl @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2019 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +/* @hide */ +parcelable MarkMaskParcel { + int mark; + int mask; +} diff --git a/server/aidl_api/netd_aidl_interface/7/android/net/NativeNetworkConfig.aidl b/server/aidl_api/netd_aidl_interface/7/android/net/NativeNetworkConfig.aidl new file mode 100644 index 00000000..76562b29 --- /dev/null +++ b/server/aidl_api/netd_aidl_interface/7/android/net/NativeNetworkConfig.aidl @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2021 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +/* @hide */ +@JavaDerive(equals=true, toString=true) @JavaOnlyImmutable +parcelable NativeNetworkConfig { + int netId; + android.net.NativeNetworkType networkType = android.net.NativeNetworkType.PHYSICAL; + int permission; + boolean secure; + android.net.NativeVpnType vpnType = android.net.NativeVpnType.PLATFORM; +} diff --git a/server/aidl_api/netd_aidl_interface/7/android/net/NativeNetworkType.aidl b/server/aidl_api/netd_aidl_interface/7/android/net/NativeNetworkType.aidl new file mode 100644 index 00000000..06c8979d --- /dev/null +++ b/server/aidl_api/netd_aidl_interface/7/android/net/NativeNetworkType.aidl @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2021 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +@Backing(type="int") +enum NativeNetworkType { + PHYSICAL = 0, + VIRTUAL = 1, +} diff --git a/server/aidl_api/netd_aidl_interface/7/android/net/NativeVpnType.aidl b/server/aidl_api/netd_aidl_interface/7/android/net/NativeVpnType.aidl new file mode 100644 index 00000000..8a8be839 --- /dev/null +++ b/server/aidl_api/netd_aidl_interface/7/android/net/NativeVpnType.aidl @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2021 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +@Backing(type="int") +enum NativeVpnType { + SERVICE = 1, + PLATFORM = 2, + LEGACY = 3, + OEM = 4, +} diff --git a/server/aidl_api/netd_aidl_interface/7/android/net/RouteInfoParcel.aidl b/server/aidl_api/netd_aidl_interface/7/android/net/RouteInfoParcel.aidl new file mode 100644 index 00000000..5ef95e67 --- /dev/null +++ b/server/aidl_api/netd_aidl_interface/7/android/net/RouteInfoParcel.aidl @@ -0,0 +1,40 @@ +/** + * Copyright (c) 2020, 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +parcelable RouteInfoParcel { + @utf8InCpp String destination; + @utf8InCpp String ifName; + @utf8InCpp String nextHop; + int mtu; +} diff --git a/server/aidl_api/netd_aidl_interface/7/android/net/TetherConfigParcel.aidl b/server/aidl_api/netd_aidl_interface/7/android/net/TetherConfigParcel.aidl new file mode 100644 index 00000000..7b39c22e --- /dev/null +++ b/server/aidl_api/netd_aidl_interface/7/android/net/TetherConfigParcel.aidl @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2019 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +/* @hide */ +parcelable TetherConfigParcel { + boolean usingLegacyDnsProxy; + @utf8InCpp String[] dhcpRanges; +} diff --git a/server/aidl_api/netd_aidl_interface/7/android/net/TetherOffloadRuleParcel.aidl b/server/aidl_api/netd_aidl_interface/7/android/net/TetherOffloadRuleParcel.aidl new file mode 100644 index 00000000..983e9860 --- /dev/null +++ b/server/aidl_api/netd_aidl_interface/7/android/net/TetherOffloadRuleParcel.aidl @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2020 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +/* @hide */ +parcelable TetherOffloadRuleParcel { + int inputInterfaceIndex; + int outputInterfaceIndex; + byte[] destination; + int prefixLength; + byte[] srcL2Address; + byte[] dstL2Address; + int pmtu = 1500; +} diff --git a/server/aidl_api/netd_aidl_interface/7/android/net/TetherStatsParcel.aidl b/server/aidl_api/netd_aidl_interface/7/android/net/TetherStatsParcel.aidl new file mode 100644 index 00000000..5f1b7226 --- /dev/null +++ b/server/aidl_api/netd_aidl_interface/7/android/net/TetherStatsParcel.aidl @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2018 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +/* @hide */ +parcelable TetherStatsParcel { + @utf8InCpp String iface; + long rxBytes; + long rxPackets; + long txBytes; + long txPackets; + int ifIndex = 0; +} diff --git a/server/aidl_api/netd_aidl_interface/7/android/net/UidRangeParcel.aidl b/server/aidl_api/netd_aidl_interface/7/android/net/UidRangeParcel.aidl new file mode 100644 index 00000000..72e987a2 --- /dev/null +++ b/server/aidl_api/netd_aidl_interface/7/android/net/UidRangeParcel.aidl @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2018 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +/* @hide */ +@JavaDerive(equals=true, toString=true) @JavaOnlyImmutable +parcelable UidRangeParcel { + int start; + int stop; +} diff --git a/server/aidl_api/netd_aidl_interface/7/android/net/netd/aidl/NativeUidRangeConfig.aidl b/server/aidl_api/netd_aidl_interface/7/android/net/netd/aidl/NativeUidRangeConfig.aidl new file mode 100644 index 00000000..9bb679f1 --- /dev/null +++ b/server/aidl_api/netd_aidl_interface/7/android/net/netd/aidl/NativeUidRangeConfig.aidl @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2021 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net.netd.aidl; +/* @hide */ +@JavaDerive(equals=true, toString=true) @JavaOnlyImmutable +parcelable NativeUidRangeConfig { + int netId; + android.net.UidRangeParcel[] uidRanges; + int subPriority; +} diff --git a/server/aidl_api/netd_aidl_interface/current/android/net/INetd.aidl b/server/aidl_api/netd_aidl_interface/current/android/net/INetd.aidl index 47e2931d..ec03d86b 100644 --- a/server/aidl_api/netd_aidl_interface/current/android/net/INetd.aidl +++ b/server/aidl_api/netd_aidl_interface/current/android/net/INetd.aidl @@ -1,14 +1,30 @@ +/** + * Copyright (c) 2016, 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. + */ /////////////////////////////////////////////////////////////////////////////// // THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // /////////////////////////////////////////////////////////////////////////////// -// This file is a snapshot of an AIDL interface (or parcelable). Do not try to -// edit this file. It looks like you are doing that because you have modified -// an AIDL interface in a backward-incompatible way, e.g., deleting a function -// from an interface or a field from a parcelable and it broke the build. That -// breakage is intended. +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. // -// You must not make a backward incompatible changes to the AIDL files built +// You must not make a backward incompatible change to any AIDL file built // with the aidl_interface module type with versions property set. The module // type is used to build AIDL files in a way that they can be used across // independently updatable components of the system. If a device is shipped @@ -19,9 +35,15 @@ package android.net; /* @hide */ interface INetd { boolean isAlive(); - boolean firewallReplaceUidChain(in @utf8InCpp String chainName, boolean isWhitelist, in int[] uids); + boolean firewallReplaceUidChain(in @utf8InCpp String chainName, boolean isAllowlist, in int[] uids); boolean bandwidthEnableDataSaver(boolean enable); + /** + * @deprecated use networkCreate() instead. + */ void networkCreatePhysical(int netId, int permission); + /** + * @deprecated use networkCreate() instead. + */ void networkCreateVpn(int netId, boolean secure); void networkDestroy(int netId); void networkAddInterface(int netId, in @utf8InCpp String iface); @@ -122,6 +144,9 @@ interface INetd { android.net.TetherStatsParcel[] tetherOffloadGetStats(); void tetherOffloadSetInterfaceQuota(int ifIndex, long quotaBytes); android.net.TetherStatsParcel tetherOffloadGetAndClearStats(int ifIndex); + void networkCreate(in android.net.NativeNetworkConfig config); + void networkAddUidRangesParcel(in android.net.netd.aidl.NativeUidRangeConfig uidRangesConfig); + void networkRemoveUidRangesParcel(in android.net.netd.aidl.NativeUidRangeConfig uidRangesConfig); const int IPV4 = 4; const int IPV6 = 6; const int CONF = 1; @@ -136,6 +161,8 @@ interface INetd { const int PENALTY_POLICY_LOG = 2; const int PENALTY_POLICY_REJECT = 3; const int LOCAL_NET_ID = 99; + const int DUMMY_NET_ID = 51; + const int UNREACHABLE_NET_ID = 52; const String NEXTHOP_NONE = ""; const String NEXTHOP_UNREACHABLE = "unreachable"; const String NEXTHOP_THROW = "throw"; @@ -146,14 +173,23 @@ interface INetd { const int PERMISSION_INTERNET = 4; const int PERMISSION_UPDATE_DEVICE_STATS = 8; const int PERMISSION_UNINSTALLED = -1; + /** + * @deprecated use FIREWALL_ALLOWLIST. + */ const int FIREWALL_WHITELIST = 0; + const int FIREWALL_ALLOWLIST = 0; + /** + * @deprecated use FIREWALL_DENYLIST. + */ const int FIREWALL_BLACKLIST = 1; + const int FIREWALL_DENYLIST = 1; const int FIREWALL_RULE_ALLOW = 1; const int FIREWALL_RULE_DENY = 2; const int FIREWALL_CHAIN_NONE = 0; const int FIREWALL_CHAIN_DOZABLE = 1; const int FIREWALL_CHAIN_STANDBY = 2; const int FIREWALL_CHAIN_POWERSAVE = 3; + const int FIREWALL_CHAIN_RESTRICTED = 4; const String IF_STATE_UP = "up"; const String IF_STATE_DOWN = "down"; const String IF_FLAG_BROADCAST = "broadcast"; diff --git a/server/aidl_api/netd_aidl_interface/current/android/net/INetdUnsolicitedEventListener.aidl b/server/aidl_api/netd_aidl_interface/current/android/net/INetdUnsolicitedEventListener.aidl index 44593632..31775dfd 100644 --- a/server/aidl_api/netd_aidl_interface/current/android/net/INetdUnsolicitedEventListener.aidl +++ b/server/aidl_api/netd_aidl_interface/current/android/net/INetdUnsolicitedEventListener.aidl @@ -1,14 +1,30 @@ +/** + * Copyright (c) 2018, 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. + */ /////////////////////////////////////////////////////////////////////////////// // THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // /////////////////////////////////////////////////////////////////////////////// -// This file is a snapshot of an AIDL interface (or parcelable). Do not try to -// edit this file. It looks like you are doing that because you have modified -// an AIDL interface in a backward-incompatible way, e.g., deleting a function -// from an interface or a field from a parcelable and it broke the build. That -// breakage is intended. +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. // -// You must not make a backward incompatible changes to the AIDL files built +// You must not make a backward incompatible change to any AIDL file built // with the aidl_interface module type with versions property set. The module // type is used to build AIDL files in a way that they can be used across // independently updatable components of the system. If a device is shipped diff --git a/server/aidl_api/netd_aidl_interface/current/android/net/InterfaceConfigurationParcel.aidl b/server/aidl_api/netd_aidl_interface/current/android/net/InterfaceConfigurationParcel.aidl index 01e0f955..1869d8d4 100644 --- a/server/aidl_api/netd_aidl_interface/current/android/net/InterfaceConfigurationParcel.aidl +++ b/server/aidl_api/netd_aidl_interface/current/android/net/InterfaceConfigurationParcel.aidl @@ -1,14 +1,30 @@ +/* + * Copyright (C) 2018 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. + */ /////////////////////////////////////////////////////////////////////////////// // THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // /////////////////////////////////////////////////////////////////////////////// -// This file is a snapshot of an AIDL interface (or parcelable). Do not try to -// edit this file. It looks like you are doing that because you have modified -// an AIDL interface in a backward-incompatible way, e.g., deleting a function -// from an interface or a field from a parcelable and it broke the build. That -// breakage is intended. +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. // -// You must not make a backward incompatible changes to the AIDL files built +// You must not make a backward incompatible change to any AIDL file built // with the aidl_interface module type with versions property set. The module // type is used to build AIDL files in a way that they can be used across // independently updatable components of the system. If a device is shipped diff --git a/server/aidl_api/netd_aidl_interface/current/android/net/MarkMaskParcel.aidl b/server/aidl_api/netd_aidl_interface/current/android/net/MarkMaskParcel.aidl index 62be8384..8ea20d11 100644 --- a/server/aidl_api/netd_aidl_interface/current/android/net/MarkMaskParcel.aidl +++ b/server/aidl_api/netd_aidl_interface/current/android/net/MarkMaskParcel.aidl @@ -1,14 +1,30 @@ +/* + * Copyright (C) 2019 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. + */ /////////////////////////////////////////////////////////////////////////////// // THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // /////////////////////////////////////////////////////////////////////////////// -// This file is a snapshot of an AIDL interface (or parcelable). Do not try to -// edit this file. It looks like you are doing that because you have modified -// an AIDL interface in a backward-incompatible way, e.g., deleting a function -// from an interface or a field from a parcelable and it broke the build. That -// breakage is intended. +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. // -// You must not make a backward incompatible changes to the AIDL files built +// You must not make a backward incompatible change to any AIDL file built // with the aidl_interface module type with versions property set. The module // type is used to build AIDL files in a way that they can be used across // independently updatable components of the system. If a device is shipped diff --git a/server/aidl_api/netd_aidl_interface/current/android/net/NativeNetworkConfig.aidl b/server/aidl_api/netd_aidl_interface/current/android/net/NativeNetworkConfig.aidl new file mode 100644 index 00000000..76562b29 --- /dev/null +++ b/server/aidl_api/netd_aidl_interface/current/android/net/NativeNetworkConfig.aidl @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2021 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +/* @hide */ +@JavaDerive(equals=true, toString=true) @JavaOnlyImmutable +parcelable NativeNetworkConfig { + int netId; + android.net.NativeNetworkType networkType = android.net.NativeNetworkType.PHYSICAL; + int permission; + boolean secure; + android.net.NativeVpnType vpnType = android.net.NativeVpnType.PLATFORM; +} diff --git a/server/aidl_api/netd_aidl_interface/current/android/net/NativeNetworkType.aidl b/server/aidl_api/netd_aidl_interface/current/android/net/NativeNetworkType.aidl new file mode 100644 index 00000000..06c8979d --- /dev/null +++ b/server/aidl_api/netd_aidl_interface/current/android/net/NativeNetworkType.aidl @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2021 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +@Backing(type="int") +enum NativeNetworkType { + PHYSICAL = 0, + VIRTUAL = 1, +} diff --git a/server/aidl_api/netd_aidl_interface/current/android/net/NativeVpnType.aidl b/server/aidl_api/netd_aidl_interface/current/android/net/NativeVpnType.aidl new file mode 100644 index 00000000..8a8be839 --- /dev/null +++ b/server/aidl_api/netd_aidl_interface/current/android/net/NativeVpnType.aidl @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2021 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +@Backing(type="int") +enum NativeVpnType { + SERVICE = 1, + PLATFORM = 2, + LEGACY = 3, + OEM = 4, +} diff --git a/server/aidl_api/netd_aidl_interface/current/android/net/RouteInfoParcel.aidl b/server/aidl_api/netd_aidl_interface/current/android/net/RouteInfoParcel.aidl index 5e0ee62a..5ef95e67 100644 --- a/server/aidl_api/netd_aidl_interface/current/android/net/RouteInfoParcel.aidl +++ b/server/aidl_api/netd_aidl_interface/current/android/net/RouteInfoParcel.aidl @@ -1,14 +1,30 @@ +/** + * Copyright (c) 2020, 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. + */ /////////////////////////////////////////////////////////////////////////////// // THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // /////////////////////////////////////////////////////////////////////////////// -// This file is a snapshot of an AIDL interface (or parcelable). Do not try to -// edit this file. It looks like you are doing that because you have modified -// an AIDL interface in a backward-incompatible way, e.g., deleting a function -// from an interface or a field from a parcelable and it broke the build. That -// breakage is intended. +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. // -// You must not make a backward incompatible changes to the AIDL files built +// You must not make a backward incompatible change to any AIDL file built // with the aidl_interface module type with versions property set. The module // type is used to build AIDL files in a way that they can be used across // independently updatable components of the system. If a device is shipped diff --git a/server/aidl_api/netd_aidl_interface/current/android/net/TetherConfigParcel.aidl b/server/aidl_api/netd_aidl_interface/current/android/net/TetherConfigParcel.aidl index b1364545..7b39c22e 100644 --- a/server/aidl_api/netd_aidl_interface/current/android/net/TetherConfigParcel.aidl +++ b/server/aidl_api/netd_aidl_interface/current/android/net/TetherConfigParcel.aidl @@ -1,14 +1,30 @@ +/* + * Copyright (C) 2019 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. + */ /////////////////////////////////////////////////////////////////////////////// // THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // /////////////////////////////////////////////////////////////////////////////// -// This file is a snapshot of an AIDL interface (or parcelable). Do not try to -// edit this file. It looks like you are doing that because you have modified -// an AIDL interface in a backward-incompatible way, e.g., deleting a function -// from an interface or a field from a parcelable and it broke the build. That -// breakage is intended. +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. // -// You must not make a backward incompatible changes to the AIDL files built +// You must not make a backward incompatible change to any AIDL file built // with the aidl_interface module type with versions property set. The module // type is used to build AIDL files in a way that they can be used across // independently updatable components of the system. If a device is shipped diff --git a/server/aidl_api/netd_aidl_interface/current/android/net/TetherOffloadRuleParcel.aidl b/server/aidl_api/netd_aidl_interface/current/android/net/TetherOffloadRuleParcel.aidl index c9d84580..983e9860 100644 --- a/server/aidl_api/netd_aidl_interface/current/android/net/TetherOffloadRuleParcel.aidl +++ b/server/aidl_api/netd_aidl_interface/current/android/net/TetherOffloadRuleParcel.aidl @@ -1,14 +1,30 @@ +/* + * Copyright (C) 2020 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. + */ /////////////////////////////////////////////////////////////////////////////// // THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // /////////////////////////////////////////////////////////////////////////////// -// This file is a snapshot of an AIDL interface (or parcelable). Do not try to -// edit this file. It looks like you are doing that because you have modified -// an AIDL interface in a backward-incompatible way, e.g., deleting a function -// from an interface or a field from a parcelable and it broke the build. That -// breakage is intended. +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. // -// You must not make a backward incompatible changes to the AIDL files built +// You must not make a backward incompatible change to any AIDL file built // with the aidl_interface module type with versions property set. The module // type is used to build AIDL files in a way that they can be used across // independently updatable components of the system. If a device is shipped diff --git a/server/aidl_api/netd_aidl_interface/current/android/net/TetherStatsParcel.aidl b/server/aidl_api/netd_aidl_interface/current/android/net/TetherStatsParcel.aidl index 0b0960ef..5f1b7226 100644 --- a/server/aidl_api/netd_aidl_interface/current/android/net/TetherStatsParcel.aidl +++ b/server/aidl_api/netd_aidl_interface/current/android/net/TetherStatsParcel.aidl @@ -1,14 +1,30 @@ +/* + * Copyright (C) 2018 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. + */ /////////////////////////////////////////////////////////////////////////////// // THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // /////////////////////////////////////////////////////////////////////////////// -// This file is a snapshot of an AIDL interface (or parcelable). Do not try to -// edit this file. It looks like you are doing that because you have modified -// an AIDL interface in a backward-incompatible way, e.g., deleting a function -// from an interface or a field from a parcelable and it broke the build. That -// breakage is intended. +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. // -// You must not make a backward incompatible changes to the AIDL files built +// You must not make a backward incompatible change to any AIDL file built // with the aidl_interface module type with versions property set. The module // type is used to build AIDL files in a way that they can be used across // independently updatable components of the system. If a device is shipped diff --git a/server/aidl_api/netd_aidl_interface/current/android/net/UidRangeParcel.aidl b/server/aidl_api/netd_aidl_interface/current/android/net/UidRangeParcel.aidl index 84ff4572..72e987a2 100644 --- a/server/aidl_api/netd_aidl_interface/current/android/net/UidRangeParcel.aidl +++ b/server/aidl_api/netd_aidl_interface/current/android/net/UidRangeParcel.aidl @@ -1,14 +1,30 @@ +/* + * Copyright (C) 2018 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. + */ /////////////////////////////////////////////////////////////////////////////// // THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // /////////////////////////////////////////////////////////////////////////////// -// This file is a snapshot of an AIDL interface (or parcelable). Do not try to -// edit this file. It looks like you are doing that because you have modified -// an AIDL interface in a backward-incompatible way, e.g., deleting a function -// from an interface or a field from a parcelable and it broke the build. That -// breakage is intended. +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. // -// You must not make a backward incompatible changes to the AIDL files built +// You must not make a backward incompatible change to any AIDL file built // with the aidl_interface module type with versions property set. The module // type is used to build AIDL files in a way that they can be used across // independently updatable components of the system. If a device is shipped @@ -17,6 +33,7 @@ package android.net; /* @hide */ +@JavaDerive(equals=true, toString=true) @JavaOnlyImmutable parcelable UidRangeParcel { int start; int stop; diff --git a/server/aidl_api/netd_aidl_interface/current/android/net/netd/aidl/NativeUidRangeConfig.aidl b/server/aidl_api/netd_aidl_interface/current/android/net/netd/aidl/NativeUidRangeConfig.aidl new file mode 100644 index 00000000..9bb679f1 --- /dev/null +++ b/server/aidl_api/netd_aidl_interface/current/android/net/netd/aidl/NativeUidRangeConfig.aidl @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2021 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net.netd.aidl; +/* @hide */ +@JavaDerive(equals=true, toString=true) @JavaOnlyImmutable +parcelable NativeUidRangeConfig { + int netId; + android.net.UidRangeParcel[] uidRanges; + int subPriority; +} diff --git a/server/binder/android/net/INetd.aidl b/server/binder/android/net/INetd.aidl index 3b221cf0..d6398c12 100644 --- a/server/binder/android/net/INetd.aidl +++ b/server/binder/android/net/INetd.aidl @@ -19,11 +19,13 @@ package android.net; import android.net.INetdUnsolicitedEventListener; import android.net.InterfaceConfigurationParcel; import android.net.MarkMaskParcel; +import android.net.NativeNetworkConfig; import android.net.RouteInfoParcel; import android.net.TetherConfigParcel; import android.net.TetherOffloadRuleParcel; import android.net.TetherStatsParcel; import android.net.UidRangeParcel; +import android.net.netd.aidl.NativeUidRangeConfig; /** {@hide} */ interface INetd { @@ -35,18 +37,18 @@ interface INetd { /** * Replaces the contents of the specified UID-based firewall chain. * - * The chain may be a whitelist chain or a blacklist chain. A blacklist chain contains DROP - * rules for the specified UIDs and a RETURN rule at the end. A whitelist chain contains RETURN + * The chain may be an allowlist chain or a denylist chain. A denylist chain contains DROP + * rules for the specified UIDs and a RETURN rule at the end. An allowlist chain contains RETURN * rules for the system UID range (0 to {@code UID_APP} - 1), RETURN rules for for the specified * UIDs, and a DROP rule at the end. The chain will be created if it does not exist. * * @param chainName The name of the chain to replace. - * @param isWhitelist Whether this is a whitelist or blacklist chain. + * @param isAllowlist Whether this is an allowlist or denylist chain. * @param uids The list of UIDs to allow/deny. * @return true if the chain was successfully replaced, false otherwise. */ boolean firewallReplaceUidChain(in @utf8InCpp String chainName, - boolean isWhitelist, + boolean isAllowlist, in int[] uids); /** @@ -69,6 +71,7 @@ interface INetd { /** * Creates a physical network (i.e., one containing physical interfaces. + * @deprecated use networkCreate() instead. * * @param netId the networkId to create. * @param permission the permission necessary to use the network. Must be one of @@ -81,6 +84,7 @@ interface INetd { /** * Creates a VPN network. + * @deprecated use networkCreate() instead. * * @param netId the network to create. * @param secure whether unprivileged apps are allowed to bypass the VPN. @@ -125,12 +129,12 @@ interface INetd { void networkRemoveInterface(int netId, in @utf8InCpp String iface); /** - * Adds the specified UID ranges to the specified network. The network must be a VPN. Traffic - * from the UID ranges will be routed through the VPN. + * Adds the specified UID ranges to the specified network. The network can be physical or + * virtual. Traffic from the UID ranges will be routed to the network by default. * * @param netId the network ID of the network to add the ranges to. - * @param uidRanges a set of non-overlapping, contiguous ranges of UIDs to add. The ranges - * must not overlap with existing ranges routed to this network. + * @param uidRanges a set of non-overlapping ranges of UIDs to add. These exact ranges + * must not overlap with existing ranges assigned to this network. * * @throws ServiceSpecificException in case of failure, with an error code corresponding to the * unix errno. @@ -138,12 +142,12 @@ interface INetd { void networkAddUidRanges(int netId, in UidRangeParcel[] uidRanges); /** - * Adds the specified UID ranges to the specified network. The network must be a VPN. Traffic - * from the UID ranges will no longer be routed through the VPN. + * Remove the specified UID ranges from the specified network. The network can be physical or + * virtual. Traffic from the UID ranges will no longer be routed to the network by default. * * @param netId the network ID of the network to remove the ranges from. - * @param uidRanges a set of non-overlapping, contiguous ranges of UIDs to add. The ranges - * must already be routed to this network. + * @param uidRanges a set of non-overlapping ranges of UIDs to remove. These exact ranges + * must already be assigned to this network. * * @throws ServiceSpecificException in case of failure, with an error code corresponding to the * unix errno. @@ -771,6 +775,22 @@ interface INetd { const int LOCAL_NET_ID = 99; + /** + * Constant net ID for the "dummy" network. + * + * The dummy network is used to blackhole or reject traffic. Any attempt to use it will + * either drop the packets or fail with ENETUNREACH. + */ + const int DUMMY_NET_ID = 51; + + /** + * Constant net ID for the "unreachable" network. + * + * The unreachable network is used to reject traffic. Any attempt to use it will fail + * with ENETUNREACH. + */ + const int UNREACHABLE_NET_ID = 52; + // Route does not specify a next hop const String NEXTHOP_NONE = ""; // Route next hop is unreachable @@ -981,17 +1001,26 @@ interface INetd { */ boolean networkCanProtect(int uid); - // Whitelist only allows packets from specific UID/Interface + /** Only allows packets from specific UID/Interface. + @deprecated use FIREWALL_ALLOWLIST. */ const int FIREWALL_WHITELIST = 0; - // Blacklist blocks packets from specific UID/Interface + + /** Only allows packets from specific UID/Interface. */ + const int FIREWALL_ALLOWLIST = 0; + + /** Blocks packets from specific UID/Interface. + @deprecated use FIREWALL_DENYLIST. */ const int FIREWALL_BLACKLIST = 1; + /** Blocks packets from specific UID/Interface. */ + const int FIREWALL_DENYLIST = 1; + /** * Set type of firewall - * Type whitelist only allows packets from specific UID/Interface - * Type blacklist blocks packets from specific UID/Interface + * Type allowlist only allows packets from specific UID/Interface + * Type denylist blocks packets from specific UID/Interface * - * @param firewalltype type of firewall, either FIREWALL_WHITELIST or FIREWALL_BLACKLIST + * @param firewalltype type of firewall, either FIREWALL_ALLOWLIST or FIREWALL_DENYLIST * @throws ServiceSpecificException in case of failure, with an error code indicating the * cause of the failure. */ @@ -1010,6 +1039,9 @@ interface INetd { const int FIREWALL_CHAIN_STANDBY = 2; // Specify POWERSAVE chain(fw_powersave) which is used in power save mode const int FIREWALL_CHAIN_POWERSAVE = 3; + // Specify RESTRICTED chain(fw_restricted) which is used in restricted + // networking mode + const int FIREWALL_CHAIN_RESTRICTED = 4; /** * Set firewall rule for interface @@ -1159,7 +1191,7 @@ interface INetd { * Add ingress interface filtering rules to a list of UIDs * * For a given uid, once a filtering rule is added, the kernel will only allow packets from the - * whitelisted interface and loopback to be sent to the list of UIDs. + * allowed interface and loopback to be sent to the list of UIDs. * * Calling this method on one or more UIDs with an existing filtering rule but a different * interface name will result in the filtering rule being updated to allow the new interface @@ -1309,4 +1341,40 @@ interface INetd { * cause of the failure. */ TetherStatsParcel tetherOffloadGetAndClearStats(int ifIndex); + + /** + * Creates a network. + * + * @param config the configuration of network. + * @throws ServiceSpecificException in case of failure, with an error code corresponding to the + * unix errno. + */ + void networkCreate(in NativeNetworkConfig config); + + /** + * Adds the specified UID ranges to the specified network. The network can be physical or + * virtual. Traffic from the UID ranges will be routed to the network by default. The possible + * value of subsidiary priority for physical and unreachable networks is 0-999. 0 is the highest + * priority. 0 is also the default value. Virtual network supports only the default value. + * + * @param NativeUidRangeConfig a parcel contains netId, UID ranges, subsidiary priority, etc. + * + * @throws ServiceSpecificException in case of failure, with an error code corresponding to the + * unix errno. + */ + void networkAddUidRangesParcel(in NativeUidRangeConfig uidRangesConfig); + + /** + * Removes the specified UID ranges from the specified network. The network can be physical or + * virtual. Traffic from the UID ranges will no longer be routed to the network by default. The + * possible value of subsidiary priority for physical and unreachable networks is 0-999. 0 is + * the highest priority. 0 is also the default value. Virtual network supports only the default + * value. + * + * @param NativeUidRangeConfig a parcel contains netId, UID ranges, subsidiary priority, etc. + * + * @throws ServiceSpecificException in case of failure, with an error code corresponding to the + * unix errno. + */ + void networkRemoveUidRangesParcel(in NativeUidRangeConfig uidRangesConfig); } diff --git a/server/binder/android/net/NativeNetworkConfig.aidl b/server/binder/android/net/NativeNetworkConfig.aidl new file mode 100644 index 00000000..2c4f83a4 --- /dev/null +++ b/server/binder/android/net/NativeNetworkConfig.aidl @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2021 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 android.net; + +import android.net.NativeNetworkType; +import android.net.NativeVpnType; + +/** + * The configuration to create a network. + * + * {@hide} + */ +@JavaDerive(toString=true, equals=true) +@JavaOnlyImmutable +parcelable NativeNetworkConfig { + /** The networkId to create. */ + int netId; + + /** + * The type of network, e.g. physical network or virtual network. + */ + NativeNetworkType networkType = NativeNetworkType.PHYSICAL; + + /** + * For physical networks. The permission necessary to use the network. Must be one of + * PERMISSION_NONE/PERMISSION_NETWORK/PERMISSION_SYSTEM. Ignored for all other network types. + */ + int permission; + + /** + * For virtual networks. Whether unprivileged apps are allowed to bypass the VPN. Ignored for + * all other network types. + */ + boolean secure; + + /** For virtual networks. The type of VPN to create. Ignored for all other network types. */ + NativeVpnType vpnType = NativeVpnType.PLATFORM; +} diff --git a/server/binder/android/net/NativeNetworkType.aidl b/server/binder/android/net/NativeNetworkType.aidl new file mode 100644 index 00000000..d6670294 --- /dev/null +++ b/server/binder/android/net/NativeNetworkType.aidl @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2021 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 android.net; + +@Backing(type="int") +enum NativeNetworkType { + /** + * Physical network type. + */ + PHYSICAL = 0, + + /** + * Virtual private network type. + */ + VIRTUAL = 1, +}
\ No newline at end of file diff --git a/server/binder/android/net/NativeVpnType.aidl b/server/binder/android/net/NativeVpnType.aidl new file mode 100644 index 00000000..cd1b4474 --- /dev/null +++ b/server/binder/android/net/NativeVpnType.aidl @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2021 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 android.net; + +@Backing(type="int") +enum NativeVpnType { + /** + * A VPN created by an app using the VpnService API. + */ + SERVICE = 1, + + /** + * A VPN created using a VpnManager API such as startProvisionedVpnProfile. + */ + PLATFORM = 2, + + /** + * An IPsec VPN created by the built-in LegacyVpnRunner. + */ + LEGACY = 3, + + /** + * An VPN created by OEM code through other means than VpnService or VpnManager. + */ + OEM = 4, +}
\ No newline at end of file diff --git a/server/binder/android/net/UidRangeParcel.aidl b/server/binder/android/net/UidRangeParcel.aidl index 08fd4918..8f1fef6f 100644 --- a/server/binder/android/net/UidRangeParcel.aidl +++ b/server/binder/android/net/UidRangeParcel.aidl @@ -21,7 +21,8 @@ package android.net; * * {@hide} */ +@JavaOnlyImmutable @JavaDerive(toString=true, equals=true) parcelable UidRangeParcel { int start; int stop; -}
\ No newline at end of file +} diff --git a/server/binder/android/net/netd/aidl/NativeUidRangeConfig.aidl b/server/binder/android/net/netd/aidl/NativeUidRangeConfig.aidl new file mode 100644 index 00000000..99497a86 --- /dev/null +++ b/server/binder/android/net/netd/aidl/NativeUidRangeConfig.aidl @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2021 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 android.net.netd.aidl; + +import android.net.UidRangeParcel; + +/** + * The configuration to add or remove UID ranges. + * + * {@hide} + */ +@JavaDerive(toString=true, equals=true) +@JavaOnlyImmutable +parcelable NativeUidRangeConfig { + /** The network ID of the network to add/remove the ranges to/from. */ + int netId; + + /** A set of non-overlapping ranges of UIDs. */ + UidRangeParcel[] uidRanges; + + /** + * The priority of this UID range config. 0 is the highest priority; 999 is the lowest priority. + * The function of this parameter is to adjust the priority when the same UID is set to + * different networks for different features. + */ + int subPriority; +}
\ No newline at end of file diff --git a/server/main.cpp b/server/main.cpp index 4949ff6f..bc48ac20 100644 --- a/server/main.cpp +++ b/server/main.cpp @@ -110,7 +110,9 @@ int main() { gLog.info("netd 1.0 starting"); android::net::process::removePidFile(PID_FILE_PATH); + gLog.info("Pid file removed"); android::net::process::blockSigPipe(); + gLog.info("SIGPIPE is blocked"); // Before we do anything that could fork, mark CLOEXEC the UNIX sockets that we get from init. // FrameworkListener does this on initialization as well, but we only initialize these @@ -118,16 +120,19 @@ int main() { for (const auto& sock : {DNSPROXYLISTENER_SOCKET_NAME, FwmarkServer::SOCKET_NAME, MDnsSdListener::SOCKET_NAME}) { setCloseOnExec(sock); + gLog.info("setCloseOnExec(%s)", sock); } // Make sure BPF programs are loaded before doing anything android::bpf::waitForProgsLoaded(); + gLog.info("BPF programs are loaded"); NetlinkManager *nm = NetlinkManager::Instance(); if (nm == nullptr) { ALOGE("Unable to create NetlinkManager"); exit(1); }; + gLog.info("NetlinkManager instanced"); gCtls = new android::net::Controllers(); gCtls->init(); diff --git a/tests/Android.bp b/tests/Android.bp index 3baaa184..c5d9bb53 100644 --- a/tests/Android.bp +++ b/tests/Android.bp @@ -13,6 +13,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "system_netd_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["system_netd_license"], +} + cc_test_library { name: "libnetd_test_tun_interface", defaults: ["netd_defaults"], @@ -43,7 +52,7 @@ cc_test_library { "libnetutils", "libsysutils", "libutils", - "netd_aidl_interface-cpp", + "netd_aidl_interface-V7-cpp", ], } @@ -86,6 +95,7 @@ cc_test { "libnetutils", "libprocessgroup", "libssl", + "libsysutils", "libutils", ], static_libs: [ @@ -97,8 +107,8 @@ cc_test { "libnetdbpf", "libnetdutils", "libqtaguid", - "netd_aidl_interface-unstable-cpp", - "netd_event_listener_interface-cpp", + "netd_aidl_interface-V7-cpp", + "netd_event_listener_interface-V1-cpp", "oemnetd_aidl_interface-cpp", ], compile_multilib: "both", @@ -111,7 +121,7 @@ cc_test { }, }, sanitize: { - address: true, + address: false, recover: [ "all" ], }, } diff --git a/tests/benchmarks/Android.bp b/tests/benchmarks/Android.bp index e1bf67cc..00c28eb5 100644 --- a/tests/benchmarks/Android.bp +++ b/tests/benchmarks/Android.bp @@ -1,5 +1,14 @@ // APCT build target for metrics tests +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "system_netd_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["system_netd_license"], +} + cc_benchmark { name: "netd_benchmark", defaults: ["netd_defaults"], @@ -13,10 +22,10 @@ cc_benchmark { ], static_libs: [ "libnetd_test_dnsresponder_ndk", - "dnsresolver_aidl_interface-unstable-ndk_platform", - "netd_aidl_interface-cpp", // system/netd/server/UidRanges.h - "netd_aidl_interface-ndk_platform", - "netd_event_listener_interface-ndk_platform", + "dnsresolver_aidl_interface-lateststable-ndk_platform", + "netd_aidl_interface-lateststable-cpp", // system/netd/server/UidRanges.h + "netd_aidl_interface-lateststable-ndk_platform", + "netd_event_listener_interface-lateststable-ndk_platform", ], aidl: { include_dirs: ["system/netd/server/binder"], diff --git a/tests/benchmarks/bpf_benchmark.cpp b/tests/benchmarks/bpf_benchmark.cpp index 12aaf958..bf4bd541 100644 --- a/tests/benchmarks/bpf_benchmark.cpp +++ b/tests/benchmarks/bpf_benchmark.cpp @@ -34,7 +34,7 @@ class BpfBenchMark : public ::benchmark::Fixture { }; BENCHMARK_DEFINE_F(BpfBenchMark, MapWriteNewEntry)(benchmark::State& state) { - for (auto _ : state) { + for (auto _ : state) { // NOLINT(clang-analyzer-deadcode.DeadStores) // TODO(b/147676069) assert mBpfTestMap.writeValue(state.range(0), state.range(0), BPF_NOEXIST); } @@ -45,7 +45,7 @@ BENCHMARK_DEFINE_F(BpfBenchMark, MapUpdateEntry)(benchmark::State& state) { // TODO(b/147676069) assert mBpfTestMap.writeValue(i, i, BPF_NOEXIST); } - for (auto _ : state) { + for (auto _ : state) { // NOLINT(clang-analyzer-deadcode.DeadStores) // TODO(b/147676069) assert mBpfTestMap.writeValue(state.range(0), state.range(0) + 1, BPF_EXIST); } @@ -56,7 +56,7 @@ BENCHMARK_DEFINE_F(BpfBenchMark, MapDeleteAddEntry)(benchmark::State& state) { // TODO(b/147676069) assert mBpfTestMap.writeValue(i, i, BPF_NOEXIST); } - for (auto _ : state) { + for (auto _ : state) { // NOLINT(clang-analyzer-deadcode.DeadStores) // TODO(b/147676069) assert mBpfTestMap.deleteValue(state.range(0)); // TODO(b/147676069) assert @@ -65,7 +65,7 @@ BENCHMARK_DEFINE_F(BpfBenchMark, MapDeleteAddEntry)(benchmark::State& state) { } BENCHMARK_DEFINE_F(BpfBenchMark, WaitForRcu)(benchmark::State& state) { - for (auto _ : state) { + for (auto _ : state) { // NOLINT(clang-analyzer-deadcode.DeadStores) int ret = android::bpf::synchronizeKernelRCU(); if (ret) { state.SkipWithError( diff --git a/tests/binder_test.cpp b/tests/binder_test.cpp index 2a6bb7c3..22d1f226 100644 --- a/tests/binder_test.cpp +++ b/tests/binder_test.cpp @@ -24,7 +24,10 @@ #include <cstdlib> #include <iostream> #include <mutex> +#include <regex> #include <set> +#include <string> +#include <thread> #include <vector> #include <dirent.h> @@ -57,11 +60,13 @@ #include <gtest/gtest.h> #include <netdbpf/bpf_shared.h> #include <netutils/ifc.h> +#include <utils/Errors.h> #include "Fwmark.h" #include "InterfaceController.h" #include "NetdClient.h" #include "NetdConstants.h" #include "NetworkController.h" +#include "RouteController.h" #include "SockDiag.h" #include "TestUnsolService.h" #include "XfrmController.h" @@ -90,6 +95,7 @@ using android::String16; using android::String8; using android::base::Join; using android::base::make_scope_guard; +using android::base::ReadFdToString; using android::base::ReadFileToString; using android::base::StartsWith; using android::base::StringPrintf; @@ -99,11 +105,28 @@ using android::net::INetd; using android::net::InterfaceConfigurationParcel; using android::net::InterfaceController; using android::net::MarkMaskParcel; +using android::net::NativeNetworkConfig; +using android::net::NativeNetworkType; +using android::net::NativeVpnType; +using android::net::RULE_PRIORITY_BYPASSABLE_VPN; +using android::net::RULE_PRIORITY_DEFAULT_NETWORK; +using android::net::RULE_PRIORITY_EXPLICIT_NETWORK; +using android::net::RULE_PRIORITY_OUTPUT_INTERFACE; +using android::net::RULE_PRIORITY_PROHIBIT_NON_VPN; +using android::net::RULE_PRIORITY_SECURE_VPN; +using android::net::RULE_PRIORITY_TETHERING; +using android::net::RULE_PRIORITY_UID_DEFAULT_NETWORK; +using android::net::RULE_PRIORITY_UID_DEFAULT_UNREACHABLE; +using android::net::RULE_PRIORITY_UID_EXPLICIT_NETWORK; +using android::net::RULE_PRIORITY_UID_IMPLICIT_NETWORK; +using android::net::RULE_PRIORITY_VPN_FALLTHROUGH; using android::net::SockDiag; using android::net::TetherOffloadRuleParcel; using android::net::TetherStatsParcel; using android::net::TunInterface; using android::net::UidRangeParcel; +using android::net::UidRanges; +using android::net::netd::aidl::NativeUidRangeConfig; using android::netdutils::IPAddress; using android::netdutils::ScopedAddrinfo; using android::netdutils::sSyscalls; @@ -113,18 +136,29 @@ static const char* IP_RULE_V4 = "-4"; static const char* IP_RULE_V6 = "-6"; static const int TEST_NETID1 = 65501; static const int TEST_NETID2 = 65502; +static const int TEST_NETID3 = 65503; +static const int TEST_NETID4 = 65504; +static const int TEST_DUMP_NETID = 65123; static const char* DNSMASQ = "dnsmasq"; // Use maximum reserved appId for applications to avoid conflict with existing // uids. static const int TEST_UID1 = 99999; static const int TEST_UID2 = 99998; +static const int TEST_UID3 = 99997; +static const int TEST_UID4 = 99996; +static const int TEST_UID5 = 99995; +static const int TEST_UID6 = 99994; constexpr int BASE_UID = AID_USER_OFFSET * 5; static const std::string NO_SOCKET_ALLOW_RULE("! owner UID match 0-4294967294"); static const std::string ESP_ALLOW_RULE("esp"); +static const in6_addr V6_ADDR = { + {// 2001:db8:cafe::8888 + .u6_addr8 = {0x20, 0x01, 0x0d, 0xb8, 0xca, 0xfe, 0, 0, 0, 0, 0, 0, 0, 0, 0x88, 0x88}}}; + class NetdBinderTest : public ::testing::Test { public: NetdBinderTest() { @@ -142,6 +176,8 @@ class NetdBinderTest : public ::testing::Test { void TearDown() override { mNetd->networkDestroy(TEST_NETID1); mNetd->networkDestroy(TEST_NETID2); + mNetd->networkDestroy(TEST_NETID3); + mNetd->networkDestroy(TEST_NETID4); setNetworkForProcess(NETID_UNSET); // Restore default network if (mStoredDefaultNetwork >= 0) mNetd->networkSetDefault(mStoredDefaultNetwork); @@ -153,14 +189,20 @@ class NetdBinderTest : public ::testing::Test { static void SetUpTestCase() { ASSERT_EQ(0, sTun.init()); ASSERT_EQ(0, sTun2.init()); + ASSERT_EQ(0, sTun3.init()); + ASSERT_EQ(0, sTun4.init()); ASSERT_LE(sTun.name().size(), static_cast<size_t>(IFNAMSIZ)); ASSERT_LE(sTun2.name().size(), static_cast<size_t>(IFNAMSIZ)); + ASSERT_LE(sTun3.name().size(), static_cast<size_t>(IFNAMSIZ)); + ASSERT_LE(sTun4.name().size(), static_cast<size_t>(IFNAMSIZ)); } static void TearDownTestCase() { // Closing the socket removes the interface and IP addresses. sTun.destroy(); sTun2.destroy(); + sTun3.destroy(); + sTun4.destroy(); } static void fakeRemoteSocketPair(unique_fd* clientSocket, unique_fd* serverSocket, @@ -169,6 +211,22 @@ class NetdBinderTest : public ::testing::Test { void createVpnNetworkWithUid(bool secure, uid_t uid, int vpnNetId = TEST_NETID2, int fallthroughNetId = TEST_NETID1); + void createAndSetDefaultNetwork(int netId, const std::string& interface, + int permission = INetd::PERMISSION_NONE); + + void createPhysicalNetwork(int netId, const std::string& interface, + int permission = INetd::PERMISSION_NONE); + + void createDefaultAndOtherPhysicalNetwork(int defaultNetId, int otherNetId); + + void createVpnAndOtherPhysicalNetwork(int systemDefaultNetId, int otherNetId, int vpnNetId, + bool secure); + + void createVpnAndAppDefaultNetworkWithUid(int systemDefaultNetId, int appDefaultNetId, + int vpnNetId, bool secure, + std::vector<UidRangeParcel>&& appDefaultUidRanges, + std::vector<UidRangeParcel>&& vpnUidRanges); + protected: // Use -1 to represent that default network was not modified because // real netId must be an unsigned value. @@ -176,10 +234,14 @@ class NetdBinderTest : public ::testing::Test { sp<INetd> mNetd; static TunInterface sTun; static TunInterface sTun2; + static TunInterface sTun3; + static TunInterface sTun4; }; TunInterface NetdBinderTest::sTun; TunInterface NetdBinderTest::sTun2; +TunInterface NetdBinderTest::sTun3; +TunInterface NetdBinderTest::sTun4; class TimedOperation : public Stopwatch { public: @@ -199,67 +261,62 @@ TEST_F(NetdBinderTest, IsAlive) { ASSERT_TRUE(isAlive); } -static bool iptablesNoSocketAllowRuleExists(const char *chainName){ - return iptablesRuleExists(IPTABLES_PATH, chainName, NO_SOCKET_ALLOW_RULE) && - iptablesRuleExists(IP6TABLES_PATH, chainName, NO_SOCKET_ALLOW_RULE); -} +namespace { -static bool iptablesEspAllowRuleExists(const char *chainName){ - return iptablesRuleExists(IPTABLES_PATH, chainName, ESP_ALLOW_RULE) && - iptablesRuleExists(IP6TABLES_PATH, chainName, ESP_ALLOW_RULE); +NativeNetworkConfig makeNativeNetworkConfig(int netId, NativeNetworkType networkType, + int permission, bool secure) { + NativeNetworkConfig config = {}; + config.netId = netId; + config.networkType = networkType; + config.permission = permission; + config.secure = secure; + // The vpnType doesn't matter in AOSP. Just pick a well defined one from INetd. + config.vpnType = NativeVpnType::PLATFORM; + return config; } -TEST_F(NetdBinderTest, FirewallReplaceUidChain) { - SKIP_IF_BPF_SUPPORTED; - - std::string chainName = StringPrintf("netd_binder_test_%u", arc4random_uniform(10000)); - const int kNumUids = 500; - std::vector<int32_t> noUids(0); - std::vector<int32_t> uids(kNumUids); - for (int i = 0; i < kNumUids; i++) { - uids[i] = randomUid(); - } - - bool ret; - { - TimedOperation op(StringPrintf("Programming %d-UID whitelist chain", kNumUids)); - mNetd->firewallReplaceUidChain(chainName, true, uids, &ret); - } - EXPECT_EQ(true, ret); - EXPECT_EQ((int) uids.size() + 9, iptablesRuleLineLength(IPTABLES_PATH, chainName.c_str())); - EXPECT_EQ((int) uids.size() + 15, iptablesRuleLineLength(IP6TABLES_PATH, chainName.c_str())); - EXPECT_EQ(true, iptablesNoSocketAllowRuleExists(chainName.c_str())); - EXPECT_EQ(true, iptablesEspAllowRuleExists(chainName.c_str())); - { - TimedOperation op("Clearing whitelist chain"); - mNetd->firewallReplaceUidChain(chainName, false, noUids, &ret); - } - EXPECT_EQ(true, ret); - EXPECT_EQ(5, iptablesRuleLineLength(IPTABLES_PATH, chainName.c_str())); - EXPECT_EQ(5, iptablesRuleLineLength(IP6TABLES_PATH, chainName.c_str())); +} // namespace - { - TimedOperation op(StringPrintf("Programming %d-UID blacklist chain", kNumUids)); - mNetd->firewallReplaceUidChain(chainName, false, uids, &ret); +bool testNetworkExistsButCannotConnect(const sp<INetd>& netd, TunInterface& ifc, const int netId) { + // If this network exists, we should definitely not be able to create it. + // Note that this networkCreate is never allowed to create reserved network IDs, so + // this call may fail for other reasons than the network already existing. + const auto& config = makeNativeNetworkConfig(netId, NativeNetworkType::PHYSICAL, + INetd::PERMISSION_NONE, false); + EXPECT_FALSE(netd->networkCreate(config).isOk()); + // Test if the network exist by adding interface. INetd has no dedicated method to query. When + // the network exists and the interface can be added, the function succeeds. When the network + // exists but the interface cannot be added, it fails with EINVAL, otherwise it is ENONET. + binder::Status status = netd->networkAddInterface(netId, ifc.name()); + if (status.isOk()) { // clean up + EXPECT_TRUE(netd->networkRemoveInterface(netId, ifc.name()).isOk()); + } else if (status.serviceSpecificErrorCode() == ENONET) { + return false; } - EXPECT_EQ(true, ret); - EXPECT_EQ((int) uids.size() + 5, iptablesRuleLineLength(IPTABLES_PATH, chainName.c_str())); - EXPECT_EQ((int) uids.size() + 5, iptablesRuleLineLength(IP6TABLES_PATH, chainName.c_str())); - EXPECT_EQ(false, iptablesNoSocketAllowRuleExists(chainName.c_str())); - EXPECT_EQ(false, iptablesEspAllowRuleExists(chainName.c_str())); - { - TimedOperation op("Clearing blacklist chain"); - mNetd->firewallReplaceUidChain(chainName, false, noUids, &ret); - } - EXPECT_EQ(true, ret); - EXPECT_EQ(5, iptablesRuleLineLength(IPTABLES_PATH, chainName.c_str())); - EXPECT_EQ(5, iptablesRuleLineLength(IP6TABLES_PATH, chainName.c_str())); + const sockaddr_in6 sin6 = {.sin6_family = AF_INET6, + .sin6_addr = {{.u6_addr32 = {htonl(0x20010db8), 0, 0, 0}}}, + .sin6_port = 53}; + const int s = socket(AF_INET6, SOCK_DGRAM, 0); + EXPECT_NE(-1, s); + if (s == -1) return true; + Fwmark fwmark; + fwmark.explicitlySelected = true; + fwmark.netId = netId; + EXPECT_EQ(0, setsockopt(s, SOL_SOCKET, SO_MARK, &fwmark.intValue, sizeof(fwmark.intValue))); + const int ret = connect(s, (struct sockaddr*)&sin6, sizeof(sin6)); + const int err = errno; + EXPECT_EQ(-1, ret); + EXPECT_EQ(ENETUNREACH, err); + close(s); + return true; +} - // Check that the call fails if iptables returns an error. - std::string veryLongStringName = "netd_binder_test_UnacceptablyLongIptablesChainName"; - mNetd->firewallReplaceUidChain(veryLongStringName, true, noUids, &ret); - EXPECT_EQ(false, ret); +TEST_F(NetdBinderTest, InitialNetworksExist) { + EXPECT_TRUE(testNetworkExistsButCannotConnect(mNetd, sTun, INetd::DUMMY_NET_ID)); + EXPECT_TRUE(testNetworkExistsButCannotConnect(mNetd, sTun, INetd::LOCAL_NET_ID)); + EXPECT_TRUE(testNetworkExistsButCannotConnect(mNetd, sTun, INetd::UNREACHABLE_NET_ID)); + EXPECT_FALSE(testNetworkExistsButCannotConnect(mNetd, sTun, 77 /* not exist */)); } TEST_F(NetdBinderTest, IpSecTunnelInterface) { @@ -507,14 +564,22 @@ TEST_F(NetdBinderTest, BandwidthEnableDataSaver) { } static bool ipRuleExistsForRange(const uint32_t priority, const UidRangeParcel& range, - const std::string& action, const char* ipVersion) { + const std::string& action, const char* ipVersion, + const char* oif) { // Output looks like this: - // "12500:\tfrom all fwmark 0x0/0x20000 iif lo uidrange 1000-2000 prohibit" + // "<priority>:\tfrom all iif lo oif netdc0ca6 uidrange 500000-500000 lookup netdc0ca6" + // "<priority>:\tfrom all fwmark 0x0/0x20000 iif lo uidrange 1000-2000 prohibit" std::vector<std::string> rules = listIpRules(ipVersion); std::string prefix = StringPrintf("%" PRIu32 ":", priority); - std::string suffix = - StringPrintf(" iif lo uidrange %d-%d %s\n", range.start, range.stop, action.c_str()); + std::string suffix; + if (oif) { + suffix = StringPrintf(" iif lo oif %s uidrange %d-%d %s\n", oif, range.start, range.stop, + action.c_str()); + } else { + suffix = StringPrintf(" iif lo uidrange %d-%d %s\n", range.start, range.stop, + action.c_str()); + } for (const auto& line : rules) { if (android::base::StartsWith(line, prefix) && android::base::EndsWith(line, suffix)) { return true; @@ -523,14 +588,20 @@ static bool ipRuleExistsForRange(const uint32_t priority, const UidRangeParcel& return false; } +// Overloads function with oif parameter for VPN rules compare. static bool ipRuleExistsForRange(const uint32_t priority, const UidRangeParcel& range, - const std::string& action) { - bool existsIp4 = ipRuleExistsForRange(priority, range, action, IP_RULE_V4); - bool existsIp6 = ipRuleExistsForRange(priority, range, action, IP_RULE_V6); + const std::string& action, const char* oif) { + bool existsIp4 = ipRuleExistsForRange(priority, range, action, IP_RULE_V4, oif); + bool existsIp6 = ipRuleExistsForRange(priority, range, action, IP_RULE_V6, oif); EXPECT_EQ(existsIp4, existsIp6); return existsIp4; } +static bool ipRuleExistsForRange(const uint32_t priority, const UidRangeParcel& range, + const std::string& action) { + return ipRuleExistsForRange(priority, range, action, nullptr); +} + namespace { UidRangeParcel makeUidRangeParcel(int start, int stop) { @@ -541,14 +612,31 @@ UidRangeParcel makeUidRangeParcel(int start, int stop) { return res; } +NativeUidRangeConfig makeNativeUidRangeConfig(unsigned netId, + std::vector<UidRangeParcel>&& uidRanges, + uint32_t subPriority) { + NativeUidRangeConfig res; + res.netId = netId; + res.uidRanges = uidRanges; + res.subPriority = subPriority; + + return res; +} + } // namespace TEST_F(NetdBinderTest, NetworkInterfaces) { - EXPECT_TRUE(mNetd->networkCreatePhysical(TEST_NETID1, INetd::PERMISSION_NONE).isOk()); - EXPECT_EQ(EEXIST, mNetd->networkCreatePhysical(TEST_NETID1, INetd::PERMISSION_NONE) - .serviceSpecificErrorCode()); - EXPECT_EQ(EEXIST, mNetd->networkCreateVpn(TEST_NETID1, true).serviceSpecificErrorCode()); - EXPECT_TRUE(mNetd->networkCreateVpn(TEST_NETID2, true).isOk()); + auto config = makeNativeNetworkConfig(TEST_NETID1, NativeNetworkType::PHYSICAL, + INetd::PERMISSION_NONE, false); + EXPECT_TRUE(mNetd->networkCreate(config).isOk()); + EXPECT_EQ(EEXIST, mNetd->networkCreate(config).serviceSpecificErrorCode()); + + config.networkType = NativeNetworkType::VIRTUAL; + config.secure = true; + EXPECT_EQ(EEXIST, mNetd->networkCreate(config).serviceSpecificErrorCode()); + + config.netId = TEST_NETID2; + EXPECT_TRUE(mNetd->networkCreate(config).isOk()); EXPECT_TRUE(mNetd->networkAddInterface(TEST_NETID1, sTun.name()).isOk()); EXPECT_EQ(EBUSY, @@ -561,51 +649,49 @@ TEST_F(NetdBinderTest, NetworkInterfaces) { } TEST_F(NetdBinderTest, NetworkUidRules) { - const uint32_t RULE_PRIORITY_SECURE_VPN = 12000; - - EXPECT_TRUE(mNetd->networkCreateVpn(TEST_NETID1, true).isOk()); - EXPECT_EQ(EEXIST, mNetd->networkCreateVpn(TEST_NETID1, true).serviceSpecificErrorCode()); + auto config = makeNativeNetworkConfig(TEST_NETID1, NativeNetworkType::VIRTUAL, + INetd::PERMISSION_NONE, true); + EXPECT_TRUE(mNetd->networkCreate(config).isOk()); + EXPECT_EQ(EEXIST, mNetd->networkCreate(config).serviceSpecificErrorCode()); EXPECT_TRUE(mNetd->networkAddInterface(TEST_NETID1, sTun.name()).isOk()); std::vector<UidRangeParcel> uidRanges = {makeUidRangeParcel(BASE_UID + 8005, BASE_UID + 8012), makeUidRangeParcel(BASE_UID + 8090, BASE_UID + 8099)}; UidRangeParcel otherRange = makeUidRangeParcel(BASE_UID + 8190, BASE_UID + 8299); - std::string suffix = StringPrintf("lookup %s ", sTun.name().c_str()); + std::string action = StringPrintf("lookup %s ", sTun.name().c_str()); EXPECT_TRUE(mNetd->networkAddUidRanges(TEST_NETID1, uidRanges).isOk()); - EXPECT_TRUE(ipRuleExistsForRange(RULE_PRIORITY_SECURE_VPN, uidRanges[0], suffix)); - EXPECT_FALSE(ipRuleExistsForRange(RULE_PRIORITY_SECURE_VPN, otherRange, suffix)); + EXPECT_TRUE(ipRuleExistsForRange(RULE_PRIORITY_SECURE_VPN, uidRanges[0], action)); + EXPECT_FALSE(ipRuleExistsForRange(RULE_PRIORITY_SECURE_VPN, otherRange, action)); EXPECT_TRUE(mNetd->networkRemoveUidRanges(TEST_NETID1, uidRanges).isOk()); - EXPECT_FALSE(ipRuleExistsForRange(RULE_PRIORITY_SECURE_VPN, uidRanges[0], suffix)); + EXPECT_FALSE(ipRuleExistsForRange(RULE_PRIORITY_SECURE_VPN, uidRanges[0], action)); EXPECT_TRUE(mNetd->networkAddUidRanges(TEST_NETID1, uidRanges).isOk()); - EXPECT_TRUE(ipRuleExistsForRange(RULE_PRIORITY_SECURE_VPN, uidRanges[1], suffix)); + EXPECT_TRUE(ipRuleExistsForRange(RULE_PRIORITY_SECURE_VPN, uidRanges[1], action)); EXPECT_TRUE(mNetd->networkDestroy(TEST_NETID1).isOk()); - EXPECT_FALSE(ipRuleExistsForRange(RULE_PRIORITY_SECURE_VPN, uidRanges[1], suffix)); + EXPECT_FALSE(ipRuleExistsForRange(RULE_PRIORITY_SECURE_VPN, uidRanges[1], action)); EXPECT_EQ(ENONET, mNetd->networkDestroy(TEST_NETID1).serviceSpecificErrorCode()); } TEST_F(NetdBinderTest, NetworkRejectNonSecureVpn) { - constexpr uint32_t RULE_PRIORITY = 12500; - std::vector<UidRangeParcel> uidRanges = {makeUidRangeParcel(BASE_UID + 150, BASE_UID + 224), makeUidRangeParcel(BASE_UID + 226, BASE_UID + 300)}; // Make sure no rules existed before calling commands. for (auto const& range : uidRanges) { - EXPECT_FALSE(ipRuleExistsForRange(RULE_PRIORITY, range, "prohibit")); + EXPECT_FALSE(ipRuleExistsForRange(RULE_PRIORITY_PROHIBIT_NON_VPN, range, "prohibit")); } // Create two valid rules. ASSERT_TRUE(mNetd->networkRejectNonSecureVpn(true, uidRanges).isOk()); for (auto const& range : uidRanges) { - EXPECT_TRUE(ipRuleExistsForRange(RULE_PRIORITY, range, "prohibit")); + EXPECT_TRUE(ipRuleExistsForRange(RULE_PRIORITY_PROHIBIT_NON_VPN, range, "prohibit")); } // Remove the rules. ASSERT_TRUE(mNetd->networkRejectNonSecureVpn(false, uidRanges).isOk()); for (auto const& range : uidRanges) { - EXPECT_FALSE(ipRuleExistsForRange(RULE_PRIORITY, range, "prohibit")); + EXPECT_FALSE(ipRuleExistsForRange(RULE_PRIORITY_PROHIBIT_NON_VPN, range, "prohibit")); } // Fail to remove the rules a second time after they are already deleted. @@ -702,6 +788,72 @@ TEST_F(NetdBinderTest, SocketDestroy) { checkSocketpairClosed(clientSocket, acceptedSocket); } +TEST_F(NetdBinderTest, SocketDestroyLinkLocal) { + // Add the same link-local address to two interfaces. + const char* kLinkLocalAddress = "fe80::ace:d00d"; + + const struct addrinfo hints = { + .ai_family = AF_INET6, + .ai_socktype = SOCK_STREAM, + .ai_flags = AI_NUMERICHOST, + }; + + binder::Status status = mNetd->interfaceAddAddress(sTun.name(), kLinkLocalAddress, 64); + EXPECT_TRUE(status.isOk()) << status.exceptionMessage(); + status = mNetd->interfaceAddAddress(sTun2.name(), kLinkLocalAddress, 64); + EXPECT_TRUE(status.isOk()) << status.exceptionMessage(); + + // Bind a listening socket to the address on each of two interfaces. + // The sockets must be open at the same time, because this test checks that SOCK_DESTROY only + // destroys the sockets on the interface where the address is deleted. + struct addrinfo* addrinfoList = nullptr; + int ret = getaddrinfo(kLinkLocalAddress, nullptr, &hints, &addrinfoList); + ScopedAddrinfo addrinfoCleanup(addrinfoList); + ASSERT_EQ(0, ret); + + socklen_t len = addrinfoList[0].ai_addrlen; + sockaddr_in6 sin6_1 = *reinterpret_cast<sockaddr_in6*>(addrinfoList[0].ai_addr); + sockaddr_in6 sin6_2 = sin6_1; + sin6_1.sin6_scope_id = if_nametoindex(sTun.name().c_str()); + sin6_2.sin6_scope_id = if_nametoindex(sTun2.name().c_str()); + + int s1 = socket(AF_INET6, SOCK_STREAM, 0); + ASSERT_EQ(0, bind(s1, reinterpret_cast<sockaddr*>(&sin6_1), len)); + ASSERT_EQ(0, getsockname(s1, reinterpret_cast<sockaddr*>(&sin6_1), &len)); + + int s2 = socket(AF_INET6, SOCK_STREAM, 0); + ASSERT_EQ(0, bind(s2, reinterpret_cast<sockaddr*>(&sin6_2), len)); + ASSERT_EQ(0, getsockname(s2, reinterpret_cast<sockaddr*>(&sin6_2), &len)); + + ASSERT_EQ(0, listen(s1, 10)); + ASSERT_EQ(0, listen(s2, 10)); + + // Connect one client socket to each and accept the connections. + int c1 = socket(AF_INET6, SOCK_STREAM, 0); + int c2 = socket(AF_INET6, SOCK_STREAM, 0); + ASSERT_EQ(0, connect(c1, reinterpret_cast<sockaddr*>(&sin6_1), len)); + ASSERT_EQ(0, connect(c2, reinterpret_cast<sockaddr*>(&sin6_2), len)); + int a1 = accept(s1, nullptr, 0); + ASSERT_NE(-1, a1); + int a2 = accept(s2, nullptr, 0); + ASSERT_NE(-1, a2); + + // Delete the address on sTun2. + status = mNetd->interfaceDelAddress(sTun2.name(), kLinkLocalAddress, 64); + EXPECT_TRUE(status.isOk()) << status.exceptionMessage(); + + // The sockets on sTun2 are closed, but the ones on sTun1 remain open. + char buf[1024]; + EXPECT_EQ(-1, read(c2, buf, sizeof(buf))); + EXPECT_EQ(ECONNABORTED, errno); + // The blocking read above ensures that SOCK_DESTROY has completed. + + EXPECT_EQ(3, write(a1, "foo", 3)); + EXPECT_EQ(3, read(c1, buf, sizeof(buf))); + EXPECT_EQ(-1, write(a2, "foo", 3)); + EXPECT_TRUE(errno == ECONNABORTED || errno == ECONNRESET); +} + namespace { int netmaskToPrefixLength(const uint8_t *buf, size_t buflen) { @@ -820,6 +972,7 @@ TEST_F(NetdBinderTest, InterfaceAddRemoveAddress) { {"2001:db8::1", 64, 0, 0}, {"2001:db8::2", 65, 0, 0}, {"2001:db8::3", 128, 0, 0}, + {"fe80::1234", 64, 0, 0}, {"2001:db8::4", 129, EINVAL, EINVAL}, {"foo:bar::bad", 64, EINVAL, EINVAL}, {"2001:db8::1/64", 64, EINVAL, EINVAL}, @@ -875,7 +1028,7 @@ TEST_F(NetdBinderTest, InterfaceAddRemoveAddress) { } TEST_F(NetdBinderTest, GetProcSysNet) { - const char LOOPBACK[] = "lo"; + const char* LOOPBACK = "lo"; static const struct { const int ipversion; const int which; @@ -1090,7 +1243,7 @@ bool iptablesIdleTimerInterfaceRuleExists(const char* binary, const char* chainN void expectIdletimerInterfaceRuleExists(const std::string& ifname, int timeout, const std::string& classLabel) { std::string IdletimerRule = - StringPrintf("timeout:%u label:%s send_nl_msg:1", timeout, classLabel.c_str()); + StringPrintf("timeout:%u label:%s send_nl_msg", timeout, classLabel.c_str()); for (const auto& binary : {IPTABLES_PATH, IP6TABLES_PATH}) { EXPECT_TRUE(iptablesIdleTimerInterfaceRuleExists(binary, IDLETIMER_RAW_PREROUTING, ifname, IdletimerRule, RAW_TABLE)); @@ -1102,7 +1255,7 @@ void expectIdletimerInterfaceRuleExists(const std::string& ifname, int timeout, void expectIdletimerInterfaceRuleNotExists(const std::string& ifname, int timeout, const std::string& classLabel) { std::string IdletimerRule = - StringPrintf("timeout:%u label:%s send_nl_msg:1", timeout, classLabel.c_str()); + StringPrintf("timeout:%u label:%s send_nl_msg", timeout, classLabel.c_str()); for (const auto& binary : {IPTABLES_PATH, IP6TABLES_PATH}) { EXPECT_FALSE(iptablesIdleTimerInterfaceRuleExists(binary, IDLETIMER_RAW_PREROUTING, ifname, IdletimerRule, RAW_TABLE)); @@ -1255,7 +1408,9 @@ TEST_F(NetdBinderTest, ClatdStartStop) { EXPECT_EQ(ENODEV, status.serviceSpecificErrorCode()); // ... so create a test physical network and add our tun to it. - EXPECT_TRUE(mNetd->networkCreatePhysical(TEST_NETID1, INetd::PERMISSION_NONE).isOk()); + const auto& config = makeNativeNetworkConfig(TEST_NETID1, NativeNetworkType::PHYSICAL, + INetd::PERMISSION_NONE, false); + EXPECT_TRUE(mNetd->networkCreate(config).isOk()); EXPECT_TRUE(mNetd->networkAddInterface(TEST_NETID1, sTun.name()).isOk()); // Prefix must be 96 bits long. @@ -1345,7 +1500,8 @@ bool ipRuleIpfwdExists(const char* ipVersion, const std::string& ipfwdRule) { } void expectIpfwdRuleExists(const char* fromIf, const char* toIf) { - std::string ipfwdRule = StringPrintf("18000:\tfrom all iif %s lookup %s ", fromIf, toIf); + std::string ipfwdRule = + StringPrintf("%u:\tfrom all iif %s lookup %s ", RULE_PRIORITY_TETHERING, fromIf, toIf); for (const auto& ipVersion : {IP_RULE_V4, IP_RULE_V6}) { EXPECT_TRUE(ipRuleIpfwdExists(ipVersion, ipfwdRule)); @@ -1353,7 +1509,8 @@ void expectIpfwdRuleExists(const char* fromIf, const char* toIf) { } void expectIpfwdRuleNotExists(const char* fromIf, const char* toIf) { - std::string ipfwdRule = StringPrintf("18000:\tfrom all iif %s lookup %s ", fromIf, toIf); + std::string ipfwdRule = + StringPrintf("%u:\tfrom all iif %s lookup %s ", RULE_PRIORITY_TETHERING, fromIf, toIf); for (const auto& ipVersion : {IP_RULE_V4, IP_RULE_V6}) { EXPECT_FALSE(ipRuleIpfwdExists(ipVersion, ipfwdRule)); @@ -1417,9 +1574,13 @@ TEST_F(NetdBinderTest, TestIpfwdEnableDisableStatusForwarding) { TEST_F(NetdBinderTest, TestIpfwdAddRemoveInterfaceForward) { // Add test physical network - EXPECT_TRUE(mNetd->networkCreatePhysical(TEST_NETID1, INetd::PERMISSION_NONE).isOk()); + auto config = makeNativeNetworkConfig(TEST_NETID1, NativeNetworkType::PHYSICAL, + INetd::PERMISSION_NONE, false); + EXPECT_TRUE(mNetd->networkCreate(config).isOk()); EXPECT_TRUE(mNetd->networkAddInterface(TEST_NETID1, sTun.name()).isOk()); - EXPECT_TRUE(mNetd->networkCreatePhysical(TEST_NETID2, INetd::PERMISSION_NONE).isOk()); + + config.netId = TEST_NETID2; + EXPECT_TRUE(mNetd->networkCreate(config).isOk()); EXPECT_TRUE(mNetd->networkAddInterface(TEST_NETID2, sTun2.name()).isOk()); binder::Status status = mNetd->ipfwdAddInterfaceForward(sTun.name(), sTun2.name()); @@ -1437,7 +1598,6 @@ constexpr char BANDWIDTH_INPUT[] = "bw_INPUT"; constexpr char BANDWIDTH_OUTPUT[] = "bw_OUTPUT"; constexpr char BANDWIDTH_FORWARD[] = "bw_FORWARD"; constexpr char BANDWIDTH_NAUGHTY[] = "bw_penalty_box"; -constexpr char BANDWIDTH_NICE[] = "bw_happy_box"; constexpr char BANDWIDTH_ALERT[] = "bw_global_alert"; // TODO: Move iptablesTargetsExists and listIptablesRuleByTable to the top. @@ -1530,29 +1690,15 @@ void expectBandwidthGlobalAlertRuleExists(long alertBytes) { expectXtQuotaValueEqual(globalAlertName, alertBytes); } -void expectBandwidthManipulateSpecialAppRuleExists(const char* chain, const char* target, int uid) { - std::string uidRule = StringPrintf("owner UID match %u", uid); - - for (const auto& binary : {IPTABLES_PATH, IP6TABLES_PATH}) { - EXPECT_TRUE(iptablesTargetsExists(binary, 1, FILTER_TABLE, chain, target, uidRule)); - } -} - -void expectBandwidthManipulateSpecialAppRuleDoesNotExist(const char* chain, int uid) { - std::string uidRule = StringPrintf("owner UID match %u", uid); - - for (const auto& binary : {IPTABLES_PATH, IP6TABLES_PATH}) { - EXPECT_FALSE(iptablesRuleExists(binary, chain, uidRule)); - } -} - } // namespace TEST_F(NetdBinderTest, BandwidthSetRemoveInterfaceQuota) { long testQuotaBytes = 5550; // Add test physical network - EXPECT_TRUE(mNetd->networkCreatePhysical(TEST_NETID1, INetd::PERMISSION_NONE).isOk()); + const auto& config = makeNativeNetworkConfig(TEST_NETID1, NativeNetworkType::PHYSICAL, + INetd::PERMISSION_NONE, false); + EXPECT_TRUE(mNetd->networkCreate(config).isOk()); EXPECT_TRUE(mNetd->networkAddInterface(TEST_NETID1, sTun.name()).isOk()); binder::Status status = mNetd->bandwidthSetInterfaceQuota(sTun.name(), testQuotaBytes); @@ -1570,7 +1716,9 @@ TEST_F(NetdBinderTest, BandwidthSetRemoveInterfaceQuota) { TEST_F(NetdBinderTest, BandwidthSetRemoveInterfaceAlert) { long testAlertBytes = 373; // Add test physical network - EXPECT_TRUE(mNetd->networkCreatePhysical(TEST_NETID1, INetd::PERMISSION_NONE).isOk()); + const auto& config = makeNativeNetworkConfig(TEST_NETID1, NativeNetworkType::PHYSICAL, + INetd::PERMISSION_NONE, false); + EXPECT_TRUE(mNetd->networkCreate(config).isOk()); EXPECT_TRUE(mNetd->networkAddInterface(TEST_NETID1, sTun.name()).isOk()); // Need to have a prior interface quota set to set an alert binder::Status status = mNetd->bandwidthSetInterfaceQuota(sTun.name(), testAlertBytes); @@ -1604,34 +1752,6 @@ TEST_F(NetdBinderTest, BandwidthSetGlobalAlert) { expectBandwidthGlobalAlertRuleExists(testAlertBytes); } -TEST_F(NetdBinderTest, BandwidthManipulateSpecialApp) { - SKIP_IF_BPF_SUPPORTED; - - int32_t uid = randomUid(); - static const char targetReject[] = "REJECT"; - static const char targetReturn[] = "RETURN"; - - // add NaughtyApp - binder::Status status = mNetd->bandwidthAddNaughtyApp(uid); - EXPECT_TRUE(status.isOk()) << status.exceptionMessage(); - expectBandwidthManipulateSpecialAppRuleExists(BANDWIDTH_NAUGHTY, targetReject, uid); - - // remove NaughtyApp - status = mNetd->bandwidthRemoveNaughtyApp(uid); - EXPECT_TRUE(status.isOk()) << status.exceptionMessage(); - expectBandwidthManipulateSpecialAppRuleDoesNotExist(BANDWIDTH_NAUGHTY, uid); - - // add NiceApp - status = mNetd->bandwidthAddNiceApp(uid); - EXPECT_TRUE(status.isOk()) << status.exceptionMessage(); - expectBandwidthManipulateSpecialAppRuleExists(BANDWIDTH_NICE, targetReturn, uid); - - // remove NiceApp - status = mNetd->bandwidthRemoveNiceApp(uid); - EXPECT_TRUE(status.isOk()) << status.exceptionMessage(); - expectBandwidthManipulateSpecialAppRuleDoesNotExist(BANDWIDTH_NICE, uid); -} - namespace { std::string ipRouteString(const std::string& ifName, const std::string& dst, @@ -1695,7 +1815,8 @@ bool ipRuleExists(const char* ipVersion, const std::string& ipRule) { void expectNetworkDefaultIpRuleExists(const char* ifName) { std::string networkDefaultRule = - StringPrintf("22000:\tfrom all fwmark 0x0/0xffff iif lo lookup %s", ifName); + StringPrintf("%u:\tfrom all fwmark 0x0/0xffff iif lo lookup %s", + RULE_PRIORITY_DEFAULT_NETWORK, ifName); for (const auto& ipVersion : {IP_RULE_V4, IP_RULE_V6}) { EXPECT_TRUE(ipRuleExists(ipVersion, networkDefaultRule)); @@ -1703,7 +1824,8 @@ void expectNetworkDefaultIpRuleExists(const char* ifName) { } void expectNetworkDefaultIpRuleDoesNotExist() { - static const char networkDefaultRule[] = "22000:\tfrom all fwmark 0x0/0xffff iif lo"; + std::string networkDefaultRule = + StringPrintf("%u:\tfrom all fwmark 0x0/0xffff iif lo", RULE_PRIORITY_DEFAULT_NETWORK); for (const auto& ipVersion : {IP_RULE_V4, IP_RULE_V6}) { EXPECT_FALSE(ipRuleExists(ipVersion, networkDefaultRule)); @@ -1714,16 +1836,19 @@ void expectNetworkPermissionIpRuleExists(const char* ifName, int permission) { std::string networkPermissionRule = ""; switch (permission) { case INetd::PERMISSION_NONE: - networkPermissionRule = StringPrintf( - "13000:\tfrom all fwmark 0x1ffdd/0x1ffff iif lo lookup %s", ifName); + networkPermissionRule = + StringPrintf("%u:\tfrom all fwmark 0x1ffdd/0x1ffff iif lo lookup %s", + RULE_PRIORITY_EXPLICIT_NETWORK, ifName); break; case INetd::PERMISSION_NETWORK: - networkPermissionRule = StringPrintf( - "13000:\tfrom all fwmark 0x5ffdd/0x5ffff iif lo lookup %s", ifName); + networkPermissionRule = + StringPrintf("%u:\tfrom all fwmark 0x5ffdd/0x5ffff iif lo lookup %s", + RULE_PRIORITY_EXPLICIT_NETWORK, ifName); break; case INetd::PERMISSION_SYSTEM: - networkPermissionRule = StringPrintf( - "13000:\tfrom all fwmark 0xdffdd/0xdffff iif lo lookup %s", ifName); + networkPermissionRule = + StringPrintf("%u:\tfrom all fwmark 0xdffdd/0xdffff iif lo lookup %s", + RULE_PRIORITY_EXPLICIT_NETWORK, ifName); break; } @@ -1804,7 +1929,9 @@ TEST_F(NetdBinderTest, NetworkAddRemoveRouteUserPermission) { const std::vector<int32_t> testUids = {testUid}; // Add test physical network - EXPECT_TRUE(mNetd->networkCreatePhysical(TEST_NETID1, INetd::PERMISSION_NONE).isOk()); + const auto& config = makeNativeNetworkConfig(TEST_NETID1, NativeNetworkType::PHYSICAL, + INetd::PERMISSION_NONE, false); + EXPECT_TRUE(mNetd->networkCreate(config).isOk()); EXPECT_TRUE(mNetd->networkAddInterface(TEST_NETID1, sTun.name()).isOk()); // Setup route for testing nextHop @@ -1958,7 +2085,9 @@ TEST_F(NetdBinderTest, NetworkAddRemoveRouteUserPermission) { TEST_F(NetdBinderTest, NetworkPermissionDefault) { // Add test physical network - EXPECT_TRUE(mNetd->networkCreatePhysical(TEST_NETID1, INetd::PERMISSION_NONE).isOk()); + const auto& config = makeNativeNetworkConfig(TEST_NETID1, NativeNetworkType::PHYSICAL, + INetd::PERMISSION_NONE, false); + EXPECT_TRUE(mNetd->networkCreate(config).isOk()); EXPECT_TRUE(mNetd->networkAddInterface(TEST_NETID1, sTun.name()).isOk()); // Get current default network NetId @@ -2279,13 +2408,8 @@ namespace { constexpr char FIREWALL_INPUT[] = "fw_INPUT"; constexpr char FIREWALL_OUTPUT[] = "fw_OUTPUT"; constexpr char FIREWALL_FORWARD[] = "fw_FORWARD"; -constexpr char FIREWALL_DOZABLE[] = "fw_dozable"; -constexpr char FIREWALL_POWERSAVE[] = "fw_powersave"; -constexpr char FIREWALL_STANDBY[] = "fw_standby"; -constexpr char targetReturn[] = "RETURN"; -constexpr char targetDrop[] = "DROP"; -void expectFirewallWhitelistMode() { +void expectFirewallAllowlistMode() { static const char dropRule[] = "DROP all"; static const char rejectRule[] = "REJECT all"; for (const auto& binary : {IPTABLES_PATH, IP6TABLES_PATH}) { @@ -2295,7 +2419,7 @@ void expectFirewallWhitelistMode() { } } -void expectFirewallBlacklistMode() { +void expectFirewallDenylistMode() { for (const auto& binary : {IPTABLES_PATH, IP6TABLES_PATH}) { EXPECT_EQ(2, iptablesRuleLineLength(binary, FIREWALL_INPUT)); EXPECT_EQ(2, iptablesRuleLineLength(binary, FIREWALL_OUTPUT)); @@ -2357,126 +2481,45 @@ void expectFireWallInterfaceRuleAllowDoesNotExist(const std::string& ifname) { } } -bool iptablesFirewallUidFirstRuleExists(const char* binary, const char* chainName, - const std::string& expectedTarget, - const std::string& expectedRule) { - std::vector<std::string> rules = listIptablesRuleByTable(binary, FILTER_TABLE, chainName); - int firstRuleIndex = 2; - if (rules.size() < 4) return false; - if (rules[firstRuleIndex].find(expectedTarget) != std::string::npos) { - if (rules[firstRuleIndex].find(expectedRule) != std::string::npos) { - return true; - } - } - return false; -} - -bool iptablesFirewallUidLastRuleExists(const char* binary, const char* chainName, - const std::string& expectedTarget, - const std::string& expectedRule) { - std::vector<std::string> rules = listIptablesRuleByTable(binary, FILTER_TABLE, chainName); - int lastRuleIndex = rules.size() - 1; - if (lastRuleIndex < 0) return false; - if (rules[lastRuleIndex].find(expectedTarget) != std::string::npos) { - if (rules[lastRuleIndex].find(expectedRule) != std::string::npos) { - return true; - } - } - return false; -} - -void expectFirewallUidFirstRuleExists(const char* chainName, int32_t uid) { - std::string uidRule = StringPrintf("owner UID match %u", uid); - for (const auto& binary : {IPTABLES_PATH, IP6TABLES_PATH}) - EXPECT_TRUE(iptablesFirewallUidFirstRuleExists(binary, chainName, targetReturn, uidRule)); -} - -void expectFirewallUidFirstRuleDoesNotExist(const char* chainName, int32_t uid) { - std::string uidRule = StringPrintf("owner UID match %u", uid); - for (const auto& binary : {IPTABLES_PATH, IP6TABLES_PATH}) - EXPECT_FALSE(iptablesFirewallUidFirstRuleExists(binary, chainName, targetReturn, uidRule)); -} - -void expectFirewallUidLastRuleExists(const char* chainName, int32_t uid) { - std::string uidRule = StringPrintf("owner UID match %u", uid); - for (const auto& binary : {IPTABLES_PATH, IP6TABLES_PATH}) - EXPECT_TRUE(iptablesFirewallUidLastRuleExists(binary, chainName, targetDrop, uidRule)); -} - -void expectFirewallUidLastRuleDoesNotExist(const char* chainName, int32_t uid) { - std::string uidRule = StringPrintf("owner UID match %u", uid); - for (const auto& binary : {IPTABLES_PATH, IP6TABLES_PATH}) - EXPECT_FALSE(iptablesFirewallUidLastRuleExists(binary, chainName, targetDrop, uidRule)); -} - -bool iptablesFirewallChildChainsLastRuleExists(const char* binary, const char* chainName) { - std::vector<std::string> inputRules = - listIptablesRuleByTable(binary, FILTER_TABLE, FIREWALL_INPUT); - std::vector<std::string> outputRules = - listIptablesRuleByTable(binary, FILTER_TABLE, FIREWALL_OUTPUT); - int inputLastRuleIndex = inputRules.size() - 1; - int outputLastRuleIndex = outputRules.size() - 1; - - if (inputLastRuleIndex < 0 || outputLastRuleIndex < 0) return false; - if (inputRules[inputLastRuleIndex].find(chainName) != std::string::npos) { - if (outputRules[outputLastRuleIndex].find(chainName) != std::string::npos) { - return true; - } - } - return false; -} - -void expectFirewallChildChainsLastRuleExists(const char* chainRule) { - for (const auto& binary : {IPTABLES_PATH, IP6TABLES_PATH}) - EXPECT_TRUE(iptablesFirewallChildChainsLastRuleExists(binary, chainRule)); -} - -void expectFirewallChildChainsLastRuleDoesNotExist(const char* chainRule) { - for (const auto& binary : {IPTABLES_PATH, IP6TABLES_PATH}) { - EXPECT_FALSE(iptablesRuleExists(binary, FIREWALL_INPUT, chainRule)); - EXPECT_FALSE(iptablesRuleExists(binary, FIREWALL_OUTPUT, chainRule)); - } -} - } // namespace TEST_F(NetdBinderTest, FirewallSetFirewallType) { - binder::Status status = mNetd->firewallSetFirewallType(INetd::FIREWALL_WHITELIST); + binder::Status status = mNetd->firewallSetFirewallType(INetd::FIREWALL_ALLOWLIST); EXPECT_TRUE(status.isOk()) << status.exceptionMessage(); - expectFirewallWhitelistMode(); + expectFirewallAllowlistMode(); - status = mNetd->firewallSetFirewallType(INetd::FIREWALL_BLACKLIST); + status = mNetd->firewallSetFirewallType(INetd::FIREWALL_DENYLIST); EXPECT_TRUE(status.isOk()) << status.exceptionMessage(); - expectFirewallBlacklistMode(); + expectFirewallDenylistMode(); // set firewall type blacklist twice - mNetd->firewallSetFirewallType(INetd::FIREWALL_BLACKLIST); - status = mNetd->firewallSetFirewallType(INetd::FIREWALL_BLACKLIST); + mNetd->firewallSetFirewallType(INetd::FIREWALL_DENYLIST); + status = mNetd->firewallSetFirewallType(INetd::FIREWALL_DENYLIST); EXPECT_TRUE(status.isOk()) << status.exceptionMessage(); - expectFirewallBlacklistMode(); + expectFirewallDenylistMode(); // set firewall type whitelist twice - mNetd->firewallSetFirewallType(INetd::FIREWALL_WHITELIST); - status = mNetd->firewallSetFirewallType(INetd::FIREWALL_WHITELIST); + mNetd->firewallSetFirewallType(INetd::FIREWALL_ALLOWLIST); + status = mNetd->firewallSetFirewallType(INetd::FIREWALL_ALLOWLIST); EXPECT_TRUE(status.isOk()) << status.exceptionMessage(); - expectFirewallWhitelistMode(); + expectFirewallAllowlistMode(); // reset firewall type to default - status = mNetd->firewallSetFirewallType(INetd::FIREWALL_BLACKLIST); + status = mNetd->firewallSetFirewallType(INetd::FIREWALL_DENYLIST); EXPECT_TRUE(status.isOk()) << status.exceptionMessage(); - expectFirewallBlacklistMode(); + expectFirewallDenylistMode(); } TEST_F(NetdBinderTest, FirewallSetInterfaceRule) { // setinterfaceRule is not supported in BLACKLIST MODE - binder::Status status = mNetd->firewallSetFirewallType(INetd::FIREWALL_BLACKLIST); + binder::Status status = mNetd->firewallSetFirewallType(INetd::FIREWALL_DENYLIST); EXPECT_TRUE(status.isOk()) << status.exceptionMessage(); status = mNetd->firewallSetInterfaceRule(sTun.name(), INetd::FIREWALL_RULE_ALLOW); EXPECT_FALSE(status.isOk()) << status.exceptionMessage(); // set WHITELIST mode first - status = mNetd->firewallSetFirewallType(INetd::FIREWALL_WHITELIST); + status = mNetd->firewallSetFirewallType(INetd::FIREWALL_ALLOWLIST); EXPECT_TRUE(status.isOk()) << status.exceptionMessage(); status = mNetd->firewallSetInterfaceRule(sTun.name(), INetd::FIREWALL_RULE_ALLOW); @@ -2488,113 +2531,9 @@ TEST_F(NetdBinderTest, FirewallSetInterfaceRule) { expectFireWallInterfaceRuleAllowDoesNotExist(sTun.name()); // reset firewall mode to default - status = mNetd->firewallSetFirewallType(INetd::FIREWALL_BLACKLIST); - EXPECT_TRUE(status.isOk()) << status.exceptionMessage(); - expectFirewallBlacklistMode(); -} - -TEST_F(NetdBinderTest, FirewallSetUidRule) { - SKIP_IF_BPF_SUPPORTED; - - int32_t uid = randomUid(); - - // Doze allow - binder::Status status = mNetd->firewallSetUidRule(INetd::FIREWALL_CHAIN_DOZABLE, uid, - INetd::FIREWALL_RULE_ALLOW); - EXPECT_TRUE(status.isOk()) << status.exceptionMessage(); - expectFirewallUidFirstRuleExists(FIREWALL_DOZABLE, uid); - - // Doze deny - status = mNetd->firewallSetUidRule(INetd::FIREWALL_CHAIN_DOZABLE, uid, - INetd::FIREWALL_RULE_DENY); - EXPECT_TRUE(status.isOk()) << status.exceptionMessage(); - expectFirewallUidFirstRuleDoesNotExist(FIREWALL_DOZABLE, uid); - - // Powersave allow - status = mNetd->firewallSetUidRule(INetd::FIREWALL_CHAIN_POWERSAVE, uid, - INetd::FIREWALL_RULE_ALLOW); - EXPECT_TRUE(status.isOk()) << status.exceptionMessage(); - expectFirewallUidFirstRuleExists(FIREWALL_POWERSAVE, uid); - - // Powersave deny - status = mNetd->firewallSetUidRule(INetd::FIREWALL_CHAIN_POWERSAVE, uid, - INetd::FIREWALL_RULE_DENY); - EXPECT_TRUE(status.isOk()) << status.exceptionMessage(); - expectFirewallUidFirstRuleDoesNotExist(FIREWALL_POWERSAVE, uid); - - // Standby deny - status = mNetd->firewallSetUidRule(INetd::FIREWALL_CHAIN_STANDBY, uid, - INetd::FIREWALL_RULE_DENY); - EXPECT_TRUE(status.isOk()) << status.exceptionMessage(); - expectFirewallUidLastRuleExists(FIREWALL_STANDBY, uid); - - // Standby allow - status = mNetd->firewallSetUidRule(INetd::FIREWALL_CHAIN_STANDBY, uid, - INetd::FIREWALL_RULE_ALLOW); - EXPECT_TRUE(status.isOk()) << status.exceptionMessage(); - expectFirewallUidLastRuleDoesNotExist(FIREWALL_STANDBY, uid); - - // None deny in BLACKLIST - status = mNetd->firewallSetUidRule(INetd::FIREWALL_CHAIN_NONE, uid, INetd::FIREWALL_RULE_DENY); - EXPECT_TRUE(status.isOk()) << status.exceptionMessage(); - expectFirewallUidLastRuleExists(FIREWALL_INPUT, uid); - expectFirewallUidLastRuleExists(FIREWALL_OUTPUT, uid); - - // None allow in BLACKLIST - status = mNetd->firewallSetUidRule(INetd::FIREWALL_CHAIN_NONE, uid, INetd::FIREWALL_RULE_ALLOW); - EXPECT_TRUE(status.isOk()) << status.exceptionMessage(); - expectFirewallUidLastRuleDoesNotExist(FIREWALL_INPUT, uid); - expectFirewallUidLastRuleDoesNotExist(FIREWALL_OUTPUT, uid); - - // set firewall type whitelist twice - status = mNetd->firewallSetFirewallType(INetd::FIREWALL_WHITELIST); - EXPECT_TRUE(status.isOk()) << status.exceptionMessage(); - expectFirewallWhitelistMode(); - - // None allow in WHITELIST - status = mNetd->firewallSetUidRule(INetd::FIREWALL_CHAIN_NONE, uid, INetd::FIREWALL_RULE_ALLOW); - EXPECT_TRUE(status.isOk()) << status.exceptionMessage(); - expectFirewallUidFirstRuleExists(FIREWALL_INPUT, uid); - expectFirewallUidFirstRuleExists(FIREWALL_OUTPUT, uid); - - // None deny in WHITELIST - status = mNetd->firewallSetUidRule(INetd::FIREWALL_CHAIN_NONE, uid, INetd::FIREWALL_RULE_DENY); - EXPECT_TRUE(status.isOk()) << status.exceptionMessage(); - expectFirewallUidFirstRuleDoesNotExist(FIREWALL_INPUT, uid); - expectFirewallUidFirstRuleDoesNotExist(FIREWALL_OUTPUT, uid); - - // reset firewall mode to default - status = mNetd->firewallSetFirewallType(INetd::FIREWALL_BLACKLIST); - EXPECT_TRUE(status.isOk()) << status.exceptionMessage(); - expectFirewallBlacklistMode(); -} - -TEST_F(NetdBinderTest, FirewallEnableDisableChildChains) { - SKIP_IF_BPF_SUPPORTED; - - binder::Status status = mNetd->firewallEnableChildChain(INetd::FIREWALL_CHAIN_DOZABLE, true); + status = mNetd->firewallSetFirewallType(INetd::FIREWALL_DENYLIST); EXPECT_TRUE(status.isOk()) << status.exceptionMessage(); - expectFirewallChildChainsLastRuleExists(FIREWALL_DOZABLE); - - status = mNetd->firewallEnableChildChain(INetd::FIREWALL_CHAIN_STANDBY, true); - EXPECT_TRUE(status.isOk()) << status.exceptionMessage(); - expectFirewallChildChainsLastRuleExists(FIREWALL_STANDBY); - - status = mNetd->firewallEnableChildChain(INetd::FIREWALL_CHAIN_POWERSAVE, true); - EXPECT_TRUE(status.isOk()) << status.exceptionMessage(); - expectFirewallChildChainsLastRuleExists(FIREWALL_POWERSAVE); - - status = mNetd->firewallEnableChildChain(INetd::FIREWALL_CHAIN_DOZABLE, false); - EXPECT_TRUE(status.isOk()) << status.exceptionMessage(); - expectFirewallChildChainsLastRuleDoesNotExist(FIREWALL_DOZABLE); - - status = mNetd->firewallEnableChildChain(INetd::FIREWALL_CHAIN_STANDBY, false); - EXPECT_TRUE(status.isOk()) << status.exceptionMessage(); - expectFirewallChildChainsLastRuleDoesNotExist(FIREWALL_STANDBY); - - status = mNetd->firewallEnableChildChain(INetd::FIREWALL_CHAIN_POWERSAVE, false); - EXPECT_TRUE(status.isOk()) << status.exceptionMessage(); - expectFirewallChildChainsLastRuleDoesNotExist(FIREWALL_POWERSAVE); + expectFirewallDenylistMode(); } namespace { @@ -2797,7 +2736,9 @@ TEST_F(NetdBinderTest, InterfaceGetCfg) { InterfaceConfigurationParcel interfaceCfgResult; // Add test physical network - EXPECT_TRUE(mNetd->networkCreatePhysical(TEST_NETID1, INetd::PERMISSION_NONE).isOk()); + const auto& config = makeNativeNetworkConfig(TEST_NETID1, NativeNetworkType::PHYSICAL, + INetd::PERMISSION_NONE, false); + EXPECT_TRUE(mNetd->networkCreate(config).isOk()); EXPECT_TRUE(mNetd->networkAddInterface(TEST_NETID1, sTun.name()).isOk()); binder::Status status = mNetd->interfaceGetCfg(sTun.name(), &interfaceCfgResult); @@ -2816,7 +2757,9 @@ TEST_F(NetdBinderTest, InterfaceSetCfg) { std::vector<std::string> downFlags = {"down"}; // Add test physical network - EXPECT_TRUE(mNetd->networkCreatePhysical(TEST_NETID1, INetd::PERMISSION_NONE).isOk()); + const auto& config = makeNativeNetworkConfig(TEST_NETID1, NativeNetworkType::PHYSICAL, + INetd::PERMISSION_NONE, false); + EXPECT_TRUE(mNetd->networkCreate(config).isOk()); EXPECT_TRUE(mNetd->networkAddInterface(TEST_NETID1, sTun.name()).isOk()); // Set tun interface down. @@ -2854,7 +2797,9 @@ TEST_F(NetdBinderTest, InterfaceClearAddr) { std::vector<std::string> noFlags{}; // Add test physical network - EXPECT_TRUE(mNetd->networkCreatePhysical(TEST_NETID1, INetd::PERMISSION_NONE).isOk()); + const auto& config = makeNativeNetworkConfig(TEST_NETID1, NativeNetworkType::PHYSICAL, + INetd::PERMISSION_NONE, false); + EXPECT_TRUE(mNetd->networkCreate(config).isOk()); EXPECT_TRUE(mNetd->networkAddInterface(TEST_NETID1, sTun.name()).isOk()); auto interfaceCfg = makeInterfaceCfgParcel(sTun.name(), testAddr, testPrefixLength, noFlags); @@ -2872,7 +2817,9 @@ TEST_F(NetdBinderTest, InterfaceClearAddr) { TEST_F(NetdBinderTest, InterfaceSetEnableIPv6) { // Add test physical network - EXPECT_TRUE(mNetd->networkCreatePhysical(TEST_NETID1, INetd::PERMISSION_NONE).isOk()); + const auto& config = makeNativeNetworkConfig(TEST_NETID1, NativeNetworkType::PHYSICAL, + INetd::PERMISSION_NONE, false); + EXPECT_TRUE(mNetd->networkCreate(config).isOk()); EXPECT_TRUE(mNetd->networkAddInterface(TEST_NETID1, sTun.name()).isOk()); // disable @@ -2893,7 +2840,9 @@ TEST_F(NetdBinderTest, InterfaceSetMtu) { const int testMtu = 1200; // Add test physical network - EXPECT_TRUE(mNetd->networkCreatePhysical(TEST_NETID1, INetd::PERMISSION_NONE).isOk()); + const auto& config = makeNativeNetworkConfig(TEST_NETID1, NativeNetworkType::PHYSICAL, + INetd::PERMISSION_NONE, false); + EXPECT_TRUE(mNetd->networkCreate(config).isOk()); EXPECT_TRUE(mNetd->networkAddInterface(TEST_NETID1, sTun.name()).isOk()); binder::Status status = mNetd->interfaceSetMtu(sTun.name(), testMtu); @@ -3059,8 +3008,6 @@ void checkUidsInPermissionMap(std::vector<int32_t>& uids, bool exist) { } // namespace TEST_F(NetdBinderTest, TestInternetPermission) { - SKIP_IF_BPF_NOT_SUPPORTED; - std::vector<int32_t> appUids = {TEST_UID1, TEST_UID2}; mNetd->trafficSetNetPermForUids(INetd::PERMISSION_INTERNET, appUids); @@ -3181,7 +3128,9 @@ TEST_F(NetdBinderTest, NDC) { } // Add test physical network - EXPECT_TRUE(mNetd->networkCreatePhysical(TEST_NETID1, INetd::PERMISSION_NONE).isOk()); + const auto& config = makeNativeNetworkConfig(TEST_NETID1, NativeNetworkType::PHYSICAL, + INetd::PERMISSION_NONE, false); + EXPECT_TRUE(mNetd->networkCreate(config).isOk()); EXPECT_TRUE(mNetd->networkAddInterface(TEST_NETID1, sTun.name()).isOk()); for (const auto& td : kTestData) { @@ -3271,11 +3220,16 @@ void NetdBinderTest::createVpnNetworkWithUid(bool secure, uid_t uid, int vpnNetI sTun2.init(); // Create physical network with fallthroughNetId but not set it as default network - EXPECT_TRUE(mNetd->networkCreatePhysical(fallthroughNetId, INetd::PERMISSION_NONE).isOk()); + auto config = makeNativeNetworkConfig(fallthroughNetId, NativeNetworkType::PHYSICAL, + INetd::PERMISSION_NONE, false); + EXPECT_TRUE(mNetd->networkCreate(config).isOk()); EXPECT_TRUE(mNetd->networkAddInterface(fallthroughNetId, sTun.name()).isOk()); // Create VPN with vpnNetId - EXPECT_TRUE(mNetd->networkCreateVpn(vpnNetId, secure).isOk()); + config.netId = vpnNetId; + config.networkType = NativeNetworkType::VIRTUAL; + config.secure = secure; + EXPECT_TRUE(mNetd->networkCreate(config).isOk()); // Add uid to VPN EXPECT_TRUE(mNetd->networkAddUidRanges(vpnNetId, {makeUidRangeParcel(uid, uid)}).isOk()); @@ -3287,12 +3241,68 @@ void NetdBinderTest::createVpnNetworkWithUid(bool secure, uid_t uid, int vpnNetI EXPECT_TRUE(mNetd->networkAddRoute(TEST_NETID2, sTun2.name(), "2001:db8::/32", "").isOk()); } +void NetdBinderTest::createAndSetDefaultNetwork(int netId, const std::string& interface, + int permission) { + // backup current default network. + ASSERT_TRUE(mNetd->networkGetDefault(&mStoredDefaultNetwork).isOk()); + + const auto& config = + makeNativeNetworkConfig(netId, NativeNetworkType::PHYSICAL, permission, false); + EXPECT_TRUE(mNetd->networkCreate(config).isOk()); + EXPECT_TRUE(mNetd->networkAddInterface(netId, interface).isOk()); + EXPECT_TRUE(mNetd->networkSetDefault(netId).isOk()); +} + +void NetdBinderTest::createPhysicalNetwork(int netId, const std::string& interface, + int permission) { + const auto& config = + makeNativeNetworkConfig(netId, NativeNetworkType::PHYSICAL, permission, false); + EXPECT_TRUE(mNetd->networkCreate(config).isOk()); + EXPECT_TRUE(mNetd->networkAddInterface(netId, interface).isOk()); +} + +// 1. Create a physical network on sTun, and set it as the system default network. +// 2. Create another physical network on sTun2. +void NetdBinderTest::createDefaultAndOtherPhysicalNetwork(int defaultNetId, int otherNetId) { + createAndSetDefaultNetwork(defaultNetId, sTun.name()); + EXPECT_TRUE(mNetd->networkAddRoute(defaultNetId, sTun.name(), "::/0", "").isOk()); + + createPhysicalNetwork(otherNetId, sTun2.name()); + EXPECT_TRUE(mNetd->networkAddRoute(otherNetId, sTun2.name(), "::/0", "").isOk()); +} + +// 1. Create a system default network and a physical network. +// 2. Create a VPN on sTun3. +void NetdBinderTest::createVpnAndOtherPhysicalNetwork(int systemDefaultNetId, int otherNetId, + int vpnNetId, bool secure) { + createDefaultAndOtherPhysicalNetwork(systemDefaultNetId, otherNetId); + + auto config = makeNativeNetworkConfig(vpnNetId, NativeNetworkType::VIRTUAL, + INetd::PERMISSION_NONE, secure); + EXPECT_TRUE(mNetd->networkCreate(config).isOk()); + EXPECT_TRUE(mNetd->networkAddInterface(vpnNetId, sTun3.name()).isOk()); + EXPECT_TRUE(mNetd->networkAddRoute(vpnNetId, sTun3.name(), "2001:db8::/32", "").isOk()); +} + +// 1. Create system default network, a physical network (for per-app default), and a VPN. +// 2. Add per-app uid ranges and VPN ranges. +void NetdBinderTest::createVpnAndAppDefaultNetworkWithUid( + int systemDefaultNetId, int appDefaultNetId, int vpnNetId, bool secure, + std::vector<UidRangeParcel>&& appDefaultUidRanges, + std::vector<UidRangeParcel>&& vpnUidRanges) { + createVpnAndOtherPhysicalNetwork(systemDefaultNetId, appDefaultNetId, vpnNetId, secure); + // add per-app uid ranges. + EXPECT_TRUE(mNetd->networkAddUidRanges(appDefaultNetId, appDefaultUidRanges).isOk()); + // add VPN uid ranges. + EXPECT_TRUE(mNetd->networkAddUidRanges(vpnNetId, vpnUidRanges).isOk()); +} + namespace { class ScopedUidChange { public: explicit ScopedUidChange(uid_t uid) : mInputUid(uid) { - mStoredUid = getuid(); + mStoredUid = geteuid(); if (mInputUid == mStoredUid) return; EXPECT_TRUE(seteuid(uid) == 0); } @@ -3306,8 +3316,6 @@ class ScopedUidChange { uid_t mStoredUid; }; -constexpr uint32_t RULE_PRIORITY_VPN_FALLTHROUGH = 21000; - void clearQueue(int tunFd) { char buf[4096]; int ret; @@ -3316,17 +3324,18 @@ void clearQueue(int tunFd) { } while (ret > 0); } -void checkDataReceived(int udpSocket, int tunFd) { +void checkDataReceived(int udpSocket, int tunFd, sockaddr* dstAddr, int addrLen) { char buf[4096] = {}; // Clear tunFd's queue before write something because there might be some // arbitrary packets in the queue. (e.g. ICMPv6 packet) clearQueue(tunFd); - EXPECT_EQ(4, write(udpSocket, "foo", sizeof("foo"))); + EXPECT_EQ(4, sendto(udpSocket, "foo", sizeof("foo"), 0, dstAddr, addrLen)); // TODO: extract header and verify data EXPECT_GT(read(tunFd, buf, sizeof(buf)), 0); } -bool sendIPv6PacketFromUid(uid_t uid, const in6_addr& dstAddr, Fwmark* fwmark, int tunFd) { +bool sendIPv6PacketFromUid(uid_t uid, const in6_addr& dstAddr, Fwmark* fwmark, int tunFd, + bool doConnect = true) { ScopedUidChange scopedUidChange(uid); unique_fd testSocket(socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0)); if (testSocket < 0) return false; @@ -3336,15 +3345,51 @@ bool sendIPv6PacketFromUid(uid_t uid, const in6_addr& dstAddr, Fwmark* fwmark, i .sin6_port = 42, .sin6_addr = dstAddr, }; - int res = connect(testSocket, (sockaddr*)&dst6, sizeof(dst6)); + if (doConnect && connect(testSocket, (sockaddr*)&dst6, sizeof(dst6)) == -1) return false; + socklen_t fwmarkLen = sizeof(fwmark->intValue); EXPECT_NE(-1, getsockopt(testSocket, SOL_SOCKET, SO_MARK, &(fwmark->intValue), &fwmarkLen)); - if (res == -1) return false; char addr[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, &dstAddr, addr, INET6_ADDRSTRLEN); - SCOPED_TRACE(StringPrintf("sendIPv6PacketFromUid, addr: %s, uid: %u", addr, uid)); - checkDataReceived(testSocket, tunFd); + SCOPED_TRACE(StringPrintf("sendIPv6Packet, addr: %s, uid: %u, doConnect: %s", addr, uid, + doConnect ? "true" : "false")); + if (doConnect) { + checkDataReceived(testSocket, tunFd, nullptr, 0); + } else { + checkDataReceived(testSocket, tunFd, (sockaddr*)&dst6, sizeof(dst6)); + } + return true; +} + +// Send an IPv6 packet from the uid. Expect to fail and get specified errno. +bool sendIPv6PacketFromUidFail(uid_t uid, const in6_addr& dstAddr, Fwmark* fwmark, bool doConnect, + int expectedErr) { + ScopedUidChange scopedUidChange(uid); + unique_fd s(socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0)); + if (s < 0) return false; + + const sockaddr_in6 dst6 = { + .sin6_family = AF_INET6, + .sin6_port = 42, + .sin6_addr = dstAddr, + }; + if (doConnect) { + if (connect(s, (sockaddr*)&dst6, sizeof(dst6)) == 0) return false; + if (errno != expectedErr) return false; + } + + socklen_t fwmarkLen = sizeof(fwmark->intValue); + EXPECT_NE(-1, getsockopt(s, SOL_SOCKET, SO_MARK, &(fwmark->intValue), &fwmarkLen)); + + char addr[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, &dstAddr, addr, INET6_ADDRSTRLEN); + SCOPED_TRACE(StringPrintf("sendIPv6PacketFail, addr: %s, uid: %u, doConnect: %s", addr, uid, + doConnect ? "true" : "false")); + if (!doConnect) { + if (sendto(s, "foo", sizeof("foo"), 0, (sockaddr*)&dst6, sizeof(dst6)) == 0) return false; + if (errno != expectedErr) return false; + } return true; } @@ -3477,24 +3522,24 @@ TEST_F(NetdBinderTest, GetFwmarkForNetwork) { // Save current default network. ASSERT_TRUE(mNetd->networkGetDefault(&mStoredDefaultNetwork).isOk()); - in6_addr v6Addr = { - {// 2001:db8:cafe::8888 - .u6_addr8 = {0x20, 0x01, 0x0d, 0xb8, 0xca, 0xfe, 0, 0, 0, 0, 0, 0, 0, 0, 0x88, 0x88}}}; // Add test physical network 1 and set as default network. - EXPECT_TRUE(mNetd->networkCreatePhysical(TEST_NETID1, INetd::PERMISSION_NONE).isOk()); + auto config = makeNativeNetworkConfig(TEST_NETID1, NativeNetworkType::PHYSICAL, + INetd::PERMISSION_NONE, false); + EXPECT_TRUE(mNetd->networkCreate(config).isOk()); EXPECT_TRUE(mNetd->networkAddInterface(TEST_NETID1, sTun.name()).isOk()); EXPECT_TRUE(mNetd->networkAddRoute(TEST_NETID1, sTun.name(), "2001:db8::/32", "").isOk()); EXPECT_TRUE(mNetd->networkSetDefault(TEST_NETID1).isOk()); // Add test physical network 2 - EXPECT_TRUE(mNetd->networkCreatePhysical(TEST_NETID2, INetd::PERMISSION_NONE).isOk()); + config.netId = TEST_NETID2; + EXPECT_TRUE(mNetd->networkCreate(config).isOk()); EXPECT_TRUE(mNetd->networkAddInterface(TEST_NETID2, sTun2.name()).isOk()); // Get fwmark for network 1. MarkMaskParcel maskMarkNet1; ASSERT_TRUE(mNetd->getFwmarkForNetwork(TEST_NETID1, &maskMarkNet1).isOk()); - uint32_t fwmarkTcp = createIpv6SocketAndCheckMark(SOCK_STREAM, v6Addr); - uint32_t fwmarkUdp = createIpv6SocketAndCheckMark(SOCK_DGRAM, v6Addr); + uint32_t fwmarkTcp = createIpv6SocketAndCheckMark(SOCK_STREAM, V6_ADDR); + uint32_t fwmarkUdp = createIpv6SocketAndCheckMark(SOCK_DGRAM, V6_ADDR); EXPECT_EQ(maskMarkNet1.mark, static_cast<int>(fwmarkTcp & maskMarkNet1.mask)); EXPECT_EQ(maskMarkNet1.mark, static_cast<int>(fwmarkUdp & maskMarkNet1.mask)); @@ -3529,9 +3574,8 @@ TetherOffloadRuleParcel makeTetherOffloadRule(int inputInterfaceIndex, int outpu } // namespace -TEST_F(NetdBinderTest, TetherOffloadRule) { - SKIP_IF_BPF_NOT_SUPPORTED; - +// TODO: probably remove the test because TetherOffload* binder calls are deprecated. +TEST_F(NetdBinderTest, DISABLED_TetherOffloadRule) { // TODO: Perhaps verify invalid interface index once the netd handle the error in methods. constexpr uint32_t kIfaceInt = 101; constexpr uint32_t kIfaceExt = 102; @@ -3645,7 +3689,27 @@ static bool expectPacket(int fd, uint8_t* ipPacket, ssize_t ipLen) { return false; } -TEST_F(NetdBinderTest, TetherOffloadForwarding) { +static bool tcQdiscExists(const std::string& interface) { + std::string command = StringPrintf("tc qdisc show dev %s", interface.c_str()); + std::vector<std::string> lines = runCommand(command); + for (const auto& line : lines) { + if (StartsWith(line, "qdisc clsact ffff:")) return true; + } + return false; +} + +static bool tcFilterExists(const std::string& interface) { + std::string command = StringPrintf("tc filter show dev %s ingress", interface.c_str()); + std::vector<std::string> lines = runCommand(command); + const std::basic_regex regex("^filter .* bpf .* prog_offload_schedcls_tether_.*$"); + for (const auto& line : lines) { + if (std::regex_match(Trim(line), regex)) return true; + } + return false; +} + +// TODO: probably remove the test because TetherOffload* binder calls are deprecated. +TEST_F(NetdBinderTest, DISABLED_TetherOffloadForwarding) { SKIP_IF_EXTENDED_BPF_NOT_SUPPORTED; constexpr const char* kDownstreamPrefix = "2001:db8:2::/64"; @@ -3672,9 +3736,12 @@ TEST_F(NetdBinderTest, TetherOffloadForwarding) { // Use one of the test's tun interfaces as upstream. // It must be part of a network or it will not have the clsact attached. - EXPECT_TRUE(mNetd->networkCreatePhysical(TEST_NETID1, INetd::PERMISSION_NONE).isOk()); + const auto& config = makeNativeNetworkConfig(TEST_NETID1, NativeNetworkType::PHYSICAL, + INetd::PERMISSION_NONE, false); + EXPECT_TRUE(mNetd->networkCreate(config).isOk()); EXPECT_TRUE(mNetd->networkAddInterface(TEST_NETID1, sTun.name()).isOk()); int fd1 = sTun.getFdForTesting(); + EXPECT_TRUE(tcQdiscExists(sTun.name())); // Create our own tap as a downstream. TunInterface tap; @@ -3692,6 +3759,7 @@ TEST_F(NetdBinderTest, TetherOffloadForwarding) { status = mNetd->tetherInterfaceAdd(tap.name()); EXPECT_TRUE(status.isOk()) << status.exceptionMessage(); expectTetherInterfaceConfigureForIPv6Router(tap.name()); + EXPECT_TRUE(tcQdiscExists(tap.name())); // Can't easily use INetd::NEXTHOP_NONE because it is a String16 constant. Use "" instead. status = mNetd->networkAddRoute(INetd::LOCAL_NET_ID, tap.name(), kDownstreamPrefix, ""); @@ -3702,6 +3770,7 @@ TEST_F(NetdBinderTest, TetherOffloadForwarding) { EXPECT_TRUE(status.isOk()) << status.exceptionMessage(); status = mNetd->ipfwdAddInterfaceForward(tap.name(), sTun.name()); EXPECT_TRUE(status.isOk()) << status.exceptionMessage(); + EXPECT_TRUE(tcFilterExists(sTun.name())); std::vector<uint8_t> kDummyMac = {02, 00, 00, 00, 00, 00}; uint8_t* daddr = reinterpret_cast<uint8_t*>(&pkt.hdr.daddr); @@ -3753,3 +3822,880 @@ TEST_F(NetdBinderTest, TetherOffloadForwarding) { EXPECT_TRUE(mNetd->networkRemoveInterface(INetd::LOCAL_NET_ID, tap.name()).isOk()); EXPECT_TRUE(mNetd->networkRemoveInterface(TEST_NETID1, sTun.name()).isOk()); } + +namespace { + +std::vector<std::string> dumpService(const sp<IBinder>& binder) { + unique_fd localFd, remoteFd; + bool success = Pipe(&localFd, &remoteFd); + EXPECT_TRUE(success) << "Failed to open pipe for dumping: " << strerror(errno); + if (!success) return {}; + + // dump() blocks until another thread has consumed all its output. + std::thread dumpThread = std::thread([binder, remoteFd{std::move(remoteFd)}]() { + android::status_t ret = binder->dump(remoteFd, {}); + EXPECT_EQ(android::OK, ret) << "Error dumping service: " << android::statusToString(ret); + }); + + std::string dumpContent; + + EXPECT_TRUE(ReadFdToString(localFd.get(), &dumpContent)) + << "Error during dump: " << strerror(errno); + dumpThread.join(); + + std::stringstream dumpStream(std::move(dumpContent)); + std::vector<std::string> lines; + std::string line; + while (std::getline(dumpStream, line)) { + lines.push_back(line); + } + + return lines; +} + +} // namespace + +TEST_F(NetdBinderTest, TestServiceDump) { + sp<IBinder> binder = INetd::asBinder(mNetd); + ASSERT_NE(nullptr, binder); + + struct TestData { + // Expected contents of the dump command. + const std::string output; + // A regex that might be helpful in matching relevant lines in the output. + // Used to make it easier to add test cases for this code. + const std::string hintRegex; + }; + std::vector<TestData> testData; + + // Send some IPCs and for each one add an element to testData telling us what to expect. + const auto& config = makeNativeNetworkConfig(TEST_DUMP_NETID, NativeNetworkType::PHYSICAL, + INetd::PERMISSION_NONE, false); + EXPECT_TRUE(mNetd->networkCreate(config).isOk()); + testData.push_back( + {"networkCreate(NativeNetworkConfig{netId: 65123, networkType: PHYSICAL, " + "permission: 0, secure: false, vpnType: PLATFORM})", + "networkCreate.*65123"}); + + EXPECT_EQ(EEXIST, mNetd->networkCreate(config).serviceSpecificErrorCode()); + testData.push_back( + {"networkCreate(NativeNetworkConfig{netId: 65123, networkType: PHYSICAL, " + "permission: 0, secure: false, vpnType: PLATFORM}) " + "-> ServiceSpecificException(17, \"File exists\")", + "networkCreate.*65123.*17"}); + + EXPECT_TRUE(mNetd->networkAddInterface(TEST_DUMP_NETID, sTun.name()).isOk()); + testData.push_back({StringPrintf("networkAddInterface(65123, %s)", sTun.name().c_str()), + StringPrintf("networkAddInterface.*65123.*%s", sTun.name().c_str())}); + + android::net::RouteInfoParcel parcel; + parcel.ifName = sTun.name(); + parcel.destination = "2001:db8:dead:beef::/64"; + parcel.nextHop = "fe80::dead:beef"; + parcel.mtu = 1234; + EXPECT_TRUE(mNetd->networkAddRouteParcel(TEST_DUMP_NETID, parcel).isOk()); + testData.push_back( + {StringPrintf("networkAddRouteParcel(65123, RouteInfoParcel{destination:" + " 2001:db8:dead:beef::/64, ifName: %s, nextHop: fe80::dead:beef," + " mtu: 1234})", + sTun.name().c_str()), + "networkAddRouteParcel.*65123.*dead:beef"}); + + EXPECT_TRUE(mNetd->networkDestroy(TEST_DUMP_NETID).isOk()); + testData.push_back({"networkDestroy(65123)", "networkDestroy.*65123"}); + + // Send the service dump request to netd. + std::vector<std::string> lines = dumpService(binder); + + // Basic regexp to match dump output lines. Matches the beginning and end of the line, and + // puts the output of the command itself into the first match group. + // Example: " 11-05 00:23:39.481 myCommand(args) <2.02ms>". + const std::basic_regex lineRegex( + "^ [0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}[.][0-9]{3} " + "(.*)" + " <[0-9]+[.][0-9]{2}ms>$"); + + // For each element of testdata, check that the expected output appears in the dump output. + // If not, fail the test and use hintRegex to print similar lines to assist in debugging. + for (const TestData& td : testData) { + const bool found = std::any_of(lines.begin(), lines.end(), [&](const std::string& line) { + std::smatch match; + if (!std::regex_match(line, match, lineRegex)) return false; + return (match.size() == 2) && (match[1].str() == td.output); + }); + EXPECT_TRUE(found) << "Didn't find line '" << td.output << "' in dumpsys output."; + if (found) continue; + std::cerr << "Similar lines" << std::endl; + for (const auto& line : lines) { + if (std::regex_search(line, std::basic_regex(td.hintRegex))) { + std::cerr << line << std::endl; + } + } + } +} + +TEST_F(NetdBinderTest, DeprecatedTetherOffloadRuleAdd) { + TetherOffloadRuleParcel emptyRule; + auto status = mNetd->tetherOffloadRuleAdd(emptyRule); + ASSERT_FALSE(status.isOk()); + ASSERT_EQ(status.exceptionCode(), binder::Status::EX_UNSUPPORTED_OPERATION); +} + +TEST_F(NetdBinderTest, DeprecatedTetherOffloadRuleRemove) { + TetherOffloadRuleParcel emptyRule; + auto status = mNetd->tetherOffloadRuleRemove(emptyRule); + ASSERT_FALSE(status.isOk()); + ASSERT_EQ(status.exceptionCode(), binder::Status::EX_UNSUPPORTED_OPERATION); +} + +TEST_F(NetdBinderTest, DeprecatedTetherOffloadGetStats) { + std::vector<TetherStatsParcel> tetherStatsList; + auto status = mNetd->tetherOffloadGetStats(&tetherStatsList); + ASSERT_FALSE(status.isOk()); + ASSERT_EQ(status.exceptionCode(), binder::Status::EX_UNSUPPORTED_OPERATION); +} + +TEST_F(NetdBinderTest, DeprecatedTetherOffloadSetInterfaceQuota) { + auto status = mNetd->tetherOffloadSetInterfaceQuota(0 /* ifIndex */, 0 /* quotaBytes */); + ASSERT_FALSE(status.isOk()); + ASSERT_EQ(status.exceptionCode(), binder::Status::EX_UNSUPPORTED_OPERATION); +} + +TEST_F(NetdBinderTest, DeprecatedTetherOffloadGetAndClearStats) { + TetherStatsParcel tetherStats; + auto status = mNetd->tetherOffloadGetAndClearStats(0 /* ifIndex */, &tetherStats); + ASSERT_FALSE(status.isOk()); + ASSERT_EQ(status.exceptionCode(), binder::Status::EX_UNSUPPORTED_OPERATION); +} + +namespace { + +// aliases for better reading +#define SYSTEM_DEFAULT_NETID TEST_NETID1 +#define APP_DEFAULT_NETID TEST_NETID2 +#define VPN_NETID TEST_NETID3 + +void verifyAppUidRules(std::vector<bool>&& expectedResults, std::vector<UidRangeParcel>& uidRanges, + const std::string& iface, uint32_t subPriority) { + ASSERT_EQ(expectedResults.size(), uidRanges.size()); + if (iface.size()) { + std::string action = StringPrintf("lookup %s ", iface.c_str()); + for (unsigned long i = 0; i < uidRanges.size(); i++) { + EXPECT_EQ(expectedResults[i], + ipRuleExistsForRange(RULE_PRIORITY_UID_EXPLICIT_NETWORK + subPriority, + uidRanges[i], action)); + EXPECT_EQ(expectedResults[i], + ipRuleExistsForRange(RULE_PRIORITY_UID_IMPLICIT_NETWORK + subPriority, + uidRanges[i], action)); + EXPECT_EQ(expectedResults[i], + ipRuleExistsForRange(RULE_PRIORITY_UID_DEFAULT_NETWORK + subPriority, + uidRanges[i], action)); + } + } else { + std::string action = "unreachable"; + for (unsigned long i = 0; i < uidRanges.size(); i++) { + EXPECT_EQ(expectedResults[i], + ipRuleExistsForRange(RULE_PRIORITY_UID_EXPLICIT_NETWORK + subPriority, + uidRanges[i], action)); + EXPECT_EQ(expectedResults[i], + ipRuleExistsForRange(RULE_PRIORITY_UID_IMPLICIT_NETWORK + subPriority, + uidRanges[i], action)); + EXPECT_EQ(expectedResults[i], + ipRuleExistsForRange(RULE_PRIORITY_UID_DEFAULT_UNREACHABLE + subPriority, + uidRanges[i], action)); + } + } +} + +void verifyAppUidRules(std::vector<bool>&& expectedResults, NativeUidRangeConfig& uidRangeConfig, + const std::string& iface) { + verifyAppUidRules(move(expectedResults), uidRangeConfig.uidRanges, iface, + uidRangeConfig.subPriority); +} + +void verifyVpnUidRules(std::vector<bool>&& expectedResults, NativeUidRangeConfig& uidRangeConfig, + const std::string& iface, bool secure) { + ASSERT_EQ(expectedResults.size(), uidRangeConfig.uidRanges.size()); + std::string action = StringPrintf("lookup %s ", iface.c_str()); + + uint32_t priority; + if (secure) { + priority = RULE_PRIORITY_SECURE_VPN; + } else { + priority = RULE_PRIORITY_BYPASSABLE_VPN; + } + for (unsigned long i = 0; i < uidRangeConfig.uidRanges.size(); i++) { + EXPECT_EQ(expectedResults[i], ipRuleExistsForRange(priority + uidRangeConfig.subPriority, + uidRangeConfig.uidRanges[i], action)); + EXPECT_EQ(expectedResults[i], + ipRuleExistsForRange(RULE_PRIORITY_EXPLICIT_NETWORK + uidRangeConfig.subPriority, + uidRangeConfig.uidRanges[i], action)); + EXPECT_EQ(expectedResults[i], + ipRuleExistsForRange(RULE_PRIORITY_OUTPUT_INTERFACE + uidRangeConfig.subPriority, + uidRangeConfig.uidRanges[i], action, iface.c_str())); + } +} + +constexpr int SUB_PRIORITY_1 = UidRanges::DEFAULT_SUB_PRIORITY + 1; +constexpr int SUB_PRIORITY_2 = UidRanges::DEFAULT_SUB_PRIORITY + 2; + +constexpr int IMPLICITLY_SELECT = 0; +constexpr int EXPLICITLY_SELECT = 1; +constexpr int UNCONNECTED_SOCKET = 2; + +// 1. Send data with the specified UID, on a connected or unconnected socket. +// 2. Verify if data is received from the specified fd. The fd should belong to a TUN, which has +// been assigned to the test network. +// 3. Verify if fwmark of data is correct. +// Note: This is a helper function used by per-app default network tests. It does not implement full +// fwmark logic in netd, and it's currently sufficient. Extension may be required for more +// complicated tests. +void expectPacketSentOnNetId(uid_t uid, unsigned netId, int fd, int selectionMode) { + Fwmark fwmark; + const bool doConnect = (selectionMode != UNCONNECTED_SOCKET); + EXPECT_TRUE(sendIPv6PacketFromUid(uid, V6_ADDR, &fwmark, fd, doConnect)); + + Fwmark expected; + expected.netId = netId; + expected.explicitlySelected = (selectionMode == EXPLICITLY_SELECT); + if (uid == AID_ROOT && selectionMode == EXPLICITLY_SELECT) { + expected.protectedFromVpn = true; + } else { + expected.protectedFromVpn = false; + } + if (selectionMode == UNCONNECTED_SOCKET) { + expected.permission = PERMISSION_NONE; + } else { + expected.permission = (uid == AID_ROOT) ? PERMISSION_SYSTEM : PERMISSION_NONE; + } + + EXPECT_EQ(expected.intValue, fwmark.intValue); +} + +void expectUnreachableError(uid_t uid, unsigned netId, int selectionMode) { + Fwmark fwmark; + const bool doConnect = (selectionMode != UNCONNECTED_SOCKET); + EXPECT_TRUE(sendIPv6PacketFromUidFail(uid, V6_ADDR, &fwmark, doConnect, ENETUNREACH)); + + Fwmark expected; + expected.netId = netId; + expected.explicitlySelected = (selectionMode == EXPLICITLY_SELECT); + if (uid == AID_ROOT && selectionMode == EXPLICITLY_SELECT) { + expected.protectedFromVpn = true; + } else { + expected.protectedFromVpn = false; + } + if (selectionMode == UNCONNECTED_SOCKET) { + expected.permission = PERMISSION_NONE; + } else { + expected.permission = (uid == AID_ROOT) ? PERMISSION_SYSTEM : PERMISSION_NONE; + } + + EXPECT_EQ(expected.intValue, fwmark.intValue); +} + +} // namespace + +// Verify whether API reject overlapped UID ranges +TEST_F(NetdBinderTest, PerAppDefaultNetwork_OverlappedUidRanges) { + const auto& config = makeNativeNetworkConfig(APP_DEFAULT_NETID, NativeNetworkType::PHYSICAL, + INetd::PERMISSION_NONE, false); + EXPECT_TRUE(mNetd->networkCreate(config).isOk()); + EXPECT_TRUE(mNetd->networkAddInterface(APP_DEFAULT_NETID, sTun.name()).isOk()); + + std::vector<UidRangeParcel> uidRanges = {makeUidRangeParcel(BASE_UID + 1, BASE_UID + 1), + makeUidRangeParcel(BASE_UID + 10, BASE_UID + 12)}; + EXPECT_TRUE(mNetd->networkAddUidRanges(APP_DEFAULT_NETID, uidRanges).isOk()); + + binder::Status status; + status = mNetd->networkAddUidRanges(APP_DEFAULT_NETID, + {makeUidRangeParcel(BASE_UID + 1, BASE_UID + 1)}); + EXPECT_FALSE(status.isOk()); + EXPECT_EQ(EINVAL, status.serviceSpecificErrorCode()); + + status = mNetd->networkAddUidRanges(APP_DEFAULT_NETID, + {makeUidRangeParcel(BASE_UID + 9, BASE_UID + 10)}); + EXPECT_FALSE(status.isOk()); + EXPECT_EQ(EINVAL, status.serviceSpecificErrorCode()); + + status = mNetd->networkAddUidRanges(APP_DEFAULT_NETID, + {makeUidRangeParcel(BASE_UID + 11, BASE_UID + 11)}); + EXPECT_FALSE(status.isOk()); + EXPECT_EQ(EINVAL, status.serviceSpecificErrorCode()); + + status = mNetd->networkAddUidRanges(APP_DEFAULT_NETID, + {makeUidRangeParcel(BASE_UID + 12, BASE_UID + 13)}); + EXPECT_FALSE(status.isOk()); + EXPECT_EQ(EINVAL, status.serviceSpecificErrorCode()); + + status = mNetd->networkAddUidRanges(APP_DEFAULT_NETID, + {makeUidRangeParcel(BASE_UID + 9, BASE_UID + 13)}); + EXPECT_FALSE(status.isOk()); + EXPECT_EQ(EINVAL, status.serviceSpecificErrorCode()); + + std::vector<UidRangeParcel> selfOverlappedUidRanges = { + makeUidRangeParcel(BASE_UID + 20, BASE_UID + 20), + makeUidRangeParcel(BASE_UID + 20, BASE_UID + 21)}; + status = mNetd->networkAddUidRanges(APP_DEFAULT_NETID, selfOverlappedUidRanges); + EXPECT_FALSE(status.isOk()); + EXPECT_EQ(EINVAL, status.serviceSpecificErrorCode()); +} + +// Verify whether IP rules for app default network are correctly configured. +TEST_F(NetdBinderTest, PerAppDefaultNetwork_VerifyIpRules) { + const auto& config = makeNativeNetworkConfig(APP_DEFAULT_NETID, NativeNetworkType::PHYSICAL, + INetd::PERMISSION_NONE, false); + EXPECT_TRUE(mNetd->networkCreate(config).isOk()); + EXPECT_TRUE(mNetd->networkAddInterface(APP_DEFAULT_NETID, sTun.name()).isOk()); + + std::vector<UidRangeParcel> uidRanges = {makeUidRangeParcel(BASE_UID + 8005, BASE_UID + 8012), + makeUidRangeParcel(BASE_UID + 8090, BASE_UID + 8099)}; + + EXPECT_TRUE(mNetd->networkAddUidRanges(APP_DEFAULT_NETID, uidRanges).isOk()); + verifyAppUidRules({true, true} /*expectedResults*/, uidRanges, sTun.name(), + UidRanges::DEFAULT_SUB_PRIORITY); + EXPECT_TRUE(mNetd->networkRemoveUidRanges(APP_DEFAULT_NETID, {uidRanges.at(0)}).isOk()); + verifyAppUidRules({false, true} /*expectedResults*/, uidRanges, sTun.name(), + UidRanges::DEFAULT_SUB_PRIORITY); + EXPECT_TRUE(mNetd->networkRemoveUidRanges(APP_DEFAULT_NETID, {uidRanges.at(1)}).isOk()); + verifyAppUidRules({false, false} /*expectedResults*/, uidRanges, sTun.name(), + UidRanges::DEFAULT_SUB_PRIORITY); + + EXPECT_TRUE(mNetd->networkAddUidRanges(INetd::UNREACHABLE_NET_ID, uidRanges).isOk()); + verifyAppUidRules({true, true} /*expectedResults*/, uidRanges, "", + UidRanges::DEFAULT_SUB_PRIORITY); + EXPECT_TRUE(mNetd->networkRemoveUidRanges(INetd::UNREACHABLE_NET_ID, {uidRanges.at(0)}).isOk()); + verifyAppUidRules({false, true} /*expectedResults*/, uidRanges, "", + UidRanges::DEFAULT_SUB_PRIORITY); + EXPECT_TRUE(mNetd->networkRemoveUidRanges(INetd::UNREACHABLE_NET_ID, {uidRanges.at(1)}).isOk()); + verifyAppUidRules({false, false} /*expectedResults*/, uidRanges, "", + UidRanges::DEFAULT_SUB_PRIORITY); +} + +// Verify whether packets go through the right network with and without per-app default network. +// Meaning of Fwmark bits (from Fwmark.h): +// 0x0000ffff - Network ID +// 0x00010000 - Explicit mark bit +// 0x00020000 - VPN protect bit +// 0x000c0000 - Permission bits +TEST_F(NetdBinderTest, PerAppDefaultNetwork_ImplicitlySelectNetwork) { + createDefaultAndOtherPhysicalNetwork(SYSTEM_DEFAULT_NETID, APP_DEFAULT_NETID); + + int systemDefaultFd = sTun.getFdForTesting(); + int appDefaultFd = sTun2.getFdForTesting(); + + // Connections go through the system default network. + expectPacketSentOnNetId(AID_ROOT, SYSTEM_DEFAULT_NETID, systemDefaultFd, IMPLICITLY_SELECT); + expectPacketSentOnNetId(TEST_UID1, SYSTEM_DEFAULT_NETID, systemDefaultFd, IMPLICITLY_SELECT); + + // Add TEST_UID1 to per-app default network. + EXPECT_TRUE(mNetd->networkAddUidRanges(APP_DEFAULT_NETID, + {makeUidRangeParcel(TEST_UID1, TEST_UID1)}) + .isOk()); + expectPacketSentOnNetId(AID_ROOT, SYSTEM_DEFAULT_NETID, systemDefaultFd, IMPLICITLY_SELECT); + expectPacketSentOnNetId(TEST_UID1, APP_DEFAULT_NETID, appDefaultFd, IMPLICITLY_SELECT); + + // Remove TEST_UID1 from per-app default network. + EXPECT_TRUE(mNetd->networkRemoveUidRanges(APP_DEFAULT_NETID, + {makeUidRangeParcel(TEST_UID1, TEST_UID1)}) + .isOk()); + expectPacketSentOnNetId(AID_ROOT, SYSTEM_DEFAULT_NETID, systemDefaultFd, IMPLICITLY_SELECT); + expectPacketSentOnNetId(TEST_UID1, SYSTEM_DEFAULT_NETID, systemDefaultFd, IMPLICITLY_SELECT); + + // Prohibit TEST_UID1 from using the default network. + EXPECT_TRUE(mNetd->networkAddUidRanges(INetd::UNREACHABLE_NET_ID, + {makeUidRangeParcel(TEST_UID1, TEST_UID1)}) + .isOk()); + expectPacketSentOnNetId(AID_ROOT, SYSTEM_DEFAULT_NETID, systemDefaultFd, IMPLICITLY_SELECT); + expectUnreachableError(TEST_UID1, INetd::UNREACHABLE_NET_ID, IMPLICITLY_SELECT); + + // restore IP rules + EXPECT_TRUE(mNetd->networkRemoveUidRanges(INetd::UNREACHABLE_NET_ID, + {makeUidRangeParcel(TEST_UID1, TEST_UID1)}) + .isOk()); +} + +// Verify whether packets go through the right network when app explicitly selects a network. +TEST_F(NetdBinderTest, PerAppDefaultNetwork_ExplicitlySelectNetwork) { + createDefaultAndOtherPhysicalNetwork(SYSTEM_DEFAULT_NETID, APP_DEFAULT_NETID); + + int systemDefaultFd = sTun.getFdForTesting(); + int appDefaultFd = sTun2.getFdForTesting(); + + // Explicitly select the system default network. + setNetworkForProcess(SYSTEM_DEFAULT_NETID); + // Connections go through the system default network. + expectPacketSentOnNetId(AID_ROOT, SYSTEM_DEFAULT_NETID, systemDefaultFd, EXPLICITLY_SELECT); + expectPacketSentOnNetId(TEST_UID1, SYSTEM_DEFAULT_NETID, systemDefaultFd, EXPLICITLY_SELECT); + + // Set TEST_UID1 to default unreachable, which won't affect the explicitly selected network. + // Connections go through the system default network. + EXPECT_TRUE(mNetd->networkAddUidRanges(INetd::UNREACHABLE_NET_ID, + {makeUidRangeParcel(TEST_UID1, TEST_UID1)}) + .isOk()); + expectPacketSentOnNetId(AID_ROOT, SYSTEM_DEFAULT_NETID, systemDefaultFd, EXPLICITLY_SELECT); + expectPacketSentOnNetId(TEST_UID1, SYSTEM_DEFAULT_NETID, systemDefaultFd, EXPLICITLY_SELECT); + + // restore IP rules + EXPECT_TRUE(mNetd->networkRemoveUidRanges(INetd::UNREACHABLE_NET_ID, + {makeUidRangeParcel(TEST_UID1, TEST_UID1)}) + .isOk()); + + // Add TEST_UID1 to per-app default network, which won't affect the explicitly selected network. + EXPECT_TRUE(mNetd->networkAddUidRanges(APP_DEFAULT_NETID, + {makeUidRangeParcel(TEST_UID1, TEST_UID1)}) + .isOk()); + expectPacketSentOnNetId(AID_ROOT, SYSTEM_DEFAULT_NETID, systemDefaultFd, EXPLICITLY_SELECT); + expectPacketSentOnNetId(TEST_UID1, SYSTEM_DEFAULT_NETID, systemDefaultFd, EXPLICITLY_SELECT); + + // Explicitly select the per-app default network. + setNetworkForProcess(APP_DEFAULT_NETID); + // Connections go through the per-app default network. + expectPacketSentOnNetId(AID_ROOT, APP_DEFAULT_NETID, appDefaultFd, EXPLICITLY_SELECT); + expectPacketSentOnNetId(TEST_UID1, APP_DEFAULT_NETID, appDefaultFd, EXPLICITLY_SELECT); +} + +// Verify whether packets go through the right network if app does not implicitly or explicitly +// select any network. +TEST_F(NetdBinderTest, PerAppDefaultNetwork_UnconnectedSocket) { + createDefaultAndOtherPhysicalNetwork(SYSTEM_DEFAULT_NETID, APP_DEFAULT_NETID); + + int systemDefaultFd = sTun.getFdForTesting(); + int appDefaultFd = sTun2.getFdForTesting(); + + // Connections go through the system default network. + expectPacketSentOnNetId(AID_ROOT, NETID_UNSET, systemDefaultFd, UNCONNECTED_SOCKET); + expectPacketSentOnNetId(TEST_UID1, NETID_UNSET, systemDefaultFd, UNCONNECTED_SOCKET); + + // Add TEST_UID1 to per-app default network. Traffic should go through the per-app default + // network if UID is in range. Otherwise, go through the system default network. + EXPECT_TRUE(mNetd->networkAddUidRanges(APP_DEFAULT_NETID, + {makeUidRangeParcel(TEST_UID1, TEST_UID1)}) + .isOk()); + expectPacketSentOnNetId(AID_ROOT, NETID_UNSET, systemDefaultFd, UNCONNECTED_SOCKET); + expectPacketSentOnNetId(TEST_UID1, NETID_UNSET, appDefaultFd, UNCONNECTED_SOCKET); + + // Set TEST_UID1's default network to unreachable. Its traffic should still go through the + // per-app default network. Other traffic go through the system default network. + // PS: per-app default network take precedence over unreachable network. This should happens + // only in the transition period when both rules are briefly set. + EXPECT_TRUE(mNetd->networkAddUidRanges(INetd::UNREACHABLE_NET_ID, + {makeUidRangeParcel(TEST_UID1, TEST_UID1)}) + .isOk()); + expectPacketSentOnNetId(AID_ROOT, NETID_UNSET, systemDefaultFd, UNCONNECTED_SOCKET); + expectPacketSentOnNetId(TEST_UID1, NETID_UNSET, appDefaultFd, UNCONNECTED_SOCKET); + + // Remove TEST_UID1's default network from OEM-paid network. Its traffic should get ENETUNREACH + // error. Other traffic still go through the system default network. + EXPECT_TRUE(mNetd->networkRemoveUidRanges(APP_DEFAULT_NETID, + {makeUidRangeParcel(TEST_UID1, TEST_UID1)}) + .isOk()); + expectPacketSentOnNetId(AID_ROOT, NETID_UNSET, systemDefaultFd, UNCONNECTED_SOCKET); + expectUnreachableError(TEST_UID1, NETID_UNSET, UNCONNECTED_SOCKET); + + // restore IP rules + EXPECT_TRUE(mNetd->networkRemoveUidRanges(INetd::UNREACHABLE_NET_ID, + {makeUidRangeParcel(TEST_UID1, TEST_UID1)}) + .isOk()); +} + +TEST_F(NetdBinderTest, PerAppDefaultNetwork_PermissionCheck) { + createPhysicalNetwork(APP_DEFAULT_NETID, sTun2.name(), INetd::PERMISSION_SYSTEM); + + { // uid is not in app range. Can not set network for process. + ScopedUidChange scopedUidChange(TEST_UID1); + EXPECT_EQ(-EACCES, setNetworkForProcess(APP_DEFAULT_NETID)); + } + + EXPECT_TRUE(mNetd->networkAddUidRanges(APP_DEFAULT_NETID, + {makeUidRangeParcel(TEST_UID1, TEST_UID1)}) + .isOk()); + + { // uid is in app range. Can set network for process. + ScopedUidChange scopedUidChange(TEST_UID1); + EXPECT_EQ(0, setNetworkForProcess(APP_DEFAULT_NETID)); + } +} + +class VpnParameterizedTest : public NetdBinderTest, public testing::WithParamInterface<bool> {}; + +// Exercise secure and bypassable VPN. +INSTANTIATE_TEST_SUITE_P(PerAppDefaultNetwork, VpnParameterizedTest, testing::Bool(), + [](const testing::TestParamInfo<bool>& info) { + return info.param ? "SecureVPN" : "BypassableVPN"; + }); + +// Verify per-app default network + VPN. +TEST_P(VpnParameterizedTest, ImplicitlySelectNetwork) { + const bool isSecureVPN = GetParam(); + createVpnAndAppDefaultNetworkWithUid( + SYSTEM_DEFAULT_NETID, APP_DEFAULT_NETID, VPN_NETID, isSecureVPN, + {makeUidRangeParcel(TEST_UID2, TEST_UID1)} /* app range */, + {makeUidRangeParcel(TEST_UID3, TEST_UID2)} /* VPN range */); + + int systemDefaultFd = sTun.getFdForTesting(); + int appDefaultFd = sTun2.getFdForTesting(); + int vpnFd = sTun3.getFdForTesting(); + + // uid is neither in app range, nor in VPN range. Traffic goes through system default network. + expectPacketSentOnNetId(AID_ROOT, SYSTEM_DEFAULT_NETID, systemDefaultFd, IMPLICITLY_SELECT); + // uid is in VPN range, not in app range. Traffic goes through VPN. + expectPacketSentOnNetId(TEST_UID3, (isSecureVPN ? SYSTEM_DEFAULT_NETID : VPN_NETID), vpnFd, + IMPLICITLY_SELECT); + // uid is in app range, not in VPN range. Traffic goes through per-app default network. + expectPacketSentOnNetId(TEST_UID1, APP_DEFAULT_NETID, appDefaultFd, IMPLICITLY_SELECT); + // uid is in both app and VPN range. Traffic goes through VPN. + expectPacketSentOnNetId(TEST_UID2, (isSecureVPN ? APP_DEFAULT_NETID : VPN_NETID), vpnFd, + IMPLICITLY_SELECT); +} + +class VpnAndSelectNetworkParameterizedTest + : public NetdBinderTest, + public testing::WithParamInterface<std::tuple<bool, int>> {}; + +// Exercise the combination of different VPN types and different user selected networks. e.g. +// secure VPN + select on system default network +// secure VPN + select on app default network +// secure VPN + select on VPN +// bypassable VPN + select on system default network +// ... +INSTANTIATE_TEST_SUITE_P(PerAppDefaultNetwork, VpnAndSelectNetworkParameterizedTest, + testing::Combine(testing::Bool(), + testing::Values(SYSTEM_DEFAULT_NETID, APP_DEFAULT_NETID, + VPN_NETID)), + [](const testing::TestParamInfo<std::tuple<bool, int>>& info) { + const std::string vpnType = std::get<0>(info.param) + ? std::string("SecureVPN") + : std::string("BypassableVPN"); + std::string selectedNetwork; + switch (std::get<1>(info.param)) { + case SYSTEM_DEFAULT_NETID: + selectedNetwork = "SystemDefaultNetwork"; + break; + case APP_DEFAULT_NETID: + selectedNetwork = "AppDefaultNetwork"; + break; + case VPN_NETID: + selectedNetwork = "VPN"; + break; + default: + selectedNetwork = "InvalidParameter"; // Should not happen. + } + return vpnType + "_select" + selectedNetwork; + }); + +TEST_P(VpnAndSelectNetworkParameterizedTest, ExplicitlySelectNetwork) { + bool isSecureVPN; + int selectedNetId; + std::tie(isSecureVPN, selectedNetId) = GetParam(); + createVpnAndAppDefaultNetworkWithUid( + SYSTEM_DEFAULT_NETID, APP_DEFAULT_NETID, VPN_NETID, isSecureVPN, + {makeUidRangeParcel(TEST_UID2, TEST_UID1)} /* app range */, + {makeUidRangeParcel(TEST_UID3, TEST_UID2)} /* VPN range */); + + int expectedFd = -1; + switch (selectedNetId) { + case SYSTEM_DEFAULT_NETID: + expectedFd = sTun.getFdForTesting(); + break; + case APP_DEFAULT_NETID: + expectedFd = sTun2.getFdForTesting(); + break; + case VPN_NETID: + expectedFd = sTun3.getFdForTesting(); + break; + default: + GTEST_LOG_(ERROR) << "unexpected netId:" << selectedNetId; // Should not happen. + } + + // In all following permutations, Traffic should go through the specified network if a process + // can select network for itself. The fwmark should contain process UID and the explicit select + // bit. + { // uid is neither in app range, nor in VPN range. Permission bits, protect bit, and explicit + // select bit are all set because of AID_ROOT. + ScopedUidChange scopedUidChange(AID_ROOT); + EXPECT_EQ(0, setNetworkForProcess(selectedNetId)); + expectPacketSentOnNetId(AID_ROOT, selectedNetId, expectedFd, EXPLICITLY_SELECT); + } + { // uid is in VPN range, not in app range. + ScopedUidChange scopedUidChange(TEST_UID3); + // Cannot select non-VPN networks when uid is subject to secure VPN. + if (isSecureVPN && selectedNetId != VPN_NETID) { + EXPECT_EQ(-EPERM, setNetworkForProcess(selectedNetId)); + } else { + EXPECT_EQ(0, setNetworkForProcess(selectedNetId)); + expectPacketSentOnNetId(TEST_UID3, selectedNetId, expectedFd, EXPLICITLY_SELECT); + } + } + { // uid is in app range, not in VPN range. + ScopedUidChange scopedUidChange(TEST_UID1); + // Cannot select the VPN because the VPN does not applies to the UID. + if (selectedNetId == VPN_NETID) { + EXPECT_EQ(-EPERM, setNetworkForProcess(selectedNetId)); + } else { + EXPECT_EQ(0, setNetworkForProcess(selectedNetId)); + expectPacketSentOnNetId(TEST_UID1, selectedNetId, expectedFd, EXPLICITLY_SELECT); + } + } + { // uid is in both app range and VPN range. + ScopedUidChange scopedUidChange(TEST_UID2); + // Cannot select non-VPN networks when uid is subject to secure VPN. + if (isSecureVPN && selectedNetId != VPN_NETID) { + EXPECT_EQ(-EPERM, setNetworkForProcess(selectedNetId)); + } else { + EXPECT_EQ(0, setNetworkForProcess(selectedNetId)); + expectPacketSentOnNetId(TEST_UID2, selectedNetId, expectedFd, EXPLICITLY_SELECT); + } + } +} + +TEST_P(VpnParameterizedTest, UnconnectedSocket) { + const bool isSecureVPN = GetParam(); + createVpnAndAppDefaultNetworkWithUid( + SYSTEM_DEFAULT_NETID, APP_DEFAULT_NETID, VPN_NETID, isSecureVPN, + {makeUidRangeParcel(TEST_UID2, TEST_UID1)} /* app range */, + {makeUidRangeParcel(TEST_UID3, TEST_UID2)} /* VPN range */); + + int systemDefaultFd = sTun.getFdForTesting(); + int appDefaultFd = sTun2.getFdForTesting(); + int vpnFd = sTun3.getFdForTesting(); + + // uid is neither in app range, nor in VPN range. Traffic goes through system default network. + expectPacketSentOnNetId(AID_ROOT, NETID_UNSET, systemDefaultFd, UNCONNECTED_SOCKET); + // uid is in VPN range, not in app range. Traffic goes through VPN. + expectPacketSentOnNetId(TEST_UID3, NETID_UNSET, vpnFd, UNCONNECTED_SOCKET); + // uid is in app range, not in VPN range. Traffic goes through per-app default network. + expectPacketSentOnNetId(TEST_UID1, NETID_UNSET, appDefaultFd, UNCONNECTED_SOCKET); + // uid is in both app and VPN range. Traffic goes through VPN. + expectPacketSentOnNetId(TEST_UID2, NETID_UNSET, vpnFd, UNCONNECTED_SOCKET); +} + +TEST_F(NetdBinderTest, NetworkCreate) { + auto config = makeNativeNetworkConfig(TEST_NETID1, NativeNetworkType::PHYSICAL, + INetd::PERMISSION_NONE, false); + EXPECT_TRUE(mNetd->networkCreate(config).isOk()); + EXPECT_TRUE(mNetd->networkDestroy(config.netId).isOk()); + + config.networkType = NativeNetworkType::VIRTUAL; + config.secure = true; + config.vpnType = NativeVpnType::OEM; + EXPECT_TRUE(mNetd->networkCreate(config).isOk()); + + // invalid network type + auto wrongConfig = makeNativeNetworkConfig(TEST_NETID2, static_cast<NativeNetworkType>(-1), + INetd::PERMISSION_NONE, false); + EXPECT_EQ(EINVAL, mNetd->networkCreate(wrongConfig).serviceSpecificErrorCode()); + + // invalid VPN type + wrongConfig.networkType = NativeNetworkType::VIRTUAL; + wrongConfig.vpnType = static_cast<NativeVpnType>(-1); + EXPECT_EQ(EINVAL, mNetd->networkCreate(wrongConfig).serviceSpecificErrorCode()); +} + +// Verifies valid and invalid inputs on networkAddUidRangesParcel method. +TEST_F(NetdBinderTest, UidRangeSubPriority_ValidateInputs) { + createVpnAndOtherPhysicalNetwork(SYSTEM_DEFAULT_NETID, APP_DEFAULT_NETID, VPN_NETID, + /*isSecureVPN=*/true); + // Invalid priority -1 on a physical network. + NativeUidRangeConfig uidRangeConfig = + makeNativeUidRangeConfig(APP_DEFAULT_NETID, {makeUidRangeParcel(BASE_UID, BASE_UID)}, + UidRanges::DEFAULT_SUB_PRIORITY - 1); + binder::Status status = mNetd->networkAddUidRangesParcel(uidRangeConfig); + EXPECT_FALSE(status.isOk()); + EXPECT_EQ(EINVAL, status.serviceSpecificErrorCode()); + + // Invalid priority 1000 on a physical network. + uidRangeConfig.subPriority = UidRanges::LOWEST_SUB_PRIORITY + 1; + status = mNetd->networkAddUidRangesParcel(uidRangeConfig); + EXPECT_FALSE(status.isOk()); + EXPECT_EQ(EINVAL, status.serviceSpecificErrorCode()); + + // Virtual networks support only default priority. + uidRangeConfig.netId = VPN_NETID; + uidRangeConfig.subPriority = SUB_PRIORITY_1; + status = mNetd->networkAddUidRangesParcel(uidRangeConfig); + EXPECT_FALSE(status.isOk()); + EXPECT_EQ(EINVAL, status.serviceSpecificErrorCode()); + + // For a single network, identical UID ranges with different priorities are allowed. + uidRangeConfig.netId = APP_DEFAULT_NETID; + uidRangeConfig.subPriority = SUB_PRIORITY_1; + EXPECT_TRUE(mNetd->networkAddUidRangesParcel(uidRangeConfig).isOk()); + uidRangeConfig.subPriority = SUB_PRIORITY_2; + EXPECT_TRUE(mNetd->networkAddUidRangesParcel(uidRangeConfig).isOk()); + + // For a single network, identical UID ranges with the same priority is invalid. + status = mNetd->networkAddUidRangesParcel(uidRangeConfig); + EXPECT_FALSE(status.isOk()); + EXPECT_EQ(EINVAL, status.serviceSpecificErrorCode()); + + // Overlapping ranges is invalid. + uidRangeConfig.uidRanges = {makeUidRangeParcel(BASE_UID + 1, BASE_UID + 1), + makeUidRangeParcel(BASE_UID + 1, BASE_UID + 1)}; + status = mNetd->networkAddUidRangesParcel(uidRangeConfig); + EXPECT_FALSE(status.isOk()); + EXPECT_EQ(EINVAL, status.serviceSpecificErrorCode()); +} + +// Examines whether IP rules for app default network with subsidiary priorities are correctly added +// and removed. +TEST_F(NetdBinderTest, UidRangeSubPriority_VerifyPhysicalNwIpRules) { + createPhysicalNetwork(TEST_NETID1, sTun.name()); + EXPECT_TRUE(mNetd->networkAddRoute(TEST_NETID1, sTun.name(), "::/0", "").isOk()); + createPhysicalNetwork(TEST_NETID2, sTun2.name()); + EXPECT_TRUE(mNetd->networkAddRoute(TEST_NETID2, sTun2.name(), "::/0", "").isOk()); + + // Adds priority 1 setting + NativeUidRangeConfig uidRangeConfig1 = makeNativeUidRangeConfig( + TEST_NETID1, {makeUidRangeParcel(BASE_UID, BASE_UID)}, SUB_PRIORITY_1); + EXPECT_TRUE(mNetd->networkAddUidRangesParcel(uidRangeConfig1).isOk()); + verifyAppUidRules({true}, uidRangeConfig1, sTun.name()); + // Adds priority 2 setting + NativeUidRangeConfig uidRangeConfig2 = makeNativeUidRangeConfig( + TEST_NETID2, {makeUidRangeParcel(BASE_UID + 1, BASE_UID + 1)}, SUB_PRIORITY_2); + EXPECT_TRUE(mNetd->networkAddUidRangesParcel(uidRangeConfig2).isOk()); + verifyAppUidRules({true}, uidRangeConfig2, sTun2.name()); + // Adds another priority 2 setting + NativeUidRangeConfig uidRangeConfig3 = makeNativeUidRangeConfig( + INetd::UNREACHABLE_NET_ID, {makeUidRangeParcel(BASE_UID + 2, BASE_UID + 2)}, + SUB_PRIORITY_2); + EXPECT_TRUE(mNetd->networkAddUidRangesParcel(uidRangeConfig3).isOk()); + verifyAppUidRules({true}, uidRangeConfig3, ""); + + // Removes. + EXPECT_TRUE(mNetd->networkRemoveUidRangesParcel(uidRangeConfig1).isOk()); + verifyAppUidRules({false}, uidRangeConfig1, sTun.name()); + verifyAppUidRules({true}, uidRangeConfig2, sTun2.name()); + verifyAppUidRules({true}, uidRangeConfig3, ""); + EXPECT_TRUE(mNetd->networkRemoveUidRangesParcel(uidRangeConfig2).isOk()); + verifyAppUidRules({false}, uidRangeConfig1, sTun.name()); + verifyAppUidRules({false}, uidRangeConfig2, sTun2.name()); + verifyAppUidRules({true}, uidRangeConfig3, ""); + EXPECT_TRUE(mNetd->networkRemoveUidRangesParcel(uidRangeConfig3).isOk()); + verifyAppUidRules({false}, uidRangeConfig1, sTun.name()); + verifyAppUidRules({false}, uidRangeConfig2, sTun2.name()); + verifyAppUidRules({false}, uidRangeConfig3, ""); +} + +// Verify uid range rules on virtual network. +TEST_P(VpnParameterizedTest, UidRangeSubPriority_VerifyVpnIpRules) { + const bool isSecureVPN = GetParam(); + constexpr int VPN_NETID2 = TEST_NETID2; + + // Create 2 VPNs, using sTun and sTun2. + auto config = makeNativeNetworkConfig(VPN_NETID, NativeNetworkType::VIRTUAL, + INetd::PERMISSION_NONE, isSecureVPN); + EXPECT_TRUE(mNetd->networkCreate(config).isOk()); + EXPECT_TRUE(mNetd->networkAddInterface(VPN_NETID, sTun.name()).isOk()); + + config = makeNativeNetworkConfig(VPN_NETID2, NativeNetworkType::VIRTUAL, INetd::PERMISSION_NONE, + isSecureVPN); + EXPECT_TRUE(mNetd->networkCreate(config).isOk()); + EXPECT_TRUE(mNetd->networkAddInterface(VPN_NETID2, sTun2.name()).isOk()); + + // Assign uid ranges to different VPNs. Check if rules match. + NativeUidRangeConfig uidRangeConfig1 = makeNativeUidRangeConfig( + VPN_NETID, {makeUidRangeParcel(BASE_UID, BASE_UID)}, UidRanges::DEFAULT_SUB_PRIORITY); + EXPECT_TRUE(mNetd->networkAddUidRangesParcel(uidRangeConfig1).isOk()); + verifyVpnUidRules({true}, uidRangeConfig1, sTun.name(), isSecureVPN); + + NativeUidRangeConfig uidRangeConfig2 = + makeNativeUidRangeConfig(VPN_NETID2, {makeUidRangeParcel(BASE_UID + 1, BASE_UID + 1)}, + UidRanges::DEFAULT_SUB_PRIORITY); + EXPECT_TRUE(mNetd->networkAddUidRangesParcel(uidRangeConfig2).isOk()); + verifyVpnUidRules({true}, uidRangeConfig2, sTun2.name(), isSecureVPN); + + // Remove uid configs one-by-one. Check if rules match. + EXPECT_TRUE(mNetd->networkRemoveUidRangesParcel(uidRangeConfig1).isOk()); + verifyVpnUidRules({false}, uidRangeConfig1, sTun.name(), isSecureVPN); + verifyVpnUidRules({true}, uidRangeConfig2, sTun2.name(), isSecureVPN); + EXPECT_TRUE(mNetd->networkRemoveUidRangesParcel(uidRangeConfig2).isOk()); + verifyVpnUidRules({false}, uidRangeConfig1, sTun.name(), isSecureVPN); + verifyVpnUidRules({false}, uidRangeConfig2, sTun2.name(), isSecureVPN); +} + +// Verify if packets go through the right network when subsidiary priority and VPN works together. +// +// Test config: +// +----------+------------------------+-------------------------------------------+ +// | Priority | UID | Assigned Network | +// +----------+------------------------+-------------------------------------------+ +// | 0 | TEST_UID1 | VPN bypassable (VPN_NETID) | +// +----------+------------------------+-------------------------------------------+ +// | 1 | TEST_UID1, TEST_UID2, | Physical Network 1 (APP_DEFAULT_1_NETID) | +// | 1 | TEST_UID3 | Physical Network 2 (APP_DEFAULT_2_NETID) | +// | 1 | TEST_UID5 | Unreachable Network (UNREACHABLE_NET_ID) | +// +----------+------------------------+-------------------------------------------+ +// | 2 | TEST_UID3 | Physical Network 1 (APP_DEFAULT_1_NETID) | +// | 2 | TEST_UID4, TEST_UID5 | Physical Network 2 (APP_DEFAULT_2_NETID) | +// +----------+------------------------+-------------------------------------------+ +// +// Expected results: +// +-----------+------------------------+ +// | UID | Using Network | +// +-----------+------------------------+ +// | TEST_UID1 | VPN | +// | TEST_UID2 | Physical Network 1 | +// | TEST_UID3 | Physical Network 2 | +// | TEST_UID4 | Physical Network 2 | +// | TEST_UID5 | Unreachable Network | +// | TEST_UID6 | System Default Network | +// +-----------+------------------------+ +// +// SYSTEM_DEFAULT_NETID uses sTun. +// APP_DEFAULT_1_NETID uses sTun2. +// VPN_NETID uses sTun3. +// APP_DEFAULT_2_NETID uses sTun4. +// +TEST_F(NetdBinderTest, UidRangeSubPriority_ImplicitlySelectNetwork) { + constexpr int APP_DEFAULT_1_NETID = TEST_NETID2; + constexpr int APP_DEFAULT_2_NETID = TEST_NETID4; + + // Creates 4 networks. + createVpnAndOtherPhysicalNetwork(SYSTEM_DEFAULT_NETID, APP_DEFAULT_1_NETID, VPN_NETID, + /*isSecureVPN=*/false); + createPhysicalNetwork(APP_DEFAULT_2_NETID, sTun4.name()); + EXPECT_TRUE(mNetd->networkAddRoute(APP_DEFAULT_2_NETID, sTun4.name(), "::/0", "").isOk()); + + // Adds VPN setting. + NativeUidRangeConfig uidRangeConfigVpn = makeNativeUidRangeConfig( + VPN_NETID, {makeUidRangeParcel(TEST_UID1, TEST_UID1)}, UidRanges::DEFAULT_SUB_PRIORITY); + EXPECT_TRUE(mNetd->networkAddUidRangesParcel(uidRangeConfigVpn).isOk()); + + // Adds uidRangeConfig1 setting. + NativeUidRangeConfig uidRangeConfig1 = makeNativeUidRangeConfig( + APP_DEFAULT_1_NETID, + {makeUidRangeParcel(TEST_UID1, TEST_UID1), makeUidRangeParcel(TEST_UID2, TEST_UID2)}, + SUB_PRIORITY_1); + EXPECT_TRUE(mNetd->networkAddUidRangesParcel(uidRangeConfig1).isOk()); + uidRangeConfig1.netId = APP_DEFAULT_2_NETID; + uidRangeConfig1.uidRanges = {makeUidRangeParcel(TEST_UID3, TEST_UID3)}; + EXPECT_TRUE(mNetd->networkAddUidRangesParcel(uidRangeConfig1).isOk()); + uidRangeConfig1.netId = INetd::UNREACHABLE_NET_ID; + uidRangeConfig1.uidRanges = {makeUidRangeParcel(TEST_UID5, TEST_UID5)}; + EXPECT_TRUE(mNetd->networkAddUidRangesParcel(uidRangeConfig1).isOk()); + + // Adds uidRangeConfig2 setting. + NativeUidRangeConfig uidRangeConfig2 = makeNativeUidRangeConfig( + APP_DEFAULT_1_NETID, {makeUidRangeParcel(TEST_UID3, TEST_UID3)}, SUB_PRIORITY_2); + EXPECT_TRUE(mNetd->networkAddUidRangesParcel(uidRangeConfig2).isOk()); + uidRangeConfig2.netId = APP_DEFAULT_2_NETID; + uidRangeConfig2.uidRanges = {makeUidRangeParcel(TEST_UID4, TEST_UID4), + makeUidRangeParcel(TEST_UID5, TEST_UID5)}; + EXPECT_TRUE(mNetd->networkAddUidRangesParcel(uidRangeConfig2).isOk()); + + int systemDefaultFd = sTun.getFdForTesting(); + int appDefault_1_Fd = sTun2.getFdForTesting(); + int vpnFd = sTun3.getFdForTesting(); + int appDefault_2_Fd = sTun4.getFdForTesting(); + // Verify routings. + expectPacketSentOnNetId(TEST_UID1, VPN_NETID, vpnFd, IMPLICITLY_SELECT); + expectPacketSentOnNetId(TEST_UID2, APP_DEFAULT_1_NETID, appDefault_1_Fd, IMPLICITLY_SELECT); + expectPacketSentOnNetId(TEST_UID3, APP_DEFAULT_2_NETID, appDefault_2_Fd, IMPLICITLY_SELECT); + expectPacketSentOnNetId(TEST_UID4, APP_DEFAULT_2_NETID, appDefault_2_Fd, IMPLICITLY_SELECT); + expectUnreachableError(TEST_UID5, INetd::UNREACHABLE_NET_ID, IMPLICITLY_SELECT); + expectPacketSentOnNetId(TEST_UID6, SYSTEM_DEFAULT_NETID, systemDefaultFd, IMPLICITLY_SELECT); + + // Remove test rules from the unreachable network. + EXPECT_TRUE(mNetd->networkRemoveUidRangesParcel(uidRangeConfig1).isOk()); +}
\ No newline at end of file diff --git a/tests/bpf_base_test.cpp b/tests/bpf_base_test.cpp index f28c5f81..7ab42904 100644 --- a/tests/bpf_base_test.cpp +++ b/tests/bpf_base_test.cpp @@ -56,25 +56,13 @@ class BpfBasicTest : public testing::Test { }; TEST_F(BpfBasicTest, TestCgroupMounted) { - SKIP_IF_BPF_NOT_SUPPORTED; - std::string cg2_path; -#if 0 - // This is the correct way to fetch cg2_path, but it occasionally hits ASAN - // problems due to memory allocated in non ASAN code being freed later by us ASSERT_EQ(true, CgroupGetControllerPath(CGROUPV2_CONTROLLER_NAME, &cg2_path)); -#else - ASSERT_EQ(true, CgroupGetControllerPath(CGROUPV2_CONTROLLER_NAME, nullptr)); - // Constant derived from //system/core/libprocessgroup/profiles/cgroups.json - cg2_path = "/dev/cg2_bpf"; -#endif ASSERT_EQ(0, access(cg2_path.c_str(), R_OK)); ASSERT_EQ(0, access((cg2_path + "/cgroup.controllers").c_str(), R_OK)); } TEST_F(BpfBasicTest, TestTrafficControllerSetUp) { - SKIP_IF_BPF_NOT_SUPPORTED; - ASSERT_EQ(0, access(BPF_EGRESS_PROG_PATH, R_OK)); ASSERT_EQ(0, access(BPF_INGRESS_PROG_PATH, R_OK)); ASSERT_EQ(0, access(XT_BPF_INGRESS_PROG_PATH, R_OK)); @@ -97,8 +85,6 @@ TEST_F(BpfBasicTest, TestSocketFilterSetUp) { } TEST_F(BpfBasicTest, TestTagSocket) { - SKIP_IF_BPF_NOT_SUPPORTED; - BpfMap<uint64_t, UidTagValue> cookieTagMap(COOKIE_TAG_MAP_PATH); ASSERT_LE(0, cookieTagMap.getMap()); int sock = socket(AF_INET6, SOCK_STREAM | SOCK_CLOEXEC, 0); @@ -117,8 +103,6 @@ TEST_F(BpfBasicTest, TestTagSocket) { } TEST_F(BpfBasicTest, TestCloseSocketWithoutUntag) { - SKIP_IF_BPF_NOT_SUPPORTED; - BpfMap<uint64_t, UidTagValue> cookieTagMap(COOKIE_TAG_MAP_PATH); ASSERT_LE(0, cookieTagMap.getMap()); int sock = socket(AF_INET6, SOCK_STREAM | SOCK_CLOEXEC, 0); @@ -144,8 +128,6 @@ TEST_F(BpfBasicTest, TestCloseSocketWithoutUntag) { } TEST_F(BpfBasicTest, TestChangeCounterSet) { - SKIP_IF_BPF_NOT_SUPPORTED; - BpfMap<uint32_t, uint8_t> uidCounterSetMap(UID_COUNTERSET_MAP_PATH); ASSERT_LE(0, uidCounterSetMap.getMap()); ASSERT_EQ(0, qtaguid_setCounterSet(TEST_COUNTERSET, TEST_UID)); @@ -160,8 +142,6 @@ TEST_F(BpfBasicTest, TestChangeCounterSet) { } TEST_F(BpfBasicTest, TestDeleteTagData) { - SKIP_IF_BPF_NOT_SUPPORTED; - BpfMap<StatsKey, StatsValue> statsMapA(STATS_MAP_A_PATH); ASSERT_LE(0, statsMapA.getMap()); BpfMap<StatsKey, StatsValue> statsMapB(STATS_MAP_B_PATH); diff --git a/tests/netd_test.cpp b/tests/netd_test.cpp index dd25f969..fcd538e3 100644 --- a/tests/netd_test.cpp +++ b/tests/netd_test.cpp @@ -48,6 +48,22 @@ TEST(NetUtilsWrapperTest, TestFileCapabilities) { ASSERT_EQ(ENODATA, errno); } +// If this test fails most likely your device is lacking device/oem specific +// selinux genfscon rules, something like .../vendor/.../genfs_contexts: +// genfscon sysfs /devices/platform/.../net u:object_r:sysfs_net:s0 +// Easiest debugging is via: +// adb root && sleep 1 && adb shell 'ls -Z /sys/class/net/*/mtu' +// and look for the mislabeled item(s). +// Everything should be 'u:object_r:sysfs_net:s0' +// +// Another useful command is: +// adb root && sleep 1 && adb shell find /sys > dump.out +// or in particular: +// adb root && sleep 1 && adb shell find /sys | egrep '/net$' +// which might (among other things) print out something like: +// /sys/devices/platform/11110000.usb/11110000.dwc3/gadget/net +// which means you need to add: +// genfscon sysfs /devices/platform/11110000.usb/11110000.dwc3/gadget/net u:object_r:sysfs_net:s0 TEST(NetdSELinuxTest, CheckProperMTULabels) { // Since we expect the egrep regexp to filter everything out, // we thus expect no matches and thus a return code of 1 @@ -105,195 +121,5 @@ TEST(NetdNamespaceTest, CheckFullNamespaceSupport) { nsTest(CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWNET, true, thread); } -// Test for presence of kernel patch: -// ANDROID: net: bpf: permit redirect from ingress L3 to egress L2 devices at near max mtu -// on 4.14+ kernels. -TEST(NetdBpfTest, testBpfSkbChangeHeadAboveMTU) { - SKIP_IF_EXTENDED_BPF_NOT_SUPPORTED; - - constexpr int mtu = 1500; - - errno = 0; - - // Amusingly can't use SIOC... on tun/tap fds. - int rv = socket(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0); - ASSERT_EQ(errno, 0); - ASSERT_GE(rv, 3); - unique_fd unixfd(rv); - - rv = open("/dev/net/tun", O_RDWR | O_NONBLOCK); - ASSERT_EQ(errno, 0); - ASSERT_GE(rv, 3); - unique_fd tun(rv); - - rv = open("/dev/net/tun", O_RDWR | O_NONBLOCK); - ASSERT_EQ(errno, 0); - ASSERT_GE(rv, 3); - unique_fd tap(rv); - - struct ifreq tun_ifr = { - .ifr_flags = IFF_TUN | IFF_NO_PI, - .ifr_name = "tun_bpftest", - }; - - struct ifreq tap_ifr = { - .ifr_flags = IFF_TAP | IFF_NO_PI, - .ifr_name = "tap_bpftest", - }; - - rv = ioctl(tun, TUNSETIFF, &tun_ifr); - ASSERT_EQ(errno, 0); - ASSERT_EQ(rv, 0); - - rv = ioctl(tap, TUNSETIFF, &tap_ifr); - ASSERT_EQ(errno, 0); - ASSERT_EQ(rv, 0); - - // prevents kernel from sending us spurious ipv6 packets - rv = open("/proc/sys/net/ipv6/conf/tap_bpftest/disable_ipv6", O_WRONLY); - ASSERT_EQ(errno, 0); - ASSERT_GE(rv, 3); - unique_fd f(rv); - - rv = write(f, "1\n", 2); - ASSERT_EQ(errno, 0); - ASSERT_EQ(rv, 2); - - rv = close(f.release()); - ASSERT_EQ(errno, 0); - ASSERT_EQ(rv, 0); - - int tunif = if_nametoindex(tun_ifr.ifr_name); - ASSERT_GE(tunif, 2); - - int tapif = if_nametoindex(tap_ifr.ifr_name); - ASSERT_GE(tapif, 2); - - tun_ifr.ifr_mtu = mtu; - rv = ioctl(unixfd, SIOCSIFMTU, &tun_ifr); - ASSERT_EQ(errno, 0); - ASSERT_EQ(rv, 0); - - tap_ifr.ifr_mtu = mtu; - rv = ioctl(unixfd, SIOCSIFMTU, &tap_ifr); - ASSERT_EQ(errno, 0); - ASSERT_EQ(rv, 0); - - rv = ioctl(unixfd, SIOCGIFFLAGS, &tun_ifr); - ASSERT_EQ(errno, 0); - ASSERT_EQ(rv, 0); - - rv = ioctl(unixfd, SIOCGIFFLAGS, &tap_ifr); - ASSERT_EQ(errno, 0); - ASSERT_EQ(rv, 0); - - tun_ifr.ifr_flags |= IFF_UP | IFF_RUNNING; - - tap_ifr.ifr_flags |= IFF_UP | IFF_RUNNING; - - rv = ioctl(unixfd, SIOCSIFFLAGS, &tun_ifr); - ASSERT_EQ(errno, 0); - ASSERT_EQ(rv, 0); - - rv = ioctl(unixfd, SIOCSIFFLAGS, &tap_ifr); - ASSERT_EQ(errno, 0); - ASSERT_EQ(rv, 0); - - rv = tcQdiscAddDevClsact(tunif); - ASSERT_EQ(rv, 0); - - int bpfFd = getTetherIngressProgFd(/* ethernet */ false); - ASSERT_EQ(errno, 0); - ASSERT_GE(bpfFd, 3); - - rv = tcFilterAddDevIngressTether(tunif, bpfFd, /* ethernet*/ false); - ASSERT_EQ(rv, 0); - - bpf::BpfMap<TetherIngressKey, TetherIngressValue> bpfIngressMap; - bpf::BpfMap<uint32_t, TetherStatsValue> bpfStatsMap; - bpf::BpfMap<uint32_t, uint64_t> bpfLimitMap; - - rv = getTetherIngressMapFd(); - ASSERT_GE(rv, 3); - bpfIngressMap.reset(rv); - - rv = getTetherStatsMapFd(); - ASSERT_GE(rv, 3); - bpfStatsMap.reset(rv); - - rv = getTetherLimitMapFd(); - ASSERT_GE(rv, 3); - bpfLimitMap.reset(rv); - - TetherIngressKey key = { - .iif = static_cast<uint32_t>(tunif), - //.neigh6 = , - }; - - ethhdr hdr = { - .h_proto = htons(ETH_P_IPV6), - }; - - TetherIngressValue value = { - .oif = static_cast<uint32_t>(tapif), - .macHeader = hdr, - .pmtu = mtu, - }; - -#define ASSERT_OK(status) ASSERT_TRUE((status).ok()) - - ASSERT_OK(bpfIngressMap.writeValue(key, value, BPF_ANY)); - - uint32_t k = tunif; - TetherStatsValue stats = {}; - ASSERT_OK(bpfStatsMap.writeValue(k, stats, BPF_NOEXIST)); - - uint64_t limit = ~0uLL; - ASSERT_OK(bpfLimitMap.writeValue(k, limit, BPF_NOEXIST)); - - // minimal 'acceptable' 40-byte hoplimit 255 IPv6 packet, src ip 2000:: - uint8_t pkt[mtu] = { - 0x60, 0, 0, 0, 0, 40, 0, 255, 0x20, - }; - - // Iterate over all packet sizes from minimal ipv6 packet to mtu. - // Tethering ebpf program should forward the packet from tun to tap interface. - // TUN is L3, TAP is L2, so it will add a 14 byte ethernet header. - for (int pkt_size = 40; pkt_size <= mtu; ++pkt_size) { - rv = write(tun, pkt, pkt_size); - ASSERT_EQ(errno, 0); - ASSERT_EQ(rv, pkt_size); - - struct pollfd p = { - .fd = tap, - .events = POLLIN, - }; - - rv = poll(&p, 1, 1000 /*milliseconds*/); - if (rv == 0) { - // we hit a timeout at this packet size, log it - EXPECT_EQ(pkt_size, -1); - // this particular packet size is where it fails without the oneline kernel fix - if (pkt_size + ETH_HLEN == mtu + 1) EXPECT_EQ("detected missing kernel patch", ""); - break; - } - EXPECT_EQ(errno, 0); - EXPECT_EQ(rv, 1); - EXPECT_EQ(p.revents, POLLIN); - - // use a buffer 1 byte larger then what we expect so we don't simply get truncated down - uint8_t buf[ETH_HLEN + mtu + 1]; - rv = read(tap, buf, sizeof(buf)); - EXPECT_EQ(errno, 0); - EXPECT_EQ(rv, ETH_HLEN + pkt_size); - errno = 0; - if (rv < 0) break; - } - - ASSERT_OK(bpfIngressMap.deleteValue(key)); - ASSERT_OK(bpfStatsMap.deleteValue(k)); - ASSERT_OK(bpfLimitMap.deleteValue(k)); -} - } // namespace net } // namespace android diff --git a/tests/netlink_listener_test.cpp b/tests/netlink_listener_test.cpp index 46394cac..249bdfbe 100644 --- a/tests/netlink_listener_test.cpp +++ b/tests/netlink_listener_test.cpp @@ -45,7 +45,7 @@ constexpr uid_t TEST_UID = UID_MAX - 2; // A test tag arbitrarily selected. constexpr uint32_t TEST_TAG = 0xFF0F0F0F; -constexpr uint32_t SOCK_CLOSE_WAIT_US = 20 * 1000; +constexpr uint32_t SOCK_CLOSE_WAIT_US = 30 * 1000; constexpr uint32_t ENOBUFS_POLL_WAIT_US = 10 * 1000; using android::base::Result; @@ -67,15 +67,11 @@ class NetlinkListenerTest : public testing::Test { BpfMap<uint64_t, UidTagValue> mCookieTagMap; void SetUp() { - SKIP_IF_BPF_NOT_SUPPORTED; - mCookieTagMap.reset(android::bpf::mapRetrieveRW(COOKIE_TAG_MAP_PATH)); ASSERT_TRUE(mCookieTagMap.isValid()); } void TearDown() { - SKIP_IF_BPF_NOT_SUPPORTED; - const auto deleteTestCookieEntries = [](const uint64_t& key, const UidTagValue& value, BpfMap<uint64_t, UidTagValue>& map) { if ((value.uid == TEST_UID) && (value.tag == TEST_TAG)) { @@ -103,7 +99,7 @@ class NetlinkListenerTest : public testing::Test { return mCookieTagMap.iterateWithValue(checkGarbageTags); } - void checkMassiveSocketDestroy(int totalNumber, bool expectError) { + bool checkMassiveSocketDestroy(int totalNumber, bool expectError) { std::unique_ptr<android::net::NetlinkListenerInterface> skDestroyListener; auto result = android::net::TrafficController::makeSkDestroyListener(); if (!isOk(result)) { @@ -141,25 +137,36 @@ class NetlinkListenerTest : public testing::Test { // If ENOBUFS triggered, check it only called into the handler once, ie. // that the netlink handler is not spinning. int currentErrorCount = rxErrorCount; - EXPECT_LT(0, rxErrorCount); + // 0 error count is acceptable because the system has chances to close all sockets + // normally. + EXPECT_LE(0, rxErrorCount); + if (!rxErrorCount) return true; + usleep(ENOBUFS_POLL_WAIT_US); EXPECT_EQ(currentErrorCount, rxErrorCount); } else { EXPECT_RESULT_OK(checkNoGarbageTagsExist()); EXPECT_EQ(0, rxErrorCount); } + return false; } }; TEST_F(NetlinkListenerTest, TestAllSocketUntagged) { - SKIP_IF_BPF_NOT_SUPPORTED; - checkMassiveSocketDestroy(10, false); checkMassiveSocketDestroy(100, false); } -TEST_F(NetlinkListenerTest, TestSkDestroyError) { - SKIP_IF_BPF_NOT_SUPPORTED; - - checkMassiveSocketDestroy(32500, true); +// Disabled because flaky on blueline-userdebug; this test relies on the main thread +// winning a race against the NetlinkListener::run() thread. There's no way to ensure +// things will be scheduled the same way across all architectures and test environments. +TEST_F(NetlinkListenerTest, DISABLED_TestSkDestroyError) { + bool needRetry = false; + int retryCount = 0; + do { + needRetry = checkMassiveSocketDestroy(32500, true); + if (needRetry) retryCount++; + } while (needRetry && retryCount < 3); + // Should review test if it can always close all sockets correctly. + EXPECT_GT(3, retryCount); } |